143 lines
3.5 KiB
JavaScript
143 lines
3.5 KiB
JavaScript
import { basename, extname } from "path";
|
|
import template from "babel-template";
|
|
import transformCommonjs from "babel-plugin-transform-es2015-modules-commonjs";
|
|
|
|
const buildDefine = template(`
|
|
define(MODULE_NAME, [SOURCES], FACTORY);
|
|
`);
|
|
|
|
const buildFactory = template(`
|
|
(function (PARAMS) {
|
|
BODY;
|
|
})
|
|
`);
|
|
|
|
export default function({ types: t }) {
|
|
function isValidRequireCall(path) {
|
|
if (!path.isCallExpression()) return false;
|
|
if (!path.get("callee").isIdentifier({ name: "require" })) return false;
|
|
if (path.scope.getBinding("require")) return false;
|
|
|
|
const args = path.get("arguments");
|
|
if (args.length !== 1) return false;
|
|
|
|
const arg = args[0];
|
|
if (!arg.isStringLiteral()) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
function buildParamsAndSource(sourcesFound) {
|
|
const params = [];
|
|
const sources = [];
|
|
|
|
let hasSeenNonBareRequire = false;
|
|
for (let i = sourcesFound.length - 1; i > -1; i--) {
|
|
const source = sourcesFound[i];
|
|
|
|
sources.unshift(source[1]);
|
|
|
|
// bare import at end, no need for param
|
|
if (!hasSeenNonBareRequire && source[2] === true) {
|
|
continue;
|
|
}
|
|
|
|
hasSeenNonBareRequire = true;
|
|
params.unshift(source[0]);
|
|
}
|
|
|
|
return [params, sources];
|
|
}
|
|
|
|
const amdVisitor = {
|
|
ReferencedIdentifier({ node, scope }) {
|
|
if (node.name === "exports" && !scope.getBinding("exports")) {
|
|
this.hasExports = true;
|
|
}
|
|
|
|
if (node.name === "module" && !scope.getBinding("module")) {
|
|
this.hasModule = true;
|
|
}
|
|
},
|
|
|
|
CallExpression(path) {
|
|
if (!isValidRequireCall(path)) return;
|
|
const source = path.node.arguments[0];
|
|
const ref = path.scope.generateUidIdentifier(
|
|
basename(source.value, extname(source.value)),
|
|
);
|
|
this.sources.push([ref, source, true]);
|
|
path.remove();
|
|
},
|
|
|
|
VariableDeclarator(path) {
|
|
const id = path.get("id");
|
|
if (!id.isIdentifier()) return;
|
|
|
|
const init = path.get("init");
|
|
if (!isValidRequireCall(init)) return;
|
|
|
|
const source = init.node.arguments[0];
|
|
this.sourceNames[source.value] = true;
|
|
this.sources.push([id.node, source]);
|
|
|
|
path.remove();
|
|
},
|
|
};
|
|
|
|
return {
|
|
inherits: transformCommonjs,
|
|
|
|
pre() {
|
|
// source strings
|
|
this.sources = [];
|
|
this.sourceNames = Object.create(null);
|
|
|
|
this.hasExports = false;
|
|
this.hasModule = false;
|
|
},
|
|
|
|
visitor: {
|
|
Program: {
|
|
exit(path) {
|
|
if (this.ran) return;
|
|
this.ran = true;
|
|
|
|
path.traverse(amdVisitor, this);
|
|
|
|
const [params, sources] = buildParamsAndSource(this.sources);
|
|
|
|
let moduleName = this.getModuleName();
|
|
if (moduleName) moduleName = t.stringLiteral(moduleName);
|
|
|
|
if (this.hasExports) {
|
|
sources.unshift(t.stringLiteral("exports"));
|
|
params.unshift(t.identifier("exports"));
|
|
}
|
|
|
|
if (this.hasModule) {
|
|
sources.unshift(t.stringLiteral("module"));
|
|
params.unshift(t.identifier("module"));
|
|
}
|
|
|
|
const { node } = path;
|
|
const factory = buildFactory({
|
|
PARAMS: params,
|
|
BODY: node.body,
|
|
});
|
|
factory.expression.body.directives = node.directives;
|
|
node.directives = [];
|
|
|
|
node.body = [
|
|
buildDefine({
|
|
MODULE_NAME: moduleName,
|
|
SOURCES: sources,
|
|
FACTORY: factory,
|
|
}),
|
|
];
|
|
},
|
|
},
|
|
},
|
|
};
|
|
}
|