fix: normalize options.context to native separators - #14525
stormslowly wants to merge 4 commits into
Conversation
On Windows a config loaded via jiti yields a forward-slash `__dirname`. Used as the resolver base, it produces mixed-separator dependency paths (e.g. `D:/a\...\App.tsx`) that watchpack cannot match against its path.join-reconstructed event paths, so file changes are silently dropped after the first rebuild — file watching appears to stop. Closes #14446.
There was a problem hiding this comment.
Pull request overview
This PR fixes a Windows-only file-watching failure by normalizing options.context to OS-native separators during default option application, preventing mixed-separator dependency paths from being emitted and later mismatched by watchpack.
Changes:
- Normalize
options.contextviapath.resolveafter defaulting it toprocess.cwd(). - Add an explanatory comment referencing the Windows watch bug (#14446).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
📦 Binary Size-limit
🙈 Size remains the same at 67.91MB |
Merging this PR will not alter performance
Comparing Footnotes
|
Skip path.resolve for an empty context so it stays falsy and the relative-output-path validation still throws (#14446 review).
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f394f5738d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (options.context) { | ||
| options.context = path.resolve(options.context); |
There was a problem hiding this comment.
Normalize context before constructing the Compiler
When a Windows config supplies a forward-slash context, this assignment normalizes only compiler.options.context; createCompiler has already passed the original value to new Compiler (rspack.ts:62), where it is retained as compiler.context (Compiler.ts:310). Consequently plugins and loaders that use the public compiler.context or loaderContext.rootContext still receive the mixed-separator-prone value, while compiler.options.context contains a different path. Normalize before construction or synchronize compiler.context so all context consumers observe the native path.
Useful? React with 👍 / 👎.
|
After some discussion, we think
The problem will be fixed by #15162 |
Why
Fixes #14446 — on Windows, file watching stops after the first change (the dev server keeps running but ignores all later edits). It works on Linux/macOS.
Root cause (confirmed on a Windows runner): the repro's config uses
context: __dirname, and the rspack CLI loads the config via jiti, whose__dirnameis forward-slash on Windows (e.g.D:/a/project). rspack only defaultscontextand never normalizes a user-provided one, so the forward-slash value becomes the resolver base and the resolver emits mixed-separator dependency paths (e.g.D:/a\project\src\App.tsx).watchpack registers file watchers keyed by the raw path but reconstructs incoming OS-event paths via
path.join(native\), so the registered key and the event key differ and the change is dropped:Single-separator platforms (Linux/macOS) are unaffected.
What
Normalize
options.contextto the OS-native separator inapplyRspackOptionsDefaults(path.resolve), so the resolver base — and therefore every watch dependency path — uses one consistent separator.F(options, 'context', () => process.cwd()); + options.context = path.resolve(options.context!);No-op on POSIX and for already-native absolute contexts (existing defaults snapshots unchanged).
Verification
Reproduced and fixed on a real Windows runner (instrumented diagnostics in #14524):
rspack serve, context as-isrspack serve, context normalized to native sepWorkaround for users on current releases:
context: path.resolve(__dirname).