HarmonyImportSpecifierDependency.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  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 Template = require("../Template");
  8. const {
  9. getDependencyUsedByExportsCondition
  10. } = require("../optimize/InnerGraph");
  11. const { getTrimmedIdsAndRange } = require("../util/chainedImports");
  12. const makeSerializable = require("../util/makeSerializable");
  13. const propertyAccess = require("../util/propertyAccess");
  14. const HarmonyImportDependency = require("./HarmonyImportDependency");
  15. /** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
  16. /** @typedef {import("../ChunkGraph")} ChunkGraph */
  17. /** @typedef {import("../Dependency").ExportsSpec} ExportsSpec */
  18. /** @typedef {import("../Dependency").GetConditionFn} GetConditionFn */
  19. /** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
  20. /** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
  21. /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
  22. /** @typedef {import("../Module")} Module */
  23. /** @typedef {import("../Module").BuildMeta} BuildMeta */
  24. /** @typedef {import("../ModuleGraph")} ModuleGraph */
  25. /** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */
  26. /** @typedef {import("../ModuleGraphConnection").ConnectionState} ConnectionState */
  27. /** @typedef {import("../WebpackError")} WebpackError */
  28. /** @typedef {import("../javascript/JavascriptParser").DestructuringAssignmentProperty} DestructuringAssignmentProperty */
  29. /** @typedef {import("../javascript/JavascriptParser").ImportAttributes} ImportAttributes */
  30. /** @typedef {import("../javascript/JavascriptParser").Range} Range */
  31. /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
  32. /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
  33. /** @typedef {import("../util/Hash")} Hash */
  34. /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
  35. const idsSymbol = Symbol("HarmonyImportSpecifierDependency.ids");
  36. const { ExportPresenceModes } = HarmonyImportDependency;
  37. class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
  38. /**
  39. * @param {TODO} request request
  40. * @param {number} sourceOrder source order
  41. * @param {string[]} ids ids
  42. * @param {string} name name
  43. * @param {Range} range range
  44. * @param {TODO} exportPresenceMode export presence mode
  45. * @param {ImportAttributes | undefined} attributes import attributes
  46. * @param {Range[] | undefined} idRanges ranges for members of ids; the two arrays are right-aligned
  47. */
  48. constructor(
  49. request,
  50. sourceOrder,
  51. ids,
  52. name,
  53. range,
  54. exportPresenceMode,
  55. attributes,
  56. idRanges // TODO webpack 6 make this non-optional. It must always be set to properly trim ids.
  57. ) {
  58. super(request, sourceOrder, attributes);
  59. this.ids = ids;
  60. this.name = name;
  61. this.range = range;
  62. this.idRanges = idRanges;
  63. this.exportPresenceMode = exportPresenceMode;
  64. /** @type {boolean | undefined} */
  65. this.namespaceObjectAsContext = false;
  66. this.call = undefined;
  67. this.directImport = undefined;
  68. this.shorthand = undefined;
  69. this.asiSafe = undefined;
  70. /** @type {Set<string> | boolean | undefined} */
  71. this.usedByExports = undefined;
  72. /** @type {Set<DestructuringAssignmentProperty> | undefined} */
  73. this.referencedPropertiesInDestructuring = undefined;
  74. }
  75. // TODO webpack 6 remove
  76. get id() {
  77. throw new Error("id was renamed to ids and type changed to string[]");
  78. }
  79. // TODO webpack 6 remove
  80. getId() {
  81. throw new Error("id was renamed to ids and type changed to string[]");
  82. }
  83. // TODO webpack 6 remove
  84. setId() {
  85. throw new Error("id was renamed to ids and type changed to string[]");
  86. }
  87. get type() {
  88. return "harmony import specifier";
  89. }
  90. /**
  91. * @param {ModuleGraph} moduleGraph the module graph
  92. * @returns {string[]} the imported ids
  93. */
  94. getIds(moduleGraph) {
  95. const meta = moduleGraph.getMetaIfExisting(this);
  96. if (meta === undefined) return this.ids;
  97. const ids = meta[idsSymbol];
  98. return ids !== undefined ? ids : this.ids;
  99. }
  100. /**
  101. * @param {ModuleGraph} moduleGraph the module graph
  102. * @param {string[]} ids the imported ids
  103. * @returns {void}
  104. */
  105. setIds(moduleGraph, ids) {
  106. moduleGraph.getMeta(this)[idsSymbol] = ids;
  107. }
  108. /**
  109. * @param {ModuleGraph} moduleGraph module graph
  110. * @returns {null | false | GetConditionFn} function to determine if the connection is active
  111. */
  112. getCondition(moduleGraph) {
  113. return getDependencyUsedByExportsCondition(
  114. this,
  115. this.usedByExports,
  116. moduleGraph
  117. );
  118. }
  119. /**
  120. * @param {ModuleGraph} moduleGraph the module graph
  121. * @returns {ConnectionState} how this dependency connects the module to referencing modules
  122. */
  123. getModuleEvaluationSideEffectsState(moduleGraph) {
  124. return false;
  125. }
  126. /**
  127. * Returns list of exports referenced by this dependency
  128. * @param {ModuleGraph} moduleGraph module graph
  129. * @param {RuntimeSpec} runtime the runtime for which the module is analysed
  130. * @returns {(string[] | ReferencedExport)[]} referenced exports
  131. */
  132. getReferencedExports(moduleGraph, runtime) {
  133. let ids = this.getIds(moduleGraph);
  134. if (ids.length === 0) return this._getReferencedExportsInDestructuring();
  135. let namespaceObjectAsContext = this.namespaceObjectAsContext;
  136. if (ids[0] === "default") {
  137. const selfModule = moduleGraph.getParentModule(this);
  138. const importedModule =
  139. /** @type {Module} */
  140. (moduleGraph.getModule(this));
  141. switch (
  142. importedModule.getExportsType(
  143. moduleGraph,
  144. /** @type {BuildMeta} */
  145. (selfModule.buildMeta).strictHarmonyModule
  146. )
  147. ) {
  148. case "default-only":
  149. case "default-with-named":
  150. if (ids.length === 1)
  151. return this._getReferencedExportsInDestructuring();
  152. ids = ids.slice(1);
  153. namespaceObjectAsContext = true;
  154. break;
  155. case "dynamic":
  156. return Dependency.EXPORTS_OBJECT_REFERENCED;
  157. }
  158. }
  159. if (
  160. this.call &&
  161. !this.directImport &&
  162. (namespaceObjectAsContext || ids.length > 1)
  163. ) {
  164. if (ids.length === 1) return Dependency.EXPORTS_OBJECT_REFERENCED;
  165. ids = ids.slice(0, -1);
  166. }
  167. return this._getReferencedExportsInDestructuring(ids);
  168. }
  169. /**
  170. * @param {string[]=} ids ids
  171. * @returns {string[][]} referenced exports
  172. */
  173. _getReferencedExportsInDestructuring(ids) {
  174. if (this.referencedPropertiesInDestructuring) {
  175. /** @type {string[][]} */
  176. const refs = [];
  177. for (const { id } of this.referencedPropertiesInDestructuring) {
  178. refs.push(ids ? ids.concat([id]) : [id]);
  179. }
  180. return refs;
  181. } else {
  182. return ids ? [ids] : Dependency.EXPORTS_OBJECT_REFERENCED;
  183. }
  184. }
  185. /**
  186. * @param {ModuleGraph} moduleGraph module graph
  187. * @returns {number} effective mode
  188. */
  189. _getEffectiveExportPresenceLevel(moduleGraph) {
  190. if (this.exportPresenceMode !== ExportPresenceModes.AUTO)
  191. return this.exportPresenceMode;
  192. const buildMeta = /** @type {BuildMeta} */ (
  193. moduleGraph.getParentModule(this).buildMeta
  194. );
  195. return buildMeta.strictHarmonyModule
  196. ? ExportPresenceModes.ERROR
  197. : ExportPresenceModes.WARN;
  198. }
  199. /**
  200. * Returns warnings
  201. * @param {ModuleGraph} moduleGraph module graph
  202. * @returns {WebpackError[] | null | undefined} warnings
  203. */
  204. getWarnings(moduleGraph) {
  205. const exportsPresence = this._getEffectiveExportPresenceLevel(moduleGraph);
  206. if (exportsPresence === ExportPresenceModes.WARN) {
  207. return this._getErrors(moduleGraph);
  208. }
  209. return null;
  210. }
  211. /**
  212. * Returns errors
  213. * @param {ModuleGraph} moduleGraph module graph
  214. * @returns {WebpackError[] | null | undefined} errors
  215. */
  216. getErrors(moduleGraph) {
  217. const exportsPresence = this._getEffectiveExportPresenceLevel(moduleGraph);
  218. if (exportsPresence === ExportPresenceModes.ERROR) {
  219. return this._getErrors(moduleGraph);
  220. }
  221. return null;
  222. }
  223. /**
  224. * @param {ModuleGraph} moduleGraph module graph
  225. * @returns {WebpackError[] | undefined} errors
  226. */
  227. _getErrors(moduleGraph) {
  228. const ids = this.getIds(moduleGraph);
  229. return this.getLinkingErrors(
  230. moduleGraph,
  231. ids,
  232. `(imported as '${this.name}')`
  233. );
  234. }
  235. /**
  236. * implement this method to allow the occurrence order plugin to count correctly
  237. * @returns {number} count how often the id is used in this dependency
  238. */
  239. getNumberOfIdOccurrences() {
  240. return 0;
  241. }
  242. /**
  243. * @param {ObjectSerializerContext} context context
  244. */
  245. serialize(context) {
  246. const { write } = context;
  247. write(this.ids);
  248. write(this.name);
  249. write(this.range);
  250. write(this.idRanges);
  251. write(this.exportPresenceMode);
  252. write(this.namespaceObjectAsContext);
  253. write(this.call);
  254. write(this.directImport);
  255. write(this.shorthand);
  256. write(this.asiSafe);
  257. write(this.usedByExports);
  258. write(this.referencedPropertiesInDestructuring);
  259. super.serialize(context);
  260. }
  261. /**
  262. * @param {ObjectDeserializerContext} context context
  263. */
  264. deserialize(context) {
  265. const { read } = context;
  266. this.ids = read();
  267. this.name = read();
  268. this.range = read();
  269. this.idRanges = read();
  270. this.exportPresenceMode = read();
  271. this.namespaceObjectAsContext = read();
  272. this.call = read();
  273. this.directImport = read();
  274. this.shorthand = read();
  275. this.asiSafe = read();
  276. this.usedByExports = read();
  277. this.referencedPropertiesInDestructuring = read();
  278. super.deserialize(context);
  279. }
  280. }
  281. makeSerializable(
  282. HarmonyImportSpecifierDependency,
  283. "webpack/lib/dependencies/HarmonyImportSpecifierDependency"
  284. );
  285. HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependencyTemplate extends (
  286. HarmonyImportDependency.Template
  287. ) {
  288. /**
  289. * @param {Dependency} dependency the dependency for which the template should be applied
  290. * @param {ReplaceSource} source the current replace source which can be modified
  291. * @param {DependencyTemplateContext} templateContext the context object
  292. * @returns {void}
  293. */
  294. apply(dependency, source, templateContext) {
  295. const dep = /** @type {HarmonyImportSpecifierDependency} */ (dependency);
  296. const { moduleGraph, runtime } = templateContext;
  297. const connection = moduleGraph.getConnection(dep);
  298. // Skip rendering depending when dependency is conditional
  299. if (connection && !connection.isTargetActive(runtime)) return;
  300. const ids = dep.getIds(moduleGraph);
  301. const {
  302. trimmedRange: [trimmedRangeStart, trimmedRangeEnd],
  303. trimmedIds
  304. } = getTrimmedIdsAndRange(ids, dep.range, dep.idRanges, moduleGraph, dep);
  305. const exportExpr = this._getCodeForIds(
  306. dep,
  307. source,
  308. templateContext,
  309. trimmedIds
  310. );
  311. if (dep.shorthand) {
  312. source.insert(trimmedRangeEnd, `: ${exportExpr}`);
  313. } else {
  314. source.replace(trimmedRangeStart, trimmedRangeEnd - 1, exportExpr);
  315. }
  316. if (dep.referencedPropertiesInDestructuring) {
  317. const prefixedIds = ids[0] === "default" ? ids.slice(1) : ids;
  318. for (let {
  319. id,
  320. shorthand,
  321. range
  322. } of dep.referencedPropertiesInDestructuring) {
  323. const concatedIds = prefixedIds.concat([id]);
  324. const module = moduleGraph.getModule(dep);
  325. const used = moduleGraph
  326. .getExportsInfo(module)
  327. .getUsedName(concatedIds, runtime);
  328. if (!used) return;
  329. const newName = used[used.length - 1];
  330. const name = concatedIds[concatedIds.length - 1];
  331. if (newName === name) continue;
  332. const comment = Template.toNormalComment(name) + " ";
  333. const key = comment + JSON.stringify(newName);
  334. source.replace(
  335. range[0],
  336. range[1] - 1,
  337. shorthand ? `${key}: ${name}` : `${key}`
  338. );
  339. }
  340. }
  341. }
  342. /**
  343. * @param {HarmonyImportSpecifierDependency} dep dependency
  344. * @param {ReplaceSource} source source
  345. * @param {DependencyTemplateContext} templateContext context
  346. * @param {string[]} ids ids
  347. * @returns {string} generated code
  348. */
  349. _getCodeForIds(dep, source, templateContext, ids) {
  350. const { moduleGraph, module, runtime, concatenationScope } =
  351. templateContext;
  352. const connection = moduleGraph.getConnection(dep);
  353. let exportExpr;
  354. if (
  355. connection &&
  356. concatenationScope &&
  357. concatenationScope.isModuleInScope(connection.module)
  358. ) {
  359. if (ids.length === 0) {
  360. exportExpr = concatenationScope.createModuleReference(
  361. connection.module,
  362. {
  363. asiSafe: dep.asiSafe
  364. }
  365. );
  366. } else if (dep.namespaceObjectAsContext && ids.length === 1) {
  367. exportExpr =
  368. concatenationScope.createModuleReference(connection.module, {
  369. asiSafe: dep.asiSafe
  370. }) + propertyAccess(ids);
  371. } else {
  372. exportExpr = concatenationScope.createModuleReference(
  373. connection.module,
  374. {
  375. ids,
  376. call: dep.call,
  377. directImport: dep.directImport,
  378. asiSafe: dep.asiSafe
  379. }
  380. );
  381. }
  382. } else {
  383. super.apply(dep, source, templateContext);
  384. const { runtimeTemplate, initFragments, runtimeRequirements } =
  385. templateContext;
  386. exportExpr = runtimeTemplate.exportFromImport({
  387. moduleGraph,
  388. module: /** @type {Module} */ (moduleGraph.getModule(dep)),
  389. request: dep.request,
  390. exportName: ids,
  391. originModule: module,
  392. asiSafe: dep.shorthand ? true : dep.asiSafe,
  393. isCall: dep.call,
  394. callContext: !dep.directImport,
  395. defaultInterop: true,
  396. importVar: dep.getImportVar(moduleGraph),
  397. initFragments,
  398. runtime,
  399. runtimeRequirements
  400. });
  401. }
  402. return exportExpr;
  403. }
  404. };
  405. module.exports = HarmonyImportSpecifierDependency;