Files
stanza/registry/modules/ui-shadcn-base/module.ts
T
jake 678b5bc1af feat: add ui-shadcn-base, ui-shadcn-radix, and ui-tailwind modules; rename styling category to ui
- Rename `styling` → `ui` across the entire codebase (CATEGORIES array, docs, AGENTS.md, CLI tests, resolver, template tests, codemod runner); `ui` is `home: package` so shadcn installs into `packages/ui/` as a workspace dep rather than wiring directly into the app shell
- Add `ui-shadcn-base` (Tailwind v4 / CSS-first, no Radix peer) and `ui-shadcn-radix` (Tailwind v4 + Radix primitives) with full per-framework template sets for Next.js and TanStack Start: `components.json`, `globals.css`, `postcss.config.mjs`, `theme-provider.tsx`, shared `Button` + `utils.ts`; light/dark logos inlined from svgl
- Rename `styling-tailwind` → `ui-tailwind` to match the new category id; update its module definition to `category: "ui"` and `home: package`
- Add four new codemod builtins with tests: `add-array-entry-in-call` (inserts an element into a named array argument of any `CallExpression`), `replace-import` (rewrites a specifier in an existing import declaration), `set-html-attributes` (sets attributes on an HTML/JSX element by tag name or selector), `set-tsconfig-paths` (merges `compilerOptions` entries into a `tsconfig.json` AST node); register all four in the builtins index and export from `@stanza/codemods`
- Add `wrap-root-layout` builtin tests
- Update registry.mdx, concepts.mdx, cli.mdx, getting-started.mdx, agents.mdx, index.mdx, AGENTS.md, README.md, TODO.md, and SKILL.md to reflect the category rename and new modules
2026-05-25 15:26:08 -04:00

231 lines
7.8 KiB
TypeScript

import { defineModule } from "@stanza/registry";
export default defineModule({
id: "shadcn-base",
category: "ui",
label: "shadcn (Base UI)",
description:
"shadcn/ui registry preset `base-nova` — Tailwind + Base UI primitives in packages/ui.",
version: "0.1.0",
peers: { framework: ["next", "tanstack-start"] },
homepage: "https://ui.shadcn.com",
// Everything shadcn pulls in regardless of framework — kept at module level so
// the two adapters only carry the per-framework deltas.
dependencies: {
"@base-ui/react": "^1.5.0",
"class-variance-authority": "^0.7.1",
clsx: "^2.1.1",
"lucide-react": "^1.16.0",
shadcn: "^4.8.0",
"tailwind-merge": "^3.6.0",
"tw-animate-css": "^1.4.0",
zod: "^3.25.76",
},
devDependencies: { tailwindcss: "^4.1.18" },
adapters: [
{
key: "next",
match: { framework: "next" },
// next-themes powers shadcn's <ThemeProvider> on Next — class strategy +
// localStorage + system-pref subscription, all out of the box.
dependencies: { "next-themes": "^0.4.6" },
devDependencies: { "@tailwindcss/postcss": "^4.1.18" },
templates: [
// packages/ui/ — shared shadcn surface.
{
src: "shared/package/src/lib/utils.ts",
dest: "src/lib/utils.ts",
scope: "package",
},
{
src: "shared/package/src/components/button.tsx",
dest: "src/components/button.tsx",
scope: "package",
template: true,
},
{
src: "shared/package/components.json",
dest: "components.json",
scope: "package",
template: true,
},
// Next-specific: the oklch theme tokens + postcss plugin config.
{
src: "next/package/src/styles/globals.css",
dest: "src/styles/globals.css",
scope: "package",
},
{
src: "next/package/postcss.config.mjs",
dest: "postcss.config.mjs",
scope: "package",
},
// apps/<id>/ — outer shadcn config + ThemeProvider + postcss re-export.
{
src: "next/app/components.json",
dest: "components.json",
scope: "app",
template: true,
},
{
src: "next/app/postcss.config.mjs",
dest: "postcss.config.mjs",
scope: "app",
template: true,
},
{
src: "next/app/components/theme-provider.tsx",
dest: "components/theme-provider.tsx",
scope: "app",
},
],
codemods: [
// Swap the framework's `import "./globals.css"` for the shadcn-managed
// bundle in packages/ui (oklch tokens + tw-animate-css + the shadcn
// tailwind base). Done as an in-place replace (not append + comment)
// because the framework's CSS file becomes orphaned otherwise.
{
id: "replace-import",
args: {
file: "app/layout.tsx",
from: "./globals.css",
to: "{{package.name}}/globals.css",
},
},
// `next-themes` toggles the `dark`/`light` class on <html>; without
// suppressHydrationWarning React complains on the first paint
// because the server-rendered class != the client-resolved one.
{
id: "set-html-attributes",
args: {
file: "app/layout.tsx",
attributes: [{ name: "suppressHydrationWarning", boolean: true }],
},
},
// Wrap `{children}` with <ThemeProvider>.
{
id: "wrap-root-layout",
args: {
providerName: "ThemeProvider",
providerImport: "@/components/theme-provider",
importKind: "named",
},
},
// IDE-only: pnpm's isolated linker + the package's subpath exports
// are enough at runtime, but shadcn's CLI (when adding more
// components) reads tsconfig.paths to know where ui/* lives.
{
id: "set-tsconfig-paths",
args: {
paths: { "{{package.name}}/*": ["../../packages/ui/src/*"] },
},
},
],
},
{
key: "tanstack-start",
match: { framework: "tanstack-start" },
// TSS has no next/font; pull Inter via @fontsource so the shadcn
// `--font-sans` token resolves to a real font.
dependencies: { "@fontsource-variable/inter": "^5.2.8" },
devDependencies: { "@tailwindcss/vite": "^4.1.18" },
templates: [
// packages/ui/ — shared shadcn surface.
{
src: "shared/package/src/lib/utils.ts",
dest: "src/lib/utils.ts",
scope: "package",
},
{
src: "shared/package/src/components/button.tsx",
dest: "src/components/button.tsx",
scope: "package",
template: true,
},
{
src: "shared/package/components.json",
dest: "components.json",
scope: "package",
template: true,
},
// Start-specific globals.css — adds the @fontsource Inter import.
{
src: "tanstack-start/package/src/styles/globals.css",
dest: "src/styles/globals.css",
scope: "package",
},
// apps/<id>/ — outer shadcn config + the TSS-native ThemeProvider
// (ScriptOnce-based, per https://ui.shadcn.com/docs/dark-mode/tanstack-start —
// next-themes is Next-only).
{
src: "tanstack-start/app/components.json",
dest: "components.json",
scope: "app",
template: true,
},
{
src: "tanstack-start/app/components/theme-provider.tsx",
dest: "components/theme-provider.tsx",
scope: "app",
},
],
codemods: [
// Splice tailwindcss() into the framework's vite.config.ts plugin array.
{
id: "add-plugin-to-call",
args: {
file: "vite.config.ts",
callee: "defineConfig",
property: "plugins",
call: "tailwindcss()",
imports: [{ from: "@tailwindcss/vite", default: "tailwindcss" }],
position: "before:react",
},
},
// TSS's idiomatic CSS wiring: import the stylesheet `?url` and
// emit it as a `<link rel="stylesheet">` via the route's
// `head: () => ({ links: [...] })`. The codemod creates the head
// arrow + links array when absent.
{
id: "add-array-entry-in-call",
args: {
file: "src/routes/__root.tsx",
callee: "createRootRoute",
property: "head().links",
entry: '{ rel: "stylesheet", href: appCss }',
imports: [{ from: "{{package.name}}/globals.css?url", default: "appCss" }],
},
},
// Hand-rolled ThemeProvider toggles `dark`/`light` on <html>;
// suppressHydrationWarning silences the SSR/CSR class mismatch.
{
id: "set-html-attributes",
args: {
file: "src/routes/__root.tsx",
attributes: [{ name: "suppressHydrationWarning", boolean: true }],
},
},
// Wrap <Outlet /> with <ThemeProvider>. ThemeProvider defaults
// (defaultTheme="system", storageKey="theme") match shadcn's
// dark-mode-docs guidance, so no extra prop wiring is needed.
{
id: "wrap-root-layout",
args: {
providerName: "ThemeProvider",
providerImport: "@/components/theme-provider",
importKind: "named",
},
},
// IDE-only: matches shadcn's app tsconfig paths so its CLI can add
// more components without manual path config.
{
id: "set-tsconfig-paths",
args: {
paths: { "{{package.name}}/*": ["../../packages/ui/src/*"] },
},
},
],
},
],
});