You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

HarmonyImportDependencyParserPlugin.js 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { SyncBailHook } = require("tapable");
  7. const HarmonyImportSideEffectDependency = require("./HarmonyImportSideEffectDependency");
  8. const HarmonyImportSpecifierDependency = require("./HarmonyImportSpecifierDependency");
  9. const HarmonyAcceptImportDependency = require("./HarmonyAcceptImportDependency");
  10. const HarmonyAcceptDependency = require("./HarmonyAcceptDependency");
  11. const ConstDependency = require("./ConstDependency");
  12. module.exports = class HarmonyImportDependencyParserPlugin {
  13. constructor(moduleOptions) {
  14. this.strictExportPresence = moduleOptions.strictExportPresence;
  15. this.strictThisContextOnImports = moduleOptions.strictThisContextOnImports;
  16. }
  17. apply(parser) {
  18. parser.hooks.import.tap(
  19. "HarmonyImportDependencyParserPlugin",
  20. (statement, source) => {
  21. parser.state.lastHarmonyImportOrder =
  22. (parser.state.lastHarmonyImportOrder || 0) + 1;
  23. const clearDep = new ConstDependency("", statement.range);
  24. clearDep.loc = statement.loc;
  25. parser.state.module.addDependency(clearDep);
  26. const sideEffectDep = new HarmonyImportSideEffectDependency(
  27. source,
  28. parser.state.module,
  29. parser.state.lastHarmonyImportOrder,
  30. parser.state.harmonyParserScope
  31. );
  32. sideEffectDep.loc = statement.loc;
  33. parser.state.module.addDependency(sideEffectDep);
  34. return true;
  35. }
  36. );
  37. parser.hooks.importSpecifier.tap(
  38. "HarmonyImportDependencyParserPlugin",
  39. (statement, source, id, name) => {
  40. parser.scope.definitions.delete(name);
  41. parser.scope.renames.set(name, "imported var");
  42. if (!parser.state.harmonySpecifier) {
  43. parser.state.harmonySpecifier = new Map();
  44. }
  45. parser.state.harmonySpecifier.set(name, {
  46. source,
  47. id,
  48. sourceOrder: parser.state.lastHarmonyImportOrder
  49. });
  50. return true;
  51. }
  52. );
  53. parser.hooks.expression
  54. .for("imported var")
  55. .tap("HarmonyImportDependencyParserPlugin", expr => {
  56. const name = expr.name;
  57. const settings = parser.state.harmonySpecifier.get(name);
  58. const dep = new HarmonyImportSpecifierDependency(
  59. settings.source,
  60. parser.state.module,
  61. settings.sourceOrder,
  62. parser.state.harmonyParserScope,
  63. settings.id,
  64. name,
  65. expr.range,
  66. this.strictExportPresence
  67. );
  68. dep.shorthand = parser.scope.inShorthand;
  69. dep.directImport = true;
  70. dep.loc = expr.loc;
  71. parser.state.module.addDependency(dep);
  72. return true;
  73. });
  74. parser.hooks.expressionAnyMember
  75. .for("imported var")
  76. .tap("HarmonyImportDependencyParserPlugin", expr => {
  77. const name = expr.object.name;
  78. const settings = parser.state.harmonySpecifier.get(name);
  79. if (settings.id !== null) return false;
  80. const dep = new HarmonyImportSpecifierDependency(
  81. settings.source,
  82. parser.state.module,
  83. settings.sourceOrder,
  84. parser.state.harmonyParserScope,
  85. expr.property.name || expr.property.value,
  86. name,
  87. expr.range,
  88. this.strictExportPresence
  89. );
  90. dep.shorthand = parser.scope.inShorthand;
  91. dep.directImport = false;
  92. dep.loc = expr.loc;
  93. parser.state.module.addDependency(dep);
  94. return true;
  95. });
  96. if (this.strictThisContextOnImports) {
  97. // only in case when we strictly follow the spec we need a special case here
  98. parser.hooks.callAnyMember
  99. .for("imported var")
  100. .tap("HarmonyImportDependencyParserPlugin", expr => {
  101. if (expr.callee.type !== "MemberExpression") return;
  102. if (expr.callee.object.type !== "Identifier") return;
  103. const name = expr.callee.object.name;
  104. const settings = parser.state.harmonySpecifier.get(name);
  105. if (settings.id !== null) return false;
  106. const dep = new HarmonyImportSpecifierDependency(
  107. settings.source,
  108. parser.state.module,
  109. settings.sourceOrder,
  110. parser.state.harmonyParserScope,
  111. expr.callee.property.name || expr.callee.property.value,
  112. name,
  113. expr.callee.range,
  114. this.strictExportPresence
  115. );
  116. dep.shorthand = parser.scope.inShorthand;
  117. dep.directImport = false;
  118. dep.namespaceObjectAsContext = true;
  119. dep.loc = expr.callee.loc;
  120. parser.state.module.addDependency(dep);
  121. if (expr.arguments) parser.walkExpressions(expr.arguments);
  122. return true;
  123. });
  124. }
  125. parser.hooks.call
  126. .for("imported var")
  127. .tap("HarmonyImportDependencyParserPlugin", expr => {
  128. const args = expr.arguments;
  129. const fullExpr = expr;
  130. expr = expr.callee;
  131. if (expr.type !== "Identifier") return;
  132. const name = expr.name;
  133. const settings = parser.state.harmonySpecifier.get(name);
  134. const dep = new HarmonyImportSpecifierDependency(
  135. settings.source,
  136. parser.state.module,
  137. settings.sourceOrder,
  138. parser.state.harmonyParserScope,
  139. settings.id,
  140. name,
  141. expr.range,
  142. this.strictExportPresence
  143. );
  144. dep.directImport = true;
  145. dep.callArgs = args;
  146. dep.call = fullExpr;
  147. dep.loc = expr.loc;
  148. parser.state.module.addDependency(dep);
  149. if (args) parser.walkExpressions(args);
  150. return true;
  151. });
  152. // TODO webpack 5: refactor this, no custom hooks
  153. if (!parser.hooks.hotAcceptCallback) {
  154. parser.hooks.hotAcceptCallback = new SyncBailHook([
  155. "expression",
  156. "requests"
  157. ]);
  158. }
  159. if (!parser.hooks.hotAcceptWithoutCallback) {
  160. parser.hooks.hotAcceptWithoutCallback = new SyncBailHook([
  161. "expression",
  162. "requests"
  163. ]);
  164. }
  165. parser.hooks.hotAcceptCallback.tap(
  166. "HarmonyImportDependencyParserPlugin",
  167. (expr, requests) => {
  168. const harmonyParserScope = parser.state.harmonyParserScope;
  169. if (!harmonyParserScope) {
  170. // This is not a harmony module, skip it
  171. return;
  172. }
  173. const dependencies = requests.map(request => {
  174. const dep = new HarmonyAcceptImportDependency(
  175. request,
  176. parser.state.module,
  177. harmonyParserScope
  178. );
  179. dep.loc = expr.loc;
  180. parser.state.module.addDependency(dep);
  181. return dep;
  182. });
  183. if (dependencies.length > 0) {
  184. const dep = new HarmonyAcceptDependency(
  185. expr.range,
  186. dependencies,
  187. true
  188. );
  189. dep.loc = expr.loc;
  190. parser.state.module.addDependency(dep);
  191. }
  192. }
  193. );
  194. parser.hooks.hotAcceptWithoutCallback.tap(
  195. "HarmonyImportDependencyParserPlugin",
  196. (expr, requests) => {
  197. const dependencies = requests.map(request => {
  198. const dep = new HarmonyAcceptImportDependency(
  199. request,
  200. parser.state.module,
  201. parser.state.harmonyParserScope
  202. );
  203. dep.loc = expr.loc;
  204. parser.state.module.addDependency(dep);
  205. return dep;
  206. });
  207. if (dependencies.length > 0) {
  208. const dep = new HarmonyAcceptDependency(
  209. expr.range,
  210. dependencies,
  211. false
  212. );
  213. dep.loc = expr.loc;
  214. parser.state.module.addDependency(dep);
  215. }
  216. }
  217. );
  218. }
  219. };