Repository navigation
feat(cli,core): let a tool run the oxnode watch watcher with its own loader - #872
Merged
Merged
Conversation
…loader
runWatch(argv, { loaderArgs, ignoreDirs, name }) is exported as
@oxc-node/cli/watch, an entry that starts nothing and loads neither
clipanion nor the binding. The child side is public as
@oxc-node/core/signal-ack and @oxc-node/core/watch-report, which now take
the binding's takeWatchDependencies as an argument. The README documents
the watch protocol.
Closes #871
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ries Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 500326bdeb
ℹ️ 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".
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 21, 2026
Closed
fengmk2
added a commit
to voidzero-dev/vite-plus
that referenced
this pull request
Oct 21, 2026
## Summary Implements #3141 except the `vp migrate` rule, which moves to a follow-up that migrates tsx scripts as a whole. `vpx --watch` now runs oxc-node's watcher (`oxnode watch`, oxc-project/oxc-node#861) with the vpx script loader instead of Node.js' own watch mode, so it works like `tsx watch`: ```bash vpx --watch ./src/server.ts --port 3000 vpx --watch --include "./config/*.json" --exclude "src/**/*.test.ts" ./src/server.ts vpx --watch --inspect --clear-screen=false ./src/index.ts ``` It takes `--include`, `--exclude`, `--clear-screen`, `--kill-signal`, `--kill-timeout`, `--rerun-on-enter`, and `--poll`, plus `tsx watch`'s `--ignore`, `--no-cache`, and `CHOKIDAR_USEPOLLING` and the Node.js watch mode options `--watch-preserve-output` and `--watch-kill-signal`, so scripts written for `tsx watch` or `node --watch` move over. Return reruns the script, Ctrl+C stops it, and `vpx --watch --help` lists the options. `vpx watch` stays package mode, so it still runs the npm package `watch`. It reruns the script in cases `tsx watch` misses: | Change | `tsx watch` (4.23.15) | `vpx --watch` | | --- | --- | --- | | An imported file | rerun | rerun | | A tsconfig edit, including a file it `extends` (a new `paths` mapping) | no rerun | rerun | | `import('./later.js')` fails, then `later.ts` is created | no rerun | rerun | | An `--env-file` file | no rerun | rerun | ## Changes - **Detection.** Both CLIs take `--watch` out of the Node.js options and run the watcher with the rest. `--watch-path`, which would start Node.js' watch mode, also selects the watcher, which rejects it with a hint to use `--include`, as it rejects `--test` and `--eval`. Detection knows which watcher options take a value (`--include`, `--exclude`, `--ignore`, `--kill-signal`, `--kill-timeout`, in the case table both CLIs share), so `vpx --watch --exclude ./gen.ts ./main.ts` runs `main.ts`. - **Watcher.** `dist/script-watch.js` runs `runWatch` (`@oxc-node/cli/watch`, oxc-project/oxc-node#872) as `vpx --watch`, with the loader flags of its own `dist/`, so the watcher and the loader always come from one `vite-plus`. That package's folder is ignored, since its loader files and binding show up in the module graph. The bundle imports only `node:` modules and loads no binding. oxc-node itself is unchanged. - **Running it.** The global `vpx` runs `node <dist>/script-watch.js <args>` through the `node` shim, so the watcher gets the Node.js `vp node` selects and, on Unix, the PID of `vpx`. It uses the project's `vite-plus` when its `dist/` has the watch entry, else the global install, as for scripts. The project-local bin runs the watcher in its own process, with `node_modules/.bin` first on `PATH`. - **tsconfig.** `--tsconfig`, as a `vpx` option or among the Node.js options, reaches the runs as `VP_SCRIPT_TSCONFIG`, as for a script, so it wins over a `TS_NODE_PROJECT` that an `--env-file` sets (oxc-project/oxc-node#876 affects `oxnode watch --tsconfig` only). - **Loader reports.** `oxc_node.rs` forwards `enableWatchReport` and `takeWatchDependencies`. Under the watcher, the preload turns recording on before the first transform or resolve, and the hooks and the CommonJS transform send what the binding recorded, the tsconfig files read and the paths a failed resolve looked for, as `{ "watch:import": [url] }`. On the `module.register()` path (older 22.x and 24.11.0), `script-register.ts` passes the hooks thread a port and relays its reports, as upstream's `register.mjs` does. Without a watcher, nothing is opened or wrapped. - **Signals.** When the watcher is not the terminal's foreground job (a pipe, `vp run`, a tool), a Ctrl+C reaches the script once: the preload installs `@oxc-node/core/signal-ack`, which tells the watcher the script already has the `SIGINT`, so it does not send a second one. A `SIGINT` sent to the `vpx` PID alone is passed on. The ack loads `node:net`, which adds under 1 ms to a run whose stdout and stderr are files (45.6 against 44.8 ms for a hello script on Node.js 22.18), and nothing with a terminal or a pipe, where Node.js loads `node:net` anyway. - **From the vendored source.** The watcher and the loader's `@oxc-node/core/watch-report` and `/signal-ack` are imported from `oxc-node/` and bundled, so they match the binding at the pinned hash. Type checking uses upstream's `watch.d.ts` for the watcher. - **Docs.** Both help texts, the Watch Mode section of the vpx guide, the RFC, `BUNDLING.md`, and the `AGENTS.md` shipped to projects. ## Behavior change `vpx --watch` no longer runs Node.js' watch mode. Through `vpx`, Node.js' `--watch-path` (use `--include`) and `--test --watch` are errors now. ## Known limitation A restart stops the script's process only, so a server the script started in a subprocess can keep its port (oxc-project/oxc-node#875). `tsx watch` and `node --watch` behave the same way. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #871.
A tool with its own loader built on the oxc-node binding (Vite+'s
vpx watch) can now run theoxnode watchwatcher.Watcher side:
@oxc-node/cli/watchrunWatch(argv, { loaderArgs, ignoreDirs, name }). All options are optional, and with noneoxnode watchbehaves and prints exactly as before (the default--helptext is byte-for-byte the same).loaderArgs["--enable-source-maps", "--import", <@oxc-node/core/register>], resolved only when not givenignoreDirs@oxc-node/core/registerwhenloaderArgsis not given, else none (real paths count too)name"oxnode watch", used in errors, usage and--help; its first word tags log lines ([vpx])dist/watch.jsis a second rolldown entry. It reaches only a shared chunk that importsnode:modules: no clipanion, no binding, and it starts nothing on import. A test walks the chunks to keep it that way.WatchCommand(clipanion) moved intosrc/index.ts, sosrc/watchhas no clipanion import.packages/cli/watch.d.ts../dist/*stays importable.App side:
@oxc-node/core/signal-ack,@oxc-node/core/watch-reportinstallSignalAck()is unchanged.flush,withFlushandcreateRelaynow take the binding'stakeWatchDependenciesas an argument (take), so the entry loads no binding of its own.register.mjsandesm.mjspass theirs.importanddefault, so a CommonJS--requirepreload can load them.package.jsonand to the build-time rewrite inreexport-oxc-runtime.mjs.Docs
README, "Embedding the watcher": both sides, with code, plus the protocol for a loader that does not use these entries (start args and env, the IPC message, the
"oxc-node": truekey, the ack byte).Tests
watch-embed.spec.ts(new):runWatchthrough@oxc-node/cli/watchwith a customregisterHooksloader built from the new core entries;tsconfig.jsonedit (the second one only works throughtake), under[vpx], with the loader folder skipped;--helpunder the tool's name;require().watch-args.spec.ts,watch-units.spec.ts: names in the errors, help text and log tag.OXC_TRANSFORM_ALLmode. Checked by hand on Node 22.18, 24.12 and 24.22.Open questions for the maintainers
oxnode watch, the hints that point tooxnode --watch(for--test, and the help footer) are left out, not rewritten. Should there be anodeWatchoption for a tool's own text?nameenough, or should there be alabeloption?OXNODE_SIGNAL_ACK, the"oxc-node"key, the ack byte) be called stable under semver?vpx?🤖 Generated with Claude Code