diff options
Diffstat (limited to 'node_modules/webpack/lib/ConstPlugin.js')
-rw-r--r-- | node_modules/webpack/lib/ConstPlugin.js | 318 |
1 files changed, 258 insertions, 60 deletions
diff --git a/node_modules/webpack/lib/ConstPlugin.js b/node_modules/webpack/lib/ConstPlugin.js index 8534a6fe8..03b279b64 100644 --- a/node_modules/webpack/lib/ConstPlugin.js +++ b/node_modules/webpack/lib/ConstPlugin.js @@ -1,60 +1,258 @@ -/*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
-*/
-"use strict";
-const ConstDependency = require("./dependencies/ConstDependency");
-const NullFactory = require("./NullFactory");
-const ParserHelpers = require("./ParserHelpers");
-
-const getQuery = (request) => {
- const i = request.indexOf("?");
- return request.indexOf("?") < 0 ? "" : request.substr(i);
-};
-
-class ConstPlugin {
- apply(compiler) {
- compiler.plugin("compilation", (compilation, params) => {
- compilation.dependencyFactories.set(ConstDependency, new NullFactory());
- compilation.dependencyTemplates.set(ConstDependency, new ConstDependency.Template());
-
- params.normalModuleFactory.plugin("parser", parser => {
- parser.plugin("statement if", function(statement) {
- const param = this.evaluateExpression(statement.test);
- const bool = param.asBool();
- if(typeof bool === "boolean") {
- if(statement.test.type !== "Literal") {
- const dep = new ConstDependency(`${bool}`, param.range);
- dep.loc = statement.loc;
- this.state.current.addDependency(dep);
- }
- return bool;
- }
- });
- parser.plugin("expression ?:", function(expression) {
- const param = this.evaluateExpression(expression.test);
- const bool = param.asBool();
- if(typeof bool === "boolean") {
- if(expression.test.type !== "Literal") {
- const dep = new ConstDependency(` ${bool}`, param.range);
- dep.loc = expression.loc;
- this.state.current.addDependency(dep);
- }
- return bool;
- }
- });
- parser.plugin("evaluate Identifier __resourceQuery", function(expr) {
- if(!this.state.module) return;
- return ParserHelpers.evaluateToString(getQuery(this.state.module.resource))(expr);
- });
- parser.plugin("expression __resourceQuery", function() {
- if(!this.state.module) return;
- this.state.current.addVariable("__resourceQuery", JSON.stringify(getQuery(this.state.module.resource)));
- return true;
- });
- });
- });
- }
-}
-
-module.exports = ConstPlugin;
+/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +"use strict"; +const ConstDependency = require("./dependencies/ConstDependency"); +const NullFactory = require("./NullFactory"); +const ParserHelpers = require("./ParserHelpers"); + +const getQuery = request => { + const i = request.indexOf("?"); + return i !== -1 ? request.substr(i) : ""; +}; + +const collectDeclaration = (declarations, pattern) => { + const stack = [pattern]; + while (stack.length > 0) { + const node = stack.pop(); + switch (node.type) { + case "Identifier": + declarations.add(node.name); + break; + case "ArrayPattern": + for (const element of node.elements) { + if (element) { + stack.push(element); + } + } + break; + case "AssignmentPattern": + stack.push(node.left); + break; + case "ObjectPattern": + for (const property of node.properties) { + stack.push(property.value); + } + break; + case "RestElement": + stack.push(node.argument); + break; + } + } +}; + +const getHoistedDeclarations = (branch, includeFunctionDeclarations) => { + const declarations = new Set(); + const stack = [branch]; + while (stack.length > 0) { + const node = stack.pop(); + // Some node could be `null` or `undefined`. + if (!node) continue; + switch (node.type) { + // Walk through control statements to look for hoisted declarations. + // Some branches are skipped since they do not allow declarations. + case "BlockStatement": + for (const stmt of node.body) { + stack.push(stmt); + } + break; + case "IfStatement": + stack.push(node.consequent); + stack.push(node.alternate); + break; + case "ForStatement": + stack.push(node.init); + stack.push(node.body); + break; + case "ForInStatement": + case "ForOfStatement": + stack.push(node.left); + stack.push(node.body); + break; + case "DoWhileStatement": + case "WhileStatement": + case "LabeledStatement": + stack.push(node.body); + break; + case "SwitchStatement": + for (const cs of node.cases) { + for (const consequent of cs.consequent) { + stack.push(consequent); + } + } + break; + case "TryStatement": + stack.push(node.block); + if (node.handler) { + stack.push(node.handler.body); + } + stack.push(node.finalizer); + break; + case "FunctionDeclaration": + if (includeFunctionDeclarations) { + collectDeclaration(declarations, node.id); + } + break; + case "VariableDeclaration": + if (node.kind === "var") { + for (const decl of node.declarations) { + collectDeclaration(declarations, decl.id); + } + } + break; + } + } + return Array.from(declarations); +}; + +class ConstPlugin { + apply(compiler) { + compiler.hooks.compilation.tap( + "ConstPlugin", + (compilation, { normalModuleFactory }) => { + compilation.dependencyFactories.set(ConstDependency, new NullFactory()); + compilation.dependencyTemplates.set( + ConstDependency, + new ConstDependency.Template() + ); + + const handler = parser => { + parser.hooks.statementIf.tap("ConstPlugin", statement => { + const param = parser.evaluateExpression(statement.test); + const bool = param.asBool(); + if (typeof bool === "boolean") { + if (statement.test.type !== "Literal") { + const dep = new ConstDependency(`${bool}`, param.range); + dep.loc = statement.loc; + parser.state.current.addDependency(dep); + } + const branchToRemove = bool + ? statement.alternate + : statement.consequent; + if (branchToRemove) { + // Before removing the dead branch, the hoisted declarations + // must be collected. + // + // Given the following code: + // + // if (true) f() else g() + // if (false) { + // function f() {} + // const g = function g() {} + // if (someTest) { + // let a = 1 + // var x, {y, z} = obj + // } + // } else { + // … + // } + // + // the generated code is: + // + // if (true) f() else {} + // if (false) { + // var f, x, y, z; (in loose mode) + // var x, y, z; (in strict mode) + // } else { + // … + // } + // + // NOTE: When code runs in strict mode, `var` declarations + // are hoisted but `function` declarations don't. + // + let declarations; + if (parser.scope.isStrict) { + // If the code runs in strict mode, variable declarations + // using `var` must be hoisted. + declarations = getHoistedDeclarations(branchToRemove, false); + } else { + // Otherwise, collect all hoisted declaration. + declarations = getHoistedDeclarations(branchToRemove, true); + } + let replacement; + if (declarations.length > 0) { + replacement = `{ var ${declarations.join(", ")}; }`; + } else { + replacement = "{}"; + } + const dep = new ConstDependency( + replacement, + branchToRemove.range + ); + dep.loc = branchToRemove.loc; + parser.state.current.addDependency(dep); + } + return bool; + } + }); + parser.hooks.expressionConditionalOperator.tap( + "ConstPlugin", + expression => { + const param = parser.evaluateExpression(expression.test); + const bool = param.asBool(); + if (typeof bool === "boolean") { + if (expression.test.type !== "Literal") { + const dep = new ConstDependency(` ${bool}`, param.range); + dep.loc = expression.loc; + parser.state.current.addDependency(dep); + } + // Expressions do not hoist. + // It is safe to remove the dead branch. + // + // Given the following code: + // + // false ? someExpression() : otherExpression(); + // + // the generated code is: + // + // false ? undefined : otherExpression(); + // + const branchToRemove = bool + ? expression.alternate + : expression.consequent; + const dep = new ConstDependency( + "undefined", + branchToRemove.range + ); + dep.loc = branchToRemove.loc; + parser.state.current.addDependency(dep); + return bool; + } + } + ); + parser.hooks.evaluateIdentifier + .for("__resourceQuery") + .tap("ConstPlugin", expr => { + if (!parser.state.module) return; + return ParserHelpers.evaluateToString( + getQuery(parser.state.module.resource) + )(expr); + }); + parser.hooks.expression + .for("__resourceQuery") + .tap("ConstPlugin", () => { + if (!parser.state.module) return; + parser.state.current.addVariable( + "__resourceQuery", + JSON.stringify(getQuery(parser.state.module.resource)) + ); + return true; + }); + }; + + normalModuleFactory.hooks.parser + .for("javascript/auto") + .tap("ConstPlugin", handler); + normalModuleFactory.hooks.parser + .for("javascript/dynamic") + .tap("ConstPlugin", handler); + normalModuleFactory.hooks.parser + .for("javascript/esm") + .tap("ConstPlugin", handler); + } + ); + } +} + +module.exports = ConstPlugin; |