Repository navigation
Conversation
Drop Nitro and the Vite server bundle to warnings only during nuxt build, list failed prerender routes ourselves, and report the server build as one line with its time and size. --log-level verbose keeps the full output. Amp-Thread-ID: https://ampcode.com/threads/T-01a1ba1d-392b-7447-9cc3-9587ae9be465
commit: |
CLI benchmark
Full report
|
| Setting | Value |
|---|---|
| Baseline | ref:db93607c43b9f9a5a811ae4771730b1a97d7198f (v4.0.3) |
| Head | local packages/nuxt-cli at df76991 (v4.1.0-alpha.0) |
| Node | v24.22.2 |
| OS | Linux 7.0.0 (kernel 7.0.0-1017-azure) |
| CPU | AMD EPYC 9V74 80-Core Processor x 4 |
| Memory | 15.6 GB |
| Load average at start | 1.35, 0.43, 0.15 |
| Run started | 2026-11-10T11:49:38.117Z |
Cold CLI startup
Median of 15 interleaved runs per command, one warmup discarded.
| Command | baseline v4.0.3 median | head v4.1.0-alpha.0 median | Delta | baseline v4.0.3 min / p95 | head v4.1.0-alpha.0 min / p95 |
|---|---|---|---|---|---|
nuxt --version |
55 ms | 55 ms | -0.3% | 53 ms / 59 ms | 52 ms / 57 ms |
nuxt --version (first output byte) |
52 ms | 51 ms | -0.7% | 48 ms / 55 ms | 48 ms / 53 ms |
nuxt --help |
113 ms | 113 ms | +0.8% | 107 ms / 116 ms | 110 ms / 116 ms |
nuxt --help (first output byte) |
107 ms | 108 ms | +1.3% | 102 ms / 110 ms | 105 ms / 111 ms |
nuxt dev --help |
87 ms | 86 ms | -1.3% | 84 ms / 113 ms | 82 ms / 94 ms |
nuxt dev --help (first output byte) |
82 ms | 81 ms | -1.0% | 79 ms / 108 ms | 77 ms / 89 ms |
nuxt <unknown-command> (no-op) |
120 ms | 121 ms | +0.2% | 115 ms / 124 ms | 115 ms / 124 ms |
nuxt <unknown-command> (no-op) (first output byte) |
114 ms | 115 ms | +0.6% | 109 ms / 118 ms | 109 ms / 118 ms |
Module load cost
Counted with a module.registerHooks load hook, compile cache disabled. Counts every JS module actually evaluated on that code path (native addons excluded). Built-ins loaded after bootstrap are counted separately, including the internal modules they load.
| Command | baseline v4.0.3 modules | head v4.1.0-alpha.0 modules | Delta | baseline v4.0.3 source bytes | head v4.1.0-alpha.0 source bytes | Delta | baseline v4.0.3 built-ins | head v4.1.0-alpha.0 built-ins | Delta |
|---|---|---|---|---|---|---|---|---|---|
nuxt --version |
35 | 35 | 0.0% | 301.2 kB | 301.2 kB | +0.0% | 29 | 29 | 0.0% |
nuxt --help |
136 | 136 | 0.0% | 861.0 kB | 872.6 kB | +1.3% | 93 | 93 | 0.0% |
nuxt dev --help |
64 | 65 | +1.6% | 460.8 kB | 463.9 kB | +0.7% | 90 | 90 | 0.0% |
Install footprint and published tarball
Each version installed on its own into an empty project with nothing but @nuxt/cli as a dependency, so the tree is exactly the CLI and its transitive dependencies. npm cache is warm and the registry is only consulted for metadata, so install wall time is indicative, not a network benchmark.
| Metric | baseline v4.0.3 | head v4.1.0-alpha.0 | Delta |
|---|---|---|---|
Direct dependencies of @nuxt/cli |
23 | 23 | 0.0% |
| Packages in the installed tree (unique name@version) | 38 | 38 | 0.0% |
| Unique package names | 38 | 38 | 0.0% |
| Package directories on disk (cross-check) | 32 | 32 | 0.0% |
Installed node_modules on disk |
2.48 MB | 2.56 MB | +3.1% |
| Installed files | 439 | 442 | +0.7% |
| Install wall time (warm npm cache, median of 3) | 1.09 s | 1.08 s | -0.7% |
| Published tarball (packed) | 245.3 kB | 249.3 kB | +1.6% |
| Published tarball (unpacked) | 793.7 kB | 806.3 kB | +1.6% |
| Files in tarball | 104 | 104 | 0.0% |
Interleaved runs on a shared runner: trust the deltas, not the absolute timings. The dev, restart and build suites run locally via pnpm bench:cli.
Merging this PR will not alter performance1 warning
Comparing |
Server builders such as @nuxt/vite-server switch on the environment API during setup, so the client/server split can no longer be decided when the CLI attaches. A small Vite plugin now reports each environment as it starts building, which also splits the environment API build into app (client) and app (server). Amp-Thread-ID: https://ampcode.com/threads/T-01a1ba1d-392b-7447-9cc3-9587ae9be465
Nitro 3 and @nuxt/vite-server build the server as a Vite environment next to the app. A Vite plugin now mutes Vite's info output while a server environment builds, times the server build from its environment, and reports the vite-server output as Server (vite). The banner leaves Nitro out when it is not the server builder, and the preview hint points at nuxt preview, which works for every builder. Amp-Thread-ID: https://ampcode.com/threads/T-01a1ba1d-392b-7447-9cc3-9587ae9be465
…) and bundle (nitro) app (client) and app (server) read as part of the app phase that generates templates. The three phases now say what they bundle, which also matches the bundle phase nuxt dev already reports. Amp-Thread-ID: https://ampcode.com/threads/T-01a1ba1d-392b-7447-9cc3-9587ae9be465
The progress line showed the phase's time and the command's next to each other with nothing to tell them apart. The second now reads 'total', as in 'Bundling nitro 2.44s · total 4.98s'. Amp-Thread-ID: https://ampcode.com/threads/T-01a1ba1d-392b-7447-9cc3-9587ae9be465
nuxt buildprints the server side twice over. Vite lists every chunk of the server bundle, then Nitro prints its success lines, one line per prerendered route and a file-by-file tree of.output/server. The phase spinner (the same onenuxt devuses) is already running the whole time, so none of that tells you anything new. On the Nitro fixture below, a piped log goes from 48 lines to 8 betweenBuilding server...and the timing breakdown, phase lines included.Videos
Real
nuxt buildruns in a 110-column terminal. Before is the CLI built frommain(db93607); after is this branch. Same fixture each time, with a clean.outputand.nuxtbefore every run.Nitro (Nuxt 4.6.4, Nitro 2.13.6: 2 pages, 1 API route,
/aboutprerendered)@nuxt/vite-server(Nuxt 4.6.4,server: { builder: '@nuxt/vite-server' })The client file lists still print in every case. That part is #1663, which is stacked on this PR.
What changed
Bundling client,Bundling serverandBundling nitro, shown asbundle (client),bundle (server)andbundle (nitro)in the timing breakdown:config 214ms · modules 161ms · app 460ms · types 476ms · bundle (client) 1.13s · bundle (server) 262ms · bundle (nitro) 3.28s.appstays the step that generates the app's templates, andnuxt devalready calls its bundling stepbundle. The videos below were recorded before this rename.@nuxt/vite-server(which switches the API on during setup) and with Nuxt 5 / Nitro 3 (where the server is anitroVite environment built after the app).logLevel: 1vianitro:config). That hides Nitro's success lines, the per-route prerender list and the file tree, which Nitro only prints whenlogLevel > 1.logLevel: 'warn'. A shared config gets its Vite logger's info messages muted while a server-side environment builds.Prerendered 2 routes in 1.70swhen anything was prerendered, thenServer (nitro) built in 2.91s · 2.32 MB(orServer (vite) …for vite-server), thenPreview: npx nuxt preview, plus Nitro's deploy command when the preset has one. The size is the server output directory on disk, without source maps (vite-server writes them as*.map.json).Prerendering /blog (12 done).Bundling nitro 2.44s · total 4.98sinstead of2.44s · 4.98s. The same reporter drivesnuxt dev, so its line reads the same way.--log-level verbose, or when the project setsnitro.logLevel,vite.logLevelorvite.$server.logLevel.--log-level silentstays silent.Checks
Server (nitro) built in 2.91s · 2.32 MB, breakdownbundle (client) 878ms · bundle (server) 257ms · bundle (nitro) 4.66s(recorded under the earlier names).playground-nightly):Server (nitro) built in 216ms · 649.21 kB, breakdownbundle (client) 525ms · bundle (server) 413ms · bundle (nitro) 220ms(recorded under the earlier names). Before this branch's latest commit, that printed 21 SSR chunk lines and the fullnitroenvironment chunk list, and credited the whole Vite build to the Nitro phase (27msfor the client and1.33sfor Nitro).@nuxt/vite-server:Server (vite) built in 403ms · 855.65 kB, which matches the 856 kB of server chunks Vite reports with--log-level verbose.--prerender(static): onlyPrerendered 8 routes in 1.84sprints, then the existing "deploy.output/public" outro.--log-level verboseprints the full Nitro tree, as onmain.tsc --noEmit,eslintandknippass. The unit tests cover the new behaviour: muting server environments in a shared Vite config, the vite-server summary, the phase split with one shared config. The whole unit suite passes once the sandbox'sTERM=dumb/NO_COLOR=1are unset; with them, 4 dev-TUI spec files fail the same way onmain.Details and open questions
Banner: leaving Nitro out of the startup banner and the dev info view when another server builder is configured is split out into fix(build,dev,info): name the server builder instead of assuming Nitro #1671.
Vite's native progress lines: with a shared config (environment API, vite-server, Nitro 3), each server environment still prints
transforming.../✓ N modules transformed./rendering chunks.... Vite 8's reporter writes these from Rust straight to stdout, so they can only be turned off with Vite's single global log level, which would also hide the client output. Fixing that needs a per-environment log level in Vite.Still noisy, upstream in Nuxt:
ℹ Building server.../✔ Server built in 276mscome from@nuxt/vite-builderand repeat the phase line. Also, the vite-builder'shideOutputfilter for the server bundle never matches in Nuxt 4: it compares messages againstrelative(config.root, outDir), androotisapp/, so it gives../.nuxt/dist/server. On vite-server,✔ Server output written to .outputrepeats the summary.Gzip size: the summary shows raw size only. Nitro gzips every server file for its total; I skipped that to keep the build fast.
Timing line width: the longer phase names wrap the breakdown on a 110-column terminal when every phase is present (see the Nitro video).
Pre-existing, unrelated: Nitro's second copy of a Rollup warning goes to stderr, which the spinner's output tap doesn't watch, so in a TTY it's written on the end of the spinner line. This also happens on
main.ELI5
When you build a Nuxt app for production, it used to print a long list of every server file it made. Now it says the server was built, how long it took and how big it is, and still shows warnings and errors. This works whether Nitro or Vite builds the server. Add
--log-level verboseto get the full list back.