Skip to content

Commit

Permalink
Merge pull request #13113 from webpack/bugfix/define-caching
Browse files Browse the repository at this point in the history
DefinePlugin invalidates modules when new defines are added
  • Loading branch information
sokra committed Apr 12, 2021
2 parents 2a4ecc8 + 33b44a0 commit 20072ba
Show file tree
Hide file tree
Showing 13 changed files with 135 additions and 13 deletions.
2 changes: 1 addition & 1 deletion lib/Compilation.js
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
if (compiler.contextTimestamps) {
this.fileSystemInfo.addContextTimestamps(compiler.contextTimestamps);
}
/** @type {Map<string, string>} */
/** @type {Map<string, string | Set<string>>} */
this.valueCacheVersions = new Map();
this.requestShortener = compiler.requestShortener;
this.compilerPath = compiler.compilerPath;
Expand Down
26 changes: 21 additions & 5 deletions lib/DefinePlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const {
evaluateToString,
toConstantDependency
} = require("./javascript/JavascriptParserHelpers");
const { provide } = require("./util/MapHelpers");

/** @typedef {import("estree").Expression} Expression */
/** @typedef {import("./Compiler")} Compiler */
Expand Down Expand Up @@ -53,7 +54,7 @@ class RuntimeValue {

/**
* @param {JavascriptParser} parser the parser
* @param {Map<string, string>} valueCacheVersions valueCacheVersions
* @param {Map<string, string | Set<string>>} valueCacheVersions valueCacheVersions
* @param {string} key the defined key
* @returns {CodeValuePrimitive} code
*/
Expand Down Expand Up @@ -88,7 +89,9 @@ class RuntimeValue {
module: parser.state.module,
key,
get version() {
return valueCacheVersions.get(VALUE_DEP_PREFIX + key);
return /** @type {string} */ (valueCacheVersions.get(
VALUE_DEP_PREFIX + key
));
}
});
}
Expand All @@ -105,7 +108,7 @@ class RuntimeValue {
/**
* @param {any[]|{[k: string]: any}} obj obj
* @param {JavascriptParser} parser Parser
* @param {Map<string, string>} valueCacheVersions valueCacheVersions
* @param {Map<string, string | Set<string>>} valueCacheVersions valueCacheVersions
* @param {string} key the defined key
* @param {RuntimeTemplate} runtimeTemplate the runtime template
* @param {boolean|undefined|null=} asiSafe asi safe (undefined: unknown, null: unneeded)
Expand Down Expand Up @@ -156,7 +159,7 @@ const stringifyObj = (
* Convert code to a string that evaluates
* @param {CodeValue} code Code to evaluate
* @param {JavascriptParser} parser Parser
* @param {Map<string, string>} valueCacheVersions valueCacheVersions
* @param {Map<string, string | Set<string>>} valueCacheVersions valueCacheVersions
* @param {string} key the defined key
* @param {RuntimeTemplate} runtimeTemplate the runtime template
* @param {boolean|undefined|null=} asiSafe asi safe (undefined: unknown, null: unneeded)
Expand Down Expand Up @@ -247,6 +250,7 @@ const toCacheVersion = code => {
};

const VALUE_DEP_PREFIX = "webpack/DefinePlugin ";
const VALUE_DEP_MAIN = "webpack/DefinePlugin";

class DefinePlugin {
/**
Expand Down Expand Up @@ -282,16 +286,27 @@ class DefinePlugin {
);
const { runtimeTemplate } = compilation;

const mainValue = /** @type {Set<string>} */ (provide(
compilation.valueCacheVersions,
VALUE_DEP_MAIN,
() => new Set()
));

/**
* Handler
* @param {JavascriptParser} parser Parser
* @returns {void}
*/
const handler = parser => {
const addValueDependency = key => {
parser.hooks.program.tap("DefinePlugin", () => {
const { buildInfo } = parser.state.module;
if (!buildInfo.valueDependencies)
buildInfo.valueDependencies = new Map();
buildInfo.valueDependencies.set(VALUE_DEP_MAIN, mainValue);
});

const addValueDependency = key => {
const { buildInfo } = parser.state.module;
buildInfo.valueDependencies.set(
VALUE_DEP_PREFIX + key,
compilation.valueCacheVersions.get(VALUE_DEP_PREFIX + key)
Expand Down Expand Up @@ -546,6 +561,7 @@ class DefinePlugin {
const code = definitions[key];
const version = toCacheVersion(code);
const name = VALUE_DEP_PREFIX + prefix + key;
mainValue.add(name);
const oldVersion = compilation.valueCacheVersions.get(name);
if (oldVersion === undefined) {
compilation.valueCacheVersions.set(name, version);
Expand Down
2 changes: 1 addition & 1 deletion lib/Module.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const makeSerializable = require("./util/makeSerializable");
/**
* @typedef {Object} NeedBuildContext
* @property {FileSystemInfo} fileSystemInfo
* @property {Map<string, string>} valueCacheVersions
* @property {Map<string, string | Set<string>>} valueCacheVersions
*/

/** @typedef {KnownBuildMeta & Record<string, any>} BuildMeta */
Expand Down
17 changes: 14 additions & 3 deletions lib/NormalModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const UnhandledSchemeError = require("./UnhandledSchemeError");
const WebpackError = require("./WebpackError");
const formatLocation = require("./formatLocation");
const LazySet = require("./util/LazySet");
const { isSubset } = require("./util/SetHelpers");
const { getScheme } = require("./util/URLAbsoluteSpecifier");
const {
compareLocations,
Expand Down Expand Up @@ -1151,12 +1152,22 @@ class NormalModule extends Module {
if (!this.buildInfo.snapshot) return callback(null, true);

// build when valueDependencies have changed
if (this.buildInfo.valueDependencies) {
/** @type {Map<string, string | Set<string>>} */
const valueDependencies = this.buildInfo.valueDependencies;
if (valueDependencies) {
if (!valueCacheVersions) return callback(null, true);
for (const [key, value] of this.buildInfo.valueDependencies) {
for (const [key, value] of valueDependencies) {
if (value === undefined) return callback(null, true);
const current = valueCacheVersions.get(key);
if (value !== current) return callback(null, true);
if (
value !== current &&
(typeof value === "string" ||
typeof current === "string" ||
current === undefined ||
!isSubset(value, current))
) {
return callback(null, true);
}
}
}

Expand Down
1 change: 1 addition & 0 deletions test/watchCases/cache/add-defines/0/a.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default [DEFINE.A, RUN];
1 change: 1 addition & 0 deletions test/watchCases/cache/add-defines/0/b.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default [DEFINE.B, RUN];
1 change: 1 addition & 0 deletions test/watchCases/cache/add-defines/0/c.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default [DEFINE.C, RUN];
9 changes: 9 additions & 0 deletions test/watchCases/cache/add-defines/0/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import a from "./a";
import b from "./b";
import c from "./c";

it("should invalidate modules when properties are added/removed from the DefinePlugin", () => {
expect(a).toEqual([0, 0]);
expect(b).toEqual([2, 0]);
expect(c).toEqual([undefined, 0]);
});
9 changes: 9 additions & 0 deletions test/watchCases/cache/add-defines/1/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import a from "./a";
import b from "./b";
import c from "./c";

it("should invalidate modules when properties are added/removed from the DefinePlugin", () => {
expect(a).toEqual([1, 1]);
expect(b).toEqual([2, 0]);
expect(c).toEqual([undefined, 0]);
});
9 changes: 9 additions & 0 deletions test/watchCases/cache/add-defines/2/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import a from "./a";
import b from "./b";
import c from "./c";

it("should invalidate modules when properties are added/removed from the DefinePlugin", () => {
expect(a).toEqual([1, 2]);
expect(b).toEqual([2, 2]);
expect(c).toEqual([3, 2]);
});
9 changes: 9 additions & 0 deletions test/watchCases/cache/add-defines/3/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import a from "./a";
import b from "./b";
import c from "./c";

it("should invalidate modules when properties are added/removed from the DefinePlugin", () => {
expect(a).toEqual([1, 2]);
expect(b).toEqual([undefined, 3]);
expect(c).toEqual([3, 2]);
});
56 changes: 56 additions & 0 deletions test/watchCases/cache/add-defines/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const { DefinePlugin } = require("../../../../");

/** @type {import("../../../../").Configuration} */
module.exports = {
cache: {
type: "memory"
},
plugins: [
compiler => {
const base = {
DEFINE: "{}",
RUN: DefinePlugin.runtimeValue(() => 3 - defines.length, [])
};
const defines = [
{
...base,
"DEFINE.A": 0,
"DEFINE.B": 2
},
{
// change
...base,
"DEFINE.A": 1,
"DEFINE.B": 2
},
{
// add
...base,
"DEFINE.A": 1,
"DEFINE.B": 2,
"DEFINE.C": 3
},
{
// remove
...base,
"DEFINE.A": 1,
"DEFINE.C": 3
}
];
compiler.hooks.compilation.tap("webpack.config", (...args) => {
const plugin = new DefinePlugin(defines.shift());
plugin.apply(
/** @type {any} */ ({
hooks: {
compilation: {
tap: (name, fn) => {
fn(...args);
}
}
}
})
);
});
}
]
};
6 changes: 3 additions & 3 deletions types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1363,7 +1363,7 @@ declare class Compilation {
resolverFactory: ResolverFactory;
inputFileSystem: InputFileSystem;
fileSystemInfo: FileSystemInfo;
valueCacheVersions: Map<string, string>;
valueCacheVersions: Map<string, string | Set<string>>;
requestShortener: RequestShortener;
compilerPath: string;
logger: WebpackLogger;
Expand Down Expand Up @@ -6514,7 +6514,7 @@ declare class NaturalModuleIdsPlugin {
}
declare interface NeedBuildContext {
fileSystemInfo: FileSystemInfo;
valueCacheVersions: Map<string, string>;
valueCacheVersions: Map<string, string | Set<string>>;
}
declare class NoEmitOnErrorsPlugin {
constructor();
Expand Down Expand Up @@ -9714,7 +9714,7 @@ declare abstract class RuntimeValue {
readonly fileDependencies?: true | string[];
exec(
parser: JavascriptParser,
valueCacheVersions: Map<string, string>,
valueCacheVersions: Map<string, string | Set<string>>,
key: string
): CodeValuePrimitive;
getCacheVersion(): undefined | string;
Expand Down

0 comments on commit 20072ba

Please sign in to comment.