CompatibilityPlugin.js (4645B)
1 /* 2 MIT License http://www.opensource.org/licenses/mit-license.php 3 Author Tobias Koppers @sokra 4 */ 5 6 "use strict"; 7 8 const ConstDependency = require("./dependencies/ConstDependency"); 9 10 /** @typedef {import("./Compiler")} Compiler */ 11 /** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */ 12 13 const nestedWebpackRequireTag = Symbol("nested __webpack_require__"); 14 15 class CompatibilityPlugin { 16 /** 17 * Apply the plugin 18 * @param {Compiler} compiler the compiler instance 19 * @returns {void} 20 */ 21 apply(compiler) { 22 compiler.hooks.compilation.tap( 23 "CompatibilityPlugin", 24 (compilation, { normalModuleFactory }) => { 25 compilation.dependencyTemplates.set( 26 ConstDependency, 27 new ConstDependency.Template() 28 ); 29 30 normalModuleFactory.hooks.parser 31 .for("javascript/auto") 32 .tap("CompatibilityPlugin", (parser, parserOptions) => { 33 if ( 34 parserOptions.browserify !== undefined && 35 !parserOptions.browserify 36 ) 37 return; 38 39 parser.hooks.call 40 .for("require") 41 .tap("CompatibilityPlugin", expr => { 42 // support for browserify style require delegator: "require(o, !0)" 43 if (expr.arguments.length !== 2) return; 44 const second = parser.evaluateExpression(expr.arguments[1]); 45 if (!second.isBoolean()) return; 46 if (second.asBool() !== true) return; 47 const dep = new ConstDependency("require", expr.callee.range); 48 dep.loc = expr.loc; 49 if (parser.state.current.dependencies.length > 0) { 50 const last = 51 parser.state.current.dependencies[ 52 parser.state.current.dependencies.length - 1 53 ]; 54 if ( 55 last.critical && 56 last.options && 57 last.options.request === "." && 58 last.userRequest === "." && 59 last.options.recursive 60 ) 61 parser.state.current.dependencies.pop(); 62 } 63 parser.state.module.addPresentationalDependency(dep); 64 return true; 65 }); 66 }); 67 68 /** 69 * @param {JavascriptParser} parser the parser 70 * @returns {void} 71 */ 72 const handler = parser => { 73 // Handle nested requires 74 parser.hooks.preStatement.tap("CompatibilityPlugin", statement => { 75 if ( 76 statement.type === "FunctionDeclaration" && 77 statement.id && 78 statement.id.name === "__webpack_require__" 79 ) { 80 const newName = `__nested_webpack_require_${statement.range[0]}__`; 81 parser.tagVariable(statement.id.name, nestedWebpackRequireTag, { 82 name: newName, 83 declaration: { 84 updated: false, 85 loc: statement.id.loc, 86 range: statement.id.range 87 } 88 }); 89 return true; 90 } 91 }); 92 parser.hooks.pattern 93 .for("__webpack_require__") 94 .tap("CompatibilityPlugin", pattern => { 95 const newName = `__nested_webpack_require_${pattern.range[0]}__`; 96 parser.tagVariable(pattern.name, nestedWebpackRequireTag, { 97 name: newName, 98 declaration: { 99 updated: false, 100 loc: pattern.loc, 101 range: pattern.range 102 } 103 }); 104 return true; 105 }); 106 parser.hooks.expression 107 .for(nestedWebpackRequireTag) 108 .tap("CompatibilityPlugin", expr => { 109 const { name, declaration } = parser.currentTagData; 110 if (!declaration.updated) { 111 const dep = new ConstDependency(name, declaration.range); 112 dep.loc = declaration.loc; 113 parser.state.module.addPresentationalDependency(dep); 114 declaration.updated = true; 115 } 116 const dep = new ConstDependency(name, expr.range); 117 dep.loc = expr.loc; 118 parser.state.module.addPresentationalDependency(dep); 119 return true; 120 }); 121 122 // Handle hashbang 123 parser.hooks.program.tap( 124 "CompatibilityPlugin", 125 (program, comments) => { 126 if (comments.length === 0) return; 127 const c = comments[0]; 128 if (c.type === "Line" && c.range[0] === 0) { 129 if (parser.state.source.slice(0, 2).toString() !== "#!") return; 130 // this is a hashbang comment 131 const dep = new ConstDependency("//", 0); 132 dep.loc = c.loc; 133 parser.state.module.addPresentationalDependency(dep); 134 } 135 } 136 ); 137 }; 138 139 normalModuleFactory.hooks.parser 140 .for("javascript/auto") 141 .tap("CompatibilityPlugin", handler); 142 normalModuleFactory.hooks.parser 143 .for("javascript/dynamic") 144 .tap("CompatibilityPlugin", handler); 145 normalModuleFactory.hooks.parser 146 .for("javascript/esm") 147 .tap("CompatibilityPlugin", handler); 148 } 149 ); 150 } 151 } 152 module.exports = CompatibilityPlugin;