HarmonyImportSpecifierDependency.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  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").ReferencedExport} ReferencedExport */
  18. /** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
  19. /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
  20. /** @typedef {import("../Module")} Module */
  21. /** @typedef {import("../Module").BuildMeta} BuildMeta */
  22. /** @typedef {import("../ModuleGraph")} ModuleGraph */
  23. /** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */
  24. /** @typedef {import("../ModuleGraphConnection").ConnectionState} ConnectionState */
  25. /** @typedef {import("../WebpackError")} WebpackError */
  26. /** @typedef {import("../javascript/JavascriptParser").Assertions} Assertions */
  27. /** @typedef {import("../javascript/JavascriptParser").Range} Range */
  28. /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
  29. /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
  30. /** @typedef {import("../util/Hash")} Hash */
  31. /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
  32. const idsSymbol = Symbol("HarmonyImportSpecifierDependency.ids");
  33. const { ExportPresenceModes } = HarmonyImportDependency;
  34. class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
  35. /**
  36. * @param {TODO} request request
  37. * @param {number} sourceOrder source order
  38. * @param {string[]} ids ids
  39. * @param {string} name name
  40. * @param {Range} range range
  41. * @param {TODO} exportPresenceMode export presence mode
  42. * @param {Assertions=} assertions assertions
  43. * @param {Range[]=} idRanges ranges for members of ids; the two arrays are right-aligned
  44. */
  45. constructor(
  46. request,
  47. sourceOrder,
  48. ids,
  49. name,
  50. range,
  51. exportPresenceMode,
  52. assertions,
  53. idRanges // TODO webpack 6 make this non-optional. It must always be set to properly trim ids.
  54. ) {
  55. super(request, sourceOrder, assertions);
  56. this.ids = ids;
  57. this.name = name;
  58. this.range = range;
  59. this.idRanges = idRanges;
  60. this.exportPresenceMode = exportPresenceMode;
  61. /** @type {boolean | undefined} */
  62. this.namespaceObjectAsContext = false;
  63. this.call = undefined;
  64. this.directImport = undefined;
  65. this.shorthand = undefined;
  66. this.asiSafe = undefined;
  67. /** @type {Set<string> | boolean | undefined} */
  68. this.usedByExports = undefined;
  69. /** @type {Set<string> | undefined} */
  70. this.referencedPropertiesInDestructuring = undefined;
  71. }
  72. // TODO webpack 6 remove
  73. get id() {
  74. throw new Error("id was renamed to ids and type changed to string[]");
  75. }
  76. // TODO webpack 6 remove
  77. getId() {
  78. throw new Error("id was renamed to ids and type changed to string[]");
  79. }
  80. // TODO webpack 6 remove
  81. setId() {
  82. throw new Error("id was renamed to ids and type changed to string[]");
  83. }
  84. get type() {
  85. return "harmony import specifier";
  86. }
  87. /**
  88. * @param {ModuleGraph} moduleGraph the module graph
  89. * @returns {string[]} the imported ids
  90. */
  91. getIds(moduleGraph) {
  92. const meta = moduleGraph.getMetaIfExisting(this);
  93. if (meta === undefined) return this.ids;
  94. const ids = meta[idsSymbol];
  95. return ids !== undefined ? ids : this.ids;
  96. }
  97. /**
  98. * @param {ModuleGraph} moduleGraph the module graph
  99. * @param {string[]} ids the imported ids
  100. * @returns {void}
  101. */
  102. setIds(moduleGraph, ids) {
  103. moduleGraph.getMeta(this)[idsSymbol] = ids;
  104. }
  105. /**
  106. * @param {ModuleGraph} moduleGraph module graph
  107. * @returns {null | false | function(ModuleGraphConnection, RuntimeSpec): ConnectionState} function to determine if the connection is active
  108. */
  109. getCondition(moduleGraph) {
  110. return getDependencyUsedByExportsCondition(
  111. this,
  112. this.usedByExports,
  113. moduleGraph
  114. );
  115. }
  116. /**
  117. * @param {ModuleGraph} moduleGraph the module graph
  118. * @returns {ConnectionState} how this dependency connects the module to referencing modules
  119. */
  120. getModuleEvaluationSideEffectsState(moduleGraph) {
  121. return false;
  122. }
  123. /**
  124. * Returns list of exports referenced by this dependency
  125. * @param {ModuleGraph} moduleGraph module graph
  126. * @param {RuntimeSpec} runtime the runtime for which the module is analysed
  127. * @returns {(string[] | ReferencedExport)[]} referenced exports
  128. */
  129. getReferencedExports(moduleGraph, runtime) {
  130. let ids = this.getIds(moduleGraph);
  131. if (ids.length === 0) return this._getReferencedExportsInDestructuring();
  132. let namespaceObjectAsContext = this.namespaceObjectAsContext;
  133. if (ids[0] === "default") {
  134. const selfModule = moduleGraph.getParentModule(this);
  135. const importedModule =
  136. /** @type {Module} */
  137. (moduleGraph.getModule(this));
  138. switch (
  139. importedModule.getExportsType(
  140. moduleGraph,
  141. /** @type {BuildMeta} */
  142. (selfModule.buildMeta).strictHarmonyModule
  143. )
  144. ) {
  145. case "default-only":
  146. case "default-with-named":
  147. if (ids.length === 1)
  148. return this._getReferencedExportsInDestructuring();
  149. ids = ids.slice(1);
  150. namespaceObjectAsContext = true;
  151. break;
  152. case "dynamic":
  153. return Dependency.EXPORTS_OBJECT_REFERENCED;
  154. }
  155. }
  156. if (
  157. this.call &&
  158. !this.directImport &&
  159. (namespaceObjectAsContext || ids.length > 1)
  160. ) {
  161. if (ids.length === 1) return Dependency.EXPORTS_OBJECT_REFERENCED;
  162. ids = ids.slice(0, -1);
  163. }
  164. return this._getReferencedExportsInDestructuring(ids);
  165. }
  166. /**
  167. * @param {string[]=} ids ids
  168. * @returns {(string[] | ReferencedExport)[]} referenced exports
  169. */
  170. _getReferencedExportsInDestructuring(ids) {
  171. if (this.referencedPropertiesInDestructuring) {
  172. /** @type {ReferencedExport[]} */
  173. const refs = [];
  174. for (const key of this.referencedPropertiesInDestructuring) {
  175. refs.push({
  176. name: ids ? ids.concat([key]) : [key],
  177. canMangle: false
  178. });
  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 {
  301. trimmedRange: [trimmedRangeStart, trimmedRangeEnd],
  302. trimmedIds
  303. } = getTrimmedIdsAndRange(
  304. dep.getIds(moduleGraph),
  305. dep.range,
  306. dep.idRanges,
  307. moduleGraph,
  308. dep
  309. );
  310. const exportExpr = this._getCodeForIds(
  311. dep,
  312. source,
  313. templateContext,
  314. trimmedIds
  315. );
  316. if (dep.shorthand) {
  317. source.insert(trimmedRangeEnd, `: ${exportExpr}`);
  318. } else {
  319. source.replace(trimmedRangeStart, trimmedRangeEnd - 1, exportExpr);
  320. }
  321. }
  322. /**
  323. * @param {HarmonyImportSpecifierDependency} dep dependency
  324. * @param {ReplaceSource} source source
  325. * @param {DependencyTemplateContext} templateContext context
  326. * @param {string[]} ids ids
  327. * @returns {string} generated code
  328. */
  329. _getCodeForIds(dep, source, templateContext, ids) {
  330. const { moduleGraph, module, runtime, concatenationScope } =
  331. templateContext;
  332. const connection = moduleGraph.getConnection(dep);
  333. let exportExpr;
  334. if (
  335. connection &&
  336. concatenationScope &&
  337. concatenationScope.isModuleInScope(connection.module)
  338. ) {
  339. if (ids.length === 0) {
  340. exportExpr = concatenationScope.createModuleReference(
  341. connection.module,
  342. {
  343. asiSafe: dep.asiSafe
  344. }
  345. );
  346. } else if (dep.namespaceObjectAsContext && ids.length === 1) {
  347. exportExpr =
  348. concatenationScope.createModuleReference(connection.module, {
  349. asiSafe: dep.asiSafe
  350. }) + propertyAccess(ids);
  351. } else {
  352. exportExpr = concatenationScope.createModuleReference(
  353. connection.module,
  354. {
  355. ids,
  356. call: dep.call,
  357. directImport: dep.directImport,
  358. asiSafe: dep.asiSafe
  359. }
  360. );
  361. }
  362. } else {
  363. super.apply(dep, source, templateContext);
  364. const { runtimeTemplate, initFragments, runtimeRequirements } =
  365. templateContext;
  366. exportExpr = runtimeTemplate.exportFromImport({
  367. moduleGraph,
  368. module: /** @type {Module} */ (moduleGraph.getModule(dep)),
  369. request: dep.request,
  370. exportName: ids,
  371. originModule: module,
  372. asiSafe: dep.shorthand ? true : dep.asiSafe,
  373. isCall: dep.call,
  374. callContext: !dep.directImport,
  375. defaultInterop: true,
  376. importVar: dep.getImportVar(moduleGraph),
  377. initFragments,
  378. runtime,
  379. runtimeRequirements
  380. });
  381. }
  382. return exportExpr;
  383. }
  384. };
  385. module.exports = HarmonyImportSpecifierDependency;