webpack/test/PersistentCaching.test.js

283 lines
7.0 KiB
JavaScript
Raw Normal View History

"use strict";
require("./helpers/warmup-webpack");
2025-07-03 17:06:45 +08:00
const fs = require("fs");
2020-01-28 15:42:49 +08:00
const path = require("path");
const util = require("util");
const vm = require("vm");
2025-07-03 17:06:45 +08:00
const rimraf = require("rimraf");
2020-01-28 15:42:49 +08:00
const readdir = util.promisify(fs.readdir);
const writeFile = util.promisify(fs.writeFile);
const utimes = util.promisify(fs.utimes);
const mkdir = util.promisify(fs.mkdir);
describe("Persistent Caching", () => {
const tempPath = path.resolve(__dirname, "js", "persistent-caching");
const outputPath = path.resolve(tempPath, "output");
const cachePath = path.resolve(tempPath, "cache");
const srcPath = path.resolve(tempPath, "src");
const config = {
mode: "none",
context: tempPath,
cache: {
type: "filesystem",
buildDependencies: {
// avoid rechecking build dependencies
// for performance
// this is already covered by another test case
defaultWebpack: []
},
cacheLocation: cachePath
},
2024-02-29 15:31:48 +08:00
experiments: {
css: true
},
resolve: {
alias: {
"image.png": false,
"image1.png": false
}
},
2020-01-28 15:42:49 +08:00
target: "node",
output: {
2020-04-29 23:40:08 +08:00
library: { type: "commonjs-module", export: "default" },
2020-01-28 15:42:49 +08:00
path: outputPath
}
};
beforeEach((done) => {
2020-01-28 15:42:49 +08:00
rimraf(tempPath, done);
});
const updateSrc = async (data) => {
2020-01-28 15:42:49 +08:00
const ts = new Date(Date.now() - 10000);
await mkdir(srcPath, { recursive: true });
for (const key of Object.keys(data)) {
const p = path.resolve(srcPath, key);
await writeFile(p, data[key]);
await utimes(p, ts, ts);
}
};
2024-07-31 11:31:11 +08:00
const compile = async (configAdditions = {}) =>
new Promise((resolve, reject) => {
const webpack = require("../");
2021-07-05 18:33:56 +08:00
webpack(
{
...config,
...configAdditions,
cache: { ...config.cache, ...configAdditions.cache }
},
(err, stats) => {
if (err) return reject(err);
if (stats.hasErrors()) {
2021-07-05 18:33:56 +08:00
return reject(stats.toString({ preset: "errors-only" }));
}
2021-07-05 18:33:56 +08:00
resolve(stats);
}
);
2020-01-28 15:42:49 +08:00
});
const getCacheFileTimes = async () => {
const cacheFiles = (await readdir(cachePath)).sort();
return new Map(
cacheFiles.map((f) => [
f,
fs.statSync(path.join(cachePath, f)).mtime.toString()
])
);
};
2020-04-29 23:40:08 +08:00
const execute = () => {
const cache = {};
const require = (name) => {
2020-04-29 23:40:08 +08:00
if (cache[name]) return cache[name].exports;
if (!name.endsWith(".js")) name += ".js";
const p = path.resolve(outputPath, name);
const source = fs.readFileSync(p, "utf8");
2020-04-29 23:40:08 +08:00
const context = {};
const fn = vm.runInThisContext(
`(function(require, module, exports) { ${source} })`,
context,
{
filename: p
}
);
const m = { exports: {} };
cache[name] = m;
fn(require, m, m.exports);
return m.exports;
};
return require("./main");
2020-01-28 15:42:49 +08:00
};
2021-07-05 18:33:56 +08:00
it("should compile fine (warmup)", async () => {
const data = {
"index.js": `import file from "./file.js";
export default 40 + file;
`,
"file.js": "export default 2;"
};
await updateSrc(data);
await compile();
expect(execute()).toBe(42);
}, 100000);
2020-01-28 15:42:49 +08:00
it("should merge multiple small files", async () => {
const files = Array.from({ length: 30 }).map((_, i) => `file${i}.js`);
const data = {
"index.js": `
2024-02-29 15:31:48 +08:00
import * as style from "./style.modules.css";
2020-04-29 23:40:08 +08:00
2020-01-28 15:42:49 +08:00
${files.map((f, i) => `import f${i} from "./${f}";`).join("\n")}
export default ${files.map((_, i) => `f${i}`).join(" + ")};
2024-02-29 15:31:48 +08:00
export { style };
`,
"style.modules.css": `.class {
color: red;
background: url('image.png');
}`
2020-01-28 15:42:49 +08:00
};
for (const file of files) {
2024-07-31 12:23:44 +08:00
data[file] = "export default 1;";
2020-01-28 15:42:49 +08:00
}
await updateSrc(data);
2021-07-05 18:33:56 +08:00
await compile({ cache: { compression: false } });
2020-04-29 23:40:08 +08:00
expect(execute()).toBe(30);
2020-01-28 15:42:49 +08:00
for (let i = 0; i < 30; i++) {
updateSrc({
2024-07-31 12:23:44 +08:00
[files[i]]: "export default 2;",
2024-02-29 15:31:48 +08:00
"style.modules.css": `.class-${i} { color: red; background: url('image1.png'); }`
2020-01-28 15:42:49 +08:00
});
2021-07-05 18:33:56 +08:00
await compile({ cache: { compression: false } });
2020-04-29 23:40:08 +08:00
expect(execute()).toBe(31 + i);
2020-01-28 15:42:49 +08:00
}
const cacheFiles = await readdir(cachePath);
expect(cacheFiles.length).toBeLessThan(20);
expect(cacheFiles.length).toBeGreaterThan(10);
2021-07-05 18:33:56 +08:00
}, 120000);
2020-04-16 15:37:11 +08:00
it("should optimize unused content", async () => {
const data = {
"a.js": 'import "react-dom";',
"b.js": 'import "acorn";',
"c.js": 'import "core-js";',
"d.js": 'import "date-fns";',
"e.js": 'import "lodash";'
};
2021-07-05 18:33:56 +08:00
await updateSrc(data);
const c = (items) => {
2020-04-16 15:37:11 +08:00
const entry = {};
2025-07-03 17:06:45 +08:00
for (const item of items) entry[item] = `./src/${item}.js`;
2021-07-05 18:33:56 +08:00
return compile({ entry, cache: { compression: false } });
2020-04-16 15:37:11 +08:00
};
2021-07-05 18:33:56 +08:00
await c("abcde");
await c("abc");
await c("cde");
await c("acd");
await c("bce");
await c("abcde");
2020-04-16 15:37:11 +08:00
const cacheFiles = await readdir(cachePath);
expect(cacheFiles.length).toBeGreaterThan(4);
2021-07-05 18:33:56 +08:00
}, 120000);
2020-04-29 23:40:08 +08:00
it("should allow persistent caching of container related objects", async () => {
const data = {
"index.js":
"export default import('container/src/exposed').then(m => m.default);",
2020-05-26 23:43:08 +08:00
"exposed.js": "import lib from 'lib'; export default 21 + lib;",
"lib.js": "export default 20",
"lib2.js": "export default 21"
2020-04-29 23:40:08 +08:00
};
await updateSrc(data);
const webpack = require("../");
2020-04-29 23:40:08 +08:00
const configAdditions = {
plugins: [
new webpack.container.ModuleFederationPlugin({
name: "container",
library: { type: "commonjs-module" },
exposes: ["./src/exposed"],
remotes: {
2020-05-26 23:43:08 +08:00
container: ["./no-container", "./container"]
},
shared: {
lib: {
import: "./src/lib",
shareKey: "lib",
version: "1.2.0",
2020-05-26 23:43:08 +08:00
requiredVersion: "^1.0.0"
},
"./src/lib2": {
shareKey: "lib",
version: "1.2.3"
2020-05-26 23:43:08 +08:00
}
}
2020-04-29 23:40:08 +08:00
})
]
};
await compile(configAdditions);
await expect(execute()).resolves.toBe(42);
await updateSrc({
"exposed.js": "module.exports = { ok: true };"
});
await compile(configAdditions);
await expect(execute()).resolves.toEqual({ ok: true });
2021-07-05 18:33:56 +08:00
}, 120000);
it("should not overwrite cache files if readonly = true", async () => {
await updateSrc({
"main.js": `
import { sum } from 'lodash';
sum([1,2,3])
`
});
2024-07-31 12:23:44 +08:00
await compile({ entry: "./src/main.js" });
const firstCacheFileTimes = await getCacheFileTimes();
await updateSrc({
"main.js": `
import 'lodash';
`
});
await compile({
2024-07-31 12:23:44 +08:00
entry: "./src/main.js",
cache: {
...config.cache,
readonly: true
}
});
await expect(getCacheFileTimes()).resolves.toEqual(firstCacheFileTimes);
}, 20000);
it("should not invalidate cache files if timestamps changed with dynamic import()", async () => {
const configAdditions = {
entry: "./src/main.js",
snapshot: {
resolve: { hash: true },
module: { hash: true },
contextModule: { hash: true }
}
};
await updateSrc({
"newer.js": "export default 2;",
// eslint-disable-next-line no-template-curly-in-string
"main.js": 'const f = "newer.js"; import(`./${f}`);'
});
await compile(configAdditions);
const firstCacheFileTimes = await getCacheFileTimes();
await utimes(path.resolve(srcPath, "newer.js"), new Date(), new Date());
await compile(configAdditions);
await expect(getCacheFileTimes()).resolves.toEqual(firstCacheFileTimes);
}, 20000);
2020-07-08 17:11:17 +08:00
});