diff --git a/scripts/hooks/pre-bash-dev-server-block.js b/scripts/hooks/pre-bash-dev-server-block.js index cd663fa7..06eb6402 100755 --- a/scripts/hooks/pre-bash-dev-server-block.js +++ b/scripts/hooks/pre-bash-dev-server-block.js @@ -161,7 +161,11 @@ process.stdin.on('data', chunk => { }); const TMUX_LAUNCHER = /^\s*tmux\s+(new|new-session|new-window|split-window)\b/; -const DEV_PATTERN = /\b(npm\s+run\s+dev|pnpm(?:\s+run)?\s+dev|yarn(?:\s+run)?\s+dev|bun(?:\s+run)?\s+dev)\b/; +// Trailing (?![\w-]) rather than \b: \b treats a hyphen as a word boundary, so +// `dev\b` matches the `dev` prefix of distinct scripts like `dev-setup` / +// `dev-docs` / `dev-build` and wrongly blocks them. The lookahead still matches +// the dev server (`dev`, `dev;`, `dev:ssr`, ...) but not a `dev-` script. +const DEV_PATTERN = /\b(npm\s+run\s+dev|pnpm(?:\s+run)?\s+dev|yarn(?:\s+run)?\s+dev|bun(?:\s+run)?\s+dev)(?![\w-])/; /** * Collect every command-line segment we should evaluate. Returns the top-level diff --git a/tests/hooks/pre-bash-dev-server-block.test.js b/tests/hooks/pre-bash-dev-server-block.test.js index 4f075ddb..89524b1e 100644 --- a/tests/hooks/pre-bash-dev-server-block.test.js +++ b/tests/hooks/pre-bash-dev-server-block.test.js @@ -63,6 +63,11 @@ function runTests() { const result = runScript('bun run dev'); assert.strictEqual(result.code, 2, `Expected exit code 2, got ${result.code}`); }) ? passed++ : failed++); + + (test('still blocks npm run dev:ssr — colon variant is a dev server (exit 2)', () => { + const result = runScript('npm run dev:ssr'); + assert.strictEqual(result.code, 2, `Expected exit code 2, got ${result.code}`); + }) ? passed++ : failed++); } else { console.log(' (skipping blocking tests on Windows)\n'); } @@ -89,6 +94,23 @@ function runTests() { assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`); }) ? passed++ : failed++); + // --- dev- scripts are distinct from the dev server, must not be blocked --- + + (test('allows npm run dev-setup — distinct script, not the dev server (exit 0)', () => { + const result = runScript('npm run dev-setup'); + assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`); + }) ? passed++ : failed++); + + (test('allows pnpm run dev-docs — distinct script (exit 0)', () => { + const result = runScript('pnpm run dev-docs'); + assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`); + }) ? passed++ : failed++); + + (test('allows yarn dev-build — distinct script (exit 0)', () => { + const result = runScript('yarn dev-build'); + assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`); + }) ? passed++ : failed++); + // --- Subshell bypass regression (issue: dev server slipped past via $(), ``, ()) --- if (!isWindows) {