HarmonyImportSpecifierDependency.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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 {
  8. getDependencyUsedByExportsCondition
  9. } = require("../optimize/InnerGraph");
  10. const makeSerializable = require("../util/makeSerializable");
  11. const propertyAccess = require("../util/propertyAccess");
  12. const HarmonyImportDependency = require("./HarmonyImportDependency");
  13. /** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
  14. /** @typedef {import("../ChunkGraph")} ChunkGraph */
  15. /** @typedef {import("../Dependency").ExportsSpec} ExportsSpec */
  16. /** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
  17. /** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
  18. /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
  19. /** @typedef {import("../ModuleGraph")} ModuleGraph */
  20. /** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */
  21. /** @typedef {import("../ModuleGraphConnection").ConnectionState} ConnectionState */
  22. /** @typedef {import("../WebpackError")} WebpackError */
  23. /** @typedef {import("../util/Hash")} Hash */
  24. /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
  25. const idsSymbol = Symbol("HarmonyImportSpecifierDependency.ids");
  26. const { ExportPresenceModes } = HarmonyImportDependency;
  27. class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
  28. constructor(
  29. request,
  30. sourceOrder,
  31. ids,
  32. name,
  33. range,
  34. exportPresenceMode,
  35. assertions
  36. ) {
  37. super(request, sourceOrder, assertions);
  38. this.ids = ids;
  39. this.name = name;
  40. this.range = range;
  41. this.exportPresenceMode = exportPresenceMode;
  42. this.namespaceObjectAsContext = false;
  43. this.call = undefined;
  44. this.directImport = undefined;
  45. this.shorthand = undefined;
  46. this.asiSafe = undefined;
  47. /** @type {Set<string> | boolean} */
  48. this.usedByExports = undefined;
  49. }
  50. // TODO webpack 6 remove
  51. get id() {
  52. throw new Error("id was renamed to ids and type changed to string[]");
  53. }
  54. // TODO webpack 6 remove
  55. getId() {
  56. throw new Error("id was renamed to ids and type changed to string[]");
  57. }
  58. // TODO webpack 6 remove
  59. setId() {
  60. throw new Error("id was renamed to ids and type changed to string[]");
  61. }
  62. get type() {
  63. return "harmony import specifier";
  64. }
  65. /**
  66. * @param {ModuleGraph} moduleGraph the module graph
  67. * @returns {string[]} the imported ids
  68. */
  69. getIds(moduleGraph) {
  70. const meta = moduleGraph.getMetaIfExisting(this);
  71. if (meta === undefined) return this.ids;
  72. const ids = meta[idsSymbol];
  73. return ids !== undefined ? ids : this.ids;
  74. }
  75. /**
  76. * @param {ModuleGraph} moduleGraph the module graph
  77. * @param {string[]} ids the imported ids
  78. * @returns {void}
  79. */
  80. setIds(moduleGraph, ids) {
  81. moduleGraph.getMeta(this)[idsSymbol] = ids;
  82. }
  83. /**
  84. * @param {ModuleGraph} moduleGraph module graph
  85. * @returns {null | false | function(ModuleGraphConnection, RuntimeSpec): ConnectionState} function to determine if the connection is active
  86. */
  87. getCondition(moduleGraph) {
  88. return getDependencyUsedByExportsCondition(
  89. this,
  90. this.usedByExports,
  91. moduleGraph
  92. );
  93. }
  94. /**
  95. * @param {ModuleGraph} moduleGraph the module graph
  96. * @returns {ConnectionState} how this dependency connects the module to referencing modules
  97. */
  98. getModuleEvaluationSideEffectsState(moduleGraph) {
  99. return false;
  100. }
  101. /**
  102. * Returns list of exports referenced by this dependency
  103. * @param {ModuleGraph} moduleGraph module graph
  104. * @param {RuntimeSpec} runtime the runtime for which the module is analysed
  105. * @returns {(string[] | ReferencedExport)[]} referenced exports
  106. */
  107. getReferencedExports(moduleGraph, runtime) {
  108. let ids = this.getIds(moduleGraph);
  109. if (ids.length === 0) return Dependency.EXPORTS_OBJECT_REFERENCED;
  110. let namespaceObjectAsContext = this.namespaceObjectAsContext;
  111. if (ids[0] === "default") {
  112. const selfModule = moduleGraph.getParentModule(this);
  113. const importedModule = moduleGraph.getModule(this);
  114. switch (
  115. importedModule.getExportsType(
  116. moduleGraph,
  117. selfModule.buildMeta.strictHarmonyModule
  118. )
  119. ) {
  120. case "default-only":
  121. case "default-with-named":
  122. if (ids.length === 1) return Dependency.EXPORTS_OBJECT_REFERENCED;
  123. ids = ids.slice(1);
  124. namespaceObjectAsContext = true;
  125. break;
  126. case "dynamic":
  127. return Dependency.EXPORTS_OBJECT_REFERENCED;
  128. }
  129. }
  130. if (
  131. this.call &&
  132. !this.directImport &&
  133. (namespaceObjectAsContext || ids.length > 1)
  134. ) {
  135. if (ids.length === 1) return Dependency.EXPORTS_OBJECT_REFERENCED;
  136. ids = ids.slice(0, -1);
  137. }
  138. return [ids];
  139. }
  140. /**
  141. * @param {ModuleGraph} moduleGraph module graph
  142. * @returns {number} effective mode
  143. */
  144. _getEffectiveExportPresenceLevel(moduleGraph) {
  145. if (this.exportPresenceMode !== ExportPresenceModes.AUTO)
  146. return this.exportPresenceMode;
  147. return moduleGraph.getParentModule(this).buildMeta.strictHarmonyModule
  148. ? ExportPresenceModes.ERROR
  149. : ExportPresenceModes.WARN;
  150. }
  151. /**
  152. * Returns warnings
  153. * @param {ModuleGraph} moduleGraph module graph
  154. * @returns {WebpackError[]} warnings
  155. */
  156. getWarnings(moduleGraph) {
  157. const exportsPresence = this._getEffectiveExportPresenceLevel(moduleGraph);
  158. if (exportsPresence === ExportPresenceModes.WARN) {
  159. return this._getErrors(moduleGraph);
  160. }
  161. return null;
  162. }
  163. /**
  164. * Returns errors
  165. * @param {ModuleGraph} moduleGraph module graph
  166. * @returns {WebpackError[]} errors
  167. */
  168. getErrors(moduleGraph) {
  169. const exportsPresence = this._getEffectiveExportPresenceLevel(moduleGraph);
  170. if (exportsPresence === ExportPresenceModes.ERROR) {
  171. return this._getErrors(moduleGraph);
  172. }
  173. return null;
  174. }
  175. /**
  176. * @param {ModuleGraph} moduleGraph module graph
  177. * @returns {WebpackError[] | undefined} errors
  178. */
  179. _getErrors(moduleGraph) {
  180. const ids = this.getIds(moduleGraph);
  181. return this.getLinkingErrors(
  182. moduleGraph,
  183. ids,
  184. `(imported as '${this.name}')`
  185. );
  186. }
  187. /**
  188. * implement this method to allow the occurrence order plugin to count correctly
  189. * @returns {number} count how often the id is used in this dependency
  190. */
  191. getNumberOfIdOccurrences() {
  192. return 0;
  193. }
  194. serialize(context) {
  195. const { write } = context;
  196. write(this.ids);
  197. write(this.name);
  198. write(this.range);
  199. write(this.exportPresenceMode);
  200. write(this.namespaceObjectAsContext);
  201. write(this.call);
  202. write(this.directImport);
  203. write(this.shorthand);
  204. write(this.asiSafe);
  205. write(this.usedByExports);
  206. super.serialize(context);
  207. }
  208. deserialize(context) {
  209. const { read } = context;
  210. this.ids = read();
  211. this.name = read();
  212. this.range = read();
  213. this.exportPresenceMode = read();
  214. this.namespaceObjectAsContext = read();
  215. this.call = read();
  216. this.directImport = read();
  217. this.shorthand = read();
  218. this.asiSafe = read();
  219. this.usedByExports = read();
  220. super.deserialize(context);
  221. }
  222. }
  223. makeSerializable(
  224. HarmonyImportSpecifierDependency,
  225. "webpack/lib/dependencies/HarmonyImportSpecifierDependency"
  226. );
  227. HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependencyTemplate extends (
  228. HarmonyImportDependency.Template
  229. ) {
  230. /**
  231. * @param {Dependency} dependency the dependency for which the template should be applied
  232. * @param {ReplaceSource} source the current replace source which can be modified
  233. * @param {DependencyTemplateContext} templateContext the context object
  234. * @returns {void}
  235. */
  236. apply(dependency, source, templateContext) {
  237. const dep = /** @type {HarmonyImportSpecifierDependency} */ (dependency);
  238. const { moduleGraph, runtime } = templateContext;
  239. const connection = moduleGraph.getConnection(dep);
  240. // Skip rendering depending when dependency is conditional
  241. if (connection && !connection.isTargetActive(runtime)) return;
  242. const ids = dep.getIds(moduleGraph);
  243. const exportExpr = this._getCodeForIds(dep, source, templateContext, ids);
  244. const range = dep.range;
  245. if (dep.shorthand) {
  246. source.insert(range[1], `: ${exportExpr}`);
  247. } else {
  248. source.replace(range[0], range[1] - 1, exportExpr);
  249. }
  250. }
  251. /**
  252. * @param {HarmonyImportSpecifierDependency} dep dependency
  253. * @param {ReplaceSource} source source
  254. * @param {DependencyTemplateContext} templateContext context
  255. * @param {string[]} ids ids
  256. * @returns {string} generated code
  257. */
  258. _getCodeForIds(dep, source, templateContext, ids) {
  259. const { moduleGraph, module, runtime, concatenationScope } =
  260. templateContext;
  261. const connection = moduleGraph.getConnection(dep);
  262. let exportExpr;
  263. if (
  264. connection &&
  265. concatenationScope &&
  266. concatenationScope.isModuleInScope(connection.module)
  267. ) {
  268. if (ids.length === 0) {
  269. exportExpr = concatenationScope.createModuleReference(
  270. connection.module,
  271. {
  272. asiSafe: dep.asiSafe
  273. }
  274. );
  275. } else if (dep.namespaceObjectAsContext && ids.length === 1) {
  276. exportExpr =
  277. concatenationScope.createModuleReference(connection.module, {
  278. asiSafe: dep.asiSafe
  279. }) + propertyAccess(ids);
  280. } else {
  281. exportExpr = concatenationScope.createModuleReference(
  282. connection.module,
  283. {
  284. ids,
  285. call: dep.call,
  286. directImport: dep.directImport,
  287. asiSafe: dep.asiSafe
  288. }
  289. );
  290. }
  291. } else {
  292. super.apply(dep, source, templateContext);
  293. const { runtimeTemplate, initFragments, runtimeRequirements } =
  294. templateContext;
  295. exportExpr = runtimeTemplate.exportFromImport({
  296. moduleGraph,
  297. module: moduleGraph.getModule(dep),
  298. request: dep.request,
  299. exportName: ids,
  300. originModule: module,
  301. asiSafe: dep.shorthand ? true : dep.asiSafe,
  302. isCall: dep.call,
  303. callContext: !dep.directImport,
  304. defaultInterop: true,
  305. importVar: dep.getImportVar(moduleGraph),
  306. initFragments,
  307. runtime,
  308. runtimeRequirements
  309. });
  310. }
  311. return exportExpr;
  312. }
  313. };
  314. module.exports = HarmonyImportSpecifierDependency;