Skip to content

Commit

Permalink
Merge pull request #16703 from ryanwilsonperkin/ryanwilsonperkin/fix-…
Browse files Browse the repository at this point in the history
…16160

Serialize generatedCode info to fix bug in asset module cache restoration
  • Loading branch information
TheLarkInn committed Mar 6, 2023
2 parents 4ba2252 + 4d561a6 commit b7fc4d8
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 5 deletions.
12 changes: 7 additions & 5 deletions lib/NormalModule.js
Expand Up @@ -330,6 +330,8 @@ class NormalModule extends Module {
this._isEvaluatingSideEffects = false;
/** @type {WeakSet<ModuleGraph> | undefined} */
this._addedSideEffectsBailout = undefined;
/** @type {Map<string, any>} */
this._codeGeneratorData = new Map();
}

/**
Expand Down Expand Up @@ -1188,11 +1190,9 @@ class NormalModule extends Module {
runtimeRequirements.add(RuntimeGlobals.thisAsExports);
}

/** @type {Map<string, any>} */
let data;
/** @type {function(): Map<string, any>} */
const getData = () => {
if (data === undefined) data = new Map();
return data;
return this._codeGeneratorData;
};

const sources = new Map();
Expand Down Expand Up @@ -1223,7 +1223,7 @@ class NormalModule extends Module {
const resultEntry = {
sources,
runtimeRequirements,
data
data: this._codeGeneratorData
};
return resultEntry;
}
Expand Down Expand Up @@ -1371,6 +1371,7 @@ class NormalModule extends Module {
write(this.error);
write(this._lastSuccessfulBuildMeta);
write(this._forceBuild);
write(this._codeGeneratorData);
super.serialize(context);
}

Expand Down Expand Up @@ -1403,6 +1404,7 @@ class NormalModule extends Module {
this.error = read();
this._lastSuccessfulBuildMeta = read();
this._forceBuild = read();
this._codeGeneratorData = read();
super.deserialize(context);
}
}
Expand Down
152 changes: 152 additions & 0 deletions test/Compiler-filesystem-caching.test.js
@@ -0,0 +1,152 @@
"use strict";

require("./helpers/warmup-webpack");

const path = require("path");
const fs = require("graceful-fs");
const rimraf = require("rimraf");

let fixtureCount = 0;

describe("Compiler (filesystem caching)", () => {
jest.setTimeout(5000);

const tempFixturePath = path.join(
__dirname,
"fixtures",
"temp-filesystem-cache-fixture"
);

function compile(entry, onSuccess, onError) {
const webpack = require("..");
const options = webpack.config.getNormalizedWebpackOptions({});
options.cache = {
type: "filesystem",
cacheDirectory: path.join(tempFixturePath, "cache")
};
options.entry = entry;
options.context = path.join(__dirname, "fixtures");
options.output.path = path.join(tempFixturePath, "dist");
options.output.filename = "bundle.js";
options.output.pathinfo = true;
options.module = {
rules: [
{
test: /\.svg$/,
type: "asset/resource",
use: {
loader: require.resolve("./fixtures/empty-svg-loader")
}
}
]
};

function runCompiler(onSuccess, onError) {
const c = webpack(options);
c.hooks.compilation.tap(
"CompilerCachingTest",
compilation => (compilation.bail = true)
);
c.run((err, stats) => {
if (err) throw err;
expect(typeof stats).toBe("object");
stats = stats.toJson({
modules: true,
reasons: true
});
expect(typeof stats).toBe("object");
expect(stats).toHaveProperty("errors");
expect(Array.isArray(stats.errors)).toBe(true);
if (stats.errors.length > 0) {
onError(new Error(JSON.stringify(stats.errors, null, 4)));
}
c.close(() => {
onSuccess(stats);
});
});
}

runCompiler(onSuccess, onError);

return {
runAgain: runCompiler
};
}

function cleanup() {
rimraf.sync(`${tempFixturePath}*`);
}

beforeAll(cleanup);
afterAll(cleanup);

function createTempFixture() {
const fixturePath = `${tempFixturePath}-${fixtureCount}`;
const usesAssetFilepath = path.join(fixturePath, "uses-asset.js");
const svgFilepath = path.join(fixturePath, "file.svg");

// Remove previous copy if present
rimraf.sync(fixturePath);

// Copy over file since we"ll be modifying some of them
fs.mkdirSync(fixturePath);
fs.copyFileSync(
path.join(__dirname, "fixtures", "uses-asset.js"),
usesAssetFilepath
);
fs.copyFileSync(path.join(__dirname, "fixtures", "file.svg"), svgFilepath);

fixtureCount++;
return {
rootPath: fixturePath,
usesAssetFilepath: usesAssetFilepath,
svgFilepath: svgFilepath
};
}

it("should compile again when cached asset has changed but loader output remains the same", done => {
const tempFixture = createTempFixture();

const onError = error => done(error);

const helper = compile(
tempFixture.usesAssetFilepath,
stats => {
// Not cached the first time
expect(stats.assets[0].name).toBe("bundle.js");
expect(stats.assets[0].emitted).toBe(true);

expect(stats.assets[1].name).toMatch(/\w+\.svg$/);
expect(stats.assets[0].emitted).toBe(true);

helper.runAgain(stats => {
// Cached the second run
expect(stats.assets[0].name).toBe("bundle.js");
expect(stats.assets[0].emitted).toBe(false);

expect(stats.assets[1].name).toMatch(/\w+\.svg$/);
expect(stats.assets[0].emitted).toBe(false);

const svgContent = fs
.readFileSync(tempFixture.svgFilepath)
.toString()
.replace("icon-square-small", "icon-square-smaller");

fs.writeFileSync(tempFixture.svgFilepath, svgContent);

helper.runAgain(stats => {
// Still cached after file modification because loader always returns empty
expect(stats.assets[0].name).toBe("bundle.js");
expect(stats.assets[0].emitted).toBe(false);

expect(stats.assets[1].name).toMatch(/\w+\.svg$/);
expect(stats.assets[0].emitted).toBe(false);

done();
}, onError);
}, onError);
},
onError
);
});
});
1 change: 1 addition & 0 deletions test/fixtures/empty-svg-loader.js
@@ -0,0 +1 @@
module.exports = () => "<svg></svg>";
1 change: 1 addition & 0 deletions test/fixtures/file.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/fixtures/uses-asset.js
@@ -0,0 +1 @@
import SVG from './file.svg';

0 comments on commit b7fc4d8

Please sign in to comment.