upgrade tapable from Parser

This commit is contained in:
Tobias Koppers 2017-11-27 15:27:41 +01:00
parent 3f453a4f3c
commit c10da6c285
8 changed files with 330 additions and 229 deletions

View File

@ -11,6 +11,7 @@ const ConstDependency = require("./dependencies/ConstDependency");
const NullFactory = require("./NullFactory");
const ParserHelpers = require("./ParserHelpers");
const createHash = require("./util/createHash");
const SyncBailHook = require("tapable").SyncBailHook;
module.exports = class HotModuleReplacementPlugin {
constructor(options) {
@ -198,6 +199,11 @@ module.exports = class HotModuleReplacementPlugin {
parser.plugin("evaluate Identifier module.hot", expr => {
return ParserHelpers.evaluateToIdentifier("module.hot", !!parser.state.compilation.hotUpdateChunkTemplate)(expr);
});
// TODO webpack 5: refactor this, no custom hooks
if(!parser.hooks.hotAcceptCallback)
parser.hooks.hotAcceptCallback = new SyncBailHook(["expression", "requests"]);
if(!parser.hooks.hotAcceptWithoutCallback)
parser.hooks.hotAcceptWithoutCallback = new SyncBailHook(["expression", "requests"]);
parser.plugin("call module.hot.accept", expr => {
if(!parser.state.compilation.hotUpdateChunkTemplate) return false;
if(expr.arguments.length >= 1) {
@ -220,9 +226,9 @@ module.exports = class HotModuleReplacementPlugin {
requests.push(request);
});
if(expr.arguments.length > 1)
parser.applyPluginsBailResult("hot accept callback", expr.arguments[1], requests);
parser.hooks.hotAcceptCallback.call(expr.arguments[1], requests);
else
parser.applyPluginsBailResult("hot accept without callback", expr, requests);
parser.hooks.hotAcceptWithoutCallback.call(expr, requests);
}
}
});

View File

@ -7,7 +7,9 @@
// Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
const acorn = require("acorn-dynamic-import").default;
const Tapable = require("tapable-old");
const Tapable = require("tapable").Tapable;
const SyncBailHook = require("tapable").SyncBailHook;
const HookMap = require("tapable/lib/HookMap");
const vm = require("vm");
const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
const StackedSetMap = require("./util/StackedSetMap");
@ -71,6 +73,75 @@ class TrackingSet {
class Parser extends Tapable {
constructor(options) {
super();
this.hooks = {
evaluateTypeof: new HookMap(() => new SyncBailHook(["expression"])),
evaluate: new HookMap(() => new SyncBailHook(["expression"])),
evaluateIdentifier: new HookMap(() => new SyncBailHook(["expression"])),
evaluateDefinedIdentifier: new HookMap(() => new SyncBailHook(["expression"])),
evaluateCallExpressionMember: new HookMap(() => new SyncBailHook(["expression", "param"])),
statement: new SyncBailHook(["statement"]),
statementIf: new SyncBailHook(["statement"]),
label: new HookMap(() => new SyncBailHook(["statement"])),
import: new SyncBailHook(["statement", "source"]),
importSpecifier: new SyncBailHook(["statement", "source", "exportName", "identifierName"]),
export: new SyncBailHook(["statement"]),
exportImport: new SyncBailHook(["statement", "source"]),
exportDeclaration: new SyncBailHook(["statement", "declaration"]),
exportExpression: new SyncBailHook(["statement", "declaration"]),
exportSpecifier: new SyncBailHook(["statement", "identifierName", "exportName", "index"]),
exportImportSpecifier: new SyncBailHook(["statement", "source", "identifierName", "exportName", "index"]),
varDeclaration: new HookMap(() => new SyncBailHook(["declaration"])),
varDeclarationLet: new HookMap(() => new SyncBailHook(["declaration"])),
varDeclarationConst: new HookMap(() => new SyncBailHook(["declaration"])),
varDeclarationVar: new HookMap(() => new SyncBailHook(["declaration"])),
canRename: new HookMap(() => new SyncBailHook(["initExpression"])),
rename: new HookMap(() => new SyncBailHook(["initExpression"])),
assigned: new HookMap(() => new SyncBailHook(["expression"])),
assign: new HookMap(() => new SyncBailHook(["expression"])),
typeof: new HookMap(() => new SyncBailHook(["expression"])),
expressionConditionalOperator: new SyncBailHook(["expression"]),
importCall: new SyncBailHook(["expression"]),
call: new HookMap(() => new SyncBailHook(["expression"])),
callAnyMember: new HookMap(() => new SyncBailHook(["expression"])),
expression: new HookMap(() => new SyncBailHook(["expression"])),
expressionAnyMember: new HookMap(() => new SyncBailHook(["expression"])),
program: new SyncBailHook(["ast", "comments"]),
};
const HOOK_MAP_COMPAT_CONFIG = {
evaluateTypeof: /^evaluate typeof (.+)$/,
evaluateIdentifier: /^evaluate Identifier (.+)$/,
evaluateDefinedIdentifier: /^evaluate defined Identifier (.+)$/,
evaluateCallExpressionMember: /^evaluate CallExpression .(.+)$/,
evaluate: /^evaluate (.+)$/,
label: /^label (.+)$/,
varDeclarationLet: /^var-let (.+)$/,
varDeclarationConst: /^var-const (.+)$/,
varDeclarationVar: /^var-var (.+)$/,
varDeclaration: /^var (.+)$/,
canRename: /^can-rename (.+)$/,
rename: /^rename (.+)$/,
typeof: /^typeof (.+)$/,
assigned: /^assigned (.+)$/,
assign: /^assign (.+)$/,
expressionConditionalOperator: /^expression \?:$/,
callAnyMember: /^call (.+)\.\*$/,
call: /^call (.+)$/,
expressionAnyMember: /^expression (.+)\.\*$/,
expression: /^expression (.+)$/,
};
this._pluginCompat.tap("Parser", options => {
for(const name of Object.keys(HOOK_MAP_COMPAT_CONFIG)) {
const regexp = HOOK_MAP_COMPAT_CONFIG[name];
const match = regexp.exec(options.name);
if(match) {
if(match[1])
this.hooks[name].tap(match[1], options.fn.name || "unnamed compat plugin", options.fn.bind(this));
else
this.hooks[name].tap(options.fn.name || "unnamed compat plugin", options.fn.bind(this));
return true;
}
}
});
this.options = options;
this.scope = undefined;
this.state = undefined;
@ -240,14 +311,14 @@ class Parser extends Tapable {
if(expr.argument.type === "Identifier") {
name = this.scope.renames.get(expr.argument.name) || expr.argument.name;
if(!this.scope.definitions.has(name)) {
res = this.applyPluginsBailResult1("evaluate typeof " + name, expr);
res = this.hooks.evaluateTypeof.for(name).call(expr);
if(res !== undefined) return res;
}
}
if(expr.argument.type === "MemberExpression") {
const exprName = this.getNameForExpression(expr.argument);
if(exprName && exprName.free) {
res = this.applyPluginsBailResult1("evaluate typeof " + exprName.name, expr);
res = this.hooks.evaluateTypeof.for(exprName.name).call(expr);
if(res !== undefined) return res;
}
}
@ -281,17 +352,17 @@ class Parser extends Tapable {
this.plugin("evaluate Identifier", expr => {
const name = this.scope.renames.get(expr.name) || expr.name;
if(!this.scope.definitions.has(expr.name)) {
const result = this.applyPluginsBailResult1("evaluate Identifier " + name, expr);
const result = this.hooks.evaluateIdentifier.for(name).call(expr);
if(result) return result;
return new BasicEvaluatedExpression().setIdentifier(name).setRange(expr.range);
} else {
return this.applyPluginsBailResult1("evaluate defined Identifier " + name, expr);
return this.hooks.evaluateDefinedIdentifier.for(name).call(expr);
}
});
this.plugin("evaluate ThisExpression", expr => {
const name = this.scope.renames.get("this");
if(name) {
const result = this.applyPluginsBailResult1("evaluate Identifier " + name, expr);
const result = this.hooks.evaluateIdentifier.for(name).call(expr);
if(result) return result;
return new BasicEvaluatedExpression().setIdentifier(name).setRange(expr.range);
}
@ -300,11 +371,11 @@ class Parser extends Tapable {
let exprName = this.getNameForExpression(expression);
if(exprName) {
if(exprName.free) {
const result = this.applyPluginsBailResult1("evaluate Identifier " + exprName.name, expression);
const result = this.hooks.evaluateIdentifier.for(exprName.name).call(expression);
if(result) return result;
return new BasicEvaluatedExpression().setIdentifier(exprName.name).setRange(expression.range);
} else {
return this.applyPluginsBailResult1("evaluate defined Identifier " + exprName.name, expression);
return this.hooks.evaluateDefinedIdentifier.for(exprName.name).call(expression);
}
}
});
@ -314,7 +385,7 @@ class Parser extends Tapable {
const param = this.evaluateExpression(expr.callee.object);
if(!param) return;
const property = expr.callee.property.name || expr.callee.property.value;
return this.applyPluginsBailResult2("evaluate CallExpression ." + property, expr, param);
return this.hooks.evaluateCallExpressionMember.for(property).call(expr, param);
});
this.plugin("evaluate CallExpression .replace", (expr, param) => {
if(!param.isString()) return;
@ -515,7 +586,7 @@ class Parser extends Tapable {
}
walkStatement(statement) {
if(this.applyPluginsBailResult1("statement", statement) !== undefined) return;
if(this.hooks.statement.call(statement) !== undefined) return;
const handler = this["walk" + statement.type];
if(handler)
handler.call(this, statement);
@ -541,7 +612,7 @@ class Parser extends Tapable {
}
walkIfStatement(statement) {
const result = this.applyPluginsBailResult1("statement if", statement);
const result = this.hooks.statementIf.call(statement);
if(result === undefined) {
this.walkExpression(statement.test);
this.walkStatement(statement.consequent);
@ -560,7 +631,7 @@ class Parser extends Tapable {
}
walkLabeledStatement(statement) {
const result = this.applyPluginsBailResult1("label " + statement.label.name, statement);
const result = this.hooks.label.for(statement.label.name).call(statement);
if(result !== true)
this.walkStatement(statement.body);
}
@ -708,20 +779,20 @@ class Parser extends Tapable {
prewalkImportDeclaration(statement) {
const source = statement.source.value;
this.applyPluginsBailResult2("import", statement, source);
this.hooks.import.call(statement, source);
statement.specifiers.forEach(specifier => {
const name = specifier.local.name;
this.scope.renames.set(name, null);
this.scope.definitions.add(name);
switch(specifier.type) {
case "ImportDefaultSpecifier":
this.applyPluginsBailResult4("import specifier", statement, source, "default", name);
this.hooks.importSpecifier.call(statement, source, "default", name);
break;
case "ImportSpecifier":
this.applyPluginsBailResult4("import specifier", statement, source, specifier.imported.name, name);
this.hooks.importSpecifier.call(statement, source, specifier.imported.name, name);
break;
case "ImportNamespaceSpecifier":
this.applyPluginsBailResult4("import specifier", statement, source, null, name);
this.hooks.importSpecifier.call(statement, source, null, name);
break;
}
});
@ -731,15 +802,15 @@ class Parser extends Tapable {
let source;
if(statement.source) {
source = statement.source.value;
this.applyPluginsBailResult2("export import", statement, source);
this.hooks.exportImport.call(statement, source);
} else {
this.applyPluginsBailResult1("export", statement);
this.hooks.export.call(statement);
}
if(statement.declaration) {
if(/Expression$/.test(statement.declaration.type)) {
throw new Error("Doesn't occur?");
} else {
if(!this.applyPluginsBailResult2("export declaration", statement, statement.declaration)) {
if(!this.hooks.exportDeclaration.call(statement, statement.declaration)) {
const originalDefinitions = this.scope.definitions;
const tracker = new TrackingSet(this.scope.definitions);
this.scope.definitions = tracker;
@ -748,7 +819,7 @@ class Parser extends Tapable {
this.scope.definitions = originalDefinitions;
for(let index = newDefs.length - 1; index >= 0; index--) {
const def = newDefs[index];
this.applyPluginsBailResult4("export specifier", statement, def, def, index);
this.hooks.exportSpecifier.call(statement, def, def, index);
}
}
}
@ -761,9 +832,9 @@ class Parser extends Tapable {
{
const name = specifier.exported.name;
if(source)
this.applyPluginsBailResult5("export import specifier", statement, source, specifier.local.name, name, specifierIndex);
this.hooks.exportImportSpecifier.call(statement, source, specifier.local.name, name, specifierIndex);
else
this.applyPluginsBailResult4("export specifier", statement, specifier.local.name, name, specifierIndex);
this.hooks.exportSpecifier.call(statement, specifier.local.name, name, specifierIndex);
break;
}
}
@ -787,29 +858,29 @@ class Parser extends Tapable {
this.scope.definitions = originalDefinitions;
for(let index = 0, len = newDefs.length; index < len; index++) {
const def = newDefs[index];
this.applyPluginsBailResult3("export specifier", statement, def, "default");
this.hooks.exportSpecifier.call(statement, def, "default");
}
}
}
walkExportDefaultDeclaration(statement) {
this.applyPluginsBailResult1("export", statement);
this.hooks.export.call(statement);
if(/Declaration$/.test(statement.declaration.type)) {
if(!this.applyPluginsBailResult2("export declaration", statement, statement.declaration)) {
if(!this.hooks.exportDeclaration.call(statement, statement.declaration)) {
this.walkStatement(statement.declaration);
}
} else {
this.walkExpression(statement.declaration);
if(!this.applyPluginsBailResult2("export expression", statement, statement.declaration)) {
this.applyPluginsBailResult3("export specifier", statement, statement.declaration, "default");
if(!this.hooks.exportExpression.call(statement, statement.declaration)) {
this.hooks.exportSpecifier.call(statement, statement.declaration, "default");
}
}
}
prewalkExportAllDeclaration(statement) {
const source = statement.source.value;
this.applyPluginsBailResult2("export import", statement, source);
this.applyPluginsBailResult5("export import specifier", statement, source, null, null, 0);
this.hooks.exportImport.call(statement, source);
this.hooks.exportImportSpecifier.call(statement, source, null, null, 0);
}
prewalkVariableDeclaration(statement) {
@ -864,8 +935,11 @@ class Parser extends Tapable {
case "VariableDeclarator":
{
this.enterPattern(declarator.id, (name, decl) => {
if(!this.applyPluginsBailResult1("var-" + declarator.kind + " " + name, decl)) {
if(!this.applyPluginsBailResult1("var " + name, decl)) {
const hookMap = declarator.kind === "const" ? this.hooks.varDeclarationConst :
declarator.kind === "let" ? this.hooks.varDeclarationLet :
this.hooks.varDeclarationVar;
if(!hookMap.for(name).call(decl)) {
if(!this.hooks.varDeclaration.for(name).call(decl)) {
this.scope.renames.set(name, null);
this.scope.definitions.add(name);
}
@ -883,9 +957,9 @@ class Parser extends Tapable {
case "VariableDeclarator":
{
const renameIdentifier = declarator.init && this.getRenameIdentifier(declarator.init);
if(renameIdentifier && declarator.id.type === "Identifier" && this.applyPluginsBailResult1("can-rename " + renameIdentifier, declarator.init)) {
if(renameIdentifier && declarator.id.type === "Identifier" && this.hooks.canRename.for(renameIdentifier).call(declarator.init)) {
// renaming with "var a = b;"
if(!this.applyPluginsBailResult1("rename " + renameIdentifier, declarator.init)) {
if(!this.hooks.rename.for(renameIdentifier).call(declarator.init)) {
this.scope.renames.set(declarator.id.name, this.scope.renames.get(renameIdentifier) || renameIdentifier);
this.scope.definitions.delete(declarator.id.name);
}
@ -1019,7 +1093,7 @@ class Parser extends Tapable {
if(expression.operator === "typeof") {
const exprName = this.getNameForExpression(expression.argument);
if(exprName && exprName.free) {
const result = this.applyPluginsBailResult1("typeof " + exprName.name, expression);
const result = this.hooks.typeof.for(exprName.name).call(expression);
if(result === true)
return;
}
@ -1042,18 +1116,18 @@ class Parser extends Tapable {
walkAssignmentExpression(expression) {
const renameIdentifier = this.getRenameIdentifier(expression.right);
if(expression.left.type === "Identifier" && renameIdentifier && this.applyPluginsBailResult1("can-rename " + renameIdentifier, expression.right)) {
if(expression.left.type === "Identifier" && renameIdentifier && this.hooks.canRename.for(renameIdentifier).call(expression.right)) {
// renaming "a = b;"
if(!this.applyPluginsBailResult1("rename " + renameIdentifier, expression.right)) {
if(!this.hooks.rename.for(renameIdentifier).call(expression.right)) {
this.scope.renames.set(expression.left.name, renameIdentifier);
this.scope.definitions.delete(expression.left.name);
}
} else if(expression.left.type === "Identifier") {
if(!this.applyPluginsBailResult1("assigned " + expression.left.name, expression)) {
if(!this.hooks.assigned.for(expression.left.name).call(expression)) {
this.walkExpression(expression.right);
}
this.scope.renames.set(expression.left.name, null);
if(!this.applyPluginsBailResult1("assign " + expression.left.name, expression)) {
if(!this.hooks.assign.for(expression.left.name).call(expression)) {
this.walkExpression(expression.left);
}
} else {
@ -1066,7 +1140,7 @@ class Parser extends Tapable {
}
walkConditionalExpression(expression) {
const result = this.applyPluginsBailResult1("expression ?:", expression);
const result = this.hooks.expressionConditionalOperator.call(expression);
if(result === undefined) {
this.walkExpression(expression.test);
this.walkExpression(expression.consequent);
@ -1113,8 +1187,8 @@ class Parser extends Tapable {
const walkIIFE = (functionExpression, options, currentThis) => {
const renameArgOrThis = argOrThis => {
const renameIdentifier = this.getRenameIdentifier(argOrThis);
if(renameIdentifier && this.applyPluginsBailResult1("can-rename " + renameIdentifier, argOrThis)) {
if(!this.applyPluginsBailResult1("rename " + renameIdentifier, argOrThis))
if(renameIdentifier && this.hooks.canRename.for(renameIdentifier).call(argOrThis)) {
if(!this.hooks.rename.for(renameIdentifier).call(argOrThis))
return renameIdentifier;
}
this.walkExpression(argOrThis);
@ -1152,7 +1226,7 @@ class Parser extends Tapable {
// (function(...) { }(...))
walkIIFE.call(this, expression.callee, expression.arguments);
} else if(expression.callee.type === "Import") {
result = this.applyPluginsBailResult1("import-call", expression);
result = this.hooks.importCall.call(expression);
if(result === true)
return;
@ -1162,12 +1236,12 @@ class Parser extends Tapable {
const callee = this.evaluateExpression(expression.callee);
if(callee.isIdentifier()) {
result = this.applyPluginsBailResult1("call " + callee.identifier, expression);
result = this.hooks.call.for(callee.identifier).call(expression);
if(result === true)
return;
let identifier = callee.identifier.replace(/\.[^.]+$/, ".*");
let identifier = callee.identifier.replace(/\.[^.]+$/, "");
if(identifier !== callee.identifier) {
result = this.applyPluginsBailResult1("call " + identifier, expression);
result = this.hooks.callAnyMember.for(identifier).call(expression);
if(result === true)
return;
}
@ -1183,10 +1257,10 @@ class Parser extends Tapable {
walkMemberExpression(expression) {
const exprName = this.getNameForExpression(expression);
if(exprName && exprName.free) {
let result = this.applyPluginsBailResult1("expression " + exprName.name, expression);
let result = this.hooks.expression.for(exprName.name).call(expression);
if(result === true)
return;
result = this.applyPluginsBailResult1("expression " + exprName.nameGeneral, expression);
result = this.hooks.expressionAnyMember.for(exprName.nameGeneral).call(expression);
if(result === true)
return;
}
@ -1197,7 +1271,7 @@ class Parser extends Tapable {
walkIdentifier(expression) {
if(!this.scope.definitions.has(expression.name)) {
const result = this.applyPluginsBailResult1("expression " + (this.scope.renames.get(expression.name) || expression.name), expression);
const result = this.hooks.expression.for(this.scope.renames.get(expression.name) || expression.name).call(expression);
if(result === true)
return;
}
@ -1265,7 +1339,7 @@ class Parser extends Tapable {
evaluateExpression(expression) {
try {
const result = this.applyPluginsBailResult1("evaluate " + expression.type, expression);
const result = this.hooks.evaluate.for(expression.type).call(expression);
if(result !== undefined)
return result;
} catch(e) {
@ -1415,7 +1489,7 @@ class Parser extends Tapable {
};
const state = this.state = initialState || {};
this.comments = comments;
if(this.applyPluginsBailResult2("program", ast, comments) === undefined) {
if(this.hooks.program.call(ast, comments) === undefined) {
this.prewalkStatements(ast.body);
this.walkStatements(ast.body);
}
@ -1481,10 +1555,12 @@ class Parser extends Tapable {
return null;
}
let prefix = "";
for(let i = exprName.length - 1; i >= 1; i--)
for(let i = exprName.length - 1; i >= 2; i--)
prefix += exprName[i] + ".";
const name = prefix + exprName[0];
const nameGeneral = prefix + "*";
if(exprName.length > 1)
prefix += exprName[1];
const name = prefix ? prefix + "." + exprName[0] : exprName[0];
const nameGeneral = prefix;
return {
name,
nameGeneral,

View File

@ -46,6 +46,83 @@ class AMDDefineDependencyParserPlugin {
apply(parser) {
const options = this.options;
const processArray = (expr, param, identifiers, namedModule) => {
if(param.isArray()) {
param.items.forEach((param, idx) => {
if(param.isString() && ["require", "module", "exports"].indexOf(param.string) >= 0)
identifiers[idx] = param.string;
const result = processItem(expr, param, namedModule);
if(result === undefined) {
processContext(expr, param);
}
});
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"].indexOf(request) >= 0) {
identifiers[idx] = request;
dep = request;
} else if(localModule = LocalModulesHelpers.getLocalModule(parser.state, request)) { // eslint-disable-line no-cond-assign
dep = new LocalModuleDependency(localModule);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
} else {
dep = new AMDRequireItemDependency(request);
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
}
deps.push(dep);
});
const dep = new AMDRequireArrayDependency(deps, param.range);
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
}
};
const processItem = (expr, param, namedModule) => {
if(param.isConditional()) {
param.options.forEach((param) => {
const result = processItem(expr, param);
if(result === undefined) {
processContext(expr, param);
}
});
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"].indexOf(param.string) >= 0) {
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 = new AMDRequireItemDependency(param.string, param.range);
}
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
}
};
const processContext = (expr, param) => {
const dep = ContextDependencyHelpers.create(AMDRequireContextDependency, param.range, param, expr, options);
if(!dep) return;
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
};
parser.plugin("call define", (expr) => {
let array, fn, obj, namedModule;
switch(expr.arguments.length) {
@ -126,7 +203,7 @@ class AMDDefineDependencyParserPlugin {
if(array) {
identifiers = {};
const param = parser.evaluateExpression(array);
const result = parser.applyPluginsBailResult("call define:amd:array", expr, param, identifiers, namedModule);
const result = processArray(expr, param, identifiers, namedModule);
if(!result) return;
if(fnParams) fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
if(identifiers[idx]) {
@ -186,81 +263,6 @@ class AMDDefineDependencyParserPlugin {
parser.state.current.addDependency(dep);
return true;
});
parser.plugin("call define:amd:array", (expr, param, identifiers, namedModule) => {
if(param.isArray()) {
param.items.forEach((param, idx) => {
if(param.isString() && ["require", "module", "exports"].indexOf(param.string) >= 0)
identifiers[idx] = param.string;
const result = parser.applyPluginsBailResult("call define:amd:item", expr, param, namedModule);
if(result === undefined) {
parser.applyPluginsBailResult("call define:amd:context", expr, param);
}
});
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"].indexOf(request) >= 0) {
identifiers[idx] = request;
dep = request;
} else if(localModule = LocalModulesHelpers.getLocalModule(parser.state, request)) { // eslint-disable-line no-cond-assign
dep = new LocalModuleDependency(localModule);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
} else {
dep = new AMDRequireItemDependency(request);
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
}
deps.push(dep);
});
const dep = new AMDRequireArrayDependency(deps, param.range);
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
}
});
parser.plugin("call define:amd:item", (expr, param, namedModule) => {
if(param.isConditional()) {
param.options.forEach((param) => {
const result = parser.applyPluginsBailResult("call define:amd:item", expr, param);
if(result === undefined) {
parser.applyPluginsBailResult("call define:amd:context", expr, param);
}
});
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"].indexOf(param.string) >= 0) {
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 = new AMDRequireItemDependency(param.string, param.range);
}
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
}
});
parser.plugin("call define:amd:context", (expr, param) => {
const dep = ContextDependencyHelpers.create(AMDRequireContextDependency, param.range, param, expr, options);
if(!dep) return;
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
});
}
}
module.exports = AMDDefineDependencyParserPlugin;

View File

@ -45,67 +45,13 @@ class AMDRequireDependenciesBlockParserPlugin {
apply(parser) {
const options = this.options;
parser.plugin("call require", (expr) => {
let param;
let dep;
let result;
const old = parser.state.current;
if(expr.arguments.length >= 1) {
param = parser.evaluateExpression(expr.arguments[0]);
dep = new AMDRequireDependenciesBlock(
expr,
param.range,
(expr.arguments.length > 1) ? expr.arguments[1].range : null,
(expr.arguments.length > 2) ? expr.arguments[2].range : null,
parser.state.module,
expr.loc
);
parser.state.current = dep;
}
if(expr.arguments.length === 1) {
parser.inScope([], () => {
result = parser.applyPluginsBailResult("call require:amd:array", expr, param);
});
parser.state.current = old;
if(!result) return;
parser.state.current.addBlock(dep);
return true;
}
if(expr.arguments.length === 2 || expr.arguments.length === 3) {
try {
parser.inScope([], () => {
result = parser.applyPluginsBailResult("call require:amd:array", expr, param);
});
if(!result) {
dep = new UnsupportedDependency("unsupported", expr.range);
old.addDependency(dep);
if(parser.state.module)
parser.state.module.errors.push(new UnsupportedFeatureWarning(parser.state.module, "Cannot statically analyse 'require(..., ...)' in line " + expr.loc.start.line));
dep = null;
return true;
}
dep.functionBindThis = this.processFunctionArgument(parser, expr.arguments[1]);
if(expr.arguments.length === 3) {
dep.errorCallbackBindThis = this.processFunctionArgument(parser, expr.arguments[2]);
}
} finally {
parser.state.current = old;
if(dep)
parser.state.current.addBlock(dep);
}
return true;
}
});
parser.plugin("call require:amd:array", (expr, param) => {
const processArray = (expr, param) => {
if(param.isArray()) {
param.items.forEach((param) => {
const result = parser.applyPluginsBailResult("call require:amd:item", expr, param);
const result = processItem(expr, param);
if(result === undefined) {
parser.applyPluginsBailResult("call require:amd:context", expr, param);
processContext(expr, param);
}
});
return true;
@ -135,13 +81,13 @@ class AMDRequireDependenciesBlockParserPlugin {
parser.state.current.addDependency(dep);
return true;
}
});
parser.plugin("call require:amd:item", (expr, param) => {
};
const processItem = (expr, param) => {
if(param.isConditional()) {
param.options.forEach((param) => {
const result = parser.applyPluginsBailResult("call require:amd:item", expr, param);
const result = processItem(expr, param);
if(result === undefined) {
parser.applyPluginsBailResult("call require:amd:context", expr, param);
processContext(expr, param);
}
});
return true;
@ -163,14 +109,70 @@ class AMDRequireDependenciesBlockParserPlugin {
parser.state.current.addDependency(dep);
return true;
}
});
parser.plugin("call require:amd:context", (expr, param) => {
};
const processContext = (expr, param) => {
const dep = ContextDependencyHelpers.create(AMDRequireContextDependency, param.range, param, expr, options);
if(!dep) return;
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
};
parser.plugin("call require", (expr) => {
let param;
let dep;
let result;
const old = parser.state.current;
if(expr.arguments.length >= 1) {
param = parser.evaluateExpression(expr.arguments[0]);
dep = new AMDRequireDependenciesBlock(
expr,
param.range,
(expr.arguments.length > 1) ? expr.arguments[1].range : null,
(expr.arguments.length > 2) ? expr.arguments[2].range : null,
parser.state.module,
expr.loc
);
parser.state.current = dep;
}
if(expr.arguments.length === 1) {
parser.inScope([], () => {
result = processArray(expr, param);
});
parser.state.current = old;
if(!result) return;
parser.state.current.addBlock(dep);
return true;
}
if(expr.arguments.length === 2 || expr.arguments.length === 3) {
try {
parser.inScope([], () => {
result = processArray(expr, param);
});
if(!result) {
dep = new UnsupportedDependency("unsupported", expr.range);
old.addDependency(dep);
if(parser.state.module)
parser.state.module.errors.push(new UnsupportedFeatureWarning(parser.state.module, "Cannot statically analyse 'require(..., ...)' in line " + expr.loc.start.line));
dep = null;
return true;
}
dep.functionBindThis = this.processFunctionArgument(parser, expr.arguments[1]);
if(expr.arguments.length === 3) {
dep.errorCallbackBindThis = this.processFunctionArgument(parser, expr.arguments[2]);
}
} finally {
parser.state.current = old;
if(dep)
parser.state.current.addBlock(dep);
}
return true;
}
});
}
}

View File

@ -19,6 +19,25 @@ class CommonJsRequireDependencyParserPlugin {
apply(parser) {
const options = this.options;
const processItem = (expr, param) => {
if(param.isString()) {
const dep = new CommonJsRequireDependency(param.string, param.range);
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
}
};
const processContext = (expr, param) => {
const dep = ContextDependencyHelpers.create(CommonJsRequireContextDependency, expr.range, param, expr, options);
if(!dep) return;
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
};
parser.plugin("expression require.cache", ParserHelpers.toConstantDependency("__webpack_require__.c"));
parser.plugin("expression require", (expr) => {
const dep = new CommonJsRequireContextDependency({
@ -44,7 +63,7 @@ class CommonJsRequireDependencyParserPlugin {
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
param.options.forEach(param => {
const result = parser.applyPluginsBailResult("call require:commonjs:item", expr, param);
const result = processItem(expr, param);
if(result === undefined) {
isExpression = true;
}
@ -61,9 +80,9 @@ class CommonJsRequireDependencyParserPlugin {
parser.state.current.addDependency(dep);
return true;
} else {
const result = parser.applyPluginsBailResult("call require:commonjs:item", expr, param);
const result = processItem(expr, param);
if(result === undefined) {
parser.applyPluginsBailResult("call require:commonjs:context", expr, param);
processContext(expr, param);
} else {
const dep = new RequireHeaderDependency(expr.callee.range);
dep.loc = expr.loc;
@ -72,23 +91,6 @@ class CommonJsRequireDependencyParserPlugin {
return true;
}
});
parser.plugin("call require:commonjs:item", (expr, param) => {
if(param.isString()) {
const dep = new CommonJsRequireDependency(param.string, param.range);
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
}
});
parser.plugin("call require:commonjs:context", (expr, param) => {
const dep = ContextDependencyHelpers.create(CommonJsRequireContextDependency, expr.range, param, expr, options);
if(!dep) return;
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
});
}
}
module.exports = CommonJsRequireDependencyParserPlugin;

View File

@ -9,6 +9,7 @@ const HarmonyImportSpecifierDependency = require("./HarmonyImportSpecifierDepend
const HarmonyAcceptImportDependency = require("./HarmonyAcceptImportDependency");
const HarmonyAcceptDependency = require("./HarmonyAcceptDependency");
const ConstDependency = require("./ConstDependency");
const SyncBailHook = require("tapable").SyncBailHook;
module.exports = class HarmonyImportDependencyParserPlugin {
constructor(moduleOptions) {
@ -97,6 +98,11 @@ module.exports = class HarmonyImportDependencyParserPlugin {
parser.walkExpressions(args);
return true;
});
// TODO webpack 5: refactor this, no custom hooks
if(!parser.hooks.hotAcceptCallback)
parser.hooks.hotAcceptCallback = new SyncBailHook(["expression", "requests"]);
if(!parser.hooks.hotAcceptWithoutCallback)
parser.hooks.hotAcceptWithoutCallback = new SyncBailHook(["expression", "requests"]);
parser.plugin("hot accept callback", (expr, requests) => {
const dependencies = requests
.map(request => {

View File

@ -16,20 +16,15 @@ class RequireResolveDependencyParserPlugin {
apply(parser) {
const options = this.options;
parser.plugin("call require.resolve", (expr) => {
return parser.applyPluginsBailResult("call require.resolve(Weak)", expr, false);
});
parser.plugin("call require.resolveWeak", (expr) => {
return parser.applyPluginsBailResult("call require.resolve(Weak)", expr, true);
});
parser.plugin("call require.resolve(Weak)", (expr, weak) => {
const process = (expr, weak) => {
if(expr.arguments.length !== 1) return;
const param = parser.evaluateExpression(expr.arguments[0]);
if(param.isConditional()) {
param.options.forEach((option) => {
const result = parser.applyPluginsBailResult("call require.resolve(Weak):item", expr, option, weak);
const result = processItem(expr, option, weak);
if(result === undefined) {
parser.applyPluginsBailResult("call require.resolve(Weak):context", expr, option, weak);
processContext(expr, option, weak);
}
});
const dep = new RequireResolveHeaderDependency(expr.callee.range);
@ -37,17 +32,17 @@ class RequireResolveDependencyParserPlugin {
parser.state.current.addDependency(dep);
return true;
} else {
const result = parser.applyPluginsBailResult("call require.resolve(Weak):item", expr, param, weak);
const result = processItem(expr, param, weak);
if(result === undefined) {
parser.applyPluginsBailResult("call require.resolve(Weak):context", expr, param, weak);
processContext(expr, param, weak);
}
const dep = new RequireResolveHeaderDependency(expr.callee.range);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
return true;
}
});
parser.plugin("call require.resolve(Weak):item", (expr, param, weak) => {
};
const processItem = (expr, param, weak) => {
if(param.isString()) {
const dep = new RequireResolveDependency(param.string, param.range);
dep.loc = expr.loc;
@ -56,8 +51,8 @@ class RequireResolveDependencyParserPlugin {
parser.state.current.addDependency(dep);
return true;
}
});
parser.plugin("call require.resolve(Weak):context", (expr, param, weak) => {
};
const processContext = (expr, param, weak) => {
const dep = ContextDependencyHelpers.create(RequireResolveContextDependency, param.range, param, expr, options, {
mode: weak ? "weak" : "sync"
});
@ -66,6 +61,13 @@ class RequireResolveDependencyParserPlugin {
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
};
parser.plugin("call require.resolve", (expr) => {
return process(expr, false);
});
parser.plugin("call require.resolveWeak", (expr) => {
return process(expr, true);
});
}
}

View File

@ -0,0 +1,5 @@
it("should name require in define correctly", function() {
define(["require"], function(require) {
(typeof require).should.be.eql("function");
});
});