vite-plus 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/AGENTS.md +4 -0
  2. package/LICENSE +1 -878
  3. package/README.md +1 -1
  4. package/bin/vpx +9 -0
  5. package/binding/index.cjs +59 -54
  6. package/binding/index.d.cts +128 -2
  7. package/dist/{agent-97KmGCyL.js → agent-D8oxxhVR.js} +5 -7
  8. package/dist/bin.js +25 -32
  9. package/dist/{cli-parse-DHE8NrBU.js → cli-parse-aeUeFDlC.js} +2 -3
  10. package/dist/command-TjS_6y9O.js +1 -0
  11. package/dist/{command-CdMBoZFD.js → command-rY79K5NI.js} +28 -30
  12. package/dist/{comparison-CmVirWIW-DUGaiYqF.js → comparison-CmVirWIW-UlEf2maz.js} +1 -2
  13. package/dist/config/bin.js +8 -10
  14. package/dist/{constants-DR55DpRm.js → constants-DRUnJ_Dz.js} +3 -5
  15. package/dist/create/bin.d.ts +4 -8
  16. package/dist/create/bin.js +46 -65
  17. package/dist/{define-config-CvE5pHqB.js → define-config-ChwJcu5e.js} +6 -9
  18. package/dist/{define-config-BxDc_9BK.cjs → define-config-Cv2P6NT2.cjs} +7 -12
  19. package/dist/{define-config-Bexdd6RZ.d.ts → define-config-rPhYIFSn.d.ts} +1 -11
  20. package/dist/define-config.cjs +1 -1
  21. package/dist/define-config.d.ts +1 -1
  22. package/dist/define-config.js +1 -1
  23. package/dist/{dist-DIpm7mot.js → dist-Bs0SObNC.js} +73 -145
  24. package/dist/{dist-Cqz8OtiO.js → dist-CdkMvIiQ.js} +11 -5
  25. package/dist/dist-zDZV5eEc.js +3 -0
  26. package/dist/{documentation-DqcZSthV.js → documentation-cv_y0Auv.js} +1 -2
  27. package/dist/{editor-CtJGQP7C.js → editor-ClL78D8u.js} +1490 -4209
  28. package/dist/{help-BmKpeOP9.js → help-B03t_iQ4.js} +1 -2
  29. package/dist/hooks/bin.js +4 -5
  30. package/dist/{hooks-DhwsWb46.js → hooks-xRcJPc-O.js} +5 -4
  31. package/dist/index.cjs +2 -3
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.js +1 -1
  34. package/dist/{json-JP24qHUF.js → json-kl84kDyH.js} +9 -18
  35. package/dist/lint-plugins.cjs +1 -1
  36. package/dist/migration/bin.js +719 -184
  37. package/dist/migration/compat/worker.js +4 -6
  38. package/dist/{oxlint-plugin-config-BIzrO2y2.js → oxlint-plugin-config-BVpqYdcC.js} +3 -4
  39. package/dist/oxlint-plugin.d.ts +0 -2
  40. package/dist/oxlint-plugin.js +6 -7
  41. package/dist/{pack-BK38MLVZ.d.ts → pack-ARPFMefW.d.ts} +0 -2
  42. package/dist/pack-bin.d.ts +1 -3
  43. package/dist/pack-bin.js +3 -4
  44. package/dist/pack.d.ts +1 -1
  45. package/dist/{package-CQDXTm6j.js → package-DzsomcUD.js} +10 -95
  46. package/dist/{package-overrides-D1RcXSFl.js → package-overrides-Ci8GXouY.js} +5 -8
  47. package/dist/picomatch-DEUoj81q.js +1916 -0
  48. package/dist/{prompts-1xNz_ne0.js → prompts-GL1vj0wk.js} +30 -50
  49. package/dist/{protocol-D99W10Qi.js → protocol-CkNmY-Mr.js} +1 -2
  50. package/dist/{report-NhQk8VHb.js → report-DLHO30yZ.js} +2 -2
  51. package/dist/{resolve-vite-config-BVr1SUNg.js → resolve-vite-config-Dn5896A4.js} +9 -25
  52. package/dist/resolve-vite-config-QO6cC0SP.js +1 -0
  53. package/dist/{rolldown-runtime-kJ_i4yPo.js → rolldown-runtime-DegdyBIC.js} +1 -2
  54. package/dist/script-esm-hooks.d.ts +8 -0
  55. package/dist/script-esm-hooks.js +24 -0
  56. package/dist/script-hooks-9Fof5UMY.js +176 -0
  57. package/dist/script-preload.cjs +468 -0
  58. package/dist/script-register.d.ts +1 -0
  59. package/dist/script-register.js +22 -0
  60. package/dist/script-watch.d.ts +1 -0
  61. package/dist/script-watch.js +1908 -0
  62. package/dist/staged/bin.js +53 -2011
  63. package/dist/sync-versions/bin.js +26 -48
  64. package/dist/sync-versions/bin.mjs +109 -217
  65. package/dist/{terminal-MKGAuy-p.js → terminal-BcuPUy-j.js} +1 -2
  66. package/dist/toolchain.js +35 -9
  67. package/dist/toolchain.json +35 -9
  68. package/dist/{tsconfig-zWgHiQ3X.js → tsconfig-Do6MAG_f.js} +1 -1
  69. package/dist/{tsconfig-BZu7kl-x.js → tsconfig-LE_mAwe_.js} +8 -10
  70. package/dist/version.d.ts +1 -3
  71. package/dist/version.js +5 -6
  72. package/dist/versions.js +4 -4
  73. package/dist/{vitest-ecosystem-BU0AqZq-.js → vitest-ecosystem-BeEYz-qF.js} +1 -2
  74. package/dist/{vitest-v5-entry-points-DthU1e_O.js → vitest-v5-entry-points-NyJsFeux.js} +1 -2
  75. package/dist/vpx-bin.d.ts +1 -0
  76. package/dist/vpx-bin.js +232 -0
  77. package/dist/vpx-script-EXgV5QOf.js +434 -0
  78. package/docs/config/run.md +4 -0
  79. package/docs/guide/check.md +1 -0
  80. package/docs/guide/ci.md +13 -3
  81. package/docs/guide/env.md +11 -4
  82. package/docs/guide/global-cli.md +52 -0
  83. package/docs/guide/migrate-rules.md +48 -5
  84. package/docs/guide/migrate.md +5 -5
  85. package/docs/guide/package-management.md +1 -1
  86. package/docs/guide/vpx.md +170 -9
  87. package/package.json +20 -17
  88. package/dist/command-DVTILtS-.js +0 -1
  89. package/dist/dist-Dhu07wmN.js +0 -3
  90. package/dist/resolve-vite-config-7WDtXO4G.js +0 -1
@@ -18,11 +18,31 @@ Migration is idempotent: running it again after a successful migration should no
18
18
 
19
19
  On a project that already depends on `vite-plus`, `vp migrate` performs an upgrade only: it updates dependencies and package-manager configuration and finalizes imports. It does not touch project setup.
20
20
 
21
- - `--full` also runs the setup actions: git hooks, editor config, agent files, ESLint, Prettier, and tsup migration, framework shims, the tsconfig `baseUrl` fix, and the `.nvmrc`/Volta to `.node-version` conversion.
21
+ - `--full` also runs the setup actions: git hooks, editor config, agent files, ESLint, Prettier, tsup, and tsx migration, framework shims, the tsconfig `baseUrl` fix, and the Volta to `.node-version` conversion.
22
22
  - `--hooks`, `--agent`, and `--editor` opt into a single setup action without `--full`.
23
23
 
24
24
  When a default upgrade skips setup actions that would apply, it prints a hint to run `vp migrate --full`. Fresh (non Vite+) projects always run the full migration.
25
25
 
26
+ ## Oxlint JSON Inheritance
27
+
28
+ When `.oxlintrc.json` or `.oxlintrc.jsonc` uses `extends`, `vp migrate` resolves each file path relative to the config that declares it. It creates shared TypeScript configs and imports them through `lint.extends`, including nested inheritance. Oxlint applies the existing merge order, rule severities, and overrides. Plain `vp lint` uses the migrated rules without a `-c` flag.
29
+
30
+ Migration assumes ESM projects. Shared configs become sibling `.ts` files. Imports from JavaScript and TypeScript configs use `.js` specifiers, which Vite and TypeScript resolve to the generated source files without changing compiler settings. File-level TypeScript pragmas stay before generated imports. Workspace consumers reuse the same generated modules, including the workspace that owns a shared config. Migration defaults stay in each workspace's local `lint` block. Edit these TypeScript files to update shared settings. Original shared JSON and JSONC files remain available to other consumers; edits to those originals do not update the migrated configs.
31
+
32
+ JS-plugin declarations stay in their original config or override. Relative plugin paths become absolute paths at runtime through `fileURLToPath(new URL(..., import.meta.url))`. Explicit inherited `typeAware` and `typeCheck` settings take precedence over migration defaults.
33
+
34
+ Cleanup removes each completed workspace symlink independently. Shared aliases that remain on disk keep their targets and dependencies available.
35
+
36
+ A shared config's owner keeps `env`, `globals`, `settings`, and `ignorePatterns` at the top level of its `lint` block through references to the shared module. Edits to these fields apply to the owner. Other consumers do not inherit them.
37
+
38
+ Generated modules include a source marker. A later migration attempt reuses these modules without replacing user edits, so a preserved consumer can migrate after its conflict is resolved. Existing modules without a matching marker remain conflicts.
39
+
40
+ Failed merges and existing `lint` blocks record a pending marker in the Vite config, so an isolated failure can be retried after the conflict is resolved. A successful merge removes this marker. Completed workspace merges record a completion marker, including configs merged inline. After fixing an unreadable config, run `vp migrate` again to migrate the remaining consumers and remove JSON files that are no longer needed.
41
+
42
+ Shared config imports use literal paths so Vite and TypeScript can resolve filenames containing `#` or `%`. A relative import path containing `?` or a literal backslash requires a manual migration: Vite treats `?` as a query delimiter, and TypeScript treats backslashes as path separators. Migration preserves the JSON config and reports a warning in these cases.
43
+
44
+ Missing files, invalid JSON, inheritance cycles, conflicting output files, and package-local plugins that resolve differently from the Vite config directory produce a warning and leave the original config in place. Configs outside the project or workspace, configs inside `node_modules`, CommonJS Vite configs, and an existing `lint` block require a manual merge. Workspace JSON removal waits until all migration outcomes are known. Preserved JSON consumers retain their transitive dependencies, including configs whose owners migrated successfully. If an inheritance graph cannot be read, workspace JSON files are kept and their owners use shared modules, because the unreadable consumer can reference them. `vp lint` continues to reject string paths in `lint.extends`; path resolution happens only during migration.
45
+
26
46
  ## Pack Configuration
27
47
 
28
48
  `vp migrate` updates static `pack` objects in `vite.config.*` and exported objects in `tsdown.config.*` for [tsdown 0.23](https://github.com/rolldown/tsdown/releases/tag/v0.23.0). This also runs on existing Vite+ projects without `--full`, including workspace packages. Arrays and direct objects returned by `defineConfig` callbacks are supported. JSON tsdown configs receive the same updates after they merge into `vite.config.ts`.
@@ -185,6 +205,8 @@ Object-valued nested npm and Bun overrides are preserved: they are user-defined
185
205
 
186
206
  Plugin packages (an unscoped name starting with `vite-plugin-` or `unplugin-`, or `vite` in `peerDependencies`/`dependencies`) skip the rewrite even in config files. Only the `vite` specifier is in scope for this rule.
187
207
 
208
+ `vite/rolldown` and `vite/rolldown/*` are never rewritten, because `vite-plus` does not export those subpaths; they keep resolving through the core alias.
209
+
188
210
  `declare module 'vite'` augmentations follow the same rule and are preserved outside config files. Through the core alias they reach the same `@voidzero-dev/vite-plus-core` module whose `UserConfig` types `defineConfig` from `vite-plus`, so they keep working after migration; `vite-plus` itself exports no `UserConfig` symbol, so a rewritten `declare module 'vite-plus'` augmentation would merge with nothing. Extensions aimed at `vite-plus`'s own surface are written against `vite-plus` by hand.
189
211
 
190
212
  ### `vitest` and Browser Imports
@@ -239,8 +261,9 @@ Migration rewrites commands provided by the Vite+ toolchain in `package.json` sc
239
261
  | `eslint` | `vp lint`, when its optional migration runs |
240
262
  | `prettier` | `vp fmt`, when its optional migration runs |
241
263
  | `tsup` | `vp pack`, when its optional migration runs |
264
+ | `tsx` | `vpx`, when its optional migration runs |
242
265
 
243
- For commands launched through `bunx`, migration preserves `bunx` and its `--bun` flag (keeping the user's chosen runtime) and rewrites only the managed command. This also works when `bunx` follows a command-launcher delimiter such as `run` or `--`:
266
+ For commands launched through `bunx`, migration preserves `bunx` and its `--bun` flag (keeping the user's chosen runtime) and rewrites only the managed command. tsx is the exception: `bunx tsx` becomes `vpx`, and `bunx --bun tsx` keeps tsx (see [tsx to `vpx`](#tsx-to-vpx)). This also works when `bunx` follows a command-launcher delimiter such as `run` or `--`:
244
267
 
245
268
  | Before | After |
246
269
  | ------------------------------------------------------- | -------------------------------------------------------- |
@@ -251,16 +274,36 @@ For commands launched through `bunx`, migration preserves `bunx` and its `--bun`
251
274
 
252
275
  Unrelated `bunx` commands and other package-executor forms remain unchanged.
253
276
 
277
+ ### tsx to `vpx`
278
+
279
+ When a `package.json` script, a lint-staged task, or a `#!/usr/bin/env tsx` shebang runs [tsx](https://tsx.is), migration offers to move it to [`vpx`](/guide/vpx), Vite+'s TypeScript script runner. It rewrites scripts and lint-staged tasks in `package.json` and JSON lint-staged files, and `#!/usr/bin/env tsx` shebangs in script files:
280
+
281
+ | Before | After |
282
+ | -------------------------------------------------------------------------- | ------------------------------------------ |
283
+ | `tsx src/seed.ts --rows 10` | `vpx src/seed.ts --rows 10` |
284
+ | `tsx watch --include "*.json" src/a.ts` | `vpx --watch --include "*.json" src/a.ts` |
285
+ | `npx tsx a.ts`, `pnpm exec tsx a.ts`, `pnpm dlx tsx a.ts`, `bunx tsx a.ts` | `vpx a.ts` |
286
+ | `node --import tsx ./a.ts` | `vpx ./a.ts` |
287
+ | `cross-env NODE_ENV=dev tsx a.ts` | `cross-env NODE_ENV=dev vpx a.ts` |
288
+ | `concurrently "tsx watch a.ts" "vp dev"` | `concurrently "vpx --watch a.ts" "vp dev"` |
289
+
290
+ `tsx watch` becomes `vpx --watch`, never `vpx watch`, which runs the npm package `watch`. Node.js options and tsx watch options keep their order, and `--no-cache` is dropped. A quoted command is rewritten only for programs that run it, such as `concurrently`, `nodemon --exec`, `sh -c`, and `cross-env-shell`; a quoted argument of other programs, as in `echo "tsx a.ts"`, is data. `vpx` needs the same Node.js versions as Vite+ itself, and migration already upgrades incompatible runtime pins to them (see [Vitest 5](/guide/vitest-v5)).
291
+
292
+ Migration keeps tsx, with a warning, for a command `vpx` cannot run: `--eval`, `--print`, or `--check`, the REPL, `--test` without files or in watch mode, an entry without a file extension, commands that set `TSX_TSCONFIG_PATH` (pass `--tsconfig` to `vpx` instead) or load tsx through `NODE_OPTIONS`, and commands with pipes, variables, or subshells. It also reads the scripts a command runs, including local `--require` and `--import` modules, and keeps tsx when one does not exist yet, as when an earlier command generates it, and when `vpx` would fail on one: a `require()` of `./a.js` that only tsx resolves to `a.ts`, and, because tsx compiles ES module syntax to CommonJS, a `.cts` file with `import` or `export`, and a file that mixes `import` with `__dirname`, `__filename`, `require()`, or `module.exports` in a package without `"type": "module"`. This check skips globs and scripts after a `cd` or another shell state change, and reads only the files a command names, not the modules they import. It does not look for what else `vpx` handles differently, such as standard decorators, tsconfig `paths` in `require()`, or a tsconfig `module` of `node16` or `nodenext`; `vpx` reports those when the script runs. A package that lists tsx in `dependencies`, `optionalDependencies`, or `peerDependencies` keeps it too, since `vpx` comes from the `vite-plus` devDependency.
293
+
294
+ Migration removes the `tsx` devDependency only when nothing uses tsx anymore: no kept script, no import of `tsx` or its subpaths such as `tsx/esm/api` (in source, build output, or a `package.json` `imports` alias), no shebang, no tool setting such as `--import=tsx` in `package.json` or a JSON or JavaScript config, and no tsx command in CI workflows, git hooks, or shell scripts. The root's dependency stays while any workspace package still uses tsx, and a package's dependency stays while a nested package under it does. When tsx stays, the warning names what still uses it.
295
+
296
+ See [Coming from tsx](/guide/vpx#coming-from-tsx) for the other differences.
297
+
254
298
  ## Continuous Integration Rules
255
299
 
256
300
  Migration replaces exact `voidzero-dev/setup-vp@v1` references in GitHub Actions workflows and composite actions under `.github` with the latest exact `setup-vp` release known to that Vite+ version. The frozen `v1` tag does not receive new releases. Existing exact versions and commit SHAs are left unchanged.
257
301
 
258
302
  ## Node.js Version Rules
259
303
 
260
- Migration converts legacy Node.js version-manager files to `.node-version`, the format Vite+ reads. On an existing Vite+ project this conversion is part of the full setup bucket, so it runs with `vp migrate --full`; fresh migrations run it unconditionally.
304
+ Migration converts the Volta `volta.node` pin to `.node-version`, because Vite+ does not read Volta. On an existing Vite+ project this conversion is part of the full setup bucket, so it runs with `vp migrate --full`; fresh migrations run it unconditionally.
261
305
 
262
- - `.nvmrc` and Volta `volta.node` pins are converted to `.node-version`. An existing `.node-version` is kept.
263
- - When `.nvmrc` is removed, any `actions/setup-node` `node-version-file: .nvmrc` reference in `.github/workflows/*.{yml,yaml}` and composite actions under `.github` (`.github/**/action.{yml,yaml}`) is repointed to `.node-version` so CI does not fail with "node version file ... does not exist".
306
+ - When `.node-version` or `.nvmrc` exists, migration keeps it and skips the Volta conversion. Vite+ reads both files directly (see [Node.js Selection](/guide/env#node-js-selection)).
264
307
 
265
308
  ## Package-Manager Rules
266
309
 
@@ -8,7 +8,7 @@ import { migrationPrompt, upgradePrompt } from '../.vitepress/theme/data/migrati
8
8
 
9
9
  ## Overview
10
10
 
11
- This command is the starting point for consolidating separate Vite, Vitest, Oxlint, Oxfmt, ESLint, Prettier, tsdown, and tsup setups into Vite+.
11
+ This command is the starting point for consolidating separate Vite, Vitest, Oxlint, Oxfmt, ESLint, Prettier, tsdown, tsup, and tsx setups into Vite+.
12
12
 
13
13
  Use it when you want to take an existing project and move it onto the Vite+ defaults instead of wiring each tool by hand.
14
14
 
@@ -95,21 +95,21 @@ Upgrade the [global CLI](./upgrade.md#global-vp) to your target 1.0 release, the
95
95
 
96
96
  ### Without the Global CLI
97
97
 
98
- Use an existing Node.js runtime that satisfies `^22.18.0 || ^24.11.0 || >=26.0.0`. Run the target migrator through your package manager without adding it to the project first. For the `1.3.0` release:
98
+ Use an existing Node.js runtime that satisfies `^22.18.0 || ^24.11.0 || >=26.0.0`. Run the target migrator through your package manager without adding it to the project first. For the `1.4.0` release:
99
99
 
100
100
  ::: code-group
101
101
 
102
102
  ```bash [pnpm]
103
- pnpm dlx --package=vite-plus@1.3.0 vp migrate --no-interactive
103
+ pnpm dlx --package=vite-plus@1.4.0 vp migrate --no-interactive
104
104
  ```
105
105
 
106
106
  ```bash [npm]
107
- npx --package=vite-plus@1.3.0 vp migrate --no-interactive
107
+ npx --package=vite-plus@1.4.0 vp migrate --no-interactive
108
108
  ```
109
109
 
110
110
  :::
111
111
 
112
- Replace `1.3.0` with your target release. For a preview, use the version from the PR and pass `--registry=https://registry-bridge.viteplus.dev` to `pnpm` or `npx` before the `vp` command. Keep the version in `--package` explicit so you run the target migrator rather than the old local CLI.
112
+ Replace `1.4.0` with your target release. For a preview, use the version from the PR and pass `--registry=https://registry-bridge.viteplus.dev` to `pnpm` or `npx` before the `vp` command. Keep the version in `--package` explicit so you run the target migrator rather than the old local CLI.
113
113
 
114
114
  After migration, finish dependency installation and validate with the updated local CLI:
115
115
 
@@ -212,7 +212,7 @@ Use these when you need lower-level package-manager behavior.
212
212
  - **Patching**: `patch`, `patch-commit`
213
213
  - **Packaging and publishing**: `pack`, `version`, `publish`, `stage`, `dist-tag`, `deprecate`
214
214
  - **Inspection**: `view`, `search`, `licenses`, `audit`, `fund`, `ping`
215
- - **Authentication and access**: `login`, `logout`, `whoami`, `token`, `owner`
215
+ - **Authentication and access**: `login`, `logout`, `whoami`, `token`, `owner`, `team`
216
216
  - **Configuration**: `config`, `cache`
217
217
  - **Package manifest**: `pkg get`, `pkg set`, `pkg delete`, `pkg fix`
218
218
 
package/docs/guide/vpx.md CHANGED
@@ -1,25 +1,29 @@
1
1
  # Running Binaries
2
2
 
3
- Use `vpx`, `vp exec`, and `vp dlx` to run binaries without switching between local installs, downloaded packages, and project-specific tools.
3
+ Use `vpx`, `vp exec`, and `vp dlx` to run package binaries and scripts without switching between local installs, downloaded packages, and project-specific tools.
4
4
 
5
5
  ## Overview
6
6
 
7
- `vpx` executes a command from a local or remote npm package. It can run a package that is already available locally, download a package on demand, or target an explicit package version.
7
+ `vpx` does one of two things, depending on its first argument:
8
8
 
9
- Use the other binary commands when you need stricter control:
9
+ - [`vpx <package>`](#running-package-binaries) runs a binary from a local or remote npm package, like `npx`.
10
+ - [`vpx <file>`](#running-scripts) runs a TypeScript or JavaScript file, like `tsx`, and [`vpx --watch <file>`](#watch-mode) runs it again when a file it uses changes, like `tsx watch`.
10
11
 
11
- - `vpx` looks for a binary in local `node_modules/.bin` directories, Vite+-managed global packages, and system `PATH`, in that order, then falls back to `vp dlx`. With `pkg@version`, `--package/-p`, or `--shell-mode`, it runs via `vp dlx` directly.
12
- - `vp exec` runs a command from local `node_modules/.bin` directories, falling back to `PATH` if not found
13
- - `vp dlx` runs a package binary without adding it as a dependency
12
+ The other commands give stricter control over where a binary comes from:
14
13
 
15
- ## `vpx`
14
+ - [`vp exec`](#vp-exec) runs a command from local `node_modules/.bin` directories, falling back to `PATH`.
15
+ - [`vp dlx`](#vp-dlx) runs a package binary without adding it as a dependency.
16
16
 
17
- Use `vpx` for running any local or remote binary:
17
+ `vpx -v` or `vpx --version` prints the Vite+ version, like `vp --version`. In `package.json` scripts, `vpx` prints the version of the project's `vite-plus` package.
18
+
19
+ ## Running Package Binaries
18
20
 
19
21
  ```bash
20
- vpx <pkg[@version]> [args...]
22
+ vpx [options] <pkg[@version]> [args...]
21
23
  ```
22
24
 
25
+ `vpx` looks for the binary in local `node_modules/.bin` directories, Vite+-managed global packages, and the system `PATH`, in that order. If none has it, `vpx` downloads the package and runs it via `vp dlx`. With `pkg@version`, `--package/-p`, or `--shell-mode`, it runs via `vp dlx` directly.
26
+
23
27
  ### Options
24
28
 
25
29
  - `-p, --package <name>` installs one or more additional packages before running the command
@@ -35,6 +39,163 @@ vpx oxlint@1.85.0 --version
35
39
  vpx -p cowsay -c 'echo "hi" | cowsay'
36
40
  ```
37
41
 
42
+ Package managers and `vp run` put `node_modules/.bin` first on `PATH`, so `package.json` scripts run the `vpx` bin of the project's `vite-plus` package. That bin does not need the global CLI, and it looks only in local `node_modules/.bin` directories. It runs an installed binary directly, as the global `vpx` does, and `pkg@version`, `--package/-p`, or `--shell-mode` via `vp dlx`. A binary that is not installed is an error, so a script never downloads a package by accident:
43
+
44
+ ```
45
+ error: vpx: 'eslint' is not installed in node_modules
46
+ note: Add it to devDependencies, or run `vp dlx eslint` to download and run it.
47
+ ```
48
+
49
+ ## Running Scripts
50
+
51
+ ```bash
52
+ vpx [--tsconfig <path>] [node options] <file> [args...]
53
+ ```
54
+
55
+ `vpx` runs TypeScript and JavaScript files directly, without a `tsx` or `ts-node` dependency. Scripts run on the project's Node.js version, the same one [`vp node`](/guide/env) selects, with the [oxc-node](https://github.com/oxc-project/oxc-node) loader that ships with Vite+.
56
+
57
+ ```bash
58
+ vpx ./scripts/seed.ts --dry-run
59
+ vpx scripts/build.mts
60
+ vpx --watch ./server.ts
61
+ vpx --env-file=.env ./scripts/migrate.ts
62
+ vpx --tsconfig tsconfig.scripts.json ./tools/gen.ts
63
+ ```
64
+
65
+ ### Files and Package Names
66
+
67
+ `vpx` runs its argument as a file when it is:
68
+
69
+ - a path (starting with `./`, `../`, or `/`; on Windows also `.\`, `..\`, a drive such as `C:\`, or `\\`) ending in `.ts`, `.mts`, `.cts`, `.tsx`, `.js`, `.mjs`, `.cjs`, or `.jsx`
70
+ - a name with one of those extensions that exists as a file, such as `scripts/seed.ts`
71
+ - a path to a file whose shebang runs `vpx` (see [Shebang Scripts](#shebang-scripts))
72
+
73
+ A missing path, or a missing name with a TypeScript extension, is an error: `vpx` never downloads a package for it. After Node.js options, such as `vpx --inspect seed.ts`, only a missing path is reported by `vpx`; Node.js reports a missing name itself. A name with a JavaScript extension that is not a file, such as `highlight.js`, runs the package of that name.
74
+
75
+ ### Supported Syntax
76
+
77
+ - TypeScript syntax that Node.js type stripping rejects: enums, namespaces, parameter properties, and JSX
78
+ - Decorators with `"experimentalDecorators": true`, including `emitDecoratorMetadata`; standard decorators and `accessor` fields are not supported yet
79
+ - tsconfig `paths` for `import`, `.js` imports that point at `.ts` files, and extensionless imports
80
+ - ESM and CommonJS, including `require()` of `.ts` files; a `.cts` file is CommonJS, so it uses `import x = require('...')` and `export =`, and `import`/`export` declarations there fail with an error that suggests renaming the file to `.mts`
81
+ - TypeScript published in `node_modules`
82
+ - tsconfig `jsx`, `jsxImportSource`, `jsxFactory`, `jsxFragmentFactory`, `useDefineForClassFields`, and `verbatimModuleSyntax`; see [JSX](#jsx) and [tsconfig](#tsconfig) for the defaults
83
+
84
+ `vpx` does not type-check. Run [`vp check`](/guide/check) for that.
85
+
86
+ ### Options
87
+
88
+ - `--tsconfig <path>` applies one tsconfig to every file (see [tsconfig](#tsconfig))
89
+ - `--watch` runs the file again when a file it uses changes (see [Watch Mode](#watch-mode))
90
+ - Node.js options before the file, such as `--inspect`, `--test`, `--env-file`, `--require`, and `--import`, are passed to Node.js; `--require` and `--import` preloads can be TypeScript too
91
+ - Everything after the file, including `--`, is passed to the script
92
+
93
+ ### Shebang Scripts
94
+
95
+ Scripts can use `vpx` as their interpreter. Make the file executable and keep its extension:
96
+
97
+ ```ts
98
+ #!/usr/bin/env vpx
99
+ // scripts/release.ts
100
+ ```
101
+
102
+ ### In `package.json` Scripts
103
+
104
+ The `vpx` bin of the `vite-plus` package runs scripts on its own, so `"seed": "vpx ./scripts/seed.ts"` works in `package.json` scripts without the global CLI. It runs them on the Node.js that started it, which inside `vp run` and package manager scripts is the project's version.
105
+
106
+ ### Watch Mode
107
+
108
+ ```bash
109
+ vpx --watch [watch options] [node options] <file> [args...]
110
+ ```
111
+
112
+ `vpx --watch` runs a file and runs it again when a file it uses changes, like `tsx watch`. It is not Node.js's own watch mode: `vpx` runs oxc-node's watcher (`oxnode watch`) instead, which runs the file with the `vpx` loader.
113
+
114
+ ```bash
115
+ vpx --watch ./src/server.ts --port 3000
116
+ vpx --watch --include "./config/*.json" --exclude "src/**/*.test.ts" ./src/server.ts
117
+ vpx --watch --inspect --clear-screen=false ./src/index.ts
118
+ ```
119
+
120
+ It watches every file the script imports or requires (in worker threads too, on Node.js 24 and later), the tsconfig files `vpx` read and the ones they `extends`, the files a failed import looked for, so creating one reruns the script, and `--env-file` files. `node_modules` is not watched unless `--include` names it. Press Return to rerun at once. Ctrl+C stops the script and the watcher; press it again to force kill. `vpx --watch --help` lists the options:
121
+
122
+ - `--include <glob>` also watches matching files, and `--exclude <glob>` never watches them
123
+ - `--clear-screen=false` (or `--no-clear-screen`) keeps the output of earlier runs; the screen is cleared only on a terminal
124
+ - `--kill-signal <signal>` stops the script before a rerun (default `SIGTERM`), and `--kill-timeout <ms>` force kills it after that long (default 5000)
125
+ - `--rerun-on-enter=false` (or `--no-rerun-on-enter`) leaves stdin to the script
126
+ - `--poll[=<ms>]` polls files instead of using file system events, for Docker, WSL, and network drives
127
+ - `--tsconfig <path>` applies one tsconfig to every file, as for `vpx <file>`
128
+
129
+ `tsx watch`'s `--ignore` and `--no-cache` and its `CHOKIDAR_USEPOLLING` variable work too (`--no-cache` does nothing: `vpx` has no transform cache), and so do the Node.js watch mode options `--watch-preserve-output` and `--watch-kill-signal`, so scripts written for `tsx watch` or `node --watch` move over. `--watch-path`, which would start Node.js's watch mode, is an error that points to `--include`, and so are `--test`, `--eval`, and other options that run no file. Other Node.js options go before the file.
130
+
131
+ ### tsconfig
132
+
133
+ `vpx` reads compiler options from a tsconfig but does not type-check with it. Each file uses the nearest `tsconfig.json` above it that includes it through `files`, `include`, `exclude`, or project `references`, as `tsc` does, and `extends` is followed. Files in `node_modules` use no tsconfig, and neither does a file that no tsconfig includes, so it gets the defaults below. Such a file still resolves `paths` aliases through the nearest `tsconfig.json` above it, as tsx does: with a root tsconfig whose `include` is `["src"]`, a script in `scripts/` can import the same aliases as `src/`. Because the config depends on the file's location, not on the working directory, `vpx` compiles a file the same way wherever it runs from.
134
+
135
+ `--tsconfig <path>` applies one config to every file instead, including files that config does not include. Without `--tsconfig`, an `OXC_TSCONFIG_PATH` or `TS_NODE_PROJECT` environment variable does the same. If it names a file that does not exist, or a tsconfig fails to parse, `vpx` prints a warning and runs without that config.
136
+
137
+ A file that no tsconfig applies to runs as if its `compilerOptions` were empty:
138
+
139
+ | Option | Without a tsconfig |
140
+ | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
141
+ | `module` | The module format comes from `package.json` `"type"`: ESM with `"module"`, CommonJS otherwise. `.mts` is always ESM and `.cts` always CommonJS. With a tsconfig `module` of `node16`, `node18`, `nodenext`, `es2015`, or later, `.ts` and `.tsx` files run as ESM regardless of `"type"`; for the `node*` values, `tsc` and tsx follow `"type"` instead. |
142
+ | `paths`, `baseUrl` | None. Imports resolve as in Node.js, plus `.js` to `.ts` or `.tsx`, `.mjs` to `.mts`, `.cjs` to `.cts`, and extensionless imports. |
143
+ | `useDefineForClassFields` | `true`: class fields have `[[Define]]` semantics and run natively, as with a `target` of `ES2022` or later. A lower `target` switches to `[[Set]]` semantics, as in `tsc`. |
144
+ | `experimentalDecorators` | `false`: a decorator fails with an error that asks for this option. |
145
+ | `emitDecoratorMetadata` | `false`. |
146
+ | `jsx`, `jsxImportSource` | The automatic runtime, importing `react/jsx-runtime` (see [JSX](#jsx)). |
147
+ | `verbatimModuleSyntax` | `false`: imports whose bindings are unused at runtime are removed, with their side effects. |
148
+ | `rewriteRelativeImportExtensions` | `false`. `./file.ts` imports work either way. |
149
+
150
+ Types are removed, and enums, namespaces, and parameter properties are compiled, whatever the tsconfig says. `strict`, `lib`, and other type-checking options have no effect, and `target` only changes the class field default.
151
+
152
+ ### JSX
153
+
154
+ `.jsx` and `.tsx` files compile to calls into a JSX runtime, React's by default. Install the runtime they use, `react` or the package named by `jsxImportSource`; Vite+ does not ship one. tsconfig options select the runtime:
155
+
156
+ | tsconfig `jsx` | Output |
157
+ | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
158
+ | not set, or `"react-jsx"` | The automatic runtime: `jsx()` and `jsxs()` from `<jsxImportSource>/jsx-runtime`, with `jsxImportSource` defaulting to `react` |
159
+ | `"react-jsxdev"` | The development runtime: `jsxDEV()` from `<jsxImportSource>/jsx-dev-runtime`, with the file name, line, and column of each element |
160
+ | `"react"` | The classic runtime: `React.createElement()`, or `jsxFactory`, with fragments as `React.Fragment`, or `jsxFragmentFactory` |
161
+ | `"preserve"`, `"react-native"` | The automatic runtime, because Node.js cannot run JSX |
162
+
163
+ A classic factory that the file imports, such as `import { h, Fragment } from 'my-jsx'`, is kept even though only JSX uses it.
164
+
165
+ Pragma comments override the tsconfig for that file:
166
+
167
+ ```tsx
168
+ /** @jsxImportSource my-jsx */
169
+ ```
170
+
171
+ ```tsx
172
+ /** @jsxRuntime classic */
173
+ /** @jsx h */
174
+ /** @jsxFrag Fragment */
175
+ import { h, Fragment } from 'my-jsx';
176
+ ```
177
+
178
+ When a file has both `@jsx` and `@jsxImportSource`, `@jsxImportSource` selects the automatic runtime and `@jsx` is ignored. Pragmas are read from every comment in the file, so prose such as `// @jsxImportSource is set above` in an ordinary comment changes the runtime too.
179
+
180
+ A `.jsx` file gets a tsconfig's JSX options only when that tsconfig sets `"allowJs": true`, as in `tsc` and tsx. Otherwise it uses the defaults above.
181
+
182
+ JSX that depends on a compiler plugin is not supported. `vpx` compiles JSX only into calls to a runtime's `jsx()`, `jsxs()`, and `jsxDEV()` exports or to a classic factory. A setup that relies on a Babel or bundler plugin to transform JSX, usually with `"jsx": "preserve"`, fails at runtime or loses what that plugin adds.
183
+
184
+ ### Coming from tsx
185
+
186
+ `vp migrate` rewrites tsx commands in `package.json` scripts to `vpx` and removes the `tsx` devDependency once nothing uses it; see [tsx to `vpx`](/guide/migrate-rules#tsx-to-vpx). Most scripts that run with tsx run with `vpx` unchanged. These differences can need a change:
187
+
188
+ - **JSX default**: without a tsconfig `jsx` option, tsx compiles JSX to `React.createElement()`, which needs `React` in scope, while `vpx` uses the automatic runtime. Set `"jsx": "react"` to keep the classic output. Because of its default, tsx also ignores a lone `/** @jsxImportSource … */` comment unless the file has `/** @jsxRuntime automatic */` or the tsconfig sets `"jsx": "react-jsx"`; `vpx` applies the comment on its own.
189
+ - **`.cts` files with `import` or `export`**: tsx compiles them to CommonJS, and `vpx` reports an error. Rename the file to `.mts`, or write it with `import x = require()` and `export =`.
190
+ - **`import` in a CommonJS package**: in a package without `"type": "module"`, tsx compiles a `.ts` or `.js` file that uses `import`, `export`, or `import.meta` to CommonJS, so `__filename`, `__dirname`, and `require` still work. `vpx` runs such a file as an ES module, as Node.js does, so they are not defined, and a default import of it from an ES module gets its default export rather than the whole module. Use `import.meta.filename`, `import.meta.dirname`, and `createRequire(import.meta.url)` instead.
191
+ - **`require()` resolution**: tsx maps `require('./a.js')` to `a.ts` and applies tsconfig `paths` to `require()`. `vpx` resolves `require()` as Node.js does, so write the `.ts` extension, and use `paths` aliases only with `import`.
192
+ - **Signals**: tsx turns `SIGINT` and `SIGTERM` into exit codes 130 and 143 and runs `process.on('exit')` handlers first. Under `vpx` the signal ends the script, as it does under Node.js, so clean up in a `process.on('SIGINT')` or `process.on('SIGTERM')` handler.
193
+ - **Node.js version**: `vpx` needs Node.js `^22.18.0 || ^24.11.0 || >=26.0.0`, while tsx also runs on older releases.
194
+ - **Watch mode**: `tsx watch` becomes `vpx --watch`, which takes tsx watch's options (see [Watch Mode](#watch-mode)). It also reruns the script on tsconfig edits and when a file a failed import looked for is created, which tsx watch misses.
195
+ - **Eval, REPL, and `--check`**: `vpx --eval`, the REPL, and `vpx --check` do not transform TypeScript.
196
+ - **`node --import tsx`**: `vpx` has no equivalent for tools that must start `node` themselves.
197
+ - **Other loaders**: remove `--import tsx` from `NODE_OPTIONS`. Two loaders registered together compete for the same files.
198
+
38
199
  ## `vp exec`
39
200
 
40
201
  Use `vp exec` to run tools already installed in your project or available in your environment.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plus",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "The Unified Toolchain for the Web",
5
5
  "homepage": "https://viteplus.dev/guide",
6
6
  "bugs": {
@@ -15,7 +15,8 @@
15
15
  },
16
16
  "bin": {
17
17
  "vp": "./bin/vp",
18
- "vpr": "./bin/vpr"
18
+ "vpr": "./bin/vpr",
19
+ "vpx": "./bin/vpx"
19
20
  },
20
21
  "files": [
21
22
  "AGENTS.md",
@@ -338,8 +339,9 @@
338
339
  }
339
340
  },
340
341
  "dependencies": {
341
- "@oxc-project/types": "=0.154.0",
342
- "@oxlint/plugins": "=1.88.0",
342
+ "@oxc-project/runtime": "=0.155.0",
343
+ "@oxc-project/types": "=0.155.0",
344
+ "@oxlint/plugins": "=1.89.0",
343
345
  "@vitest/browser": "5.0.5",
344
346
  "@vitest/browser-preview": "5.0.5",
345
347
  "@vitest/mocker": "5.0.5",
@@ -347,10 +349,10 @@
347
349
  "@vitest/snapshot": "5.0.5",
348
350
  "@vitest/spy": "5.0.5",
349
351
  "@vitest/utils": "5.0.5",
350
- "oxfmt": "=0.73.0",
351
- "oxlint": "=1.88.0",
352
+ "oxfmt": "=0.74.0",
353
+ "oxlint": "=1.89.0",
352
354
  "oxlint-tsgolint": "=7.0.2004",
353
- "vite": "npm:@voidzero-dev/vite-plus-core@1.3.0",
355
+ "vite": "npm:@voidzero-dev/vite-plus-core@1.4.0",
354
356
  "vitest": "5.0.5"
355
357
  },
356
358
  "devDependencies": {
@@ -362,19 +364,20 @@
362
364
  "@types/validate-npm-package-name": "^4.0.2",
363
365
  "@vitest/browser-playwright": "5.0.5",
364
366
  "@vitest/browser-webdriverio": "5.0.0",
365
- "@voidzero-dev/vite-plus-prompts": "1.3.0",
367
+ "@voidzero-dev/vite-plus-prompts": "1.4.0",
366
368
  "@voidzero-dev/vite-task-client": "0.2.0",
367
369
  "bingo": "^0.13.2",
368
370
  "cac": "^7.0.0",
369
371
  "cross-spawn": "^7.0.5",
370
372
  "detect-indent": "^7.0.2",
371
373
  "detect-newline": "^4.0.1",
372
- "glob": "^13.0.0",
373
374
  "jsonc-parser": "^3.3.1",
374
375
  "lint-staged": "^17.6.0",
375
376
  "minimatch": "^10.0.3",
376
377
  "nanotar": "^0.3.0",
377
378
  "picocolors": "^1.1.1",
379
+ "pirates": "^4.0.7",
380
+ "tinyglobby": "^0.2.15",
378
381
  "tsdown": "^0.23.1",
379
382
  "validate-npm-package-name": "^8.0.0",
380
383
  "verkit": "^0.5.0",
@@ -415,14 +418,14 @@
415
418
  "node": "^22.18.0 || ^24.11.0 || >=26.0.0"
416
419
  },
417
420
  "optionalDependencies": {
418
- "@voidzero-dev/vite-plus-darwin-arm64": "1.3.0",
419
- "@voidzero-dev/vite-plus-darwin-x64": "1.3.0",
420
- "@voidzero-dev/vite-plus-linux-arm64-gnu": "1.3.0",
421
- "@voidzero-dev/vite-plus-linux-arm64-musl": "1.3.0",
422
- "@voidzero-dev/vite-plus-linux-x64-gnu": "1.3.0",
423
- "@voidzero-dev/vite-plus-linux-x64-musl": "1.3.0",
424
- "@voidzero-dev/vite-plus-win32-x64-msvc": "1.3.0",
425
- "@voidzero-dev/vite-plus-win32-arm64-msvc": "1.3.0"
421
+ "@voidzero-dev/vite-plus-darwin-arm64": "1.4.0",
422
+ "@voidzero-dev/vite-plus-darwin-x64": "1.4.0",
423
+ "@voidzero-dev/vite-plus-linux-arm64-gnu": "1.4.0",
424
+ "@voidzero-dev/vite-plus-linux-arm64-musl": "1.4.0",
425
+ "@voidzero-dev/vite-plus-linux-x64-gnu": "1.4.0",
426
+ "@voidzero-dev/vite-plus-linux-x64-musl": "1.4.0",
427
+ "@voidzero-dev/vite-plus-win32-x64-msvc": "1.4.0",
428
+ "@voidzero-dev/vite-plus-win32-arm64-msvc": "1.4.0"
426
429
  },
427
430
  "scripts": {
428
431
  "build": "oxnode -C dev ./build.ts",
@@ -1 +0,0 @@
1
- export { n as runCommandSilently } from "./command-CdMBoZFD.js";
@@ -1,3 +0,0 @@
1
- import { t as require_dist } from "./dist-DIpm7mot.js";
2
- export default require_dist();
3
- export {};
@@ -1 +0,0 @@
1
- export { a as resolveUniversalViteConfig } from "./resolve-vite-config-BVr1SUNg.js";