Skip to content

Commit b7fc4d8

Browse files
authored
Merge pull request #16703 from ryanwilsonperkin/ryanwilsonperkin/fix-16160
Serialize generatedCode info to fix bug in asset module cache restoration
2 parents 4ba2252 + 4d561a6 commit b7fc4d8

5 files changed

Lines changed: 162 additions & 5 deletions

File tree

lib/NormalModule.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@ class NormalModule extends Module {
330330
this._isEvaluatingSideEffects = false;
331331
/** @type {WeakSet<ModuleGraph> | undefined} */
332332
this._addedSideEffectsBailout = undefined;
333+
/** @type {Map<string, any>} */
334+
this._codeGeneratorData = new Map();
333335
}
334336

335337
/**
@@ -1188,11 +1190,9 @@ class NormalModule extends Module {
11881190
runtimeRequirements.add(RuntimeGlobals.thisAsExports);
11891191
}
11901192

1191-
/** @type {Map<string, any>} */
1192-
let data;
1193+
/** @type {function(): Map<string, any>} */
11931194
const getData = () => {
1194-
if (data === undefined) data = new Map();
1195-
return data;
1195+
return this._codeGeneratorData;
11961196
};
11971197

11981198
const sources = new Map();
@@ -1223,7 +1223,7 @@ class NormalModule extends Module {
12231223
const resultEntry = {
12241224
sources,
12251225
runtimeRequirements,
1226-
data
1226+
data: this._codeGeneratorData
12271227
};
12281228
return resultEntry;
12291229
}
@@ -1371,6 +1371,7 @@ class NormalModule extends Module {
13711371
write(this.error);
13721372
write(this._lastSuccessfulBuildMeta);
13731373
write(this._forceBuild);
1374+
write(this._codeGeneratorData);
13741375
super.serialize(context);
13751376
}
13761377

@@ -1403,6 +1404,7 @@ class NormalModule extends Module {
14031404
this.error = read();
14041405
this._lastSuccessfulBuildMeta = read();
14051406
this._forceBuild = read();
1407+
this._codeGeneratorData = read();
14061408
super.deserialize(context);
14071409
}
14081410
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
"use strict";
2+
3+
require("./helpers/warmup-webpack");
4+
5+
const path = require("path");
6+
const fs = require("graceful-fs");
7+
const rimraf = require("rimraf");
8+
9+
let fixtureCount = 0;
10+
11+
describe("Compiler (filesystem caching)", () => {
12+
jest.setTimeout(5000);
13+
14+
const tempFixturePath = path.join(
15+
__dirname,
16+
"fixtures",
17+
"temp-filesystem-cache-fixture"
18+
);
19+
20+
function compile(entry, onSuccess, onError) {
21+
const webpack = require("..");
22+
const options = webpack.config.getNormalizedWebpackOptions({});
23+
options.cache = {
24+
type: "filesystem",
25+
cacheDirectory: path.join(tempFixturePath, "cache")
26+
};
27+
options.entry = entry;
28+
options.context = path.join(__dirname, "fixtures");
29+
options.output.path = path.join(tempFixturePath, "dist");
30+
options.output.filename = "bundle.js";
31+
options.output.pathinfo = true;
32+
options.module = {
33+
rules: [
34+
{
35+
test: /\.svg$/,
36+
type: "asset/resource",
37+
use: {
38+
loader: require.resolve("./fixtures/empty-svg-loader")
39+
}
40+
}
41+
]
42+
};
43+
44+
function runCompiler(onSuccess, onError) {
45+
const c = webpack(options);
46+
c.hooks.compilation.tap(
47+
"CompilerCachingTest",
48+
compilation => (compilation.bail = true)
49+
);
50+
c.run((err, stats) => {
51+
if (err) throw err;
52+
expect(typeof stats).toBe("object");
53+
stats = stats.toJson({
54+
modules: true,
55+
reasons: true
56+
});
57+
expect(typeof stats).toBe("object");
58+
expect(stats).toHaveProperty("errors");
59+
expect(Array.isArray(stats.errors)).toBe(true);
60+
if (stats.errors.length > 0) {
61+
onError(new Error(JSON.stringify(stats.errors, null, 4)));
62+
}
63+
c.close(() => {
64+
onSuccess(stats);
65+
});
66+
});
67+
}
68+
69+
runCompiler(onSuccess, onError);
70+
71+
return {
72+
runAgain: runCompiler
73+
};
74+
}
75+
76+
function cleanup() {
77+
rimraf.sync(`${tempFixturePath}*`);
78+
}
79+
80+
beforeAll(cleanup);
81+
afterAll(cleanup);
82+
83+
function createTempFixture() {
84+
const fixturePath = `${tempFixturePath}-${fixtureCount}`;
85+
const usesAssetFilepath = path.join(fixturePath, "uses-asset.js");
86+
const svgFilepath = path.join(fixturePath, "file.svg");
87+
88+
// Remove previous copy if present
89+
rimraf.sync(fixturePath);
90+
91+
// Copy over file since we"ll be modifying some of them
92+
fs.mkdirSync(fixturePath);
93+
fs.copyFileSync(
94+
path.join(__dirname, "fixtures", "uses-asset.js"),
95+
usesAssetFilepath
96+
);
97+
fs.copyFileSync(path.join(__dirname, "fixtures", "file.svg"), svgFilepath);
98+
99+
fixtureCount++;
100+
return {
101+
rootPath: fixturePath,
102+
usesAssetFilepath: usesAssetFilepath,
103+
svgFilepath: svgFilepath
104+
};
105+
}
106+
107+
it("should compile again when cached asset has changed but loader output remains the same", done => {
108+
const tempFixture = createTempFixture();
109+
110+
const onError = error => done(error);
111+
112+
const helper = compile(
113+
tempFixture.usesAssetFilepath,
114+
stats => {
115+
// Not cached the first time
116+
expect(stats.assets[0].name).toBe("bundle.js");
117+
expect(stats.assets[0].emitted).toBe(true);
118+
119+
expect(stats.assets[1].name).toMatch(/\w+\.svg$/);
120+
expect(stats.assets[0].emitted).toBe(true);
121+
122+
helper.runAgain(stats => {
123+
// Cached the second run
124+
expect(stats.assets[0].name).toBe("bundle.js");
125+
expect(stats.assets[0].emitted).toBe(false);
126+
127+
expect(stats.assets[1].name).toMatch(/\w+\.svg$/);
128+
expect(stats.assets[0].emitted).toBe(false);
129+
130+
const svgContent = fs
131+
.readFileSync(tempFixture.svgFilepath)
132+
.toString()
133+
.replace("icon-square-small", "icon-square-smaller");
134+
135+
fs.writeFileSync(tempFixture.svgFilepath, svgContent);
136+
137+
helper.runAgain(stats => {
138+
// Still cached after file modification because loader always returns empty
139+
expect(stats.assets[0].name).toBe("bundle.js");
140+
expect(stats.assets[0].emitted).toBe(false);
141+
142+
expect(stats.assets[1].name).toMatch(/\w+\.svg$/);
143+
expect(stats.assets[0].emitted).toBe(false);
144+
145+
done();
146+
}, onError);
147+
}, onError);
148+
},
149+
onError
150+
);
151+
});
152+
});

test/fixtures/empty-svg-loader.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = () => "<svg></svg>";

test/fixtures/file.svg

Lines changed: 1 addition & 0 deletions
Loading

test/fixtures/uses-asset.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import SVG from './file.svg';

0 commit comments

Comments
 (0)