diff --git a/plugins/nodejs.json b/plugins/nodejs.json index 3dc93c129e4..f56ef938210 100644 --- a/plugins/nodejs.json +++ b/plugins/nodejs.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/main/.schema/devbox-plugin.schema.json", - "version": "0.0.3", + "version": "0.0.4", "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\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": { @@ -9,11 +9,11 @@ }, "shell": { "init_hook": [ - "node \"{{ .Virtenv }}/bin/setup-corepack.js\"" + "node \"{{ .Virtenv }}/bin/setup-corepack.mjs\"" ] }, "create_files": { "{{ .Virtenv }}/corepack-bin": "", - "{{ .Virtenv }}/bin/setup-corepack.js": "nodejs/setup-corepack.js" + "{{ .Virtenv }}/bin/setup-corepack.mjs": "nodejs/setup-corepack.mjs" } } diff --git a/plugins/nodejs/setup-corepack.js b/plugins/nodejs/setup-corepack.mjs similarity index 67% rename from plugins/nodejs/setup-corepack.js rename to plugins/nodejs/setup-corepack.mjs index f2471067895..1ca1dd175cc 100644 --- a/plugins/nodejs/setup-corepack.js +++ b/plugins/nodejs/setup-corepack.mjs @@ -1,5 +1,10 @@ // Configures Corepack for the Devbox shell. This is the nodejs plugin's -// init_hook, invoked as: node setup-corepack.js +// init_hook, invoked as: node setup-corepack.mjs +// +// The .mjs extension forces Node to treat this as an ES module regardless of +// the project's package.json "type" field. A plain .js file would be parsed as +// CommonJS or ESM depending on that field, so it would break in one case or the +// other (see issue #2856). // // 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 @@ -9,8 +14,9 @@ // "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"); +import { execFileSync } from "node:child_process"; +import { readFileSync } from "node:fs"; +import path from "node:path"; if (!process.env.DEVBOX_COREPACK_ENABLED) { process.exit(0); @@ -37,11 +43,17 @@ function activatePinnedPackageManager() { return; } + // Read package.json directly rather than importing it: JSON module import + // syntax differs across Node versions, whereas readFileSync + JSON.parse + // works everywhere. let packageManager; try { - ({ packageManager } = require(path.join(projectRoot, "package.json"))); + const pkg = JSON.parse( + readFileSync(path.join(projectRoot, "package.json"), "utf8"), + ); + ({ packageManager } = pkg); } catch { - // No package.json (or it is unreadable) — nothing to autodetect. + // No package.json (or it is unreadable/invalid) — nothing to autodetect. return; } diff --git a/testscripts/plugin/nodejs_corepack_autodetect.test.txt b/testscripts/plugin/nodejs_corepack_autodetect.test.txt index ed10396b30b..81e05655b9a 100644 --- a/testscripts/plugin/nodejs_corepack_autodetect.test.txt +++ b/testscripts/plugin/nodejs_corepack_autodetect.test.txt @@ -23,8 +23,21 @@ cp package-no-pkgmgr.json package.json exec devbox run -- node -e 'console.log("case2-ok")' stdout 'case2-ok' +# Case 3: package.json sets "type": "module" (regression test for #2856). +# The setup script must still run; its .mjs extension keeps it an ES module +# regardless of the project package.json's "type" field. +cp package-esm.json package.json +exec devbox run -- node -e 'console.log("case3-ok")' +stdout 'case3-ok' + -- package-no-pkgmgr.json -- { "name": "nodejs-corepack-autodetect", "version": "1.0.0" } +-- package-esm.json -- +{ + "name": "nodejs-corepack-autodetect", + "version": "1.0.0", + "type": "module" +}