1 | /*
|
---|
2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
---|
3 | Author Tobias Koppers @sokra
|
---|
4 | */
|
---|
5 |
|
---|
6 | "use strict";
|
---|
7 |
|
---|
8 | /** @typedef {import("../Dependency")} Dependency */
|
---|
9 | /** @typedef {import("../Module")} Module */
|
---|
10 | /** @typedef {import("../ModuleGraph")} ModuleGraph */
|
---|
11 | /** @typedef {import("../javascript/JavascriptParser").Range} Range */
|
---|
12 |
|
---|
13 | /**
|
---|
14 | * @summary Get the subset of ids and their corresponding range in an id chain that should be re-rendered by webpack.
|
---|
15 | * Only those in the chain that are actually referring to namespaces or imports should be re-rendered.
|
---|
16 | * Deeper member accessors on the imported object should not be re-rendered. If deeper member accessors are re-rendered,
|
---|
17 | * there is a potential loss of meaning with rendering a quoted accessor as an unquoted accessor, or vice versa,
|
---|
18 | * because minifiers treat quoted accessors differently. e.g. import { a } from "./module"; a["b"] vs a.b
|
---|
19 | * @param {string[]} untrimmedIds chained ids
|
---|
20 | * @param {Range} untrimmedRange range encompassing allIds
|
---|
21 | * @param {Range[] | undefined} ranges cumulative range of ids for each of allIds
|
---|
22 | * @param {ModuleGraph} moduleGraph moduleGraph
|
---|
23 | * @param {Dependency} dependency dependency
|
---|
24 | * @returns {{trimmedIds: string[], trimmedRange: Range}} computed trimmed ids and cumulative range of those ids
|
---|
25 | */
|
---|
26 | module.exports.getTrimmedIdsAndRange = (
|
---|
27 | untrimmedIds,
|
---|
28 | untrimmedRange,
|
---|
29 | ranges,
|
---|
30 | moduleGraph,
|
---|
31 | dependency
|
---|
32 | ) => {
|
---|
33 | let trimmedIds = trimIdsToThoseImported(
|
---|
34 | untrimmedIds,
|
---|
35 | moduleGraph,
|
---|
36 | dependency
|
---|
37 | );
|
---|
38 | let trimmedRange = untrimmedRange;
|
---|
39 | if (trimmedIds.length !== untrimmedIds.length) {
|
---|
40 | // The array returned from dep.idRanges is right-aligned with the array returned from dep.names.
|
---|
41 | // Meaning, the two arrays may not always have the same number of elements, but the last element of
|
---|
42 | // dep.idRanges corresponds to [the expression fragment to the left of] the last element of dep.names.
|
---|
43 | // Use this to find the correct replacement range based on the number of ids that were trimmed.
|
---|
44 | const idx =
|
---|
45 | ranges === undefined
|
---|
46 | ? -1 /* trigger failure case below */
|
---|
47 | : ranges.length + (trimmedIds.length - untrimmedIds.length);
|
---|
48 | if (idx < 0 || idx >= /** @type {Range[]} */ (ranges).length) {
|
---|
49 | // cspell:ignore minifiers
|
---|
50 | // Should not happen but we can't throw an error here because of backward compatibility with
|
---|
51 | // external plugins in wp5. Instead, we just disable trimming for now. This may break some minifiers.
|
---|
52 | trimmedIds = untrimmedIds;
|
---|
53 | // TODO webpack 6 remove the "trimmedIds = ids" above and uncomment the following line instead.
|
---|
54 | // throw new Error("Missing range starts data for id replacement trimming.");
|
---|
55 | } else {
|
---|
56 | trimmedRange = /** @type {Range[]} */ (ranges)[idx];
|
---|
57 | }
|
---|
58 | }
|
---|
59 |
|
---|
60 | return { trimmedIds, trimmedRange };
|
---|
61 | };
|
---|
62 |
|
---|
63 | /**
|
---|
64 | * @summary Determine which IDs in the id chain are actually referring to namespaces or imports,
|
---|
65 | * and which are deeper member accessors on the imported object.
|
---|
66 | * @param {string[]} ids untrimmed ids
|
---|
67 | * @param {ModuleGraph} moduleGraph moduleGraph
|
---|
68 | * @param {Dependency} dependency dependency
|
---|
69 | * @returns {string[]} trimmed ids
|
---|
70 | */
|
---|
71 | function trimIdsToThoseImported(ids, moduleGraph, dependency) {
|
---|
72 | /** @type {string[]} */
|
---|
73 | let trimmedIds = [];
|
---|
74 | let currentExportsInfo = moduleGraph.getExportsInfo(
|
---|
75 | /** @type {Module} */ (moduleGraph.getModule(dependency))
|
---|
76 | );
|
---|
77 | for (let i = 0; i < ids.length; i++) {
|
---|
78 | if (i === 0 && ids[i] === "default") {
|
---|
79 | continue; // ExportInfo for the next level under default is still at the root ExportsInfo, so don't advance currentExportsInfo
|
---|
80 | }
|
---|
81 | const exportInfo = currentExportsInfo.getExportInfo(ids[i]);
|
---|
82 | if (exportInfo.provided === false) {
|
---|
83 | // json imports have nested ExportInfo for elements that things that are not actually exported, so check .provided
|
---|
84 | trimmedIds = ids.slice(0, i);
|
---|
85 | break;
|
---|
86 | }
|
---|
87 | const nestedInfo = exportInfo.getNestedExportsInfo();
|
---|
88 | if (!nestedInfo) {
|
---|
89 | // once all nested exports are traversed, the next item is the actual import so stop there
|
---|
90 | trimmedIds = ids.slice(0, i + 1);
|
---|
91 | break;
|
---|
92 | }
|
---|
93 | currentExportsInfo = nestedInfo;
|
---|
94 | }
|
---|
95 | // Never trim to nothing. This can happen for invalid imports (e.g. import { notThere } from "./module", or import { anything } from "./missingModule")
|
---|
96 | return trimmedIds.length ? trimmedIds : ids;
|
---|
97 | }
|
---|