From 3bee91dc61b10c70be1f38f114caf84f99caf866 Mon Sep 17 00:00:00 2001 From: Ivan Kopeykin Date: Mon, 4 Apr 2022 17:59:01 +0300 Subject: [PATCH] add tree-shaking to ProvidedDependency --- lib/dependencies/ProvidedDependency.js | 56 +++++++++++++++++-- test/configCases/plugins/provide-plugin/a.js | 2 + test/configCases/plugins/provide-plugin/b.js | 7 +++ .../plugins/provide-plugin/harmony2.js | 2 + .../plugins/provide-plugin/index.js | 5 ++ .../plugins/provide-plugin/webpack.config.js | 2 + 6 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 test/configCases/plugins/provide-plugin/a.js create mode 100644 test/configCases/plugins/provide-plugin/b.js create mode 100644 test/configCases/plugins/provide-plugin/harmony2.js diff --git a/lib/dependencies/ProvidedDependency.js b/lib/dependencies/ProvidedDependency.js index 0962ba76230..bce0d780fb3 100644 --- a/lib/dependencies/ProvidedDependency.js +++ b/lib/dependencies/ProvidedDependency.js @@ -5,19 +5,23 @@ "use strict"; +const Dependency = require("../Dependency"); const InitFragment = require("../InitFragment"); const makeSerializable = require("../util/makeSerializable"); const ModuleDependency = require("./ModuleDependency"); /** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */ /** @typedef {import("../ChunkGraph")} ChunkGraph */ -/** @typedef {import("../Dependency")} Dependency */ +/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */ /** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */ /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */ /** @typedef {import("../DependencyTemplates")} DependencyTemplates */ /** @typedef {import("../ModuleGraph")} ModuleGraph */ /** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */ /** @typedef {import("../util/Hash")} Hash */ +/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */ + +const idsSymbol = Symbol("ProvidedDependency.ids"); /** * @param {string[]|null} path the property path array @@ -29,10 +33,16 @@ const pathToString = path => : ""; class ProvidedDependency extends ModuleDependency { - constructor(request, identifier, path, range) { + /** + * @param {string} request request + * @param {string} identifier identifier + * @param {string[]} ids ids + * @param {[number, number]} range range + */ + constructor(request, identifier, ids, range) { super(request); this.identifier = identifier; - this.path = path; + this.ids = ids; this.range = range; this._hashUpdate = undefined; } @@ -45,6 +55,38 @@ class ProvidedDependency extends ModuleDependency { return "esm"; } + /** + * @param {ModuleGraph} moduleGraph the module graph + * @returns {string[]} the imported ids + */ + getIds(moduleGraph) { + const meta = moduleGraph.getMetaIfExisting(this); + if (meta === undefined) return this.ids; + const ids = meta[idsSymbol]; + return ids !== undefined ? ids : this.ids; + } + + /** + * @param {ModuleGraph} moduleGraph the module graph + * @param {string[]} ids the imported ids + * @returns {void} + */ + setIds(moduleGraph, ids) { + moduleGraph.getMeta(this)[idsSymbol] = ids; + } + + /** + * Returns list of exports referenced by this dependency + * @param {ModuleGraph} moduleGraph module graph + * @param {RuntimeSpec} runtime the runtime for which the module is analysed + * @returns {(string[] | ReferencedExport)[]} referenced exports + */ + getReferencedExports(moduleGraph, runtime) { + let ids = this.getIds(moduleGraph); + if (ids.length === 0) return Dependency.EXPORTS_OBJECT_REFERENCED; + return [ids]; + } + /** * Update the hash * @param {Hash} hash hash to be updated @@ -63,6 +105,7 @@ class ProvidedDependency extends ModuleDependency { const { write } = context; write(this.identifier); write(this.path); + write(this.ids); super.serialize(context); } @@ -70,6 +113,7 @@ class ProvidedDependency extends ModuleDependency { const { read } = context; this.identifier = read(); this.path = read(); + this.ids = read(); super.deserialize(context); } } @@ -90,6 +134,7 @@ class ProvidedDependencyTemplate extends ModuleDependency.Template { dependency, source, { + runtime, runtimeTemplate, moduleGraph, chunkGraph, @@ -98,6 +143,9 @@ class ProvidedDependencyTemplate extends ModuleDependency.Template { } ) { const dep = /** @type {ProvidedDependency} */ (dependency); + const connection = moduleGraph.getConnection(dep); + const exportsInfo = moduleGraph.getExportsInfo(connection.module); + const usedName = exportsInfo.getUsedName(dep.getIds(moduleGraph), runtime); initFragments.push( new InitFragment( `/* provided dependency */ var ${ @@ -107,7 +155,7 @@ class ProvidedDependencyTemplate extends ModuleDependency.Template { chunkGraph, request: dep.request, runtimeRequirements - })}${pathToString(dep.path)};\n`, + })}${pathToString(/** @type {string[]} */ (usedName))};\n`, InitFragment.STAGE_PROVIDES, 1, `provided ${dep.identifier}` diff --git a/test/configCases/plugins/provide-plugin/a.js b/test/configCases/plugins/provide-plugin/a.js new file mode 100644 index 00000000000..f8297ed707d --- /dev/null +++ b/test/configCases/plugins/provide-plugin/a.js @@ -0,0 +1,2 @@ +export * as c from "./b"; +export * as c2 from "./harmony2"; diff --git a/test/configCases/plugins/provide-plugin/b.js b/test/configCases/plugins/provide-plugin/b.js new file mode 100644 index 00000000000..64bcdcfb6b8 --- /dev/null +++ b/test/configCases/plugins/provide-plugin/b.js @@ -0,0 +1,7 @@ +export function square(x) { + return x * x; +} + +export function cube(x) { + return x * x * x; +} diff --git a/test/configCases/plugins/provide-plugin/harmony2.js b/test/configCases/plugins/provide-plugin/harmony2.js new file mode 100644 index 00000000000..cabd2fbbb5f --- /dev/null +++ b/test/configCases/plugins/provide-plugin/harmony2.js @@ -0,0 +1,2 @@ +export const a = 1; +export const aUsed = __webpack_exports_info__.a.used; diff --git a/test/configCases/plugins/provide-plugin/index.js b/test/configCases/plugins/provide-plugin/index.js index 976ac6a2ce6..989d9ff0692 100644 --- a/test/configCases/plugins/provide-plugin/index.js +++ b/test/configCases/plugins/provide-plugin/index.js @@ -48,6 +48,11 @@ it("should provide a module for a property request", function() { expect(x).toBe("fff"); }); +it("should tree-shake unused exports", function() { + expect(aa1(2)).toBe(8); + expect(es2015_aUsed).toBe(false); +}); + it("should provide ES2015 modules", function() { expect((es2015.default)).toBe("ECMAScript 2015"); expect((es2015.alias)).toBe("ECMAScript Harmony"); diff --git a/test/configCases/plugins/provide-plugin/webpack.config.js b/test/configCases/plugins/provide-plugin/webpack.config.js index 508bb2c5719..d51e6549adf 100644 --- a/test/configCases/plugins/provide-plugin/webpack.config.js +++ b/test/configCases/plugins/provide-plugin/webpack.config.js @@ -6,6 +6,8 @@ module.exports = { aaa: "./aaa", "bbb.ccc": "./bbbccc", dddeeefff: ["./ddd", "eee", "3-f"], + aa1: ["./a", "c", "cube"], + es2015_aUsed: ["./harmony2", "aUsed"], "process.env.NODE_ENV": "./env", es2015: "./harmony", es2015_name: ["./harmony", "default"],