You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
npm v12 is out (12.0.2, already latest on the registry) and changes several npm install defaults that break our repos. npm has asked for it to be backported into the active LTS lines, Node 24 and Node 26 (nodejs/Release#1161), so pinning .nvmrc to Node 24 only protects us until that lands. Node 24.20.0 and 26.8.1 still bundle npm 11.19.0 today.
This issue tracks making the MDN repos forward-compatible with npm 12 while we stay on npm 11. Most of the work can land now: npm 11.16+ writes and understands allowScripts, but treats it as advisory.
All claims below were verified by running npm@12.0.2 against the real manifests and lockfiles.
1. Dependency install scripts are blocked by default
npm 12 won't run preinstall/install/postinstall from dependencies unless the root package.json has an allowScripts allowlist. Root-package scripts still run.
npm ci still succeeds — the binaries just never get fetched, so this surfaces later as confusing errors.
Packages that ship platform optionalDependencies (esbuild, unrs-resolver, @parcel/watcher, @swc/core) are fine. lefthook is degraded (binary resolves, git hooks not registered). The hard breaks have no fallback:
@mdn/rari is the most consequential: its postinstall is the only thing that downloads bin/rari, so every consumer breaks.
Fix: on npm >= 11.16, run npm approve-scripts --all --no-allow-scripts-pin and commit the resulting package.json. We want name-only entries, not version pins — pinned entries silently stop matching after a Dependabot bump, which would mean a re-approval commit on every dependency update. Note npm approve-scripts is workspace-unaware.
npx/npm exec have no project package.json to read an allowlist from, so CI calls like npx @mdn/rari build … and npx --package=@mdn/fred fred-ssr need --allow-scripts.
Note
Longer term, the packages we publish should stop relying on install scripts entirely (shipping per-platform optionalDependencies instead), which would remove the need for any consumer to allowlist us. That's a significant piece of work and should be tracked separately.
2. engines.npm: "^11" + engine-strict=true is a hard failure
browser-compat-data is the only repo with an engines.npm constraint. Under npm 12:
Downstream consumers are unaffected, though: the published package is built from a generated build/package.json that carries only name and version, so engines never reaches the registry.
Everything else uses node-version-file: .nvmrc, which keeps our exposure small.
4. npm adduser was removed — and we document it
Referenced in content (files/en-us/webassembly/guides/rust_to_wasm/index.md, line 397, incl. a link to the npm-adduser docs page) and the ja/ko/ru/zh-cn/de translations. Should become npm login.
5. packageManager pins
15 manifests pin packageManager: "npm@11.x". Nothing enforces them today (no CI enables Corepack), but they need to move with the npm bump.
Verified as not a problem
Listed so nobody re-investigates: allow-git/allow-remote defaulting to none (no lockfile in the org has a git or remote-tarball resolution; tree-sitter-mdn's npm install file:$TARBALL is unaffected since allow-file keeps its default) · unknown configs/abbreviated flags now throwing (every config in our committed .npmrc files and every CI flag we use is still valid) · npm pkg no longer forcing JSON (fred's publish simulation passes --json explicitly) · npm view --json returning an array (our one npm view call doesn't pass --json) · npm shrinkwrap removed (no npm-shrinkwrap.json anywhere) · npm star/stars/unstar removed (unused) · root preinstall now running first (no repo has one) · npm init license default (no impact).
Tasks
browser-compat-data: widen or drop engines.npm: "^11"
webextensions-examples: replace node-version: "latest" with node-version-file
mdn-http-observatory: bump the pinned global npm, and confirm the Node 26 matrix leg passes on npm 12
content + translated-content*: replace npm adduser with npm login
Add a single scheduled matrix job in mdn/workflows that runs npm ci under npm@latest against the affected repos, so regressions surface early; remove it once we're on npm 12
Bump .nvmrc, engines.node, and packageManager everywhere when Node 26 goes LTS (October 2026)
Pull requests
All opened as drafts. Verified against npm@12.0.2 with npm ci --strict-allow-scripts.
allowScripts allowlists (--no-allow-scripts-pin, so entries are name-only):
No --allow-scripts flags are needed for our CI npx/npm exec calls. Every one of them resolves the package from a local node_modules (the @mdn/rari and @mdn/fred calls all run with working-directory: mdn/content, where both are dependencies), so the root allowScripts applies. Confirmed under npm 12 for both a direct and a transitive local dependency; it only fails when there is no local tree.
dex needs three allowlists, not one.prepare runs npm run install:all, which installs deployer-js and cloud-function as separate projects that do not read the root package.json.
Summary
npm v12 is out (
12.0.2, alreadylateston the registry) and changes severalnpm installdefaults that break our repos. npm has asked for it to be backported into the active LTS lines, Node 24 and Node 26 (nodejs/Release#1161), so pinning.nvmrcto Node 24 only protects us until that lands. Node 24.20.0 and 26.8.1 still bundle npm 11.19.0 today.This issue tracks making the MDN repos forward-compatible with npm 12 while we stay on npm 11. Most of the work can land now: npm 11.16+ writes and understands
allowScripts, but treats it as advisory.Refs: GitHub changelog · npm/cli v12.0.0
All claims below were verified by running
npm@12.0.2against the real manifests and lockfiles.1. Dependency install scripts are blocked by default
npm 12 won't run
preinstall/install/postinstallfrom dependencies unless the rootpackage.jsonhas anallowScriptsallowlist. Root-package scripts still run.npm cistill succeeds — the binaries just never get fetched, so this surfaces later as confusing errors.Packages that ship platform
optionalDependencies(esbuild,unrs-resolver,@parcel/watcher,@swc/core) are fine.lefthookis degraded (binary resolves, git hooks not registered). The hard breaks have no fallback:fred@mdn/rari(x2),gifsicle,mozjpeg,pngquant-bin,edgedriver,geckodrivermdn-http-observatorypostinstall,libpq,node_extra_ca_certs_mozilla_bundlecontent@mdn/rari,gifsicle,mozjpeg,pngquant-binmcp@mdn/raridex@mdn/raritree-sitter-mdninstall: node-gyp-build,tree-sitter-cliexpress-locallibrary-tutorialmongodb-memory-serverbrowser-compat-data,bcd-utils,curriculum,triagebot,translated-content{,-de,-it},todo-vue,client-toolchain-example@mdn/rariis the most consequential: itspostinstallis the only thing that downloadsbin/rari, so every consumer breaks.Fix: on npm >= 11.16, run
npm approve-scripts --all --no-allow-scripts-pinand commit the resultingpackage.json. We want name-only entries, not version pins — pinned entries silently stop matching after a Dependabot bump, which would mean a re-approval commit on every dependency update. Notenpm approve-scriptsis workspace-unaware.npx/npm exechave no projectpackage.jsonto read an allowlist from, so CI calls likenpx @mdn/rari build …andnpx --package=@mdn/fred fred-ssrneed--allow-scripts.Note
Longer term, the packages we publish should stop relying on install scripts entirely (shipping per-platform
optionalDependenciesinstead), which would remove the need for any consumer to allowlist us. That's a significant piece of work and should be tracked separately.2.
engines.npm: "^11"+engine-strict=trueis a hard failurebrowser-compat-datais the only repo with anengines.npmconstraint. Under npm 12:Downstream consumers are unaffected, though: the published package is built from a generated
build/package.jsonthat carries onlynameandversion, soenginesnever reaches the registry.3. Unpinned npm/Node in CI
mdn-http-observatory—npm-publish.ymlpinsnpm install -g npm@^11.8.0; needs a conscious bump.mdn-http-observatory—test.ymlhas anode-version: [24, 26]matrix; the 26 leg flips to npm 12 once the backport lands.webextensions-examples—lint.ymlusesnode-version: "latest"(twice).Everything else uses
node-version-file: .nvmrc, which keeps our exposure small.4.
npm adduserwas removed — and we document itReferenced in
content(files/en-us/webassembly/guides/rust_to_wasm/index.md, line 397, incl. a link to thenpm-adduserdocs page) and theja/ko/ru/zh-cn/detranslations. Should becomenpm login.5.
packageManagerpins15 manifests pin
packageManager: "npm@11.x". Nothing enforces them today (no CI enables Corepack), but they need to move with the npm bump.Verified as not a problem
Listed so nobody re-investigates:
allow-git/allow-remotedefaulting tonone(no lockfile in the org has a git or remote-tarball resolution;tree-sitter-mdn'snpm install file:$TARBALLis unaffected sinceallow-filekeeps its default) · unknown configs/abbreviated flags now throwing (every config in our committed.npmrcfiles and every CI flag we use is still valid) ·npm pkgno longer forcing JSON (fred's publish simulation passes--jsonexplicitly) ·npm view --jsonreturning an array (our onenpm viewcall doesn't pass--json) ·npm shrinkwrapremoved (nonpm-shrinkwrap.jsonanywhere) ·npm star/stars/unstarremoved (unused) · rootpreinstallnow running first (no repo has one) ·npm initlicense default (no impact).Tasks
browser-compat-data: widen or dropengines.npm: "^11"allowScripts(--no-allow-scripts-pin):fred,content,mcp,dex,mdn-http-observatory,browser-compat-data,bcd-utils,curriculum,triagebot,translated-content{,-de,-it},tree-sitter-mdn,express-locallibrary-tutorial,client-toolchain-example,todo-vue--allow-scriptsto CInpx/npm execcallswebextensions-examples: replacenode-version: "latest"withnode-version-filemdn-http-observatory: bump the pinned global npm, and confirm the Node 26 matrix leg passes on npm 12content+translated-content*: replacenpm adduserwithnpm loginmdn/workflowsthat runsnpm ciundernpm@latestagainst the affected repos, so regressions surface early; remove it once we're on npm 12.nvmrc,engines.node, andpackageManagereverywhere when Node 26 goes LTS (October 2026)Pull requests
All opened as drafts. Verified against
npm@12.0.2withnpm ci --strict-allow-scripts.allowScriptsallowlists (--no-allow-scripts-pin, so entries are name-only):engines.npm)test-clitarball install).nvmrcinstead oflatestwebextensions-examples#651 replacesnode-version: "latest"with a new.nvmrcnpm adduser->npm login:npm adduserwithnpm logincontent#45568npm adduserwithnpm logintranslated-content#38369 (ja,ko,ru,zh-cn)npm adduserwithnpm logintranslated-content-de#289npm ci --strict-allow-scriptsmatrix undernpm@latestTwo notes on the task list:
--allow-scriptsflags are needed for our CInpx/npm execcalls. Every one of them resolves the package from a localnode_modules(the@mdn/rariand@mdn/fredcalls all run withworking-directory: mdn/content, where both are dependencies), so the rootallowScriptsapplies. Confirmed under npm 12 for both a direct and a transitive local dependency; it only fails when there is no local tree.dexneeds three allowlists, not one.preparerunsnpm run install:all, which installsdeployer-jsandcloud-functionas separate projects that do not read the rootpackage.json.