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

View File

@ -46,6 +46,83 @@ class AMDDefineDependencyParserPlugin {
apply(parser) { apply(parser) {
const options = this.options; 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) => { parser.plugin("call define", (expr) => {
let array, fn, obj, namedModule; let array, fn, obj, namedModule;
switch(expr.arguments.length) { switch(expr.arguments.length) {
@ -126,7 +203,7 @@ class AMDDefineDependencyParserPlugin {
if(array) { if(array) {
identifiers = {}; identifiers = {};
const param = parser.evaluateExpression(array); 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(!result) return;
if(fnParams) fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => { if(fnParams) fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
if(identifiers[idx]) { if(identifiers[idx]) {
@ -186,81 +263,6 @@ class AMDDefineDependencyParserPlugin {
parser.state.current.addDependency(dep); parser.state.current.addDependency(dep);
return true; 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; module.exports = AMDDefineDependencyParserPlugin;

View File

@ -45,67 +45,13 @@ class AMDRequireDependenciesBlockParserPlugin {
apply(parser) { apply(parser) {
const options = this.options; const options = this.options;
parser.plugin("call require", (expr) => {
let param;
let dep;
let result;
const old = parser.state.current; const processArray = (expr, param) => {
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) => {
if(param.isArray()) { if(param.isArray()) {
param.items.forEach((param) => { param.items.forEach((param) => {
const result = parser.applyPluginsBailResult("call require:amd:item", expr, param); const result = processItem(expr, param);
if(result === undefined) { if(result === undefined) {
parser.applyPluginsBailResult("call require:amd:context", expr, param); processContext(expr, param);
} }
}); });
return true; return true;
@ -135,13 +81,13 @@ class AMDRequireDependenciesBlockParserPlugin {
parser.state.current.addDependency(dep); parser.state.current.addDependency(dep);
return true; return true;
} }
}); };
parser.plugin("call require:amd:item", (expr, param) => { const processItem = (expr, param) => {
if(param.isConditional()) { if(param.isConditional()) {
param.options.forEach((param) => { param.options.forEach((param) => {
const result = parser.applyPluginsBailResult("call require:amd:item", expr, param); const result = processItem(expr, param);
if(result === undefined) { if(result === undefined) {
parser.applyPluginsBailResult("call require:amd:context", expr, param); processContext(expr, param);
} }
}); });
return true; return true;
@ -163,14 +109,70 @@ class AMDRequireDependenciesBlockParserPlugin {
parser.state.current.addDependency(dep); parser.state.current.addDependency(dep);
return true; return true;
} }
}); };
parser.plugin("call require:amd:context", (expr, param) => { const processContext = (expr, param) => {
const dep = ContextDependencyHelpers.create(AMDRequireContextDependency, param.range, param, expr, options); const dep = ContextDependencyHelpers.create(AMDRequireContextDependency, param.range, param, expr, options);
if(!dep) return; if(!dep) return;
dep.loc = expr.loc; dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry; dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep); parser.state.current.addDependency(dep);
return true; 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) { apply(parser) {
const options = this.options; 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.cache", ParserHelpers.toConstantDependency("__webpack_require__.c"));
parser.plugin("expression require", (expr) => { parser.plugin("expression require", (expr) => {
const dep = new CommonJsRequireContextDependency({ const dep = new CommonJsRequireContextDependency({
@ -44,7 +63,7 @@ class CommonJsRequireDependencyParserPlugin {
dep.loc = expr.loc; dep.loc = expr.loc;
parser.state.current.addDependency(dep); parser.state.current.addDependency(dep);
param.options.forEach(param => { param.options.forEach(param => {
const result = parser.applyPluginsBailResult("call require:commonjs:item", expr, param); const result = processItem(expr, param);
if(result === undefined) { if(result === undefined) {
isExpression = true; isExpression = true;
} }
@ -61,9 +80,9 @@ class CommonJsRequireDependencyParserPlugin {
parser.state.current.addDependency(dep); parser.state.current.addDependency(dep);
return true; return true;
} else { } else {
const result = parser.applyPluginsBailResult("call require:commonjs:item", expr, param); const result = processItem(expr, param);
if(result === undefined) { if(result === undefined) {
parser.applyPluginsBailResult("call require:commonjs:context", expr, param); processContext(expr, param);
} else { } else {
const dep = new RequireHeaderDependency(expr.callee.range); const dep = new RequireHeaderDependency(expr.callee.range);
dep.loc = expr.loc; dep.loc = expr.loc;
@ -72,23 +91,6 @@ class CommonJsRequireDependencyParserPlugin {
return true; 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; module.exports = CommonJsRequireDependencyParserPlugin;

View File

@ -9,6 +9,7 @@ const HarmonyImportSpecifierDependency = require("./HarmonyImportSpecifierDepend
const HarmonyAcceptImportDependency = require("./HarmonyAcceptImportDependency"); const HarmonyAcceptImportDependency = require("./HarmonyAcceptImportDependency");
const HarmonyAcceptDependency = require("./HarmonyAcceptDependency"); const HarmonyAcceptDependency = require("./HarmonyAcceptDependency");
const ConstDependency = require("./ConstDependency"); const ConstDependency = require("./ConstDependency");
const SyncBailHook = require("tapable").SyncBailHook;
module.exports = class HarmonyImportDependencyParserPlugin { module.exports = class HarmonyImportDependencyParserPlugin {
constructor(moduleOptions) { constructor(moduleOptions) {
@ -97,6 +98,11 @@ module.exports = class HarmonyImportDependencyParserPlugin {
parser.walkExpressions(args); parser.walkExpressions(args);
return true; 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) => { parser.plugin("hot accept callback", (expr, requests) => {
const dependencies = requests const dependencies = requests
.map(request => { .map(request => {

View File

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