fix: tree-shaking unused `RawModule` (#19671)

This commit is contained in:
Qingyu Wang 2025-07-10 21:23:05 +08:00 committed by GitHub
parent 24b1ec7611
commit 34df7eec7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 62 additions and 5 deletions

View File

@ -85,9 +85,11 @@ const memoize = require("./util/memoize");
const TRANSITIVE = Symbol("transitive");
const getIgnoredModule = memoize(
() => new RawModule("/* (ignored) */", "ignored", "(ignored)")
);
const getIgnoredModule = memoize(() => {
const module = new RawModule("/* (ignored) */", "ignored", "(ignored)");
module.factoryMeta = { sideEffectFree: true };
return module;
});
class Dependency {
constructor() {

View File

@ -80,11 +80,14 @@ class IgnorePlugin {
resolveData.dependencies.length > 0 &&
resolveData.dependencies[0] instanceof EntryDependency
) {
resolveData.ignoredModule = new RawModule(
const module = new RawModule(
"",
"ignored-entry-module",
"(ignored-entry-module)"
);
module.factoryMeta = { sideEffectFree: true };
resolveData.ignoredModule = module;
}
return result;

View File

@ -24,6 +24,8 @@ const makeSerializable = require("./util/makeSerializable");
/** @typedef {import("./Module").NeedBuildCallback} NeedBuildCallback */
/** @typedef {import("./Module").NeedBuildContext} NeedBuildContext */
/** @typedef {import("./Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */
/** @typedef {import("./ModuleGraph")} ModuleGraph */
/** @typedef {import("./ModuleGraphConnection").ConnectionState} ConnectionState */
/** @typedef {import("./RequestShortener")} RequestShortener */
/** @typedef {import("./ResolverFactory").ResolverWithOptions} ResolverWithOptions */
/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
@ -105,6 +107,18 @@ class RawModule extends Module {
callback();
}
/**
* @param {ModuleGraph} moduleGraph the module graph
* @returns {ConnectionState} how this module should be connected to referencing modules when consumed for side-effects only
*/
getSideEffectsConnectionState(moduleGraph) {
if (this.factoryMeta !== undefined) {
if (this.factoryMeta.sideEffectFree) return false;
if (this.factoryMeta.sideEffectFree === false) return true;
}
return true;
}
/**
* @param {CodeGenerationContext} context context for code generation
* @returns {CodeGenerationResult} result

View File

@ -61,11 +61,13 @@ class ModuleDependency extends Dependency {
* @returns {Module} ignored module
*/
createIgnoredModule(context) {
return new RawModule(
const module = new RawModule(
"/* (ignored) */",
`ignored|${context}|${this.request}`,
`${this.request} (ignored)`
);
module.factoryMeta = { sideEffectFree: true };
return module;
}
/**

View File

@ -0,0 +1 @@
module.exports = "ignored";

View File

@ -0,0 +1 @@
export default "a";

View File

@ -0,0 +1 @@
export default "b";

View File

@ -0,0 +1,15 @@
"use strict";
import "ignored-module";
import "./ignored-module";
it("should remove all ignored modules", async function() {
// Current module + module with runtime code to load context modules
expect(Object.keys(__webpack_modules__)).toHaveLength(2);
const x = "a";
const locale = (await import("./locales/" + x)).default;
expect(locale).toBe("a");
expect(Object.keys(__webpack_modules__)).toHaveLength(3);
});

View File

@ -0,0 +1,18 @@
"use strict";
const webpack = require("../../../../");
/** @type {import("../../../../").Configuration} */
module.exports = {
entry: "./test.js",
resolve: {
alias: {
"ignored-module": false,
"./ignored-module": false
}
},
plugins: [new webpack.IgnorePlugin({ resourceRegExp: /(b\.js|b)$/ })],
optimization: {
sideEffects: true
}
};