[79a0317] | 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 | }
|
---|