create-quadrokit 0.1.0 → 0.2.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.
Files changed (91) hide show
  1. package/README.md +10 -6
  2. package/biome.monorepo.json +67 -0
  3. package/dist/index.mjs +361 -0
  4. package/package.json +10 -4
  5. package/templates/README.md +39 -0
  6. package/templates/admin-shell/.cursor/rules/commitlint-conventional.mdc +47 -0
  7. package/templates/admin-shell/.env.example +2 -0
  8. package/templates/admin-shell/biome.json +12 -0
  9. package/templates/admin-shell/index.html +12 -0
  10. package/templates/admin-shell/package.json +46 -0
  11. package/templates/admin-shell/postcss.config.js +6 -0
  12. package/templates/admin-shell/src/components/AppShell.tsx +68 -0
  13. package/templates/admin-shell/src/i18n.ts +12 -0
  14. package/templates/admin-shell/src/lib/quadro-client.ts +4 -0
  15. package/templates/admin-shell/src/locales/en.json +15 -0
  16. package/templates/admin-shell/src/main.tsx +15 -0
  17. package/templates/admin-shell/src/pages/AgenciesPage.tsx +69 -0
  18. package/templates/admin-shell/src/pages/HomePage.tsx +67 -0
  19. package/templates/admin-shell/src/router.tsx +15 -0
  20. package/templates/admin-shell/src/stores/useSidebarHint.ts +12 -0
  21. package/templates/admin-shell/src/vite-env.d.ts +9 -0
  22. package/templates/admin-shell/tailwind.config.ts +7 -0
  23. package/templates/admin-shell/tsconfig.app.json +16 -0
  24. package/templates/admin-shell/tsconfig.json +4 -0
  25. package/templates/admin-shell/tsconfig.node.json +10 -0
  26. package/templates/admin-shell/vite.config.ts +25 -0
  27. package/templates/dashboard/.cursor/rules/commitlint-conventional.mdc +47 -0
  28. package/templates/dashboard/.env.example +2 -0
  29. package/templates/dashboard/biome.json +12 -0
  30. package/templates/dashboard/index.html +12 -0
  31. package/templates/dashboard/package.json +46 -0
  32. package/templates/dashboard/postcss.config.js +6 -0
  33. package/templates/dashboard/src/components/AppShell.tsx +44 -0
  34. package/templates/dashboard/src/i18n.ts +12 -0
  35. package/templates/dashboard/src/lib/quadro-client.ts +4 -0
  36. package/templates/dashboard/src/locales/en.json +15 -0
  37. package/templates/dashboard/src/main.tsx +15 -0
  38. package/templates/dashboard/src/pages/AgenciesPage.tsx +69 -0
  39. package/templates/dashboard/src/pages/HomePage.tsx +67 -0
  40. package/templates/dashboard/src/router.tsx +15 -0
  41. package/templates/dashboard/src/stores/useSidebarHint.ts +12 -0
  42. package/templates/dashboard/src/vite-env.d.ts +9 -0
  43. package/templates/dashboard/tailwind.config.ts +7 -0
  44. package/templates/dashboard/tsconfig.app.json +16 -0
  45. package/templates/dashboard/tsconfig.json +4 -0
  46. package/templates/dashboard/tsconfig.node.json +10 -0
  47. package/templates/dashboard/vite.config.ts +25 -0
  48. package/templates/ecommerce/.cursor/rules/commitlint-conventional.mdc +47 -0
  49. package/templates/ecommerce/.env.example +2 -0
  50. package/templates/ecommerce/biome.json +12 -0
  51. package/templates/ecommerce/index.html +12 -0
  52. package/templates/ecommerce/package.json +46 -0
  53. package/templates/ecommerce/postcss.config.js +6 -0
  54. package/templates/ecommerce/src/components/AppShell.tsx +44 -0
  55. package/templates/ecommerce/src/i18n.ts +12 -0
  56. package/templates/ecommerce/src/lib/quadro-client.ts +4 -0
  57. package/templates/ecommerce/src/locales/en.json +20 -0
  58. package/templates/ecommerce/src/main.tsx +15 -0
  59. package/templates/ecommerce/src/pages/AgenciesPage.tsx +69 -0
  60. package/templates/ecommerce/src/pages/HomePage.tsx +52 -0
  61. package/templates/ecommerce/src/router.tsx +15 -0
  62. package/templates/ecommerce/src/stores/useSidebarHint.ts +12 -0
  63. package/templates/ecommerce/src/vite-env.d.ts +9 -0
  64. package/templates/ecommerce/tailwind.config.ts +7 -0
  65. package/templates/ecommerce/tsconfig.app.json +16 -0
  66. package/templates/ecommerce/tsconfig.json +4 -0
  67. package/templates/ecommerce/tsconfig.node.json +10 -0
  68. package/templates/ecommerce/vite.config.ts +25 -0
  69. package/templates/website/.cursor/rules/commitlint-conventional.mdc +47 -0
  70. package/templates/website/.env.example +2 -0
  71. package/templates/website/biome.json +12 -0
  72. package/templates/website/index.html +12 -0
  73. package/templates/website/package.json +46 -0
  74. package/templates/website/postcss.config.js +6 -0
  75. package/templates/website/src/components/AppShell.tsx +44 -0
  76. package/templates/website/src/i18n.ts +12 -0
  77. package/templates/website/src/lib/quadro-client.ts +4 -0
  78. package/templates/website/src/locales/en.json +21 -0
  79. package/templates/website/src/main.tsx +15 -0
  80. package/templates/website/src/pages/AgenciesPage.tsx +69 -0
  81. package/templates/website/src/pages/HomePage.tsx +83 -0
  82. package/templates/website/src/router.tsx +15 -0
  83. package/templates/website/src/stores/useSidebarHint.ts +12 -0
  84. package/templates/website/src/vite-env.d.ts +9 -0
  85. package/templates/website/tailwind.config.ts +7 -0
  86. package/templates/website/tsconfig.app.json +16 -0
  87. package/templates/website/tsconfig.json +4 -0
  88. package/templates/website/tsconfig.node.json +10 -0
  89. package/templates/website/vite.config.ts +25 -0
  90. package/src/index.ts +0 -237
  91. package/tsconfig.json +0 -9
package/README.md CHANGED
@@ -16,16 +16,20 @@ bun create-quadrokit/src/index.ts --template dashboard --dir /path/to/my-app
16
16
  | `--template <name>` | `dashboard` \| `website` \| `ecommerce` \| `admin-shell` |
17
17
  | `--dir <path>` | Target directory (created; must be empty) |
18
18
  | `--keep-workspace` | Keep `workspace:*` in `package.json` (for development inside the QuadroKit monorepo only) |
19
+ | `--git` / `--no-git` | Initialize a git repo after scaffolding (skip the prompt) |
20
+ | `--install` / `--no-install` | Run `bun install` or `npm install` after scaffolding (skip the prompt) |
19
21
 
20
- Interactive mode: run without `--template` / `--dir` to be prompted.
22
+ On start, the CLI prints a short **prerequisites** check (Node.js, Bun, Git). Node 18+ is required; Bun is optional (falls back to npm for installs). Git is only needed if you choose to init a repository.
23
+
24
+ Interactive mode: run without `--template` / `--dir` to be prompted for those; you are then asked about **git** and **install** unless overridden with the flags above.
21
25
 
22
26
  ## What it does
23
27
 
24
- 1. Copies the chosen template from `packages/templates/<name>` (skips `node_modules`, `dist`).
25
- 2. Copies `packages/sample-client/generated` `<dir>/.quadrokit/generated`.
26
- 3. Rewrites `src/lib/quadro-client.ts` to import from `../../.quadrokit/generated/client.gen.js`.
27
- 4. Removes `@quadrokit/sample-client` and rewrites other `workspace:*` deps to `^0.1.0` (unless `--keep-workspace`).
28
- 5. Writes `QUADROKIT.md` with proxy and `quadrokit-client generate` instructions.
28
+ 1. Copies the chosen template from `create-quadrokit/templates/<name>` when installed from npm, or `packages/templates/<name>` when you run the CLI from the monorepo (skips `node_modules`, `dist`, `.quadrokit`, lockfiles).
29
+ 2. Rewrites `src/lib/quadro-client.ts` to import from `../../.quadrokit/generated/client.gen.js`.
30
+ 3. Removes `@quadrokit/sample-client` and rewrites other `workspace:*` deps to published semver (`@quadrokit/client` → `^0.2.1`, other `@quadrokit/*` → `^0.2.0`) unless `--keep-workspace`.
31
+ 4. Writes `QUADROKIT.md` with proxy and `quadrokit:generate` instructions. Does **not** copy `.quadrokit/generated` run `bun run quadrokit:generate` after install.
32
+ 5. Optionally runs `git init` and installs dependencies (`bun` if available, otherwise `npm`).
29
33
 
30
34
  ## Published usage (future)
31
35
 
@@ -0,0 +1,67 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/2.4.10/schema.json",
3
+ "vcs": {
4
+ "enabled": true,
5
+ "clientKind": "git",
6
+ "useIgnoreFile": true
7
+ },
8
+ "files": {
9
+ "includes": [
10
+ "**",
11
+ "!**/node_modules",
12
+ "!**/dist",
13
+ "!**/.quadrokit",
14
+ "!**/packages/sample-client/generated",
15
+ "!**/*.tsbuildinfo"
16
+ ]
17
+ },
18
+ "formatter": {
19
+ "indentStyle": "space",
20
+ "indentWidth": 2,
21
+ "lineWidth": 100
22
+ },
23
+ "linter": {
24
+ "enabled": true,
25
+ "rules": {
26
+ "recommended": true,
27
+ "correctness": {
28
+ "noUnusedImports": {
29
+ "level": "error"
30
+ }
31
+ },
32
+ "style": {
33
+ "noNonNullAssertion": "off"
34
+ },
35
+ "suspicious": {
36
+ "noUnknownAtRules": {
37
+ "level": "off"
38
+ },
39
+ "noExplicitAny": {
40
+ "level": "warn"
41
+ }
42
+ },
43
+ "nursery": {
44
+ "useSortedClasses": {
45
+ "level": "off"
46
+ }
47
+ }
48
+ }
49
+ },
50
+ "javascript": {
51
+ "formatter": {
52
+ "quoteStyle": "single",
53
+ "trailingCommas": "es5",
54
+ "semicolons": "asNeeded"
55
+ }
56
+ },
57
+ "html": {
58
+ "parser": {
59
+ "interpolation": true
60
+ }
61
+ },
62
+ "css": {
63
+ "parser": {
64
+ "tailwindDirectives": true
65
+ }
66
+ }
67
+ }
package/dist/index.mjs ADDED
@@ -0,0 +1,361 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from 'node:child_process';
3
+ import { cp, mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import prompts from 'prompts';
7
+ const MIN_NODE_MAJOR = 18;
8
+ function cmdVersion(cmd, args) {
9
+ const r = spawnSync(cmd, args, { encoding: 'utf8', shell: false });
10
+ if (r.error || r.status !== 0)
11
+ return null;
12
+ const line = r.stdout.trim().split('\n')[0];
13
+ return line || null;
14
+ }
15
+ function nodeMajor() {
16
+ const m = /^v?(\d+)/.exec(process.version);
17
+ return m ? Number(m[1]) : 0;
18
+ }
19
+ function checkPrereqs() {
20
+ const nodeVersion = process.version;
21
+ const nodeOk = nodeMajor() >= MIN_NODE_MAJOR;
22
+ const bunVersion = cmdVersion('bun', ['--version']);
23
+ const gitVersion = cmdVersion('git', ['--version']);
24
+ return {
25
+ nodeOk,
26
+ nodeVersion,
27
+ bunOk: bunVersion !== null,
28
+ bunVersion,
29
+ gitOk: gitVersion !== null,
30
+ gitVersion,
31
+ };
32
+ }
33
+ function printWelcome() {
34
+ console.log(`
35
+ ${'✨'.repeat(2)} ${'\u001b[1mcreate-quadrokit\u001b[0m'} ${'✨'.repeat(2)}
36
+ ${'🚀'.repeat(2)} Scaffold a QuadroKit Vite + React app for 4D REST
37
+ `);
38
+ }
39
+ function printPrereqReport(p) {
40
+ console.log(' Prerequisites');
41
+ const line = (ok, emoji, label, detail) => {
42
+ const mark = ok ? '✅' : '⚠️ ';
43
+ console.log(` ${mark} ${emoji} ${label}: ${detail}`);
44
+ };
45
+ line(p.nodeOk, '📗', 'Node.js', p.nodeOk ? p.nodeVersion : `${p.nodeVersion} (need >= ${MIN_NODE_MAJOR})`);
46
+ line(p.bunOk, '🥟', 'Bun', p.bunVersion ?? 'not found (you can use npm install instead)');
47
+ line(p.gitOk, '📌', 'Git', p.gitVersion ?? 'not found (needed only to init a repository)');
48
+ console.log('');
49
+ }
50
+ async function runInDir(cwd, cmd, args) {
51
+ const shell = process.platform === 'win32';
52
+ const r = spawnSync(cmd, args, { cwd, stdio: 'inherit', shell });
53
+ return r.status === 0;
54
+ }
55
+ const TEMPLATES = ['dashboard', 'website', 'ecommerce', 'admin-shell'];
56
+ /** Directory of the create-quadrokit package (works when installed under node_modules). */
57
+ function packageRoot() {
58
+ return path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
59
+ }
60
+ async function resolveTemplateDir(template) {
61
+ const pkg = packageRoot();
62
+ const bundled = path.join(pkg, 'templates', template);
63
+ if (await pathExists(bundled)) {
64
+ return bundled;
65
+ }
66
+ const monorepo = path.join(pkg, '..', 'packages', 'templates', template);
67
+ if (await pathExists(monorepo)) {
68
+ return monorepo;
69
+ }
70
+ return null;
71
+ }
72
+ async function resolveBiomeJsonPath() {
73
+ const pkg = packageRoot();
74
+ const bundled = path.join(pkg, 'biome.monorepo.json');
75
+ if (await pathExists(bundled)) {
76
+ return bundled;
77
+ }
78
+ const monorepo = path.join(pkg, '..', 'biome.json');
79
+ if (await pathExists(monorepo)) {
80
+ return monorepo;
81
+ }
82
+ return null;
83
+ }
84
+ function parseArgs(argv) {
85
+ let template;
86
+ let dir;
87
+ let keepWorkspace = false;
88
+ /** `undefined` = prompt later */
89
+ let initGit;
90
+ let installDeps;
91
+ for (let i = 0; i < argv.length; i++) {
92
+ const a = argv[i];
93
+ if (a === '--template' && argv[i + 1]) {
94
+ template = argv[++i];
95
+ }
96
+ else if (a === '--dir' && argv[i + 1]) {
97
+ dir = argv[++i];
98
+ }
99
+ else if (a === '--keep-workspace') {
100
+ keepWorkspace = true;
101
+ }
102
+ else if (a === '--git') {
103
+ initGit = true;
104
+ }
105
+ else if (a === '--no-git') {
106
+ initGit = false;
107
+ }
108
+ else if (a === '--install') {
109
+ installDeps = true;
110
+ }
111
+ else if (a === '--no-install') {
112
+ installDeps = false;
113
+ }
114
+ }
115
+ return { template, dir, keepWorkspace, initGit, installDeps };
116
+ }
117
+ async function pathExists(p) {
118
+ try {
119
+ await stat(p);
120
+ return true;
121
+ }
122
+ catch {
123
+ return false;
124
+ }
125
+ }
126
+ const SKIP_TEMPLATE_NAMES = new Set([
127
+ 'node_modules',
128
+ 'dist',
129
+ '.turbo',
130
+ '.quadrokit',
131
+ 'bun.lock',
132
+ 'bun.lockb',
133
+ 'package-lock.json',
134
+ 'pnpm-lock.yaml',
135
+ 'yarn.lock',
136
+ ]);
137
+ async function copyTemplate(src, dest) {
138
+ await mkdir(dest, { recursive: true });
139
+ const entries = await readdir(src, { withFileTypes: true });
140
+ for (const e of entries) {
141
+ if (SKIP_TEMPLATE_NAMES.has(e.name)) {
142
+ continue;
143
+ }
144
+ const from = path.join(src, e.name);
145
+ const to = path.join(dest, e.name);
146
+ if (e.isDirectory()) {
147
+ await copyTemplate(from, to);
148
+ }
149
+ else {
150
+ await cp(from, to);
151
+ }
152
+ }
153
+ }
154
+ function rewriteWorkspaceDeps(pkg) {
155
+ for (const key of ['dependencies', 'devDependencies']) {
156
+ const deps = pkg[key];
157
+ if (!deps) {
158
+ continue;
159
+ }
160
+ for (const name of Object.keys(deps)) {
161
+ if (deps[name] === 'workspace:*') {
162
+ deps[name] = name === '@quadrokit/client' ? '^0.2.1' : '^0.2.0';
163
+ }
164
+ }
165
+ }
166
+ }
167
+ async function patchPackageJson(dest, projectName, keepWorkspace) {
168
+ const pkgPath = path.join(dest, 'package.json');
169
+ const raw = await readFile(pkgPath, 'utf8');
170
+ const pkg = JSON.parse(raw);
171
+ pkg.name = projectName;
172
+ if (pkg.dependencies) {
173
+ pkg.dependencies = Object.fromEntries(Object.entries(pkg.dependencies).filter(([name]) => name !== '@quadrokit/sample-client'));
174
+ }
175
+ if (!keepWorkspace) {
176
+ rewriteWorkspaceDeps(pkg);
177
+ }
178
+ await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8');
179
+ }
180
+ /** Standalone Biome config for projects outside the monorepo (no `extends` to repo root). */
181
+ async function writeStandaloneBiome(dest, keepWorkspace) {
182
+ if (keepWorkspace) {
183
+ return;
184
+ }
185
+ const rootBiomePath = await resolveBiomeJsonPath();
186
+ if (!rootBiomePath) {
187
+ console.warn('Warning: biome.json not found — skipped writing standalone biome.json for the new project.');
188
+ return;
189
+ }
190
+ const rootCfg = JSON.parse(await readFile(rootBiomePath, 'utf8'));
191
+ const { extends: _ext, ...rest } = rootCfg;
192
+ const projectBiome = {
193
+ ...rest,
194
+ vcs: {
195
+ enabled: true,
196
+ clientKind: 'git',
197
+ useIgnoreFile: false,
198
+ },
199
+ files: {
200
+ includes: ['**', '!**/node_modules', '!**/dist', '!**/.quadrokit'],
201
+ },
202
+ };
203
+ await writeFile(path.join(dest, 'biome.json'), `${JSON.stringify(projectBiome, null, 2)}\n`, 'utf8');
204
+ }
205
+ async function writeQuadroClientImport(dest) {
206
+ const p = path.join(dest, 'src', 'lib', 'quadro-client.ts');
207
+ const body = `import { createClient } from '../../.quadrokit/generated/client.gen.mjs';
208
+
209
+ /** Same-origin \`/rest\` in dev (Vite proxy) and production (reverse proxy). */
210
+ export const quadro = createClient({ baseURL: '/rest' });
211
+ `;
212
+ await writeFile(p, body, 'utf8');
213
+ }
214
+ async function writeReadme(dest, template) {
215
+ const text = `# ${path.basename(dest)}
216
+
217
+ Created with **create-quadrokit** (template: \`${template}\`).
218
+
219
+ ## Next steps
220
+
221
+ 1. \`cd ${path.basename(dest)}\` and install dependencies (\`bun install\` or \`npm install\`).
222
+ 2. Generate the typed REST client (required before \`dev\` / \`build\`):
223
+
224
+ \`\`\`bash
225
+ bun run quadrokit:generate
226
+ \`\`\`
227
+
228
+ This uses \`assets/catalog.placeholder.json\` by default (demo catalog from QuadroKit). Point at your live 4D catalog:
229
+
230
+ \`\`\`bash
231
+ QUADROKIT_CATALOG_URL='http://localhost:7080/rest/\\$catalog' QUADROKIT_CATALOG_TOKEN='…' bun run quadrokit:generate
232
+ \`\`\`
233
+
234
+ 3. Copy \`.env.example\` to \`.env\` and set \`VITE_4D_ORIGIN\` to your 4D web server.
235
+ 4. \`bun run dev\` — the dev server proxies \`/rest\` to \`VITE_4D_ORIGIN\` so **4DSID_** cookies stay same-origin.
236
+
237
+ Production: serve the SPA and reverse-proxy \`/rest\` to 4D on the **same host** as the UI.
238
+ `;
239
+ await writeFile(path.join(dest, 'QUADROKIT.md'), text, 'utf8');
240
+ }
241
+ async function main() {
242
+ const argv = process.argv.slice(2);
243
+ const { template: tArg, dir: dirArg, keepWorkspace, initGit: gitFlag, installDeps: installFlag, } = parseArgs(argv);
244
+ printWelcome();
245
+ const prereqs = checkPrereqs();
246
+ printPrereqReport(prereqs);
247
+ if (!prereqs.nodeOk) {
248
+ console.error(` ❌ Node.js ${MIN_NODE_MAJOR}+ is required.`);
249
+ process.exit(1);
250
+ }
251
+ const template = tArg && TEMPLATES.includes(tArg)
252
+ ? tArg
253
+ : (await prompts({
254
+ type: 'select',
255
+ name: 'template',
256
+ message: 'Template',
257
+ choices: TEMPLATES.map((value) => ({ title: value, value })),
258
+ })).template;
259
+ if (!template) {
260
+ process.exit(1);
261
+ }
262
+ const dirAns = dirArg ??
263
+ (await prompts({
264
+ type: 'text',
265
+ name: 'dir',
266
+ message: 'Project directory',
267
+ initial: `quadro-${template}`,
268
+ })).dir;
269
+ if (!dirAns || typeof dirAns !== 'string') {
270
+ process.exit(1);
271
+ }
272
+ const dest = path.resolve(process.cwd(), dirAns);
273
+ if (await pathExists(dest)) {
274
+ const files = await readdir(dest);
275
+ if (files.length > 0) {
276
+ console.error(`Directory not empty: ${dest}`);
277
+ process.exit(1);
278
+ }
279
+ }
280
+ const src = await resolveTemplateDir(template);
281
+ if (!src) {
282
+ console.error(`Template not found: ${template} (expected under create-quadrokit/templates or packages/templates)`);
283
+ process.exit(1);
284
+ }
285
+ await copyTemplate(src, dest);
286
+ await writeStandaloneBiome(dest, keepWorkspace);
287
+ await patchPackageJson(dest, path
288
+ .basename(dest)
289
+ .replace(/[^a-z0-9-]/gi, '-')
290
+ .toLowerCase() || 'quadro-app', keepWorkspace);
291
+ await writeQuadroClientImport(dest);
292
+ await writeReadme(dest, template);
293
+ console.log(`\n 🎉 Project files are ready at ${'\u001b[1m'}${dest}${'\u001b[0m'}\n`);
294
+ let initGit = gitFlag;
295
+ if (initGit === undefined) {
296
+ const g = await prompts({
297
+ type: 'confirm',
298
+ name: 'v',
299
+ message: 'Initialize a git repository here?',
300
+ initial: true,
301
+ });
302
+ if (typeof g.v !== 'boolean') {
303
+ process.exit(1);
304
+ }
305
+ initGit = g.v;
306
+ }
307
+ if (initGit) {
308
+ if (!prereqs.gitOk) {
309
+ console.log(' ⚠️ Skipping git init — Git is not available on your PATH.\n');
310
+ }
311
+ else {
312
+ const ok = await runInDir(dest, 'git', ['init']);
313
+ if (ok) {
314
+ console.log(' ✅ Git repository initialized.\n');
315
+ }
316
+ else {
317
+ console.log(' ⚠️ `git init` failed — you can run it manually later.\n');
318
+ }
319
+ }
320
+ }
321
+ let installDeps = installFlag;
322
+ if (installDeps === undefined) {
323
+ const ins = await prompts({
324
+ type: 'confirm',
325
+ name: 'v',
326
+ message: 'Install dependencies now?',
327
+ initial: true,
328
+ });
329
+ if (typeof ins.v !== 'boolean') {
330
+ process.exit(1);
331
+ }
332
+ installDeps = ins.v;
333
+ }
334
+ if (installDeps) {
335
+ const useBun = prereqs.bunOk;
336
+ const cmd = useBun ? 'bun' : 'npm';
337
+ const args = useBun ? ['install'] : ['install'];
338
+ console.log(` 📦 Running ${cmd} ${args.join(' ')} …\n`);
339
+ const ok = await runInDir(dest, cmd, args);
340
+ if (ok) {
341
+ console.log(` ✅ Dependencies installed with ${cmd}.\n`);
342
+ }
343
+ else {
344
+ console.log(` ⚠️ Install failed — run \`${useBun ? 'bun install' : 'npm install'}\` inside the project.\n`);
345
+ }
346
+ }
347
+ const pm = prereqs.bunOk ? 'bun' : 'npm';
348
+ const runDev = prereqs.bunOk ? 'bun run dev' : 'npm run dev';
349
+ const installLine = installDeps ? ` → ${runDev}` : ` → ${pm} install\n → ${runDev}`;
350
+ console.log(` 🚀🚀 Next steps
351
+ → cd ${path.basename(dest)}
352
+ ${installLine}
353
+ → Copy .env.example → .env and set VITE_4D_ORIGIN
354
+
355
+ Happy building! ✨
356
+ `);
357
+ }
358
+ main().catch((e) => {
359
+ console.error(e);
360
+ process.exit(1);
361
+ });
package/package.json CHANGED
@@ -1,17 +1,23 @@
1
1
  {
2
2
  "name": "create-quadrokit",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Scaffold a QuadroKit Vite + React app from a template",
5
5
  "type": "module",
6
- "bin": "./src/index.ts",
6
+ "bin": "./dist/index.mjs",
7
+ "files": [
8
+ "dist",
9
+ "templates",
10
+ "biome.monorepo.json"
11
+ ],
7
12
  "scripts": {
8
- "typecheck": "tsc -p tsconfig.json --noEmit"
13
+ "build": "tsc -p tsconfig.build.json && bun ../scripts/rename-dist-js-to-mjs.ts dist",
14
+ "typecheck": "tsc -p tsconfig.json --noEmit",
15
+ "prepublishOnly": "bun run scripts/sync-for-publish.ts && bun run build"
9
16
  },
10
17
  "dependencies": {
11
18
  "prompts": "^2.4.2"
12
19
  },
13
20
  "devDependencies": {
14
- "@types/bun": "^1.3.11",
15
21
  "@types/node": "^25.5.0",
16
22
  "@types/prompts": "^2.4.9",
17
23
  "typescript": "^5.9.3"
@@ -0,0 +1,39 @@
1
+ # QuadroKit templates
2
+
3
+ Vite + React 19 + React Router 7 + **Tailwind 3.4** + **i18next** + **react-hook-form** + **zod** + **zustand** (minimal example).
4
+
5
+ | Template | Use case |
6
+ |----------|----------|
7
+ | `dashboard` | Default shell, agencies sample page, form demo |
8
+ | `website` | Marketing-style hero + same stack |
9
+ | `ecommerce` | Product-card style placeholder |
10
+ | `admin-shell` | Sidebar + main content layout |
11
+
12
+ ## Monorepo
13
+
14
+ ```bash
15
+ cd packages/templates/dashboard # or website / ecommerce / admin-shell
16
+ bun install # from repo root once
17
+ bun run dev
18
+ ```
19
+
20
+ Copy [`.env.example`](dashboard/.env.example) to `.env` and set `VITE_4D_ORIGIN`.
21
+
22
+ ## Biome (per template)
23
+
24
+ Each template has a [`biome.json`](dashboard/biome.json) with `"root": false` and `"extends": "//"` (Biome v2 monorepo pattern: inherit repo root, scope files to that template). `vcs.useIgnoreFile` is off so Biome does not require a `.gitignore` next to the template.
25
+
26
+ | Script | Purpose |
27
+ |--------|---------|
28
+ | `bun run format` | `biome format --write .` |
29
+ | `bun run lint` | `biome lint .` (read-only) |
30
+ | `bun run lint:fix` | `biome lint --write .` |
31
+ | `bun run check` / `biome:check` | `biome check .` (read-only) |
32
+ | `bun run check:fix` | `biome check --write .` |
33
+ | `bun run ci:quick` | format → lint:fix → check:fix → build |
34
+
35
+ ## Scaffolding outside the monorepo
36
+
37
+ Use [`create-quadrokit`](../../create-quadrokit/README.md); it copies a template, rewrites `workspace:*` to published semver, and skips lockfiles and `.quadrokit/` (so new projects run `bun run quadrokit:generate` after install).
38
+
39
+ For **monorepo** template packages, `.quadrokit/generated` is kept (regenerate with `bun run quadrokit:generate` in a template after changing `assets/catalog.placeholder.json`) so `tsc` can typecheck against the demo catalog.
@@ -0,0 +1,47 @@
1
+ ---
2
+ description: Commit messages must follow Conventional Commits (commitlint @commitlint/config-conventional)
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Git commits (Conventional Commits / commitlint)
7
+
8
+ When proposing or writing **commit messages** (including one-line suggestions, full messages, or PR squash titles), use **Conventional Commits** compatible with **@commitlint/config-conventional**.
9
+
10
+ ## Header format
11
+
12
+ ```
13
+ <type>(<optional-scope>): <short description>
14
+ ```
15
+
16
+ - **type** (pick one): `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`
17
+ - **scope** (optional): lowercase, short package or area (e.g. `client`, `templates`, `ui`, `create-quadrokit`)
18
+ - **description**: imperative mood, **lowercase**, **no trailing period**, **≤ ~72 characters** for the subject line
19
+
20
+ ## Breaking changes
21
+
22
+ - Add `!` after type or scope: `feat(api)!: remove legacy login endpoint`
23
+ - Or explain in the body with a line starting with `BREAKING CHANGE:`
24
+
25
+ ## Body and footer (when needed)
26
+
27
+ - Separate body from subject with a blank line.
28
+ - Wrap body at ~100 chars.
29
+ - Footer: `Refs: #123` / `Fixes: #123` if applicable.
30
+
31
+ ## Examples
32
+
33
+ ```
34
+ feat(client): add entity set release helper
35
+
36
+ fix(templates): correct vite proxy target env var
37
+
38
+ chore: bump biome to 2.4.10
39
+
40
+ docs(readme): document ci:quick script
41
+ ```
42
+
43
+ ## Do not
44
+
45
+ - Vague subjects: `update`, `fix stuff`, `wip`
46
+ - End the subject line with a period
47
+ - Use title case for the description (use sentence case / lowercase)
@@ -0,0 +1,2 @@
1
+ # Origin of your 4D web server (REST). The Vite dev server proxies /rest here.
2
+ VITE_4D_ORIGIN=http://127.0.0.1:7080
@@ -0,0 +1,12 @@
1
+ {
2
+ "root": false,
3
+ "extends": "//",
4
+ "vcs": {
5
+ "enabled": true,
6
+ "clientKind": "git",
7
+ "useIgnoreFile": false
8
+ },
9
+ "files": {
10
+ "includes": ["**", "!**/node_modules", "!**/dist", "!**/.quadrokit"]
11
+ }
12
+ }
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>QuadroKit — Admin shell</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@quadrokit/template-admin-shell",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "quadrokit:generate": "node scripts/quadrokit-generate.mjs",
8
+ "dev": "vite",
9
+ "build": "tsc -b && vite build",
10
+ "preview": "vite preview",
11
+ "typecheck": "tsc -b --noEmit",
12
+ "format": "biome format --write .",
13
+ "lint": "biome lint .",
14
+ "lint:fix": "biome lint --write .",
15
+ "check": "biome check .",
16
+ "check:fix": "biome check --write .",
17
+ "biome:check": "biome check .",
18
+ "ci:quick": "bun run format && bun run lint:fix && bun run check:fix && bun run build"
19
+ },
20
+ "dependencies": {
21
+ "@hookform/resolvers": "^5.2.2",
22
+ "@quadrokit/client": "workspace:*",
23
+ "@quadrokit/sample-client": "workspace:*",
24
+ "@quadrokit/ui": "workspace:*",
25
+ "i18next": "^26.0.3",
26
+ "react": "^19.2.4",
27
+ "react-dom": "^19.2.4",
28
+ "react-hook-form": "^7.72.0",
29
+ "react-i18next": "^17.0.2",
30
+ "react-router-dom": "^7.13.2",
31
+ "zod": "^4.3.6",
32
+ "zustand": "^5.0.12"
33
+ },
34
+ "devDependencies": {
35
+ "@biomejs/biome": "2.4.10",
36
+ "@types/node": "^25.5.0",
37
+ "@types/react": "^19.2.14",
38
+ "@types/react-dom": "^19.2.3",
39
+ "@vitejs/plugin-react": "^6.0.1",
40
+ "autoprefixer": "^10.4.27",
41
+ "postcss": "^8.5.8",
42
+ "tailwindcss": "^3.4.17",
43
+ "typescript": "^5.9.3",
44
+ "vite": "^8.0.3"
45
+ }
46
+ }
@@ -0,0 +1,6 @@
1
+ export default {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }