Save npm dev dependencies with --save-dev and keep them under NODE_ENV=production
Split out of #7054. This is the bug fix that turned out to be behind most of the "npm deploys are slow" number in that PR.
Problem
_install_frontend_packages adds dev dependencies with <pm> add -d. bun reads -d as --dev; npm reads it as --loglevel info. On npm projects every framework devDependency (vite, postcss, @react-router/dev, …) was therefore saved into dependencies. On the next run sync_root_package_json_to_web re-rendered them under devDependencies, saw the manifest as changed, invalidated the install cache, and the install step ran npm remove + npm install + 2× npm add on every single export, even with nothing changed.
Changes
- Pass
--save-devon npm (bun keeps-d). - Pass
--include=devon every npminstall/add/remove, soNODE_ENV=productioncannot prune the build tooling out ofnode_modules. - Tests for both package managers, for the restored-lockfile install, and for later add/remove operations under
NODE_ENV=production.
Measurement
Unchanged app, repeat reflex export, npm:
| main | this PR | |
|---|---|---|
| package-manager commands per export | 4 (remove, install, add, add) | 0 |
| compile phase (includes install) | 7.75 s | 0.47 s |
| total export | 15.2 s | 7.6 s (−50%) |
bun is unaffected (6.9 s on main, no package-manager commands on repeat exports either way).
Measurement setup
Five-page app from the blank template (Radix Themes + Tailwind v4 plugins), Linux x86_64, 4 vCPU, Python 3.14, Node 22.22, npm 10.9, bun 1.4.2. Each run is a fresh interpreter calling reflex.utils.export.export() the same way reflex export does; durations are the phase timings the export already reports to telemetry (compile includes the dependency install). Medians of 3 runs unless noted. Vite dominates every export (~6.5 s); these numbers isolate the part this PR touches.
Type of change
- Bug fix (non-breaking change which fixes an issue)
Checklist
- Tests added for the change; the full
tests/unitssuite passes locally except the 14reflex_bench/drivers/test_browser.pycases that need a Playwright browser launch, which fail identically on untouchedmainin this sandbox -
ruff check,ruff format --check,codespell, andpyright reflex testspass locally - News fragment added for every package touched
https://claude.ai/code/session_018nWB9r2HcC5tkT72UxWgGj
Generated by Claude Code