Skip to content

fix: Windows path compatibility for web and native (Metro) development#695

Open
YevheniiKotyrlo wants to merge 1 commit intoonejs:mainfrom
YevheniiKotyrlo:fix/windows-path-compatibility
Open

fix: Windows path compatibility for web and native (Metro) development#695
YevheniiKotyrlo wants to merge 1 commit intoonejs:mainfrom
YevheniiKotyrlo:fix/windows-path-compatibility

Conversation

@YevheniiKotyrlo
Copy link
Copy Markdown

Summary

Fix Windows path handling across one, vxrn, @vxrn/resolve, and @vxrn/vite-plugin-metro — web dev, native (Metro) dev, production build, and production serve all crash on Windows due to 15 distinct path-related issues.

Root causes fixed

import() with native paths — ESM import() on Windows rejects bare paths like C:\path as unsupported protocol c:. Fixed with pathToFileURL(path).href across 10 files (8 direct + 9 via a new toAbsoluteUrl() helper).

new URL('.', import.meta.url).pathname — returns /C:/path/... on Windows (leading / before drive letter). join(import.meta.url, '..') — produces .\file:\C:\... on Windows. Both replaced with import.meta.dirname (available since Node 20.11, already required by this repo).

Vite→Metro path boundary — Vite's config.root uses forward slashes, Metro's RootPathUtils expects native backslashes. Fixed with path.resolve() in metroPlugin.ts and getMetroConfigFromViteConfig.ts.

normalizePath for Vite internalspath.join() and path.relative() return backslash paths on Windows, but Vite uses forward slashes for facadeModuleId, moduleIds, and manifest keys. Middleware chunk lookup (build.ts:352), layout contextKey (:429), route chunk lookup (:472), critical CSS (criticalCSSPlugin.ts:66), and image data (imageDataPlugin.ts:15) all fail silently on Windows. Fixed with normalizePath from Vite.

micromatch.makeRe() Windows regex — picomatch 2.x auto-detects Windows and produces [\\/] instead of \/, breaking Metro API route exclusion at getViteMetroPluginOptions.ts:63. Fixed with normalizeReSource() helper.

startsWith(allowed + '/') bounds check — never matches on Windows where resolve() produces C:\path. Fixed with path.sep in imageDataPlugin.ts:22.

startsWith('../') in isWithinrelative() returns ..\other on Windows, startsWith('../') never matches — function always returns true. Fixed with startsWith('..') + isAbsolute() guard in isWithin.ts:5.

dirname(relativePath) in asset URL'/assets/' + dirname(relative(...)) produces /assets/src\img (mixed separators) in Metro asset registration. Fixed with .replace(/\\/g, '/') in createNativeDevEngine.ts:595.

filePath.includes('/src/index.ts') — Metro's filePath uses \ on Windows, so includes('/src/index.ts') never matches — react-native-svg compiled JS redirect is skipped. Fixed with path.sep in getViteMetroPluginOptions.ts:156.

Cross-platform safety

Every change is a no-op on POSIX:

  • pathToFileURL on Linux: file:///usr/src/app.js — valid ESM URL
  • path.resolve() on absolute POSIX path: returns input unchanged
  • normalizeReSource(): no [\\/] groups on POSIX, .replace() finds no matches
  • normalizePath: no backslashes on POSIX

Extends the pattern established in #640 (urlPathToFilePath).

Fixes #152

Note: #281 is a separate Windows issue — [vite:define] Transform failed because loadEnv in vxrn spreads process.env into Vite's define config, and Windows env vars like ProgramFiles(x86) contain characters that aren't valid JS identifiers. Not a path issue — not addressed by this PR.

Test plan

CI runs on ubuntu-latest only — no Windows runner available. All changes verified manually on both platforms.

Linux (WSL — Ubuntu 22.04, Bun 1.3.11, Node 24.3.0)

  • bun run build — 22/22 packages
  • bun run lint — 0 errors
  • bun run format:check — pass
  • bun run test:one — 24/24 files, 365 passed + 56 skipped

Windows (Windows 11, Bun 1.3.11, Node 25.2.1)

Clean install: node_modules/ + .one/ deleted, bun install from scratch.

  • bun run build — 22/22 packages
  • bun run lint — 0 errors
  • bun run format:check — pass
  • bun run test:one — 24/24 files, 365 passed + 56 skipped
  • one dev web (SSG + SSR + SPA) — all render modes work
  • one dev native (Metro) — cold bundle completes, app renders on Android emulator
  • one build — production build completes
  • one serve — SSG, SSR, SPA, API all respond correctly
  • one prebuild — generates native project
  • Web HMR — edit reflected in SSG response
  • Metro native HMR — edit reflected on Android emulator
  • Concurrent web + native requests — server stays alive

Use pathToFileURL().href for all dynamic import() calls receiving
filesystem paths — ESM import() on Windows rejects bare paths like
C:\path as unsupported protocol "c:".

Use import.meta.dirname instead of new URL('.', import.meta.url).pathname
and join(import.meta.url, '..') — both produce invalid paths on Windows.

Use path.resolve() at the Vite→Metro boundary to normalize forward-slash
paths from Vite config to native backslash paths expected by Metro.

Use normalizePath from Vite for comparisons with facadeModuleId,
moduleIds, manifest keys, and relative() output in the build pipeline —
Vite uses forward slashes internally, path.join/relative use native
backslashes on Windows.

Add toAbsoluteUrl() helper for relative-path-to-file-URL conversion.
Normalize micromatch regex for cross-platform Metro route exclusion.
Fix isPathWithinBounds to use path.sep instead of hardcoded '/'.
Fix test fixtures to use platform-correct paths via resolve().

Extends the pattern established in onejs#640 (urlPathToFilePath).

Fixes onejs#152
@YevheniiKotyrlo
Copy link
Copy Markdown
Author

YevheniiKotyrlo commented Apr 2, 2026

Web (Vite SSG via localhost:8081) and native (Metro on Android emulator) running concurrently on Windows 11.

Environment: Windows 11, Bun 1.3.11, Node 25.2.1, Android emulator API 36.
1

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.

when pnpm dev , filepath was wrong,there's one more part of the path

1 participant