From dcfff3f6e8bb93ce50d03254441d058e1d18f177 Mon Sep 17 00:00:00 2001 From: Bao Nguyen Date: Sun, 20 Sep 2026 15:58:57 +0700 Subject: [PATCH] fix(clean): resolve clean patterns against the config cwd `cleanOutDir` globs `clean` with `cwd: config.cwd`, but the guard that rejects a pattern pointing at the working directory resolved it against `process.cwd()`. Workspace mode sets `cwd` to the package directory, so the two differ and the guard stops matching: `clean: ['.']` in a package config deletes that package's sources instead of throwing. The same mismatch rejects a pattern that only resolves to the cwd when read from `process.cwd()`, aborting a build that asked for nothing of the kind. --- src/features/clean.ts | 2 +- tests/clean.test.ts | 44 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/features/clean.ts b/src/features/clean.ts index 5ab936b03..b9e92e494 100644 --- a/src/features/clean.ts +++ b/src/features/clean.ts @@ -63,7 +63,7 @@ export function resolveClean( clean = [] } - if (clean.some((item) => path.resolve(item) === cwd)) { + if (clean.some((item) => path.resolve(cwd, item) === cwd)) { throw new Error( 'Cannot clean the current working directory. Please specify a different path to clean option.', ) diff --git a/tests/clean.test.ts b/tests/clean.test.ts index 2b25cf6b8..eceeb9b0f 100644 --- a/tests/clean.test.ts +++ b/tests/clean.test.ts @@ -159,6 +159,50 @@ describe('clean', () => { ).rejects.toThrow('Cannot clean the current working directory') }) + test('should not clean a custom cwd', async (context) => { + const files = { + 'pkg/index.ts': 'export const hello = "world"', + } + + // `cwd` points at a package directory, as it does in workspace mode, while + // the process stays in the test directory. + await expect( + testBuild({ + context, + files, + options: (cwd) => ({ + cwd: path.join(cwd, 'pkg'), + clean: ['.'], + }), + }), + ).rejects.toThrow('Cannot clean the current working directory') + }) + + test('should resolve clean patterns against a custom cwd', async (context) => { + const files = { + 'pkg/index.ts': 'export const hello = "world"', + 'pkg/pkg/old-file.js': 'old content', + } + + const testDir = getTestDir(context.task) + + await testBuild({ + context, + files, + options: (cwd) => ({ + cwd: path.join(cwd, 'pkg'), + clean: ['pkg'], + }), + snapshot: false, + }) + + // `pkg` is relative to the custom cwd, so it means `pkg/pkg`, not the cwd + const staleExists = await fsExists( + path.join(testDir, 'pkg', 'pkg', 'old-file.js'), + ) + expect(staleExists).toBe(false) + }) + test('should clean nested directories', async (context) => { const files = { 'index.ts': 'export const hello = "world"',