fix(plugin): resolve Windows path and symlink issues#400
fix(plugin): resolve Windows path and symlink issues#400ByteYue merged 3 commits intojackwener:mainfrom
Conversation
- Replace `new URL(import.meta.url).pathname` with `fileURLToPath()` from node:url — the former returns `/C:/Users/...` on Windows (leading slash before drive letter), breaking path resolution for host linking and esbuild binary lookup. - Use junction (`'junction'`) instead of directory symlink (`'dir'`) on Windows in linkHostOpencli — junctions don't require admin privileges, while `fs.symlinkSync(..., 'dir')` does on Windows. - Use `where` instead of `which` on Windows for global esbuild lookup. All changes are platform-conditional and preserve existing Unix behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- npm execFileSync needs shell:true on Windows (.cmd wrapper) - esbuild binary is a shebang script, needs shell:true on Windows - resolveEsbuildBin: prefer .cmd in node_modules/.bin/ on Windows over import.meta.resolve (which returns a shebang script) - Updated test to accept .cmd extension on Windows Found during UAT testing on Windows 11.
UAT Results (Windows 11)
UAT found 3 additional Windows issues beyond the original fixes:
All fixed in the latest commit. |
ByteYue
left a comment
There was a problem hiding this comment.
Review of PR #400 — Windows Plugin Path and Symlink Fixes
Thanks for the contribution! This PR correctly identifies and addresses all three core Windows issues reported in #392. Here's my detailed review:
✅ What looks good
-
fileURLToPathmigration — Correctly replacesnew URL(import.meta.url).pathnamewhich produces broken paths like/C:/Users/...on Windows. ThefileURLToPathimport fromnode:urlis the canonical way to handle this. -
NTFS junction fallback — Using
'junction'on Windows avoids the admin-privilege requirement for directory symlinks. Good platform detection with theisWindowsconst. -
shell: truefornpmandesbuild— On Windows,npmis a.cmdwrapper andexecFileSynccan't invoke it directly without a shell. Same for the esbuild binary. The conditional spread...(isWindows && { shell: true })is clean. -
.cmdwrapper priority on Windows — TheresolveEsbuildBin()refactor to preferesbuild.cmdinnode_modules/.bin/on Windows is correct, since the raw esbuild binary is a shebang script that Windows can't execute directly. -
which→whereon Windows — Correct platform-aware global binary lookup. -
Test update — The regex
/esbuild(\.cmd)?$/in the test is a reasonable cross-platform assertion.
🔧 Minor suggestions (will fix directly)
-
The esbuild
execFileSyncintranspilePluginTsneeds consideration — WhenresolveEsbuildBin()returns an.cmdpath on Windows, theexecFileSync(esbuildBin, ...)call intranspilePluginTswill use that.cmdfile. Withshell: truealready added in the PR, this should work correctly. Good. -
Minor:
whereon Windows may return multiple lines —where esbuildcan return multiple paths. The current.trim()may leave just the first line on Unix (which) but on Windows it could return multiple lines. Consider using.split('\n')[0].trim()for safety.
Overall this is a solid, well-structured PR that correctly addresses the Windows compatibility issues. I'll checkout and apply the minor where fix mentioned above, then push.
'where esbuild' on Windows can return multiple matching paths, one per line. Take only the first match to get a valid single path for resolveEsbuildBin().
Fixes #392
What's broken on Windows
Three issues prevent the plugin system from working on Windows:
new URL(import.meta.url).pathnamereturns/C:/Users/...— the leading slash before the drive letter breaks all path operations. Replaced withfileURLToPath(import.meta.url)which handles this correctly on all platforms.fs.symlinkSync(..., 'dir')requires admin privileges — Windows directory symlinks need elevated permissions. Changed to use NTFS junctions ('junction') on Windows, which work without admin. Unix keeps the original symlink behavior.which esbuilddoesn't exist on Windows — changed towhere esbuildon Windows,which esbuildon Unix.Files changed
src/plugin.ts— 3 path fixes (fileURLToPath), junction on Windows, platform-awarewhich/whereTesting
npx tsc --noEmit— zero type errorsnpm test— fixed 1 previously-failing test (resolveEsbuildBin). 3 remaining failures are pre-existing and unrelated.