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
5 changes: 5 additions & 0 deletions examples/development/nodejs/nodejs-npm/devbox.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
{
"lockfile_version": "1",
"packages": {
"github:NixOS/nixpkgs/nixpkgs-unstable": {
"last_modified": "2026-06-01T17:55:45Z",
"resolved": "github:NixOS/nixpkgs/4df1b885d76a54e1aa1a318f8d16fd6005b6401f?lastModified=1780336545&narHash=sha256-vhVhuXzFrIOfcssC%2F9hDHx7MHzDKjF3keHuREOQqQiQ%3D"
},
"nodejs@18": {
"last_modified": "2024-02-15T12:53:33Z",
"plugin_version": "0.0.3",
"resolved": "github:NixOS/nixpkgs/085589047343aad800c4d305cf7b98e8a3d51ae2#nodejs_18",
"source": "devbox-search",
"version": "18.19.1",
Expand Down
6 changes: 5 additions & 1 deletion examples/development/nodejs/nodejs-pnpm/devbox.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
"pnpm install"
],
"scripts": {
"run_test": "pnpm run start"
"run_test": [
"echo \"Expecting pnpm $(node -e \"console.log(require('./package.json').packageManager.split('@')[1])\")\"",
"test \"$(pnpm --version)\" = \"$(node -e \"console.log(require('./package.json').packageManager.split('@')[1])\")\"",
"pnpm run start"
]
}
}
}
5 changes: 5 additions & 0 deletions examples/development/nodejs/nodejs-pnpm/devbox.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
{
"lockfile_version": "1",
"packages": {
"github:NixOS/nixpkgs/nixpkgs-unstable": {
"last_modified": "2026-06-01T17:55:45Z",
"resolved": "github:NixOS/nixpkgs/4df1b885d76a54e1aa1a318f8d16fd6005b6401f?lastModified=1780336545&narHash=sha256-vhVhuXzFrIOfcssC%2F9hDHx7MHzDKjF3keHuREOQqQiQ%3D"
},
"nodejs@latest": {
"last_modified": "2024-02-24T23:06:34Z",
"plugin_version": "0.0.3",
"resolved": "github:NixOS/nixpkgs/9a9dae8f6319600fa9aebde37f340975cab4b8c0#nodejs_21",
"source": "devbox-search",
"version": "21.6.2",
Expand Down
6 changes: 5 additions & 1 deletion examples/development/nodejs/nodejs-yarn/devbox.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
{
"lockfile_version": "1",
"packages": {
"github:NixOS/nixpkgs/nixpkgs-unstable": {
"last_modified": "2026-06-01T17:55:45Z",
"resolved": "github:NixOS/nixpkgs/4df1b885d76a54e1aa1a318f8d16fd6005b6401f?lastModified=1780336545&narHash=sha256-vhVhuXzFrIOfcssC%2F9hDHx7MHzDKjF3keHuREOQqQiQ%3D"
},
"nodejs@latest": {
"last_modified": "2024-02-24T23:06:34Z",
"plugin_version": "0.0.1",
"plugin_version": "0.0.3",
"resolved": "github:NixOS/nixpkgs/9a9dae8f6319600fa9aebde37f340975cab4b8c0#nodejs_21",
"source": "devbox-search",
"version": "21.6.2",
Expand Down
14 changes: 9 additions & 5 deletions plugins/nodejs.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
{
"$schema": "https://raw.githubusercontent.com/jetify-com/devbox/main/.schema/devbox-plugin.schema.json",
"version": "0.0.2",
"version": "0.0.3",
"name": "nodejs",
"readme": "Devbox automatically configures Corepack for Nodejs when DEVBOX_COREPACK_ENABLED=1. You can install Yarn or Pnpm by adding them to your `package.json` file using `packageManager`\nCorepack binaries will be installed in your local `.devbox` directory",
"readme": "Devbox automatically configures Corepack for Nodejs when DEVBOX_COREPACK_ENABLED=1. You can install Yarn or Pnpm by adding them to your `package.json` file using `packageManager`\nCorepack binaries will be installed in your local `.devbox` directory\n\nWhen Corepack is enabled, Devbox also activates the package manager pinned in your `package.json` `packageManager` field automatically. Set DEVBOX_DISABLE_NODEJS_PACKAGE_MANAGER_AUTODETECT=1 to disable this behavior.",
"env": {
"DEVBOX_COREPACK_BIN_DIR": "{{ .Virtenv }}/corepack-bin",
"PATH": "{{ .Virtenv }}/corepack-bin:$PATH"
},
"shell": {
"init_hook": [
"test -z $DEVBOX_COREPACK_ENABLED || corepack enable --install-directory \"{{ .Virtenv }}/corepack-bin/\"",
"test -z $DEVBOX_COREPACK_ENABLED || export PATH=\"{{ .Virtenv }}/corepack-bin/:$PATH\""
"node \"{{ .Virtenv }}/bin/setup-corepack.js\""
]
},
"create_files": {
"{{ .Virtenv }}/corepack-bin": ""
"{{ .Virtenv }}/corepack-bin": "",
"{{ .Virtenv }}/bin/setup-corepack.js": "nodejs/setup-corepack.js"
}
}
63 changes: 63 additions & 0 deletions plugins/nodejs/setup-corepack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Configures Corepack for the Devbox shell. This is the nodejs plugin's
// init_hook, invoked as: node setup-corepack.js
//
// It is a no-op unless DEVBOX_COREPACK_ENABLED is set, in which case it:
// 1. Enables Corepack, installing its package-manager shims into the
// directory given by DEVBOX_COREPACK_BIN_DIR (which the plugin also puts
// on PATH via its `env` block, so no PATH export is needed here).
// 2. Activates the package manager pinned in the project's package.json
// "packageManager" field (pnpm, yarn, npm, ...), unless
// DEVBOX_DISABLE_NODEJS_PACKAGE_MANAGER_AUTODETECT is set.

const { execFileSync } = require("node:child_process");
const path = require("node:path");

if (!process.env.DEVBOX_COREPACK_ENABLED) {
process.exit(0);
}

const corepackBinDir = process.env.DEVBOX_COREPACK_BIN_DIR;
if (!corepackBinDir) {
process.exit(0);
}

// Enable Corepack, installing the pnpm/yarn/npm shims into corepackBinDir.
run("corepack", ["enable", "--install-directory", corepackBinDir]);

// Activate the package manager pinned in package.json's "packageManager" field.
activatePinnedPackageManager();

function activatePinnedPackageManager() {
if (process.env.DEVBOX_DISABLE_NODEJS_PACKAGE_MANAGER_AUTODETECT) {
return;
}

const projectRoot = process.env.DEVBOX_PROJECT_ROOT;
if (!projectRoot) {
return;
}

let packageManager;
try {
({ packageManager } = require(path.join(projectRoot, "package.json")));
} catch {
// No package.json (or it is unreadable) — nothing to autodetect.
return;
}

if (!packageManager) {
return;
}

run("corepack", ["prepare", "--activate", packageManager]);
}

// Run a command, inheriting stdio so Corepack's output is visible. Failures
// must not block shell initialization.
function run(command, args) {
try {
execFileSync(command, args, { stdio: "inherit" });
} catch {
// Ignore: e.g. Corepack unavailable, or offline during activation.
}
}
30 changes: 30 additions & 0 deletions testscripts/plugin/nodejs_corepack_autodetect.test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Tests the nodejs plugin's packageManager autodetect via Corepack.
#
# When Corepack is enabled, the plugin's init_hook activates the package
# manager pinned in package.json's "packageManager" field. It must gracefully
# no-op (without breaking shell init) when package.json is missing or has no
# "packageManager" field.

env DEVBOX_COREPACK_ENABLED=1

# Pin to a Node version that still bundles Corepack (unbundled as of Node 25).
exec devbox init
exec devbox add nodejs@22

# Case 1: package.json does not exist.
# The autodetect hook must be skipped and shell init must still succeed.
! exists package.json
exec devbox run -- node -e 'console.log("case1-ok")'
stdout 'case1-ok'

# Case 2: package.json exists but has no "packageManager" field.
# The autodetect hook must no-op and shell init must still succeed.
cp package-no-pkgmgr.json package.json
exec devbox run -- node -e 'console.log("case2-ok")'
stdout 'case2-ok'

-- package-no-pkgmgr.json --
{
"name": "nodejs-corepack-autodetect",
"version": "1.0.0"
}
Loading