HarmonyImportSpecifierDependency.js 14 KB

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