CompatibilityPlugin.js 5.9 KB

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