Summary
When invoked with no package argument, npm install in almostnode resolves and installs only dependencies from package.json. The devDependencies are silently skipped — there is no warning, no flag, no --production involved.
Real npm installs both dependencies and devDependencies by default; only npm install --production (or NODE_ENV=production) drops devDeps.
Repro
import { createContainer } from "almostnode";
const c = createContainer();
c.vfs.mkdirSync("/work", { recursive: true });
c.vfs.writeFileSync("/work/package.json", JSON.stringify({
name: "test",
version: "0.0.0",
dependencies: { "react": "^18.3.1" },
devDependencies: { "vite": "^5.4.8" },
}, null, 2));
const r = await c.run("npm install", { cwd: "/work" });
console.log(r.stdout);
// Resolving react@^18.3.1 ... Installed 1 packages
// (vite is NOT downloaded)
console.log(c.vfs.existsSync("/work/node_modules/react")); // true
console.log(c.vfs.existsSync("/work/node_modules/vite")); // false ← bug
Why this matters
Modern JS projects use devDependencies for build/test tooling (vite, webpack, tsc, vitest, etc.). When an agent (or human) follows the standard pattern — write package.json with framework runtime in dependencies and the build tool in devDependencies, then npm install — the project compiles but the dev/build command (node ./node_modules/vite/bin/vite.js, npm run dev, etc.) fails with Cannot find module. The user/agent then loops trying to figure out why.
This compounds badly with #16 (the util.styleText issue): when npm create vite fails for that reason, the human workaround is to write package.json by hand. That workaround relies on npm install actually installing devDeps. With this bug, it doesn't.
Suspected cause
Looking at src/npm/index.ts installFromPackageJson:
async installFromPackageJson(options: InstallOptions = {}): Promise<InstallResult> {
...
// Resolve all dependencies
const resolved = await resolveDependencies(name, version, {
registry: this.registry,
includeDev: options.includeDev, // ← undefined by default
includeOptional: options.includeOptional,
onProgress,
});
...
}
And the npm shell command (src/shims/child_process.ts, the handleNpmInstall path) presumably calls installFromPackageJson({}) with no options, so includeDev defaults to undefined/false.
Suggested fix
In src/shims/child_process.ts, the npm install (no-args) path should set includeDev: true to match real npm's default behavior. Optionally also add --production / --omit=dev flag parsing to match npm's actual contract (NODE_ENV=production → omit dev; --omit=dev → omit dev).
// before:
return packageManager.installFromPackageJson({ onProgress });
// after:
const isProd = process.env.NODE_ENV === 'production' || hasFlag('--omit=dev') || hasFlag('--production');
return packageManager.installFromPackageJson({
onProgress,
includeDev: !isProd,
});
Environment
- almostnode
0.2.14
- mode:
createContainer() (trusted main-thread)
Related
Summary
When invoked with no package argument,
npm installin almostnode resolves and installs onlydependenciesfrompackage.json. ThedevDependenciesare silently skipped — there is no warning, no flag, no--productioninvolved.Real
npminstalls bothdependenciesanddevDependenciesby default; onlynpm install --production(orNODE_ENV=production) drops devDeps.Repro
Why this matters
Modern JS projects use
devDependenciesfor build/test tooling (vite,webpack,tsc,vitest, etc.). When an agent (or human) follows the standard pattern — writepackage.jsonwith framework runtime independenciesand the build tool indevDependencies, thennpm install— the project compiles but the dev/build command (node ./node_modules/vite/bin/vite.js,npm run dev, etc.) fails withCannot find module. The user/agent then loops trying to figure out why.This compounds badly with #16 (the
util.styleTextissue): whennpm create vitefails for that reason, the human workaround is to writepackage.jsonby hand. That workaround relies onnpm installactually installing devDeps. With this bug, it doesn't.Suspected cause
Looking at
src/npm/index.tsinstallFromPackageJson:And the npm shell command (
src/shims/child_process.ts, thehandleNpmInstallpath) presumably callsinstallFromPackageJson({})with no options, soincludeDevdefaults to undefined/false.Suggested fix
In
src/shims/child_process.ts, thenpm install(no-args) path should setincludeDev: trueto match real npm's default behavior. Optionally also add--production/--omit=devflag parsing to match npm's actual contract (NODE_ENV=production→ omit dev;--omit=dev→ omit dev).Environment
0.2.14createContainer()(trusted main-thread)Related
styleText— breakscreate-vite@9and other Node 21+ scaffolders #16 (util.styleTextmissing) — without that fix users fall back to manualpackage.jsonauthoring, which is where this devDeps bug bites.