mycontext-cli 2.0.2 → 2.0.4
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 +426 -103
- package/dist/README.md +426 -103
- package/dist/agents/implementations/ClaudeAgentWorkflow.d.ts.map +1 -1
- package/dist/agents/implementations/ClaudeAgentWorkflow.js +75 -4
- package/dist/agents/implementations/ClaudeAgentWorkflow.js.map +1 -1
- package/dist/agents/implementations/CodeGenSubAgent.d.ts.map +1 -1
- package/dist/agents/implementations/CodeGenSubAgent.js +69 -0
- package/dist/agents/implementations/CodeGenSubAgent.js.map +1 -1
- package/dist/agents/implementations/PromptConstructorAgent.d.ts.map +1 -1
- package/dist/agents/implementations/PromptConstructorAgent.js +23 -0
- package/dist/agents/implementations/PromptConstructorAgent.js.map +1 -1
- package/dist/agents/interfaces/SubAgent.d.ts +2 -0
- package/dist/agents/interfaces/SubAgent.d.ts.map +1 -1
- package/dist/cli.js +13 -40
- package/dist/cli.js.map +1 -1
- package/dist/commands/generate-components.d.ts +0 -5
- package/dist/commands/generate-components.d.ts.map +1 -1
- package/dist/commands/generate-components.js +0 -101
- package/dist/commands/generate-components.js.map +1 -1
- package/dist/commands/health-check.d.ts +28 -0
- package/dist/commands/health-check.d.ts.map +1 -0
- package/dist/commands/health-check.js +271 -0
- package/dist/commands/health-check.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +2 -6
- package/dist/commands/init.js.map +1 -1
- package/dist/config/build-strategies.json +173 -22
- package/dist/package.json +2 -2
- package/dist/utils/NextJSProjectGenerator.d.ts +70 -0
- package/dist/utils/NextJSProjectGenerator.d.ts.map +1 -0
- package/dist/utils/NextJSProjectGenerator.js +811 -0
- package/dist/utils/NextJSProjectGenerator.js.map +1 -0
- package/dist/utils/NextJSProjectValidator.d.ts +103 -0
- package/dist/utils/NextJSProjectValidator.d.ts.map +1 -0
- package/dist/utils/NextJSProjectValidator.js +759 -0
- package/dist/utils/NextJSProjectValidator.js.map +1 -0
- package/dist/utils/PreCommandValidator.d.ts +77 -0
- package/dist/utils/PreCommandValidator.d.ts.map +1 -0
- package/dist/utils/PreCommandValidator.js +251 -0
- package/dist/utils/PreCommandValidator.js.map +1 -0
- package/dist/utils/ProjectHealthMonitor.d.ts +131 -0
- package/dist/utils/ProjectHealthMonitor.d.ts.map +1 -0
- package/dist/utils/ProjectHealthMonitor.js +454 -0
- package/dist/utils/ProjectHealthMonitor.js.map +1 -0
- package/dist/utils/ProjectInitializationSafeguards.d.ts +81 -0
- package/dist/utils/ProjectInitializationSafeguards.d.ts.map +1 -0
- package/dist/utils/ProjectInitializationSafeguards.js +620 -0
- package/dist/utils/ProjectInitializationSafeguards.js.map +1 -0
- package/dist/utils/ProjectStructureRepair.d.ts +110 -0
- package/dist/utils/ProjectStructureRepair.d.ts.map +1 -0
- package/dist/utils/ProjectStructureRepair.js +785 -0
- package/dist/utils/ProjectStructureRepair.js.map +1 -0
- package/dist/utils/ProjectStructureValidator.d.ts +128 -0
- package/dist/utils/ProjectStructureValidator.d.ts.map +1 -0
- package/dist/utils/ProjectStructureValidator.js +662 -0
- package/dist/utils/ProjectStructureValidator.js.map +1 -0
- package/dist/utils/agentDefinitions.d.ts +5 -0
- package/dist/utils/agentDefinitions.d.ts.map +1 -1
- package/dist/utils/agentDefinitions.js +99 -1
- package/dist/utils/agentDefinitions.js.map +1 -1
- package/dist/utils/mcpTools.d.ts +189 -0
- package/dist/utils/mcpTools.d.ts.map +1 -1
- package/dist/utils/mcpTools.js +462 -1
- package/dist/utils/mcpTools.js.map +1 -1
- package/package.json +2 -2
- package/dist/commands/core.d.ts +0 -24
- package/dist/commands/core.d.ts.map +0 -1
- package/dist/commands/core.js +0 -410
- package/dist/commands/core.js.map +0 -1
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.NextJSProjectValidator = void 0;
|
|
40
|
+
const fs = __importStar(require("fs-extra"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
+
const ProjectStructureValidator_1 = require("./ProjectStructureValidator");
|
|
44
|
+
class NextJSProjectValidator {
|
|
45
|
+
constructor(projectRoot = process.cwd()) {
|
|
46
|
+
this.projectRoot = projectRoot;
|
|
47
|
+
this.validator = new ProjectStructureValidator_1.ProjectStructureValidator(projectRoot);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Comprehensive Next.js project validation
|
|
51
|
+
*/
|
|
52
|
+
async validateNextJSProject() {
|
|
53
|
+
console.log(chalk_1.default.blue("🔍 Validating Next.js project structure..."));
|
|
54
|
+
const issues = [];
|
|
55
|
+
// Run all Next.js specific validation checks
|
|
56
|
+
await this.validateAppRouterStructure(issues);
|
|
57
|
+
await this.validateLayoutFiles(issues);
|
|
58
|
+
await this.validatePageFiles(issues);
|
|
59
|
+
await this.validateComponentStructure(issues);
|
|
60
|
+
await this.validateShadcnIntegration(issues);
|
|
61
|
+
await this.validateNextJSConfiguration(issues);
|
|
62
|
+
await this.validateDependencies(issues);
|
|
63
|
+
const isValid = issues.filter((issue) => issue.type === "error").length === 0;
|
|
64
|
+
const appRouterCompliant = this.checkAppRouterCompliance(issues);
|
|
65
|
+
const shadcnCompliant = this.checkShadcnCompliance(issues);
|
|
66
|
+
return {
|
|
67
|
+
isValid,
|
|
68
|
+
issues,
|
|
69
|
+
recommendations: this.generateRecommendations(issues),
|
|
70
|
+
appRouterCompliant,
|
|
71
|
+
shadcnCompliant,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Validate App Router structure
|
|
76
|
+
*/
|
|
77
|
+
async validateAppRouterStructure(issues) {
|
|
78
|
+
console.log(chalk_1.default.blue("📁 Validating App Router structure..."));
|
|
79
|
+
const appDir = path.join(this.projectRoot, "app");
|
|
80
|
+
// Check if app directory exists
|
|
81
|
+
if (!(await fs.pathExists(appDir))) {
|
|
82
|
+
issues.push({
|
|
83
|
+
type: "error",
|
|
84
|
+
severity: "critical",
|
|
85
|
+
message: "App Router directory 'app' not found. Next.js 13+ requires App Router structure.",
|
|
86
|
+
category: "routing",
|
|
87
|
+
autoFixable: true,
|
|
88
|
+
fix: "Create app directory with root layout.tsx and page.tsx",
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// Check for root layout.tsx
|
|
93
|
+
const rootLayoutPath = path.join(appDir, "layout.tsx");
|
|
94
|
+
if (!(await fs.pathExists(rootLayoutPath))) {
|
|
95
|
+
issues.push({
|
|
96
|
+
type: "error",
|
|
97
|
+
severity: "critical",
|
|
98
|
+
message: "Root layout.tsx not found in app directory",
|
|
99
|
+
file: rootLayoutPath,
|
|
100
|
+
category: "layout",
|
|
101
|
+
autoFixable: true,
|
|
102
|
+
fix: "Create root layout.tsx file",
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
// Check for root page.tsx
|
|
106
|
+
const rootPagePath = path.join(appDir, "page.tsx");
|
|
107
|
+
if (!(await fs.pathExists(rootPagePath))) {
|
|
108
|
+
issues.push({
|
|
109
|
+
type: "error",
|
|
110
|
+
severity: "critical",
|
|
111
|
+
message: "Root page.tsx not found in app directory",
|
|
112
|
+
file: rootPagePath,
|
|
113
|
+
category: "routing",
|
|
114
|
+
autoFixable: true,
|
|
115
|
+
fix: "Create root page.tsx file",
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
// Check for pages directory (conflict with App Router)
|
|
119
|
+
const pagesDir = path.join(this.projectRoot, "pages");
|
|
120
|
+
if (await fs.pathExists(pagesDir)) {
|
|
121
|
+
issues.push({
|
|
122
|
+
type: "warning",
|
|
123
|
+
severity: "high",
|
|
124
|
+
message: "Both 'app' and 'pages' directories found. This can cause routing conflicts.",
|
|
125
|
+
file: pagesDir,
|
|
126
|
+
category: "routing",
|
|
127
|
+
autoFixable: true,
|
|
128
|
+
fix: "Remove pages directory or migrate to App Router",
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// Validate nested routes structure
|
|
132
|
+
await this.validateNestedRoutes(appDir, issues);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Validate nested routes structure
|
|
136
|
+
*/
|
|
137
|
+
async validateNestedRoutes(appDir, issues) {
|
|
138
|
+
const routeDirs = await this.findRouteDirectories(appDir);
|
|
139
|
+
for (const routeDir of routeDirs) {
|
|
140
|
+
const relativePath = path.relative(appDir, routeDir);
|
|
141
|
+
// Check for page.tsx in route directories
|
|
142
|
+
const pagePath = path.join(routeDir, "page.tsx");
|
|
143
|
+
if (!(await fs.pathExists(pagePath))) {
|
|
144
|
+
issues.push({
|
|
145
|
+
type: "warning",
|
|
146
|
+
severity: "medium",
|
|
147
|
+
message: `Route directory '${relativePath}' missing page.tsx file`,
|
|
148
|
+
file: routeDir,
|
|
149
|
+
category: "routing",
|
|
150
|
+
autoFixable: true,
|
|
151
|
+
fix: `Create page.tsx in ${relativePath} directory`,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
// Check for layout.tsx in route directories
|
|
155
|
+
const layoutPath = path.join(routeDir, "layout.tsx");
|
|
156
|
+
if (await fs.pathExists(layoutPath)) {
|
|
157
|
+
// Validate layout structure
|
|
158
|
+
await this.validateLayoutFile(layoutPath, issues);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Validate layout files
|
|
164
|
+
*/
|
|
165
|
+
async validateLayoutFiles(issues) {
|
|
166
|
+
console.log(chalk_1.default.blue("🎨 Validating layout files..."));
|
|
167
|
+
const layoutFiles = await this.findFiles("**/layout.tsx");
|
|
168
|
+
for (const layoutFile of layoutFiles) {
|
|
169
|
+
await this.validateLayoutFile(layoutFile, issues);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Validate individual layout file
|
|
174
|
+
*/
|
|
175
|
+
async validateLayoutFile(layoutPath, issues) {
|
|
176
|
+
try {
|
|
177
|
+
const content = await fs.readFile(layoutPath, "utf-8");
|
|
178
|
+
// Check for proper layout structure
|
|
179
|
+
if (!content.includes("children")) {
|
|
180
|
+
issues.push({
|
|
181
|
+
type: "warning",
|
|
182
|
+
severity: "medium",
|
|
183
|
+
message: "Layout file missing 'children' prop",
|
|
184
|
+
file: layoutPath,
|
|
185
|
+
category: "layout",
|
|
186
|
+
autoFixable: true,
|
|
187
|
+
fix: "Add children prop to layout component",
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
// Check for proper export
|
|
191
|
+
if (!content.includes("export default")) {
|
|
192
|
+
issues.push({
|
|
193
|
+
type: "error",
|
|
194
|
+
severity: "high",
|
|
195
|
+
message: "Layout file missing default export",
|
|
196
|
+
file: layoutPath,
|
|
197
|
+
category: "layout",
|
|
198
|
+
autoFixable: true,
|
|
199
|
+
fix: "Add default export to layout component",
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
// Check for metadata export (recommended)
|
|
203
|
+
if (!content.includes("export const metadata")) {
|
|
204
|
+
issues.push({
|
|
205
|
+
type: "info",
|
|
206
|
+
severity: "low",
|
|
207
|
+
message: "Layout file missing metadata export (recommended for SEO)",
|
|
208
|
+
file: layoutPath,
|
|
209
|
+
category: "layout",
|
|
210
|
+
autoFixable: true,
|
|
211
|
+
fix: "Add metadata export for better SEO",
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
issues.push({
|
|
217
|
+
type: "error",
|
|
218
|
+
severity: "medium",
|
|
219
|
+
message: `Failed to read layout file: ${error}`,
|
|
220
|
+
file: layoutPath,
|
|
221
|
+
category: "layout",
|
|
222
|
+
autoFixable: false,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Validate page files
|
|
228
|
+
*/
|
|
229
|
+
async validatePageFiles(issues) {
|
|
230
|
+
console.log(chalk_1.default.blue("📄 Validating page files..."));
|
|
231
|
+
const pageFiles = await this.findFiles("**/page.tsx");
|
|
232
|
+
for (const pageFile of pageFiles) {
|
|
233
|
+
await this.validatePageFile(pageFile, issues);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Validate individual page file
|
|
238
|
+
*/
|
|
239
|
+
async validatePageFile(pagePath, issues) {
|
|
240
|
+
try {
|
|
241
|
+
const content = await fs.readFile(pagePath, "utf-8");
|
|
242
|
+
// Check for proper export
|
|
243
|
+
if (!content.includes("export default")) {
|
|
244
|
+
issues.push({
|
|
245
|
+
type: "error",
|
|
246
|
+
severity: "high",
|
|
247
|
+
message: "Page file missing default export",
|
|
248
|
+
file: pagePath,
|
|
249
|
+
category: "routing",
|
|
250
|
+
autoFixable: true,
|
|
251
|
+
fix: "Add default export to page component",
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
// Check for metadata export (recommended)
|
|
255
|
+
if (!content.includes("export const metadata")) {
|
|
256
|
+
issues.push({
|
|
257
|
+
type: "info",
|
|
258
|
+
severity: "low",
|
|
259
|
+
message: "Page file missing metadata export (recommended for SEO)",
|
|
260
|
+
file: pagePath,
|
|
261
|
+
category: "routing",
|
|
262
|
+
autoFixable: true,
|
|
263
|
+
fix: "Add metadata export for better SEO",
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
// Check for proper component structure
|
|
267
|
+
if (!content.includes("function") &&
|
|
268
|
+
!content.includes("const") &&
|
|
269
|
+
!content.includes("=>")) {
|
|
270
|
+
issues.push({
|
|
271
|
+
type: "warning",
|
|
272
|
+
severity: "medium",
|
|
273
|
+
message: "Page file doesn't appear to contain a React component",
|
|
274
|
+
file: pagePath,
|
|
275
|
+
category: "routing",
|
|
276
|
+
autoFixable: false,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
issues.push({
|
|
282
|
+
type: "error",
|
|
283
|
+
severity: "medium",
|
|
284
|
+
message: `Failed to read page file: ${error}`,
|
|
285
|
+
file: pagePath,
|
|
286
|
+
category: "routing",
|
|
287
|
+
autoFixable: false,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Validate component structure
|
|
293
|
+
*/
|
|
294
|
+
async validateComponentStructure(issues) {
|
|
295
|
+
console.log(chalk_1.default.blue("🧩 Validating component structure..."));
|
|
296
|
+
// Check for components directory
|
|
297
|
+
const componentsDir = path.join(this.projectRoot, "components");
|
|
298
|
+
if (!(await fs.pathExists(componentsDir))) {
|
|
299
|
+
issues.push({
|
|
300
|
+
type: "warning",
|
|
301
|
+
severity: "medium",
|
|
302
|
+
message: "Components directory not found",
|
|
303
|
+
category: "components",
|
|
304
|
+
autoFixable: true,
|
|
305
|
+
fix: "Create components directory for reusable components",
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
// Check for UI components directory (shadcn/ui pattern)
|
|
309
|
+
const uiDir = path.join(this.projectRoot, "components", "ui");
|
|
310
|
+
if (!(await fs.pathExists(uiDir))) {
|
|
311
|
+
issues.push({
|
|
312
|
+
type: "info",
|
|
313
|
+
severity: "low",
|
|
314
|
+
message: "UI components directory not found (recommended for shadcn/ui)",
|
|
315
|
+
category: "components",
|
|
316
|
+
autoFixable: true,
|
|
317
|
+
fix: "Create components/ui directory for shadcn/ui components",
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
// Validate component files
|
|
321
|
+
const componentFiles = await this.findFiles("**/*.tsx");
|
|
322
|
+
for (const componentFile of componentFiles) {
|
|
323
|
+
if (!componentFile.includes("page.tsx") &&
|
|
324
|
+
!componentFile.includes("layout.tsx")) {
|
|
325
|
+
await this.validateComponentFile(componentFile, issues);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Validate individual component file
|
|
331
|
+
*/
|
|
332
|
+
async validateComponentFile(componentPath, issues) {
|
|
333
|
+
try {
|
|
334
|
+
const content = await fs.readFile(componentPath, "utf-8");
|
|
335
|
+
// Check for proper export
|
|
336
|
+
if (!content.includes("export")) {
|
|
337
|
+
issues.push({
|
|
338
|
+
type: "warning",
|
|
339
|
+
severity: "medium",
|
|
340
|
+
message: "Component file missing export statement",
|
|
341
|
+
file: componentPath,
|
|
342
|
+
category: "components",
|
|
343
|
+
autoFixable: false,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
// Check for TypeScript types
|
|
347
|
+
if (!content.includes("interface") &&
|
|
348
|
+
!content.includes("type") &&
|
|
349
|
+
!content.includes("Props")) {
|
|
350
|
+
issues.push({
|
|
351
|
+
type: "info",
|
|
352
|
+
severity: "low",
|
|
353
|
+
message: "Component file missing TypeScript prop types",
|
|
354
|
+
file: componentPath,
|
|
355
|
+
category: "components",
|
|
356
|
+
autoFixable: true,
|
|
357
|
+
fix: "Add TypeScript prop interface",
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
issues.push({
|
|
363
|
+
type: "error",
|
|
364
|
+
severity: "medium",
|
|
365
|
+
message: `Failed to read component file: ${error}`,
|
|
366
|
+
file: componentPath,
|
|
367
|
+
category: "components",
|
|
368
|
+
autoFixable: false,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Validate shadcn/ui integration
|
|
374
|
+
*/
|
|
375
|
+
async validateShadcnIntegration(issues) {
|
|
376
|
+
console.log(chalk_1.default.blue("🎨 Validating shadcn/ui integration..."));
|
|
377
|
+
// Check for components.json
|
|
378
|
+
const componentsJsonPath = path.join(this.projectRoot, "components.json");
|
|
379
|
+
if (!(await fs.pathExists(componentsJsonPath))) {
|
|
380
|
+
issues.push({
|
|
381
|
+
type: "warning",
|
|
382
|
+
severity: "medium",
|
|
383
|
+
message: "components.json not found (required for shadcn/ui)",
|
|
384
|
+
category: "configuration",
|
|
385
|
+
autoFixable: true,
|
|
386
|
+
fix: "Run 'pnpm dlx shadcn@latest init' to setup shadcn/ui",
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
// Validate components.json content
|
|
391
|
+
await this.validateComponentsJson(componentsJsonPath, issues);
|
|
392
|
+
}
|
|
393
|
+
// Check for lib/utils.ts
|
|
394
|
+
const utilsPath = path.join(this.projectRoot, "lib", "utils.ts");
|
|
395
|
+
if (!(await fs.pathExists(utilsPath))) {
|
|
396
|
+
issues.push({
|
|
397
|
+
type: "warning",
|
|
398
|
+
severity: "medium",
|
|
399
|
+
message: "lib/utils.ts not found (required for shadcn/ui)",
|
|
400
|
+
category: "configuration",
|
|
401
|
+
autoFixable: true,
|
|
402
|
+
fix: "Create lib/utils.ts with cn utility function",
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
// Check for Tailwind CSS configuration
|
|
406
|
+
const tailwindConfigPath = path.join(this.projectRoot, "tailwind.config.ts");
|
|
407
|
+
if (!(await fs.pathExists(tailwindConfigPath))) {
|
|
408
|
+
issues.push({
|
|
409
|
+
type: "warning",
|
|
410
|
+
severity: "medium",
|
|
411
|
+
message: "Tailwind CSS configuration not found (required for shadcn/ui)",
|
|
412
|
+
category: "configuration",
|
|
413
|
+
autoFixable: true,
|
|
414
|
+
fix: "Create tailwind.config.ts for shadcn/ui",
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
// Check for CSS variables
|
|
418
|
+
const globalCssPath = path.join(this.projectRoot, "app", "globals.css");
|
|
419
|
+
if (await fs.pathExists(globalCssPath)) {
|
|
420
|
+
await this.validateGlobalCSS(globalCssPath, issues);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Validate components.json
|
|
425
|
+
*/
|
|
426
|
+
async validateComponentsJson(componentsJsonPath, issues) {
|
|
427
|
+
try {
|
|
428
|
+
const content = await fs.readJson(componentsJsonPath);
|
|
429
|
+
// Check required fields
|
|
430
|
+
if (!content.style) {
|
|
431
|
+
issues.push({
|
|
432
|
+
type: "warning",
|
|
433
|
+
severity: "medium",
|
|
434
|
+
message: "components.json missing 'style' field",
|
|
435
|
+
file: componentsJsonPath,
|
|
436
|
+
category: "configuration",
|
|
437
|
+
autoFixable: true,
|
|
438
|
+
fix: "Add style field to components.json",
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
if (!content.rsc) {
|
|
442
|
+
issues.push({
|
|
443
|
+
type: "warning",
|
|
444
|
+
severity: "medium",
|
|
445
|
+
message: "components.json missing 'rsc' field (React Server Components)",
|
|
446
|
+
file: componentsJsonPath,
|
|
447
|
+
category: "configuration",
|
|
448
|
+
autoFixable: true,
|
|
449
|
+
fix: "Add rsc: true to components.json",
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
if (!content.tsx) {
|
|
453
|
+
issues.push({
|
|
454
|
+
type: "warning",
|
|
455
|
+
severity: "medium",
|
|
456
|
+
message: "components.json missing 'tsx' field",
|
|
457
|
+
file: componentsJsonPath,
|
|
458
|
+
category: "configuration",
|
|
459
|
+
autoFixable: true,
|
|
460
|
+
fix: "Add tsx: true to components.json",
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
issues.push({
|
|
466
|
+
type: "error",
|
|
467
|
+
severity: "medium",
|
|
468
|
+
message: `Failed to read components.json: ${error}`,
|
|
469
|
+
file: componentsJsonPath,
|
|
470
|
+
category: "configuration",
|
|
471
|
+
autoFixable: false,
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Validate global CSS
|
|
477
|
+
*/
|
|
478
|
+
async validateGlobalCSS(globalCssPath, issues) {
|
|
479
|
+
try {
|
|
480
|
+
const content = await fs.readFile(globalCssPath, "utf-8");
|
|
481
|
+
// Check for CSS variables
|
|
482
|
+
if (!content.includes("--background") ||
|
|
483
|
+
!content.includes("--foreground")) {
|
|
484
|
+
issues.push({
|
|
485
|
+
type: "warning",
|
|
486
|
+
severity: "medium",
|
|
487
|
+
message: "Global CSS missing shadcn/ui CSS variables",
|
|
488
|
+
file: globalCssPath,
|
|
489
|
+
category: "configuration",
|
|
490
|
+
autoFixable: true,
|
|
491
|
+
fix: "Add shadcn/ui CSS variables to globals.css",
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
// Check for Tailwind directives
|
|
495
|
+
if (!content.includes("@tailwind base") ||
|
|
496
|
+
!content.includes("@tailwind components")) {
|
|
497
|
+
issues.push({
|
|
498
|
+
type: "warning",
|
|
499
|
+
severity: "medium",
|
|
500
|
+
message: "Global CSS missing Tailwind directives",
|
|
501
|
+
file: globalCssPath,
|
|
502
|
+
category: "configuration",
|
|
503
|
+
autoFixable: true,
|
|
504
|
+
fix: "Add Tailwind directives to globals.css",
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
catch (error) {
|
|
509
|
+
issues.push({
|
|
510
|
+
type: "error",
|
|
511
|
+
severity: "medium",
|
|
512
|
+
message: `Failed to read global CSS: ${error}`,
|
|
513
|
+
file: globalCssPath,
|
|
514
|
+
category: "configuration",
|
|
515
|
+
autoFixable: false,
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Validate Next.js configuration
|
|
521
|
+
*/
|
|
522
|
+
async validateNextJSConfiguration(issues) {
|
|
523
|
+
console.log(chalk_1.default.blue("⚙️ Validating Next.js configuration..."));
|
|
524
|
+
const nextConfigPath = path.join(this.projectRoot, "next.config.ts");
|
|
525
|
+
if (await fs.pathExists(nextConfigPath)) {
|
|
526
|
+
await this.validateNextConfig(nextConfigPath, issues);
|
|
527
|
+
}
|
|
528
|
+
// Check for TypeScript configuration
|
|
529
|
+
const tsConfigPath = path.join(this.projectRoot, "tsconfig.json");
|
|
530
|
+
if (await fs.pathExists(tsConfigPath)) {
|
|
531
|
+
await this.validateTsConfig(tsConfigPath, issues);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Validate Next.js config
|
|
536
|
+
*/
|
|
537
|
+
async validateNextConfig(nextConfigPath, issues) {
|
|
538
|
+
try {
|
|
539
|
+
const content = await fs.readFile(nextConfigPath, "utf-8");
|
|
540
|
+
// Check for proper TypeScript configuration
|
|
541
|
+
if (!content.includes("NextConfig")) {
|
|
542
|
+
issues.push({
|
|
543
|
+
type: "warning",
|
|
544
|
+
severity: "low",
|
|
545
|
+
message: "Next.js config missing TypeScript types",
|
|
546
|
+
file: nextConfigPath,
|
|
547
|
+
category: "configuration",
|
|
548
|
+
autoFixable: true,
|
|
549
|
+
fix: "Add NextConfig type import",
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
// Check for experimental features
|
|
553
|
+
if (!content.includes("experimental")) {
|
|
554
|
+
issues.push({
|
|
555
|
+
type: "info",
|
|
556
|
+
severity: "low",
|
|
557
|
+
message: "Next.js config missing experimental features (turbo, etc.)",
|
|
558
|
+
file: nextConfigPath,
|
|
559
|
+
category: "configuration",
|
|
560
|
+
autoFixable: true,
|
|
561
|
+
fix: "Add experimental features for better performance",
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
catch (error) {
|
|
566
|
+
issues.push({
|
|
567
|
+
type: "error",
|
|
568
|
+
severity: "medium",
|
|
569
|
+
message: `Failed to read Next.js config: ${error}`,
|
|
570
|
+
file: nextConfigPath,
|
|
571
|
+
category: "configuration",
|
|
572
|
+
autoFixable: false,
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Validate TypeScript configuration
|
|
578
|
+
*/
|
|
579
|
+
async validateTsConfig(tsConfigPath, issues) {
|
|
580
|
+
try {
|
|
581
|
+
const content = await fs.readJson(tsConfigPath);
|
|
582
|
+
// Check for proper Next.js TypeScript configuration
|
|
583
|
+
if (!content.compilerOptions?.plugins?.some((plugin) => plugin.name === "next")) {
|
|
584
|
+
issues.push({
|
|
585
|
+
type: "warning",
|
|
586
|
+
severity: "medium",
|
|
587
|
+
message: "TypeScript config missing Next.js plugin",
|
|
588
|
+
file: tsConfigPath,
|
|
589
|
+
category: "configuration",
|
|
590
|
+
autoFixable: true,
|
|
591
|
+
fix: "Add Next.js plugin to TypeScript config",
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
// Check for path mapping
|
|
595
|
+
if (!content.compilerOptions?.paths?.["@/*"]) {
|
|
596
|
+
issues.push({
|
|
597
|
+
type: "warning",
|
|
598
|
+
severity: "medium",
|
|
599
|
+
message: "TypeScript config missing path mapping for @/*",
|
|
600
|
+
file: tsConfigPath,
|
|
601
|
+
category: "configuration",
|
|
602
|
+
autoFixable: true,
|
|
603
|
+
fix: "Add @/* path mapping to TypeScript config",
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
catch (error) {
|
|
608
|
+
issues.push({
|
|
609
|
+
type: "error",
|
|
610
|
+
severity: "medium",
|
|
611
|
+
message: `Failed to read TypeScript config: ${error}`,
|
|
612
|
+
file: tsConfigPath,
|
|
613
|
+
category: "configuration",
|
|
614
|
+
autoFixable: false,
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Validate dependencies
|
|
620
|
+
*/
|
|
621
|
+
async validateDependencies(issues) {
|
|
622
|
+
console.log(chalk_1.default.blue("📦 Validating dependencies..."));
|
|
623
|
+
const packageJsonPath = path.join(this.projectRoot, "package.json");
|
|
624
|
+
if (!(await fs.pathExists(packageJsonPath))) {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
try {
|
|
628
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
629
|
+
const dependencies = {
|
|
630
|
+
...packageJson.dependencies,
|
|
631
|
+
...packageJson.devDependencies,
|
|
632
|
+
};
|
|
633
|
+
// Check for Next.js
|
|
634
|
+
if (!dependencies.next) {
|
|
635
|
+
issues.push({
|
|
636
|
+
type: "error",
|
|
637
|
+
severity: "critical",
|
|
638
|
+
message: "Next.js not found in dependencies",
|
|
639
|
+
category: "dependencies",
|
|
640
|
+
autoFixable: true,
|
|
641
|
+
fix: "Install Next.js: pnpm add next",
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
// Check for React
|
|
645
|
+
if (!dependencies.react || !dependencies["react-dom"]) {
|
|
646
|
+
issues.push({
|
|
647
|
+
type: "error",
|
|
648
|
+
severity: "critical",
|
|
649
|
+
message: "React dependencies missing",
|
|
650
|
+
category: "dependencies",
|
|
651
|
+
autoFixable: true,
|
|
652
|
+
fix: "Install React: pnpm add react react-dom",
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
// Check for TypeScript
|
|
656
|
+
if (!dependencies.typescript) {
|
|
657
|
+
issues.push({
|
|
658
|
+
type: "warning",
|
|
659
|
+
severity: "medium",
|
|
660
|
+
message: "TypeScript not found in devDependencies",
|
|
661
|
+
category: "dependencies",
|
|
662
|
+
autoFixable: true,
|
|
663
|
+
fix: "Install TypeScript: pnpm add -D typescript @types/react @types/node",
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
// Check for Tailwind CSS (if shadcn/ui is expected)
|
|
667
|
+
if (await fs.pathExists(path.join(this.projectRoot, "components.json"))) {
|
|
668
|
+
if (!dependencies.tailwindcss) {
|
|
669
|
+
issues.push({
|
|
670
|
+
type: "warning",
|
|
671
|
+
severity: "medium",
|
|
672
|
+
message: "Tailwind CSS not found (required for shadcn/ui)",
|
|
673
|
+
category: "dependencies",
|
|
674
|
+
autoFixable: true,
|
|
675
|
+
fix: "Install Tailwind CSS: pnpm add -D tailwindcss autoprefixer postcss",
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
catch (error) {
|
|
681
|
+
issues.push({
|
|
682
|
+
type: "error",
|
|
683
|
+
severity: "medium",
|
|
684
|
+
message: `Failed to read package.json: ${error}`,
|
|
685
|
+
file: packageJsonPath,
|
|
686
|
+
category: "dependencies",
|
|
687
|
+
autoFixable: false,
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Check App Router compliance
|
|
693
|
+
*/
|
|
694
|
+
checkAppRouterCompliance(issues) {
|
|
695
|
+
const criticalIssues = issues.filter((issue) => issue.category === "routing" && issue.severity === "critical");
|
|
696
|
+
return criticalIssues.length === 0;
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Check shadcn/ui compliance
|
|
700
|
+
*/
|
|
701
|
+
checkShadcnCompliance(issues) {
|
|
702
|
+
const shadcnIssues = issues.filter((issue) => issue.message.includes("shadcn") ||
|
|
703
|
+
issue.message.includes("components.json") ||
|
|
704
|
+
issue.message.includes("Tailwind"));
|
|
705
|
+
return shadcnIssues.length === 0;
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Generate recommendations
|
|
709
|
+
*/
|
|
710
|
+
generateRecommendations(issues) {
|
|
711
|
+
const recommendations = [];
|
|
712
|
+
const criticalIssues = issues.filter((issue) => issue.severity === "critical");
|
|
713
|
+
if (criticalIssues.length > 0) {
|
|
714
|
+
recommendations.push("Address critical issues immediately to ensure proper Next.js functionality");
|
|
715
|
+
}
|
|
716
|
+
const routingIssues = issues.filter((issue) => issue.category === "routing");
|
|
717
|
+
if (routingIssues.length > 0) {
|
|
718
|
+
recommendations.push("Review App Router structure and ensure proper page.tsx and layout.tsx files");
|
|
719
|
+
}
|
|
720
|
+
const shadcnIssues = issues.filter((issue) => issue.message.includes("shadcn"));
|
|
721
|
+
if (shadcnIssues.length > 0) {
|
|
722
|
+
recommendations.push("Run 'pnpm dlx shadcn@latest init' to properly setup shadcn/ui");
|
|
723
|
+
}
|
|
724
|
+
const configIssues = issues.filter((issue) => issue.category === "configuration");
|
|
725
|
+
if (configIssues.length > 0) {
|
|
726
|
+
recommendations.push("Update configuration files to follow Next.js 13+ best practices");
|
|
727
|
+
}
|
|
728
|
+
return recommendations;
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Helper methods
|
|
732
|
+
*/
|
|
733
|
+
async findFiles(pattern) {
|
|
734
|
+
const glob = require("glob");
|
|
735
|
+
const matches = await glob(pattern, {
|
|
736
|
+
cwd: this.projectRoot,
|
|
737
|
+
absolute: true,
|
|
738
|
+
ignore: ["**/node_modules/**"],
|
|
739
|
+
});
|
|
740
|
+
return matches;
|
|
741
|
+
}
|
|
742
|
+
async findRouteDirectories(appDir) {
|
|
743
|
+
const routeDirs = [];
|
|
744
|
+
const findDirs = async (dir) => {
|
|
745
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
746
|
+
for (const entry of entries) {
|
|
747
|
+
if (entry.isDirectory()) {
|
|
748
|
+
const fullPath = path.join(dir, entry.name);
|
|
749
|
+
routeDirs.push(fullPath);
|
|
750
|
+
await findDirs(fullPath);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
await findDirs(appDir);
|
|
755
|
+
return routeDirs;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
exports.NextJSProjectValidator = NextJSProjectValidator;
|
|
759
|
+
//# sourceMappingURL=NextJSProjectValidator.js.map
|