/
githubmirror
/
webpack
Обзор
Документация
Войти
/
githubmirror
/
webpack
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v5.100.2
test/FileSystemInfo.unittest.js
503 строки
15 KB
Alexander Akait
chore: eslint more rules (#19648)
03 июл 2025, 12:06
Не верифицирован
03 июл 2025, 12:06
87f648e
Код
Авторство
О чём код?
"use strict"; const util = require("util"); const { Volume, createFsFromVolume } = require("memfs"); const FileSystemInfo = require("../lib/FileSystemInfo"); const { buffersSerializer } = require("../lib/util/serialization"); describe("FileSystemInfo", () => { afterEach(() => { // restore the spy created with spyOn jest.restoreAllMocks(); }); const files = [ "/path/file.txt", "/path/nested/deep/file.txt", "/path/nested/deep/ignored.txt", "/path/context+files/file.txt", "/path/context+files/sub/file.txt", "/path/context+files/sub/ignored.txt", "/path/node_modules/package/file.txt", "/path/cache/package-1234/file.txt", "/path/circular/circular/file2.txt", "/path/nested/deep/symlink/file.txt", "/path/context+files/sub/symlink/file.txt", "/path/context/sub/symlink/file.txt", "/path/missing.txt", "/path/node_modules/@foo/package1/index.js", "/path/node_modules/@foo/package2/index.js", "/path/node_modules/bar-package3/index.js" ]; const directories = [ "/path/context+files", "/path/context", "/path/missing", "/path/node_modules/package", "/path/node_modules/missing", "/path/node_modules/@foo", "/path/node_modules/@foo/package1", "/path/node_modules/@foo/package2", "/path/node_modules/bar-package3", "/path/cache/package-1234", "/path/cache/package-missing" ]; const missing = [ "/path/package.json", "/path/file2.txt", "/path/context+files/file2.txt", "/path/node_modules/package.txt", "/path/node_modules/package/missing.txt", "/path/cache/package-2345", "/path/cache/package-1234/missing.txt", "/path/ignored.txt" ]; const ignored = [ "/path/nested/deep/ignored.txt", "/path/context+files/sub/ignored.txt", "/path/context/sub/ignored.txt", "/path/ignored.txt", "/path/node_modules/package/ignored.txt", "/path/cache/package-1234/ignored.txt" ]; const unmanagedPaths = [ "/path/node_modules/@foo/package1", "/path/node_modules/@foo/package2", "/path/node_modules/bar-package3" ]; const managedPaths = ["/path/node_modules"]; const immutablePaths = ["/path/cache"]; const createFs = () => { const fs = createFsFromVolume(new Volume()); fs.mkdirSync("/path/context+files/sub", { recursive: true }); fs.mkdirSync("/path/context/sub", { recursive: true }); fs.mkdirSync("/path/nested/deep", { recursive: true }); fs.mkdirSync("/path/node_modules/package", { recursive: true }); fs.mkdirSync("/path/node_modules/@foo", { recursive: true }); fs.mkdirSync("/path/node_modules/@foo/package1", { recursive: true }); fs.mkdirSync("/path/node_modules/@foo/package2", { recursive: true }); fs.mkdirSync("/path/node_modules/bar-package3", { recursive: true }); fs.mkdirSync("/path/cache/package-1234", { recursive: true }); fs.mkdirSync("/path/folder/context", { recursive: true }); fs.mkdirSync("/path/folder/context+files", { recursive: true }); fs.mkdirSync("/path/folder/nested", { recursive: true }); fs.writeFileSync("/path/file.txt", "Hello World"); fs.writeFileSync("/path/file2.txt", "Hello World2"); fs.writeFileSync("/path/nested/deep/file.txt", "Hello World"); fs.writeFileSync("/path/nested/deep/ignored.txt", "Ignored"); fs.writeFileSync("/path/context+files/file.txt", "Hello World"); fs.writeFileSync("/path/context+files/file2.txt", "Hello World2"); fs.writeFileSync("/path/context+files/sub/file.txt", "Hello World"); fs.writeFileSync("/path/context+files/sub/file2.txt", "Hello World2"); fs.writeFileSync("/path/context+files/sub/file3.txt", "Hello World3"); fs.writeFileSync("/path/context+files/sub/ignored.txt", "Ignored"); fs.writeFileSync("/path/context/file.txt", "Hello World"); fs.writeFileSync("/path/context/file2.txt", "Hello World2"); fs.writeFileSync("/path/context/sub/file.txt", "Hello World"); fs.writeFileSync("/path/context/sub/file2.txt", "Hello World2"); fs.writeFileSync("/path/context/sub/file3.txt", "Hello World3"); fs.writeFileSync("/path/context/sub/ignored.txt", "Ignored"); fs.writeFileSync( "/path/node_modules/package/package.json", JSON.stringify({ name: "package", version: "1.0.0" }) ); fs.writeFileSync("/path/node_modules/package/file.txt", "Hello World"); fs.writeFileSync("/path/node_modules/package/ignored.txt", "Ignored"); fs.writeFileSync( "/path/cache/package-1234/package.json", JSON.stringify({ name: "package", version: "1.0.0" }) ); fs.writeFileSync("/path/cache/package-1234/file.txt", "Hello World"); fs.writeFileSync("/path/cache/package-1234/ignored.txt", "Ignored"); fs.symlinkSync("/path", "/path/circular", "dir"); fs.writeFileSync("/path/folder/context/file.txt", "Hello World"); fs.writeFileSync("/path/folder/context+files/file.txt", "Hello World"); fs.writeFileSync("/path/folder/nested/file.txt", "Hello World"); fs.writeFileSync( "/path/node_modules/@foo/package1/index.js", "Hello World" ); fs.writeFileSync( "/path/node_modules/@foo/package2/index.js", "Hello World" ); fs.writeFileSync("/path/node_modules/bar-package3/index.js", "Hello World"); fs.symlinkSync("/path/folder/context", "/path/context/sub/symlink", "dir"); fs.symlinkSync( "/path/folder/context+files", "/path/context+files/sub/symlink", "dir" ); fs.symlinkSync("/path/folder/nested", "/path/nested/deep/symlink", "dir"); return fs; }; const createFsInfo = fs => { const logger = { error: (...args) => { throw new Error(util.format(...args)); } }; const fsInfo = new FileSystemInfo(fs, { logger, unmanagedPaths, managedPaths, immutablePaths, hashFunction: "sha256" }); for (const method of ["warn", "info", "log", "debug"]) { fsInfo.logs = []; fsInfo[method] = []; logger[method] = (...args) => { const msg = util.format(...args); fsInfo[method].push(msg); fsInfo.logs.push(`[${method}] ${msg}`); }; } fsInfo.addFileTimestamps(new Map(ignored.map(i => [i, "ignore"]))); return fsInfo; }; const createSnapshot = (fs, options, callback) => { const fsInfo = createFsInfo(fs); fsInfo.createSnapshot( Date.now() + 10000, files, directories, missing, options, (err, snapshot) => { if (err) return callback(err); snapshot.name = "initial snapshot"; // create another one to test the caching fsInfo.createSnapshot( Date.now() + 10000, files, directories, missing, options, (err, snapshot2) => { if (err) return callback(err); snapshot2.name = "cached snapshot"; callback(null, snapshot, snapshot2); } ); } ); }; const clone = object => { const serialized = buffersSerializer.serialize(object, {}); return buffersSerializer.deserialize(serialized, {}); }; const expectSnapshotsState = ( fs, snapshot, snapshot2, expected, callback ) => { expectSnapshotState(fs, snapshot, expected, err => { if (err) return callback(err); if (!snapshot2) return callback(); expectSnapshotState(fs, snapshot2, expected, callback); }); }; const expectSnapshotState = (fs, snapshot, expected, callback) => { const fsInfo = createFsInfo(fs); const details = snapshot => `${fsInfo.logs.join("\n")} ${util.inspect(snapshot, false, Infinity, true)}`; fsInfo.checkSnapshotValid(snapshot, (err, valid) => { if (err) return callback(err); if (valid !== expected) { return callback( new Error(`Expected snapshot to be ${ expected ? "valid" : "invalid" } but it is ${valid ? "valid" : "invalid"}: ${details(snapshot)}`) ); } // Another try to check if direct caching works fsInfo.checkSnapshotValid(snapshot, (err, valid) => { if (err) return callback(err); if (valid !== expected) { return callback( new Error(`Expected snapshot lead to the same result when directly cached: ${details(snapshot)}`) ); } // Another try to check if indirect caching works fsInfo.checkSnapshotValid(clone(snapshot), (err, valid) => { if (err) return callback(err); if (valid !== expected) { return callback( new Error(`Expected snapshot lead to the same result when indirectly cached: ${details(snapshot)}`) ); } callback(); }); }); }); }; const updateFile = (fs, filename) => { const oldContent = fs.readFileSync(filename, "utf8"); if (filename.endsWith(".json")) { const data = JSON.parse(oldContent); fs.writeFileSync( filename, JSON.stringify({ ...data, version: `${data.version}.1` }) ); } else { fs.writeFileSync(filename, `${oldContent}!`); } }; for (const [name, options] of [ ["timestamp", { timestamp: true }], ["hash", { hash: true }], ["tsh", { timestamp: true, hash: true }] ]) { describe(`${name} mode`, () => { it("should always accept an empty snapshot", done => { const fs = createFs(); const fsInfo = createFsInfo(fs); fsInfo.createSnapshot( Date.now() + 10000, [], [], [], options, (err, snapshot) => { if (err) return done(err); const fs = createFs(); expectSnapshotState(fs, snapshot, true, done); } ); }); it("should accept a snapshot when fs is unchanged", done => { const fs = createFs(); createSnapshot(fs, options, (err, snapshot, snapshot2) => { if (err) return done(err); expectSnapshotsState(fs, snapshot, snapshot2, true, done); }); }); const ignoredFileChanges = [ "/path/nested/deep/ignored.txt", "/path/context+files/sub/ignored.txt" ]; for (const fileChange of [ "/path/file.txt", "/path/file2.txt", "/path/nested/deep/file.txt", "/path/context+files/file.txt", "/path/context+files/file2.txt", "/path/context+files/sub/file.txt", "/path/context+files/sub/file2.txt", "/path/context+files/sub/file3.txt", "/path/context/file.txt", "/path/context/file2.txt", "/path/context/sub/file.txt", "/path/context/sub/file2.txt", "/path/context/sub/file3.txt", "/path/node_modules/package/package.json", "/path/folder/context/file.txt", "/path/folder/context+files/file.txt", "/path/folder/nested/file.txt", "/path/node_modules/@foo/package1/index.js", "/path/node_modules/@foo/package2/index.js", "/path/node_modules/bar-package3/index.js", ...(name !== "timestamp" ? ignoredFileChanges : []), ...(name === "hash" ? ["/path/context/sub/ignored.txt"] : []) ]) { it(`should invalidate the snapshot when ${fileChange} is changed`, done => { const fs = createFs(); createSnapshot(fs, options, (err, snapshot, snapshot2) => { if (err) return done(err); updateFile(fs, fileChange); expectSnapshotsState(fs, snapshot, snapshot2, false, done); }); }); } for (const fileChange of [ "/path/node_modules/package/file.txt", "/path/node_modules/package/ignored.txt", "/path/cache/package-1234/package.json", "/path/cache/package-1234/file.txt", "/path/cache/package-1234/ignored.txt", ...(name === "timestamp" ? ignoredFileChanges : []), ...(name !== "hash" ? ["/path/context/sub/ignored.txt"] : []) ]) { it(`should not invalidate the snapshot when ${fileChange} is changed`, done => { const fs = createFs(); createSnapshot(fs, options, (err, snapshot, snapshot2) => { if (err) return done(err); updateFile(fs, fileChange); expectSnapshotsState(fs, snapshot, snapshot2, true, done); }); }); } for (const newFile of [ "/path/package.json", "/path/file2.txt", "/path/context+files/file2.txt", "/path/node_modules/package.txt" ]) { it(`should invalidate the snapshot when ${newFile} is created`, done => { const fs = createFs(); createSnapshot(fs, options, (err, snapshot, snapshot2) => { if (err) return done(err); fs.writeFileSync(newFile, "New file"); expectSnapshotsState(fs, snapshot, snapshot2, false, done); }); }); } for (const newFile of [ "/path/node_modules/package/missing.txt", "/path/cache/package-1234/missing.txt", "/path/cache/package-2345", "/path/ignored.txt" ]) { it(`should not invalidate the snapshot when ${newFile} is created`, done => { const fs = createFs(); createSnapshot(fs, options, (err, snapshot, snapshot2) => { if (err) return done(err); fs.writeFileSync(newFile, "New file"); expectSnapshotsState(fs, snapshot, snapshot2, true, done); }); }); } if (name !== "timestamp") { it("should not invalidate snapshot when only timestamps have changed", done => { const fs = createFs(); createSnapshot(fs, options, (err, snapshot, snapshot2) => { if (err) return done(err); const fs = createFs(); expectSnapshotsState(fs, snapshot, snapshot2, true, done); }); }); } }); } describe("stable iterables identity", () => { const options = { timestamp: true }; /** * @param {(err?: WebpackError | null, snapshot?: Snapshot | null) => void} callback callback function */ function getSnapshot(callback) { const fs = createFs(); const fsInfo = createFsInfo(fs); fsInfo.createSnapshot( Date.now() + 10000, files, directories, missing, options, callback ); } it("should return same iterable for getFileIterable()", done => { getSnapshot((err, snapshot) => { if (err) done(err); expect(snapshot.getFileIterable()).toEqual(snapshot.getFileIterable()); done(); }); }); it("should return same iterable for getContextIterable()", done => { getSnapshot((err, snapshot) => { if (err) done(err); expect(snapshot.getContextIterable()).toEqual( snapshot.getContextIterable() ); done(); }); }); it("should return same iterable for getMissingIterable()", done => { getSnapshot((err, snapshot) => { if (err) done(err); expect(snapshot.getFileIterable()).toEqual(snapshot.getFileIterable()); done(); }); }); }); describe("symlinks", () => { it("should work with symlinks with errors", done => { const fs = createFs(); fs.symlinkSync( "/path/folder/context", "/path/context/sub/symlink-error", "dir" ); const originalReadlink = fs.readlink; let i = 0; jest.spyOn(fs, "readlink").mockImplementation((path, callback) => { if (path === "/path/context/sub/symlink-error" && i < 2) { i += 1; callback(new Error("test")); return; } originalReadlink(path, callback); }); createSnapshot( fs, ["timestamp", { timestamp: true }], (err, snapshot, snapshot2) => { if (err) return done(err); expectSnapshotsState(fs, snapshot, snapshot2, true, done); } ); }); it("should work with symlinks with errors #1", done => { const fs = createFs(); fs.symlinkSync( "/path/folder/context", "/path/context/sub/symlink-error", "dir" ); jest.spyOn(fs, "readlink").mockImplementation((path, callback) => { callback(new Error("test")); }); const fsInfo = createFsInfo(fs); fsInfo.createSnapshot( Date.now() + 10000, files, directories, missing, ["timestamp", { timestamp: true }], (err, snapshot) => { expect(snapshot).toBeNull(); done(); } ); }); }); });