fix(dev-server-block): stop blocking dev-<suffix> scripts (#2179)

`DEV_PATTERN`'s trailing `\b` treats a hyphen as a word boundary, so
`dev\b` matched the `dev` prefix of distinct npm scripts like
`dev-setup` / `dev-docs` / `dev-build` and blocked them with exit 2.
Replace the trailing `\b` with `(?![\w-])` so the dev server still
matches (`dev`, `dev;`, `dev:ssr`) but `dev-<suffix>` scripts pass.

Adds regression tests for dev-setup/dev-docs/dev-build (allowed) and
dev:ssr (still blocked).

Co-authored-by: bymle <229636660+bymle@users.noreply.github.com>
This commit is contained in:
bymle 2026-06-07 13:25:39 +08:00 committed by GitHub
parent e7e38cd508
commit 7883da658b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 27 additions and 1 deletions

View File

@ -161,7 +161,11 @@ process.stdin.on('data', chunk => {
}); });
const TMUX_LAUNCHER = /^\s*tmux\s+(new|new-session|new-window|split-window)\b/; 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-<suffix>` 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 * Collect every command-line segment we should evaluate. Returns the top-level

View File

@ -63,6 +63,11 @@ function runTests() {
const result = runScript('bun run dev'); const result = runScript('bun run dev');
assert.strictEqual(result.code, 2, `Expected exit code 2, got ${result.code}`); assert.strictEqual(result.code, 2, `Expected exit code 2, got ${result.code}`);
}) ? passed++ : failed++); }) ? 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 { } else {
console.log(' (skipping blocking tests on Windows)\n'); 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}`); assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
}) ? passed++ : failed++); }) ? passed++ : failed++);
// --- dev-<suffix> 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 $(), ``, ()) --- // --- Subshell bypass regression (issue: dev server slipped past via $(), ``, ()) ---
if (!isWindows) { if (!isWindows) {