HarmonyAcceptDependency.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const Template = require("../Template");
  7. const AwaitDependenciesInitFragment = require("../async-modules/AwaitDependenciesInitFragment");
  8. const makeSerializable = require("../util/makeSerializable");
  9. const HarmonyImportDependency = require("./HarmonyImportDependency");
  10. const { ImportPhaseUtils } = require("./ImportPhase");
  11. const NullDependency = require("./NullDependency");
  12. /** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
  13. /** @typedef {import("../Dependency")} Dependency */
  14. /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
  15. /** @typedef {import("../javascript/JavascriptParser").Range} Range */
  16. /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
  17. /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
  18. /** @typedef {import("./HarmonyAcceptImportDependency")} HarmonyAcceptImportDependency */
  19. /** @typedef {import("../Module")} Module */
  20. /** @typedef {import("../Module").ModuleId} ModuleId */
  21. class HarmonyAcceptDependency extends NullDependency {
  22. /**
  23. * @param {Range} range expression range
  24. * @param {HarmonyAcceptImportDependency[]} dependencies import dependencies
  25. * @param {boolean} hasCallback true, if the range wraps an existing callback
  26. */
  27. constructor(range, dependencies, hasCallback) {
  28. super();
  29. this.range = range;
  30. this.dependencies = dependencies;
  31. this.hasCallback = hasCallback;
  32. }
  33. get type() {
  34. return "accepted harmony modules";
  35. }
  36. /**
  37. * @param {ObjectSerializerContext} context context
  38. */
  39. serialize(context) {
  40. const { write } = context;
  41. write(this.range);
  42. write(this.dependencies);
  43. write(this.hasCallback);
  44. super.serialize(context);
  45. }
  46. /**
  47. * @param {ObjectDeserializerContext} context context
  48. */
  49. deserialize(context) {
  50. const { read } = context;
  51. this.range = read();
  52. this.dependencies = read();
  53. this.hasCallback = read();
  54. super.deserialize(context);
  55. }
  56. }
  57. makeSerializable(
  58. HarmonyAcceptDependency,
  59. "webpack/lib/dependencies/HarmonyAcceptDependency"
  60. );
  61. HarmonyAcceptDependency.Template = class HarmonyAcceptDependencyTemplate extends (
  62. NullDependency.Template
  63. ) {
  64. /**
  65. * @param {Dependency} dependency the dependency for which the template should be applied
  66. * @param {ReplaceSource} source the current replace source which can be modified
  67. * @param {DependencyTemplateContext} templateContext the context object
  68. * @returns {void}
  69. */
  70. apply(dependency, source, templateContext) {
  71. const dep = /** @type {HarmonyAcceptDependency} */ (dependency);
  72. const {
  73. module,
  74. runtime,
  75. runtimeRequirements,
  76. runtimeTemplate,
  77. moduleGraph,
  78. chunkGraph
  79. } = templateContext;
  80. /**
  81. * @param {Dependency} dependency the dependency to get module id for
  82. * @returns {ModuleId | null} the module id or null if not found
  83. */
  84. const getDependencyModuleId = (dependency) =>
  85. chunkGraph.getModuleId(
  86. /** @type {Module} */ (moduleGraph.getModule(dependency))
  87. );
  88. /**
  89. * @param {Dependency} a the first dependency
  90. * @param {Dependency} b the second dependency
  91. * @returns {boolean} true if the dependencies are related
  92. */
  93. const isRelatedHarmonyImportDependency = (a, b) =>
  94. a !== b &&
  95. b instanceof HarmonyImportDependency &&
  96. getDependencyModuleId(a) === getDependencyModuleId(b);
  97. /**
  98. * HarmonyAcceptImportDependency lacks a lot of information, such as the defer property.
  99. * One HarmonyAcceptImportDependency may need to generate multiple ImportStatements.
  100. * Therefore, we find its original HarmonyImportDependency for code generation.
  101. * @param {HarmonyAcceptImportDependency} dependency the dependency to get harmony import dependencies for
  102. * @returns {HarmonyImportDependency[]} array of related harmony import dependencies
  103. */
  104. const getHarmonyImportDependencies = (dependency) => {
  105. /** @type {HarmonyImportDependency[]} */
  106. const result = [];
  107. /** @type {HarmonyImportDependency | null} */
  108. let deferDependency = null;
  109. /** @type {HarmonyImportDependency | null} */
  110. let noDeferredDependency = null;
  111. for (const d of module.dependencies) {
  112. if (deferDependency && noDeferredDependency) break;
  113. if (isRelatedHarmonyImportDependency(dependency, d)) {
  114. if (
  115. ImportPhaseUtils.isDefer(
  116. /** @type {HarmonyImportDependency} */ (d).phase
  117. )
  118. ) {
  119. deferDependency = /** @type {HarmonyImportDependency} */ (d);
  120. } else {
  121. noDeferredDependency = /** @type {HarmonyImportDependency} */ (d);
  122. }
  123. }
  124. }
  125. if (deferDependency) result.push(deferDependency);
  126. if (noDeferredDependency) result.push(noDeferredDependency);
  127. if (result.length === 0) {
  128. // fallback to the original dependency
  129. result.push(dependency);
  130. }
  131. return result;
  132. };
  133. /** @type {HarmonyImportDependency[]} */
  134. const syncDeps = [];
  135. /** @type {HarmonyAcceptImportDependency[]} */
  136. const asyncDeps = [];
  137. for (const dependency of dep.dependencies) {
  138. const connection = moduleGraph.getConnection(dependency);
  139. if (connection && moduleGraph.isAsync(connection.module)) {
  140. asyncDeps.push(dependency);
  141. } else {
  142. syncDeps.push(...getHarmonyImportDependencies(dependency));
  143. }
  144. }
  145. let content = syncDeps
  146. .map((dependency) => {
  147. const referencedModule = moduleGraph.getModule(dependency);
  148. return {
  149. dependency,
  150. runtimeCondition: referencedModule
  151. ? HarmonyImportDependency.Template.getImportEmittedRuntime(
  152. module,
  153. referencedModule
  154. )
  155. : false
  156. };
  157. })
  158. .filter(({ runtimeCondition }) => runtimeCondition !== false)
  159. .map(({ dependency, runtimeCondition }) => {
  160. const condition = runtimeTemplate.runtimeConditionExpression({
  161. chunkGraph,
  162. runtime,
  163. runtimeCondition,
  164. runtimeRequirements
  165. });
  166. const s = dependency.getImportStatement(true, templateContext);
  167. const code = s[0] + s[1];
  168. if (condition !== "true") {
  169. return `if (${condition}) {\n${Template.indent(code)}\n}\n`;
  170. }
  171. return code;
  172. })
  173. .join("");
  174. const promises = new Map(
  175. asyncDeps.map((dependency) => [
  176. dependency.getImportVar(moduleGraph),
  177. dependency.getModuleExports(templateContext)
  178. ])
  179. );
  180. let optAsync = "";
  181. if (promises.size !== 0) {
  182. optAsync = "async ";
  183. content += new AwaitDependenciesInitFragment(promises).getContent({
  184. ...templateContext,
  185. type: "javascript"
  186. });
  187. }
  188. if (dep.hasCallback) {
  189. if (runtimeTemplate.supportsArrowFunction()) {
  190. source.insert(
  191. dep.range[0],
  192. `${optAsync}__WEBPACK_OUTDATED_DEPENDENCIES__ => { ${content} return (`
  193. );
  194. source.insert(dep.range[1], ")(__WEBPACK_OUTDATED_DEPENDENCIES__); }");
  195. } else {
  196. source.insert(
  197. dep.range[0],
  198. `${optAsync}function(__WEBPACK_OUTDATED_DEPENDENCIES__) { ${content} return (`
  199. );
  200. source.insert(
  201. dep.range[1],
  202. ")(__WEBPACK_OUTDATED_DEPENDENCIES__); }.bind(this)"
  203. );
  204. }
  205. return;
  206. }
  207. const arrow = runtimeTemplate.supportsArrowFunction();
  208. source.insert(
  209. dep.range[1] - 0.5,
  210. `, ${arrow ? `${optAsync}() =>` : `${optAsync}function()`} { ${content} }`
  211. );
  212. }
  213. };
  214. module.exports = HarmonyAcceptDependency;