Description
When a package with build scripts has been previously built and its side-effects are cached in the store, subsequent installs see isBuilt: true from the side-effects cache. This causes buildModules to skip the package entirely (via the !node.isBuilt filter at building/during-install/src/index.ts:77), which means:
- The
allowBuild check is never reached
- The package is not added to
ignoredBuilds
strictDepBuilds does not fail
- No warning about skipped builds is shown
This affects both GVS (Global Virtual Store) mode and regular mode — any store that has cached side-effects from a prior build will exhibit this behavior.
Steps to reproduce
- Install a package with build scripts (e.g.
esbuild) with allowBuilds: { esbuild: true } — this builds it and caches side-effects in the store
- Remove the
allowBuilds entry (or set a placeholder)
- Run
pnpm install — esbuild's build is silently reused from the store cache because isBuilt: true, bypassing the allowBuild check entirely
Expected behavior
Even if a package's side-effects are cached in the store, strictDepBuilds should still fail and the ignored builds warning should still appear when the package is not approved in allowBuilds.
Investigation notes
The fix is not straightforward because it spans multiple code paths:
building/during-install (buildModules): The !node.isBuilt filter removes cached packages before the allowBuild check. Adding a pre-loop check helps when the dep graph is fully populated, but the graph can be sparse on repeat installs.
installing/deps-restorer (headless install): The lockfileToDepGraph function excludes unchanged deps when includeUnchangedDeps is false. Removing the GVS guard on the allowBuilds comparison (line 349) helps trigger full graph population when allowBuilds changes, but this only works for the headless path.
installing/deps-installer (_installInContext): The entire build/ignoredBuilds block is inside if (result.newDepPaths?.length), so it's completely skipped on repeat installs with no changes.
A comprehensive fix likely needs to check allowBuild against all lockfile packages with build scripts after the install completes, independent of the dep graph's isBuilt state.
Description
When a package with build scripts has been previously built and its side-effects are cached in the store, subsequent installs see
isBuilt: truefrom the side-effects cache. This causesbuildModulesto skip the package entirely (via the!node.isBuiltfilter atbuilding/during-install/src/index.ts:77), which means:allowBuildcheck is never reachedignoredBuildsstrictDepBuildsdoes not failThis affects both GVS (Global Virtual Store) mode and regular mode — any store that has cached side-effects from a prior build will exhibit this behavior.
Steps to reproduce
esbuild) withallowBuilds: { esbuild: true }— this builds it and caches side-effects in the storeallowBuildsentry (or set a placeholder)pnpm install— esbuild's build is silently reused from the store cache becauseisBuilt: true, bypassing theallowBuildcheck entirelyExpected behavior
Even if a package's side-effects are cached in the store,
strictDepBuildsshould still fail and the ignored builds warning should still appear when the package is not approved inallowBuilds.Investigation notes
The fix is not straightforward because it spans multiple code paths:
building/during-install(buildModules): The!node.isBuiltfilter removes cached packages before theallowBuildcheck. Adding a pre-loop check helps when the dep graph is fully populated, but the graph can be sparse on repeat installs.installing/deps-restorer(headless install): ThelockfileToDepGraphfunction excludes unchanged deps whenincludeUnchangedDepsis false. Removing the GVS guard on theallowBuildscomparison (line 349) helps trigger full graph population whenallowBuildschanges, but this only works for the headless path.installing/deps-installer(_installInContext): The entire build/ignoredBuilds block is insideif (result.newDepPaths?.length), so it's completely skipped on repeat installs with no changes.A comprehensive fix likely needs to check
allowBuildagainst all lockfile packages with build scripts after the install completes, independent of the dep graph'sisBuiltstate.