HarmonyEvaluatedImportSpecifierDependency.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Ivan Kopeykin @vankop
  4. */
  5. "use strict";
  6. const { InlinedUsedName } = require("../optimize/InlineExports");
  7. const {
  8. getDependencyUsedByExportsCondition
  9. } = require("../optimize/InnerGraph");
  10. const makeSerializable = require("../util/makeSerializable");
  11. const { ExportPresenceModes } = require("./HarmonyImportDependency");
  12. const HarmonyImportSpecifierDependency = require("./HarmonyImportSpecifierDependency");
  13. const { ImportPhase } = require("./ImportPhase");
  14. /** @import { ReplaceSource } from "webpack-sources" */
  15. /** @import Dependency, { GetConditionFn } from "../Dependency" */
  16. /** @import { DependencyTemplateContext } from "../DependencyTemplate" */
  17. /** @import { BuildMeta } from "../Module" */
  18. /** @import ModuleGraph from "../ModuleGraph" */
  19. /** @import ModuleGraphConnection from "../ModuleGraphConnection" */
  20. /** @import { ImportAttributes, Range } from "../javascript/JavascriptParser" */
  21. /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<string[]>} ObjectDeserializerContext */
  22. /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<string[]>} ObjectSerializerContext */
  23. /** @import { Ids } from "./HarmonyImportDependency" */
  24. /**
  25. * Dependency for static evaluating import specifier. e.g.
  26. * @example
  27. * import a from "a";
  28. * "x" in a;
  29. * a.x !== undefined; // if x value statically analyzable
  30. */
  31. class HarmonyEvaluatedImportSpecifierDependency extends HarmonyImportSpecifierDependency {
  32. // read by `HarmonyImportGuard` in place of `instanceof`, which would cycle
  33. get isHarmonyEvaluatedImportSpecifier() {
  34. return true;
  35. }
  36. /**
  37. * Creates an instance of HarmonyEvaluatedImportSpecifierDependency.
  38. * @param {string} request the request string
  39. * @param {number} sourceOrder source order
  40. * @param {Ids} ids ids
  41. * @param {string} name name
  42. * @param {Range} range location in source code
  43. * @param {ImportAttributes | undefined} attributes import assertions
  44. * @param {string} operator operator
  45. */
  46. constructor(request, sourceOrder, ids, name, range, attributes, operator) {
  47. super(
  48. request,
  49. sourceOrder,
  50. ids,
  51. name,
  52. range,
  53. ExportPresenceModes.NONE,
  54. ImportPhase.Evaluation,
  55. attributes,
  56. []
  57. );
  58. /** @type {string} */
  59. this.operator = operator;
  60. }
  61. get type() {
  62. return `evaluated X ${this.operator} harmony import specifier`;
  63. }
  64. /**
  65. * Returns function to determine if the connection is active.
  66. * @param {ModuleGraph} moduleGraph module graph
  67. * @returns {null | false | GetConditionFn} function to determine if the connection is active
  68. */
  69. getCondition(moduleGraph) {
  70. // Existence check is independent of inlining; skip the base class's
  71. // inline-sensitivity, which would wrongly deactivate the connection.
  72. return getDependencyUsedByExportsCondition(this, moduleGraph);
  73. }
  74. /**
  75. * Serializes this instance into the provided serializer context.
  76. * @param {ObjectSerializerContext} context context
  77. */
  78. serialize(context) {
  79. super.serialize(context);
  80. const { write } = context;
  81. write(this.operator);
  82. }
  83. /**
  84. * Restores this instance from the provided deserializer context.
  85. * @param {ObjectDeserializerContext} context context
  86. */
  87. deserialize(context) {
  88. super.deserialize(context);
  89. const { read } = context;
  90. this.operator = read();
  91. }
  92. }
  93. makeSerializable(
  94. HarmonyEvaluatedImportSpecifierDependency,
  95. "webpack/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency"
  96. );
  97. HarmonyEvaluatedImportSpecifierDependency.Template = class HarmonyEvaluatedImportSpecifierDependencyTemplate extends (
  98. HarmonyImportSpecifierDependency.Template
  99. ) {
  100. /**
  101. * Applies the plugin by registering its hooks on the compiler.
  102. * @param {Dependency} dependency the dependency for which the template should be applied
  103. * @param {ReplaceSource} source the current replace source which can be modified
  104. * @param {DependencyTemplateContext} templateContext the context object
  105. * @returns {void}
  106. */
  107. apply(dependency, source, templateContext) {
  108. const dep =
  109. /** @type {HarmonyEvaluatedImportSpecifierDependency} */
  110. (dependency);
  111. const { module, moduleGraph, runtime } = templateContext;
  112. const connection = moduleGraph.getConnection(dep);
  113. // Skip rendering depending when dependency is conditional
  114. if (connection && !connection.isTargetActive(runtime)) return;
  115. const exportsInfo = moduleGraph.getExportsInfo(
  116. /** @type {ModuleGraphConnection} */ (connection).module
  117. );
  118. const ids = dep.getIds(moduleGraph);
  119. /** @type {boolean | undefined | null} */
  120. let value;
  121. const exportsType =
  122. /** @type {ModuleGraphConnection} */
  123. (connection).module.getExportsType(
  124. moduleGraph,
  125. /** @type {BuildMeta} */
  126. (module.buildMeta).strictHarmonyModule
  127. );
  128. switch (exportsType) {
  129. case "default-with-named": {
  130. if (ids[0] === "default") {
  131. value =
  132. ids.length === 1 || exportsInfo.isExportProvided(ids.slice(1));
  133. } else {
  134. value = exportsInfo.isExportProvided(ids);
  135. }
  136. break;
  137. }
  138. case "namespace": {
  139. value =
  140. ids[0] === "__esModule"
  141. ? ids.length === 1 || undefined
  142. : exportsInfo.isExportProvided(ids);
  143. break;
  144. }
  145. case "dynamic": {
  146. if (ids[0] !== "default") {
  147. value = exportsInfo.isExportProvided(ids);
  148. }
  149. break;
  150. }
  151. // default-only could lead to runtime error, when default value is primitive
  152. }
  153. if (typeof value === "boolean") {
  154. source.replace(dep.range[0], dep.range[1] - 1, ` ${value}`);
  155. } else {
  156. const usedName = exportsInfo.getUsedName(ids, runtime);
  157. if (usedName instanceof InlinedUsedName) {
  158. // Inlined exports only work with ESM export dependency
  159. // which only existed in modules with `namespace` exportsType.
  160. throw new Error(
  161. "Evaluated import specifier dependency has inlined export name: This should not happen"
  162. );
  163. }
  164. const code = this._getCodeForIds(
  165. dep,
  166. source,
  167. templateContext,
  168. ids.slice(0, -1),
  169. connection
  170. );
  171. source.replace(
  172. dep.range[0],
  173. dep.range[1] - 1,
  174. `${
  175. usedName ? JSON.stringify(usedName[usedName.length - 1]) : '""'
  176. } in ${code}`
  177. );
  178. }
  179. }
  180. };
  181. module.exports = HarmonyEvaluatedImportSpecifierDependency;