@expcat/tigercat-cli 1.0.7 → 1.1.0
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 +33 -0
- package/dist/index.js +432 -77
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -48,6 +48,39 @@ tigercat generate docs
|
|
|
48
48
|
tigercat generate docs --output ./docs/api
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
### `tigercat doctor`
|
|
52
|
+
|
|
53
|
+
Check whether the current project has compatible Node, pnpm, Tailwind CSS, Tigercat peer dependencies, and template tooling.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
tigercat doctor
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Windows Support
|
|
60
|
+
|
|
61
|
+
The CLI is fully cross-platform. All template file paths use forward slashes and are
|
|
62
|
+
resolved via Node.js `path.resolve()` at write time, so they work correctly on Windows
|
|
63
|
+
with backslash paths, paths containing spaces, and UNC paths.
|
|
64
|
+
|
|
65
|
+
### Package Manager `.cmd` Shims
|
|
66
|
+
|
|
67
|
+
When installed globally or locally, each package manager creates platform-specific shims
|
|
68
|
+
so that `tigercat` can be invoked directly from PowerShell, CMD, or Git Bash:
|
|
69
|
+
|
|
70
|
+
| Package manager | Global install | Shim files created |
|
|
71
|
+
| --------------- | ------------------------------------- | -------------------------------- |
|
|
72
|
+
| **pnpm** | `pnpm add -g @expcat/tigercat-cli` | `tigercat.cmd`, `tigercat` (sh) |
|
|
73
|
+
| **npm** | `npm i -g @expcat/tigercat-cli` | `tigercat.cmd`, `tigercat` (sh), `tigercat.ps1` |
|
|
74
|
+
| **bun** | `bun add -g @expcat/tigercat-cli` | `tigercat.cmd`, `tigercat` (sh) |
|
|
75
|
+
|
|
76
|
+
For local (non-global) installs, run via `npx tigercat`, `pnpm exec tigercat`, or
|
|
77
|
+
`bunx tigercat`. The `#!/usr/bin/env node` shebang in the built output is used by all
|
|
78
|
+
three package managers to locate the Node.js runtime.
|
|
79
|
+
|
|
80
|
+
> **Note:** If you use Corepack with pnpm on Windows, ensure `shell: true` is passed
|
|
81
|
+
> when spawning pnpm programmatically (the project scripts already handle this via
|
|
82
|
+
> `scripts/utils/pnpm.mjs`).
|
|
83
|
+
|
|
51
84
|
## License
|
|
52
85
|
|
|
53
86
|
MIT
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,28 @@ import { execSync } from 'child_process';
|
|
|
10
10
|
var CLI_NAME = "tigercat";
|
|
11
11
|
var CLI_VERSION = "0.9.0";
|
|
12
12
|
var TEMPLATES = ["vue3", "react"];
|
|
13
|
+
var TEMPLATE_VERSIONS = {
|
|
14
|
+
// Tigercat packages (use caret on latest major)
|
|
15
|
+
tigercat: "^1.0.0",
|
|
16
|
+
// Frameworks
|
|
17
|
+
vue: "^3.5.33",
|
|
18
|
+
react: "^19.2.5",
|
|
19
|
+
reactDom: "^19.2.5",
|
|
20
|
+
// Build toolchain
|
|
21
|
+
typescript: "^6.0.3",
|
|
22
|
+
vite: "^8.0.10",
|
|
23
|
+
tailwindcss: "^4.2.4",
|
|
24
|
+
tailwindcssVite: "^4.2.4",
|
|
25
|
+
// Vite plugins
|
|
26
|
+
vitejsPluginVue: "^6.0.6",
|
|
27
|
+
vitejsPluginReact: "^6.0.1",
|
|
28
|
+
// Type definitions
|
|
29
|
+
typesReact: "^19.2.14",
|
|
30
|
+
typesReactDom: "^19.2.3",
|
|
31
|
+
// Vue-specific
|
|
32
|
+
vueTsconfig: "^0.9.1",
|
|
33
|
+
vueTsc: "^3.2.7"
|
|
34
|
+
};
|
|
13
35
|
var COMPONENT_CATEGORIES = {
|
|
14
36
|
basic: [
|
|
15
37
|
"Alert",
|
|
@@ -123,17 +145,18 @@ function vue3PackageJson(name) {
|
|
|
123
145
|
preview: "vite preview"
|
|
124
146
|
},
|
|
125
147
|
dependencies: {
|
|
126
|
-
"@expcat/tigercat-vue":
|
|
127
|
-
vue:
|
|
148
|
+
"@expcat/tigercat-vue": TEMPLATE_VERSIONS.tigercat,
|
|
149
|
+
vue: TEMPLATE_VERSIONS.vue
|
|
128
150
|
},
|
|
129
151
|
devDependencies: {
|
|
130
|
-
"@
|
|
131
|
-
"@
|
|
132
|
-
"@vue
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
152
|
+
"@expcat/tigercat-core": TEMPLATE_VERSIONS.tigercat,
|
|
153
|
+
"@tailwindcss/vite": TEMPLATE_VERSIONS.tailwindcssVite,
|
|
154
|
+
"@vitejs/plugin-vue": TEMPLATE_VERSIONS.vitejsPluginVue,
|
|
155
|
+
"@vue/tsconfig": TEMPLATE_VERSIONS.vueTsconfig,
|
|
156
|
+
tailwindcss: TEMPLATE_VERSIONS.tailwindcss,
|
|
157
|
+
typescript: TEMPLATE_VERSIONS.typescript,
|
|
158
|
+
vite: TEMPLATE_VERSIONS.vite,
|
|
159
|
+
"vue-tsc": TEMPLATE_VERSIONS.vueTsc
|
|
137
160
|
}
|
|
138
161
|
},
|
|
139
162
|
null,
|
|
@@ -145,9 +168,9 @@ function vue3Tsconfig() {
|
|
|
145
168
|
{
|
|
146
169
|
extends: "@vue/tsconfig/tsconfig.dom.json",
|
|
147
170
|
compilerOptions: {
|
|
148
|
-
target: "
|
|
171
|
+
target: "ES2022",
|
|
149
172
|
module: "ESNext",
|
|
150
|
-
lib: ["
|
|
173
|
+
lib: ["ES2022", "DOM", "DOM.Iterable"],
|
|
151
174
|
skipLibCheck: true,
|
|
152
175
|
moduleResolution: "bundler",
|
|
153
176
|
resolveJsonModule: true,
|
|
@@ -204,18 +227,58 @@ createApp(App).mount('#app')
|
|
|
204
227
|
}
|
|
205
228
|
function vue3App() {
|
|
206
229
|
return `<script setup lang="ts">
|
|
207
|
-
import {
|
|
230
|
+
import { ref } from 'vue'
|
|
231
|
+
import { Button, Alert, Switch } from '@expcat/tigercat-vue'
|
|
232
|
+
|
|
233
|
+
const dark = ref(false)
|
|
234
|
+
const modern = ref(true)
|
|
235
|
+
|
|
236
|
+
function syncRoot() {
|
|
237
|
+
const root = document.documentElement
|
|
238
|
+
root.classList.toggle('dark', dark.value)
|
|
239
|
+
if (modern.value) {
|
|
240
|
+
root.setAttribute('data-tiger-style', 'modern')
|
|
241
|
+
} else {
|
|
242
|
+
root.removeAttribute('data-tiger-style')
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
syncRoot()
|
|
247
|
+
|
|
248
|
+
function toggleDark(v: boolean) {
|
|
249
|
+
dark.value = v
|
|
250
|
+
syncRoot()
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function toggleModern(v: boolean) {
|
|
254
|
+
modern.value = v
|
|
255
|
+
syncRoot()
|
|
256
|
+
}
|
|
208
257
|
</script>
|
|
209
258
|
|
|
210
259
|
<template>
|
|
211
260
|
<div class="min-h-screen bg-[var(--tiger-surface,#ffffff)] p-8">
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
261
|
+
<div class="flex items-center justify-between mb-6">
|
|
262
|
+
<h1 class="text-2xl font-bold text-[var(--tiger-text,#111827)]">
|
|
263
|
+
Tigercat + Vue 3
|
|
264
|
+
</h1>
|
|
265
|
+
<div class="flex items-center gap-4 text-sm text-[var(--tiger-text-muted,#6b7280)]">
|
|
266
|
+
<label class="flex items-center gap-2">
|
|
267
|
+
<span>Modern</span>
|
|
268
|
+
<Switch :checked="modern" size="sm" @update:checked="toggleModern" />
|
|
269
|
+
</label>
|
|
270
|
+
<label class="flex items-center gap-2">
|
|
271
|
+
<span>Dark</span>
|
|
272
|
+
<Switch :checked="dark" size="sm" @update:checked="toggleDark" />
|
|
273
|
+
</label>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
215
276
|
|
|
216
277
|
<div class="space-y-4">
|
|
217
278
|
<Alert variant="info">
|
|
218
279
|
Welcome to your Tigercat project! Edit src/App.vue to get started.
|
|
280
|
+
Toggle <code>Modern</code> to preview the opt-in modern visual style
|
|
281
|
+
(radius / shadow / motion tokens).
|
|
219
282
|
</Alert>
|
|
220
283
|
|
|
221
284
|
<div class="flex gap-2">
|
|
@@ -240,31 +303,19 @@ declare module '*.vue' {
|
|
|
240
303
|
}
|
|
241
304
|
function commonStyleCss() {
|
|
242
305
|
return `@import "tailwindcss";
|
|
306
|
+
@plugin "@expcat/tigercat-core/tailwind/modern";
|
|
243
307
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
@media (prefers-color-scheme: dark) {
|
|
255
|
-
:root {
|
|
256
|
-
--tiger-primary: #3b82f6;
|
|
257
|
-
--tiger-primary-hover: #2563eb;
|
|
258
|
-
--tiger-surface: #111827;
|
|
259
|
-
--tiger-surface-muted: #1f2937;
|
|
260
|
-
--tiger-text: #f9fafb;
|
|
261
|
-
--tiger-text-muted: #9ca3af;
|
|
262
|
-
--tiger-border: #374151;
|
|
263
|
-
}
|
|
308
|
+
/*
|
|
309
|
+
* The tigercat tailwind plugin injects every --tiger-* design token for
|
|
310
|
+
* both light (:root) and dark (.dark) modes, plus the opt-in modern
|
|
311
|
+
* overrides activated by data-tiger-style="modern". The demo App toggles
|
|
312
|
+
* dark mode via .dark on <html> and prefers-color-scheme via the rule
|
|
313
|
+
* below. Swap the @plugin line for a tailwind.config.ts calling
|
|
314
|
+
* createTigercatPlugin({ preset }) to use a custom preset.
|
|
315
|
+
*/
|
|
264
316
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
317
|
+
html {
|
|
318
|
+
color-scheme: light dark;
|
|
268
319
|
}
|
|
269
320
|
`;
|
|
270
321
|
}
|
|
@@ -295,18 +346,19 @@ function reactPackageJson(name) {
|
|
|
295
346
|
preview: "vite preview"
|
|
296
347
|
},
|
|
297
348
|
dependencies: {
|
|
298
|
-
"@expcat/tigercat-react":
|
|
299
|
-
react:
|
|
300
|
-
"react-dom":
|
|
349
|
+
"@expcat/tigercat-react": TEMPLATE_VERSIONS.tigercat,
|
|
350
|
+
react: TEMPLATE_VERSIONS.react,
|
|
351
|
+
"react-dom": TEMPLATE_VERSIONS.reactDom
|
|
301
352
|
},
|
|
302
353
|
devDependencies: {
|
|
303
|
-
"@
|
|
304
|
-
"@
|
|
305
|
-
"@types/react
|
|
306
|
-
"@
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
354
|
+
"@expcat/tigercat-core": TEMPLATE_VERSIONS.tigercat,
|
|
355
|
+
"@tailwindcss/vite": TEMPLATE_VERSIONS.tailwindcssVite,
|
|
356
|
+
"@types/react": TEMPLATE_VERSIONS.typesReact,
|
|
357
|
+
"@types/react-dom": TEMPLATE_VERSIONS.typesReactDom,
|
|
358
|
+
"@vitejs/plugin-react": TEMPLATE_VERSIONS.vitejsPluginReact,
|
|
359
|
+
tailwindcss: TEMPLATE_VERSIONS.tailwindcss,
|
|
360
|
+
typescript: TEMPLATE_VERSIONS.typescript,
|
|
361
|
+
vite: TEMPLATE_VERSIONS.vite
|
|
310
362
|
}
|
|
311
363
|
},
|
|
312
364
|
null,
|
|
@@ -317,9 +369,9 @@ function reactTsconfig() {
|
|
|
317
369
|
return JSON.stringify(
|
|
318
370
|
{
|
|
319
371
|
compilerOptions: {
|
|
320
|
-
target: "
|
|
372
|
+
target: "ES2022",
|
|
321
373
|
useDefineForClassFields: true,
|
|
322
|
-
lib: ["
|
|
374
|
+
lib: ["ES2022", "DOM", "DOM.Iterable"],
|
|
323
375
|
module: "ESNext",
|
|
324
376
|
skipLibCheck: true,
|
|
325
377
|
moduleResolution: "bundler",
|
|
@@ -400,18 +452,49 @@ createRoot(document.getElementById('root')!).render(
|
|
|
400
452
|
`;
|
|
401
453
|
}
|
|
402
454
|
function reactApp() {
|
|
403
|
-
return `import {
|
|
455
|
+
return `import { useState, useCallback, useEffect } from 'react'
|
|
456
|
+
import { Button, Alert, Switch } from '@expcat/tigercat-react'
|
|
404
457
|
|
|
405
458
|
export default function App() {
|
|
459
|
+
const [dark, setDark] = useState(false)
|
|
460
|
+
const [modern, setModern] = useState(true)
|
|
461
|
+
|
|
462
|
+
useEffect(() => {
|
|
463
|
+
const root = document.documentElement
|
|
464
|
+
root.classList.toggle('dark', dark)
|
|
465
|
+
if (modern) {
|
|
466
|
+
root.setAttribute('data-tiger-style', 'modern')
|
|
467
|
+
} else {
|
|
468
|
+
root.removeAttribute('data-tiger-style')
|
|
469
|
+
}
|
|
470
|
+
}, [dark, modern])
|
|
471
|
+
|
|
472
|
+
const onDark = useCallback((v: boolean) => setDark(v), [])
|
|
473
|
+
const onModern = useCallback((v: boolean) => setModern(v), [])
|
|
474
|
+
|
|
406
475
|
return (
|
|
407
476
|
<div className="min-h-screen bg-[var(--tiger-surface,#ffffff)] p-8">
|
|
408
|
-
<
|
|
409
|
-
|
|
410
|
-
|
|
477
|
+
<div className="flex items-center justify-between mb-6">
|
|
478
|
+
<h1 className="text-2xl font-bold text-[var(--tiger-text,#111827)]">
|
|
479
|
+
Tigercat + React
|
|
480
|
+
</h1>
|
|
481
|
+
<div className="flex items-center gap-4 text-sm text-[var(--tiger-text-muted,#6b7280)]">
|
|
482
|
+
<label className="flex items-center gap-2">
|
|
483
|
+
<span>Modern</span>
|
|
484
|
+
<Switch checked={modern} size="sm" onChange={onModern} />
|
|
485
|
+
</label>
|
|
486
|
+
<label className="flex items-center gap-2">
|
|
487
|
+
<span>Dark</span>
|
|
488
|
+
<Switch checked={dark} size="sm" onChange={onDark} />
|
|
489
|
+
</label>
|
|
490
|
+
</div>
|
|
491
|
+
</div>
|
|
411
492
|
|
|
412
493
|
<div className="space-y-4">
|
|
413
494
|
<Alert variant="info">
|
|
414
495
|
Welcome to your Tigercat project! Edit src/App.tsx to get started.
|
|
496
|
+
Toggle <code>Modern</code> to preview the opt-in modern visual style
|
|
497
|
+
(radius / shadow / motion tokens).
|
|
415
498
|
</Alert>
|
|
416
499
|
|
|
417
500
|
<div className="flex gap-2">
|
|
@@ -427,31 +510,19 @@ export default function App() {
|
|
|
427
510
|
}
|
|
428
511
|
function commonStyleCss2() {
|
|
429
512
|
return `@import "tailwindcss";
|
|
513
|
+
@plugin "@expcat/tigercat-core/tailwind/modern";
|
|
430
514
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
@media (prefers-color-scheme: dark) {
|
|
442
|
-
:root {
|
|
443
|
-
--tiger-primary: #3b82f6;
|
|
444
|
-
--tiger-primary-hover: #2563eb;
|
|
445
|
-
--tiger-surface: #111827;
|
|
446
|
-
--tiger-surface-muted: #1f2937;
|
|
447
|
-
--tiger-text: #f9fafb;
|
|
448
|
-
--tiger-text-muted: #9ca3af;
|
|
449
|
-
--tiger-border: #374151;
|
|
450
|
-
}
|
|
515
|
+
/*
|
|
516
|
+
* The tigercat tailwind plugin injects every --tiger-* design token for
|
|
517
|
+
* both light (:root) and dark (.dark) modes, plus the opt-in modern
|
|
518
|
+
* overrides activated by data-tiger-style="modern". The demo App toggles
|
|
519
|
+
* dark mode via .dark on <html> and prefers-color-scheme via the rule
|
|
520
|
+
* below. Swap the @plugin line for a tailwind.config.ts calling
|
|
521
|
+
* createTigercatPlugin({ preset }) to use a custom preset.
|
|
522
|
+
*/
|
|
451
523
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
}
|
|
524
|
+
html {
|
|
525
|
+
color-scheme: light dark;
|
|
455
526
|
}
|
|
456
527
|
`;
|
|
457
528
|
}
|
|
@@ -779,6 +850,289 @@ async function runGenerateDocs(inputDir, outputDir) {
|
|
|
779
850
|
writeFileSafe(join(resolvedOutput, "index.md"), indexLines.join("\n"));
|
|
780
851
|
logSuccess(`Generated docs for ${docs.length} components in ${outputDir}`);
|
|
781
852
|
}
|
|
853
|
+
var MIN_NODE_MAJOR = 20;
|
|
854
|
+
var MIN_PNPM_MAJOR = 8;
|
|
855
|
+
var REQUIRED_TAILWIND_MAJOR = 4;
|
|
856
|
+
var REQUIRED_TIGERCAT_MAJOR = 1;
|
|
857
|
+
var FRAMEWORK_REQUIREMENTS = {
|
|
858
|
+
vue3: {
|
|
859
|
+
peers: ["@expcat/tigercat-vue", "@expcat/tigercat-core", "vue"],
|
|
860
|
+
templateDeps: ["@tailwindcss/vite", "@vitejs/plugin-vue", "typescript", "vite", "vue-tsc"]
|
|
861
|
+
},
|
|
862
|
+
react: {
|
|
863
|
+
peers: ["@expcat/tigercat-react", "@expcat/tigercat-core", "react", "react-dom"],
|
|
864
|
+
templateDeps: ["@tailwindcss/vite", "@vitejs/plugin-react", "typescript", "vite"]
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
function createDoctorCommand() {
|
|
868
|
+
return new Command("doctor").description("Check whether the current project is compatible with Tigercat").action(() => {
|
|
869
|
+
runDoctor();
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
function collectDoctorChecks(options = {}) {
|
|
873
|
+
const cwd = options.cwd ?? process.cwd();
|
|
874
|
+
const env = options.env ?? process.env;
|
|
875
|
+
const packageResult = readProjectPackage(cwd);
|
|
876
|
+
const nodeVersion = options.nodeVersion ?? process.versions.node;
|
|
877
|
+
const checks = [createPackageCheck(packageResult)];
|
|
878
|
+
checks.push(createNodeCheck(nodeVersion));
|
|
879
|
+
checks.push(createPnpmCheck(packageResult.packageJson, env));
|
|
880
|
+
if (!packageResult.packageJson) {
|
|
881
|
+
return checks;
|
|
882
|
+
}
|
|
883
|
+
checks.push(createTailwindCheck(packageResult.packageJson));
|
|
884
|
+
checks.push(createPeerDepsCheck(packageResult.packageJson));
|
|
885
|
+
checks.push(createTemplateCompatibilityCheck(packageResult.packageJson));
|
|
886
|
+
return checks;
|
|
887
|
+
}
|
|
888
|
+
function runDoctor() {
|
|
889
|
+
const checks = collectDoctorChecks();
|
|
890
|
+
logInfo("Running Tigercat project checks...");
|
|
891
|
+
console.log();
|
|
892
|
+
for (const check of checks) {
|
|
893
|
+
printCheck(check);
|
|
894
|
+
}
|
|
895
|
+
console.log();
|
|
896
|
+
const failures = checks.filter((check) => check.status === "fail");
|
|
897
|
+
const warnings = checks.filter((check) => check.status === "warn");
|
|
898
|
+
if (failures.length > 0) {
|
|
899
|
+
logError(`${failures.length} check${failures.length === 1 ? "" : "s"} failed`);
|
|
900
|
+
process.exit(1);
|
|
901
|
+
}
|
|
902
|
+
if (warnings.length > 0) {
|
|
903
|
+
logWarn(`${warnings.length} warning${warnings.length === 1 ? "" : "s"} found`);
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
logSuccess("All checks passed");
|
|
907
|
+
}
|
|
908
|
+
function readProjectPackage(cwd) {
|
|
909
|
+
const packagePath = join(cwd, "package.json");
|
|
910
|
+
const content = readFileSafe(packagePath);
|
|
911
|
+
if (!content) {
|
|
912
|
+
return { packageJson: null, error: `package.json not found in ${cwd}` };
|
|
913
|
+
}
|
|
914
|
+
try {
|
|
915
|
+
return { packageJson: JSON.parse(content) };
|
|
916
|
+
} catch {
|
|
917
|
+
return { packageJson: null, error: "package.json is not valid JSON" };
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
function createPackageCheck(result) {
|
|
921
|
+
if (!result.packageJson) {
|
|
922
|
+
return {
|
|
923
|
+
name: "Project package",
|
|
924
|
+
status: "fail",
|
|
925
|
+
message: result.error ?? "package.json could not be read"
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
return {
|
|
929
|
+
name: "Project package",
|
|
930
|
+
status: "pass",
|
|
931
|
+
message: "package.json is readable"
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
function createNodeCheck(version) {
|
|
935
|
+
const parsed = parseVersion(version);
|
|
936
|
+
if (!parsed || parsed.major < MIN_NODE_MAJOR) {
|
|
937
|
+
return {
|
|
938
|
+
name: "Node.js",
|
|
939
|
+
status: "fail",
|
|
940
|
+
message: `Node ${MIN_NODE_MAJOR}+ is required, current version is ${version}`
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
return {
|
|
944
|
+
name: "Node.js",
|
|
945
|
+
status: "pass",
|
|
946
|
+
message: `Node ${version} satisfies >=${MIN_NODE_MAJOR}`
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
function createPnpmCheck(packageJson, env) {
|
|
950
|
+
const version = getPnpmVersion(packageJson, env);
|
|
951
|
+
if (!version) {
|
|
952
|
+
return {
|
|
953
|
+
name: "pnpm",
|
|
954
|
+
status: "warn",
|
|
955
|
+
message: `Could not detect pnpm version; Tigercat templates expect pnpm ${MIN_PNPM_MAJOR}+`
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
const parsed = parseVersion(version);
|
|
959
|
+
if (!parsed || parsed.major < MIN_PNPM_MAJOR) {
|
|
960
|
+
return {
|
|
961
|
+
name: "pnpm",
|
|
962
|
+
status: "fail",
|
|
963
|
+
message: `pnpm ${MIN_PNPM_MAJOR}+ is required, detected ${version}`
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
return {
|
|
967
|
+
name: "pnpm",
|
|
968
|
+
status: "pass",
|
|
969
|
+
message: `pnpm ${version} satisfies >=${MIN_PNPM_MAJOR}`
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
function createTailwindCheck(packageJson) {
|
|
973
|
+
const allDeps = collectDependencies(packageJson);
|
|
974
|
+
const tailwindRange = allDeps.tailwindcss;
|
|
975
|
+
const vitePluginRange = allDeps["@tailwindcss/vite"];
|
|
976
|
+
if (!tailwindRange) {
|
|
977
|
+
return {
|
|
978
|
+
name: "Tailwind CSS",
|
|
979
|
+
status: "fail",
|
|
980
|
+
message: "tailwindcss is missing; Tigercat requires Tailwind CSS 4+"
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
const tailwindMajor = getRangeMajor(tailwindRange);
|
|
984
|
+
const pluginMajor = vitePluginRange ? getRangeMajor(vitePluginRange) : null;
|
|
985
|
+
if (tailwindMajor !== null && tailwindMajor < REQUIRED_TAILWIND_MAJOR) {
|
|
986
|
+
return {
|
|
987
|
+
name: "Tailwind CSS",
|
|
988
|
+
status: "fail",
|
|
989
|
+
message: `tailwindcss ${tailwindRange} is not compatible; use Tailwind CSS ${REQUIRED_TAILWIND_MAJOR}+`
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
if (tailwindMajor === null) {
|
|
993
|
+
return {
|
|
994
|
+
name: "Tailwind CSS",
|
|
995
|
+
status: "warn",
|
|
996
|
+
message: `Could not verify tailwindcss range ${tailwindRange}; expected ${REQUIRED_TAILWIND_MAJOR}+`
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
if (vitePluginRange && pluginMajor !== null && pluginMajor < REQUIRED_TAILWIND_MAJOR) {
|
|
1000
|
+
return {
|
|
1001
|
+
name: "Tailwind CSS",
|
|
1002
|
+
status: "fail",
|
|
1003
|
+
message: `@tailwindcss/vite ${vitePluginRange} is not compatible; use ${REQUIRED_TAILWIND_MAJOR}+`
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
const details = vitePluginRange ? [`@tailwindcss/vite ${vitePluginRange}`] : ["@tailwindcss/vite is recommended for Vite templates"];
|
|
1007
|
+
return {
|
|
1008
|
+
name: "Tailwind CSS",
|
|
1009
|
+
status: vitePluginRange ? "pass" : "warn",
|
|
1010
|
+
message: `tailwindcss ${tailwindRange} satisfies Tigercat requirements`,
|
|
1011
|
+
details
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
function createPeerDepsCheck(packageJson) {
|
|
1015
|
+
const allDeps = collectDependencies(packageJson);
|
|
1016
|
+
const frameworks = detectTigercatFrameworks(allDeps);
|
|
1017
|
+
if (frameworks.length === 0) {
|
|
1018
|
+
return {
|
|
1019
|
+
name: "Peer dependencies",
|
|
1020
|
+
status: "warn",
|
|
1021
|
+
message: "No Tigercat Vue or React package was detected"
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
const missing = [
|
|
1025
|
+
...new Set(
|
|
1026
|
+
frameworks.flatMap(
|
|
1027
|
+
(framework) => FRAMEWORK_REQUIREMENTS[framework].peers.filter((dependency) => !allDeps[dependency])
|
|
1028
|
+
)
|
|
1029
|
+
)
|
|
1030
|
+
];
|
|
1031
|
+
const incompatible = frameworks.flatMap(
|
|
1032
|
+
(framework) => FRAMEWORK_REQUIREMENTS[framework].peers.filter((dependency) => dependency.startsWith("@expcat/tigercat-")).filter((dependency) => isOlderMajor(allDeps[dependency], REQUIRED_TIGERCAT_MAJOR)).map((dependency) => `${dependency}@${allDeps[dependency]}`)
|
|
1033
|
+
);
|
|
1034
|
+
if (missing.length > 0 || incompatible.length > 0) {
|
|
1035
|
+
return {
|
|
1036
|
+
name: "Peer dependencies",
|
|
1037
|
+
status: "fail",
|
|
1038
|
+
message: "Tigercat peer dependencies are incomplete or incompatible",
|
|
1039
|
+
details: [...missing.map((dependency) => `Missing ${dependency}`), ...incompatible]
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
return {
|
|
1043
|
+
name: "Peer dependencies",
|
|
1044
|
+
status: "pass",
|
|
1045
|
+
message: `${frameworks.map(formatFramework).join(" + ")} peer dependencies are present`
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
function createTemplateCompatibilityCheck(packageJson) {
|
|
1049
|
+
const allDeps = collectDependencies(packageJson);
|
|
1050
|
+
const frameworks = detectTigercatFrameworks(allDeps);
|
|
1051
|
+
if (frameworks.length === 0) {
|
|
1052
|
+
return {
|
|
1053
|
+
name: "Template compatibility",
|
|
1054
|
+
status: "warn",
|
|
1055
|
+
message: "Skipped because no supported Tigercat framework package was detected"
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
const missing = [
|
|
1059
|
+
...new Set(
|
|
1060
|
+
frameworks.flatMap(
|
|
1061
|
+
(framework) => FRAMEWORK_REQUIREMENTS[framework].templateDeps.filter((dependency) => !allDeps[dependency])
|
|
1062
|
+
)
|
|
1063
|
+
)
|
|
1064
|
+
];
|
|
1065
|
+
if (missing.length > 0) {
|
|
1066
|
+
return {
|
|
1067
|
+
name: "Template compatibility",
|
|
1068
|
+
status: "warn",
|
|
1069
|
+
message: "Project differs from current CLI template dependencies",
|
|
1070
|
+
details: missing.map((dependency) => `Template dependency not found: ${dependency}`)
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
return {
|
|
1074
|
+
name: "Template compatibility",
|
|
1075
|
+
status: "pass",
|
|
1076
|
+
message: `${frameworks.map(formatFramework).join(" + ")} template dependencies are present`
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
function collectDependencies(packageJson) {
|
|
1080
|
+
return {
|
|
1081
|
+
...packageJson.peerDependencies,
|
|
1082
|
+
...packageJson.dependencies,
|
|
1083
|
+
...packageJson.devDependencies
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
function detectTigercatFrameworks(dependencies) {
|
|
1087
|
+
const frameworks = [];
|
|
1088
|
+
if (dependencies["@expcat/tigercat-vue"]) {
|
|
1089
|
+
frameworks.push("vue3");
|
|
1090
|
+
}
|
|
1091
|
+
if (dependencies["@expcat/tigercat-react"]) {
|
|
1092
|
+
frameworks.push("react");
|
|
1093
|
+
}
|
|
1094
|
+
return frameworks;
|
|
1095
|
+
}
|
|
1096
|
+
function getPnpmVersion(packageJson, env) {
|
|
1097
|
+
const packageManager = packageJson?.packageManager;
|
|
1098
|
+
if (typeof packageManager === "string") {
|
|
1099
|
+
const match2 = /^pnpm@(.+)$/.exec(packageManager);
|
|
1100
|
+
if (match2) return match2[1];
|
|
1101
|
+
}
|
|
1102
|
+
const userAgent = env.npm_config_user_agent;
|
|
1103
|
+
const match = userAgent ? /pnpm\/(\d+\.\d+\.\d+)/.exec(userAgent) : null;
|
|
1104
|
+
return match?.[1] ?? null;
|
|
1105
|
+
}
|
|
1106
|
+
function parseVersion(value) {
|
|
1107
|
+
const match = /^(?:v)?(\d+)(?:\.(\d+))?(?:\.(\d+))?/.exec(value.trim());
|
|
1108
|
+
if (!match) return null;
|
|
1109
|
+
return {
|
|
1110
|
+
major: Number(match[1]),
|
|
1111
|
+
minor: Number(match[2] ?? 0),
|
|
1112
|
+
patch: Number(match[3] ?? 0)
|
|
1113
|
+
};
|
|
1114
|
+
}
|
|
1115
|
+
function getRangeMajor(range) {
|
|
1116
|
+
if (!range) return null;
|
|
1117
|
+
if (/^(workspace|file|link|catalog):/.test(range)) return null;
|
|
1118
|
+
const match = /(?:\^|~|>=|<=|>|<|=)?\s*v?(\d+)/.exec(range);
|
|
1119
|
+
return match ? Number(match[1]) : null;
|
|
1120
|
+
}
|
|
1121
|
+
function isOlderMajor(range, expectedMajor) {
|
|
1122
|
+
const major = getRangeMajor(range);
|
|
1123
|
+
return major !== null && major < expectedMajor;
|
|
1124
|
+
}
|
|
1125
|
+
function formatFramework(framework) {
|
|
1126
|
+
return framework === "vue3" ? "Vue 3" : "React";
|
|
1127
|
+
}
|
|
1128
|
+
function printCheck(check) {
|
|
1129
|
+
const symbol = check.status === "pass" ? pc.green("\u2714") : check.status === "warn" ? pc.yellow("\u26A0") : pc.red("\u2716");
|
|
1130
|
+
const name = pc.bold(check.name);
|
|
1131
|
+
console.log(`${symbol} ${name}: ${check.message}`);
|
|
1132
|
+
for (const detail of check.details ?? []) {
|
|
1133
|
+
console.log(` ${pc.dim("-")} ${detail}`);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
782
1136
|
|
|
783
1137
|
// src/index.ts
|
|
784
1138
|
var program = new Command();
|
|
@@ -787,4 +1141,5 @@ program.addCommand(createCreateCommand());
|
|
|
787
1141
|
program.addCommand(createAddCommand());
|
|
788
1142
|
program.addCommand(createPlaygroundCommand());
|
|
789
1143
|
program.addCommand(createGenerateCommand());
|
|
1144
|
+
program.addCommand(createDoctorCommand());
|
|
790
1145
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expcat/tigercat-cli",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI tooling for Tigercat UI library — project scaffolding, component generation, and more",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
"code-generator"
|
|
23
23
|
],
|
|
24
24
|
"bin": {
|
|
25
|
-
"tigercat": "./dist/index.
|
|
25
|
+
"tigercat": "./dist/index.js"
|
|
26
26
|
},
|
|
27
|
-
"main": "./dist/index.
|
|
28
|
-
"module": "./dist/index.
|
|
29
|
-
"types": "./dist/index.d.
|
|
27
|
+
"main": "./dist/index.js",
|
|
28
|
+
"module": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
30
|
"files": [
|
|
31
31
|
"dist",
|
|
32
32
|
"templates"
|
|
@@ -36,13 +36,13 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"commander": "^13.1.0",
|
|
39
|
-
"
|
|
40
|
-
"
|
|
39
|
+
"picocolors": "^1.1.1",
|
|
40
|
+
"prompts": "^2.4.2"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/prompts": "^2.4.9",
|
|
44
44
|
"tsup": "^8.5.1",
|
|
45
|
-
"typescript": "^
|
|
45
|
+
"typescript": "^6.0.3"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "tsup --config tsup.config.ts",
|