@pauldvlp/vp-react-ts-shadcn 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # @pauldvlp/vp-react-ts-shadcn
2
+
3
+ A [Vite+](https://viteplus.dev) **monorepo generator** that scaffolds a minimal front-end workspace:
4
+
5
+ - `apps/website` — a React + Vite+ app (Tailwind v4, React Compiler)
6
+ - `packages/ui` — a shared **shadcn** design system consumed by the app
7
+
8
+ It's a [Bingo](https://create.bingo) template, so options can be passed on the `vp create` command
9
+ line (anything after `--`) and the **shadcn theme is materialized at create time** from the preset
10
+ you choose.
11
+
12
+ ## Usage
13
+
14
+ Published under the [`@pauldvlp/create`](../create) manifest:
15
+
16
+ ```bash
17
+ # Interactive (prompts for anything you don't pass)
18
+ vp create @pauldvlp:react-ts-shadcn
19
+
20
+ # Non-interactive, fully specified
21
+ vp create @pauldvlp:react-ts-shadcn -- \
22
+ --name my-app --scope @acme --base base --preset vega --iconLibrary lucide --components button,card,dialog
23
+ ```
24
+
25
+ > Only **string/enum** options parse reliably as `vp create -- --flag value`. **Boolean** options
26
+ > (`--cssVariables`, `--rtl`, `--pointer`, `--install`) are best left to the interactive prompt —
27
+ > Bingo's CLI does not accept `--no-x` / `--x=false` cleanly. Omit them and answer the prompt.
28
+
29
+ ## Options
30
+
31
+ | Option | Type / values | Default | Notes |
32
+ | --------------- | ---------------------------------------------------------- | -------------- | --------------------------------------------------------------------------------------- |
33
+ | `--name` | string | `my-app` | Root project / package name. |
34
+ | `--scope` | string | `@<name>` | npm scope for workspace packages → `@scope/website`, `@scope/ui`. Defaults to the project name prefixed with `@` (e.g. `--name acme` → `@acme`); falls back to `@app`. |
35
+ | `--base` | `radix` \| `base` | `radix` | shadcn component library (radix-ui or @base-ui). Honored by `shadcn init --base`. |
36
+ | `--preset` | style name or code | `b30557okNu` | A style (`nova`, `vega`, `maia`, `lyra`, `mira`, `luma`, `sera`, `rhea`) **or** a code from ui.shadcn.com. **Owns** color, fonts, radius, baseColor, menu styling. |
37
+ | `--iconLibrary` | `lucide` \| `hugeicons` \| `radix` \| `tabler` | `hugeicons` | Icon library (persists; not part of the preset). |
38
+ | `--cssVariables`| boolean | `true` | CSS variables for theming. |
39
+ | `--rtl` | boolean | `false` | RTL support. |
40
+ | `--pointer` | boolean | `false` | Pointer cursor on interactive elements. |
41
+ | `--components` | comma list | `button,badge` | shadcn components to pre-install. `button` + `badge` are always included. |
42
+ | `--install` | boolean | `true` | Run install + apply the shadcn theme after scaffolding. `false` = files only. |
43
+
44
+ ### What the preset controls vs. what you control
45
+
46
+ shadcn presets are **authoritative for the theme**: `--preset` sets the `style`, `baseColor`, accent
47
+ color, fonts, radius and menu styling. Generate one at <https://ui.shadcn.com> (theme editor →
48
+ copy the preset code) and pass it as `--preset <code>`. Independent of the preset you still pick
49
+ `--base` (radix-ui vs @base-ui), `--iconLibrary`, `--rtl`, `--pointer`, and `--cssVariables`.
50
+
51
+ ## How it scaffolds
52
+
53
+ `produce()` emits the monorepo skeleton (workspace, Vite+/TS config, the website shell, and a UI
54
+ package with a **minimal** `globals.css` so Tailwind v4 is detectable), then runs:
55
+
56
+ ```bash
57
+ pnpm install
58
+ pnpm --filter <scope>/ui exec shadcn init --base <base> --preset <preset> ... --no-reinstall -y -f
59
+ pnpm --filter <scope>/ui exec shadcn add button badge <extra> -y
60
+ ```
61
+
62
+ shadcn `init` fills the theme tokens into `globals.css` and creates `lib/utils.ts`; `add` writes
63
+ components into `src/components/ui/`. The UI package `exports` (`./components/* → ./src/components/*.tsx`)
64
+ resolves them as `@scope/ui/components/ui/<name>`, which is how the starter `App.tsx` imports them.
65
+
66
+ ## Re-theme later
67
+
68
+ ```bash
69
+ pnpm --filter @scope/ui exec shadcn init --preset <new-code> --no-reinstall -y -f
70
+ pnpm --filter @scope/ui exec shadcn add <component>
71
+ ```
72
+
73
+ ## Develop the generator
74
+
75
+ ```bash
76
+ pnpm install
77
+ node bin/index.ts --help # list options
78
+ node bin/index.ts --directory /tmp/demo --name demo --scope @demo --base base --preset vega
79
+ ```
package/dist/index.js ADDED
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env node
2
+
3
+ // bin/index.ts
4
+ import { runTemplateCLI } from "bingo";
5
+
6
+ // src/template.ts
7
+ import fs from "node:fs";
8
+ import path from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+ import { createTemplate } from "bingo";
11
+ import { z } from "zod";
12
+
13
+ // package.json
14
+ var package_default = {
15
+ name: "@pauldvlp/vp-react-ts-shadcn",
16
+ version: "0.3.1",
17
+ description: "Vite+ monorepo template: one frontend app (website) + a shared shadcn UI package, themeable via shadcn presets.",
18
+ keywords: [
19
+ "vite-plus-generator",
20
+ "vite-plus",
21
+ "shadcn",
22
+ "react",
23
+ "template"
24
+ ],
25
+ bin: "./dist/index.js",
26
+ type: "module",
27
+ files: [
28
+ "dist",
29
+ "template"
30
+ ],
31
+ scripts: {
32
+ build: "esbuild bin/index.ts --bundle --outfile=dist/index.js --format=esm --platform=node --target=node22 --packages=external",
33
+ dev: "node bin/index.ts",
34
+ prepack: "pnpm run build"
35
+ },
36
+ dependencies: {
37
+ bingo: "^0.9.3",
38
+ zod: "^3.25.76"
39
+ },
40
+ devDependencies: {
41
+ "@types/node": "^24",
42
+ esbuild: "^0.25.0",
43
+ typescript: "^5"
44
+ },
45
+ engines: {
46
+ node: ">=22.18.0"
47
+ }
48
+ };
49
+
50
+ // src/template.ts
51
+ var TEMPLATE_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "template");
52
+ var RENAME = {
53
+ _gitignore: ".gitignore"
54
+ };
55
+ var DEFAULT_PRESET = "b30557okNu";
56
+ var ICON_LIBS = {
57
+ hugeicons: { "@hugeicons/react": "^1.1.9", "@hugeicons/core-free-icons": "^4.2.1" },
58
+ lucide: { "lucide-react": "^0" },
59
+ radix: { "@radix-ui/react-icons": "^1" },
60
+ tabler: { "@tabler/icons-react": "^3" }
61
+ };
62
+ function toScope(name) {
63
+ const n = name.trim();
64
+ return n.startsWith("@") ? n : `@${n}`;
65
+ }
66
+ function readTree(dir, transform, base = dir) {
67
+ const out = {};
68
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
69
+ const abs = path.join(dir, entry.name);
70
+ const name = RENAME[entry.name] ?? entry.name;
71
+ if (entry.isDirectory()) {
72
+ out[name] = readTree(abs, transform, base);
73
+ } else {
74
+ const rel = path.relative(base, abs);
75
+ out[name] = transform(rel, fs.readFileSync(abs, "utf8"));
76
+ }
77
+ }
78
+ return out;
79
+ }
80
+ function setPath(tree, relPath, content) {
81
+ const parts = relPath.split("/");
82
+ let node = tree;
83
+ for (let i = 0; i < parts.length - 1; i++) {
84
+ const key = parts[i];
85
+ if (typeof node[key] !== "object") node[key] = {};
86
+ node = node[key];
87
+ }
88
+ node[parts[parts.length - 1]] = content;
89
+ }
90
+ function patchJson(tree, relPath, mutate) {
91
+ const parts = relPath.split("/");
92
+ let node = tree;
93
+ for (let i = 0; i < parts.length - 1; i++) node = node[parts[i]];
94
+ const key = parts[parts.length - 1];
95
+ const json = JSON.parse(node[key]);
96
+ mutate(json);
97
+ node[key] = `${JSON.stringify(json, null, 2)}
98
+ `;
99
+ }
100
+ var template_default = createTemplate({
101
+ about: {
102
+ name: package_default.name,
103
+ description: package_default.description
104
+ },
105
+ options: {
106
+ name: z.string().describe("Root project / package name").default("my-app"),
107
+ scope: z.string().describe("npm scope for workspace packages, e.g. @acme (defaults to @<name>)"),
108
+ base: z.enum(["radix", "base"]).describe("shadcn component library base (radix-ui or @base-ui)").default("radix"),
109
+ preset: z.string().describe("shadcn preset: a style name (nova, vega, maia, lyra, mira, luma, sera, rhea) or a code from ui.shadcn.com").default(DEFAULT_PRESET),
110
+ iconLibrary: z.enum(["lucide", "hugeicons", "radix", "tabler"]).describe("Icon library").default("hugeicons"),
111
+ cssVariables: z.boolean().describe("Use CSS variables for theming").default(true),
112
+ rtl: z.boolean().describe("Enable RTL support").default(false),
113
+ pointer: z.boolean().describe("Use pointer cursor on interactive elements").default(false),
114
+ components: z.string().describe("Comma-separated shadcn components to pre-install, e.g. button,card,dialog").default("button,badge"),
115
+ install: z.boolean().describe("Install deps and apply the shadcn theme after scaffolding").default(true)
116
+ },
117
+ // Lazily default the package scope to `@<name>` (prefixing `@` unless the name already has one),
118
+ // so it tracks the project name instead of a fixed value. Falls back to `@app` when no name is set.
119
+ prepare({ options }) {
120
+ return {
121
+ scope: () => options.name ? toScope(options.name) : "@app"
122
+ };
123
+ },
124
+ async produce({ options }) {
125
+ const scope = toScope(options.scope || options.name || "app");
126
+ const files = readTree(
127
+ TEMPLATE_DIR,
128
+ (_rel, content) => content.split("@app").join(scope).split("__PROJECT_NAME__").join(options.name)
129
+ );
130
+ const uiComponentsJson = {
131
+ $schema: "https://ui.shadcn.com/schema.json",
132
+ style: `${options.base}-nova`,
133
+ rsc: false,
134
+ tsx: true,
135
+ tailwind: { config: "", css: "./src/styles/globals.css", baseColor: "neutral", cssVariables: options.cssVariables },
136
+ iconLibrary: options.iconLibrary,
137
+ rtl: options.rtl,
138
+ base: options.base,
139
+ aliases: {
140
+ components: `${scope}/ui/components`,
141
+ ui: `${scope}/ui/components`,
142
+ lib: `${scope}/ui/lib`,
143
+ utils: `${scope}/ui/lib/utils`,
144
+ hooks: `${scope}/ui/hooks`
145
+ }
146
+ };
147
+ const websiteComponentsJson = {
148
+ $schema: "https://ui.shadcn.com/schema.json",
149
+ style: `${options.base}-nova`,
150
+ rsc: false,
151
+ tsx: true,
152
+ tailwind: { config: "", css: "../../packages/ui/src/styles/globals.css", baseColor: "neutral", cssVariables: options.cssVariables },
153
+ iconLibrary: options.iconLibrary,
154
+ rtl: options.rtl,
155
+ aliases: {
156
+ components: "@/components",
157
+ hooks: "@/hooks",
158
+ lib: "@/lib",
159
+ utils: `${scope}/ui/lib/utils`,
160
+ ui: `${scope}/ui/components`
161
+ }
162
+ };
163
+ setPath(files, "packages/ui/components.json", `${JSON.stringify(uiComponentsJson, null, 2)}
164
+ `);
165
+ setPath(files, "apps/website/components.json", `${JSON.stringify(websiteComponentsJson, null, 2)}
166
+ `);
167
+ const iconDeps = ICON_LIBS[options.iconLibrary] ?? ICON_LIBS.hugeicons;
168
+ for (const rel of ["packages/ui/package.json", "apps/website/package.json"]) {
169
+ patchJson(files, rel, (pkg) => {
170
+ pkg.dependencies = { ...pkg.dependencies, ...iconDeps };
171
+ });
172
+ }
173
+ const requested = options.components.split(",").map((c) => c.trim()).filter(Boolean);
174
+ const adds = Array.from(/* @__PURE__ */ new Set(["button", "badge", ...requested]));
175
+ const ui = `${scope}/ui`;
176
+ const initFlags = [
177
+ `--base ${options.base}`,
178
+ `--preset ${options.preset}`,
179
+ options.cssVariables ? "--css-variables" : "--no-css-variables",
180
+ options.rtl ? "--rtl" : "--no-rtl",
181
+ options.pointer ? "--pointer" : "--no-pointer",
182
+ "--no-reinstall",
183
+ "-y",
184
+ "-f"
185
+ ].join(" ");
186
+ const scripts = options.install ? [
187
+ { commands: ["pnpm install --silent"], phase: 0 },
188
+ { commands: [`pnpm --filter ${ui} exec shadcn init ${initFlags}`], phase: 1 },
189
+ { commands: [`pnpm --filter ${ui} exec shadcn add ${adds.join(" ")} -y`], phase: 2 }
190
+ ] : [];
191
+ return {
192
+ files,
193
+ scripts,
194
+ suggestions: [
195
+ `cd into the project and run \`vp run ${scope}/website#dev\` to start the app.`,
196
+ options.install ? `Theme applied via shadcn preset "${options.preset}". Re-theme any time: \`pnpm --filter ${ui} exec shadcn init --preset <code> --no-reinstall -y -f\`.` : `Skipped install. Run \`vp install\`, then \`pnpm --filter ${ui} exec shadcn init --preset ${options.preset} --no-reinstall -y -f\` and \`shadcn add ${adds.join(" ")}\`.`,
197
+ `Add more components later: \`pnpm --filter ${ui} exec shadcn add <name>\`.`
198
+ ]
199
+ };
200
+ }
201
+ });
202
+
203
+ // bin/index.ts
204
+ process.exitCode = await runTemplateCLI(template_default);
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@pauldvlp/vp-react-ts-shadcn",
3
+ "version": "0.3.1",
4
+ "description": "Vite+ monorepo template: one frontend app (website) + a shared shadcn UI package, themeable via shadcn presets.",
5
+ "keywords": [
6
+ "vite-plus-generator",
7
+ "vite-plus",
8
+ "shadcn",
9
+ "react",
10
+ "template"
11
+ ],
12
+ "type": "module",
13
+ "files": [
14
+ "dist",
15
+ "template"
16
+ ],
17
+ "dependencies": {
18
+ "bingo": "^0.9.3",
19
+ "zod": "^3.25.76"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^24",
23
+ "esbuild": "^0.25.0",
24
+ "typescript": "^5"
25
+ },
26
+ "engines": {
27
+ "node": ">=22.18.0"
28
+ },
29
+ "scripts": {
30
+ "build": "esbuild bin/index.ts --bundle --outfile=dist/index.js --format=esm --platform=node --target=node22 --packages=external",
31
+ "dev": "node bin/index.ts"
32
+ },
33
+ "bin": {
34
+ "vp-react-ts-shadcn": "./dist/index.js"
35
+ }
36
+ }
@@ -0,0 +1,82 @@
1
+ # @app/website
2
+
3
+ Monorepo mínimo basado en [Vite+](https://viteplus.dev): **una app frontend** + un **paquete de UI shadcn compartido**.
4
+
5
+ ```
6
+ .
7
+ ├── apps/
8
+ │ └── website # @app/website — app React + Vite+ (Tailwind v4, React Compiler)
9
+ └── packages/
10
+ └── ui # @app/ui — design system shadcn (Tailwind v4)
11
+ ```
12
+
13
+ ## Requisitos
14
+
15
+ - Node `>=22.18.0`
16
+ - pnpm `11.9.0` (se descarga solo vía `devEngines`)
17
+ - CLI `vp` (Vite+) instalado globalmente
18
+
19
+ ## Empezar
20
+
21
+ ```bash
22
+ vp install # si aún no se instaló al crear el proyecto
23
+ vp run @app/website#dev # o: pnpm dev
24
+ ```
25
+
26
+ ## Scripts (raíz)
27
+
28
+ | Script | Qué hace |
29
+ | --------------------- | ------------------------------------------------- |
30
+ | `vp dev` / `pnpm dev` | Levanta `@app/website` en modo dev |
31
+ | `vp check` | Formatea, lintea y type-checkea todo el workspace |
32
+ | `vp run -r build` | Buildea todos los paquetes |
33
+ | `pnpm ready` | `vp check` + build (pensado para CI) |
34
+
35
+ ## Cómo está cableado
36
+
37
+ - **`packages/ui`** publica sus fuentes vía `exports` (sin paso de build):
38
+ - `@app/ui/globals.css` — Tailwind v4 + tokens del tema (generados por shadcn)
39
+ - `@app/ui/components/*` → `src/components/*.tsx` — shadcn escribe en `src/components/ui/`, así que los componentes se importan como `@app/ui/components/ui/<nombre>`
40
+ - `@app/ui/lib/*` — utilidades (`cn`)
41
+ - `@app/ui/hooks/*` — hooks
42
+ - **`apps/website`** consume `@app/ui` como `workspace:*` e importa `@app/ui/globals.css` en `main.tsx`.
43
+ - Tailwind escanea las fuentes de la app y de `ui` vía las directivas `@source` en `globals.css`.
44
+
45
+ ## Iconos
46
+
47
+ La librería de iconos (la que elegiste con `--iconLibrary`) está declarada como dependencia tanto
48
+ en `packages/ui` como en `apps/website`, así que **puedes usar iconos directamente en el website**:
49
+
50
+ ```tsx
51
+ // hugeicons (por defecto)
52
+ import { Home01Icon } from '@hugeicons/core-free-icons'
53
+ import { HugeiconsIcon } from '@hugeicons/react'
54
+
55
+ <HugeiconsIcon icon={Home01Icon} />
56
+ ```
57
+
58
+ ```tsx
59
+ // lucide → import { Home } from 'lucide-react'
60
+ // tabler → import { IconHome } from '@tabler/icons-react'
61
+ // radix → import { HomeIcon } from '@radix-ui/react-icons'
62
+ ```
63
+
64
+ > Se importan **directamente** del paquete de iconos (no re-exportados desde `@app/ui`): los named
65
+ > imports son tree-shakeables y evitan inflar el grafo de módulos.
66
+
67
+ ## Añadir componentes shadcn
68
+
69
+ Desde la raíz, contra el paquete `ui`:
70
+
71
+ ```bash
72
+ pnpm --filter @app/ui exec shadcn add <componente>
73
+ ```
74
+
75
+ ## Cambiar el tema (preset shadcn)
76
+
77
+ El tema (color, fuentes, radius, baseColor) lo define un **preset de shadcn**. Genera uno en
78
+ <https://ui.shadcn.com> (copia el preset code) y reaplícalo:
79
+
80
+ ```bash
81
+ pnpm --filter @app/ui exec shadcn init --preset <code> --no-reinstall -y -f
82
+ ```
@@ -0,0 +1,28 @@
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/settings.json
18
+ !.vscode/extensions.json
19
+ .idea
20
+ .DS_Store
21
+ *.suo
22
+ *.ntvs*
23
+ *.njsproj
24
+ *.sln
25
+ *.sw?
26
+
27
+ # AI agent worktrees
28
+ .claude/worktrees/
@@ -0,0 +1,24 @@
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/extensions.json
18
+ .idea
19
+ .DS_Store
20
+ *.suo
21
+ *.ntvs*
22
+ *.njsproj
23
+ *.sln
24
+ *.sw?
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>website</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@app/website",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vp dev",
8
+ "build": "tsc -b && vp build",
9
+ "lint": "vp lint",
10
+ "preview": "vp preview"
11
+ },
12
+ "dependencies": {
13
+ "@app/ui": "workspace:*",
14
+ "react": "^19.2.7",
15
+ "react-dom": "^19.2.7"
16
+ },
17
+ "devDependencies": {
18
+ "@rolldown/plugin-babel": "catalog:",
19
+ "@tailwindcss/vite": "^4",
20
+ "@types/node": "^24.13.2",
21
+ "@types/react": "^19.2.17",
22
+ "@types/react-dom": "^19.2.3",
23
+ "@vitejs/plugin-react": "^6.0.2",
24
+ "tailwindcss": "^4",
25
+ "typescript": "~6.0.2",
26
+ "vite": "catalog:",
27
+ "vite-plus": "catalog:"
28
+ }
29
+ }
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
2
+ <rect width="32" height="32" rx="7" fill="#0b0b0b" />
3
+ <path d="M9 10l4.5 12h2L20 10h-2.6l-2.9 8.3L11.6 10z" fill="#e3d000" />
4
+ </svg>
@@ -0,0 +1,11 @@
1
+ import { Badge } from '@app/ui/components/ui/badge'
2
+ import { Button } from '@app/ui/components/ui/button'
3
+
4
+ export const App = () => {
5
+ return (
6
+ <main className='h-screen grid justify-items-center content-center gap-4'>
7
+ <Badge variant='secondary'>Vite+ template</Badge>
8
+ <Button>Website</Button>
9
+ </main>
10
+ )
11
+ }
@@ -0,0 +1,10 @@
1
+ import '@app/ui/globals.css'
2
+ import { App } from '@app/website/App'
3
+ import { StrictMode } from 'react'
4
+ import { createRoot } from 'react-dom/client'
5
+
6
+ createRoot(document.getElementById('root')!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>
10
+ )
@@ -0,0 +1,31 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
+ "target": "es2023",
5
+ "lib": ["ES2023", "DOM"],
6
+ "module": "esnext",
7
+ "types": ["vite-plus/client"],
8
+ "skipLibCheck": true,
9
+
10
+ /* Bundler mode */
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": true,
13
+ "verbatimModuleSyntax": true,
14
+ "moduleDetection": "force",
15
+ "noEmit": true,
16
+ "jsx": "react-jsx",
17
+
18
+ /* Linting */
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "erasableSyntaxOnly": true,
22
+ "noFallthroughCasesInSwitch": true,
23
+
24
+ /* Aliases */
25
+ "paths": {
26
+ "@app/website/*": ["./src/*"],
27
+ "@app/ui/*": ["../../packages/ui/src/*"]
28
+ }
29
+ },
30
+ "include": ["src"]
31
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "files": [],
3
+ "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
4
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
+ "target": "es2023",
5
+ "lib": ["ES2023"],
6
+ "types": ["node"],
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "module": "nodenext",
11
+ "allowImportingTsExtensions": true,
12
+ "verbatimModuleSyntax": true,
13
+ "moduleDetection": "force",
14
+ "noEmit": true,
15
+
16
+ /* Linting */
17
+ "noUnusedLocals": true,
18
+ "noUnusedParameters": true,
19
+ "erasableSyntaxOnly": true,
20
+ "noFallthroughCasesInSwitch": true
21
+ },
22
+ "include": ["vite.config.ts"]
23
+ }
@@ -0,0 +1,38 @@
1
+ import babel from '@rolldown/plugin-babel'
2
+ import tailwindcss from '@tailwindcss/vite'
3
+ import react, { reactCompilerPreset } from '@vitejs/plugin-react'
4
+ import { fileURLToPath, URL } from 'url'
5
+ import { defineConfig, lazyPlugins } from 'vite-plus'
6
+
7
+ // https://vite.dev/config/
8
+ export default defineConfig({
9
+ lint: {
10
+ plugins: ['react', 'typescript', 'oxc'],
11
+ rules: {
12
+ 'react/rules-of-hooks': 'error',
13
+ 'react/only-export-components': [
14
+ 'warn',
15
+ {
16
+ allowConstantExport: true
17
+ }
18
+ ],
19
+ 'vite-plus/prefer-vite-plus-imports': 'error'
20
+ },
21
+ options: {
22
+ typeAware: true,
23
+ typeCheck: true
24
+ },
25
+ jsPlugins: [
26
+ {
27
+ name: 'vite-plus',
28
+ specifier: 'vite-plus/oxlint-plugin'
29
+ }
30
+ ]
31
+ },
32
+ plugins: lazyPlugins(() => [react(), tailwindcss(), babel({ presets: [reactCompilerPreset()] })]),
33
+ resolve: {
34
+ alias: {
35
+ '@app/website': fileURLToPath(new URL('./src', import.meta.url))
36
+ }
37
+ }
38
+ })
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vp run @app/website#dev",
8
+ "build": "vp run -r build",
9
+ "check": "vp check",
10
+ "ready": "vp check && vp run -r build"
11
+ },
12
+ "devDependencies": {
13
+ "babel-plugin-react-compiler": "catalog:",
14
+ "vite-plus": "catalog:"
15
+ },
16
+ "devEngines": {
17
+ "packageManager": {
18
+ "name": "pnpm",
19
+ "version": "11.9.0",
20
+ "onFail": "download"
21
+ }
22
+ },
23
+ "engines": {
24
+ "node": ">=22.18.0"
25
+ }
26
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@app/ui",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ "./globals.css": "./src/styles/globals.css",
8
+ "./lib/*": "./src/lib/*.ts",
9
+ "./components/*": "./src/components/*.tsx",
10
+ "./hooks/*": "./src/hooks/*.ts"
11
+ },
12
+ "scripts": {
13
+ "lint": "vp lint",
14
+ "check": "vp check"
15
+ },
16
+ "dependencies": {
17
+ "@fontsource-variable/outfit": "^5.2.8",
18
+ "@fontsource-variable/space-grotesk": "^5.2.10",
19
+ "class-variance-authority": "^0.7.1",
20
+ "clsx": "^2.1.1",
21
+ "shadcn": "^4.11.0",
22
+ "tailwind-merge": "^3.6.0",
23
+ "tw-animate-css": "^1.4.0"
24
+ },
25
+ "devDependencies": {
26
+ "@tailwindcss/vite": "^4",
27
+ "@types/react": "^19.2.17",
28
+ "@types/react-dom": "^19.2.3",
29
+ "@vitejs/plugin-react": "^6.0.2",
30
+ "react": "^19.2.7",
31
+ "react-dom": "^19.2.7",
32
+ "tailwindcss": "^4",
33
+ "typescript": "~6.0.2",
34
+ "vite": "catalog:",
35
+ "vite-plus": "catalog:"
36
+ },
37
+ "peerDependencies": {
38
+ "react": "^19",
39
+ "react-dom": "^19"
40
+ }
41
+ }
File without changes
File without changes
File without changes
@@ -0,0 +1,6 @@
1
+ @import 'tailwindcss';
2
+ @import 'tw-animate-css';
3
+
4
+ @custom-variant dark (&:is(.dark *));
5
+ @source "../../../../apps/**/*.{ts,tsx}";
6
+ @source "../**/*.{ts,tsx}";
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "types": ["vite-plus/client"],
10
+ "skipLibCheck": true,
11
+ "strict": true,
12
+ "verbatimModuleSyntax": true,
13
+ "allowImportingTsExtensions": true,
14
+ "noEmit": true,
15
+ "paths": {
16
+ "@app/ui/*": ["./src/*"]
17
+ }
18
+ },
19
+ "include": ["src"],
20
+ "exclude": ["node_modules", "dist"]
21
+ }
@@ -0,0 +1,31 @@
1
+ import tailwindcss from '@tailwindcss/vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import { defineConfig, lazyPlugins } from 'vite-plus'
4
+
5
+ // https://vite.dev/config/
6
+ export default defineConfig({
7
+ lint: {
8
+ plugins: ['react', 'typescript', 'oxc'],
9
+ rules: {
10
+ 'react/rules-of-hooks': 'error',
11
+ 'react/only-export-components': [
12
+ 'warn',
13
+ {
14
+ allowConstantExport: true
15
+ }
16
+ ],
17
+ 'vite-plus/prefer-vite-plus-imports': 'error'
18
+ },
19
+ options: {
20
+ typeAware: true,
21
+ typeCheck: true
22
+ },
23
+ jsPlugins: [
24
+ {
25
+ name: 'vite-plus',
26
+ specifier: 'vite-plus/oxlint-plugin'
27
+ }
28
+ ]
29
+ },
30
+ plugins: lazyPlugins(() => [react(), tailwindcss()])
31
+ })
@@ -0,0 +1,28 @@
1
+ packages:
2
+ - apps/*
3
+ - packages/*
4
+
5
+ catalogMode: prefer
6
+
7
+ catalog:
8
+ '@types/node': ^24
9
+ typescript: ^5
10
+ vite: npm:@voidzero-dev/vite-plus-core@latest
11
+ vitest: 4.1.9
12
+ vite-plus: ^0.2.1
13
+ babel-plugin-react-compiler: ^1.0.0
14
+ '@rolldown/plugin-babel': ^0.2.3
15
+ overrides:
16
+ vite: 'catalog:'
17
+ vitest: 'catalog:'
18
+ peerDependencyRules:
19
+ allowAny:
20
+ - vite
21
+ - vitest
22
+ allowedVersions:
23
+ vite: '*'
24
+ vitest: '*'
25
+ minimumReleaseAgeExclude:
26
+ - '@hugeicons/react@1.1.9'
27
+ allowBuilds:
28
+ '@swc/core': set this to true or false
@@ -0,0 +1,9 @@
1
+ {
2
+ "compilerOptions": {
3
+ "noEmit": true,
4
+ "module": "nodenext",
5
+ "moduleResolution": "nodenext",
6
+ "allowImportingTsExtensions": true,
7
+ "esModuleInterop": true
8
+ }
9
+ }
@@ -0,0 +1,24 @@
1
+ import { defaultExclude, defineConfig } from 'vite-plus'
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ // vite-plus's defaultExclude is only node_modules + .git (no dist), so a root-level
6
+ // `vp test` would otherwise scan compiled build output. Keep build outputs out of the scan.
7
+ exclude: [...defaultExclude, '**/dist/**', '**/build/**']
8
+ },
9
+ fmt: {
10
+ jsxSingleQuote: true,
11
+ printWidth: 160,
12
+ semi: false,
13
+ singleQuote: true,
14
+ trailingComma: 'none'
15
+ },
16
+ lint: {
17
+ jsPlugins: [{ name: 'vite-plus', specifier: 'vite-plus/oxlint-plugin' }],
18
+ rules: { 'vite-plus/prefer-vite-plus-imports': 'error' },
19
+ options: { typeAware: true, typeCheck: true }
20
+ },
21
+ run: {
22
+ cache: true
23
+ }
24
+ })