diff --git a/crates/rspack_storage/src/filesystem/scope_fs.rs b/crates/rspack_storage/src/filesystem/scope_fs.rs index 05ea00077f38..ecebec24dd67 100644 --- a/crates/rspack_storage/src/filesystem/scope_fs.rs +++ b/crates/rspack_storage/src/filesystem/scope_fs.rs @@ -71,6 +71,9 @@ impl ScopeFileSystem { ) -> Result<()> { let from_file = from.workspace.join(relative_path.as_ref()); let to_file = to.workspace.join(relative_path.as_ref()); + to.fs + .create_dir_all(to_file.parent().expect("should have parent")) + .await?; if let Err(e) = from.fs.rename(&from_file, &to_file).await { // If the source file is not found, ignore the error. let e: Error = e.into(); diff --git a/tests/rspack-test/watchCases/cache/persistent-cache-delete/0/index.js b/tests/rspack-test/watchCases/cache/persistent-cache-delete/0/index.js new file mode 100644 index 000000000000..531c2e2e27c2 --- /dev/null +++ b/tests/rspack-test/watchCases/cache/persistent-cache-delete/0/index.js @@ -0,0 +1,84 @@ +import { existsSync, readdirSync, rmSync } from "fs"; +import value from "./value"; + +const cacheDir = __CACHE_DIR__; + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +function listCacheFiles() { + if (!existsSync(cacheDir)) { + return []; + } + + const files = []; + const walk = dir => { + let entries; + try { + entries = readdirSync(dir, { withFileTypes: true }); + } catch (error) { + if (error && error.code === "ENOENT") { + return; + } + throw error; + } + for (const entry of entries) { + const file = `${dir}/${entry.name}`; + if (entry.isDirectory()) { + walk(file); + } else { + files.push(file); + } + } + }; + walk(cacheDir); + return files; +} + +function hasCommittedPackFile() { + return listCacheFiles().some( + file => file.endsWith(".pack") && !file.includes("/.temp/") + ); +} + +async function waitForPackFiles() { + const start = Date.now(); + while (Date.now() - start < 5000) { + if (hasCommittedPackFile()) { + return; + } + await sleep(100); + } + throw new Error("Timed out waiting for persistent cache pack files"); +} + +async function removeCacheDir() { + const start = Date.now(); + while (Date.now() - start < 5000) { + try { + rmSync(cacheDir, { recursive: true, force: true }); + if (listCacheFiles().length === 0) { + return; + } + } catch (error) { + if (error && error.code !== "ENOTEMPTY") { + throw error; + } + } + await sleep(100); + } + throw new Error("Timed out removing persistent cache directory"); +} + +it("should keep writing persistent cache after the cache directory is removed", async () => { + expect(value).toBe(WATCH_STEP); + await waitForPackFiles(); + + if (WATCH_STEP === "0") { + await removeCacheDir(); + expect(listCacheFiles().length).toBe(0); + } else { + expect(WATCH_STEP).toBe("1"); + } +}); diff --git a/tests/rspack-test/watchCases/cache/persistent-cache-delete/0/value.js b/tests/rspack-test/watchCases/cache/persistent-cache-delete/0/value.js new file mode 100644 index 000000000000..b428ff41a01f --- /dev/null +++ b/tests/rspack-test/watchCases/cache/persistent-cache-delete/0/value.js @@ -0,0 +1 @@ +export default "0"; diff --git a/tests/rspack-test/watchCases/cache/persistent-cache-delete/1/value.js b/tests/rspack-test/watchCases/cache/persistent-cache-delete/1/value.js new file mode 100644 index 000000000000..508074e2ae07 --- /dev/null +++ b/tests/rspack-test/watchCases/cache/persistent-cache-delete/1/value.js @@ -0,0 +1 @@ +export default "1"; diff --git a/tests/rspack-test/watchCases/cache/persistent-cache-delete/rspack.config.js b/tests/rspack-test/watchCases/cache/persistent-cache-delete/rspack.config.js new file mode 100644 index 000000000000..cdf97f33d382 --- /dev/null +++ b/tests/rspack-test/watchCases/cache/persistent-cache-delete/rspack.config.js @@ -0,0 +1,26 @@ +const path = require('path'); +const { DefinePlugin } = require('@rspack/core'); + +/** @type {(_env: unknown, args: { tempPath: string }) => import("@rspack/core").Configuration} */ +module.exports = (_env, { tempPath }) => { + const cacheDir = path.join(tempPath, 'node_modules/.cache/rspack'); + + return { + mode: 'development', + cache: { + type: 'persistent', + storage: { + type: 'filesystem', + directory: cacheDir, + }, + }, + watchOptions: { + ignored: /node_modules/, + }, + plugins: [ + new DefinePlugin({ + __CACHE_DIR__: JSON.stringify(cacheDir), + }), + ], + }; +};