mirror of https://github.com/webpack/webpack.git
feat: static analyze destructuring assignment dynamic import variable for tree shaking (#19925)
This commit is contained in:
parent
ae52500e95
commit
73cecf5e78
|
@ -8,7 +8,10 @@
|
||||||
const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
|
const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
|
||||||
const CommentCompilationWarning = require("../CommentCompilationWarning");
|
const CommentCompilationWarning = require("../CommentCompilationWarning");
|
||||||
const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
|
const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
|
||||||
const { getImportAttributes } = require("../javascript/JavascriptParser");
|
const {
|
||||||
|
VariableInfo,
|
||||||
|
getImportAttributes
|
||||||
|
} = require("../javascript/JavascriptParser");
|
||||||
const ContextDependencyHelpers = require("./ContextDependencyHelpers");
|
const ContextDependencyHelpers = require("./ContextDependencyHelpers");
|
||||||
const ImportContextDependency = require("./ImportContextDependency");
|
const ImportContextDependency = require("./ImportContextDependency");
|
||||||
const ImportDependency = require("./ImportDependency");
|
const ImportDependency = require("./ImportDependency");
|
||||||
|
@ -184,6 +187,15 @@ class ImportParserPlugin {
|
||||||
PLUGIN_NAME,
|
PLUGIN_NAME,
|
||||||
(expr) => {
|
(expr) => {
|
||||||
if (expr.type === "ImportExpression") return true;
|
if (expr.type === "ImportExpression") return true;
|
||||||
|
const nameInfo = parser.getNameForExpression(expr);
|
||||||
|
if (
|
||||||
|
nameInfo &&
|
||||||
|
nameInfo.rootInfo instanceof VariableInfo &&
|
||||||
|
nameInfo.rootInfo.name &&
|
||||||
|
parser.getTagData(nameInfo.rootInfo.name, dynamicImportTag)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
parser.hooks.preDeclarator.tap(PLUGIN_NAME, (decl) => {
|
parser.hooks.preDeclarator.tap(PLUGIN_NAME, (decl) => {
|
||||||
|
@ -196,9 +208,19 @@ class ImportParserPlugin {
|
||||||
tagDynamicImportReferenced(parser, decl.init.argument, decl.id.name);
|
tagDynamicImportReferenced(parser, decl.init.argument, decl.id.name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
parser.hooks.expression.for(dynamicImportTag).tap(PLUGIN_NAME, () => {
|
parser.hooks.expression.for(dynamicImportTag).tap(PLUGIN_NAME, (expr) => {
|
||||||
const settings = /** @type {ImportSettings} */ (parser.currentTagData);
|
const settings = /** @type {ImportSettings} */ (parser.currentTagData);
|
||||||
|
const referencedPropertiesInDestructuring =
|
||||||
|
parser.destructuringAssignmentPropertiesFor(expr);
|
||||||
|
if (referencedPropertiesInDestructuring) {
|
||||||
|
for (const ids of exportsFromEnumerable(
|
||||||
|
[...referencedPropertiesInDestructuring].map(({ id }) => id)
|
||||||
|
)) {
|
||||||
|
settings.references.push(ids);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
settings.references.push([]);
|
settings.references.push([]);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
parser.hooks.expressionMemberChain
|
parser.hooks.expressionMemberChain
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
export const a = 1;
|
|
||||||
export default 3;
|
|
||||||
export const usedExports = __webpack_exports_info__.usedExports;
|
|
|
@ -1,2 +0,0 @@
|
||||||
exports.a = 1;
|
|
||||||
exports.b = 2;
|
|
|
@ -1 +0,0 @@
|
||||||
["a"]
|
|
|
@ -1 +0,0 @@
|
||||||
{"a": 1}
|
|
|
@ -1 +0,0 @@
|
||||||
"a"
|
|
|
@ -1,2 +0,0 @@
|
||||||
exports.a = 1;
|
|
||||||
exports.b = 2;
|
|
|
@ -1 +0,0 @@
|
||||||
["a"]
|
|
|
@ -1 +0,0 @@
|
||||||
{"a": 1}
|
|
|
@ -1 +0,0 @@
|
||||||
"a"
|
|
|
@ -1,36 +0,0 @@
|
||||||
it("should load only used exports", async (done) => {
|
|
||||||
const { default: def, usedExports } = await import("./dir1/a");
|
|
||||||
expect(def).toBe(3);
|
|
||||||
expect(usedExports).toEqual(["default", "usedExports"]);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should get warning on using 'webpackExports' with destructuring assignment", async (done) => {
|
|
||||||
const { default: def } = await import(/* webpackExports: ["a"] */"./dir1/a?2");
|
|
||||||
expect(def).toBe(3);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not tree-shake default export for exportsType=default module", async () => {
|
|
||||||
const { default: object } = await import("./dir2/json/object.json");
|
|
||||||
const { default: array } = await import("./dir2/json/array.json");
|
|
||||||
const { default: primitive } = await import("./dir2/json/primitive.json");
|
|
||||||
expect(object).toEqual({ a: 1 });
|
|
||||||
expect(array).toEqual(["a"]);
|
|
||||||
expect(primitive).toBe("a");
|
|
||||||
const { default: a } = await import("./dir2/a");
|
|
||||||
expect(a).toEqual({ a: 1, b: 2 });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not tree-shake default export for exportsType=default context module", async () => {
|
|
||||||
const dir = "json";
|
|
||||||
const { default: object } = await import(`./dir3/${dir}/object.json`);
|
|
||||||
const { default: array } = await import(`./dir3/${dir}/array.json`);
|
|
||||||
const { default: primitive } = await import(`./dir3/${dir}/primitive.json`);
|
|
||||||
expect(object).toEqual({ a: 1 });
|
|
||||||
expect(array).toEqual(["a"]);
|
|
||||||
expect(primitive).toBe("a");
|
|
||||||
const file = "a";
|
|
||||||
const { default: a } = await import(`./dir3/${file}`);
|
|
||||||
expect(a).toEqual({ a: 1, b: 2 });
|
|
||||||
});
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
it("should load only used exports", async (done) => {
|
||||||
|
const { default: def, usedExports } = await import("../statical-dynamic-import/dir1/a");
|
||||||
|
expect(def).toBe(3);
|
||||||
|
expect(usedExports).toEqual(["default", "usedExports"]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should get warning on using 'webpackExports' with destructuring assignment", async (done) => {
|
||||||
|
const { default: def } = await import(/* webpackExports: ["a"] */"../statical-dynamic-import/dir1/a?2");
|
||||||
|
expect(def).toBe(3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not tree-shake default export for exportsType=default module", async () => {
|
||||||
|
const { default: object } = await import("../statical-dynamic-import/dir2/json/object.json");
|
||||||
|
const { default: array } = await import("../statical-dynamic-import/dir2/json/array.json");
|
||||||
|
const { default: primitive } = await import("../statical-dynamic-import/dir2/json/primitive.json");
|
||||||
|
expect(object).toEqual({ a: 1 });
|
||||||
|
expect(array).toEqual(["a"]);
|
||||||
|
expect(primitive).toBe("a");
|
||||||
|
const { default: a } = await import("../statical-dynamic-import/dir2/a");
|
||||||
|
expect(a).toEqual({ a: 1, b: 2 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not tree-shake default export for exportsType=default context module", async () => {
|
||||||
|
const dir = "json";
|
||||||
|
const { default: object } = await import(`../statical-dynamic-import/dir3/${dir}/object.json`);
|
||||||
|
const { default: array } = await import(`../statical-dynamic-import/dir3/${dir}/array.json`);
|
||||||
|
const { default: primitive } = await import(`../statical-dynamic-import/dir3/${dir}/primitive.json`);
|
||||||
|
expect(object).toEqual({ a: 1 });
|
||||||
|
expect(array).toEqual(["a"]);
|
||||||
|
expect(primitive).toBe("a");
|
||||||
|
const file = "a";
|
||||||
|
const { default: a } = await import(`../statical-dynamic-import/dir3/${file}`);
|
||||||
|
expect(a).toEqual({ a: 1, b: 2 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should static analyze dynamic import variable destructuring assignment", async () => {
|
||||||
|
const m = await import("../statical-dynamic-import/dir1/a?3");
|
||||||
|
const { default: def, usedExports } = m;
|
||||||
|
expect(def).toBe(3);
|
||||||
|
expect(usedExports).toEqual(["default", "usedExports"]);
|
||||||
|
});
|
|
@ -53,12 +53,20 @@ it("should walk with correct order", async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should analyze arguments in call member chain", async () => {
|
it("should analyze arguments in call member chain", async () => {
|
||||||
import("../statical-dynamic-import/dir4/lib?2").then(({ b }) => {
|
await import("../statical-dynamic-import/dir4/lib?2").then(({ b }) => {
|
||||||
b.f((async () => {
|
b.f((async () => {
|
||||||
import("../statical-dynamic-import/dir4/a?2").then(({ a, usedExports }) => {
|
await import("../statical-dynamic-import/dir4/a?2").then(({ a, usedExports }) => {
|
||||||
expect(a).toBe(1);
|
expect(a).toBe(1);
|
||||||
expect(usedExports).toEqual(["a", "usedExports"]);
|
expect(usedExports).toEqual(["a", "usedExports"]);
|
||||||
});
|
});
|
||||||
})());
|
})());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should static analyze dynamic import variable destructuring assignment", async () => {
|
||||||
|
await import("../statical-dynamic-import/dir1/a?3").then(m => {
|
||||||
|
const { default: def, usedExports } = m;
|
||||||
|
expect(def).toBe(3);
|
||||||
|
expect(usedExports).toEqual(["default", "usedExports"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -73,9 +73,9 @@ it("should walk with correct order", async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should analyze arguments in call member chain", async () => {
|
it("should analyze arguments in call member chain", async () => {
|
||||||
import("../statical-dynamic-import/dir4/lib?2").then(m => {
|
await import("../statical-dynamic-import/dir4/lib?2").then(m => {
|
||||||
m.b.f((async () => {
|
m.b.f((async () => {
|
||||||
import("../statical-dynamic-import/dir4/a?2").then(m2 => {
|
await import("../statical-dynamic-import/dir4/a?2").then(m2 => {
|
||||||
expect(m2.a).toBe(1);
|
expect(m2.a).toBe(1);
|
||||||
expect(m2.usedExports).toEqual(["a", "usedExports"]);
|
expect(m2.usedExports).toEqual(["a", "usedExports"]);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue