fix: stop reporting Nuxt/Nitro convention files and auto-imported Vue components as orphaned - #724
Open
MercurialUroboros wants to merge 1 commit into
Conversation
… components as orphaned The orphaned detector knows Next.js App Router conventions but nothing about Nuxt, so every file-routed and auto-registered file in a Nuxt project came back as "zero importers, not an entry point". Adds Nuxt project detection (nuxt.config.* at the scan root, or a `nuxt` dependency in package.json) and, behind that gate: - Convention entry points: server/api, server/routes, server/middleware, server/plugins, server/tasks, plus pages/, layouts/, middleware/, plugins/ and modules/ under both the Nuxt 3 root layout and the Nuxt 4 app/ srcDir, and the root config and shell files. - Auto-import surfaces (components/, composables/, utils/, shared/, server/utils/) resolved by usage rather than exempted wholesale: the file's registered name is derived the way Nuxt derives it, and a project-wide index of identifiers and template tags decides whether anything references it. A component nobody uses is still reported. Claude-Session: https://claude.ai/code/session_01D5dUe5YLshYGAFwwxf1JJx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The
orphaneddetector has full support for Next.js App Router conventions (_NEXTJS_APP_DIR_CONVENTIONS,_detect_nextjs_project,_is_nextjs_convention_entry) but nothing equivalent for Nuxt. On a Nuxt project, almost every finding it produces is a false positive, for two separate reasons.First, Nuxt and Nitro register entire directory trees by convention. Nothing imports
server/api/**,server/middleware/**,server/plugins/**,server/tasks/**,pages/**,layouts/**,middleware/**orplugins/**, because the framework picks them up from the filesystem. Every one of those files has zero importers by design.Second, Nuxt auto-imports
components/,composables/,utils/andshared/. Consumers write<GameOutcomeDrawer />in a template, or calluseUser()as a bare identifier, with noimportstatement anywhere. Those references are invisible to an import graph, so the components and composables that are used most heavily are exactly the ones reported as dead.I measured this against a real Nuxt 4 codebase (roughly 1,700 files, Nuxt 4 with
app/as srcDir plus a Nitroserver/tree). Runningdetect orphanedand filtering to that project's own scan exclusions and the TypeScript entry patterns, the detector reported 429 orphans. Broken down by directory:server/api/app/components/app/composables/shared/server/plugins/app/{layouts,middleware,plugins}/app/utils/server/middleware/server/tasks/server/routes/All 359 findings on Nuxt-owned surfaces were false positives. I checked the 103 component findings by hand: every one is referenced as a PascalCase tag in some
.vuetemplate. Nothing in that project was actually dead code.Fix
The detector now recognises a Nuxt project the way it recognises a Next.js one: a
nuxt.config.{ts,js,mjs,cjs,mts}at the scan root, or anuxtdependency inpackage.json. Everything below is gated on that detection, so Next.js projects and the existingapp/handling are untouched, anddetect_frameworks=Falsestill disables the whole thing.Convention entry points are exempted by location, mirroring
_is_nextjs_convention_entry. That covers the Nitro trees (server/api,server/routes,server/middleware,server/plugins,server/tasks), the app trees (pages,layouts,middleware,plugins,modules), and the root config and shell files (nuxt.config,app.config,capacitor.config,app,error). Nuxt 3 keeps these at the project root and Nuxt 4 puts them underapp/, so an optional leadingapp/orsrc/srcDir segment is stripped before matching.server/lib/**and similar ordinary module directories keep their orphan verdict.Auto-import surfaces are handled differently, because blanket-exempting
components/would hide genuinely dead components and cost the detector most of its value on a Nuxt codebase. Instead the file's registered name is computed and then looked up in a project-wide usage index:app/components/game/GameOutcomeDrawer.vueregisters asGameOutcomeDrawer;app/components/account/ThemeSwitcher.vueregisters asAccountThemeSwitcher..client,.serverand.globalsuffixes are stripped, and anindexfile takes its directory's name. The undeduplicated join and the bare filename are accepted too, sincecomponents.dirswithpathPrefix: falseregisters under the bare stem and so does a consumer that imports the file by path.shared/modules resolve through the identifiers they export, parsed fromexportdeclarations andexport { … }lists, plus the filename stem..vue,.ts,.tsx,.js,.jsx,.mjsand.mtsfile under the scan root for identifiers and for component tags. Kebab-case tags are normalised to PascalCase, so<game-outcome-drawer />and<GameOutcomeDrawer />both resolve to the one registered name. The index records which file each name was seen in, so a module's own mention of itself does not count as a reference and an unused composable is still reported.The index is built lazily, only when the first auto-import candidate is reached, so nothing changes for projects that are not Nuxt. On the 1,700-file codebase above the whole run takes about five seconds, unchanged from before.
Convention matching is anchored on the scan root rather than on
rel(), which resolves against the project root and yields a../..prefix when the tool is invoked from outside the project being scanned.After the fix, the same run on the same codebase reports 67 orphans instead of 429. All 359 Nuxt-surface false positives are gone, and the 67 that remain are outside Nuxt's conventions: CLI scripts under
scripts/invoked through npm scripts, tooling configs, a worker bundled by a build step, and a directory the project imports through a~/alias the graph does not resolve. None of them is a Nuxt or Nitro convention file, and none of them is an auto-imported component or composable.Implementation lives in a new
desloppify/engine/detectors/_orphaned/nuxt.py, following the_flat_dirsprecedent of keeping the public detector module small, andorphaned.pywires it in beside the Next.js checks. Tests are indesloppify/tests/detectors/test_orphaned_nuxt.py: 43 cases covering project detection, convention paths, Nuxt's component-name derivation, export parsing, the usage index, and end-to-end orphan verdicts on a Nuxt-shaped tree, including the negative cases (an unreferenced composable is still reported, a non-Nuxt project still reportsserver/api/**,detect_frameworks=Falsedisables all of it).python -m pytest desloppify/tests/ -qpasses: 5,695 passed, 152 skipped. Three failures indesloppify/tests/lang/common/test_bash_unused_imports.pyare present onmainbefore this change and are unrelated.https://claude.ai/code/session_01D5dUe5YLshYGAFwwxf1JJx