Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "ts-node",
"version": "10.9.2"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "typescript",
"version": "5.9.3"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "migration-ts-node",
"type": "module",
"scripts": {
"seed": "ts-node --transpileOnly ./scripts/seed.ts",
"loader": "node --loader ts-node/esm/transpile-only ./scripts/seed.ts",
"register": "node --require=ts-node/register/transpile-only ./scripts/seed.ts",
"env-loader": "NODE_OPTIONS='--require ts-node/register/transpile-only' node ./scripts/seed.ts"
},
"devDependencies": {
"ts-node": "file:.fixture/ts-node",
"typescript": "file:.fixture/typescript",
"vite": "^7.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env -S ts-node --transpileOnly
const version: string = '1.0.0';
console.log(version);
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
enum State {
Ready = 'ready',
}
console.log(State.Ready);
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[[case]]
name = "migration_ts_node_scripts"
vp = "local"
comment = "Local stub dependencies keep the runner migration independent of npm packages."
steps = [
{ argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "transpile-only commands and shebangs use vpx; preloads keep Node.js" },
["vpt", "print-file", "package.json"],
["vpt", "print-file", "scripts/release.ts"],
{ argv = ["vp", "run", "seed"], comment = "the migrated script runs TypeScript without ts-node" },
["vp", "run", "loader"],
{ argv = ["vp", "run", "register"], comment = "the CommonJS registration option becomes the public ESM preload" },
]

[[case]]
name = "migration_ts_node_kept"
vp = "local"
steps = [
{ argv = ["vpt", "json-edit", "package.json", "scripts.checked", "ts-node ./scripts/seed.ts"], snapshot = false },
{ argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "commands that type-check keep ts-node and explain the difference" },
["vpt", "print-file", "package.json"],
]

[[case]]
name = "migration_ts_node_config"
vp = "local"
steps = [
{ argv = ["vpt", "rm", "scripts/release.ts"], snapshot = false },
{ argv = ["vpt", "write-file", "tsconfig.json", "{\"ts-node\":{\"transpileOnly\":true}}\n"], snapshot = false },
{ argv = ["vpt", "json-edit", "package.json", "scripts.seed", "ts-node ./scripts/seed.ts"], snapshot = false },
{ argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "the runner pins its config; Node preloads keep ts-node until TS_NODE_PROJECT names that file" },
["vpt", "print-file", "package.json"],
["vp", "run", "seed"],
]

[[case]]
name = "migration_ts_node_already_vite_plus"
vp = "local"
steps = [
{ argv = ["vpt", "json-edit", "package.json", "devDependencies.vite-plus", "latest"], snapshot = false },
{ argv = ["vp", "migrate", "--no-interactive"], comment = "a plain upgrade leaves ts-node migration to --full" },
{ argv = ["vp", "migrate", "--no-interactive", "--full", "--no-hooks", "--no-agent", "--no-editor"], comment = "--full migrates transpile-only scripts on an existing Vite+ project" },
["vpt", "print-file", "package.json"],
["vp", "run", "seed"],
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# migration_ts_node_already_vite_plus

## `vpt json-edit package.json devDependencies.vite-plus latest`


## `vp migrate --no-interactive`

a plain upgrade leaves ts-node migration to --full

```
VITE+ - The Unified Toolchain for the Web

◇ Updated . to Vite+ <version>
• Node <version> pnpm <version>
• Dependencies:
vite-plus latest → <version>
vite → <version>
• Package manager settings configured
• Skipped editor, hooks, and lint setup. Run `vp migrate --full` to apply them.
```

## `vp migrate --no-interactive --full --no-hooks --no-agent --no-editor`

--full migrates transpile-only scripts on an existing Vite+ project

```
VITE+ - The Unified Toolchain for the Web

▲ No package manager is declared in package.json; using pnpm for this migration without adding a pin. Run `vp env pin` to declare it explicitly.
● ts-node scripts detected. Auto-migrating supported commands to Vite+...
◇ Updated . to Vite+ <version>
• Node <version> pnpm <version>
• Dependencies:
vite → <version>
• ts-node scripts migrated to Vite+
```

## `vpt print-file package.json`

```
{
"devDependencies": {
"typescript": "file:.fixture/typescript",
"vite": "catalog:",
"vite-plus": "catalog:"
},
"name": "migration-ts-node",
"scripts": {
"env-loader": "NODE_OPTIONS='--import vite-plus/register' node ./scripts/seed.ts",
"loader": "node --import vite-plus/register ./scripts/seed.ts",
"register": "node --import=vite-plus/register ./scripts/seed.ts",
"seed": "vpx ./scripts/seed.ts"
},
"type": "module"
}
```

## `vp run seed`

```
$ vpx ./scripts/seed.ts ⊘ cache disabled
ready
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# migration_ts_node_config

## `vpt rm scripts/release.ts`


## `vpt write-file tsconfig.json '{"ts-node":{"transpileOnly":true}}
'`


## `vpt json-edit package.json scripts.seed 'ts-node ./scripts/seed.ts'`


## `vp migrate --no-interactive --no-hooks --no-agent --no-editor`

the runner pins its config; Node preloads keep ts-node until TS_NODE_PROJECT names that file

```
VITE+ - The Unified Toolchain for the Web

● ts-node scripts detected. Auto-migrating supported commands to Vite+...
◇ Migrated . to Vite+ <version>
• Node <version> pnpm <version>
• 1 config update applied
• ts-node scripts migrated to Vite+
! Warnings:
- Kept ts-node in package.json scripts "env-loader": the Node.js preload needs the ts-node config pinned explicitly; set TS_NODE_PROJECT to its file path before migrating.
- Kept ts-node in package.json scripts "loader": the Node.js preload needs the ts-node config pinned explicitly; set TS_NODE_PROJECT to its file path before migrating.
- Kept ts-node in package.json scripts "register": the Node.js preload needs the ts-node config pinned explicitly; set TS_NODE_PROJECT to its file path before migrating.
- Kept the ts-node devDependency in package.json: `scripts` in package.json still uses ts-node.
```

## `vpt print-file package.json`

```
{
"devDependencies": {
"ts-node": "file:.fixture/ts-node",
"typescript": "file:.fixture/typescript",
"vite": "catalog:",
"vite-plus": "catalog:"
},
"name": "migration-ts-node",
"scripts": {
"env-loader": "NODE_OPTIONS='--require ts-node/register/transpile-only' node ./scripts/seed.ts",
"loader": "node --loader ts-node/esm/transpile-only ./scripts/seed.ts",
"register": "node --require=ts-node/register/transpile-only ./scripts/seed.ts",
"seed": "vpx --tsconfig=tsconfig.json ./scripts/seed.ts"
},
"type": "module"
}
```

## `vp run seed`

```
$ vpx --tsconfig=tsconfig.json ./scripts/seed.ts ⊘ cache disabled
ready
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# migration_ts_node_kept

## `vpt json-edit package.json scripts.checked 'ts-node ./scripts/seed.ts'`


## `vp migrate --no-interactive --no-hooks --no-agent --no-editor`

commands that type-check keep ts-node and explain the difference

```
VITE+ - The Unified Toolchain for the Web

● ts-node scripts detected. Auto-migrating supported commands to Vite+...
◇ Migrated . to Vite+ <version>
• Node <version> pnpm <version>
• 1 config update applied
• ts-node scripts migrated to Vite+
! Warnings:
- Kept ts-node in package.json scripts "checked": this ts-node command type-checks before execution; `vpx` does not type-check. Keep a separate type-check command before changing this script.
- Kept the ts-node devDependency in package.json: `scripts` in package.json still uses ts-node.
```

## `vpt print-file package.json`

```
{
"devDependencies": {
"ts-node": "file:.fixture/ts-node",
"typescript": "file:.fixture/typescript",
"vite": "catalog:",
"vite-plus": "catalog:"
},
"name": "migration-ts-node",
"scripts": {
"checked": "ts-node ./scripts/seed.ts",
"env-loader": "NODE_OPTIONS='--import vite-plus/register' node ./scripts/seed.ts",
"loader": "node --import vite-plus/register ./scripts/seed.ts",
"register": "node --import=vite-plus/register ./scripts/seed.ts",
"seed": "vpx ./scripts/seed.ts"
},
"type": "module"
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# migration_ts_node_scripts

Local stub dependencies keep the runner migration independent of npm packages.

## `vp migrate --no-interactive --no-hooks --no-agent --no-editor`

transpile-only commands and shebangs use vpx; preloads keep Node.js

```
VITE+ - The Unified Toolchain for the Web

● ts-node scripts detected. Auto-migrating supported commands to Vite+...
◇ Migrated . to Vite+ <version>
• Node <version> pnpm <version>
• 1 config update applied
• ts-node scripts migrated to Vite+
```

## `vpt print-file package.json`

```
{
"name": "migration-ts-node",
"type": "module",
"scripts": {
"seed": "vpx ./scripts/seed.ts",
"loader": "node --import vite-plus/register ./scripts/seed.ts",
"register": "node --import=vite-plus/register ./scripts/seed.ts",
"env-loader": "NODE_OPTIONS='--import vite-plus/register' node ./scripts/seed.ts"
},
"devDependencies": {
"typescript": "file:.fixture/typescript",
"vite": "catalog:",
"vite-plus": "catalog:"
}
}
```

## `vpt print-file scripts/release.ts`

```
#!/usr/bin/env -S vpx
const version: string = '1.0.0';
console.log(version);
```

## `vp run seed`

the migrated script runs TypeScript without ts-node

```
$ vpx ./scripts/seed.ts ⊘ cache disabled
ready
```

## `vp run loader`

```
$ node --import vite-plus/register ./scripts/seed.ts ⊘ cache disabled
ready
```

## `vp run register`

the CommonJS registration option becomes the public ESM preload

```
$ node --import=vite-plus/register ./scripts/seed.ts ⊘ cache disabled
ready
```
50 changes: 37 additions & 13 deletions docs/guide/migrate-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,19 +250,20 @@ The `prefer-vite-plus-imports` lint rule follows the same Nuxt exception, so lin

Migration rewrites commands provided by the Vite+ toolchain in `package.json` scripts while preserving their arguments:

| Before | After |
| ------------- | ------------------------------------------- |
| `vite` | `vp dev`, or the matching `vp` subcommand |
| `vitest` | `vp test` |
| `oxlint` | `vp lint` |
| `oxfmt` | `vp fmt` |
| `tsdown` | `vp pack` |
| `lint-staged` | `vp staged` |
| `eslint` | `vp lint`, when its optional migration runs |
| `prettier` | `vp fmt`, when its optional migration runs |
| `tsup` | `vp pack`, when its optional migration runs |
| `tsx` | `vpx`, when its optional migration runs |
| `oxnode` | `vpx`, when its optional migration runs |
| Before | After |
| ------------- | -------------------------------------------- |
| `vite` | `vp dev`, or the matching `vp` subcommand |
| `vitest` | `vp test` |
| `oxlint` | `vp lint` |
| `oxfmt` | `vp fmt` |
| `tsdown` | `vp pack` |
| `lint-staged` | `vp staged` |
| `eslint` | `vp lint`, when its optional migration runs |
| `prettier` | `vp fmt`, when its optional migration runs |
| `tsup` | `vp pack`, when its optional migration runs |
| `tsx` | `vpx`, when its optional migration runs |
| `oxnode` | `vpx`, when its optional migration runs |
| `ts-node` | `vpx`, for supported transpile-only commands |

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 `--`:

Expand Down Expand Up @@ -324,6 +325,29 @@ Node.js preload commands also stay unchanged when another startup preload can ch

The `@oxc-node/cli` and `@oxc-node/core` development dependencies are removed only when no remaining use needs them. Loader references, API imports, CI commands, hooks, and shell scripts can keep these dependencies installed. Runtime and peer dependencies stay in place.

### ts-node to `vpx`

Migration offers to replace supported ts-node commands with `vpx` only when type-checking is explicitly disabled. Plain `ts-node` type-checks before execution; `vpx` does not. Commands that still type-check stay unchanged with a warning, even when another option enables transpile-only mode.

| Before | After |
| --------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| `ts-node --transpileOnly ./scripts/seed.ts` | `vpx ./scripts/seed.ts` |
| `ts-node -T -P tsconfig.scripts.json ./scripts/seed.ts` | `vpx --tsconfig=tsconfig.scripts.json ./scripts/seed.ts` |
| `ts-node-transpile-only ./scripts/seed.ts` | `vpx ./scripts/seed.ts` |
| `node -r ts-node/register/transpile-only ./scripts/seed.ts` | `node --import vite-plus/register ./scripts/seed.ts` |
| `node --loader ts-node/esm/transpile-only ./scripts/seed.ts` | `node --import vite-plus/register ./scripts/seed.ts` |
| `NODE_OPTIONS='--require ts-node/register/transpile-only' node ./scripts/seed.ts` | `NODE_OPTIONS='--import vite-plus/register' node ./scripts/seed.ts` |

The migration also reads `transpileOnly` and `typeCheck` from local JSONC tsconfig files, local `extends` chains, and literal `TS_NODE_TRANSPILE_ONLY` / `TS_NODE_TYPE_CHECK` assignments. `-P`, `--project`, and `TS_NODE_PROJECT` select the configuration. A directory passed to `-P` or `--project` resolves to its `tsconfig.json`; a directory in `TS_NODE_PROJECT` stays unchanged. When ts-node discovers a config implicitly, migration pins that file with `--tsconfig` so `vpx` uses it for the whole run. `ts-node-esm` and `--esm` are supported when type-checking is disabled.

Supported Node.js preload commands keep Node.js and replace both the old loader option and module with `--import vite-plus/register`. Literal `NODE_OPTIONS` assignments also migrate when they reach a known `node` command and pass the same checks. An existing `TS_NODE_PROJECT` file selection stays in place. A preload that needs an implicit config pinned stays unchanged: set `TS_NODE_PROJECT` to the config file before migration, because Node.js cannot take `--tsconfig`.

Preloads also stay unchanged when the working directory has no config but the entry file would discover a different one. Migration does not scan imported modules for additional config files.

Custom compilers, transpilers, SWC, ts-node compiler overrides, unsupported configuration inheritance, eval, and REPL usage stay unchanged. Environment files, Node.js config files, additional preloads, and unsupported `NODE_OPTIONS` assignments also keep the original command because they can change ts-node settings before registration. Nonempty `VP_SCRIPT_TSCONFIG`, `TSX_TSCONFIG_PATH`, or `OXC_TSCONFIG_PATH` assignments stay unchanged because they would select a different config. Migration checks named scripts for known module incompatibilities; it does not follow their imports. Migrated scripts use the [tsconfig and module rules of `vpx`](/guide/vpx#tsconfig).

As with oxnode, migration rewrites package scripts, JSON lint-staged tasks, and supported env shebangs. Shebangs that need a pinned config stay unchanged because the config path depends on the caller's working directory. Existing Vite+ projects use `vp migrate --full`. The `ts-node` development dependency is removed only when no remaining command, loader, API import, or config reference needs it. Runtime and peer dependencies remain, and `typescript` is not removed.

## Continuous Integration Rules

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.
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/migrate.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { migrationPrompt, upgradePrompt } from '../.vitepress/theme/data/migrati

## Overview

This command is the starting point for consolidating separate Vite, Vitest, Oxlint, Oxfmt, ESLint, Prettier, tsdown, tsup, tsx, and oxnode setups into Vite+.
This command is the starting point for consolidating separate Vite, Vitest, Oxlint, Oxfmt, ESLint, Prettier, tsdown, tsup, tsx, oxnode, and ts-node setups into Vite+.

Use it when you want to take an existing project and move it onto the Vite+ defaults instead of wiring each tool by hand.

Expand Down
Loading
Loading