Repository navigation
feat: read shadcn registry folders as read-only design systems, and make exclude add to the defaults - #183
Merged
Conversation
A config's `exclude` replaced the default list, so `"exclude": ["components/signoff-ui/**"]`, the advice for a folder installed from a registry, left that folder out and started checking every test, story and .d.ts file. The list now adds to the defaults; a pattern that starts with `!` takes out the default it names, and one that names no default gets a warning. A design-system package's list adds to them too.
Zero config took only the ui folder of components.json as the design system, so code `npx shadcn add` copied from other registries (components/ai-elements, components/signoff-ui) was checked as the app's own, and the next `shadcn add` could bring findings in files nobody edited. For each registry in the `registries` of components.json, the folder of that name under the components alias is now a design system of its own, after the others: check, the hook, ESLint and the Action skip its files, the app's code is checked against its components, agents find them (get_component says which registry each came from), none is suggested for a native element, and its lib/ helpers stay out of list_components and search_components. inspect names the folders on its detected line, and --explain says why each was read or not. A config that lists such a folder, as init writes it, reads it the same way. Not when the components come from a workspace package imported through its exports.
…s from a shadcn registry The app keeps 31 files from @ai-elements in components/ai-elements, as its components.json names the registry. With this branch, zero config reads that folder as a read-only design system: the run checks 3 files and reports 1 error and 3 warnings in components/chat-interface.tsx. `pnpm corpus --update` wrote the run into the snapshot; the counts of the other 24 runs are unchanged.
Merged
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.
Roadmap item: X2-37 (registry-aware onsystem: registry folders as read-only secondary design systems,
excludeadditive, corpus fixture). Builds on #173 (init writes the registry folders), #175 (one exclude matcher) and #176 (installed packages).What was wrong
Zero config took only the
uifolder ofcomponents.jsonas the design system, so code thatnpx shadcn addcopied from other registries was checked as the app's own. On VoltAgent's chatbot starter (pinned below),check .on main gives7 errors, 7 warnings in 7 of 34 files checked (18 design-system files skipped), and 11 of those 14 findings are incomponents/ai-elements(no-hardcoded-color 8, prefer-design-system-component 3), code the app did not write. The baseline did not hold either: in a small app with@signoff-uiincomponents.json, aftercheck . --update-baseline, addingcomponents/ui/textarea.tsx(asshadcn add @ai-elements/prompt-inputdoes) makes main report1 error, 0 warnings in 1 of 2 files checked (1 in the baseline; …), a newNative <textarea>error in the untouchedcomponents/signoff-ui/diff-review.tsx; this branch reportsNo new problems in 1 file. Agents did not see the registry's components: on main,get_component DiffReviewanswersNo component named "DiffReview", and<DiffReview tone="danger">,<DiffReviewPanel />,<Message size="lg">and<Message.Header />passcheckwithout a finding.Separately, a config's
excludereplaced the default list, so"exclude": ["components/signoff-ui/**"](the advice for registry code) left the folder out and started checking every test, story and.d.tsfile.What changed
Three commits:
excludeadds to the defaults (src/config.ts,schema.json). A pattern that starts with!takes out the default it names ("!**/*.d.ts"); one that names no default gets a warning.--include-testsstill checks tests and stories for one run. A design-system package'sexcludeadds to the defaults for finding its own components too.src/detect.ts,src/design-system.ts,src/explain.ts,src/lint/rules/components.ts,src/server/render.ts). For each registry in theregistriesofcomponents.json, the folder of that name under thecomponentsalias (@signoff-ui→components/signoff-ui) that holds.tsxor.jsxfiles is added tocomponentsand todesignSystemswith aregistryfield, after every other design system:check, the hook (check --skip-excluded <file>), ESLint and the Action skip its files, through the samecomponentFilesthey already use forcomponents/ui.<DiffReview> has no prop "tone",Message has no member "Header","DiffReviewPanel" is not a design-system component … Did you mean <DiffReview>?).prefer-design-system-componentnever suggests a registry component for a native element unlesselementsmaps it, so signoff-ui's internalImgno longer replaces an app's<img>. Where a registry exports a name the primary has (Button), the primary wins, as with several design systems before.get_componentaddsInstalled from the shadcn registry @signoff-ui into components/signoff-ui: use it as it is. …;list_componentsmarks each(from @signoff-ui, read-only); components in the folder'slib/stay out oflist_componentsandsearch_components.inspectnames the folders on itsdetectedline (components.json (ui: @/components/ui → components/ui); from shadcn registries, read-only: components/ai-elements (@ai-elements, 31 files)), andinspect --explainhas aregistry @xstep for each, with why it was not read (does not exist yet, holds no.tsx/.jsxfile yet, inside the ui folder).components, asinitwrites it since fix(init): add the shadcn registry folders to the components it writes #173, reads it the same way (rankedSourcesinsrc/design-system.ts), so the read-only treatment does not end atinit. A foldercomponents.jsonnames in noregistriesentry is read as before.exports(@workspace/uiin shadcn's monorepo templates):initwould then write a config whoseimportPathnames that package for the registry code too.--explainsays so.corpus/repos.json,corpus/snapshot.json): VoltAgent'sexamples/next-js-chatbot-starter-templateat72a46c7, a shadcn app whosecomponents.jsonnames@ai-elementsand keeps 31 files incomponents/ai-elements.docs/configuration.md: the "Code from shadcn registries" section now starts with what zero config does, and the sentence at line 88 that #173 left ("add the registry folders to itscomponents") is rewritten:initwrites them and a config that lists them keeps them read-only. The two configs it gives remain for code zero config does not set apart (loose files, unnamed registries, the monorepo layout). Theexcluderow says a list adds to the defaults, and the example no longer lists patterns the defaults cover.initoutputsrc/init.tsis not touched (OS2-38, #182, holds it this wave). Since zero config now finds the registry folders,initwrites them fromconfig.componentsand names them on itsFound the design system through …line; its own "Added components/… to components" note now prints only for folders that do not exist yet or hold no component file. The written config is the same set of globs, with a folder that does not exist yet last instead of in alphabetical order (test/init-registry-folders.test.tsupdated for both).Changesets (minor, two)
Behaviour changes for existing configs:
excludelists folders without repeating the defaults now also leaves out tests, stories,__tests__/__stories__/__mocks__folders and.d.tsfiles.components.jsonnames: its components are no longer suggested for native elements (map one inelementsto keep that),get_componentlabels them, and theirlib/helpers leavelist_componentsandsearch_components.components.jsonnames registries: their folders' files are no longer reported, and the app's code gets new findings where it misuses their components, such as the icon-onlyPromptInputButtonbelow.Files outside the item's listed files
src/config.tsandschema.json: theexcludechange needs them (the item's title includes it). No other wave 2 PR touches them (feat(corpus): check the bench repositories with their dependencies installed, weekly, and draw a holdout to label blind #181 and fix(hook): check the files a Codex apply_patch changes; init offers an AGENTS.md section #182 do not).docs/ci.md(theexcludeclause, and the sentence that said registry code is checked as your own) anddocs/troubleshooting.md(said zero config takes only the ui folder): both became wrong with this change.test/config.test.ts,test/design-system-package.test.ts,test/exclude-scope.test.ts(resolvedexcludenow includes the defaults, a story stays out),test/registry-code.test.ts(theexcludeadvice and field row),test/init-registry-folders.test.ts(above).Corpus
pnpm corpus --update(all runs, against a cached checkout), thenpnpm corpus:Counts match corpus/snapshot.json. Before adding the new run,pnpm corpuson this branch also printedCounts match corpus/snapshot.json: neither change moves any of the 24 existing runs. The--updatetable:The run is new, so "before" is empty. With this branch it checks 3 files (
1 error, 3 warnings in 1 of 3 files checked (49 design-system files skipped)); main's CLI on the same checkout gives 7 errors and 7 warnings, as above. The error is new:Icon-only <PromptInputButton> has no accessible nameatcomponents/chat-interface.tsx:188, a submit button whose only child is a<Send>icon. It is reported now because the rule knowsPromptInputButtonrenders a button. The 3 warnings (bg-green-500/10,bg-green-500,text-green-600) are reported on main as well.node scripts/corpus.mjs eslint --only voltagent:same as check.Labels: 290, 244 still reported (as on main); the blind sample has 80 of its 80 labels still reported.
corpus/sample-stale.jsonis kept, so the README still quotes the sample with the corpus it was drawn from (12 repositories, 24 runs); no new labels. The snapshot'sversionfield moved from 0.6.0 to 0.7.0 because--updatewrites the current package version.There is no public app with signoff-ui installed through
shadcn addto pin yet;test/registry-aware.test.tscovers that shape (alib/primitives.tsxwithImg), and the follow-ups name the corpus part.How it was tested
New tests, both failing on main (12 of their 15 tests fail there; the other 3 are guards that pass before and after):
test/registry-aware.test.ts: a shadcn app with@signoff-ui,@ai-elementsand@react-bitsincomponents.json. Registry files skipped bycheck .and when named (the hook's path), the nextshadcn addkeepscheckpassing, invented props and parts reported against registry components,<img>not told to useImgwhile<button>is still told to useButton, a same-nameButtonin a registry loses tocomponents/ui's,inspectand--explaintext,get_component/search_components/list_components, the ESLint plugin skipping the folders, no registry read withoutregistriesor with a workspace package's exports, a config that lists the folders keeping them read-only (andelementsmappingImgback), and a config with its owncomponentsleft as it is.test/exclude-additive.test.ts: defaults kept with a folder inexclude,--include-tests, a list that repeats the defaults,!taking one out, the warning for a!that names no default, and an empty list.Gate, all passing:
pnpm install --frozen-lockfile --prefer-offline,pnpm format:check,pnpm lint,pnpm typecheck,pnpm test(78 files passed, 1 skipped; 1014 tests passed, 3 skipped),pnpm build(schema.jsoncommitted),pnpm smoke(smoke test passed),pnpm corpus(above).Follow-ups
init: a note of its own for the registry folders zero config already found, with how to check that code as your own (needssrc/init.ts, after fix(hook): check the files a Codex apply_patch changes; init offers an AGENTS.md section #182).check_uion a registry file: a one-line "installed from @x, use it as it is" answer instead of findings (needssrc/server/index.ts).exports): read its registry folders onceinitkeeps them out of a config whoseimportPathnames that package.shadcn add, pinned in the corpus, so each release checks the two together; and the CI step from the roadmap row that applies every fix and runstsc --noEmit.README.md:79anddocs/quickstart.md:18still describe registry folders as something to set apart by hand; they could say zero config reads the onescomponents.jsonnames.components.jsonchanges; a registry folder thatshadcn addcreates while it runs is read at its next start (checkand the hook read it at once).