HarmonyAcceptDependency.js 6.9 KB

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