The build process failed because webpack does not allow failed imports when it is run as a module. This detects modules with default exports and only generates import statements for default exports where they are available.
132 lines
3.8 KiB
JavaScript
132 lines
3.8 KiB
JavaScript
import esMain from 'es-main';
|
|
import fse from 'fs-extra';
|
|
import generateInfo from './generate-info.js';
|
|
import path from 'path';
|
|
import {dirname} from 'path';
|
|
import {fileURLToPath} from 'url';
|
|
|
|
/**
|
|
* Read the symbols from info file.
|
|
* @return {Promise<Array>} Resolves with an array of symbol objects.
|
|
*/
|
|
async function getSymbols() {
|
|
const info = await generateInfo();
|
|
return info.symbols.filter((symbol) => symbol.kind != 'member');
|
|
}
|
|
|
|
/**
|
|
* Generate an import statement.
|
|
* @param {Object} symbol Symbol.
|
|
* @param {string} member Member.
|
|
* @return {string} An import statement.
|
|
*/
|
|
function getImport(symbol, member) {
|
|
const defaultExport = symbol.name.split('~');
|
|
if (symbol.isDefaultExport) {
|
|
const from = defaultExport[0].replace(/^module\:/, './');
|
|
const importName = from.replace(/[.\/]+/g, '$');
|
|
return `import ${importName} from '${from}.js';`;
|
|
}
|
|
const namedExport = symbol.name.split('.');
|
|
if (
|
|
member &&
|
|
namedExport.length > 1 &&
|
|
(defaultExport.length <= 1 || defaultExport[0].indexOf('.') !== -1)
|
|
) {
|
|
const from = namedExport[0].replace(/^module\:/, './');
|
|
const importName = from.replace(/[.\/]+/g, '_');
|
|
return `import {${member} as ${importName}$${member}} from '${from}.js';`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate code to export a named symbol.
|
|
* @param {Object} symbol Symbol.
|
|
* @param {Object<string, string>} namespaces Already defined namespaces.
|
|
* @param {Object} imports Imports.
|
|
* @return {string} Export code.
|
|
*/
|
|
function formatSymbolExport(symbol, namespaces, imports) {
|
|
const name = symbol.name;
|
|
const parts = name.split('~');
|
|
const nsParts = parts[0].replace(/^module\:/, '').split(/[\/\.]/);
|
|
const last = nsParts.length - 1;
|
|
const imp = getImport(symbol, nsParts[last]);
|
|
if (imp) {
|
|
const isNamed = parts[0].indexOf('.') !== -1;
|
|
const importName = isNamed
|
|
? '_' + nsParts.slice(0, last).join('_') + '$' + nsParts[last]
|
|
: '$' + nsParts.join('$');
|
|
let line = nsParts[0];
|
|
for (let i = 1, ii = nsParts.length; i < ii; ++i) {
|
|
line += `.${nsParts[i]}`;
|
|
namespaces[line] =
|
|
(line in namespaces ? namespaces[line] : true) && i < ii - 1;
|
|
}
|
|
line += ` = ${importName};`;
|
|
imports[imp] = true;
|
|
return line;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate export code given a list symbol names.
|
|
* @param {Array<Object>} symbols List of symbols.
|
|
* @return {string} Export code.
|
|
*/
|
|
function generateExports(symbols) {
|
|
const namespaces = {};
|
|
const imports = {};
|
|
const blocks = [];
|
|
symbols.forEach(function (symbol) {
|
|
const name = symbol.name;
|
|
if (name.indexOf('#') == -1) {
|
|
const imp = getImport(symbol);
|
|
if (imp) {
|
|
imports[imp] = true;
|
|
}
|
|
const line = formatSymbolExport(symbol, namespaces, imports);
|
|
if (line) {
|
|
blocks.push(line);
|
|
}
|
|
}
|
|
});
|
|
const nsdefs = [];
|
|
const ns = Object.keys(namespaces).sort();
|
|
for (let i = 0, ii = ns.length; i < ii; ++i) {
|
|
if (namespaces[ns[i]]) {
|
|
nsdefs.push(`${ns[i]} = {};`);
|
|
}
|
|
}
|
|
const defs = ['\nvar ol = {};'].concat(nsdefs, [...new Set(blocks)]);
|
|
const lines = Object.keys(imports).concat(defs.sort());
|
|
lines.push('', 'export default ol;');
|
|
return lines.join('\n');
|
|
}
|
|
|
|
/**
|
|
* Generate the exports code.
|
|
* @return {Promise<string>} Resolves with the exports code.
|
|
*/
|
|
export default async function main() {
|
|
const symbols = await getSymbols();
|
|
return generateExports(symbols);
|
|
}
|
|
|
|
/**
|
|
* If running this module directly, read the config file, call the main
|
|
* function, and write the output file.
|
|
*/
|
|
if (esMain(import.meta)) {
|
|
const baseDir = dirname(fileURLToPath(import.meta.url));
|
|
|
|
main()
|
|
.then(async (code) => {
|
|
const filepath = path.join(baseDir, '..', 'build', 'index.js');
|
|
await fse.outputFile(filepath, code);
|
|
})
|
|
.catch((err) => {
|
|
process.stderr.write(`${err.message}\n`, () => process.exit(1));
|
|
});
|
|
}
|