CompatibilityPlugin.js 5.7 KB

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