HarmonyImportSpecifierDependency.js 12 KB

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