webpack/lib/dependencies/AMDDefineDependencyParserPl...

329 lines
9.0 KiB
JavaScript
Raw Normal View History

2013-01-31 01:49:25 +08:00
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const AMDRequireItemDependency = require("./AMDRequireItemDependency");
const AMDRequireContextDependency = require("./AMDRequireContextDependency");
const ConstDependency = require("./ConstDependency");
const AMDDefineDependency = require("./AMDDefineDependency");
const AMDRequireArrayDependency = require("./AMDRequireArrayDependency");
const LocalModuleDependency = require("./LocalModuleDependency");
const ContextDependencyHelpers = require("./ContextDependencyHelpers");
const LocalModulesHelpers = require("./LocalModulesHelpers");
2013-01-31 01:49:25 +08:00
2017-11-08 18:32:05 +08:00
const isBoundFunctionExpression = expr => {
2018-02-25 09:00:20 +08:00
if (expr.type !== "CallExpression") return false;
if (expr.callee.type !== "MemberExpression") return false;
if (expr.callee.computed) return false;
if (expr.callee.object.type !== "FunctionExpression") return false;
if (expr.callee.property.type !== "Identifier") return false;
if (expr.callee.property.name !== "bind") return false;
return true;
2017-11-08 18:32:05 +08:00
};
2017-11-19 02:33:48 +08:00
function isUnboundFunctionExpression(expr) {
2018-02-25 09:00:20 +08:00
if (expr.type === "FunctionExpression") return true;
if (expr.type === "ArrowFunctionExpression") return true;
2017-11-19 02:33:48 +08:00
return false;
}
function isCallable(expr) {
2018-02-25 09:00:20 +08:00
if (isUnboundFunctionExpression(expr)) return true;
if (isBoundFunctionExpression(expr)) return true;
2017-11-19 02:33:48 +08:00
return false;
}
class AMDDefineDependencyParserPlugin {
constructor(options) {
this.options = options;
}
2018-03-05 20:51:12 +08:00
apply(parser) {
2018-03-17 00:57:57 +08:00
parser.hooks.call
.for("define")
.tap(
"AMDDefineDependencyParserPlugin",
this.processCallDefine.bind(this, parser)
);
}
2018-03-17 00:57:57 +08:00
processArray(parser, expr, param, identifiers, namedModule) {
2018-03-05 20:51:12 +08:00
if (param.isArray()) {
param.items.forEach((param, idx) => {
if (
param.isString() &&
["require", "module", "exports"].includes(param.string)
)
identifiers[idx] = param.string;
2018-03-17 00:57:57 +08:00
const result = this.processItem(parser, expr, param, namedModule);
2018-03-05 20:51:12 +08:00
if (result === undefined) {
2018-03-17 00:57:57 +08:00
this.processContext(parser, expr, param);
2018-03-05 20:51:12 +08:00
}
});
return true;
} else if (param.isConstArray()) {
const deps = [];
param.array.forEach((request, idx) => {
let dep;
let localModule;
if (request === "require") {
identifiers[idx] = request;
dep = "__webpack_require__";
} else if (["exports", "module"].includes(request)) {
identifiers[idx] = request;
dep = request;
2018-02-25 09:00:20 +08:00
} else if (
(localModule = LocalModulesHelpers.getLocalModule(
parser.state,
2018-03-05 20:51:12 +08:00
request
2018-02-25 09:00:20 +08:00
))
) {
// eslint-disable-line no-cond-assign
2018-03-05 20:51:12 +08:00
dep = new LocalModuleDependency(localModule);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
2017-11-27 22:27:41 +08:00
} else {
2018-03-05 20:51:12 +08:00
dep = this.newRequireItemDependency(request);
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
}
deps.push(dep);
});
const dep = this.newRequireArrayDependency(deps, param.range);
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
}
}
2018-03-17 00:57:57 +08:00
processItem(parser, expr, param, namedModule) {
2018-03-05 20:51:12 +08:00
if (param.isConditional()) {
param.options.forEach(param => {
2018-03-17 00:57:57 +08:00
const result = this.processItem(parser, expr, param);
2018-03-05 20:51:12 +08:00
if (result === undefined) {
2018-03-17 00:57:57 +08:00
this.processContext(parser, expr, param);
2017-11-27 22:27:41 +08:00
}
2018-03-05 20:51:12 +08:00
});
return true;
} else if (param.isString()) {
let dep, localModule;
if (param.string === "require") {
dep = new ConstDependency("__webpack_require__", param.range);
} else if (["require", "exports", "module"].includes(param.string)) {
dep = new ConstDependency(param.string, param.range);
} else if (
(localModule = LocalModulesHelpers.getLocalModule(
parser.state,
param.string,
namedModule
))
) {
// eslint-disable-line no-cond-assign
dep = new LocalModuleDependency(localModule, param.range);
} else {
dep = this.newRequireItemDependency(param.string, param.range);
2017-11-27 22:27:41 +08:00
}
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
2018-03-05 20:51:12 +08:00
}
}
2018-03-17 00:57:57 +08:00
processContext(parser, expr, param) {
2018-03-05 20:51:12 +08:00
const dep = ContextDependencyHelpers.create(
AMDRequireContextDependency,
param.range,
param,
expr,
this.options
);
if (!dep) return;
dep.loc = expr.loc;
2018-03-17 00:57:57 +08:00
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
2018-03-05 20:51:12 +08:00
return true;
}
2017-11-27 22:27:41 +08:00
2018-03-17 00:57:57 +08:00
processCallDefine(parser, expr) {
2018-03-05 20:51:12 +08:00
let array, fn, obj, namedModule;
switch (expr.arguments.length) {
case 1:
if (isCallable(expr.arguments[0])) {
2018-03-22 17:54:18 +08:00
// define(f() {…})
2018-03-05 20:51:12 +08:00
fn = expr.arguments[0];
} else if (expr.arguments[0].type === "ObjectExpression") {
2018-03-22 17:54:18 +08:00
// define({…})
2018-03-05 20:51:12 +08:00
obj = expr.arguments[0];
} else {
// define(expr)
// unclear if function or object
obj = fn = expr.arguments[0];
2014-01-31 21:09:08 +08:00
}
2018-03-05 20:51:12 +08:00
break;
case 2:
if (expr.arguments[0].type === "Literal") {
namedModule = expr.arguments[0].value;
2018-03-22 17:54:18 +08:00
// define("…", …)
2018-03-05 20:51:12 +08:00
if (isCallable(expr.arguments[1])) {
2018-03-22 17:54:18 +08:00
// define("…", f() {…})
2018-03-05 20:51:12 +08:00
fn = expr.arguments[1];
} else if (expr.arguments[1].type === "ObjectExpression") {
2018-03-22 17:54:18 +08:00
// define("…", {…})
2018-03-05 20:51:12 +08:00
obj = expr.arguments[1];
} else {
2018-03-22 17:54:18 +08:00
// define("…", expr)
2018-03-05 20:51:12 +08:00
// unclear if function or object
obj = fn = expr.arguments[1];
}
} else {
array = expr.arguments[0];
if (isCallable(expr.arguments[1])) {
2018-03-22 17:54:18 +08:00
// define([…], f() {})
2018-03-05 20:51:12 +08:00
fn = expr.arguments[1];
} else if (expr.arguments[1].type === "ObjectExpression") {
2018-03-22 17:54:18 +08:00
// define([…], {…})
2018-03-05 20:51:12 +08:00
obj = expr.arguments[1];
} else {
2018-03-22 17:54:18 +08:00
// define([…], expr)
2018-03-05 20:51:12 +08:00
// unclear if function or object
obj = fn = expr.arguments[1];
}
2018-02-25 09:00:20 +08:00
}
2018-03-05 20:51:12 +08:00
break;
case 3:
2018-03-22 17:54:18 +08:00
// define("…", […], f() {…})
2018-03-05 20:51:12 +08:00
namedModule = expr.arguments[0].value;
array = expr.arguments[1];
if (isCallable(expr.arguments[2])) {
2018-03-22 17:54:18 +08:00
// define("…", […], f() {})
2018-03-05 20:51:12 +08:00
fn = expr.arguments[2];
} else if (expr.arguments[2].type === "ObjectExpression") {
2018-03-22 17:54:18 +08:00
// define("…", […], {…})
2018-03-05 20:51:12 +08:00
obj = expr.arguments[2];
2018-02-25 09:00:20 +08:00
} else {
2018-03-22 17:54:18 +08:00
// define("…", […], expr)
2018-03-05 20:51:12 +08:00
// unclear if function or object
obj = fn = expr.arguments[2];
2018-02-25 09:00:20 +08:00
}
2018-03-05 20:51:12 +08:00
break;
default:
return;
}
let fnParams = null;
let fnParamsOffset = 0;
if (fn) {
if (isUnboundFunctionExpression(fn)) fnParams = fn.params;
else if (isBoundFunctionExpression(fn)) {
fnParams = fn.callee.object.params;
fnParamsOffset = fn.arguments.length - 1;
if (fnParamsOffset < 0) fnParamsOffset = 0;
}
}
let fnRenames = parser.scope.renames.createChild();
let identifiers;
if (array) {
identifiers = {};
const param = parser.evaluateExpression(array);
2018-03-17 00:57:57 +08:00
const result = this.processArray(
parser,
expr,
param,
identifiers,
namedModule
);
2018-03-05 20:51:12 +08:00
if (!result) return;
if (fnParams)
fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
if (identifiers[idx]) {
fnRenames.set(param.name, identifiers[idx]);
return false;
}
return true;
});
} else {
identifiers = ["require", "exports", "module"];
if (fnParams)
fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
if (identifiers[idx]) {
fnRenames.set(param.name, identifiers[idx]);
return false;
}
return true;
});
}
let inTry;
if (fn && isUnboundFunctionExpression(fn)) {
inTry = parser.scope.inTry;
parser.inScope(fnParams, () => {
parser.scope.renames = fnRenames;
parser.scope.inTry = inTry;
2018-03-05 23:22:38 +08:00
if (fn.body.type === "BlockStatement") {
2018-03-05 20:51:12 +08:00
parser.walkStatement(fn.body);
2018-03-05 23:22:38 +08:00
} else {
parser.walkExpression(fn.body);
}
2018-03-05 20:51:12 +08:00
});
} else if (fn && isBoundFunctionExpression(fn)) {
inTry = parser.scope.inTry;
parser.inScope(
fn.callee.object.params.filter(
i => !["require", "module", "exports"].includes(i.name)
),
() => {
parser.scope.renames = fnRenames;
parser.scope.inTry = inTry;
if (fn.callee.object.body.type === "BlockStatement")
parser.walkStatement(fn.callee.object.body);
else parser.walkExpression(fn.callee.object.body);
2018-02-25 09:00:20 +08:00
}
2018-03-05 20:51:12 +08:00
);
if (fn.arguments) parser.walkExpressions(fn.arguments);
} else if (fn || obj) {
parser.walkExpression(fn || obj);
}
2018-03-05 20:51:12 +08:00
const dep = this.newDefineDependency(
expr.range,
array ? array.range : null,
fn ? fn.range : null,
obj ? obj.range : null,
namedModule ? namedModule : null
);
dep.loc = expr.loc;
if (namedModule) {
dep.localModule = LocalModulesHelpers.addLocalModule(
parser.state,
namedModule
);
}
parser.state.current.addDependency(dep);
return true;
}
2018-03-17 00:57:57 +08:00
newDefineDependency(
range,
arrayRange,
functionRange,
objectRange,
namedModule
) {
return new AMDDefineDependency(
range,
arrayRange,
functionRange,
objectRange,
namedModule
);
2018-03-05 20:51:12 +08:00
}
2018-03-17 00:57:57 +08:00
newRequireArrayDependency(depsArray, range) {
return new AMDRequireArrayDependency(depsArray, range);
2018-03-05 20:51:12 +08:00
}
2018-03-17 00:57:57 +08:00
newRequireItemDependency(request, range) {
return new AMDRequireItemDependency(request, range);
}
}
module.exports = AMDDefineDependencyParserPlugin;