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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const assert = require('node:assert/strict');
const { execFileSync } = require('node:child_process');

const installed = JSON.parse(
execFileSync('vp', ['env', 'list', 'npm', '--json'], { encoding: 'utf8' }),
).package_managers.npm;
assert.equal(installed.length, 1);
assert.match(installed[0].version, /^\d+\.\d+\.\d+/);
assert.equal(installed[0].current, false);
assert.equal(installed[0].default, false);
console.log('Standalone npm is installed but is not current');
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ steps = [
[[case]]
name = "command_env_install_standalone_npm_fallback"
vp = "global"
comment = "Explicit npm family scopes use standalone registry npm; only the directly invoked npm shim keeps Node.js' bundled npm fallback."
comment = "Explicit npm installation uses standalone registry npm without installing Node.js."
local-registry = true
skip-platforms = ["windows"]
seed-runtime = false
env = { VP_ENV_USE_EVAL_ENABLE = "1", VP_SHELL = "bash" }
steps = [
{ argv = ["vp", "env", "use", "npm", "--no-install"], comment = "an explicit npm scope exports the standalone npm fallback" },
{ argv = ["vp", "env", "install", "npm"], comment = "an explicit npm scope installs the standalone registry fallback" },
{ argv = ["vp", "env", "current", "npm", "--json"], comment = "the standalone npm fallback is installed" },
{ argv = ["vpt", "stat-file", "$VP_HOME/js_runtime/node", "--assert", "missing"], comment = "installing standalone npm does not install Node.js" },
{ argv = ["vp", "env", "install", "22.18.0"], snapshot = false, timeout = 120000 },
{ argv = ["node", "assert-installed-npm.cjs"], envs = [["VP_NODE_VERSION", "22.18.0"]], comment = "the standalone npm fallback is installed but is not selected" },
{ argv = ["vp", "env", "current", "npm", "--json"], envs = [["VP_NODE_VERSION", "22.18.0"]], comment = "installing standalone npm does not select it: current reports Node's bundled npm until a version is configured" },
]
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# command_env_install_standalone_npm_fallback

Explicit npm family scopes use standalone registry npm; only the directly invoked npm shim keeps Node.js' bundled npm fallback.
Explicit npm installation uses standalone registry npm without installing Node.js.

## `vp env use npm --no-install`

Expand All @@ -22,30 +22,41 @@ Installing npm <version>...
Installed npm <version>
```

## `vp env current npm --json`
## `vpt stat-file $VP_HOME/js_runtime/node --assert missing`

installing standalone npm does not install Node.js

```
<home>/.vite-plus/js_runtime/node: missing
```

## `vp env install 22.18.0`


the standalone npm fallback is installed
## `VP_NODE_VERSION=22.18.0 node assert-installed-npm.cjs`

the standalone npm fallback is installed but is not selected

```
Standalone npm is installed but is not current
```

## `VP_NODE_VERSION=22.18.0 vp env current npm --json`

installing standalone npm does not select it: current reports Node's bundled npm until a version is configured

```
{
"package_manager": {
"name": "npm",
"version": "<version>",
"source": "registry fallback",
"source": "Node.js bundled npm",
"bin_paths": {
"npm": "<home>/.vite-plus/package_manager/npm/<version>/npm/bin/npm",
"npx": "<home>/.vite-plus/package_manager/npm/<version>/npm/bin/npx"
"npm": "<home>/.vite-plus/js_runtime/node/<version>/bin/npm",
"npx": "<home>/.vite-plus/js_runtime/node/<version>/bin/npx"
},
"installed": true,
"mode": "managed"
}
}
```

## `vpt stat-file $VP_HOME/js_runtime/node --assert missing`

installing standalone npm does not install Node.js

```
<home>/.vite-plus/js_runtime/node: missing
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const assert = require('node:assert/strict');
const { execSync } = require('node:child_process');
const { dirname, resolve } = require('node:path');

const bundled = require(resolve(
dirname(process.execPath),
process.platform === 'win32' ? 'node_modules/npm/package.json' : '../lib/node_modules/npm/package.json',
)).version;
const expected = process.argv[2] || bundled;
const run = (command) => execSync(command, { encoding: 'utf8' }).trim();

// Clear inherited tool markers so each shim resolves this project's configuration itself.
delete process.env.VP_PATH_INJECTED_TOOLS;
assert.equal(run('npm --version'), expected);
assert.equal(run('npx --version'), expected);
assert.equal(JSON.parse(run('vp pm version --json')).npm, expected);
for (const scope of ['pm', 'npm']) {
const current = JSON.parse(run(`vp env current ${scope} --json`)).package_manager;
assert.equal(current.version, expected);
if (!process.argv[2]) {
assert.equal(current.source, 'Node.js bundled npm');
}
}
console.log(process.argv[2] ? `npm/npx and vp use configured npm ${expected}` : 'npm/npx and vp use Node bundled npm');

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "npm-bundled-default",
"private": true,
"devEngines": {
"runtime": { "name": "node", "version": "22.18.0" }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
[[case]]
name = "npm_lockfile_uses_bundled_version"
vp = "global"
steps = [
{ argv = ["node", "assert-npm.cjs"], comment = "A Node-only devEngines declaration and npm lockfile use bundled npm without querying the registry", envs = [["NPM_CONFIG_REGISTRY", "http://127.0.0.1:9"]] },
{ argv = ["vp", "pm", "patch", "example"], comment = "Bundled npm retains its version gates for unsupported commands" },
{ argv = ["vp", "pm", "approve-builds"], comment = "Bundled npm does not invoke approval commands added in later npm releases" },
{ argv = ["vp", "env", "default", "npm@10.5.0"], snapshot = false },
{ argv = ["node", "assert-npm.cjs", "10.5.0"], comment = "An npm default overrides the bundled version even when a lockfile exists" },
{ argv = ["vpt", "json-edit", "package.json", "devEngines.packageManager", "{\"name\":\"npm\",\"version\":\"10.9.4\"}"], snapshot = false },
{ argv = ["node", "assert-npm.cjs", "10.9.4"], comment = "A devEngines npm version overrides the default" },
{ argv = ["vpt", "json-edit", "package.json", "packageManager", "npm@10.8.2"], snapshot = false },
{ argv = ["node", "assert-npm.cjs", "10.8.2"], comment = "A top-level npm pin still has priority" },
]

[[case]]
name = "npm_current_does_not_install_node"
vp = "global"
seed-runtime = false
env = { VP_NODE_VERSION = "22.18.0", VP_NODE_DIST_MIRROR = "http://127.0.0.1:9" }
steps = [
{ argv = ["vp", "env", "current", "--json"], comment = "An absent Node runtime and bundled npm are reported without downloading" },
{ argv = ["vp", "env", "current", "npm", "--json"], comment = "The explicit npm scope also reports an unknown, uninstalled version" },
{ argv = ["vpt", "stat-file", "$VP_HOME/js_runtime/node/22.18.0", "--assert", "missing"], comment = "Reporting the environment leaves the runtime uninstalled" },
]

[[case]]
name = "npm_bundled_ignores_project_bin"
vp = "global"
skip-platforms = ["windows"]
steps = [
{ argv = ["vpt", "write-file", "bin/npm", "#!/bin/sh\necho project npm must not run\nexit 1\n"], snapshot = false },
{ argv = ["vpt", "chmod", "+x", "bin/npm"], snapshot = false },
{ argv = ["node", "assert-npm.cjs"], comment = "The version probe and actual command both use bundled npm despite an executable project bin/npm" },
]

[[case]]
name = "npm_bundled_env_print"
vp = "global"
env = { VP_SHELL = "bash", NPM_CONFIG_REGISTRY = "http://127.0.0.1:9" }
steps = [
{ argv = ["vp", "env", "print", "pm"], comment = "The selected package manager uses Node's bundled npm directory" },
{ argv = ["vp", "env", "print", "npm"], comment = "The explicit npm scope uses the same bundled directory" },
{ argv = ["vp", "env", "print"], comment = "The combined scope includes the shared directory only once" },
]

[[case]]
name = "npm_bundled_version_probe_ignores_preloads"
vp = "global"
env = { NODE_OPTIONS = "--require ./preload.cjs" }
steps = [
{ argv = ["vpt", "write-file", "preload.cjs", "console.log('preload start');\n"], snapshot = false },
{ argv = ["vp", "pm", "patch", "example"], comment = "A noisy preload cannot bypass npm's patch version gate" },
{ argv = ["vp", "pm", "approve-builds"], comment = "A noisy preload cannot bypass npm's approval version gate" },
{ argv = ["vp", "pm", "version", "--json"], comment = "The actual npm command still executes the user's preload" },
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# npm_bundled_env_print

## `vp env print pm`

The selected package manager uses Node's bundled npm directory

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

# Add to your shell to use this environment for this session:
export PATH="<home>/.vite-plus/js_runtime/node/<version>/bin:$PATH"
```

## `vp env print npm`

The explicit npm scope uses the same bundled directory

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

# Add to your shell to use this environment for this session:
export PATH="<home>/.vite-plus/js_runtime/node/<version>/bin:$PATH"
```

## `vp env print`

The combined scope includes the shared directory only once

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

# Add to your shell to use this environment for this session:
export PATH="<home>/.vite-plus/js_runtime/node/<version>/bin:$PATH"
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# npm_bundled_ignores_project_bin

## `vpt write-file bin/npm '#'\!'/bin/sh
echo project npm must not run
exit 1
'`


## `vpt chmod +x bin/npm`


## `node assert-npm.cjs`

The version probe and actual command both use bundled npm despite an executable project bin/npm

```
npm/npx and vp use Node bundled npm
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# npm_bundled_version_probe_ignores_preloads

## `vpt write-file preload.cjs 'console.log('\''preload start'\'');
'`


## `vp pm patch example`

A noisy preload cannot bypass npm's patch version gate

```
warn: npm does not have a 'patch' command.
```

## `vp pm approve-builds`

A noisy preload cannot bypass npm's approval version gate

```
warn: npm runs lifecycle scripts by default. Upgrade to npm >= 11.16.0 for `npm approve-scripts`/`deny-scripts`, or set `ignore-scripts=true` in .npmrc and rebuild approved packages with `vp pm rebuild <package>`.
```

## `vp pm version --json`

The actual npm command still executes the user's preload

```
preload start
{
"npm": "10.9.3",
"node": "22.18.0",
"acorn": "8.15.0",
"ada": "2.9.2",
"amaro": "1.1.0",
"ares": "1.34.5",
"brotli": "1.1.0",
"cjs_module_lexer": "2.1.0",
"cldr": "47.0",
"icu": "77.1",
"llhttp": "9.3.0",
"modules": "127",
"napi": "10",
"nbytes": "0.1.1",
"ncrypto": "0.0.1",
"nghttp2": "1.64.0",
"openssl": "3.0.16",
"simdjson": "3.13.0",
"simdutf": "6.4.2",
"sqlite": "3.50.2",
"tz": "2025b",
"undici": "6.21.2",
"unicode": "16.0",
"uv": "1.51.0",
"uvwasi": "0.0.21",
"v8": "12.4.254.21-node.27",
"zlib": "1.3.1-470d3a2",
"zstd": "1.5.7"
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# npm_current_does_not_install_node

## `vp env current --json`

An absent Node runtime and bundled npm are reported without downloading

```
{
"node": {
"version": "22.18.0",
"source": "VP_NODE_VERSION",
"bin_path": "<home>/.vite-plus/js_runtime/node/<version>/bin/node",
"installed": false,
"mode": "managed"
},
"package_manager": {
"name": "npm",
"version": "unknown",
"source": "Node.js bundled npm",
"project_root": "<workspace>",
"bin_paths": {
"npm": "<home>/.vite-plus/js_runtime/node/<version>/bin/npm",
"npx": "<home>/.vite-plus/js_runtime/node/<version>/bin/npx"
},
"installed": false,
"mode": "managed"
}
}
```

## `vp env current npm --json`

The explicit npm scope also reports an unknown, uninstalled version

```
{
"package_manager": {
"name": "npm",
"version": "unknown",
"source": "Node.js bundled npm",
"bin_paths": {
"npm": "<home>/.vite-plus/js_runtime/node/<version>/bin/npm",
"npx": "<home>/.vite-plus/js_runtime/node/<version>/bin/npx"
},
"installed": false,
"mode": "managed"
}
}
```

## `vpt stat-file $VP_HOME/js_runtime/node/22.18.0 --assert missing`

Reporting the environment leaves the runtime uninstalled

```
<home>/.vite-plus/js_runtime/node/<version>: missing
```
Loading
Loading