Skip to content

perf: render root help without loading every command - #1591

Open
productdevbook wants to merge 1 commit into
nuxt:mainfrom
productdevbook:perf/help-without-commands
Open

productdevbook wants to merge 1 commit into
nuxt:mainfrom
productdevbook:perf/help-without-commands

Conversation

@productdevbook

Copy link
Copy Markdown
Member

nuxt --help and bare nuxt evaluated all 21 command modules (dev, build, upgrade, typecheck, …) only to print one description line for each. The modules were loaded in two places:

  1. citty's _findSubCommand resolves every subcommand to look for an alias, even when no command name was given (undefined in subCommands is false, so it falls through to the scan).
  2. renderUsage resolves every subcommand again to read meta.description / meta.hidden.

Changes

  • commands/meta.ts: the commands' meta objects move here, and each command imports its own entry (meta: commandMeta.build), so there is still a single source for each description. The table is typed satisfies Record<keyof typeof commands, CommandMeta>, so adding a command without a meta is a type error.
  • run.ts: showUsage renders the root command with each subcommand reduced to { meta } from that table. Subcommand help (nuxt dev --help, nuxt module --help) is unchanged.
  • citty patch: _findSubCommand returns early when name is undefined. The lockfile change is only the new patch hash.

The output of nuxt --help, nuxt and nuxt foo is byte-identical before and after (NO_COLOR=1, diffed).

Benchmark

pnpm bench:cli --baseline ref:main --fixture playground --suite startup --suite modules --startup-reps 21, run on an M3 Max with Node 24.21 and interleaved runs. The baseline is main at f140924.

nuxt --help main this PR Delta
median 81 ms 50 ms -38.3%
modules evaluated 134 35 -73.9%
source bytes 839.9 kB 300.2 kB -64.3%
built-ins 87 27 -69.0%

--version, dev --help and unknown commands load the same modules as before.

Checks

  • eslint packages/nuxt-cli/src and tsc --noEmit pass
  • vitest run packages/nuxt-cli/test/unit plus e2e commands, unknown-command, hidden-commands and unknown-flags: 136 files, 1893 passed, 10 todo. This includes the inline help snapshots in help.spec.ts
  • test:dist, generate-command-docs --check and pnpm i --frozen-lockfile pass

🤖 Generated with Claude Code

`nuxt --help` (and bare `nuxt`) evaluated all 21 command modules: citty's
`_findSubCommand` resolved each one looking for an alias even when no
command was given, and `renderUsage` resolved each one again to read its
meta.

Command metas now live in `commands/meta.ts`, which the commands import,
so the root usage is rendered from that table instead. The citty patch
returns early from `_findSubCommand` when there is no name to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pkg-pr-new

pkg-pr-new Bot commented Oct 3, 2026

Copy link
Copy Markdown
  • nuxt-cli-playground

    npm i https://pkg.pr.new/create-nuxt@1591
    
    npm i https://pkg.pr.new/nuxi@1591
    
    npm i https://pkg.pr.new/@nuxt/cli@1591
    

commit: dc966ae

@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

CLI benchmark

@nuxt/cli v4.0.0-alpha.1 (baseline) vs v4.0.0-alpha.1 (this PR)

Metric baseline v4.0.0-alpha.1 head v4.0.0-alpha.1 Delta
nuxt --version wall time (median) 66 ms 65 ms -1.9%
nuxt --help wall time (median) 139 ms 65 ms -52.7%
nuxt dev --help wall time (median) 103 ms 103 ms -0.5%
nuxt --version modules loaded 35 35 0.0%
nuxt --version built-ins loaded 27 27 0.0%
nuxt --help modules loaded 134 35 -73.9%
nuxt --help built-ins loaded 87 27 -69.0%
nuxt dev --help modules loaded 63 63 0.0%
nuxt dev --help built-ins loaded 87 87 0.0%
Installed node_modules 2.43 MB 2.43 MB +0.1%
Published tarball (packed) 239.6 kB 240.5 kB +0.4%
Full report

@nuxt/cli v4.0.0-alpha.1 (baseline) vs v4.0.0-alpha.1 (head)

Setting Value
Baseline ref:f140924fb88992febb831316ac01bce275609336 (v4.0.0-alpha.1)
Head local packages/nuxt-cli at 1c7c2c0 (v4.0.0-alpha.1)
Node v24.21.0
OS Linux 6.17.0 (kernel 6.17.0-1022-azure)
CPU AMD EPYC 7763 64-Core Processor x 4
Memory 15.6 GB
Load average at start 1.48, 0.46, 0.16
Run started 2026-10-03T16:59:23.741Z

Cold CLI startup

Median of 15 interleaved runs per command, one warmup discarded.

Command baseline v4.0.0-alpha.1 median head v4.0.0-alpha.1 median Delta baseline v4.0.0-alpha.1 min / p95 head v4.0.0-alpha.1 min / p95
nuxt --version 66 ms 65 ms -1.9% 64 ms / 68 ms 63 ms / 67 ms
nuxt --version (first output byte) 62 ms 61 ms -1.9% 60 ms / 63 ms 59 ms / 63 ms
nuxt --help 139 ms 65 ms -52.7% 134 ms / 142 ms 63 ms / 69 ms
nuxt --help (first output byte) 133 ms 62 ms -53.5% 129 ms / 137 ms 59 ms / 65 ms
nuxt dev --help 103 ms 103 ms -0.5% 101 ms / 106 ms 99 ms / 108 ms
nuxt dev --help (first output byte) 98 ms 97 ms -1.1% 96 ms / 101 ms 95 ms / 103 ms
nuxt &lt;unknown-command> (no-op) 148 ms 149 ms +0.3% 144 ms / 151 ms 144 ms / 153 ms
nuxt &lt;unknown-command> (no-op) (first output byte) 142 ms 143 ms +0.4% 138 ms / 145 ms 139 ms / 147 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.0-alpha.1 modules head v4.0.0-alpha.1 modules Delta baseline v4.0.0-alpha.1 source bytes head v4.0.0-alpha.1 source bytes Delta baseline v4.0.0-alpha.1 built-ins head v4.0.0-alpha.1 built-ins Delta
nuxt --version 35 35 0.0% 297.8 kB 300.2 kB +0.8% 27 27 0.0%
nuxt --help 134 35 -73.9% 839.9 kB 300.2 kB -64.3% 87 27 -69.0%
nuxt dev --help 63 63 0.0% 453.0 kB 455.3 kB +0.5% 87 87 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.0-alpha.1 head v4.0.0-alpha.1 Delta
Direct dependencies of @nuxt/cli 23 23 0.0%
Packages in the installed tree (unique name@version) 39 39 0.0%
Unique package names 39 39 0.0%
Package directories on disk (cross-check) 32 32 0.0%
Installed node_modules on disk 2.43 MB 2.43 MB +0.1%
Installed files 434 434 0.0%
Install wall time (warm npm cache, median of 3) 1.30 s 1.31 s +0.8%
Published tarball (packed) 239.6 kB 240.5 kB +0.4%
Published tarball (unpacked) 775.2 kB 776.6 kB +0.2%
Files in tarball 99 99 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.

@codspeed

codspeed Bot commented Oct 3, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 2 untouched benchmarks


Comparing productdevbook:perf/help-without-commands (dc966ae) with main (f140924)

Open in CodSpeed

@codecov-commenter

codecov-commenter commented Oct 3, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 50.00000% with 3 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@f140924). Learn more about missing BASE report.

Files with missing lines Patch % Lines
packages/nuxt-cli/src/run.ts 25.00% 3 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1591   +/-   ##
=======================================
  Coverage        ?   83.37%           
=======================================
  Files           ?      178           
  Lines           ?    11310           
  Branches        ?     3247           
=======================================
  Hits            ?     9430           
  Misses          ?     1584           
  Partials        ?      296           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai

coderabbitai Bot commented Oct 3, 2026

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 43a1b09b-6c76-41f6-873e-14910d25a657
📥 Commits

Reviewing files that changed from the base of the PR and between f140924 and dc966ae.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (24)
  • packages/nuxt-cli/src/commands/add-template.ts
  • packages/nuxt-cli/src/commands/analyze.ts
  • packages/nuxt-cli/src/commands/build.ts
  • packages/nuxt-cli/src/commands/cleanup.ts
  • packages/nuxt-cli/src/commands/curl.ts
  • packages/nuxt-cli/src/commands/dev-child.ts
  • packages/nuxt-cli/src/commands/dev.ts
  • packages/nuxt-cli/src/commands/devtools.ts
  • packages/nuxt-cli/src/commands/docs.ts
  • packages/nuxt-cli/src/commands/generate.ts
  • packages/nuxt-cli/src/commands/info.ts
  • packages/nuxt-cli/src/commands/init.ts
  • packages/nuxt-cli/src/commands/meta.ts
  • packages/nuxt-cli/src/commands/module/add.ts
  • packages/nuxt-cli/src/commands/module/index.ts
  • packages/nuxt-cli/src/commands/prepare.ts
  • packages/nuxt-cli/src/commands/preview.ts
  • packages/nuxt-cli/src/commands/start.ts
  • packages/nuxt-cli/src/commands/task/index.ts
  • packages/nuxt-cli/src/commands/test.ts
  • packages/nuxt-cli/src/commands/typecheck.ts
  • packages/nuxt-cli/src/commands/upgrade.ts
  • packages/nuxt-cli/src/run.ts
  • patches/citty@0.2.2.patch

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

CLI commands now use a shared metadata catalog, including hidden status for _dev, init, and start. Main-command usage renders subcommands from that catalog. The Citty patch changes argument inheritance, repeated-value parsing, and usage output for hidden and repeated arguments.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to dc966

The reviewed help-text and argument-forwarding paths retain their expected behavior; no identified issue blocks merging.

Security Architecture Review

Security architecture risk: ⚪ Minimal · up to dc966

The change avoids loading command implementations for root help while preserving named-command execution and existing controls. No introduced or worsened security issue was identified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The assessed change is confined to local CLI metadata loading and help rendering. The inspected dispatch changes do not grant additional authority or introduce a new execution path into build or documentation operations.

Trust Boundaries and Controls

  • observed — Programmatic command selection retains its own-property validation against the registry. Citty’s early return applies only to an undefined command name; explicit names continue through the existing lookup and alias-resolution paths.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 23 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: root help renders without loading every command.
Description check ✅ Passed The description explains the performance issue, the metadata and citty changes, and the reported benchmarks and test results. It is directly related to the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 23 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants