@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 +79 -0
- package/dist/index.js +204 -0
- package/package.json +36 -0
- package/template/README.md +82 -0
- package/template/_gitignore +28 -0
- package/template/apps/website/_gitignore +24 -0
- package/template/apps/website/index.html +13 -0
- package/template/apps/website/package.json +29 -0
- package/template/apps/website/public/favicon.svg +4 -0
- package/template/apps/website/src/App.tsx +11 -0
- package/template/apps/website/src/main.tsx +10 -0
- package/template/apps/website/tsconfig.app.json +31 -0
- package/template/apps/website/tsconfig.json +4 -0
- package/template/apps/website/tsconfig.node.json +23 -0
- package/template/apps/website/vite.config.ts +38 -0
- package/template/package.json +26 -0
- package/template/packages/ui/package.json +41 -0
- package/template/packages/ui/src/components/.gitkeep +0 -0
- package/template/packages/ui/src/hooks/.gitkeep +0 -0
- package/template/packages/ui/src/lib/.gitkeep +0 -0
- package/template/packages/ui/src/styles/globals.css +6 -0
- package/template/packages/ui/tsconfig.json +21 -0
- package/template/packages/ui/vite.config.ts +31 -0
- package/template/pnpm-workspace.yaml +28 -0
- package/template/tsconfig.json +9 -0
- package/template/vite.config.ts +24 -0
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,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,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,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,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,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
|
+
})
|