@expcat/tigercat-cli 1.3.4 → 1.5.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/dist/index.js +240 -112
- package/package.json +6 -4
package/dist/index.js
CHANGED
|
@@ -5,32 +5,67 @@ import { existsSync, readdirSync, mkdirSync, writeFileSync, readFileSync } from
|
|
|
5
5
|
import { resolve, join, dirname, basename } from 'path';
|
|
6
6
|
import pc2 from 'picocolors';
|
|
7
7
|
import { execSync } from 'child_process';
|
|
8
|
+
import { createRequire } from 'module';
|
|
9
|
+
import { pathToFileURL } from 'url';
|
|
10
|
+
|
|
11
|
+
function logSuccess(msg) {
|
|
12
|
+
console.log(pc2.green("\u2714") + " " + msg);
|
|
13
|
+
}
|
|
14
|
+
function logInfo(msg) {
|
|
15
|
+
console.log(pc2.blue("\u2139") + " " + msg);
|
|
16
|
+
}
|
|
17
|
+
function logWarn(msg) {
|
|
18
|
+
console.log(pc2.yellow("\u26A0") + " " + msg);
|
|
19
|
+
}
|
|
20
|
+
function logError(msg) {
|
|
21
|
+
console.error(pc2.red("\u2716") + " " + msg);
|
|
22
|
+
}
|
|
23
|
+
function logStep(step, total, msg) {
|
|
24
|
+
console.log(pc2.dim(`[${step}/${total}]`) + " " + msg);
|
|
25
|
+
}
|
|
26
|
+
function ensureDir(dir) {
|
|
27
|
+
if (!existsSync(dir)) {
|
|
28
|
+
mkdirSync(dir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function writeFileSafe(filePath, content) {
|
|
32
|
+
ensureDir(dirname(filePath));
|
|
33
|
+
writeFileSync(filePath, content, "utf-8");
|
|
34
|
+
}
|
|
35
|
+
function isDirEmpty(dir) {
|
|
36
|
+
if (!existsSync(dir)) return true;
|
|
37
|
+
return readdirSync(dir).length === 0;
|
|
38
|
+
}
|
|
39
|
+
function readFileSafe(filePath) {
|
|
40
|
+
if (!existsSync(filePath)) return null;
|
|
41
|
+
return readFileSync(filePath, "utf-8");
|
|
42
|
+
}
|
|
8
43
|
|
|
9
44
|
// src/constants.ts
|
|
10
45
|
var CLI_NAME = "tigercat";
|
|
11
|
-
var CLI_VERSION = "1.
|
|
46
|
+
var CLI_VERSION = "1.5.0";
|
|
12
47
|
var TEMPLATES = ["vue3", "react"];
|
|
13
48
|
var TEMPLATE_VERSIONS = {
|
|
14
49
|
// Tigercat packages (use caret on latest major)
|
|
15
|
-
tigercat: "^1.
|
|
50
|
+
tigercat: "^1.5.0",
|
|
16
51
|
// Frameworks
|
|
17
|
-
vue: "^3.5.
|
|
18
|
-
react: "^19.2.
|
|
19
|
-
reactDom: "^19.2.
|
|
52
|
+
vue: "^3.5.38",
|
|
53
|
+
react: "^19.2.7",
|
|
54
|
+
reactDom: "^19.2.7",
|
|
20
55
|
// Build toolchain
|
|
21
56
|
typescript: "^6.0.3",
|
|
22
|
-
vite: "^8.0
|
|
23
|
-
tailwindcss: "^4.
|
|
24
|
-
tailwindcssVite: "^4.
|
|
57
|
+
vite: "^8.1.0",
|
|
58
|
+
tailwindcss: "^4.3.1",
|
|
59
|
+
tailwindcssVite: "^4.3.1",
|
|
25
60
|
// Vite plugins
|
|
26
|
-
vitejsPluginVue: "^6.0.
|
|
27
|
-
vitejsPluginReact: "^6.0.
|
|
61
|
+
vitejsPluginVue: "^6.0.7",
|
|
62
|
+
vitejsPluginReact: "^6.0.3",
|
|
28
63
|
// Type definitions
|
|
29
|
-
typesReact: "^19.2.
|
|
64
|
+
typesReact: "^19.2.17",
|
|
30
65
|
typesReactDom: "^19.2.3",
|
|
31
66
|
// Vue-specific
|
|
32
67
|
vueTsconfig: "^0.9.1",
|
|
33
|
-
vueTsc: "^3.
|
|
68
|
+
vueTsc: "^3.3.5"
|
|
34
69
|
};
|
|
35
70
|
var COMPONENT_CATEGORIES = {
|
|
36
71
|
basic: [
|
|
@@ -86,37 +121,50 @@ var COMPONENT_CATEGORIES = {
|
|
|
86
121
|
]
|
|
87
122
|
};
|
|
88
123
|
var ALL_COMPONENTS = Object.values(COMPONENT_CATEGORIES).flat();
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
function
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
console.error(pc2.red("\u2716") + " " + msg);
|
|
100
|
-
}
|
|
101
|
-
function logStep(step, total, msg) {
|
|
102
|
-
console.log(pc2.dim(`[${step}/${total}]`) + " " + msg);
|
|
103
|
-
}
|
|
104
|
-
function ensureDir(dir) {
|
|
105
|
-
if (!existsSync(dir)) {
|
|
106
|
-
mkdirSync(dir, { recursive: true });
|
|
124
|
+
|
|
125
|
+
// src/utils/validate.ts
|
|
126
|
+
var PACKAGE_NAME_REGEX = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
127
|
+
function validateProjectName(name) {
|
|
128
|
+
if (name.trim().length === 0) return "Project name must not be empty";
|
|
129
|
+
if (name.trim() !== name) return "Project name must not have leading or trailing spaces";
|
|
130
|
+
if (name.length > 214) return "Project name must be 214 characters or fewer";
|
|
131
|
+
if (/[A-Z]/.test(name)) return "Project name must not contain uppercase letters";
|
|
132
|
+
if (!PACKAGE_NAME_REGEX.test(name)) {
|
|
133
|
+
return 'Project name is not a valid npm package name (use lowercase letters, digits, "-", "_" or ".")';
|
|
107
134
|
}
|
|
135
|
+
return null;
|
|
108
136
|
}
|
|
109
|
-
function
|
|
110
|
-
|
|
111
|
-
|
|
137
|
+
function suggestProjectName(name) {
|
|
138
|
+
const sanitizeSegment = (segment) => segment.trim().toLowerCase().replace(/[^a-z0-9-._~]+/g, "-").replace(/^[-._~]+|[-._~]+$/g, "").replace(/-{2,}/g, "-");
|
|
139
|
+
const scopeMatch = /^@(.+?)\/(.+)$/.exec(name.trim());
|
|
140
|
+
const suggestion = scopeMatch ? `@${sanitizeSegment(scopeMatch[1])}/${sanitizeSegment(scopeMatch[2])}` : sanitizeSegment(name);
|
|
141
|
+
return suggestion || "tigercat-app";
|
|
112
142
|
}
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
return readdirSync(dir).length === 0;
|
|
143
|
+
function isFramework(value) {
|
|
144
|
+
return value === "vue3" || value === "react";
|
|
116
145
|
}
|
|
117
|
-
function
|
|
118
|
-
if (
|
|
119
|
-
|
|
146
|
+
async function resolveTemplateOption(arg, promptMessage) {
|
|
147
|
+
if (arg !== void 0) {
|
|
148
|
+
if (!TEMPLATES.includes(arg)) {
|
|
149
|
+
logError(`Invalid template "${arg}". Valid templates: ${TEMPLATES.join(", ")}`);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
return arg;
|
|
153
|
+
}
|
|
154
|
+
const response = await prompts({
|
|
155
|
+
type: "select",
|
|
156
|
+
name: "template",
|
|
157
|
+
message: promptMessage,
|
|
158
|
+
choices: [
|
|
159
|
+
{ title: "Vue 3", value: "vue3" },
|
|
160
|
+
{ title: "React", value: "react" }
|
|
161
|
+
]
|
|
162
|
+
});
|
|
163
|
+
if (!response.template) {
|
|
164
|
+
logError("Operation cancelled");
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
return response.template;
|
|
120
168
|
}
|
|
121
169
|
|
|
122
170
|
// src/templates/vue3.ts
|
|
@@ -534,31 +582,18 @@ function createCreateCommand() {
|
|
|
534
582
|
});
|
|
535
583
|
}
|
|
536
584
|
async function runCreate(name, templateArg, dryRun = false) {
|
|
537
|
-
|
|
538
|
-
if (
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
const response = await prompts({
|
|
542
|
-
type: "select",
|
|
543
|
-
name: "template",
|
|
544
|
-
message: "Select a framework",
|
|
545
|
-
choices: [
|
|
546
|
-
{ title: "Vue 3", value: "vue3" },
|
|
547
|
-
{ title: "React", value: "react" }
|
|
548
|
-
]
|
|
549
|
-
});
|
|
550
|
-
if (!response.template) {
|
|
551
|
-
logError("Operation cancelled");
|
|
552
|
-
process.exit(1);
|
|
553
|
-
}
|
|
554
|
-
template = response.template;
|
|
585
|
+
const nameError = validateProjectName(name);
|
|
586
|
+
if (nameError) {
|
|
587
|
+
logError(`${nameError}. Try "${suggestProjectName(name)}" instead.`);
|
|
588
|
+
process.exit(1);
|
|
555
589
|
}
|
|
590
|
+
const template = await resolveTemplateOption(templateArg, "Select a framework");
|
|
556
591
|
const targetDir = resolve(process.cwd(), name);
|
|
557
592
|
if (!dryRun && existsSync(targetDir) && !isDirEmpty(targetDir)) {
|
|
558
593
|
const { overwrite } = await prompts({
|
|
559
594
|
type: "confirm",
|
|
560
595
|
name: "overwrite",
|
|
561
|
-
message: `Directory "${name}" is not empty.
|
|
596
|
+
message: `Directory "${name}" is not empty. Overwrite conflicting template files? (other files are kept)`,
|
|
562
597
|
initial: false
|
|
563
598
|
});
|
|
564
599
|
if (!overwrite) {
|
|
@@ -590,6 +625,20 @@ async function runCreate(name, templateArg, dryRun = false) {
|
|
|
590
625
|
console.log(" pnpm install");
|
|
591
626
|
console.log(" pnpm dev\n");
|
|
592
627
|
}
|
|
628
|
+
function runCommand(command, options = {}) {
|
|
629
|
+
try {
|
|
630
|
+
execSync(command, { cwd: options.cwd, stdio: "inherit" });
|
|
631
|
+
} catch (error) {
|
|
632
|
+
if (options.allowFailure) return;
|
|
633
|
+
if (options.failureMessage !== void 0) {
|
|
634
|
+
logError(options.failureMessage);
|
|
635
|
+
process.exit(1);
|
|
636
|
+
}
|
|
637
|
+
throw error;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// src/commands/add.ts
|
|
593
642
|
function createAddCommand() {
|
|
594
643
|
return new Command("add").argument("[components...]", "Component names to add (e.g. Button Input Select)").option("-f, --framework <framework>", "Framework override (vue3 | react)").option("--install", "Install missing Tigercat dependencies before generating snippets").option("--snippet <file>", "Generate a reusable import snippet file").option("--dry-run", "Preview generated demo files without writing them").description("Add component import boilerplate to your project").action(async (components, opts) => {
|
|
595
644
|
await runAdd(components ?? [], opts);
|
|
@@ -608,8 +657,7 @@ function detectFramework(cwd) {
|
|
|
608
657
|
return null;
|
|
609
658
|
}
|
|
610
659
|
function normalizeFramework(value) {
|
|
611
|
-
|
|
612
|
-
return null;
|
|
660
|
+
return value !== void 0 && isFramework(value) ? value : null;
|
|
613
661
|
}
|
|
614
662
|
async function resolveComponents(components) {
|
|
615
663
|
if (components.length > 0) return components;
|
|
@@ -661,6 +709,10 @@ function validateComponents(names) {
|
|
|
661
709
|
}
|
|
662
710
|
async function runAdd(components, options = {}) {
|
|
663
711
|
const cwd = process.cwd();
|
|
712
|
+
if (options.framework !== void 0 && !isFramework(options.framework)) {
|
|
713
|
+
logError(`Invalid framework "${options.framework}". Valid frameworks: vue3, react`);
|
|
714
|
+
process.exit(1);
|
|
715
|
+
}
|
|
664
716
|
const framework = normalizeFramework(options.framework) ?? detectFramework(cwd);
|
|
665
717
|
const dryRun = Boolean(options.dryRun);
|
|
666
718
|
if (!framework) {
|
|
@@ -672,7 +724,7 @@ async function runAdd(components, options = {}) {
|
|
|
672
724
|
const selectedComponents = await resolveComponents(components);
|
|
673
725
|
const { valid, invalid } = validateComponents(selectedComponents);
|
|
674
726
|
if (invalid.length > 0) {
|
|
675
|
-
|
|
727
|
+
logWarn(`Unknown components: ${invalid.join(", ")}`);
|
|
676
728
|
logInfo(`Available: ${ALL_COMPONENTS.join(", ")}`);
|
|
677
729
|
}
|
|
678
730
|
if (valid.length === 0) {
|
|
@@ -688,7 +740,7 @@ async function runAdd(components, options = {}) {
|
|
|
688
740
|
const installCommand = formatAddCommand(packageManager, missingDeps);
|
|
689
741
|
if (options.install && !dryRun) {
|
|
690
742
|
logInfo(`Installing missing dependencies: ${missingDeps.join(", ")}`);
|
|
691
|
-
|
|
743
|
+
runCommand(installCommand, { cwd });
|
|
692
744
|
} else {
|
|
693
745
|
logInfo(`Missing dependencies detected. Run: ${installCommand}`);
|
|
694
746
|
}
|
|
@@ -731,7 +783,7 @@ async function runAdd(components, options = {}) {
|
|
|
731
783
|
const ext = framework === "vue3" ? "vue" : "tsx";
|
|
732
784
|
const sampleFile = join(sampleDir, `${comp}Demo.${ext}`);
|
|
733
785
|
if (existsSync(sampleFile)) {
|
|
734
|
-
|
|
786
|
+
logWarn(`${sampleFile} already exists, skipping`);
|
|
735
787
|
continue;
|
|
736
788
|
}
|
|
737
789
|
if (dryRun) {
|
|
@@ -783,29 +835,14 @@ function createPlaygroundCommand() {
|
|
|
783
835
|
});
|
|
784
836
|
}
|
|
785
837
|
async function runPlayground(templateArg, port = "3456", open = true, dryRun = false) {
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
const response = await prompts({
|
|
791
|
-
type: "select",
|
|
792
|
-
name: "template",
|
|
793
|
-
message: "Select a framework for playground",
|
|
794
|
-
choices: [
|
|
795
|
-
{ title: "Vue 3", value: "vue3" },
|
|
796
|
-
{ title: "React", value: "react" }
|
|
797
|
-
]
|
|
798
|
-
});
|
|
799
|
-
if (!response.template) {
|
|
800
|
-
logError("Operation cancelled");
|
|
801
|
-
process.exit(1);
|
|
802
|
-
}
|
|
803
|
-
template = response.template;
|
|
804
|
-
}
|
|
838
|
+
const template = await resolveTemplateOption(
|
|
839
|
+
templateArg,
|
|
840
|
+
"Select a framework for playground"
|
|
841
|
+
);
|
|
805
842
|
const tmpDir = resolve(process.cwd(), ".tigercat-playground");
|
|
806
843
|
const projectDir = join(tmpDir, `playground-${template}`);
|
|
807
844
|
if (dryRun) {
|
|
808
|
-
const
|
|
845
|
+
const safePort2 = /^\d+$/.test(port) ? port : "3456";
|
|
809
846
|
logInfo(`Dry run: would prepare ${template} playground in ${projectDir}.`);
|
|
810
847
|
if (!existsSync(projectDir)) {
|
|
811
848
|
const files = template === "vue3" ? getVue3Template("playground") : getReactTemplate("playground");
|
|
@@ -814,7 +851,7 @@ async function runPlayground(templateArg, port = "3456", open = true, dryRun = f
|
|
|
814
851
|
}
|
|
815
852
|
logInfo("Would run pnpm install");
|
|
816
853
|
}
|
|
817
|
-
logInfo(`Would start Vite on port ${
|
|
854
|
+
logInfo(`Would start Vite on port ${safePort2}${open ? " and open the browser" : ""}`);
|
|
818
855
|
return;
|
|
819
856
|
}
|
|
820
857
|
if (!existsSync(projectDir)) {
|
|
@@ -829,21 +866,16 @@ async function runPlayground(templateArg, port = "3456", open = true, dryRun = f
|
|
|
829
866
|
writeFileSafe(resolve(projectDir, filePath), content);
|
|
830
867
|
}
|
|
831
868
|
logInfo("Installing dependencies...");
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
process.exit(1);
|
|
837
|
-
}
|
|
869
|
+
runCommand("pnpm install", {
|
|
870
|
+
cwd: projectDir,
|
|
871
|
+
failureMessage: "Failed to install dependencies. Make sure pnpm is available."
|
|
872
|
+
});
|
|
838
873
|
}
|
|
839
874
|
logSuccess(`Starting playground on port ${port}...
|
|
840
875
|
`);
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
execSync(`npx vite --port ${safePort}${openFlag}`, { cwd: projectDir, stdio: "inherit" });
|
|
845
|
-
} catch {
|
|
846
|
-
}
|
|
876
|
+
const safePort = /^\d+$/.test(port) ? port : "3456";
|
|
877
|
+
const openFlag = open ? " --open" : "";
|
|
878
|
+
runCommand(`npx vite --port ${safePort}${openFlag}`, { cwd: projectDir, allowFailure: true });
|
|
847
879
|
}
|
|
848
880
|
function createGenerateCommand() {
|
|
849
881
|
const cmd = new Command("generate").description("Code generation utilities");
|
|
@@ -1131,13 +1163,37 @@ Document keyboard behavior, roles, labels, and focus management.
|
|
|
1131
1163
|
List boundary states, empty states, loading states, and controlled/uncontrolled behavior.
|
|
1132
1164
|
`;
|
|
1133
1165
|
}
|
|
1134
|
-
var
|
|
1135
|
-
var
|
|
1166
|
+
var MIN_NODE_VERSION = { major: 22, minor: 13, patch: 0 };
|
|
1167
|
+
var MIN_PNPM_VERSION = { major: 11, minor: 9, patch: 0 };
|
|
1168
|
+
var MIN_NODE_RANGE = "22.13.0";
|
|
1169
|
+
var MIN_PNPM_RANGE = "11.9.0";
|
|
1136
1170
|
var REQUIRED_TAILWIND_MAJOR = 4;
|
|
1137
1171
|
var REQUIRED_TIGERCAT_MAJOR = 1;
|
|
1172
|
+
var FRAMEWORK_PEER_RANGES = {
|
|
1173
|
+
vue3: [{ dep: "vue", major: 3 }],
|
|
1174
|
+
react: [
|
|
1175
|
+
{ dep: "react", major: 19 },
|
|
1176
|
+
{ dep: "react-dom", major: 19 }
|
|
1177
|
+
]
|
|
1178
|
+
};
|
|
1179
|
+
var REQUIRED_CORE_EXPORTS = [
|
|
1180
|
+
".",
|
|
1181
|
+
"./tailwind",
|
|
1182
|
+
"./tailwind/modern",
|
|
1183
|
+
"./tokens.css",
|
|
1184
|
+
"./figma-variables.json"
|
|
1185
|
+
];
|
|
1138
1186
|
var VERSION_COMPATIBILITY_MATRIX = [
|
|
1139
|
-
{
|
|
1140
|
-
|
|
1187
|
+
{
|
|
1188
|
+
name: "Node.js",
|
|
1189
|
+
range: `>=${MIN_NODE_RANGE}`,
|
|
1190
|
+
reason: "Matches workspace engines and CLI templates"
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
name: "pnpm",
|
|
1194
|
+
range: `>=${MIN_PNPM_RANGE}`,
|
|
1195
|
+
reason: "Required by workspace package management"
|
|
1196
|
+
},
|
|
1141
1197
|
{ name: "Tailwind CSS", range: ">=4.0.0", reason: "Required by Tigercat theme utilities" },
|
|
1142
1198
|
{ name: "Vue", range: "^3.0.0", reason: "Peer range for @expcat/tigercat-vue" },
|
|
1143
1199
|
{ name: "React", range: "^19.0.0", reason: "Peer range for @expcat/tigercat-react" }
|
|
@@ -1160,6 +1216,7 @@ function createDoctorCommand() {
|
|
|
1160
1216
|
function collectDoctorChecks(options = {}) {
|
|
1161
1217
|
const cwd = options.cwd ?? process.cwd();
|
|
1162
1218
|
const env = options.env ?? process.env;
|
|
1219
|
+
const readCorePackageJson = options.readCorePackageJson ?? defaultReadCorePackageJson;
|
|
1163
1220
|
const packageResult = readProjectPackage(cwd);
|
|
1164
1221
|
const nodeVersion = options.nodeVersion ?? process.versions.node;
|
|
1165
1222
|
const checks = [createPackageCheck(packageResult)];
|
|
@@ -1172,6 +1229,14 @@ function collectDoctorChecks(options = {}) {
|
|
|
1172
1229
|
checks.push(createPeerDepsCheck(packageResult.packageJson));
|
|
1173
1230
|
checks.push(createTemplateCompatibilityCheck(packageResult.packageJson));
|
|
1174
1231
|
checks.push(createCompatibilityMatrixCheck(packageResult.packageJson));
|
|
1232
|
+
const coreExportsCheck = createCoreExportsCheck(
|
|
1233
|
+
packageResult.packageJson,
|
|
1234
|
+
cwd,
|
|
1235
|
+
readCorePackageJson
|
|
1236
|
+
);
|
|
1237
|
+
if (coreExportsCheck) {
|
|
1238
|
+
checks.push(coreExportsCheck);
|
|
1239
|
+
}
|
|
1175
1240
|
return checks;
|
|
1176
1241
|
}
|
|
1177
1242
|
function runDoctor(json = false) {
|
|
@@ -1206,7 +1271,7 @@ function runDoctor(json = false) {
|
|
|
1206
1271
|
process.exit(1);
|
|
1207
1272
|
}
|
|
1208
1273
|
if (warnings.length > 0) {
|
|
1209
|
-
|
|
1274
|
+
logWarn(`${warnings.length} warning${warnings.length === 1 ? "" : "s"} found`);
|
|
1210
1275
|
return;
|
|
1211
1276
|
}
|
|
1212
1277
|
logSuccess("All checks passed");
|
|
@@ -1240,18 +1305,18 @@ function createPackageCheck(result) {
|
|
|
1240
1305
|
}
|
|
1241
1306
|
function createNodeCheck(version) {
|
|
1242
1307
|
const parsed = parseVersion(version);
|
|
1243
|
-
if (!parsed || parsed
|
|
1308
|
+
if (!parsed || isVersionLessThan(parsed, MIN_NODE_VERSION)) {
|
|
1244
1309
|
return {
|
|
1245
1310
|
name: "Node.js",
|
|
1246
1311
|
status: "fail",
|
|
1247
|
-
message: `Node ${
|
|
1248
|
-
suggestions: [`Install Node ${
|
|
1312
|
+
message: `Node ${MIN_NODE_RANGE}+ is required, current version is ${version}`,
|
|
1313
|
+
suggestions: [`Install Node ${MIN_NODE_RANGE}+ and rerun tigercat doctor`]
|
|
1249
1314
|
};
|
|
1250
1315
|
}
|
|
1251
1316
|
return {
|
|
1252
1317
|
name: "Node.js",
|
|
1253
1318
|
status: "pass",
|
|
1254
|
-
message: `Node ${version} satisfies >=${
|
|
1319
|
+
message: `Node ${version} satisfies >=${MIN_NODE_RANGE}`
|
|
1255
1320
|
};
|
|
1256
1321
|
}
|
|
1257
1322
|
function createPnpmCheck(packageJson, env) {
|
|
@@ -1260,23 +1325,23 @@ function createPnpmCheck(packageJson, env) {
|
|
|
1260
1325
|
return {
|
|
1261
1326
|
name: "pnpm",
|
|
1262
1327
|
status: "warn",
|
|
1263
|
-
message: `Could not detect pnpm version; Tigercat templates expect pnpm ${
|
|
1264
|
-
suggestions: ["Add packageManager: pnpm@
|
|
1328
|
+
message: `Could not detect pnpm version; Tigercat templates expect pnpm ${MIN_PNPM_RANGE}+`,
|
|
1329
|
+
suggestions: ["Add packageManager: pnpm@11.9.0 to package.json or run through pnpm"]
|
|
1265
1330
|
};
|
|
1266
1331
|
}
|
|
1267
1332
|
const parsed = parseVersion(version);
|
|
1268
|
-
if (!parsed || parsed
|
|
1333
|
+
if (!parsed || isVersionLessThan(parsed, MIN_PNPM_VERSION)) {
|
|
1269
1334
|
return {
|
|
1270
1335
|
name: "pnpm",
|
|
1271
1336
|
status: "fail",
|
|
1272
|
-
message: `pnpm ${
|
|
1273
|
-
suggestions: [`Upgrade pnpm to ${
|
|
1337
|
+
message: `pnpm ${MIN_PNPM_RANGE}+ is required, detected ${version}`,
|
|
1338
|
+
suggestions: [`Upgrade pnpm to ${MIN_PNPM_RANGE}+`]
|
|
1274
1339
|
};
|
|
1275
1340
|
}
|
|
1276
1341
|
return {
|
|
1277
1342
|
name: "pnpm",
|
|
1278
1343
|
status: "pass",
|
|
1279
|
-
message: `pnpm ${version} satisfies >=${
|
|
1344
|
+
message: `pnpm ${version} satisfies >=${MIN_PNPM_RANGE}`
|
|
1280
1345
|
};
|
|
1281
1346
|
}
|
|
1282
1347
|
function createTailwindCheck(packageJson) {
|
|
@@ -1424,13 +1489,71 @@ function createCompatibilityMatrixCheck(packageJson) {
|
|
|
1424
1489
|
suggestions: ["Install a Tigercat Vue or React package to validate framework peer ranges"]
|
|
1425
1490
|
};
|
|
1426
1491
|
}
|
|
1492
|
+
const incompatible = frameworks.flatMap(
|
|
1493
|
+
(framework) => FRAMEWORK_PEER_RANGES[framework].filter(({ dep, major }) => isOlderMajor(dependencies[dep], major)).map(({ dep, major }) => `${dep}@${dependencies[dep]} is below the supported major ${major}`)
|
|
1494
|
+
);
|
|
1495
|
+
if (incompatible.length > 0) {
|
|
1496
|
+
return {
|
|
1497
|
+
name: "Version compatibility matrix",
|
|
1498
|
+
status: "fail",
|
|
1499
|
+
message: "Installed framework versions are outside the supported range",
|
|
1500
|
+
details: [...incompatible, ...details],
|
|
1501
|
+
suggestions: ["Upgrade the listed framework packages to the supported major versions"]
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1427
1504
|
return {
|
|
1428
1505
|
name: "Version compatibility matrix",
|
|
1429
1506
|
status: "pass",
|
|
1430
|
-
message: `${frameworks.map(formatFramework).join(" + ")} compatibility matrix
|
|
1507
|
+
message: `${frameworks.map(formatFramework).join(" + ")} versions satisfy the compatibility matrix`,
|
|
1431
1508
|
details
|
|
1432
1509
|
};
|
|
1433
1510
|
}
|
|
1511
|
+
function createCoreExportsCheck(packageJson, cwd, readCorePackageJson) {
|
|
1512
|
+
const dependencies = collectDependencies2(packageJson);
|
|
1513
|
+
if (!dependencies["@expcat/tigercat-core"]) {
|
|
1514
|
+
return null;
|
|
1515
|
+
}
|
|
1516
|
+
const corePackageJson = readCorePackageJson(cwd);
|
|
1517
|
+
if (!corePackageJson) {
|
|
1518
|
+
return null;
|
|
1519
|
+
}
|
|
1520
|
+
const exportsMap = corePackageJson.exports ?? {};
|
|
1521
|
+
const missing = REQUIRED_CORE_EXPORTS.filter((subpath) => !(subpath in exportsMap));
|
|
1522
|
+
if (missing.length > 0) {
|
|
1523
|
+
return {
|
|
1524
|
+
name: "Core exports",
|
|
1525
|
+
status: "fail",
|
|
1526
|
+
message: "@expcat/tigercat-core is missing required export subpaths",
|
|
1527
|
+
details: missing.map((subpath) => `Missing export: ${subpath}`),
|
|
1528
|
+
suggestions: ["Upgrade @expcat/tigercat-core or reinstall dependencies"]
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
return {
|
|
1532
|
+
name: "Core exports",
|
|
1533
|
+
status: "pass",
|
|
1534
|
+
message: "@expcat/tigercat-core exposes the required Tailwind, token and entry exports"
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1537
|
+
function defaultReadCorePackageJson(cwd) {
|
|
1538
|
+
try {
|
|
1539
|
+
const requireFromProject = createRequire(pathToFileURL(join(cwd, "package.json")));
|
|
1540
|
+
const mainEntry = requireFromProject.resolve("@expcat/tigercat-core");
|
|
1541
|
+
let dir = dirname(mainEntry);
|
|
1542
|
+
for (let depth = 0; depth < 6; depth++) {
|
|
1543
|
+
const content = readFileSafe(join(dir, "package.json"));
|
|
1544
|
+
if (content) {
|
|
1545
|
+
const parsed = JSON.parse(content);
|
|
1546
|
+
if (parsed.name === "@expcat/tigercat-core") return parsed;
|
|
1547
|
+
}
|
|
1548
|
+
const parent = dirname(dir);
|
|
1549
|
+
if (parent === dir) break;
|
|
1550
|
+
dir = parent;
|
|
1551
|
+
}
|
|
1552
|
+
return null;
|
|
1553
|
+
} catch {
|
|
1554
|
+
return null;
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1434
1557
|
function collectDependencies2(packageJson) {
|
|
1435
1558
|
return {
|
|
1436
1559
|
...packageJson.peerDependencies,
|
|
@@ -1467,6 +1590,11 @@ function parseVersion(value) {
|
|
|
1467
1590
|
patch: Number(match[3] ?? 0)
|
|
1468
1591
|
};
|
|
1469
1592
|
}
|
|
1593
|
+
function isVersionLessThan(current, minimum) {
|
|
1594
|
+
if (current.major !== minimum.major) return current.major < minimum.major;
|
|
1595
|
+
if (current.minor !== minimum.minor) return current.minor < minimum.minor;
|
|
1596
|
+
return current.patch < minimum.patch;
|
|
1597
|
+
}
|
|
1470
1598
|
function getRangeMajor(range) {
|
|
1471
1599
|
if (!range) return null;
|
|
1472
1600
|
if (/^(workspace|file|link|catalog):/.test(range)) return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expcat/tigercat-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI tooling for Tigercat UI library — project scaffolding, component generation, and more",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,14 +28,16 @@
|
|
|
28
28
|
"module": "./dist/index.js",
|
|
29
29
|
"types": "./dist/index.d.ts",
|
|
30
30
|
"files": [
|
|
31
|
-
"dist"
|
|
32
|
-
"templates"
|
|
31
|
+
"dist"
|
|
33
32
|
],
|
|
34
33
|
"publishConfig": {
|
|
35
34
|
"access": "public"
|
|
36
35
|
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=22.12.0"
|
|
38
|
+
},
|
|
37
39
|
"dependencies": {
|
|
38
|
-
"commander": "^
|
|
40
|
+
"commander": "^15.0.0",
|
|
39
41
|
"picocolors": "^1.1.1",
|
|
40
42
|
"prompts": "^2.4.2"
|
|
41
43
|
},
|