diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/binding-guard.cjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/binding-guard.cjs new file mode 100644 index 0000000000..d954506ca3 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/binding-guard.cjs @@ -0,0 +1,9 @@ +// An unsupported runtime must stop before loading a native binding. +const Module = require('node:module'); +const original = Module._load; +Module._load = function (request, ...args) { + if (/binding[\\/]index\.(?:cjs|js)$/.test(request) || request.endsWith('.node')) { + throw new Error('NATIVE_BINDING_LOADED_BEFORE_VERSION_CHECK'); + } + return original.call(this, request, ...args); +}; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/compat.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/compat.json new file mode 100644 index 0000000000..8922635f5b --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/compat.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "baseUrl": "..", + "paths": { + "@value": [ + "./src/config-compat.ts" + ] + }, + "useDefineForClassFields": true + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/explicit.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/explicit.json new file mode 100644 index 0000000000..db215fb662 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/explicit.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "baseUrl": "..", + "paths": { + "@value": [ + "./src/config-explicit.ts" + ] + }, + "useDefineForClassFields": true + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/parent.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/parent.json new file mode 100644 index 0000000000..7124d6f287 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/parent.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "baseUrl": "..", + "paths": { + "@value": [ + "./src/config-parent.ts" + ] + }, + "useDefineForClassFields": true + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/upstream.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/upstream.json new file mode 100644 index 0000000000..3e8896ed47 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/upstream.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "baseUrl": "..", + "paths": { + "@value": [ + "./src/config-upstream.ts" + ] + }, + "useDefineForClassFields": true + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/worker-one.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/worker-one.json new file mode 100644 index 0000000000..576137795b --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/worker-one.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "baseUrl": "..", + "paths": { + "@value": [ + "./src/config-worker-one.ts" + ] + }, + "useDefineForClassFields": false + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/worker-two.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/worker-two.json new file mode 100644 index 0000000000..9405e11031 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/configs with spaces/worker-two.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "baseUrl": "..", + "paths": { + "@value": [ + "./src/config-worker-two.ts" + ] + }, + "useDefineForClassFields": true + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/dependencies/fixture-ts-import/error.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/dependencies/fixture-ts-import/error.ts new file mode 100644 index 0000000000..6da43f5634 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/dependencies/fixture-ts-import/error.ts @@ -0,0 +1,4 @@ +enum Failure { Value = 'dependency failure' } +export function fail() { + throw new Error(Failure.Value); +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/dependencies/fixture-ts-import/index.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/dependencies/fixture-ts-import/index.ts new file mode 100644 index 0000000000..e73fd8a7b9 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/dependencies/fixture-ts-import/index.ts @@ -0,0 +1,2 @@ +export enum Kind { Value = 'esm dependency' } +export const kind: Kind = Kind.Value; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/dependencies/fixture-ts-import/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/dependencies/fixture-ts-import/package.json new file mode 100644 index 0000000000..4d77d72565 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/dependencies/fixture-ts-import/package.json @@ -0,0 +1,8 @@ +{ + "name": "fixture-ts-import", + "type": "module", + "exports": { + ".": "./index.ts", + "./error": "./error.ts" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/dependencies/fixture-ts-require/index.cts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/dependencies/fixture-ts-require/index.cts new file mode 100644 index 0000000000..9821302c94 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/dependencies/fixture-ts-require/index.cts @@ -0,0 +1,2 @@ +enum Kind { Value = 'cjs dependency' } +module.exports = Kind.Value; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/dependencies/fixture-ts-require/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/dependencies/fixture-ts-require/package.json new file mode 100644 index 0000000000..bd0c61a274 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/dependencies/fixture-ts-require/package.json @@ -0,0 +1,4 @@ +{ + "name": "fixture-ts-require", + "main": "./index.cts" +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/package.json new file mode 100644 index 0000000000..e455992a16 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/package.json @@ -0,0 +1,9 @@ +{ + "name": "command-script-import", + "version": "1.0.0", + "private": true, + "type": "module", + "engines": { + "node": "22.18.0" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/resolved-import.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/resolved-import.mjs new file mode 100644 index 0000000000..7f66c4f4ac --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/resolved-import.mjs @@ -0,0 +1,13 @@ +import { spawnSync } from 'node:child_process'; +import { createRequire } from 'node:module'; +import { pathToFileURL } from 'node:url'; + +const register = createRequire(import.meta.url).resolve('vite-plus/register'); +const preload = process.argv[2] === 'path' ? register : pathToFileURL(register).href; +console.log('preload:', preload.replaceAll('\\', '/')); +const child = spawnSync(process.execPath, ['--import', preload, './src/name #%.ts'], { + stdio: 'inherit', + timeout: 30000, +}); +if (child.error) throw child.error; +process.exitCode = child.status ?? 1; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/runtime.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/runtime.mjs new file mode 100644 index 0000000000..8bb433404c --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/runtime.mjs @@ -0,0 +1,16 @@ +import { createRequire } from 'node:module'; +import { pathToFileURL } from 'node:url'; + +const require = createRequire(import.meta.url); +const register = require.resolve('vite-plus/register'); +console.log(JSON.stringify({ + node: process.versions.node, + requireExport: register.replaceAll('\\', '/'), + importExport: import.meta.resolve('vite-plus/register'), + sameExport: import.meta.resolve('vite-plus/register') === pathToFileURL(register).href, +}, null, 2)); +try { + console.log('tsx:', require.resolve('tsx')); +} catch (error) { + console.log('tsx:', error.code); +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots.toml new file mode 100644 index 0000000000..b5897eccde --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots.toml @@ -0,0 +1,282 @@ +[[case]] +name = "script_import_node22_async" +vp = ["local","global"] +env = { VP_NODE_VERSION = "22.18.0" } +unset-env = ["NODE_OPTIONS", "VP_SCRIPT_TSCONFIG", "TSX_TSCONFIG_PATH", "OXC_TSCONFIG_PATH", "TS_NODE_PROJECT"] +comment = "The public installed package preload works without tsx on the asynchronous hook path." +steps = [ + { argv = ["vpt","cp","-r","dependencies","node_modules"], snapshot = false }, + { argv = ["node","runtime.mjs"] }, + { argv = ["node","--import=vite-plus/register","./src/main.ts"] }, + { argv = ["node","--import","vite-plus/register","./src/main.cts"] }, + { argv = ["node","resolved-import.mjs","url"], comment = "A resolved file URL and an encoded script filename reach Node unchanged." }, + { argv = ["node","./src/main.ts"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","./src/main.cts"], envs = [["NODE_OPTIONS","--import vite-plus/register"]] }, + { argv = ["node","src/argv-host.mjs","--import=vite-plus/register","--import","./src/setup.ts","./src/argv.ts","one","--","--flag","space argument"], continue-on-failure = true, comment = "Report argv, preload state, process identity, and the script's exit code." }, + { argv = ["node","src/argv-host.mjs","--import","./src/setup.ts","./src/argv.ts","one","--","--flag","space argument"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]], continue-on-failure = true }, + { argv = ["node","--import=vite-plus/register","--input-type=module","--eval","const { format } = await import('./src/module.mts'); console.log(format);"] }, + { argv = ["node","--import=vite-plus/register","--eval","console.log(require('./src/common.cts').format);"] }, + { argv = ["vpt","pipe-stdin","const { format } = await import('./src/module.mts'); console.log(format);","--","node","--import=vite-plus/register","--input-type=module","-"] }, + { argv = ["vpt","pipe-stdin","console.log(require('./src/common.cts').format);","--","node","--import=vite-plus/register","-"] }, + { argv = ["node","--input-type=module","--eval","await import('vite-plus/register'); const { format } = await import('./src/module.mts'); console.log(format);"] }, + { argv = ["node","--import=vite-plus/register","--test","--test-reporter=dot","./tests/enum.test.ts"] }, + { argv = ["node","--import=vite-plus/register","--test","--test-reporter=dot"], cwd = "tests", comment = "Node discovers the TypeScript test without an explicit filename." }, + { argv = ["node","--import=vite-plus/register","./src/children.mjs"] }, + { argv = ["node","./src/children.mjs"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","./src/spawn.mjs"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","--import=vite-plus/register","./src/children.mjs"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config-workers.mjs"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/parent.json"],["TS_NODE_PROJECT","configs with spaces/parent.json"],["OXC_TSCONFIG_PATH","configs with spaces/parent.json"]] }, + { argv = ["node","./src/worker-host.mjs"] }, + { argv = ["node","--import=vite-plus/register","./src/cluster.mjs"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","./src/internal-host.mjs"] }, + { argv = ["node","--import=vite-plus/register","--import","vite-plus/register","./src/dedup.mjs"] }, + { argv = ["node","--import=vite-plus/register","./src/dedup.mjs"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TS_NODE_PROJECT","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TS_NODE_PROJECT","configs with spaces/compat.json"],["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"],["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["VP_SCRIPT_TSCONFIG",""],["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["VP_SCRIPT_TSCONFIG","configs with spaces/explicit.json"],["TSX_TSCONFIG_PATH","configs with spaces/compat.json"],["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"],["TS_NODE_PROJECT","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/error-report.mjs","./error.ts"], continue-on-failure = true, comment = "Show the application source-map location." }, + { argv = ["node","--import=vite-plus/register","./src/error-report.mjs","./dependency-error.ts"], continue-on-failure = true, comment = "Show the installed dependency's source-map location." }, + { argv = ["vpx","--import=vite-plus/register","./src/dedup.mjs"], comment = "The CLI and public preload share one registration." }, +] + +[[case]] +name = "script_import_node22_sync" +vp = "global" +env = { VP_NODE_VERSION = "22.23.2" } +unset-env = ["NODE_OPTIONS", "VP_SCRIPT_TSCONFIG", "TSX_TSCONFIG_PATH", "OXC_TSCONFIG_PATH", "TS_NODE_PROJECT"] +comment = "The complete preload contract also runs on Node.js 22 synchronous hooks." +steps = [ + { argv = ["vpt","cp","-r","dependencies","node_modules"], snapshot = false }, + { argv = ["node","runtime.mjs"] }, + { argv = ["node","--import=vite-plus/register","./src/main.ts"] }, + { argv = ["node","--import","vite-plus/register","./src/main.cts"] }, + { argv = ["node","resolved-import.mjs","url"], comment = "A resolved file URL and an encoded script filename reach Node unchanged." }, + { argv = ["node","./src/main.ts"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","./src/main.cts"], envs = [["NODE_OPTIONS","--import vite-plus/register"]] }, + { argv = ["node","src/argv-host.mjs","--import=vite-plus/register","--import","./src/setup.ts","./src/argv.ts","one","--","--flag","space argument"], continue-on-failure = true, comment = "Report argv, preload state, process identity, and the script's exit code." }, + { argv = ["node","src/argv-host.mjs","--import","./src/setup.ts","./src/argv.ts","one","--","--flag","space argument"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]], continue-on-failure = true }, + { argv = ["node","--import=vite-plus/register","--input-type=module","--eval","const { format } = await import('./src/module.mts'); console.log(format);"] }, + { argv = ["node","--import=vite-plus/register","--eval","console.log(require('./src/common.cts').format);"] }, + { argv = ["vpt","pipe-stdin","const { format } = await import('./src/module.mts'); console.log(format);","--","node","--import=vite-plus/register","--input-type=module","-"] }, + { argv = ["vpt","pipe-stdin","console.log(require('./src/common.cts').format);","--","node","--import=vite-plus/register","-"] }, + { argv = ["node","--input-type=module","--eval","await import('vite-plus/register'); const { format } = await import('./src/module.mts'); console.log(format);"] }, + { argv = ["node","--import=vite-plus/register","--test","--test-reporter=dot","./tests/enum.test.ts"] }, + { argv = ["node","--import=vite-plus/register","--test","--test-reporter=dot"], cwd = "tests", comment = "Node discovers the TypeScript test without an explicit filename." }, + { argv = ["node","--import=vite-plus/register","./src/children.mjs"] }, + { argv = ["node","./src/children.mjs"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","./src/spawn.mjs"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","--import=vite-plus/register","./src/children.mjs"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config-workers.mjs"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/parent.json"],["TS_NODE_PROJECT","configs with spaces/parent.json"],["OXC_TSCONFIG_PATH","configs with spaces/parent.json"]] }, + { argv = ["node","./src/worker-host.mjs"] }, + { argv = ["node","--import=vite-plus/register","./src/cluster.mjs"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","./src/internal-host.mjs"] }, + { argv = ["node","--import=vite-plus/register","--import","vite-plus/register","./src/dedup.mjs"] }, + { argv = ["node","--import=vite-plus/register","./src/dedup.mjs"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TS_NODE_PROJECT","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TS_NODE_PROJECT","configs with spaces/compat.json"],["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"],["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["VP_SCRIPT_TSCONFIG",""],["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["VP_SCRIPT_TSCONFIG","configs with spaces/explicit.json"],["TSX_TSCONFIG_PATH","configs with spaces/compat.json"],["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"],["TS_NODE_PROJECT","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/error-report.mjs","./error.ts"], continue-on-failure = true, comment = "Show the application source-map location." }, + { argv = ["node","--import=vite-plus/register","./src/error-report.mjs","./dependency-error.ts"], continue-on-failure = true, comment = "Show the installed dependency's source-map location." }, + { argv = ["vpx","--import=vite-plus/register","./src/dedup.mjs"], comment = "The CLI and public preload share one registration." }, +] + +[[case]] +name = "script_import_node24_async" +vp = "global" +env = { VP_NODE_VERSION = "24.11.0" } +unset-env = ["NODE_OPTIONS", "VP_SCRIPT_TSCONFIG", "TSX_TSCONFIG_PATH", "OXC_TSCONFIG_PATH", "TS_NODE_PROJECT"] +comment = "The earliest supported Node.js 24 also uses asynchronous hooks, including workers with independent configs." +steps = [ + { argv = ["vpt","cp","-r","dependencies","node_modules"], snapshot = false }, + { argv = ["node","runtime.mjs"] }, + { argv = ["node","--import=vite-plus/register","./src/main.ts"] }, + { argv = ["node","--import","vite-plus/register","./src/main.cts"] }, + { argv = ["node","resolved-import.mjs","url"], comment = "A resolved file URL and an encoded script filename reach Node unchanged." }, + { argv = ["node","./src/main.ts"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","./src/main.cts"], envs = [["NODE_OPTIONS","--import vite-plus/register"]] }, + { argv = ["node","src/argv-host.mjs","--import=vite-plus/register","--import","./src/setup.ts","./src/argv.ts","one","--","--flag","space argument"], continue-on-failure = true, comment = "Report argv, preload state, process identity, and the script's exit code." }, + { argv = ["node","src/argv-host.mjs","--import","./src/setup.ts","./src/argv.ts","one","--","--flag","space argument"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]], continue-on-failure = true }, + { argv = ["node","--import=vite-plus/register","--input-type=module","--eval","const { format } = await import('./src/module.mts'); console.log(format);"] }, + { argv = ["node","--import=vite-plus/register","--eval","console.log(require('./src/common.cts').format);"] }, + { argv = ["vpt","pipe-stdin","const { format } = await import('./src/module.mts'); console.log(format);","--","node","--import=vite-plus/register","--input-type=module","-"] }, + { argv = ["vpt","pipe-stdin","console.log(require('./src/common.cts').format);","--","node","--import=vite-plus/register","-"] }, + { argv = ["node","--input-type=module","--eval","await import('vite-plus/register'); const { format } = await import('./src/module.mts'); console.log(format);"] }, + { argv = ["node","--import=vite-plus/register","--test","--test-reporter=dot","./tests/enum.test.ts"] }, + { argv = ["node","--import=vite-plus/register","--test","--test-reporter=dot"], cwd = "tests", comment = "Node discovers the TypeScript test without an explicit filename." }, + { argv = ["node","--import=vite-plus/register","./src/children.mjs"] }, + { argv = ["node","./src/children.mjs"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","./src/spawn.mjs"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","--import=vite-plus/register","./src/children.mjs"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config-workers.mjs"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/parent.json"],["TS_NODE_PROJECT","configs with spaces/parent.json"],["OXC_TSCONFIG_PATH","configs with spaces/parent.json"]] }, + { argv = ["node","./src/worker-host.mjs"] }, + { argv = ["node","--import=vite-plus/register","./src/cluster.mjs"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","./src/internal-host.mjs"] }, + { argv = ["node","--import=vite-plus/register","--import","vite-plus/register","./src/dedup.mjs"] }, + { argv = ["node","--import=vite-plus/register","./src/dedup.mjs"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TS_NODE_PROJECT","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TS_NODE_PROJECT","configs with spaces/compat.json"],["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"],["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["VP_SCRIPT_TSCONFIG",""],["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["VP_SCRIPT_TSCONFIG","configs with spaces/explicit.json"],["TSX_TSCONFIG_PATH","configs with spaces/compat.json"],["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"],["TS_NODE_PROJECT","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/error-report.mjs","./error.ts"], continue-on-failure = true, comment = "Show the application source-map location." }, + { argv = ["node","--import=vite-plus/register","./src/error-report.mjs","./dependency-error.ts"], continue-on-failure = true, comment = "Show the installed dependency's source-map location." }, + { argv = ["vpx","--import=vite-plus/register","./src/dedup.mjs"], comment = "The CLI and public preload share one registration." }, +] + +[[case]] +name = "script_import_node24_sync" +vp = "global" +env = { VP_NODE_VERSION = "24.12.0" } +unset-env = ["NODE_OPTIONS", "VP_SCRIPT_TSCONFIG", "TSX_TSCONFIG_PATH", "OXC_TSCONFIG_PATH", "TS_NODE_PROJECT"] +comment = "The complete preload contract runs on Node.js 24 synchronous hooks." +steps = [ + { argv = ["vpt","cp","-r","dependencies","node_modules"], snapshot = false }, + { argv = ["node","runtime.mjs"] }, + { argv = ["node","--import=vite-plus/register","./src/main.ts"] }, + { argv = ["node","--import","vite-plus/register","./src/main.cts"] }, + { argv = ["node","resolved-import.mjs","url"], comment = "A resolved file URL and an encoded script filename reach Node unchanged." }, + { argv = ["node","./src/main.ts"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","./src/main.cts"], envs = [["NODE_OPTIONS","--import vite-plus/register"]] }, + { argv = ["node","src/argv-host.mjs","--import=vite-plus/register","--import","./src/setup.ts","./src/argv.ts","one","--","--flag","space argument"], continue-on-failure = true, comment = "Report argv, preload state, process identity, and the script's exit code." }, + { argv = ["node","src/argv-host.mjs","--import","./src/setup.ts","./src/argv.ts","one","--","--flag","space argument"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]], continue-on-failure = true }, + { argv = ["node","--import=vite-plus/register","--input-type=module","--eval","const { format } = await import('./src/module.mts'); console.log(format);"] }, + { argv = ["node","--import=vite-plus/register","--eval","console.log(require('./src/common.cts').format);"] }, + { argv = ["vpt","pipe-stdin","const { format } = await import('./src/module.mts'); console.log(format);","--","node","--import=vite-plus/register","--input-type=module","-"] }, + { argv = ["vpt","pipe-stdin","console.log(require('./src/common.cts').format);","--","node","--import=vite-plus/register","-"] }, + { argv = ["node","--input-type=module","--eval","await import('vite-plus/register'); const { format } = await import('./src/module.mts'); console.log(format);"] }, + { argv = ["node","--import=vite-plus/register","--test","--test-reporter=dot","./tests/enum.test.ts"] }, + { argv = ["node","--import=vite-plus/register","--test","--test-reporter=dot"], cwd = "tests", comment = "Node discovers the TypeScript test without an explicit filename." }, + { argv = ["node","--import=vite-plus/register","./src/children.mjs"] }, + { argv = ["node","./src/children.mjs"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","./src/spawn.mjs"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","--import=vite-plus/register","./src/children.mjs"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config-workers.mjs"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/parent.json"],["TS_NODE_PROJECT","configs with spaces/parent.json"],["OXC_TSCONFIG_PATH","configs with spaces/parent.json"]] }, + { argv = ["node","./src/worker-host.mjs"] }, + { argv = ["node","--import=vite-plus/register","./src/cluster.mjs"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","./src/internal-host.mjs"] }, + { argv = ["node","--import=vite-plus/register","--import","vite-plus/register","./src/dedup.mjs"] }, + { argv = ["node","--import=vite-plus/register","./src/dedup.mjs"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TS_NODE_PROJECT","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TS_NODE_PROJECT","configs with spaces/compat.json"],["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"],["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["VP_SCRIPT_TSCONFIG",""],["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["VP_SCRIPT_TSCONFIG","configs with spaces/explicit.json"],["TSX_TSCONFIG_PATH","configs with spaces/compat.json"],["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"],["TS_NODE_PROJECT","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/error-report.mjs","./error.ts"], continue-on-failure = true, comment = "Show the application source-map location." }, + { argv = ["node","--import=vite-plus/register","./src/error-report.mjs","./dependency-error.ts"], continue-on-failure = true, comment = "Show the installed dependency's source-map location." }, + { argv = ["vpx","--import=vite-plus/register","./src/dedup.mjs"], comment = "The CLI and public preload share one registration." }, +] + +[[case]] +name = "script_import_node26_sync" +vp = "global" +env = { VP_NODE_VERSION = "26.5.0" } +unset-env = ["NODE_OPTIONS", "VP_SCRIPT_TSCONFIG", "TSX_TSCONFIG_PATH", "OXC_TSCONFIG_PATH", "TS_NODE_PROJECT"] +comment = "The complete preload contract runs on Node.js 26 synchronous hooks." +steps = [ + { argv = ["vpt","cp","-r","dependencies","node_modules"], snapshot = false }, + { argv = ["node","runtime.mjs"] }, + { argv = ["node","--import=vite-plus/register","./src/main.ts"] }, + { argv = ["node","--import","vite-plus/register","./src/main.cts"] }, + { argv = ["node","resolved-import.mjs","url"], comment = "A resolved file URL and an encoded script filename reach Node unchanged." }, + { argv = ["node","./src/main.ts"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","./src/main.cts"], envs = [["NODE_OPTIONS","--import vite-plus/register"]] }, + { argv = ["node","src/argv-host.mjs","--import=vite-plus/register","--import","./src/setup.ts","./src/argv.ts","one","--","--flag","space argument"], continue-on-failure = true, comment = "Report argv, preload state, process identity, and the script's exit code." }, + { argv = ["node","src/argv-host.mjs","--import","./src/setup.ts","./src/argv.ts","one","--","--flag","space argument"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]], continue-on-failure = true }, + { argv = ["node","--import=vite-plus/register","--input-type=module","--eval","const { format } = await import('./src/module.mts'); console.log(format);"] }, + { argv = ["node","--import=vite-plus/register","--eval","console.log(require('./src/common.cts').format);"] }, + { argv = ["vpt","pipe-stdin","const { format } = await import('./src/module.mts'); console.log(format);","--","node","--import=vite-plus/register","--input-type=module","-"] }, + { argv = ["vpt","pipe-stdin","console.log(require('./src/common.cts').format);","--","node","--import=vite-plus/register","-"] }, + { argv = ["node","--input-type=module","--eval","await import('vite-plus/register'); const { format } = await import('./src/module.mts'); console.log(format);"] }, + { argv = ["node","--import=vite-plus/register","--test","--test-reporter=dot","./tests/enum.test.ts"] }, + { argv = ["node","--import=vite-plus/register","--test","--test-reporter=dot"], cwd = "tests", comment = "Node discovers the TypeScript test without an explicit filename." }, + { argv = ["node","--import=vite-plus/register","./src/children.mjs"] }, + { argv = ["node","./src/children.mjs"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","./src/spawn.mjs"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","--import=vite-plus/register","./src/children.mjs"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config-workers.mjs"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/parent.json"],["TS_NODE_PROJECT","configs with spaces/parent.json"],["OXC_TSCONFIG_PATH","configs with spaces/parent.json"]] }, + { argv = ["node","./src/worker-host.mjs"] }, + { argv = ["node","--import=vite-plus/register","./src/cluster.mjs"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","./src/internal-host.mjs"] }, + { argv = ["node","--import=vite-plus/register","--import","vite-plus/register","./src/dedup.mjs"] }, + { argv = ["node","--import=vite-plus/register","./src/dedup.mjs"], envs = [["NODE_OPTIONS","--import=vite-plus/register"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TS_NODE_PROJECT","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TS_NODE_PROJECT","configs with spaces/compat.json"],["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["TSX_TSCONFIG_PATH","configs with spaces/compat.json"],["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["VP_SCRIPT_TSCONFIG",""],["TSX_TSCONFIG_PATH","configs with spaces/compat.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/config.ts"], envs = [["VP_SCRIPT_TSCONFIG","configs with spaces/explicit.json"],["TSX_TSCONFIG_PATH","configs with spaces/compat.json"],["OXC_TSCONFIG_PATH","configs with spaces/upstream.json"],["TS_NODE_PROJECT","configs with spaces/upstream.json"]] }, + { argv = ["node","--import=vite-plus/register","./src/error-report.mjs","./error.ts"], continue-on-failure = true, comment = "Show the application source-map location." }, + { argv = ["node","--import=vite-plus/register","./src/error-report.mjs","./dependency-error.ts"], continue-on-failure = true, comment = "Show the installed dependency's source-map location." }, + { argv = ["vpx","--import=vite-plus/register","./src/dedup.mjs"], comment = "The CLI and public preload share one registration." }, +] + +[[case]] +name = "script_import_node22_async_absolute_path" +vp = ["local","global"] +env = { VP_NODE_VERSION = "22.18.0" } +skip-platforms = ["windows"] +comment = "Node also accepts an absolute POSIX path for the public preload." +steps = [["node", "resolved-import.mjs", "path"]] + +[[case]] +name = "script_import_node22_sync_absolute_path" +vp = "global" +env = { VP_NODE_VERSION = "22.23.2" } +skip-platforms = ["windows"] +comment = "Node also accepts an absolute POSIX path for the public preload." +steps = [["node", "resolved-import.mjs", "path"]] + +[[case]] +name = "script_import_node24_async_absolute_path" +vp = "global" +env = { VP_NODE_VERSION = "24.11.0" } +skip-platforms = ["windows"] +comment = "Node also accepts an absolute POSIX path for the public preload." +steps = [["node", "resolved-import.mjs", "path"]] + +[[case]] +name = "script_import_node24_sync_absolute_path" +vp = "global" +env = { VP_NODE_VERSION = "24.12.0" } +skip-platforms = ["windows"] +comment = "Node also accepts an absolute POSIX path for the public preload." +steps = [["node", "resolved-import.mjs", "path"]] + +[[case]] +name = "script_import_node26_sync_absolute_path" +vp = "global" +env = { VP_NODE_VERSION = "26.5.0" } +skip-platforms = ["windows"] +comment = "Node also accepts an absolute POSIX path for the public preload." +steps = [["node", "resolved-import.mjs", "path"]] + +[[case]] +name = "script_import_native_watch" +vp = "global" +comment = "A public preload preserves Node.js native watch mode." +steps = [["node", "verify-watch.mjs"]] + +[[case]] +name = "script_import_unsupported_node" +vp = "global" +env = { VP_NODE_VERSION = "20.19.0" } +comment = "Unsupported Node.js reaches the version diagnostic before loading native code." +steps = [["node", "verify-unsupported.mjs"]] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_native_watch.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_native_watch.md new file mode 100644 index 0000000000..c5692e29a9 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_native_watch.md @@ -0,0 +1,9 @@ +# script_import_native_watch + +A public preload preserves Node.js native watch mode. + +## `node verify-watch.mjs` + +``` +native node --watch reloads an imported TypeScript dependency +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_async.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_async.global.md new file mode 100644 index 0000000000..92f2e92e37 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_async.global.md @@ -0,0 +1,600 @@ +# script_import_node22_async + +The public installed package preload works without tsx on the asynchronous hook path. + +## `vpt cp -r dependencies node_modules` + + +## `node runtime.mjs` + +``` +{ + "node": "22.18.0", + "requireExport": "/packages/cli/dist/register.js", + "importExport": "file:///packages/cli/dist/register.js", + "sameExport": true +} +tsx: MODULE_NOT_FOUND +``` + +## `node --import=vite-plus/register ./src/main.ts` + +``` +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node --import vite-plus/register ./src/main.cts` + +``` +{ + "common": { + "format": "cts" + }, + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node resolved-import.mjs url` + +A resolved file URL and an encoded script filename reach Node unchanged. + +``` +preload: file:///packages/cli/dist/register.js +encoded filename +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/main.ts` + +``` +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `NODE_OPTIONS=--import vite-plus/register node ./src/main.cts` + +``` +{ + "common": { + "format": "cts" + }, + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node src/argv-host.mjs --import=vite-plus/register --import ./src/setup.ts ./src/argv.ts one -- --flag 'space argument'` + +Report argv, preload state, process identity, and the script's exit code. + +**Exit code:** 7 + +``` +{ + "sameExecutable": true, + "directChild": true, + "absoluteScript": true, + "script": "src/argv.ts", + "setup": "loaded", + "args": [ + "one", + "--", + "--flag", + "space argument" + ], + "execArgv": [ + "--import=vite-plus/register", + "--import", + "./src/setup.ts" + ] +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node src/argv-host.mjs --import ./src/setup.ts ./src/argv.ts one -- --flag 'space argument'` + +**Exit code:** 7 + +``` +{ + "sameExecutable": true, + "directChild": true, + "absoluteScript": true, + "script": "src/argv.ts", + "setup": "loaded", + "args": [ + "one", + "--", + "--flag", + "space argument" + ], + "execArgv": [ + "--import", + "./src/setup.ts" + ] +} +``` + +## `node --import=vite-plus/register --input-type=module --eval 'const { format } = await import('\''./src/module.mts'\''); console.log(format);'` + +``` +mts +``` + +## `node --import=vite-plus/register --eval console.log(require('./src/common.cts').format);` + +``` +cts +``` + +## `vpt pipe-stdin 'const { format } = await import('\''./src/module.mts'\''); console.log(format);' -- node --import=vite-plus/register --input-type=module -` + +``` +mts +``` + +## `vpt pipe-stdin console.log(require('./src/common.cts').format); -- node --import=vite-plus/register -` + +``` +cts +``` + +## `node --input-type=module --eval 'await import('\''vite-plus/register'\''); const { format } = await import('\''./src/module.mts'\''); console.log(format);'` + +``` +mts +``` + +## `node --import=vite-plus/register --test --test-reporter=dot ./tests/enum.test.ts` + +``` +. +``` + +## `cd tests && node --import=vite-plus/register --test --test-reporter=dot` + +Node discovers the TypeScript test without an explicit filename. + +``` +. +``` + +## `node --import=vite-plus/register ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: default", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: default", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: default", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: default", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/spawn.mjs` + +``` +{ + "execArgv": [], + "exitCode": 0, + "signal": null +} +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: compat", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: compat", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/parent.json TS_NODE_PROJECT=configs with spaces/parent.json OXC_TSCONFIG_PATH=configs with spaces/parent.json node --import=vite-plus/register ./src/config-workers.mjs` + +``` +{ + "parentBefore": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "compat": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "worker-two" + }, + "result": { + "stage": "result", + "value": "worker-two", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + }, + "upstream": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "worker-two" + }, + "result": { + "stage": "result", + "value": "worker-two", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + }, + "explicitAndDefault": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "default" + }, + "result": { + "stage": "result", + "value": "default", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + } +} +``` + +## `node ./src/worker-host.mjs` + +``` +{ + "message": "esm worker: cts: default" +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/cluster.mjs` + +``` +{ + "message": "fork: compat", + "exitCode": 0 +} +``` + +## `node ./src/internal-host.mjs` + +``` +{ + "internal": true, + "unchanged": true +} +``` + +## `node --import=vite-plus/register --import vite-plus/register ./src/dedup.mjs` + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node --import=vite-plus/register ./src/dedup.mjs` + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` + +## `node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"default","explicit":null,"compat":null,"upstream":null,"tsnode":null} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":"configs with spaces/compat.json","upstream":null,"tsnode":null} +``` + +## `OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"upstream","explicit":null,"compat":null,"upstream":"configs with spaces/upstream.json","tsnode":null} +``` + +## `TS_NODE_PROJECT=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"upstream","explicit":null,"compat":null,"upstream":null,"tsnode":"configs with spaces/upstream.json"} +``` + +## `TS_NODE_PROJECT=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":null,"upstream":"configs with spaces/upstream.json","tsnode":"configs with spaces/compat.json"} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":"configs with spaces/compat.json","upstream":"configs with spaces/upstream.json","tsnode":null} +``` + +## `VP_SCRIPT_TSCONFIG= TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":"","compat":"configs with spaces/compat.json","upstream":null,"tsnode":null} +``` + +## `VP_SCRIPT_TSCONFIG=configs with spaces/explicit.json TSX_TSCONFIG_PATH=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json TS_NODE_PROJECT=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"explicit","explicit":"configs with spaces/explicit.json","compat":"configs with spaces/compat.json","upstream":"configs with spaces/upstream.json","tsnode":"configs with spaces/upstream.json"} +``` + +## `node --import=vite-plus/register ./src/error-report.mjs ./error.ts` + +Show the application source-map location. + +**Exit code:** 1 + +``` +mapped failure +source: new Broken (/src/error.ts:4:11) +``` + +## `node --import=vite-plus/register ./src/error-report.mjs ./dependency-error.ts` + +Show the installed dependency's source-map location. + +**Exit code:** 1 + +``` +dependency failure +source: fail (/node_modules/fixture-ts-import/error.ts:3:9) +``` + +## `vpx --import=vite-plus/register ./src/dedup.mjs` + +The CLI and public preload share one registration. + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_async.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_async.local.md new file mode 100644 index 0000000000..92f2e92e37 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_async.local.md @@ -0,0 +1,600 @@ +# script_import_node22_async + +The public installed package preload works without tsx on the asynchronous hook path. + +## `vpt cp -r dependencies node_modules` + + +## `node runtime.mjs` + +``` +{ + "node": "22.18.0", + "requireExport": "/packages/cli/dist/register.js", + "importExport": "file:///packages/cli/dist/register.js", + "sameExport": true +} +tsx: MODULE_NOT_FOUND +``` + +## `node --import=vite-plus/register ./src/main.ts` + +``` +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node --import vite-plus/register ./src/main.cts` + +``` +{ + "common": { + "format": "cts" + }, + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node resolved-import.mjs url` + +A resolved file URL and an encoded script filename reach Node unchanged. + +``` +preload: file:///packages/cli/dist/register.js +encoded filename +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/main.ts` + +``` +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `NODE_OPTIONS=--import vite-plus/register node ./src/main.cts` + +``` +{ + "common": { + "format": "cts" + }, + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node src/argv-host.mjs --import=vite-plus/register --import ./src/setup.ts ./src/argv.ts one -- --flag 'space argument'` + +Report argv, preload state, process identity, and the script's exit code. + +**Exit code:** 7 + +``` +{ + "sameExecutable": true, + "directChild": true, + "absoluteScript": true, + "script": "src/argv.ts", + "setup": "loaded", + "args": [ + "one", + "--", + "--flag", + "space argument" + ], + "execArgv": [ + "--import=vite-plus/register", + "--import", + "./src/setup.ts" + ] +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node src/argv-host.mjs --import ./src/setup.ts ./src/argv.ts one -- --flag 'space argument'` + +**Exit code:** 7 + +``` +{ + "sameExecutable": true, + "directChild": true, + "absoluteScript": true, + "script": "src/argv.ts", + "setup": "loaded", + "args": [ + "one", + "--", + "--flag", + "space argument" + ], + "execArgv": [ + "--import", + "./src/setup.ts" + ] +} +``` + +## `node --import=vite-plus/register --input-type=module --eval 'const { format } = await import('\''./src/module.mts'\''); console.log(format);'` + +``` +mts +``` + +## `node --import=vite-plus/register --eval console.log(require('./src/common.cts').format);` + +``` +cts +``` + +## `vpt pipe-stdin 'const { format } = await import('\''./src/module.mts'\''); console.log(format);' -- node --import=vite-plus/register --input-type=module -` + +``` +mts +``` + +## `vpt pipe-stdin console.log(require('./src/common.cts').format); -- node --import=vite-plus/register -` + +``` +cts +``` + +## `node --input-type=module --eval 'await import('\''vite-plus/register'\''); const { format } = await import('\''./src/module.mts'\''); console.log(format);'` + +``` +mts +``` + +## `node --import=vite-plus/register --test --test-reporter=dot ./tests/enum.test.ts` + +``` +. +``` + +## `cd tests && node --import=vite-plus/register --test --test-reporter=dot` + +Node discovers the TypeScript test without an explicit filename. + +``` +. +``` + +## `node --import=vite-plus/register ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: default", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: default", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: default", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: default", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/spawn.mjs` + +``` +{ + "execArgv": [], + "exitCode": 0, + "signal": null +} +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: compat", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: compat", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/parent.json TS_NODE_PROJECT=configs with spaces/parent.json OXC_TSCONFIG_PATH=configs with spaces/parent.json node --import=vite-plus/register ./src/config-workers.mjs` + +``` +{ + "parentBefore": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "compat": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "worker-two" + }, + "result": { + "stage": "result", + "value": "worker-two", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + }, + "upstream": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "worker-two" + }, + "result": { + "stage": "result", + "value": "worker-two", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + }, + "explicitAndDefault": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "default" + }, + "result": { + "stage": "result", + "value": "default", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + } +} +``` + +## `node ./src/worker-host.mjs` + +``` +{ + "message": "esm worker: cts: default" +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/cluster.mjs` + +``` +{ + "message": "fork: compat", + "exitCode": 0 +} +``` + +## `node ./src/internal-host.mjs` + +``` +{ + "internal": true, + "unchanged": true +} +``` + +## `node --import=vite-plus/register --import vite-plus/register ./src/dedup.mjs` + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node --import=vite-plus/register ./src/dedup.mjs` + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` + +## `node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"default","explicit":null,"compat":null,"upstream":null,"tsnode":null} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":"configs with spaces/compat.json","upstream":null,"tsnode":null} +``` + +## `OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"upstream","explicit":null,"compat":null,"upstream":"configs with spaces/upstream.json","tsnode":null} +``` + +## `TS_NODE_PROJECT=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"upstream","explicit":null,"compat":null,"upstream":null,"tsnode":"configs with spaces/upstream.json"} +``` + +## `TS_NODE_PROJECT=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":null,"upstream":"configs with spaces/upstream.json","tsnode":"configs with spaces/compat.json"} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":"configs with spaces/compat.json","upstream":"configs with spaces/upstream.json","tsnode":null} +``` + +## `VP_SCRIPT_TSCONFIG= TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":"","compat":"configs with spaces/compat.json","upstream":null,"tsnode":null} +``` + +## `VP_SCRIPT_TSCONFIG=configs with spaces/explicit.json TSX_TSCONFIG_PATH=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json TS_NODE_PROJECT=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"explicit","explicit":"configs with spaces/explicit.json","compat":"configs with spaces/compat.json","upstream":"configs with spaces/upstream.json","tsnode":"configs with spaces/upstream.json"} +``` + +## `node --import=vite-plus/register ./src/error-report.mjs ./error.ts` + +Show the application source-map location. + +**Exit code:** 1 + +``` +mapped failure +source: new Broken (/src/error.ts:4:11) +``` + +## `node --import=vite-plus/register ./src/error-report.mjs ./dependency-error.ts` + +Show the installed dependency's source-map location. + +**Exit code:** 1 + +``` +dependency failure +source: fail (/node_modules/fixture-ts-import/error.ts:3:9) +``` + +## `vpx --import=vite-plus/register ./src/dedup.mjs` + +The CLI and public preload share one registration. + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_async_absolute_path.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_async_absolute_path.global.md new file mode 100644 index 0000000000..eb68eb5965 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_async_absolute_path.global.md @@ -0,0 +1,10 @@ +# script_import_node22_async_absolute_path + +Node also accepts an absolute POSIX path for the public preload. + +## `node resolved-import.mjs path` + +``` +preload: /packages/cli/dist/register.js +encoded filename +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_async_absolute_path.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_async_absolute_path.local.md new file mode 100644 index 0000000000..eb68eb5965 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_async_absolute_path.local.md @@ -0,0 +1,10 @@ +# script_import_node22_async_absolute_path + +Node also accepts an absolute POSIX path for the public preload. + +## `node resolved-import.mjs path` + +``` +preload: /packages/cli/dist/register.js +encoded filename +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_sync.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_sync.md new file mode 100644 index 0000000000..a1ac0bd237 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_sync.md @@ -0,0 +1,600 @@ +# script_import_node22_sync + +The complete preload contract also runs on Node.js 22 synchronous hooks. + +## `vpt cp -r dependencies node_modules` + + +## `node runtime.mjs` + +``` +{ + "node": "22.23.2", + "requireExport": "/packages/cli/dist/register.js", + "importExport": "file:///packages/cli/dist/register.js", + "sameExport": true +} +tsx: MODULE_NOT_FOUND +``` + +## `node --import=vite-plus/register ./src/main.ts` + +``` +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node --import vite-plus/register ./src/main.cts` + +``` +{ + "common": { + "format": "cts" + }, + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node resolved-import.mjs url` + +A resolved file URL and an encoded script filename reach Node unchanged. + +``` +preload: file:///packages/cli/dist/register.js +encoded filename +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/main.ts` + +``` +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `NODE_OPTIONS=--import vite-plus/register node ./src/main.cts` + +``` +{ + "common": { + "format": "cts" + }, + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node src/argv-host.mjs --import=vite-plus/register --import ./src/setup.ts ./src/argv.ts one -- --flag 'space argument'` + +Report argv, preload state, process identity, and the script's exit code. + +**Exit code:** 7 + +``` +{ + "sameExecutable": true, + "directChild": true, + "absoluteScript": true, + "script": "src/argv.ts", + "setup": "loaded", + "args": [ + "one", + "--", + "--flag", + "space argument" + ], + "execArgv": [ + "--import=vite-plus/register", + "--import", + "./src/setup.ts" + ] +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node src/argv-host.mjs --import ./src/setup.ts ./src/argv.ts one -- --flag 'space argument'` + +**Exit code:** 7 + +``` +{ + "sameExecutable": true, + "directChild": true, + "absoluteScript": true, + "script": "src/argv.ts", + "setup": "loaded", + "args": [ + "one", + "--", + "--flag", + "space argument" + ], + "execArgv": [ + "--import", + "./src/setup.ts" + ] +} +``` + +## `node --import=vite-plus/register --input-type=module --eval 'const { format } = await import('\''./src/module.mts'\''); console.log(format);'` + +``` +mts +``` + +## `node --import=vite-plus/register --eval console.log(require('./src/common.cts').format);` + +``` +cts +``` + +## `vpt pipe-stdin 'const { format } = await import('\''./src/module.mts'\''); console.log(format);' -- node --import=vite-plus/register --input-type=module -` + +``` +mts +``` + +## `vpt pipe-stdin console.log(require('./src/common.cts').format); -- node --import=vite-plus/register -` + +``` +cts +``` + +## `node --input-type=module --eval 'await import('\''vite-plus/register'\''); const { format } = await import('\''./src/module.mts'\''); console.log(format);'` + +``` +mts +``` + +## `node --import=vite-plus/register --test --test-reporter=dot ./tests/enum.test.ts` + +``` +. +``` + +## `cd tests && node --import=vite-plus/register --test --test-reporter=dot` + +Node discovers the TypeScript test without an explicit filename. + +``` +. +``` + +## `node --import=vite-plus/register ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: default", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: default", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: default", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: default", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/spawn.mjs` + +``` +{ + "execArgv": [], + "exitCode": 0, + "signal": null +} +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: compat", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: compat", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/parent.json TS_NODE_PROJECT=configs with spaces/parent.json OXC_TSCONFIG_PATH=configs with spaces/parent.json node --import=vite-plus/register ./src/config-workers.mjs` + +``` +{ + "parentBefore": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "compat": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "worker-two" + }, + "result": { + "stage": "result", + "value": "worker-two", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + }, + "upstream": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "worker-two" + }, + "result": { + "stage": "result", + "value": "worker-two", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + }, + "explicitAndDefault": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "default" + }, + "result": { + "stage": "result", + "value": "default", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + } +} +``` + +## `node ./src/worker-host.mjs` + +``` +{ + "message": "esm worker: cts: default" +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/cluster.mjs` + +``` +{ + "message": "fork: compat", + "exitCode": 0 +} +``` + +## `node ./src/internal-host.mjs` + +``` +{ + "internal": true, + "unchanged": true +} +``` + +## `node --import=vite-plus/register --import vite-plus/register ./src/dedup.mjs` + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node --import=vite-plus/register ./src/dedup.mjs` + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` + +## `node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"default","explicit":null,"compat":null,"upstream":null,"tsnode":null} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":"configs with spaces/compat.json","upstream":null,"tsnode":null} +``` + +## `OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"upstream","explicit":null,"compat":null,"upstream":"configs with spaces/upstream.json","tsnode":null} +``` + +## `TS_NODE_PROJECT=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"upstream","explicit":null,"compat":null,"upstream":null,"tsnode":"configs with spaces/upstream.json"} +``` + +## `TS_NODE_PROJECT=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":null,"upstream":"configs with spaces/upstream.json","tsnode":"configs with spaces/compat.json"} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":"configs with spaces/compat.json","upstream":"configs with spaces/upstream.json","tsnode":null} +``` + +## `VP_SCRIPT_TSCONFIG= TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":"","compat":"configs with spaces/compat.json","upstream":null,"tsnode":null} +``` + +## `VP_SCRIPT_TSCONFIG=configs with spaces/explicit.json TSX_TSCONFIG_PATH=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json TS_NODE_PROJECT=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"explicit","explicit":"configs with spaces/explicit.json","compat":"configs with spaces/compat.json","upstream":"configs with spaces/upstream.json","tsnode":"configs with spaces/upstream.json"} +``` + +## `node --import=vite-plus/register ./src/error-report.mjs ./error.ts` + +Show the application source-map location. + +**Exit code:** 1 + +``` +mapped failure +source: new Broken (/src/error.ts:4:11) +``` + +## `node --import=vite-plus/register ./src/error-report.mjs ./dependency-error.ts` + +Show the installed dependency's source-map location. + +**Exit code:** 1 + +``` +dependency failure +source: fail (/node_modules/fixture-ts-import/error.ts:3:9) +``` + +## `vpx --import=vite-plus/register ./src/dedup.mjs` + +The CLI and public preload share one registration. + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_sync_absolute_path.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_sync_absolute_path.md new file mode 100644 index 0000000000..236049ac06 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node22_sync_absolute_path.md @@ -0,0 +1,10 @@ +# script_import_node22_sync_absolute_path + +Node also accepts an absolute POSIX path for the public preload. + +## `node resolved-import.mjs path` + +``` +preload: /packages/cli/dist/register.js +encoded filename +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node24_async.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node24_async.md new file mode 100644 index 0000000000..d11c2bff17 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node24_async.md @@ -0,0 +1,600 @@ +# script_import_node24_async + +The earliest supported Node.js 24 also uses asynchronous hooks, including workers with independent configs. + +## `vpt cp -r dependencies node_modules` + + +## `node runtime.mjs` + +``` +{ + "node": "24.11.0", + "requireExport": "/packages/cli/dist/register.js", + "importExport": "file:///packages/cli/dist/register.js", + "sameExport": true +} +tsx: MODULE_NOT_FOUND +``` + +## `node --import=vite-plus/register ./src/main.ts` + +``` +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node --import vite-plus/register ./src/main.cts` + +``` +{ + "common": { + "format": "cts" + }, + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node resolved-import.mjs url` + +A resolved file URL and an encoded script filename reach Node unchanged. + +``` +preload: file:///packages/cli/dist/register.js +encoded filename +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/main.ts` + +``` +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `NODE_OPTIONS=--import vite-plus/register node ./src/main.cts` + +``` +{ + "common": { + "format": "cts" + }, + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node src/argv-host.mjs --import=vite-plus/register --import ./src/setup.ts ./src/argv.ts one -- --flag 'space argument'` + +Report argv, preload state, process identity, and the script's exit code. + +**Exit code:** 7 + +``` +{ + "sameExecutable": true, + "directChild": true, + "absoluteScript": true, + "script": "src/argv.ts", + "setup": "loaded", + "args": [ + "one", + "--", + "--flag", + "space argument" + ], + "execArgv": [ + "--import=vite-plus/register", + "--import", + "./src/setup.ts" + ] +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node src/argv-host.mjs --import ./src/setup.ts ./src/argv.ts one -- --flag 'space argument'` + +**Exit code:** 7 + +``` +{ + "sameExecutable": true, + "directChild": true, + "absoluteScript": true, + "script": "src/argv.ts", + "setup": "loaded", + "args": [ + "one", + "--", + "--flag", + "space argument" + ], + "execArgv": [ + "--import", + "./src/setup.ts" + ] +} +``` + +## `node --import=vite-plus/register --input-type=module --eval 'const { format } = await import('\''./src/module.mts'\''); console.log(format);'` + +``` +mts +``` + +## `node --import=vite-plus/register --eval console.log(require('./src/common.cts').format);` + +``` +cts +``` + +## `vpt pipe-stdin 'const { format } = await import('\''./src/module.mts'\''); console.log(format);' -- node --import=vite-plus/register --input-type=module -` + +``` +mts +``` + +## `vpt pipe-stdin console.log(require('./src/common.cts').format); -- node --import=vite-plus/register -` + +``` +cts +``` + +## `node --input-type=module --eval 'await import('\''vite-plus/register'\''); const { format } = await import('\''./src/module.mts'\''); console.log(format);'` + +``` +mts +``` + +## `node --import=vite-plus/register --test --test-reporter=dot ./tests/enum.test.ts` + +``` +. +``` + +## `cd tests && node --import=vite-plus/register --test --test-reporter=dot` + +Node discovers the TypeScript test without an explicit filename. + +``` +. +``` + +## `node --import=vite-plus/register ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: default", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: default", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: default", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: default", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/spawn.mjs` + +``` +{ + "execArgv": [], + "exitCode": 0, + "signal": null +} +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: compat", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: compat", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/parent.json TS_NODE_PROJECT=configs with spaces/parent.json OXC_TSCONFIG_PATH=configs with spaces/parent.json node --import=vite-plus/register ./src/config-workers.mjs` + +``` +{ + "parentBefore": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "compat": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "worker-two" + }, + "result": { + "stage": "result", + "value": "worker-two", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + }, + "upstream": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "worker-two" + }, + "result": { + "stage": "result", + "value": "worker-two", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + }, + "explicitAndDefault": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "default" + }, + "result": { + "stage": "result", + "value": "default", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + } +} +``` + +## `node ./src/worker-host.mjs` + +``` +{ + "message": "esm worker: cts: default" +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/cluster.mjs` + +``` +{ + "message": "fork: compat", + "exitCode": 0 +} +``` + +## `node ./src/internal-host.mjs` + +``` +{ + "internal": true, + "unchanged": true +} +``` + +## `node --import=vite-plus/register --import vite-plus/register ./src/dedup.mjs` + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node --import=vite-plus/register ./src/dedup.mjs` + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` + +## `node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"default","explicit":null,"compat":null,"upstream":null,"tsnode":null} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":"configs with spaces/compat.json","upstream":null,"tsnode":null} +``` + +## `OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"upstream","explicit":null,"compat":null,"upstream":"configs with spaces/upstream.json","tsnode":null} +``` + +## `TS_NODE_PROJECT=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"upstream","explicit":null,"compat":null,"upstream":null,"tsnode":"configs with spaces/upstream.json"} +``` + +## `TS_NODE_PROJECT=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":null,"upstream":"configs with spaces/upstream.json","tsnode":"configs with spaces/compat.json"} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":"configs with spaces/compat.json","upstream":"configs with spaces/upstream.json","tsnode":null} +``` + +## `VP_SCRIPT_TSCONFIG= TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":"","compat":"configs with spaces/compat.json","upstream":null,"tsnode":null} +``` + +## `VP_SCRIPT_TSCONFIG=configs with spaces/explicit.json TSX_TSCONFIG_PATH=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json TS_NODE_PROJECT=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"explicit","explicit":"configs with spaces/explicit.json","compat":"configs with spaces/compat.json","upstream":"configs with spaces/upstream.json","tsnode":"configs with spaces/upstream.json"} +``` + +## `node --import=vite-plus/register ./src/error-report.mjs ./error.ts` + +Show the application source-map location. + +**Exit code:** 1 + +``` +mapped failure +source: new Broken (/src/error.ts:4:11) +``` + +## `node --import=vite-plus/register ./src/error-report.mjs ./dependency-error.ts` + +Show the installed dependency's source-map location. + +**Exit code:** 1 + +``` +dependency failure +source: fail (/node_modules/fixture-ts-import/error.ts:3:9) +``` + +## `vpx --import=vite-plus/register ./src/dedup.mjs` + +The CLI and public preload share one registration. + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node24_async_absolute_path.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node24_async_absolute_path.md new file mode 100644 index 0000000000..1504d65441 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node24_async_absolute_path.md @@ -0,0 +1,10 @@ +# script_import_node24_async_absolute_path + +Node also accepts an absolute POSIX path for the public preload. + +## `node resolved-import.mjs path` + +``` +preload: /packages/cli/dist/register.js +encoded filename +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node24_sync.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node24_sync.md new file mode 100644 index 0000000000..f7477f6f76 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node24_sync.md @@ -0,0 +1,600 @@ +# script_import_node24_sync + +The complete preload contract runs on Node.js 24 synchronous hooks. + +## `vpt cp -r dependencies node_modules` + + +## `node runtime.mjs` + +``` +{ + "node": "24.12.0", + "requireExport": "/packages/cli/dist/register.js", + "importExport": "file:///packages/cli/dist/register.js", + "sameExport": true +} +tsx: MODULE_NOT_FOUND +``` + +## `node --import=vite-plus/register ./src/main.ts` + +``` +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node --import vite-plus/register ./src/main.cts` + +``` +{ + "common": { + "format": "cts" + }, + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node resolved-import.mjs url` + +A resolved file URL and an encoded script filename reach Node unchanged. + +``` +preload: file:///packages/cli/dist/register.js +encoded filename +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/main.ts` + +``` +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `NODE_OPTIONS=--import vite-plus/register node ./src/main.cts` + +``` +{ + "common": { + "format": "cts" + }, + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node src/argv-host.mjs --import=vite-plus/register --import ./src/setup.ts ./src/argv.ts one -- --flag 'space argument'` + +Report argv, preload state, process identity, and the script's exit code. + +**Exit code:** 7 + +``` +{ + "sameExecutable": true, + "directChild": true, + "absoluteScript": true, + "script": "src/argv.ts", + "setup": "loaded", + "args": [ + "one", + "--", + "--flag", + "space argument" + ], + "execArgv": [ + "--import=vite-plus/register", + "--import", + "./src/setup.ts" + ] +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node src/argv-host.mjs --import ./src/setup.ts ./src/argv.ts one -- --flag 'space argument'` + +**Exit code:** 7 + +``` +{ + "sameExecutable": true, + "directChild": true, + "absoluteScript": true, + "script": "src/argv.ts", + "setup": "loaded", + "args": [ + "one", + "--", + "--flag", + "space argument" + ], + "execArgv": [ + "--import", + "./src/setup.ts" + ] +} +``` + +## `node --import=vite-plus/register --input-type=module --eval 'const { format } = await import('\''./src/module.mts'\''); console.log(format);'` + +``` +mts +``` + +## `node --import=vite-plus/register --eval console.log(require('./src/common.cts').format);` + +``` +cts +``` + +## `vpt pipe-stdin 'const { format } = await import('\''./src/module.mts'\''); console.log(format);' -- node --import=vite-plus/register --input-type=module -` + +``` +mts +``` + +## `vpt pipe-stdin console.log(require('./src/common.cts').format); -- node --import=vite-plus/register -` + +``` +cts +``` + +## `node --input-type=module --eval 'await import('\''vite-plus/register'\''); const { format } = await import('\''./src/module.mts'\''); console.log(format);'` + +``` +mts +``` + +## `node --import=vite-plus/register --test --test-reporter=dot ./tests/enum.test.ts` + +``` +. +``` + +## `cd tests && node --import=vite-plus/register --test --test-reporter=dot` + +Node discovers the TypeScript test without an explicit filename. + +``` +. +``` + +## `node --import=vite-plus/register ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: default", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: default", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: default", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: default", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/spawn.mjs` + +``` +{ + "execArgv": [], + "exitCode": 0, + "signal": null +} +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: compat", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: compat", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/parent.json TS_NODE_PROJECT=configs with spaces/parent.json OXC_TSCONFIG_PATH=configs with spaces/parent.json node --import=vite-plus/register ./src/config-workers.mjs` + +``` +{ + "parentBefore": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "compat": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "worker-two" + }, + "result": { + "stage": "result", + "value": "worker-two", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + }, + "upstream": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "worker-two" + }, + "result": { + "stage": "result", + "value": "worker-two", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + }, + "explicitAndDefault": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "default" + }, + "result": { + "stage": "result", + "value": "default", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + } +} +``` + +## `node ./src/worker-host.mjs` + +``` +{ + "message": "esm worker: cts: default" +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/cluster.mjs` + +``` +{ + "message": "fork: compat", + "exitCode": 0 +} +``` + +## `node ./src/internal-host.mjs` + +``` +{ + "internal": true, + "unchanged": true +} +``` + +## `node --import=vite-plus/register --import vite-plus/register ./src/dedup.mjs` + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node --import=vite-plus/register ./src/dedup.mjs` + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` + +## `node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"default","explicit":null,"compat":null,"upstream":null,"tsnode":null} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":"configs with spaces/compat.json","upstream":null,"tsnode":null} +``` + +## `OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"upstream","explicit":null,"compat":null,"upstream":"configs with spaces/upstream.json","tsnode":null} +``` + +## `TS_NODE_PROJECT=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"upstream","explicit":null,"compat":null,"upstream":null,"tsnode":"configs with spaces/upstream.json"} +``` + +## `TS_NODE_PROJECT=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":null,"upstream":"configs with spaces/upstream.json","tsnode":"configs with spaces/compat.json"} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":"configs with spaces/compat.json","upstream":"configs with spaces/upstream.json","tsnode":null} +``` + +## `VP_SCRIPT_TSCONFIG= TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":"","compat":"configs with spaces/compat.json","upstream":null,"tsnode":null} +``` + +## `VP_SCRIPT_TSCONFIG=configs with spaces/explicit.json TSX_TSCONFIG_PATH=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json TS_NODE_PROJECT=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"explicit","explicit":"configs with spaces/explicit.json","compat":"configs with spaces/compat.json","upstream":"configs with spaces/upstream.json","tsnode":"configs with spaces/upstream.json"} +``` + +## `node --import=vite-plus/register ./src/error-report.mjs ./error.ts` + +Show the application source-map location. + +**Exit code:** 1 + +``` +mapped failure +source: new Broken (/src/error.ts:4:11) +``` + +## `node --import=vite-plus/register ./src/error-report.mjs ./dependency-error.ts` + +Show the installed dependency's source-map location. + +**Exit code:** 1 + +``` +dependency failure +source: fail (/node_modules/fixture-ts-import/error.ts:3:9) +``` + +## `vpx --import=vite-plus/register ./src/dedup.mjs` + +The CLI and public preload share one registration. + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node24_sync_absolute_path.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node24_sync_absolute_path.md new file mode 100644 index 0000000000..42c04e2802 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node24_sync_absolute_path.md @@ -0,0 +1,10 @@ +# script_import_node24_sync_absolute_path + +Node also accepts an absolute POSIX path for the public preload. + +## `node resolved-import.mjs path` + +``` +preload: /packages/cli/dist/register.js +encoded filename +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node26_sync.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node26_sync.md new file mode 100644 index 0000000000..47655db48e --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node26_sync.md @@ -0,0 +1,600 @@ +# script_import_node26_sync + +The complete preload contract runs on Node.js 26 synchronous hooks. + +## `vpt cp -r dependencies node_modules` + + +## `node runtime.mjs` + +``` +{ + "node": "26.5.0", + "requireExport": "/packages/cli/dist/register.js", + "importExport": "file:///packages/cli/dist/register.js", + "sameExport": true +} +tsx: MODULE_NOT_FOUND +``` + +## `node --import=vite-plus/register ./src/main.ts` + +``` +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node --import vite-plus/register ./src/main.cts` + +``` +{ + "common": { + "format": "cts" + }, + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node resolved-import.mjs url` + +A resolved file URL and an encoded script filename reach Node unchanged. + +``` +preload: file:///packages/cli/dist/register.js +encoded filename +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/main.ts` + +``` +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `NODE_OPTIONS=--import vite-plus/register node ./src/main.cts` + +``` +{ + "common": { + "format": "cts" + }, + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `node src/argv-host.mjs --import=vite-plus/register --import ./src/setup.ts ./src/argv.ts one -- --flag 'space argument'` + +Report argv, preload state, process identity, and the script's exit code. + +**Exit code:** 7 + +``` +{ + "sameExecutable": true, + "directChild": true, + "absoluteScript": true, + "script": "src/argv.ts", + "setup": "loaded", + "args": [ + "one", + "--", + "--flag", + "space argument" + ], + "execArgv": [ + "--import=vite-plus/register", + "--import", + "./src/setup.ts" + ] +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node src/argv-host.mjs --import ./src/setup.ts ./src/argv.ts one -- --flag 'space argument'` + +**Exit code:** 7 + +``` +{ + "sameExecutable": true, + "directChild": true, + "absoluteScript": true, + "script": "src/argv.ts", + "setup": "loaded", + "args": [ + "one", + "--", + "--flag", + "space argument" + ], + "execArgv": [ + "--import", + "./src/setup.ts" + ] +} +``` + +## `node --import=vite-plus/register --input-type=module --eval 'const { format } = await import('\''./src/module.mts'\''); console.log(format);'` + +``` +mts +``` + +## `node --import=vite-plus/register --eval console.log(require('./src/common.cts').format);` + +``` +cts +``` + +## `vpt pipe-stdin 'const { format } = await import('\''./src/module.mts'\''); console.log(format);' -- node --import=vite-plus/register --input-type=module -` + +``` +mts +``` + +## `vpt pipe-stdin console.log(require('./src/common.cts').format); -- node --import=vite-plus/register -` + +``` +cts +``` + +## `node --input-type=module --eval 'await import('\''vite-plus/register'\''); const { format } = await import('\''./src/module.mts'\''); console.log(format);'` + +``` +mts +``` + +## `node --import=vite-plus/register --test --test-reporter=dot ./tests/enum.test.ts` + +``` +. +``` + +## `cd tests && node --import=vite-plus/register --test --test-reporter=dot` + +Node discovers the TypeScript test without an explicit filename. + +``` +. +``` + +## `node --import=vite-plus/register ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: default", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: default", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: default", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: default", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node ./src/spawn.mjs` + +``` +{ + "execArgv": [], + "exitCode": 0, + "signal": null +} +{ + "value": "default", + "remapped": "remapped", + "format": "mts", + "common": { + "format": "cts" + }, + "view": { + "tag": "section", + "children": [ + "tsx" + ] + }, + "jsx": { + "tag": "section", + "children": [ + "jsx" + ] + }, + "kind": "esm dependency", + "dependency": "cjs dependency", + "total": 5, + "decorated": [ + "total" + ] +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/children.mjs` + +``` +{ + "fork": { + "message": "fork: compat", + "exitCode": 0 + }, + "esmWorker": { + "message": "esm worker: cts: compat", + "exitCode": 0 + }, + "cjsWorker": { + "message": "cjs worker: cts", + "exitCode": 0 + } +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/parent.json TS_NODE_PROJECT=configs with spaces/parent.json OXC_TSCONFIG_PATH=configs with spaces/parent.json node --import=vite-plus/register ./src/config-workers.mjs` + +``` +{ + "parentBefore": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "compat": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "worker-two" + }, + "result": { + "stage": "result", + "value": "worker-two", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + }, + "upstream": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "worker-two" + }, + "result": { + "stage": "result", + "value": "worker-two", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + }, + "explicitAndDefault": { + "workers": [ + { + "ready": { + "stage": "ready", + "initial": "worker-one" + }, + "result": { + "stage": "result", + "value": "worker-one", + "writes": 1 + } + }, + { + "ready": { + "stage": "ready", + "initial": "default" + }, + "result": { + "stage": "result", + "value": "default", + "writes": 0 + } + } + ], + "parentDuring": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + }, + "parentAfter": { + "value": "parent", + "writes": 0, + "environment": { + "VP_SCRIPT_TSCONFIG": null, + "TSX_TSCONFIG_PATH": "configs with spaces/parent.json", + "TS_NODE_PROJECT": "configs with spaces/parent.json", + "OXC_TSCONFIG_PATH": "configs with spaces/parent.json" + }, + "sameEnvironment": true + } + } +} +``` + +## `node ./src/worker-host.mjs` + +``` +{ + "message": "esm worker: cts: default" +} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/cluster.mjs` + +``` +{ + "message": "fork: compat", + "exitCode": 0 +} +``` + +## `node ./src/internal-host.mjs` + +``` +{ + "internal": true, + "unchanged": true +} +``` + +## `node --import=vite-plus/register --import vite-plus/register ./src/dedup.mjs` + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` + +## `NODE_OPTIONS=--import=vite-plus/register node --import=vite-plus/register ./src/dedup.mjs` + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` + +## `node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"default","explicit":null,"compat":null,"upstream":null,"tsnode":null} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":"configs with spaces/compat.json","upstream":null,"tsnode":null} +``` + +## `OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"upstream","explicit":null,"compat":null,"upstream":"configs with spaces/upstream.json","tsnode":null} +``` + +## `TS_NODE_PROJECT=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"upstream","explicit":null,"compat":null,"upstream":null,"tsnode":"configs with spaces/upstream.json"} +``` + +## `TS_NODE_PROJECT=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":null,"upstream":"configs with spaces/upstream.json","tsnode":"configs with spaces/compat.json"} +``` + +## `TSX_TSCONFIG_PATH=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":null,"compat":"configs with spaces/compat.json","upstream":"configs with spaces/upstream.json","tsnode":null} +``` + +## `VP_SCRIPT_TSCONFIG= TSX_TSCONFIG_PATH=configs with spaces/compat.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"compat","explicit":"","compat":"configs with spaces/compat.json","upstream":null,"tsnode":null} +``` + +## `VP_SCRIPT_TSCONFIG=configs with spaces/explicit.json TSX_TSCONFIG_PATH=configs with spaces/compat.json OXC_TSCONFIG_PATH=configs with spaces/upstream.json TS_NODE_PROJECT=configs with spaces/upstream.json node --import=vite-plus/register ./src/config.ts` + +``` +{"value":"explicit","explicit":"configs with spaces/explicit.json","compat":"configs with spaces/compat.json","upstream":"configs with spaces/upstream.json","tsnode":"configs with spaces/upstream.json"} +``` + +## `node --import=vite-plus/register ./src/error-report.mjs ./error.ts` + +Show the application source-map location. + +**Exit code:** 1 + +``` +mapped failure +source: new Broken (/src/error.ts:4:11) +``` + +## `node --import=vite-plus/register ./src/error-report.mjs ./dependency-error.ts` + +Show the installed dependency's source-map location. + +**Exit code:** 1 + +``` +dependency failure +source: fail (/node_modules/fixture-ts-import/error.ts:3:9) +``` + +## `vpx --import=vite-plus/register ./src/dedup.mjs` + +The CLI and public preload share one registration. + +``` +{ + "transformType": "function", + "sameTransform": true, + "sameResolve": true, + "format": "mts" +} +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node26_sync_absolute_path.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node26_sync_absolute_path.md new file mode 100644 index 0000000000..84a238822c --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_node26_sync_absolute_path.md @@ -0,0 +1,10 @@ +# script_import_node26_sync_absolute_path + +Node also accepts an absolute POSIX path for the public preload. + +## `node resolved-import.mjs path` + +``` +preload: /packages/cli/dist/register.js +encoded filename +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_unsupported_node.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_unsupported_node.md new file mode 100644 index 0000000000..d12c274a93 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/snapshots/script_import_unsupported_node.md @@ -0,0 +1,9 @@ +# script_import_unsupported_node + +Unsupported Node.js reaches the version diagnostic before loading native code. + +## `node verify-unsupported.mjs` + +``` +unsupported Node.js fails before binding and application loading +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/argv-host.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/argv-host.mjs new file mode 100644 index 0000000000..87c9b357bd --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/argv-host.mjs @@ -0,0 +1,14 @@ +import { spawnSync } from 'node:child_process'; + +// The parent supplies its identity so the payload can report any extra process hop. +const child = spawnSync(process.execPath, process.argv.slice(2), { + env: { + ...process.env, + EXPECTED_EXEC_PATH: process.execPath, + EXPECTED_PARENT_PID: String(process.pid), + }, + stdio: 'inherit', + timeout: 30000, +}); +if (child.error) throw child.error; +process.exitCode = child.status ?? 1; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/argv.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/argv.ts new file mode 100644 index 0000000000..6a8adfaca3 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/argv.ts @@ -0,0 +1,12 @@ +import path from 'node:path'; + +console.log(JSON.stringify({ + sameExecutable: process.execPath === process.env.EXPECTED_EXEC_PATH, + directChild: process.ppid === Number(process.env.EXPECTED_PARENT_PID), + absoluteScript: path.isAbsolute(process.argv[1]), + script: path.relative(process.cwd(), process.argv[1]).split(path.sep).join('/'), + setup: (globalThis as any).setup, + args: process.argv.slice(2), + execArgv: process.execArgv, +}, null, 2)); +process.exitCode = 7; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/child.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/child.ts new file mode 100644 index 0000000000..cd3b494a81 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/child.ts @@ -0,0 +1,3 @@ +import { value } from '@value'; +enum Result { Value = 'fork' } +process.send!(`${Result.Value}: ${value}`, () => process.disconnect()); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/children.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/children.mjs new file mode 100644 index 0000000000..afeccc61ba --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/children.mjs @@ -0,0 +1,15 @@ +import { fork } from 'node:child_process'; +import { once } from 'node:events'; +import { Worker } from 'node:worker_threads'; + +async function collect(child) { + const signal = AbortSignal.timeout(15000); + const exited = once(child, 'exit', { signal }); + const [message] = await once(child, 'message', { signal }); + const [exitCode] = await exited; + return { message, exitCode }; +} +const forked = await collect(fork(new URL('./child.ts', import.meta.url))); +const esmWorker = await collect(new Worker(new URL('./worker.mts', import.meta.url))); +const cjsWorker = await collect(new Worker(new URL('./worker.cts', import.meta.url))); +console.log(JSON.stringify({ fork: forked, esmWorker, cjsWorker }, null, 2)); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/cluster.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/cluster.mjs new file mode 100644 index 0000000000..495a9f5230 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/cluster.mjs @@ -0,0 +1,10 @@ +import cluster from 'node:cluster'; +import { once } from 'node:events'; +import { fileURLToPath } from 'node:url'; + +cluster.setupPrimary({ exec: fileURLToPath(new URL('./child.ts', import.meta.url)) }); +const child = cluster.fork(); +const exited = once(child, 'exit'); +const [message] = await once(child, 'message'); +const [exitCode] = await exited; +console.log(JSON.stringify({ message, exitCode }, null, 2)); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/common.cts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/common.cts new file mode 100644 index 0000000000..16d3520789 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/common.cts @@ -0,0 +1,5 @@ +const assert = require('node:assert/strict'); +enum Format { Value = 'cts' } +const nested: string = require('./nested.cts'); +assert.equal(nested, 'nested'); +module.exports = { format: Format.Value }; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-compat.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-compat.ts new file mode 100644 index 0000000000..b361e7f026 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-compat.ts @@ -0,0 +1,2 @@ +export enum Value { Current = 'compat' } +export const value = Value.Current; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-explicit.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-explicit.ts new file mode 100644 index 0000000000..95b0842591 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-explicit.ts @@ -0,0 +1,2 @@ +export enum Value { Current = 'explicit' } +export const value = Value.Current; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-fields.cts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-fields.cts new file mode 100644 index 0000000000..17ef0793bd --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-fields.cts @@ -0,0 +1,9 @@ +let writes: number = 0; +class Base { + set field(_value: number) { writes += 1; } +} +class Derived extends Base { + field = 1; +} +new Derived(); +module.exports = { writes }; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-parent-check.mts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-parent-check.mts new file mode 100644 index 0000000000..e1ac29af16 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-parent-check.mts @@ -0,0 +1 @@ +export { value } from '@value'; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-parent.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-parent.ts new file mode 100644 index 0000000000..dbf41d417f --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-parent.ts @@ -0,0 +1,2 @@ +export enum Value { Current = 'parent' } +export const value = Value.Current; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-upstream.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-upstream.ts new file mode 100644 index 0000000000..09c4e8ebb6 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-upstream.ts @@ -0,0 +1,2 @@ +export enum Value { Current = 'upstream' } +export const value = Value.Current; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-worker-one.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-worker-one.ts new file mode 100644 index 0000000000..7a82b55d6c --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-worker-one.ts @@ -0,0 +1,2 @@ +export enum Value { Current = 'worker-one' } +export const value = Value.Current; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-worker-two.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-worker-two.ts new file mode 100644 index 0000000000..2f8e73dc64 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-worker-two.ts @@ -0,0 +1,2 @@ +export enum Value { Current = 'worker-two' } +export const value = Value.Current; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-worker.mts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-worker.mts new file mode 100644 index 0000000000..9ed1135325 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-worker.mts @@ -0,0 +1,14 @@ +import { once } from 'node:events'; +import { createRequire } from 'node:module'; +import { parentPort } from 'node:worker_threads'; +import { value as initial } from '@value'; + +enum Stage { Ready = 'ready', Result = 'result' } +const run = once(parentPort!, 'message'); +parentPort!.postMessage({ stage: Stage.Ready, initial }); +await run; +// The other worker has registered before either worker resolves or transforms these. +const { value } = await import('./config-parent-check.mts'); +const { writes } = createRequire(import.meta.url)('./config-fields.cts'); +parentPort!.postMessage({ stage: Stage.Result, value, writes }); +parentPort!.close(); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-workers.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-workers.mjs new file mode 100644 index 0000000000..02f161d107 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config-workers.mjs @@ -0,0 +1,66 @@ +import { once } from 'node:events'; +import { createRequire } from 'node:module'; +import { Worker } from 'node:worker_threads'; + +const require = createRequire(import.meta.url); +const selectors = ['VP_SCRIPT_TSCONFIG', 'TSX_TSCONFIG_PATH', 'TS_NODE_PROJECT', 'OXC_TSCONFIG_PATH']; +const original = selectors.map(key => process.env[key]); +const one = 'configs with spaces/worker-one.json'; +const two = 'configs with spaces/worker-two.json'; +let checks = 0; +async function checkParent() { + const { value } = await import(`./config-parent-check.mts?check=${checks++}`); + delete require.cache[require.resolve('./config-fields.cts')]; + return { + value, + writes: require('./config-fields.cts').writes, + environment: Object.fromEntries(selectors.map(key => [key, process.env[key] ?? null])), + sameEnvironment: selectors.every((key, index) => process.env[key] === original[index]), + }; +} + +async function pair(configurations) { + let readyCount = 0; + let release; + const gate = new Promise(resolve => { release = resolve; }); + const tasks = configurations.map(async (overrides) => { + const env = { ...process.env }; + for (const key of selectors) delete env[key]; + Object.assign(env, overrides); + const worker = new Worker(new URL('./config-worker.mts', import.meta.url), { env }); + const signal = AbortSignal.timeout(15000); + try { + const [ready] = await once(worker, 'message', { signal }); + if (++readyCount === configurations.length) release(); + await gate; + const result = once(worker, 'message', { signal }); + worker.postMessage('run'); + return { ready, result: (await result)[0] }; + } finally { + await worker.terminate(); + } + }); + // Attach rejection handlers immediately, including when a worker fails before the gate. + const completed = Promise.all(tasks); + await Promise.race([gate, completed]); + const [workers, parentDuring] = await Promise.all([completed, checkParent()]); + const parentAfter = await checkParent(); + return { workers, parentDuring, parentAfter }; +} + +const parentBefore = await checkParent(); +const compat = await pair([ + { TSX_TSCONFIG_PATH: one }, + { TSX_TSCONFIG_PATH: two }, +]); +// Worker environments differ from the main thread's OS environment. In particular, +// no missing variable in a worker may fall back to the parent's native getenv(). +const upstream = await pair([ + { TS_NODE_PROJECT: one, OXC_TSCONFIG_PATH: two }, + { OXC_TSCONFIG_PATH: two }, +]); +const explicitAndDefault = await pair([ + { VP_SCRIPT_TSCONFIG: one, TSX_TSCONFIG_PATH: two }, + {}, +]); +console.log(JSON.stringify({ parentBefore, compat, upstream, explicitAndDefault }, null, 2)); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config.ts new file mode 100644 index 0000000000..f3911873e4 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/config.ts @@ -0,0 +1,8 @@ +import { value } from '@value'; +console.log(JSON.stringify({ + value, + explicit: process.env.VP_SCRIPT_TSCONFIG ?? null, + compat: process.env.TSX_TSCONFIG_PATH ?? null, + upstream: process.env.OXC_TSCONFIG_PATH ?? null, + tsnode: process.env.TS_NODE_PROJECT ?? null, +})); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/dedup.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/dedup.mjs new file mode 100644 index 0000000000..bc60db957d --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/dedup.mjs @@ -0,0 +1,16 @@ +import Module from 'node:module'; + +const transform = Module._extensions['.ts']; +const resolve = Module._resolveFilename; +await import('vite-plus/register'); +// Re-evaluate the public wrapper, while retaining the emitted hook identities. +const again = new URL(import.meta.resolve('vite-plus/register')); +again.searchParams.set('again', '1'); +await import(again.href); +const { format } = await import('./module.mts'); +console.log(JSON.stringify({ + transformType: typeof transform, + sameTransform: Module._extensions['.ts'] === transform, + sameResolve: Module._resolveFilename === resolve, + format, +}, null, 2)); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/dependency-error.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/dependency-error.ts new file mode 100644 index 0000000000..0399633fc2 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/dependency-error.ts @@ -0,0 +1,2 @@ +import { fail } from 'fixture-ts-import/error'; +fail(); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/error-report.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/error-report.mjs new file mode 100644 index 0000000000..39d51ef62d --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/error-report.mjs @@ -0,0 +1,8 @@ +try { + await import(process.argv[2]); +} catch (error) { + console.log(error.message); + // Keep the source location visible: the runner removes ordinary file:// stack frames. + console.log('source:', error.stack.split('\n')[1].trim().replace(/^at /, '')); + process.exitCode = 1; +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/error.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/error.ts new file mode 100644 index 0000000000..fda19bce35 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/error.ts @@ -0,0 +1,7 @@ +enum Failure { Message = 'mapped failure' } +class Broken { + constructor(public message: string) { + throw new Error(message); + } +} +new Broken(Failure.Message); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/internal-host.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/internal-host.mjs new file mode 100644 index 0000000000..b5429a0560 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/internal-host.mjs @@ -0,0 +1,12 @@ +import { once } from 'node:events'; +import { register } from 'node:module'; +import { MessageChannel } from 'node:worker_threads'; + +const { port1, port2 } = new MessageChannel(); +const message = once(port1, 'message', { signal: AbortSignal.timeout(15000) }); +register(new URL('./internal-loader.mjs', import.meta.url), { + parentURL: import.meta.url, data: { port: port2 }, transferList: [port2], +}); +const [result] = await message; +port1.close(); +console.log(JSON.stringify(result, null, 2)); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/internal-loader.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/internal-loader.mjs new file mode 100644 index 0000000000..4a89bdff16 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/internal-loader.mjs @@ -0,0 +1,13 @@ +import Module from 'node:module'; +import { isInternalThread } from 'node:worker_threads'; + +export async function initialize({ port }) { + const transform = Module._extensions['.ts']; + const resolve = Module._resolveFilename; + await import('vite-plus/register'); + port.postMessage({ + internal: isInternalThread, + unchanged: Module._extensions['.ts'] === transform && Module._resolveFilename === resolve, + }); + port.close(); +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/javascript-view.jsx b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/javascript-view.jsx new file mode 100644 index 0000000000..661fc2ee73 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/javascript-view.jsx @@ -0,0 +1,4 @@ +function h(tag, props, ...children) { + return { tag, children }; +} +export const view =
jsx
; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/main.cts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/main.cts new file mode 100644 index 0000000000..6732f14972 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/main.cts @@ -0,0 +1,13 @@ +const common = require('./common.cts'); +const dependency: string = require('fixture-ts-require'); +const decorated: string[] = []; +function record(_target: object, key: string) { decorated.push(key); } +class Counter { + field = 2; + constructor(public initial: number) {} + @record + total() { return this.initial + this.field; } +} +console.log(JSON.stringify({ + common, dependency, total: new Counter(3).total(), decorated, +}, null, 2)); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/main.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/main.ts new file mode 100644 index 0000000000..b4dda26937 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/main.ts @@ -0,0 +1,22 @@ +import { value } from '@value'; +import { value as remapped } from './remapped.js'; +import { format } from './module.mts'; +import common from './common.cts'; +import { view } from './view.tsx'; +import { view as jsx } from './javascript-view.jsx'; +import { kind } from 'fixture-ts-import'; +import dependency from 'fixture-ts-require'; + +namespace Options { export const count = 3; } +const decorated: string[] = []; +function record(_target: object, key: string) { decorated.push(key); } +class Counter { + field = 2; + constructor(public initial: number) {} + @record + total() { return this.initial + this.field; } +} +console.log(JSON.stringify({ + value, remapped, format, common, view, jsx, kind, dependency, + total: new Counter(Options.count).total(), decorated, +}, null, 2)); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/module.mts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/module.mts new file mode 100644 index 0000000000..b619caf0f5 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/module.mts @@ -0,0 +1,2 @@ +enum Format { Value = 'mts' } +export const format: Format = await Promise.resolve(Format.Value); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/name #%.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/name #%.ts new file mode 100644 index 0000000000..a06fba0a4c --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/name #%.ts @@ -0,0 +1,2 @@ +enum Value { Current = 'encoded filename' } +console.log(Value.Current); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/nested.cts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/nested.cts new file mode 100644 index 0000000000..f6dc2278ca --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/nested.cts @@ -0,0 +1,2 @@ +enum Value { Current = 'nested' } +module.exports = Value.Current; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/remapped.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/remapped.ts new file mode 100644 index 0000000000..bc2cfa2634 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/remapped.ts @@ -0,0 +1,2 @@ +export enum Value { Current = 'remapped' } +export const value: Value = Value.Current; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/setup.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/setup.ts new file mode 100644 index 0000000000..a16c228f87 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/setup.ts @@ -0,0 +1,2 @@ +enum Setup { Value = 'loaded' } +(globalThis as any).setup = Setup.Value; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/spawn.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/spawn.mjs new file mode 100644 index 0000000000..0c053ce69f --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/spawn.mjs @@ -0,0 +1,10 @@ +import { spawnSync } from 'node:child_process'; + +const child = spawnSync(process.execPath, ['./src/main.ts'], { + encoding: 'utf8', + timeout: 15000, +}); +if (child.error) throw child.error; +console.log(JSON.stringify({ execArgv: process.execArgv, exitCode: child.status, signal: child.signal }, null, 2)); +process.stdout.write(child.stdout); +process.stderr.write(child.stderr); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/value.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/value.ts new file mode 100644 index 0000000000..97214aaa78 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/value.ts @@ -0,0 +1,2 @@ +export enum Value { Current = 'default' } +export const value: Value = Value.Current; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/view.tsx b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/view.tsx new file mode 100644 index 0000000000..ee2d34fdee --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/view.tsx @@ -0,0 +1,4 @@ +function h(tag: string, props: object | null, ...children: unknown[]) { + return { tag, children }; +} +export const view =
tsx
; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/worker-host.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/worker-host.mjs new file mode 100644 index 0000000000..bb571b349a --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/worker-host.mjs @@ -0,0 +1,15 @@ +import { once } from 'node:events'; +import { createRequire } from 'node:module'; +import { pathToFileURL } from 'node:url'; +import { Worker } from 'node:worker_threads'; + +const register = pathToFileURL(createRequire(import.meta.url).resolve('vite-plus/register')).href; +const worker = new Worker(new URL('./worker.mts', import.meta.url), { + execArgv: ['--import', register], +}); +try { + const [message] = await once(worker, 'message', { signal: AbortSignal.timeout(15000) }); + console.log(JSON.stringify({ message }, null, 2)); +} finally { + await worker.terminate(); +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/worker.cts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/worker.cts new file mode 100644 index 0000000000..061206ad74 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/worker.cts @@ -0,0 +1,4 @@ +const { parentPort } = require('node:worker_threads'); +const common = require('./common.cts'); +enum Result { Value = 'cjs worker' } +parentPort.postMessage(`${Result.Value}: ${common.format}`); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/worker.mts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/worker.mts new file mode 100644 index 0000000000..7471baca9b --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/src/worker.mts @@ -0,0 +1,5 @@ +import { parentPort } from 'node:worker_threads'; +import common from './common.cts'; +import { value } from '@value'; +enum Result { Value = 'esm worker' } +parentPort!.postMessage(`${Result.Value}: ${common.format}: ${value}`); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/tests/enum.test.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/tests/enum.test.ts new file mode 100644 index 0000000000..43190dc102 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/tests/enum.test.ts @@ -0,0 +1,6 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +enum Result { Value = 'node:test' } +test('preload reaches the test process', () => { + assert.equal(Result.Value, 'node:test'); +}); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/tsconfig.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/tsconfig.json new file mode 100644 index 0000000000..4805b9707d --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "experimentalDecorators": true, + "allowJs": true, + "jsx": "react", + "jsxFactory": "h", + "baseUrl": ".", + "paths": { + "@value": ["./src/value.ts"] + } + }, + "include": ["src", "tests"] +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/verify-unsupported.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/verify-unsupported.mjs new file mode 100644 index 0000000000..a8a32df2ca --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/verify-unsupported.mjs @@ -0,0 +1,15 @@ +import assert from 'node:assert/strict'; +import { spawnSync } from 'node:child_process'; + +assert.equal(Number(process.versions.node.split('.')[0]), 20); +const child = spawnSync(process.execPath, [ + '--require', './binding-guard.cjs', + '--import=vite-plus/register', + '--eval', 'throw new Error("APPLICATION_RAN");', +], { encoding: 'utf8', timeout: 15000 }); +assert.equal(child.error, undefined); +assert.equal(child.status, 1, child.stderr); +assert.equal(child.stdout, ''); +assert.match(child.stderr, /requires Node\.js/); +assert.doesNotMatch(child.stderr, /NATIVE_BINDING_LOADED|APPLICATION_RAN|SyntaxError/); +console.log('unsupported Node.js fails before binding and application loading'); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/verify-watch.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/verify-watch.mjs new file mode 100644 index 0000000000..2459b38bc3 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_script_import/verify-watch.mjs @@ -0,0 +1,50 @@ +import assert from 'node:assert/strict'; +import { spawn } from 'node:child_process'; +import { once } from 'node:events'; +import { writeFileSync } from 'node:fs'; +import { writeFile, unlink } from 'node:fs/promises'; + +const entry = 'src/watch-entry.ts'; +const dependency = 'src/watch-dependency.ts'; +await writeFile(entry, "import { value } from './watch-dependency.ts'; console.log('watch-value:' + value);\n"); +await writeFile(dependency, "export enum Value { Current = 'first' }\nexport const value = Value.Current;\n"); +const child = spawn(process.execPath, [ + '--watch', '--watch-preserve-output', '--import=vite-plus/register', entry, +], { stdio: ['ignore', 'pipe', 'pipe'] }); +let output = ''; +let edited = false; +const exited = once(child, 'exit'); +let timer; +let rewrite; +try { + await new Promise((resolve, reject) => { + timer = setTimeout(() => reject(new Error(`native watch timed out:\n${output}`)), 20000); + child.once('error', reject); + child.once('exit', (code, signal) => reject(new Error(`native watch exited ${code}/${signal}:\n${output}`))); + child.stderr.on('data', chunk => { output += chunk; }); + child.stdout.on('data', chunk => { + output += chunk; + if (!edited && output.includes('watch-value:first')) { + edited = true; + // The child can print before the parent receives its dependency report. + // Repeat the edit until the real watcher observes it, as the vpx fixture does. + const edit = () => { + try { + writeFileSync(dependency, "export enum Value { Current = 'second' }\nexport const value = Value.Current;\n"); + } catch (error) { reject(error); } + }; + edit(); + rewrite = setInterval(edit, 500); + } + if (output.includes('watch-value:second')) resolve(); + }); + }); + assert.equal(edited, true); + console.log('native node --watch reloads an imported TypeScript dependency'); +} finally { + clearTimeout(timer); + clearInterval(rewrite); + child.kill(); + await exited; + await Promise.all([unlink(entry), unlink(dependency)]); +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx/snapshots/migration_tsx_already_vite_plus.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx/snapshots/migration_tsx_already_vite_plus.md index 924cf81287..c2fa29fee6 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx/snapshots/migration_tsx_already_vite_plus.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx/snapshots/migration_tsx_already_vite_plus.md @@ -29,12 +29,12 @@ VITE+ - The Unified Toolchain for the Web 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. -● tsx scripts detected. Auto-migrating to vpx... +● tsx scripts detected. Auto-migrating to Vite+... ◇ Updated . to Vite+ • Node pnpm • Dependencies: vite → -• tsx scripts migrated to `vpx` +• tsx scripts migrated to Vite+ ``` ## `vpt print-file package.json` @@ -49,7 +49,7 @@ scripts run vpx, and the tsx devDependency is gone }, "name": "migration-tsx", "scripts": { - "bench": "vpx ./scripts/bench.ts", + "bench": "node --import vite-plus/register ./scripts/bench.ts", "dev": "vpx --watch --clear-screen=false src/server.ts", "seed": "cross-env NODE_ENV=development vpx scripts/seed.ts", "start": "concurrently \"vpx --watch src/server.ts\" \"vp dev\"" diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx/snapshots/migration_tsx_kept.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx/snapshots/migration_tsx_kept.md index 83ca0a570a..3a1e8331d9 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx/snapshots/migration_tsx_kept.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx/snapshots/migration_tsx_kept.md @@ -15,11 +15,11 @@ scripts vpx cannot run keep tsx, with a warning for each ``` VITE+ - The Unified Toolchain for the Web -● tsx scripts detected. Auto-migrating to vpx... +● tsx scripts detected. Auto-migrating to Vite+... ◇ Migrated . to Vite+ • Node pnpm • 1 config update applied -• tsx scripts migrated to `vpx` +• tsx scripts migrated to Vite+ ! Warnings: - Kept tsx in package.json scripts "calc": `vpx` does not run TypeScript passed to --eval or --print. - Kept tsx in package.json scripts "repl": `vpx` does not start a TypeScript REPL. @@ -39,7 +39,7 @@ the other scripts run vpx, and the tsx devDependency stays }, "name": "migration-tsx", "scripts": { - "bench": "vpx ./scripts/bench.ts", + "bench": "node --import vite-plus/register ./scripts/bench.ts", "calc": "tsx -e 'console.log(1 + 1)'", "dev": "vpx --watch --clear-screen=false src/server.ts", "repl": "tsx", diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx/snapshots/migration_tsx_scripts.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx/snapshots/migration_tsx_scripts.md index 6bbb17a93e..fdcc90f780 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx/snapshots/migration_tsx_scripts.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx/snapshots/migration_tsx_scripts.md @@ -9,11 +9,11 @@ tsx scripts should migrate to vpx automatically ``` VITE+ - The Unified Toolchain for the Web -● tsx scripts detected. Auto-migrating to vpx... +● tsx scripts detected. Auto-migrating to Vite+... ◇ Migrated . to Vite+ • Node pnpm • 1 config update applied -• tsx scripts migrated to `vpx` +• tsx scripts migrated to Vite+ ``` ## `vpt print-file package.json` @@ -27,7 +27,7 @@ scripts run vpx, and the tsx devDependency is gone "scripts": { "dev": "vpx --watch --clear-screen=false src/server.ts", "seed": "cross-env NODE_ENV=development vpx scripts/seed.ts", - "bench": "vpx ./scripts/bench.ts", + "bench": "node --import vite-plus/register ./scripts/bench.ts", "start": "concurrently \"vpx --watch src/server.ts\" \"vp dev\"" }, "devDependencies": { diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/bad.cts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/bad.cts new file mode 100644 index 0000000000..91749b5dff --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/bad.cts @@ -0,0 +1,3 @@ +import { readFileSync } from 'node:fs'; + +console.log(typeof readFileSync); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/good.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/good.ts new file mode 100644 index 0000000000..356dbebdb6 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/good.ts @@ -0,0 +1,3 @@ +const message: string = 'compatible script'; + +console.log(message); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/package.json new file mode 100644 index 0000000000..e8339b8470 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/package.json @@ -0,0 +1,13 @@ +{ + "name": "migration-tsx-inherited-preload", + "type": "module", + "scripts": { + "generate": "tsx bad.cts", + "inherited": "NODE_OPTIONS=--import=tsx node --run generate", + "compatible": "tsx good.ts" + }, + "devDependencies": { + "tsx": "4.23.15", + "vite-plus": "latest" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/pnpm-workspace.yaml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/pnpm-workspace.yaml new file mode 100644 index 0000000000..5ed0b5af0d --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +allowBuilds: + esbuild: true diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/snapshots.toml new file mode 100644 index 0000000000..5990177ece --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/snapshots.toml @@ -0,0 +1,46 @@ +[[case]] +name = "migration_tsx_inherited_node_run" +vp = "local" +local-registry = true +skip-platforms = ["windows"] +comment = "A Node.js script launcher passes NODE_OPTIONS to its retained tsx child. The local registry supplies published tsx so both commands execute its loader. The environment assignment uses POSIX shell syntax." +steps = [ + { argv = ["vp", "install"], snapshot = false }, + { argv = ["vp", "run", "inherited"], comment = "The original inherited tsx preload executes the CommonJS TypeScript child." }, + { argv = ["vp", "migrate", "--full", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "Full migration keeps the child script and its inherited tsx preload." }, + { argv = ["vpt", "print-file", "package.json"], comment = "The compatible script moves to vpx; the retained child and NODE_OPTIONS keep tsx." }, + { argv = ["vp", "run", "inherited"], comment = "The migrated project still prints function." }, + { argv = ["vp", "run", "compatible"], comment = "The compatible TypeScript script executes through vpx." }, +] + +[[case]] +name = "migration_tsx_inherited_npm_run" +vp = "local" +local-registry = true +skip-platforms = ["windows"] +comment = "An npm script launcher passes NODE_OPTIONS to its retained tsx child. Real tsx from the local registry executes the original and migrated commands. The environment assignment uses POSIX shell syntax." +steps = [ + { argv = ["vpt", "json-edit", "package.json", "scripts.inherited", "NODE_OPTIONS=--import=tsx npm run generate"], snapshot = false }, + { argv = ["vp", "install"], snapshot = false }, + { argv = ["vp", "run", "inherited"], comment = "The original inherited tsx preload executes the CommonJS TypeScript child." }, + { argv = ["vp", "migrate", "--full", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "Full migration keeps the child script and its inherited tsx preload." }, + { argv = ["vpt", "print-file", "package.json"], comment = "The compatible script moves to vpx; the retained child and NODE_OPTIONS keep tsx." }, + { argv = ["vp", "run", "inherited"], comment = "The migrated project still prints function." }, + { argv = ["vp", "run", "compatible"], comment = "The compatible TypeScript script executes through vpx." }, +] + +[[case]] +name = "migration_tsx_inherited_env_wrapper" +vp = "local" +local-registry = true +skip-platforms = ["windows"] +comment = "The env wrapper must preserve an inherited tsx preload for a CommonJS TypeScript file with imports. Real tsx from the local registry executes both commands. The wrapper and environment assignment require POSIX." +steps = [ + { argv = ["vpt", "json-edit", "package.json", "scripts.inherited", "NODE_OPTIONS=--import=tsx env node bad.cts"], snapshot = false }, + { argv = ["vp", "install"], snapshot = false }, + { argv = ["vp", "run", "inherited"], comment = "The original env-wrapped command prints function." }, + { argv = ["vp", "migrate", "--full", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "Full migration keeps tsx for the incompatible entry behind env." }, + { argv = ["vpt", "print-file", "package.json"], comment = "The compatible script moves to vpx; the wrapper keeps its preload and tsx remains installed." }, + { argv = ["vp", "run", "inherited"], comment = "The migrated project still prints function." }, + { argv = ["vp", "run", "compatible"], comment = "The compatible TypeScript script executes through vpx." }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/snapshots/migration_tsx_inherited_env_wrapper.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/snapshots/migration_tsx_inherited_env_wrapper.md new file mode 100644 index 0000000000..eede7d825f --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/snapshots/migration_tsx_inherited_env_wrapper.md @@ -0,0 +1,78 @@ +# migration_tsx_inherited_env_wrapper + +The env wrapper must preserve an inherited tsx preload for a CommonJS TypeScript file with imports. Real tsx from the local registry executes both commands. The wrapper and environment assignment require POSIX. + +## `vpt json-edit package.json scripts.inherited 'NODE_OPTIONS=--import=tsx env node bad.cts'` + + +## `vp install` + + +## `vp run inherited` + +The original env-wrapped command prints function. + +``` +$ NODE_OPTIONS=--import=tsx env node bad.cts ⊘ cache disabled +function +``` + +## `vp migrate --full --no-interactive --no-hooks --no-agent --no-editor` + +Full migration keeps tsx for the incompatible entry behind env. + +``` +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. +● tsx scripts detected. Auto-migrating to Vite+... +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• tsx scripts migrated to Vite+ +• Package manager settings configured +! Warnings: + - Kept tsx in package.json scripts "generate": `bad.cts` uses import or export, which `vpx` does not compile to CommonJS; rename it to .mts. + - Kept tsx in package.json scripts "inherited": `bad.cts` uses import or export, which `vpx` does not compile to CommonJS; rename it to .mts. + - Kept the tsx devDependency in package.json: `scripts` in package.json still uses tsx. +``` + +## `vpt print-file package.json` + +The compatible script moves to vpx; the wrapper keeps its preload and tsx remains installed. + +``` +{ + "devDependencies": { + "tsx": "4.23.15", + "vite-plus": "catalog:" + }, + "name": "migration-tsx-inherited-preload", + "scripts": { + "compatible": "vpx good.ts", + "generate": "tsx bad.cts", + "inherited": "NODE_OPTIONS=--import=tsx env node bad.cts" + }, + "type": "module" +} +``` + +## `vp run inherited` + +The migrated project still prints function. + +``` +$ NODE_OPTIONS=--import=tsx env node bad.cts ⊘ cache disabled +function +``` + +## `vp run compatible` + +The compatible TypeScript script executes through vpx. + +``` +$ vpx good.ts ⊘ cache disabled +compatible script +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/snapshots/migration_tsx_inherited_node_run.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/snapshots/migration_tsx_inherited_node_run.md new file mode 100644 index 0000000000..c40dbf6818 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/snapshots/migration_tsx_inherited_node_run.md @@ -0,0 +1,75 @@ +# migration_tsx_inherited_node_run + +A Node.js script launcher passes NODE_OPTIONS to its retained tsx child. The local registry supplies published tsx so both commands execute its loader. The environment assignment uses POSIX shell syntax. + +## `vp install` + + +## `vp run inherited` + +The original inherited tsx preload executes the CommonJS TypeScript child. + +``` +$ NODE_OPTIONS=--import=tsx node --run generate ⊘ cache disabled +function +``` + +## `vp migrate --full --no-interactive --no-hooks --no-agent --no-editor` + +Full migration keeps the child script and its inherited tsx preload. + +``` +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. +● tsx scripts detected. Auto-migrating to Vite+... +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• tsx scripts migrated to Vite+ +• Package manager settings configured +! Warnings: + - Kept tsx in package.json scripts "generate": `bad.cts` uses import or export, which `vpx` does not compile to CommonJS; rename it to .mts. + - Kept tsx in package.json scripts "inherited": script `generate` needs tsx: `bad.cts` uses import or export, which `vpx` does not compile to CommonJS; rename it to .mts. + - Kept the tsx devDependency in package.json: `scripts` in package.json still uses tsx. +``` + +## `vpt print-file package.json` + +The compatible script moves to vpx; the retained child and NODE_OPTIONS keep tsx. + +``` +{ + "name": "migration-tsx-inherited-preload", + "type": "module", + "scripts": { + "generate": "tsx bad.cts", + "inherited": "NODE_OPTIONS=--import=tsx node --run generate", + "compatible": "vpx good.ts" + }, + "devDependencies": { + "tsx": "4.23.15", + "vite-plus": "catalog:" + } +} +``` + +## `vp run inherited` + +The migrated project still prints function. + +``` +$ NODE_OPTIONS=--import=tsx node --run generate ⊘ cache disabled +function +``` + +## `vp run compatible` + +The compatible TypeScript script executes through vpx. + +``` +$ vpx good.ts ⊘ cache disabled +compatible script +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/snapshots/migration_tsx_inherited_npm_run.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/snapshots/migration_tsx_inherited_npm_run.md new file mode 100644 index 0000000000..a0e7ba0952 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_inherited_preload/snapshots/migration_tsx_inherited_npm_run.md @@ -0,0 +1,86 @@ +# migration_tsx_inherited_npm_run + +An npm script launcher passes NODE_OPTIONS to its retained tsx child. Real tsx from the local registry executes the original and migrated commands. The environment assignment uses POSIX shell syntax. + +## `vpt json-edit package.json scripts.inherited 'NODE_OPTIONS=--import=tsx npm run generate'` + + +## `vp install` + + +## `vp run inherited` + +The original inherited tsx preload executes the CommonJS TypeScript child. + +``` +$ NODE_OPTIONS=--import=tsx npm run generate ⊘ cache disabled + +> generate +> tsx bad.cts + +function +``` + +## `vp migrate --full --no-interactive --no-hooks --no-agent --no-editor` + +Full migration keeps the child script and its inherited tsx preload. + +``` +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. +● tsx scripts detected. Auto-migrating to Vite+... +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• tsx scripts migrated to Vite+ +• Package manager settings configured +! Warnings: + - Kept tsx in package.json scripts "generate": `bad.cts` uses import or export, which `vpx` does not compile to CommonJS; rename it to .mts. + - Kept tsx in package.json scripts "inherited": script `generate` needs tsx: `bad.cts` uses import or export, which `vpx` does not compile to CommonJS; rename it to .mts. + - Kept the tsx devDependency in package.json: `scripts` in package.json still uses tsx. +``` + +## `vpt print-file package.json` + +The compatible script moves to vpx; the retained child and NODE_OPTIONS keep tsx. + +``` +{ + "devDependencies": { + "tsx": "4.23.15", + "vite-plus": "catalog:" + }, + "name": "migration-tsx-inherited-preload", + "scripts": { + "compatible": "vpx good.ts", + "generate": "tsx bad.cts", + "inherited": "NODE_OPTIONS=--import=tsx npm run generate" + }, + "type": "module" +} +``` + +## `vp run inherited` + +The migrated project still prints function. + +``` +$ NODE_OPTIONS=--import=tsx npm run generate ⊘ cache disabled + +> generate +> tsx bad.cts + +function +``` + +## `vp run compatible` + +The compatible TypeScript script executes through vpx. + +``` +$ vpx good.ts ⊘ cache disabled +compatible script +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/.fixture/tsx/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/.fixture/tsx/package.json new file mode 100644 index 0000000000..e900a90163 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/.fixture/tsx/package.json @@ -0,0 +1,4 @@ +{ + "name": "tsx", + "version": "4.20.0" +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/package.json new file mode 100644 index 0000000000..27eea0adc6 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/package.json @@ -0,0 +1,17 @@ +{ + "name": "migration-tsx-preload", + "type": "module", + "scripts": { + "seed": "node --import=tsx ./scripts/seed.ts", + "eval": "node --import tsx --eval \"import('./scripts/seed.ts')\"", + "test": "node --import tsx --test", + "watch": "node --import=tsx --watch --test ./scripts/seed.ts", + "tool": "cross-env NODE_OPTIONS=\"--trace-warnings --import tsx\" mocha", + "config": "TSX_TSCONFIG_PATH=tsconfig.json node --import tsx ./scripts/seed.ts", + "launcher": "NODE_OPTIONS=--import=tsx concurrently \"node ./scripts/seed.ts\" \"node --test\"" + }, + "devDependencies": { + "tsx": "file:.fixture/tsx", + "vite": "^7.0.0" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/scripts/seed.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/scripts/seed.ts new file mode 100644 index 0000000000..81130916e2 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/scripts/seed.ts @@ -0,0 +1,2 @@ +const message: string = 'TypeScript preload works'; +console.log(message); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/snapshots.toml new file mode 100644 index 0000000000..87daf044f5 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/snapshots.toml @@ -0,0 +1,12 @@ +[[case]] +name = "migration_tsx_preload" +vp = "local" +comment = "Plain tsx preloads keep Node.js and its flags. The local tsx stub keeps installation offline." +steps = [ + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "Migrate preload flags and literal NODE_OPTIONS assignments.", continue-on-failure = true }, + { argv = ["vpt", "print-file", "package.json"], comment = "All commands keep Node.js or the tool launcher, and tsx is removed.", continue-on-failure = true }, + { argv = ["vp", "run", "seed"], comment = "The migrated file command resolves the installed preload.", continue-on-failure = true }, + { argv = ["vp", "run", "eval"], comment = "The migrated eval command keeps native Node.js behavior.", continue-on-failure = true }, + { argv = ["vp", "migrate", "--full", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "A second migration finds no remaining tsx use.", continue-on-failure = true }, + { argv = ["vpt", "print-file", "package.json"], comment = "The scripts are unchanged on the second run.", continue-on-failure = true }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/snapshots/migration_tsx_preload.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/snapshots/migration_tsx_preload.md new file mode 100644 index 0000000000..282df84862 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/snapshots/migration_tsx_preload.md @@ -0,0 +1,94 @@ +# migration_tsx_preload + +Plain tsx preloads keep Node.js and its flags. The local tsx stub keeps installation offline. + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +Migrate preload flags and literal NODE_OPTIONS assignments. + +``` +VITE+ - The Unified Toolchain for the Web + +● tsx scripts detected. Auto-migrating to Vite+... +◇ Migrated . to Vite+ +• Node pnpm +• 1 config update applied +• tsx scripts migrated to Vite+ +``` + +## `vpt print-file package.json` + +All commands keep Node.js or the tool launcher, and tsx is removed. + +``` +{ + "name": "migration-tsx-preload", + "type": "module", + "scripts": { + "seed": "node --import=vite-plus/register ./scripts/seed.ts", + "eval": "node --import vite-plus/register --eval \"import('./scripts/seed.ts')\"", + "test": "node --import vite-plus/register --test", + "watch": "node --import=vite-plus/register --watch --test ./scripts/seed.ts", + "tool": "cross-env NODE_OPTIONS=\"--trace-warnings --import vite-plus/register\" mocha", + "config": "TSX_TSCONFIG_PATH=tsconfig.json node --import vite-plus/register ./scripts/seed.ts", + "launcher": "NODE_OPTIONS=--import=vite-plus/register concurrently \"node ./scripts/seed.ts\" \"node --test\"" + }, + "devDependencies": { + "vite": "catalog:", + "vite-plus": "catalog:" + } +} +``` + +## `vp run seed` + +The migrated file command resolves the installed preload. + +``` +$ node --import=vite-plus/register ./scripts/seed.ts ⊘ cache disabled +TypeScript preload works +``` + +## `vp run eval` + +The migrated eval command keeps native Node.js behavior. + +``` +$ node --import vite-plus/register --eval "import('./scripts/seed.ts')" ⊘ cache disabled +TypeScript preload works +``` + +## `vp migrate --full --no-interactive --no-hooks --no-agent --no-editor` + +A second migration finds no remaining tsx use. + +``` +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. +This project is already using Vite+! Happy coding! +``` + +## `vpt print-file package.json` + +The scripts are unchanged on the second run. + +``` +{ + "name": "migration-tsx-preload", + "type": "module", + "scripts": { + "seed": "node --import=vite-plus/register ./scripts/seed.ts", + "eval": "node --import vite-plus/register --eval \"import('./scripts/seed.ts')\"", + "test": "node --import vite-plus/register --test", + "watch": "node --import=vite-plus/register --watch --test ./scripts/seed.ts", + "tool": "cross-env NODE_OPTIONS=\"--trace-warnings --import vite-plus/register\" mocha", + "config": "TSX_TSCONFIG_PATH=tsconfig.json node --import vite-plus/register ./scripts/seed.ts", + "launcher": "NODE_OPTIONS=--import=vite-plus/register concurrently \"node ./scripts/seed.ts\" \"node --test\"" + }, + "devDependencies": { + "vite": "catalog:", + "vite-plus": "catalog:" + } +} +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/tsconfig.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/tsconfig.json new file mode 100644 index 0000000000..678ff85dad --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_tsx_preload/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "target": "ESNext" + } +} diff --git a/docs/guide/migrate-rules.md b/docs/guide/migrate-rules.md index 503c2f401e..bc8f63147c 100644 --- a/docs/guide/migrate-rules.md +++ b/docs/guide/migrate-rules.md @@ -278,21 +278,29 @@ Unrelated `bunx` commands and other package-executor forms remain unchanged. 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: -| Before | After | -| -------------------------------------------------------------------------- | ------------------------------------------ | -| `tsx src/seed.ts --rows 10` | `vpx src/seed.ts --rows 10` | -| `tsx watch --include "*.json" src/a.ts` | `vpx --watch --include "*.json" src/a.ts` | -| `npx tsx a.ts`, `pnpm exec tsx a.ts`, `pnpm dlx tsx a.ts`, `bunx tsx a.ts` | `vpx a.ts` | -| `node --import tsx ./a.ts` | `vpx ./a.ts` | -| `cross-env NODE_ENV=dev tsx a.ts` | `cross-env NODE_ENV=dev vpx a.ts` | -| `concurrently "tsx watch a.ts" "vp dev"` | `concurrently "vpx --watch a.ts" "vp dev"` | +| Before | After | +| -------------------------------------------------------------------------- | -------------------------------------------------- | +| `tsx src/seed.ts --rows 10` | `vpx src/seed.ts --rows 10` | +| `tsx watch --include "*.json" src/a.ts` | `vpx --watch --include "*.json" src/a.ts` | +| `npx tsx a.ts`, `pnpm exec tsx a.ts`, `pnpm dlx tsx a.ts`, `bunx tsx a.ts` | `vpx a.ts` | +| `node --import tsx ./a.ts` | `node --import vite-plus/register ./a.ts` | +| `node --import=tsx --test` | `node --import=vite-plus/register --test` | +| `NODE_OPTIONS='--import tsx' mocha` | `NODE_OPTIONS='--import vite-plus/register' mocha` | +| `cross-env NODE_ENV=dev tsx a.ts` | `cross-env NODE_ENV=dev vpx a.ts` | +| `concurrently "tsx watch a.ts" "vp dev"` | `concurrently "vpx --watch a.ts" "vp dev"` | `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)). -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. +The plain `node --import tsx` and `node --import=tsx` forms keep Node.js, its options, wrappers, and arguments. They use the [`vite-plus/register` preload](/guide/vpx#nodejs-preload), so Node.js retains its eval, test discovery, and native watch behavior. Literal `NODE_OPTIONS` assignments also migrate, including assignments through `cross-env` and known command launchers. The preload accepts `TSX_TSCONFIG_PATH` as a fallback. Assignments that also load a legacy tsx hook, or reach a command that still needs tsx, stay unchanged. + +For the tsx executable and legacy loader forms such as `--loader tsx/esm` and `-r tsx/cjs`, migration keeps the existing `vpx` rules. It keeps tsx, with a warning, for `--eval`, `--print`, or `--check`, the REPL, `--test` without files or in watch mode, an entry without a file extension, or commands that load tsx through `NODE_OPTIONS`. Commands with pipes, variables, or subshells also stay unchanged. `vpx` accepts `TSX_TSCONFIG_PATH` as a fallback too; an explicit `--tsconfig` takes priority. + +Both migration paths read the scripts a command names, including local `--require` and `--import` modules. They keep tsx when a script does not exist yet, as when an earlier command generates it, or when the Vite+ loader would fail on it: a `require()` of `./a.js` that only tsx resolves to `a.ts`, a `.cts` file with `import` or `export`, or 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 other compiler differences, such as standard decorators, tsconfig `paths` in `require()`, or a tsconfig `module` of `node16` or `nodenext`; the loader reports those when the script runs. A package that lists tsx in `dependencies`, `optionalDependencies`, or `peerDependencies` keeps it too, since the replacement comes from the `vite-plus` devDependency. 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. +Tool configurations and source files need manual edits. For example, change `nodeArguments: ['--import=tsx']` to `nodeArguments: ['--import=vite-plus/register']`, or Mocha's `"import": "tsx"` setting to `"import": "vite-plus/register"`. Keep tsx if the project uses its programmatic APIs. + See [Coming from tsx](/guide/vpx#coming-from-tsx) for the other differences. ## Continuous Integration Rules diff --git a/docs/guide/vpx.md b/docs/guide/vpx.md index 5f3fdcf4a4..e94a531033 100644 --- a/docs/guide/vpx.md +++ b/docs/guide/vpx.md @@ -103,6 +103,38 @@ Scripts can use `vpx` as their interpreter. Make the file executable and keep it 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. +### Node.js Preload + +If a tool starts Node.js itself, replace `--import=tsx` with `--import=vite-plus/register`: + +```bash +node --import=vite-plus/register ./scripts/seed.ts +node --import vite-plus/register --test ./test/example.test.ts +NODE_OPTIONS='--import=vite-plus/register' node ./scripts/seed.ts +``` + +Install `vite-plus` in the project first. The preload resolves from the project and runs on the Node.js that started the command; it requires the same Node.js versions as Vite+. It provides the same TypeScript transforms, tsconfig resolution, and source maps as `vpx`. + +Tools with a Node.js argument setting can replace `["--import", "tsx"]` with `["--import", "vite-plus/register"]`. For example, an AVA configuration can use: + +```json +{ + "ava": { + "nodeArguments": ["--import=vite-plus/register"] + } +} +``` + +Keep the tool's existing TypeScript file patterns and other settings. A JavaScript launcher can also run `await import('vite-plus/register')` before dynamically importing a TypeScript file. A static TypeScript import in that launcher is resolved before registration, so use `--import` for static imports. + +`fork()` and workers inherit the preload through Node.js arguments. `NODE_OPTIONS` also passes it to spawned Node.js processes. Replacing a child's arguments or environment follows Node.js's normal inheritance rules. + +To select a tsconfig, set `VP_SCRIPT_TSCONFIG=tsconfig.scripts.json`. The existing `TSX_TSCONFIG_PATH` variable also works, with `VP_SCRIPT_TSCONFIG` taking precedence. Otherwise the [usual tsconfig discovery](#tsconfig) applies. Each worker selects its config from its own environment, so workers can use different configs without changing their parent. + +Node.js owns its flags, test discovery, and native watch mode. Eval and stdin can import TypeScript files, but their input text is not transformed. All `--require` preloads run before `--import`: put a TypeScript setup file after the loader as `--import ./setup.ts`. Repeated imports of `vite-plus/register` are harmless, including when a tool already runs under `vpx`. + +Remove the old tsx preload when you add this one. The [compiler differences from tsx](#coming-from-tsx) also apply to the public preload. + ### Watch Mode ```bash @@ -132,7 +164,7 @@ It watches every file the script imports or requires (in worker threads too, on `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. -`--tsconfig ` 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. +`--tsconfig ` applies one config to every file instead, including files that config does not include. Without `--tsconfig`, `TSX_TSCONFIG_PATH`, `TS_NODE_PROJECT`, or `OXC_TSCONFIG_PATH` can select a config, in that order. If it names a file that does not exist, or a tsconfig fails to parse, `vpx` prints a warning and runs without that config. A file that no tsconfig applies to runs as if its `compilerOptions` were empty: @@ -193,8 +225,8 @@ JSX that depends on a compiler plugin is not supported. `vpx` compiles JSX only - **Node.js version**: `vpx` needs Node.js `^22.18.0 || ^24.11.0 || >=26.0.0`, while tsx also runs on older releases. - **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. - **Eval, REPL, and `--check`**: `vpx --eval`, the REPL, and `vpx --check` do not transform TypeScript. -- **`node --import tsx`**: `vpx` has no equivalent for tools that must start `node` themselves. -- **Other loaders**: remove `--import tsx` from `NODE_OPTIONS`. Two loaders registered together compete for the same files. +- **`node --import tsx`**: replace it with [`node --import vite-plus/register`](#nodejs-preload), including in tool settings and `NODE_OPTIONS`. +- **Other loaders**: replace the tsx preload instead of adding a second loader. Two loaders registered together compete for the same files. ## `vp exec` diff --git a/packages/cli/BUNDLING.md b/packages/cli/BUNDLING.md index 58e854fb7a..fd32bd237e 100644 --- a/packages/cli/BUNDLING.md +++ b/packages/cli/BUNDLING.md @@ -312,8 +312,9 @@ A binding built by `pnpm build` contains: `vpx