@jspm/generator
Version:
Package Import Map Generation Tool
146 lines (144 loc) • 5.65 kB
JavaScript
import { getIntegrity } from '../common/integrity.js';
// See: https://nodejs.org/docs/latest/api/modules.html#the-module-scope
const cjsGlobals = [
'__dirname',
'__filename',
'exports',
'module',
'require'
];
let babel;
export function setBabel(_babel) {
babel = _babel;
}
export async function createCjsAnalysis(imports, source, url) {
if (!babel) babel = await import('@babel/core');
const requires = new Set();
const lazy = new Set();
const unboundGlobals = new Set();
babel.transform(source, {
ast: false,
sourceMaps: false,
inputSourceMap: false,
babelrc: false,
babelrcRoots: false,
configFile: false,
highlightCode: false,
compact: false,
sourceType: 'script',
parserOpts: {
allowReturnOutsideFunction: true,
// plugins: stage3Syntax,
errorRecovery: true
},
plugins: [
({ types: t })=>{
return {
visitor: {
Program (path, state) {
state.functionDepth = 0;
},
CallExpression (path, state) {
if (t.isIdentifier(path.node.callee, {
name: 'require'
}) || t.isIdentifier(path.node.callee.object, {
name: 'require'
}) && t.isIdentifier(path.node.callee.property, {
name: 'resolve'
}) || t.isMemberExpression(path.node.callee) && t.isIdentifier(path.node.callee.object, {
name: 'module'
}) && t.isIdentifier(path.node.callee.property, {
name: 'require'
})) {
const req = buildDynamicString(path.get('arguments.0').node, url);
requires.add(req);
if (state.functionDepth > 0) lazy.add(req);
}
},
ReferencedIdentifier (path) {
let identifierName = path.node.name;
if (!path.scope.hasBinding(identifierName)) {
unboundGlobals.add(identifierName);
}
},
Scope: {
enter (path, state) {
if (t.isFunction(path.scope.block)) state.functionDepth++;
},
exit (path, state) {
if (t.isFunction(path.scope.block)) state.functionDepth--;
}
}
}
};
}
]
});
// Check if the module actually uses any CJS-specific globals, as otherwise
// other host runtimes like browser/deno can run this module anyway:
let usesCjs = false;
for (let g of cjsGlobals){
if (unboundGlobals.has(g)) {
usesCjs = true;
break;
}
}
return {
deps: [
...requires
],
dynamicDeps: imports.filter((impt)=>impt.n).map((impt)=>impt.n),
cjsLazyDeps: [
...lazy
],
size: source.length,
format: 'commonjs',
usesCjs,
integrity: await getIntegrity(source)
};
}
function buildDynamicString(node, fileName, isEsm = false, lastIsWildcard = false) {
if (node.type === 'StringLiteral') {
return node.value;
}
if (node.type === 'TemplateLiteral') {
let str = '';
for(let i = 0; i < node.quasis.length; i++){
const quasiStr = node.quasis[i].value.cooked;
if (quasiStr.length) {
str += quasiStr;
lastIsWildcard = false;
}
const nextNode = node.expressions[i];
if (nextNode) {
const nextStr = buildDynamicString(nextNode, fileName, isEsm, lastIsWildcard);
if (nextStr.length) {
lastIsWildcard = nextStr.endsWith('*');
str += nextStr;
}
}
}
return str;
}
if (node.type === 'BinaryExpression' && node.operator === '+') {
const leftResolved = buildDynamicString(node.left, fileName, isEsm, lastIsWildcard);
if (leftResolved.length) lastIsWildcard = leftResolved.endsWith('*');
const rightResolved = buildDynamicString(node.right, fileName, isEsm, lastIsWildcard);
return leftResolved + rightResolved;
}
if (node.type === 'Identifier') {
if (node.name === '__dirname') return '.';
if (node.name === '__filename') return './' + fileName;
}
// TODO: proper expression support
// new URL('...', import.meta.url).href | new URL('...', import.meta.url).toString() | new URL('...', import.meta.url).pathname
// import.meta.X
/*if (isEsm && node.type === 'MemberExpression' && node.object.type === 'MetaProperty' &&
node.object.meta.type === 'Identifier' && node.object.meta.name === 'import' &&
node.object.property.type === 'Identifier' && node.object.property.name === 'meta') {
if (node.property.type === 'Identifier' && node.property.name === 'url') {
return './' + fileName;
}
}*/ return lastIsWildcard ? '' : '*';
}
//# sourceMappingURL=cjs.js.map