CompatibilityPlugin.js 5.0 KB

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