Repository navigation
perf(nuxt): reuse the composable scan between manifest writes - #308
Conversation
Each manifest write re-ran unimport's scanDirExports over every import dir, so a nuxt build read and parsed every composable and util six times. Keep the scan for the current import dirs and drop it on imports:extend, which Nuxt calls right after each of its own rescans.
Drop a rejected scan from the cache so the next manifest write scans again, as it did before the scan was reused.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Latest result Pull request merged. Final answersSession Pull request merged. Session Waiting for GitHub merge request 5f3a7c1f-5fec-42e9-807c-50b4cc02a448. Ready: verified the expected HEAD, complete diff, and supplied reviews. No actionable findings or failing hosted checks remain. Focused composable tests passed 20/20; vp fmt --check, vp lint, and git diff --check passed. Initial pnpm commands hit an automatic-install/no-TTY error; disabling pre-run dependency verification allowed validation with installed dependencies. Working tree is clean; no repair commit needed. Session Waiting for running checks before the next pass. Session Waiting for running checks before the next pass. Session Waiting for running checks before the next pass. Session Waiting for running checks before the next pass. Session Waiting for running checks before the next pass. Session Waiting for running checks before the next pass. Session Waiting for running checks before the next pass. |
commit: |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes across the complete two-file diff, including the cache lifecycle against Nuxt's import-regeneration ordering and the new regression coverage.
- Composable scan reuse — caches the unimport directory scan per Nuxt evidence instance and import-directory key, while preserving the existing manifest output.
- Invalidation and recovery — clears reuse after
imports:extendand after a rejected active scan so watcher changes and transient failures are reflected by the next manifest write. - Regression coverage — verifies repeated writes avoid extra composable reads, watcher-added files enter
scannedComposableFiles, and failed scans retry successfully.
Focused verification passed: 20 tests across composable-scan-reuse.test.ts and composable-scan-inventory.test.ts, plus lint and formatting checks for both changed files.
gpt-5.6-sol | 𝕏

The Nuxt module writes
doctor.manifest.jsononnitro:init,builder:generateApp,prepare:types, Nitro'srollup:beforeandcompiled, andclose. Every write called unimport'sscanDirExportsover all import dirs again, so each one re-read and re-parsed every composable and util in every layer. Anuxt buildscanned six times.The module now keeps the scan for the current import dirs and drops it whenever
imports:extendfires. Nuxt calls that hook right after each of its own rescans, including the ones its watcher triggers in dev, so Doctor rescans only when Nuxt does. A scan that fails is dropped too, so the next write retries. The manifest content doesn't change.Numbers
Two generated fixtures, Nuxt 4.5.2, driven by
loadNuxt+buildNuxt: an app with 6 local layers and 154 composable/util/shared files, and one with 22 layers and 966 files. "Reads" countsreadFilecalls on those files that came fromwriteManifest. Start-up stops atbuild:beforeand is measured beforenuxt.close(). Times are the median of 5 alternating runs on a shared 16-core machine (1-minute load 1.0 to 1.7).The full build counts include the
closewrite that Nuxt's build fires itself. Nuxt's own reads are the same before and after.In every run, the manifests from both builds were identical once
generatedAtwas removed: 109,945 bytes with 6 layers and 518,504 bytes with 22, both at start-up and after close.Test plan
tests/rule-packs/nuxt/composable-scan-reuse.test.ts. The first case checks that two extrabuilder:generateAppwrites read no composable files and that a composable added throughbuilder:watchstill reachesscannedComposableFiles. Onmainit fails withexpected 5 to be 3, and it also fails if theimports:extendreset is removed. The second case fails one scan withEIOand checks that the next write rescans. It fails if the rejected scan stays cached.composable-scan-inventory,extension-hooks,nuxt-manifest-evidence,nuxt-modern-rules-2/-3,keyed-composable-registration,export-name-matches-file, the auto-import tests ande2e-fixturespass.vp fmt --checkandvp linton the changed files.pnpm testwith only the root package installed: 207 of 208 files pass.docs/app/utils/rule-catalog.test.tsneeds the docs install (can't findvue); CI covers it.🤖 Generated with Claude Code