HarmonyExportSpecifierDependency.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const Dependency = require("../Dependency");
  7. const { InlinedUsedName } = require("../optimize/InlineExports");
  8. const makeSerializable = require("../util/makeSerializable");
  9. const memoize = require("../util/memoize");
  10. const ExportBindingInitFragment = require("./ExportBindingInitFragment");
  11. const HarmonyExportInitFragment = require("./HarmonyExportInitFragment");
  12. const NullDependency = require("./NullDependency");
  13. /** @import { ReplaceSource } from "webpack-sources" */
  14. /** @import { ExportsSpec, LazyUntil } from "../Dependency" */
  15. /** @import { DependencyTemplateContext } from "../DependencyTemplate" */
  16. /** @import ModuleGraph from "../ModuleGraph" */
  17. /** @import { ConnectionState } from "../ModuleGraphConnection" */
  18. /** @import { InlinedValue } from "../optimize/InlineExports" */
  19. /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[string, string, InlinedValue | null | undefined]>} ObjectDeserializerContext */
  20. /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[string, string, InlinedValue | null | undefined]>} ObjectSerializerContext */
  21. /** @import { UnusedExports, ExportMap } from "./HarmonyExportInitFragment" */
  22. /**
  23. * @import {
  24. * JavascriptModuleBuildInfo
  25. * } from "../javascript/JavascriptModule"
  26. */
  27. // Required here rather than at the top: the plugin reaches back into the dependencies.
  28. const getJavascriptModulesPlugin = memoize(() =>
  29. require("../javascript/JavascriptModulesPlugin")
  30. );
  31. class HarmonyExportSpecifierDependency extends NullDependency {
  32. /**
  33. * Creates an instance of HarmonyExportSpecifierDependency.
  34. * @param {string} id the id
  35. * @param {string} name the name
  36. * @param {InlinedValue | null=} constValue undefined = not const, null = const but not inline-eligible, InlinedValue = const and inline-eligible
  37. */
  38. constructor(id, name, constValue) {
  39. super();
  40. /** @type {string} */
  41. this.id = id;
  42. /** @type {string} */
  43. this.name = name;
  44. /** @type {InlinedValue | null | undefined} */
  45. this.constValue = constValue;
  46. }
  47. get type() {
  48. return "harmony export specifier";
  49. }
  50. /**
  51. * Returns how this dependency may be deferred when its parent module is side-effect-free (lazy barrel optimization).
  52. * @returns {LazyUntil | null} lazy classification, null when it must be processed eagerly
  53. */
  54. getLazyUntil() {
  55. return Dependency.LAZY_UNTIL_LOCAL;
  56. }
  57. /**
  58. * Returns the export name for a `LAZY_UNTIL_LOCAL`/`LAZY_UNTIL_ID` classification (lazy barrel optimization).
  59. * @returns {string | null} export name, null when not applicable
  60. */
  61. getLazyName() {
  62. return this.name;
  63. }
  64. /**
  65. * Returns the exported names
  66. * @param {ModuleGraph} moduleGraph module graph
  67. * @returns {ExportsSpec | undefined} export names
  68. */
  69. getExports(moduleGraph) {
  70. const module = moduleGraph.getParentModule(this);
  71. const pureFunctions =
  72. module &&
  73. module.buildInfo &&
  74. /** @type {JavascriptModuleBuildInfo} */ (module.buildInfo).pureFunctions;
  75. const isPure = Boolean(pureFunctions && pureFunctions.has(this.id));
  76. return {
  77. exports: [
  78. {
  79. name: this.name,
  80. isPure: isPure || undefined,
  81. inlined: this.constValue || undefined
  82. }
  83. ],
  84. priority: 1,
  85. terminalBinding: true,
  86. dependencies: undefined
  87. };
  88. }
  89. /**
  90. * Gets module evaluation side effects state.
  91. * @param {ModuleGraph} moduleGraph the module graph
  92. * @returns {ConnectionState} how this dependency connects the module to referencing modules
  93. */
  94. getModuleEvaluationSideEffectsState(moduleGraph) {
  95. return false;
  96. }
  97. /**
  98. * Serializes this instance into the provided serializer context.
  99. * @param {ObjectSerializerContext} context context
  100. */
  101. serialize(context) {
  102. context.write(this.id).write(this.name).write(this.constValue);
  103. super.serialize(context);
  104. }
  105. /**
  106. * Restores this instance from the provided deserializer context.
  107. * @param {ObjectDeserializerContext} context context
  108. */
  109. deserialize(context) {
  110. this.id = context.read();
  111. const c1 = context.rest;
  112. this.name = c1.read();
  113. const c2 = c1.rest;
  114. this.constValue = c2.read();
  115. super.deserialize(c2.rest);
  116. }
  117. }
  118. makeSerializable(
  119. HarmonyExportSpecifierDependency,
  120. "webpack/lib/dependencies/HarmonyExportSpecifierDependency"
  121. );
  122. HarmonyExportSpecifierDependency.Template = class HarmonyExportSpecifierDependencyTemplate extends (
  123. NullDependency.Template
  124. ) {
  125. /**
  126. * Applies the plugin by registering its hooks on the compiler.
  127. * @param {Dependency} dependency the dependency for which the template should be applied
  128. * @param {ReplaceSource} source the current replace source which can be modified
  129. * @param {DependencyTemplateContext} templateContext the context object
  130. * @returns {void}
  131. */
  132. apply(
  133. dependency,
  134. source,
  135. {
  136. module,
  137. moduleGraph,
  138. initFragments,
  139. runtime,
  140. runtimeTemplate,
  141. concatenationScope
  142. }
  143. ) {
  144. const dep = /** @type {HarmonyExportSpecifierDependency} */ (dependency);
  145. // a wrapped module's bindings stay inside the wrapper, not in the shared scope
  146. if (concatenationScope && !concatenationScope.isWrapped()) {
  147. concatenationScope.registerExport(dep.name, dep.id);
  148. return;
  149. }
  150. const used = moduleGraph
  151. .getExportsInfo(module)
  152. .getUsedName(dep.name, runtime);
  153. if (!used) {
  154. /** @type {UnusedExports} */
  155. const set = new Set();
  156. set.add(dep.name || "namespace");
  157. initFragments.push(
  158. new HarmonyExportInitFragment(module.exportsArgument, undefined, set)
  159. );
  160. return;
  161. }
  162. if (used instanceof InlinedUsedName) {
  163. // Inlined: importing side substitutes the literal directly, skip export init
  164. return;
  165. }
  166. const canUseValueBinding =
  167. dep.constValue !== undefined &&
  168. /** @type {import("../Module").BuildInfo} */ (module.buildInfo)
  169. .isCircular === false;
  170. // A library exporting the same bindings natively reads none of them back off the
  171. // exports object, so it takes the call over and emits it only where wrapped.
  172. const hooks = getJavascriptModulesPlugin().getCompilationHooks(
  173. runtimeTemplate.compilation
  174. );
  175. const onDemand = hooks.onDemandExportsGeneration.isUsed()
  176. ? (/** @type {string} */ definition, /** @type {boolean} */ placeAtEnd) =>
  177. hooks.onDemandExportsGeneration.call(
  178. module,
  179. runtime,
  180. definition,
  181. placeAtEnd
  182. ) === true
  183. : undefined;
  184. if (canUseValueBinding) {
  185. initFragments.push(
  186. new ExportBindingInitFragment(
  187. module.exportsArgument,
  188. [
  189. {
  190. name: used,
  191. value: `/* binding */ ${dep.id}`,
  192. bindingType: "value"
  193. }
  194. ],
  195. true,
  196. onDemand
  197. )
  198. );
  199. } else {
  200. /** @type {ExportMap} */
  201. const map = new Map();
  202. map.set(used, `/* binding */ ${dep.id}`);
  203. initFragments.push(
  204. new HarmonyExportInitFragment(
  205. module.exportsArgument,
  206. map,
  207. undefined,
  208. onDemand
  209. )
  210. );
  211. }
  212. }
  213. };
  214. module.exports = HarmonyExportSpecifierDependency;