HarmonyImportSpecifierDependency.js 14 KB

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