CompatibilityPlugin.js 4.5 KB

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