Skip to content

fix(plan): rewrite node_modules/.bin/*.cmd to powershell on Windows#345

Open
fengmk2 wants to merge 1 commit intomainfrom
fix/prefer-ps1-over-cmd-shim
Open

fix(plan): rewrite node_modules/.bin/*.cmd to powershell on Windows#345
fengmk2 wants to merge 1 commit intomainfrom
fix/prefer-ps1-over-cmd-shim

Conversation

@fengmk2
Copy link
Copy Markdown
Member

@fengmk2 fengmk2 commented Apr 18, 2026

Summary

Running a node_modules/.bin/*.cmd shim on Windows triggers cmd.exe's "Terminate batch job (Y/N)?" prompt on Ctrl+C, which corrupts the terminal. At plan time, when which() resolves such a shim inside the workspace and a sibling .ps1 exists, rewrite the invocation to run the .ps1 directly through PowerShell — the spawn layer stays untouched and Ctrl+C propagates cleanly.

Closes voidzero-dev/vite-plus#1176

What the rewrite does

program_path: <abs path to pwsh.exe or powershell.exe>
args:         [-NoProfile, -NoLogo, -ExecutionPolicy, Bypass, -File,
               <.ps1 path, relative to task cwd>, ...original_args]

The .ps1 path is stored relative to the task's cwd (with / separators), so SpawnFingerprint.args is portable across machines — no absolute paths leak into cache keys. PowerShell resolves -File <relative> against its own working directory (the task's cwd) and lands on the correct file.

When it fires

Only when all of the following hold:

  1. Extension is .cmd (case-insensitive) — cmd-shim never emits .bat.
  2. Path lives inside the workspace root — global shims like %AppData%\npm\node_modules\.bin\foo.cmd are left alone.
  3. Path's last two components are .bin / node_modules (case-insensitive).
  4. A sibling .ps1 exists.
  5. pwsh.exe or powershell.exe is on PATH.

Any miss and the original .cmd is kept — behavior matches pre-PR.

Tests

  • 6 cross-platform unit tests in vite_task_plan::ps1_shim::tests cover the pure rewrite logic: workspace root, hoisted monorepo subpackage (.. traversal), missing sibling, non-.cmd extensions, .cmd outside .bin, .cmd outside the workspace.
  • Windows-only plan snapshot in plan_snapshots/fixtures/windows_cmd_shim_rewrite/ — a pnpm workspace with a sub-package. Two plan cases (dev_in_subpackage via cwd, dev_filter_from_root via --filter) produce byte-identical snapshots, proving the cache key doesn't depend on how the user navigates to the task. The harness gained a windows_only: bool flag on SnapshotsFile; redact_snapshot gained a pass that maps pwsh / powershell and their absolute host paths to <powershell> so the snapshot doesn't pin a specific runner image.

Test plan

  • cargo clippy --workspace --all-targets + cargo test --workspace green on macOS
  • Windows CI green
  • Manual on Windows: https://github.com/Curtion/report-vite-plus-1, Ctrl+C during vp run dev leaves terminal clean
  • Manual: confirm .cmd fallback on a Windows box with PATH stripped of PowerShell
  • Manual: confirm a globally installed .cmd outside the workspace is not rewritten

Note

Medium Risk
Changes Windows command resolution and cache fingerprint inputs for spawned tasks, which could affect execution behavior and caching; scope is gated to workspace-local .cmd shims with .ps1 siblings and is covered by targeted tests.

Overview
Avoids Windows Ctrl+C terminal corruption by rewriting workspace-local node_modules/.bin/*.cmd shims (with sibling .ps1) at plan time to run via pwsh.exe/powershell.exe -File <cwd-relative .ps1>; this changes the planned program_path/args and therefore the cache fingerprint to match what actually executes.

Adds ps1_shim rewrite logic with unit tests, integrates it into both normal spawn planning and synthetic task planning, and extends snapshot testing with a Windows-only fixture plus redaction/harness tweaks (including PowerShell host redaction and allowing fixture node_modules in git).

Reviewed by Cursor Bugbot for commit 24f2a8f. Configure here.

@fengmk2 fengmk2 self-assigned this Apr 18, 2026
fengmk2 added a commit that referenced this pull request Apr 18, 2026
Address review feedback on #345:
- Extract the 5 fixed PowerShell prefix flags to a `const POWERSHELL_PREFIX`
- Build `new_args` via iterator chain instead of manual Vec pushes
- Drop the no-op `let _ = (&program_path, &args);` on non-Windows
fengmk2 added a commit to voidzero-dev/vite-plus that referenced this pull request Apr 18, 2026
Pulls in voidzero-dev/vite-task#345 which prefers .ps1 shims over .cmd
on Windows to avoid the "Terminate batch job (Y/N)?" prompt and terminal
corruption on Ctrl+C during `vp run dev`.

Closes #1176
@fengmk2

This comment was marked as resolved.

fengmk2 added a commit to voidzero-dev/vite-plus that referenced this pull request Apr 18, 2026
Pulls in voidzero-dev/vite-task#345 which prefers .ps1 shims over .cmd
on Windows to avoid the "Terminate batch job (Y/N)?" prompt and terminal
corruption on Ctrl+C during `vp run dev`.

Closes #1176
Comment thread crates/vite_task_plan/src/plan.rs Outdated
@fengmk2

This comment was marked as resolved.

fengmk2 added a commit to voidzero-dev/vite-plus that referenced this pull request Apr 18, 2026
Picks up the cache-portability fix for voidzero-dev/vite-task#345
(PowerShell rewrite moved from plan layer to spawn layer).
fengmk2 added a commit to voidzero-dev/vite-plus that referenced this pull request Apr 18, 2026
Picks up voidzero-dev/vite-task#345 fix for missing `which` dep on
Windows after the module move.
cursor[bot]

This comment was marked as resolved.

@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 18, 2026

@cursor review

fengmk2 added a commit to voidzero-dev/vite-plus that referenced this pull request Apr 18, 2026
Pulls in voidzero-dev/vite-task#345 which prefers .ps1 shims over .cmd
on Windows to avoid the "Terminate batch job (Y/N)?" prompt and terminal
corruption on Ctrl+C during `vp run dev`.

Closes #1176
fengmk2 added a commit to voidzero-dev/vite-plus that referenced this pull request Apr 18, 2026
Picks up the cache-portability fix for voidzero-dev/vite-task#345
(PowerShell rewrite moved from plan layer to spawn layer).
fengmk2 added a commit to voidzero-dev/vite-plus that referenced this pull request Apr 18, 2026
Picks up voidzero-dev/vite-task#345 fix for missing `which` dep on
Windows after the module move.
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 4f14ebe. Configure here.

@fengmk2 fengmk2 changed the title fix(plan): prefer .ps1 over .cmd shim on Windows fix(spawn): prefer .ps1 over .cmd shim on Windows Apr 18, 2026
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 18, 2026

before

image

after

image

fengmk2 added a commit that referenced this pull request Apr 18, 2026
Address review feedback on #345:
- Extract the 5 fixed PowerShell prefix flags to a `const POWERSHELL_PREFIX`
- Build `new_args` via iterator chain instead of manual Vec pushes
- Drop the no-op `let _ = (&program_path, &args);` on non-Windows
@fengmk2 fengmk2 force-pushed the fix/prefer-ps1-over-cmd-shim branch from 5ce6c62 to 3e1fc38 Compare April 18, 2026 14:25
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 18, 2026

@cursor review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 3e1fc38. Configure here.

@fengmk2 fengmk2 changed the title fix(spawn): prefer .ps1 over .cmd shim on Windows fix: route node_modules/.bin .cmd shims through PowerShell on Windows Apr 18, 2026
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 18, 2026

@cursor review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 26021ca. Configure here.

@fengmk2 fengmk2 changed the title fix: route node_modules/.bin .cmd shims through PowerShell on Windows refactor: prefer .ps1 over .cmd shim on Windows Apr 18, 2026
@fengmk2 fengmk2 changed the title refactor: prefer .ps1 over .cmd shim on Windows fix: prefer .ps1 over .cmd shim on Windows Apr 18, 2026
Comment thread crates/vite_task_plan/src/ps1_shim.rs Outdated
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 19, 2026

@cursor review

@fengmk2 fengmk2 marked this pull request as ready for review April 19, 2026 07:32
@fengmk2 fengmk2 requested a review from branchseer April 19, 2026 07:33
Comment thread crates/vite_task_plan/src/ps1_shim.rs
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 19, 2026

@cursor review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit be08530. Configure here.

@fengmk2 fengmk2 marked this pull request as draft April 20, 2026 02:17
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 20, 2026

@cursor review

Comment thread crates/vite_task_plan/src/ps1_shim.rs
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 20, 2026

@cursor review

Comment thread crates/vite_task_plan/tests/plan_snapshots/redact.rs Outdated
fengmk2 added a commit that referenced this pull request Apr 20, 2026
Address review feedback on #345:
- Extract the 5 fixed PowerShell prefix flags to a `const POWERSHELL_PREFIX`
- Build `new_args` via iterator chain instead of manual Vec pushes
- Drop the no-op `let _ = (&program_path, &args);` on non-Windows
@fengmk2 fengmk2 force-pushed the fix/prefer-ps1-over-cmd-shim branch 2 times, most recently from ea43983 to 24f2a8f Compare April 20, 2026 06:27
@fengmk2 fengmk2 changed the title fix: prefer .ps1 over .cmd shim on Windows fix(plan): rewrite node_modules/.bin/*.cmd to powershell on Windows Apr 20, 2026
@fengmk2 fengmk2 changed the title fix(plan): rewrite node_modules/.bin/*.cmd to powershell on Windows fix(plan): rewrite node_modules/.bin/*.cmd to powershell on Windows Apr 20, 2026
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 20, 2026

@cursor review

Running a `node_modules/.bin/*.cmd` shim on Windows triggers `cmd.exe`'s
"Terminate batch job (Y/N)?" prompt on Ctrl+C, which corrupts the
terminal (backspace prints ^H, Ctrl+C prints ^C, input sluggish).

At plan time, when `which()` resolves a `.cmd` shim inside the
workspace and a sibling `.ps1` exists, rewrite the invocation to run
the `.ps1` directly through PowerShell. The spawn layer stays
untouched — it runs whatever the plan records, 1-for-1. Ctrl+C then
propagates cleanly without the intermediate `cmd.exe` hop.

The rewrite records:

    program_path: <abs path to pwsh.exe or powershell.exe>
    args:         [-NoProfile, -NoLogo, -ExecutionPolicy, Bypass, -File,
                   <.ps1 path relative to task cwd>, ...original_args]

The `.ps1` path is stored relative to the task's cwd (with `/`
separators), so `SpawnFingerprint.args` is portable across machines —
no absolute paths leak into cache keys. PowerShell resolves
`-File <relative>` against its own working directory (the task's
cwd) and lands on the correct file.

The rewrite fires only when all of the following hold:
  1. Extension is `.cmd` (case-insensitive) — cmd-shim never emits `.bat`.
  2. Path lives inside the workspace root — global shims like
     `%AppData%\npm\node_modules\.bin\foo.cmd` are left alone.
  3. Path's last two components are `.bin` / `node_modules`
     (case-insensitive).
  4. A sibling `.ps1` exists.
  5. `pwsh.exe` or `powershell.exe` is on PATH.

If any miss, the original `.cmd` is kept — behavior matches pre-PR.

Tests
-----
- 6 cross-platform unit tests in `vite_task_plan::ps1_shim::tests`
  cover the pure rewrite logic (root cwd, hoisted monorepo subpackage
  with `..` traversal, missing sibling, non-.cmd extensions, outside
  `.bin`, outside workspace).
- Windows-only plan snapshot in
  `plan_snapshots/fixtures/windows_cmd_shim_rewrite/` — a pnpm
  workspace with a sub-package. Two plan cases (`dev_in_subpackage`
  via cwd, `dev_filter_from_root` via --filter) produce byte-identical
  snapshots, proving the cache key doesn't depend on how the user
  navigates to the task. The harness gained a `windows_only: bool`
  flag on `SnapshotsFile`; `redact_snapshot` gained a pass that maps
  `pwsh` / `powershell` and their absolute host paths to
  `<powershell>` so the snapshot doesn't pin a specific runner image.

Closes voidzero-dev/vite-plus#1176
@fengmk2 fengmk2 force-pushed the fix/prefer-ps1-over-cmd-shim branch from 24f2a8f to 892af62 Compare April 20, 2026 06:32
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 24f2a8f. Configure here.

@fengmk2 fengmk2 marked this pull request as ready for review April 20, 2026 06:41
fengmk2 added a commit to voidzero-dev/vite-plus that referenced this pull request Apr 20, 2026
Pulls in voidzero-dev/vite-task#345 which prefers .ps1 shims over .cmd
on Windows to avoid the "Terminate batch job (Y/N)?" prompt and terminal
corruption on Ctrl+C during `vp run dev`.

Closes #1176
fengmk2 added a commit to voidzero-dev/vite-plus that referenced this pull request Apr 20, 2026
Picks up the cache-portability fix for voidzero-dev/vite-task#345
(PowerShell rewrite moved from plan layer to spawn layer).
fengmk2 added a commit to voidzero-dev/vite-plus that referenced this pull request Apr 20, 2026
Picks up voidzero-dev/vite-task#345 fix for missing `which` dep on
Windows after the module move.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pressing Ctrl+C after vp run dev, pnpm run dev, or npm run dev leaves the terminal in a broken state on Windows

1 participant