CompatibilityPlugin.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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. parser.hooks.call.for("require").tap(
  44. PLUGIN_NAME,
  45. /**
  46. * @param {CallExpression} expr call expression
  47. * @returns {boolean | void} true when need to handle
  48. */
  49. expr => {
  50. // support for browserify style require delegator: "require(o, !0)"
  51. if (expr.arguments.length !== 2) return;
  52. const second = parser.evaluateExpression(expr.arguments[1]);
  53. if (!second.isBoolean()) return;
  54. if (second.asBool() !== true) return;
  55. const dep = new ConstDependency(
  56. "require",
  57. /** @type {Range} */ (expr.callee.range)
  58. );
  59. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  60. if (parser.state.current.dependencies.length > 0) {
  61. const last =
  62. parser.state.current.dependencies[
  63. parser.state.current.dependencies.length - 1
  64. ];
  65. if (
  66. last.critical &&
  67. last.options &&
  68. last.options.request === "." &&
  69. last.userRequest === "." &&
  70. last.options.recursive
  71. )
  72. parser.state.current.dependencies.pop();
  73. }
  74. parser.state.module.addPresentationalDependency(dep);
  75. return true;
  76. }
  77. );
  78. });
  79. /**
  80. * @param {JavascriptParser} parser the parser
  81. * @returns {void}
  82. */
  83. const handler = parser => {
  84. // Handle nested requires
  85. parser.hooks.preStatement.tap(PLUGIN_NAME, statement => {
  86. if (
  87. statement.type === "FunctionDeclaration" &&
  88. statement.id &&
  89. statement.id.name === RuntimeGlobals.require
  90. ) {
  91. const newName = `__nested_webpack_require_${
  92. /** @type {Range} */ (statement.range)[0]
  93. }__`;
  94. parser.tagVariable(
  95. statement.id.name,
  96. nestedWebpackIdentifierTag,
  97. {
  98. name: newName,
  99. declaration: {
  100. updated: false,
  101. loc: statement.id.loc,
  102. range: statement.id.range
  103. }
  104. }
  105. );
  106. return true;
  107. }
  108. });
  109. parser.hooks.pattern
  110. .for(RuntimeGlobals.require)
  111. .tap(PLUGIN_NAME, pattern => {
  112. const newName = `__nested_webpack_require_${
  113. /** @type {Range} */ (pattern.range)[0]
  114. }__`;
  115. parser.tagVariable(pattern.name, nestedWebpackIdentifierTag, {
  116. name: newName,
  117. declaration: {
  118. updated: false,
  119. loc: pattern.loc,
  120. range: pattern.range
  121. }
  122. });
  123. return true;
  124. });
  125. parser.hooks.pattern
  126. .for(RuntimeGlobals.exports)
  127. .tap(PLUGIN_NAME, pattern => {
  128. parser.tagVariable(pattern.name, nestedWebpackIdentifierTag, {
  129. name: "__nested_webpack_exports__",
  130. declaration: {
  131. updated: false,
  132. loc: pattern.loc,
  133. range: pattern.range
  134. }
  135. });
  136. return true;
  137. });
  138. parser.hooks.expression
  139. .for(nestedWebpackIdentifierTag)
  140. .tap(PLUGIN_NAME, expr => {
  141. const { name, declaration } =
  142. /** @type {TagData} */
  143. (parser.currentTagData);
  144. if (!declaration.updated) {
  145. const dep = new ConstDependency(name, declaration.range);
  146. dep.loc = declaration.loc;
  147. parser.state.module.addPresentationalDependency(dep);
  148. declaration.updated = true;
  149. }
  150. const dep = new ConstDependency(
  151. name,
  152. /** @type {Range} */ (expr.range)
  153. );
  154. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  155. parser.state.module.addPresentationalDependency(dep);
  156. return true;
  157. });
  158. // Handle hashbang
  159. parser.hooks.program.tap(PLUGIN_NAME, (program, comments) => {
  160. if (comments.length === 0) return;
  161. const c = comments[0];
  162. if (c.type === "Line" && /** @type {Range} */ (c.range)[0] === 0) {
  163. if (parser.state.source.slice(0, 2).toString() !== "#!") return;
  164. // this is a hashbang comment
  165. const dep = new ConstDependency("//", 0);
  166. dep.loc = /** @type {DependencyLocation} */ (c.loc);
  167. parser.state.module.addPresentationalDependency(dep);
  168. }
  169. });
  170. };
  171. normalModuleFactory.hooks.parser
  172. .for(JAVASCRIPT_MODULE_TYPE_AUTO)
  173. .tap(PLUGIN_NAME, handler);
  174. normalModuleFactory.hooks.parser
  175. .for(JAVASCRIPT_MODULE_TYPE_DYNAMIC)
  176. .tap(PLUGIN_NAME, handler);
  177. normalModuleFactory.hooks.parser
  178. .for(JAVASCRIPT_MODULE_TYPE_ESM)
  179. .tap(PLUGIN_NAME, handler);
  180. }
  181. );
  182. }
  183. }
  184. module.exports = CompatibilityPlugin;