HarmonyImportSpecifierDependency.js 10 KB

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