@polymorphism-tech/morph-spec 4.10.6 → 4.10.8
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/CLAUDE.md +3 -0
- package/LICENSE +71 -71
- package/README.md +379 -379
- package/bin/morph-spec.js +89 -0
- package/bin/task-manager.js +59 -6
- package/claude-plugin.json +14 -14
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +206 -206
- package/framework/agents/README.md +14 -14
- package/framework/agents/architecture/standards-architect.md +159 -159
- package/framework/agents/frontend/blazor-builder.md +13 -0
- package/framework/agents/frontend/css-specialist.md +87 -0
- package/framework/agents/frontend/nextjs-expert.md +13 -0
- package/framework/agents/frontend/nextjs-ui-specialist.md +218 -0
- package/framework/agents/frontend/ui-ux-designer.md +80 -10
- package/framework/agents/infrastructure/azure-architect.md +149 -149
- package/framework/agents/infrastructure/infra-architect.md +48 -48
- package/framework/agents.json +2268 -2567
- package/framework/hooks/README.md +204 -204
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +10 -5
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +10 -24
- package/framework/hooks/shared/phase-utils.js +139 -139
- package/framework/hooks/shared/worktree-helpers.js +30 -8
- package/framework/rules/csharp-standards.md +13 -13
- package/framework/rules/frontend-standards.md +13 -13
- package/framework/rules/infrastructure-standards.md +16 -16
- package/framework/rules/testing-standards.md +14 -14
- package/framework/skills/README.md +0 -1
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +12 -0
- package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +254 -254
- package/framework/skills/level-0-meta/morph-init/SKILL.md +88 -3
- package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +247 -247
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +270 -270
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +515 -499
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +508 -471
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +12 -1
- package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +272 -258
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +317 -311
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +424 -323
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
- package/framework/standards/STANDARDS.json +2040 -2040
- package/framework/standards/frontend/nextjs/motion-patterns.md +321 -0
- package/framework/standards/frontend/nextjs/nextjs-patterns.md +17 -17
- package/framework/templates/README.md +814 -814
- package/framework/templates/code/dotnet/contracts/Commands.cs +74 -74
- package/framework/templates/code/dotnet/contracts/Entities.cs +25 -25
- package/framework/templates/code/dotnet/contracts/Queries.cs +74 -74
- package/framework/templates/code/dotnet/contracts/README.md +74 -74
- package/framework/templates/code/dotnet/contracts/api-contracts.cs +173 -173
- package/framework/templates/code/dotnet/frontend/component.razor +239 -239
- package/framework/templates/code/dotnet/jobs/agent.cs +163 -163
- package/framework/templates/code/dotnet/jobs/job.cs +171 -171
- package/framework/templates/code/dotnet/test.cs +239 -239
- package/framework/templates/code/sql/rls-policy.sql +57 -57
- package/framework/templates/code/sql/supabase-migration.sql +100 -100
- package/framework/templates/code/sql/supabase-migration.template.sql +113 -113
- package/framework/templates/code/typescript/contracts.ts +168 -168
- package/framework/templates/context/CONTEXT.md +181 -181
- package/framework/templates/context/design-system.md +211 -0
- package/framework/templates/docs/proposal.md +182 -182
- package/framework/templates/docs/user-stories.md +34 -34
- package/framework/templates/examples/design-system-examples.md +357 -357
- package/framework/templates/examples/spec-examples.md +90 -90
- package/framework/templates/feature/decisions.md +187 -187
- package/framework/templates/feature/recap.md +146 -146
- package/framework/templates/feature/tasks.md +199 -199
- package/framework/templates/infrastructure/azure/README.md +286 -286
- package/framework/templates/infrastructure/azure/deploy-checklist.md +426 -426
- package/framework/templates/infrastructure/azure/parameters.dev.json +29 -29
- package/framework/templates/infrastructure/azure/parameters.prod.json +29 -29
- package/framework/templates/infrastructure/azure/parameters.staging.json +29 -29
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +67 -67
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/docker/easypanel.template.json +54 -54
- package/package.json +82 -82
- package/src/commands/agents/agent-metrics.js +174 -0
- package/src/commands/agents/audit-dispatch.js +82 -0
- package/src/commands/agents/detect-agents.js +275 -0
- package/src/commands/agents/dispatch-agents.js +41 -135
- package/src/commands/agents/index.js +13 -4
- package/src/commands/agents/manage-agents.js +352 -0
- package/src/commands/project/doctor.js +59 -0
- package/src/commands/project/install-plugin-cmd.js +28 -28
- package/src/commands/project/setup-infra-cmd.js +12 -12
- package/src/commands/project/update.js +1 -17
- package/src/commands/project/worktree.js +58 -2
- package/src/commands/state/advance-phase.js +11 -3
- package/src/commands/tasks/index.js +4 -4
- package/src/commands/templates/index.js +5 -5
- package/src/commands/utils/index.js +3 -3
- package/src/commands/validation/index.js +4 -4
- package/src/core/state/phase-state-machine.js +217 -217
- package/src/core/state/state-manager.js +81 -0
- package/src/lib/agents/agents-manager.js +349 -0
- package/src/lib/agents/metrics-calculator.js +125 -0
- package/src/lib/agents/output-collector.js +75 -0
- package/src/lib/generators/context-generator.js +8 -7
- package/src/lib/generators/design-system-generator.js +298 -298
- package/src/lib/generators/index.js +10 -10
- package/src/lib/generators/metadata-extractor.js +387 -387
- package/src/lib/stack-filter.js +65 -65
- package/src/lib/standards/index.js +7 -7
- package/src/lib/standards/standards-context-injector.js +302 -302
- package/src/lib/tasks/task-parser.js +94 -3
- package/src/lib/validators/architecture/architecture-validator.js +387 -387
- package/src/lib/validators/architecture/index.js +7 -7
- package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +316 -316
- package/src/lib/validators/blazor/blazor-state-validator.js +279 -279
- package/src/lib/validators/blazor/blazor-validator.js +369 -369
- package/src/lib/validators/blazor/index.js +9 -9
- package/src/lib/validators/checkpoint-persistence.js +39 -0
- package/src/lib/validators/content/content-validator.js +287 -279
- package/src/lib/validators/content/index.js +7 -7
- package/src/lib/validators/contracts/index.js +7 -7
- package/src/lib/validators/css/css-validator.js +352 -352
- package/src/lib/validators/css/index.js +7 -7
- package/src/lib/validators/design-system/design-system-validator.js +231 -231
- package/src/lib/validators/design-system/index.js +7 -7
- package/src/lib/validators/nextjs/index.js +6 -6
- package/src/lib/validators/nextjs/next-component-validator.js +183 -183
- package/src/lib/validators/packages/index.js +7 -7
- package/src/lib/validators/packages/package-validator.js +360 -360
- package/src/lib/validators/shared/index.js +12 -12
- package/src/lib/validators/shared/issue-counter.js +18 -18
- package/src/lib/validators/shared/result-formatter.js +124 -124
- package/src/lib/validators/spec-validator.js +2 -2
- package/src/lib/validators/ui/index.js +7 -7
- package/src/lib/validators/ui/ui-contrast-validator.js +422 -422
- package/src/lib/validators/validation-runner.js +20 -0
- package/src/scripts/global-install.js +58 -58
- package/src/scripts/install-plugin.js +126 -126
- package/src/scripts/setup-infra.js +1 -17
- package/src/utils/agent-id-mapper.js +11 -0
- package/src/utils/agents-installer.js +40 -8
- package/src/utils/claude-md-injector.js +90 -90
- package/src/utils/claude-settings-manager.js +1 -1
- package/src/utils/file-copier.js +200 -196
- package/src/utils/hooks-installer.js +556 -739
- package/src/utils/logger.js +32 -32
- package/src/utils/skills-installer.js +178 -178
- package/src/utils/tier-helpers.js +24 -0
- package/src/utils/version-checker.js +175 -175
- package/framework/agents/architecture/po-pm-advisor.md +0 -203
- package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +0 -68
- package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
- package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
- package/framework/templates/.idea/morph-templates.xml +0 -92
- package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
|
@@ -1,183 +1,183 @@
|
|
|
1
|
-
import { glob } from 'glob';
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Next.js Component Validator
|
|
7
|
-
*
|
|
8
|
-
* Validates .tsx files for:
|
|
9
|
-
* 1. Unnecessary 'use client' directives (no interactivity)
|
|
10
|
-
* 2. Missing 'use client' when React hooks are used
|
|
11
|
-
* 3. File naming convention (kebab-case)
|
|
12
|
-
*
|
|
13
|
-
* @module next-component-validator
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
// ============================================
|
|
17
|
-
// CONSTANTS
|
|
18
|
-
// ============================================
|
|
19
|
-
|
|
20
|
-
/** Hooks/patterns that require 'use client' */
|
|
21
|
-
const CLIENT_HOOKS = ['useState', 'useEffect', 'useRef', 'useReducer', 'useCallback', 'useMemo', 'useContext'];
|
|
22
|
-
|
|
23
|
-
/** Event handler patterns that indicate interactivity (hooks are checked via CLIENT_HOOKS) */
|
|
24
|
-
const EVENT_HANDLER_PATTERNS = [
|
|
25
|
-
/\bonClick\b/,
|
|
26
|
-
/\bonChange\b/,
|
|
27
|
-
/\bonSubmit\b/,
|
|
28
|
-
/\bonFocus\b/,
|
|
29
|
-
/\bonBlur\b/,
|
|
30
|
-
/\bonKeyDown\b/,
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
/** Next.js special files exempt from naming checks */
|
|
34
|
-
const NEXTJS_SPECIAL_FILES = ['page.tsx', 'layout.tsx', 'loading.tsx', 'error.tsx', 'not-found.tsx', 'route.ts', 'middleware.ts'];
|
|
35
|
-
|
|
36
|
-
// ============================================
|
|
37
|
-
// MAIN VALIDATION FUNCTION
|
|
38
|
-
// ============================================
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* @typedef {Object} ValidationIssue
|
|
42
|
-
* @property {'error' | 'warning' | 'info'} type
|
|
43
|
-
* @property {string} message
|
|
44
|
-
* @property {string} [suggestion]
|
|
45
|
-
* @property {string} file
|
|
46
|
-
* @property {number} [line]
|
|
47
|
-
*/
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Validates a Next.js component file.
|
|
51
|
-
*
|
|
52
|
-
* @param {string} content - File content
|
|
53
|
-
* @param {string} filePath - Relative file path
|
|
54
|
-
* @returns {ValidationIssue[]}
|
|
55
|
-
*/
|
|
56
|
-
export function validateNextComponent(content, filePath) {
|
|
57
|
-
const issues = [];
|
|
58
|
-
|
|
59
|
-
// Only validate .tsx and .ts files
|
|
60
|
-
if (!filePath.endsWith('.tsx') && !filePath.endsWith('.ts')) {
|
|
61
|
-
return issues;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Skip non-component utility files (no JSX, no hooks)
|
|
65
|
-
const hasJsx = /(?<![a-zA-Z0-9])<[A-Z][a-zA-Z]*[\s/>]|(?<![a-zA-Z0-9])<[a-z][a-z]+[\s>/]/.test(content);
|
|
66
|
-
const hasHooks = CLIENT_HOOKS.some(hook => content.includes(hook));
|
|
67
|
-
|
|
68
|
-
if (!hasJsx && !hasHooks) {
|
|
69
|
-
return issues;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const hasUseClient = /^'use client';?|^"use client";?/.test(content.trimStart());
|
|
73
|
-
|
|
74
|
-
// Check 1: use client present but no interactivity
|
|
75
|
-
if (hasUseClient) {
|
|
76
|
-
const hasEventHandlers = EVENT_HANDLER_PATTERNS.some(pattern => pattern.test(content));
|
|
77
|
-
const hasClientHooks = CLIENT_HOOKS.some(hook => new RegExp(`\\b${hook}\\b`).test(content));
|
|
78
|
-
if (!hasEventHandlers && !hasClientHooks) {
|
|
79
|
-
issues.push({
|
|
80
|
-
type: 'warning',
|
|
81
|
-
message: "'use client' directive present but no interactivity detected (no hooks, no event handlers)",
|
|
82
|
-
suggestion: "Remove 'use client' to make this a Server Component, or add interactive behavior",
|
|
83
|
-
file: filePath,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Check 2: hooks used without use client
|
|
89
|
-
// .ts files (custom hooks, utilities) never render JSX — skip this check
|
|
90
|
-
if (!hasUseClient) {
|
|
91
|
-
if (!filePath.endsWith('.tsx')) return issues;
|
|
92
|
-
const usedHooks = CLIENT_HOOKS.filter(hook => new RegExp(`\\b${hook}\\b`).test(content));
|
|
93
|
-
if (usedHooks.length > 0) {
|
|
94
|
-
issues.push({
|
|
95
|
-
type: 'error',
|
|
96
|
-
message: `React hook(s) used (${usedHooks.join(', ')}) without 'use client' directive`,
|
|
97
|
-
suggestion: "Add 'use client' as the first line of the file",
|
|
98
|
-
file: filePath,
|
|
99
|
-
line: 1,
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Check 3: file naming convention (kebab-case)
|
|
105
|
-
const fileName = filePath.split('/').pop() ?? '';
|
|
106
|
-
const isSpecialFile = NEXTJS_SPECIAL_FILES.includes(fileName);
|
|
107
|
-
|
|
108
|
-
if (!isSpecialFile && fileName.endsWith('.tsx')) {
|
|
109
|
-
const baseName = fileName.replace('.tsx', '');
|
|
110
|
-
const hasUpperCase = /[A-Z]/.test(baseName);
|
|
111
|
-
if (hasUpperCase) {
|
|
112
|
-
issues.push({
|
|
113
|
-
type: 'warning',
|
|
114
|
-
message: `Component file '${fileName}' should use kebab-case naming`,
|
|
115
|
-
suggestion: `Rename to '${toKebabCase(baseName)}.tsx'`,
|
|
116
|
-
file: filePath,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return issues;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// ============================================
|
|
125
|
-
// HELPERS
|
|
126
|
-
// ============================================
|
|
127
|
-
|
|
128
|
-
function toKebabCase(str) {
|
|
129
|
-
return str
|
|
130
|
-
.replace(/([A-Z])/g, '-$1')
|
|
131
|
-
.toLowerCase()
|
|
132
|
-
.replace(/^-/, '');
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// ============================================
|
|
136
|
-
// PROJECT-LEVEL SCANNER
|
|
137
|
-
// ============================================
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Validates all Next.js component files in a project directory.
|
|
141
|
-
* Wraps validateNextComponent for use in validation-runner.js.
|
|
142
|
-
*
|
|
143
|
-
* @param {string} projectPath - Root path of the project
|
|
144
|
-
* @param {Object} _options - Validator options (unused, for interface compatibility)
|
|
145
|
-
* @returns {Promise<{ errors: number, warnings: number, issues: Array }>}
|
|
146
|
-
*/
|
|
147
|
-
export async function validateNextComponentFiles(projectPath, _options = {}) {
|
|
148
|
-
const result = { errors: 0, warnings: 0, issues: [] };
|
|
149
|
-
|
|
150
|
-
// Find all .tsx and .ts files under src/
|
|
151
|
-
const srcDir = join(projectPath, 'src');
|
|
152
|
-
const normalizedSrc = srcDir.replace(/\\/g, '/');
|
|
153
|
-
const pattern = normalizedSrc + '/**/*.{tsx,ts}';
|
|
154
|
-
const files = await glob(pattern, { ignore: ['**/node_modules/**', '**/.next/**'] });
|
|
155
|
-
|
|
156
|
-
for (const filePath of files) {
|
|
157
|
-
let content;
|
|
158
|
-
try {
|
|
159
|
-
content = readFileSync(filePath, 'utf8');
|
|
160
|
-
} catch {
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Make path relative for cleaner output
|
|
165
|
-
const normFilePath = filePath.replace(/\\/g, '/');
|
|
166
|
-
const relPath = normFilePath.replace(projectPath.replace(/\\/g, '/') + '/', '');
|
|
167
|
-
const fileIssues = validateNextComponent(content, relPath);
|
|
168
|
-
|
|
169
|
-
for (const issue of fileIssues) {
|
|
170
|
-
result.issues.push({
|
|
171
|
-
level: issue.type,
|
|
172
|
-
message: issue.message,
|
|
173
|
-
file: issue.file,
|
|
174
|
-
line: issue.line,
|
|
175
|
-
solution: issue.suggestion,
|
|
176
|
-
});
|
|
177
|
-
if (issue.type === 'error') result.errors++;
|
|
178
|
-
else if (issue.type === 'warning') result.warnings++;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return result;
|
|
183
|
-
}
|
|
1
|
+
import { glob } from 'glob';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Next.js Component Validator
|
|
7
|
+
*
|
|
8
|
+
* Validates .tsx files for:
|
|
9
|
+
* 1. Unnecessary 'use client' directives (no interactivity)
|
|
10
|
+
* 2. Missing 'use client' when React hooks are used
|
|
11
|
+
* 3. File naming convention (kebab-case)
|
|
12
|
+
*
|
|
13
|
+
* @module next-component-validator
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// ============================================
|
|
17
|
+
// CONSTANTS
|
|
18
|
+
// ============================================
|
|
19
|
+
|
|
20
|
+
/** Hooks/patterns that require 'use client' */
|
|
21
|
+
const CLIENT_HOOKS = ['useState', 'useEffect', 'useRef', 'useReducer', 'useCallback', 'useMemo', 'useContext'];
|
|
22
|
+
|
|
23
|
+
/** Event handler patterns that indicate interactivity (hooks are checked via CLIENT_HOOKS) */
|
|
24
|
+
const EVENT_HANDLER_PATTERNS = [
|
|
25
|
+
/\bonClick\b/,
|
|
26
|
+
/\bonChange\b/,
|
|
27
|
+
/\bonSubmit\b/,
|
|
28
|
+
/\bonFocus\b/,
|
|
29
|
+
/\bonBlur\b/,
|
|
30
|
+
/\bonKeyDown\b/,
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
/** Next.js special files exempt from naming checks */
|
|
34
|
+
const NEXTJS_SPECIAL_FILES = ['page.tsx', 'layout.tsx', 'loading.tsx', 'error.tsx', 'not-found.tsx', 'route.ts', 'middleware.ts'];
|
|
35
|
+
|
|
36
|
+
// ============================================
|
|
37
|
+
// MAIN VALIDATION FUNCTION
|
|
38
|
+
// ============================================
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @typedef {Object} ValidationIssue
|
|
42
|
+
* @property {'error' | 'warning' | 'info'} type
|
|
43
|
+
* @property {string} message
|
|
44
|
+
* @property {string} [suggestion]
|
|
45
|
+
* @property {string} file
|
|
46
|
+
* @property {number} [line]
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Validates a Next.js component file.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} content - File content
|
|
53
|
+
* @param {string} filePath - Relative file path
|
|
54
|
+
* @returns {ValidationIssue[]}
|
|
55
|
+
*/
|
|
56
|
+
export function validateNextComponent(content, filePath) {
|
|
57
|
+
const issues = [];
|
|
58
|
+
|
|
59
|
+
// Only validate .tsx and .ts files
|
|
60
|
+
if (!filePath.endsWith('.tsx') && !filePath.endsWith('.ts')) {
|
|
61
|
+
return issues;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Skip non-component utility files (no JSX, no hooks)
|
|
65
|
+
const hasJsx = /(?<![a-zA-Z0-9])<[A-Z][a-zA-Z]*[\s/>]|(?<![a-zA-Z0-9])<[a-z][a-z]+[\s>/]/.test(content);
|
|
66
|
+
const hasHooks = CLIENT_HOOKS.some(hook => content.includes(hook));
|
|
67
|
+
|
|
68
|
+
if (!hasJsx && !hasHooks) {
|
|
69
|
+
return issues;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const hasUseClient = /^'use client';?|^"use client";?/.test(content.trimStart());
|
|
73
|
+
|
|
74
|
+
// Check 1: use client present but no interactivity
|
|
75
|
+
if (hasUseClient) {
|
|
76
|
+
const hasEventHandlers = EVENT_HANDLER_PATTERNS.some(pattern => pattern.test(content));
|
|
77
|
+
const hasClientHooks = CLIENT_HOOKS.some(hook => new RegExp(`\\b${hook}\\b`).test(content));
|
|
78
|
+
if (!hasEventHandlers && !hasClientHooks) {
|
|
79
|
+
issues.push({
|
|
80
|
+
type: 'warning',
|
|
81
|
+
message: "'use client' directive present but no interactivity detected (no hooks, no event handlers)",
|
|
82
|
+
suggestion: "Remove 'use client' to make this a Server Component, or add interactive behavior",
|
|
83
|
+
file: filePath,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check 2: hooks used without use client
|
|
89
|
+
// .ts files (custom hooks, utilities) never render JSX — skip this check
|
|
90
|
+
if (!hasUseClient) {
|
|
91
|
+
if (!filePath.endsWith('.tsx')) return issues;
|
|
92
|
+
const usedHooks = CLIENT_HOOKS.filter(hook => new RegExp(`\\b${hook}\\b`).test(content));
|
|
93
|
+
if (usedHooks.length > 0) {
|
|
94
|
+
issues.push({
|
|
95
|
+
type: 'error',
|
|
96
|
+
message: `React hook(s) used (${usedHooks.join(', ')}) without 'use client' directive`,
|
|
97
|
+
suggestion: "Add 'use client' as the first line of the file",
|
|
98
|
+
file: filePath,
|
|
99
|
+
line: 1,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Check 3: file naming convention (kebab-case)
|
|
105
|
+
const fileName = filePath.split('/').pop() ?? '';
|
|
106
|
+
const isSpecialFile = NEXTJS_SPECIAL_FILES.includes(fileName);
|
|
107
|
+
|
|
108
|
+
if (!isSpecialFile && fileName.endsWith('.tsx')) {
|
|
109
|
+
const baseName = fileName.replace('.tsx', '');
|
|
110
|
+
const hasUpperCase = /[A-Z]/.test(baseName);
|
|
111
|
+
if (hasUpperCase) {
|
|
112
|
+
issues.push({
|
|
113
|
+
type: 'warning',
|
|
114
|
+
message: `Component file '${fileName}' should use kebab-case naming`,
|
|
115
|
+
suggestion: `Rename to '${toKebabCase(baseName)}.tsx'`,
|
|
116
|
+
file: filePath,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return issues;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ============================================
|
|
125
|
+
// HELPERS
|
|
126
|
+
// ============================================
|
|
127
|
+
|
|
128
|
+
function toKebabCase(str) {
|
|
129
|
+
return str
|
|
130
|
+
.replace(/([A-Z])/g, '-$1')
|
|
131
|
+
.toLowerCase()
|
|
132
|
+
.replace(/^-/, '');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ============================================
|
|
136
|
+
// PROJECT-LEVEL SCANNER
|
|
137
|
+
// ============================================
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Validates all Next.js component files in a project directory.
|
|
141
|
+
* Wraps validateNextComponent for use in validation-runner.js.
|
|
142
|
+
*
|
|
143
|
+
* @param {string} projectPath - Root path of the project
|
|
144
|
+
* @param {Object} _options - Validator options (unused, for interface compatibility)
|
|
145
|
+
* @returns {Promise<{ errors: number, warnings: number, issues: Array }>}
|
|
146
|
+
*/
|
|
147
|
+
export async function validateNextComponentFiles(projectPath, _options = {}) {
|
|
148
|
+
const result = { errors: 0, warnings: 0, issues: [] };
|
|
149
|
+
|
|
150
|
+
// Find all .tsx and .ts files under src/
|
|
151
|
+
const srcDir = join(projectPath, 'src');
|
|
152
|
+
const normalizedSrc = srcDir.replace(/\\/g, '/');
|
|
153
|
+
const pattern = normalizedSrc + '/**/*.{tsx,ts}';
|
|
154
|
+
const files = await glob(pattern, { ignore: ['**/node_modules/**', '**/.next/**'] });
|
|
155
|
+
|
|
156
|
+
for (const filePath of files) {
|
|
157
|
+
let content;
|
|
158
|
+
try {
|
|
159
|
+
content = readFileSync(filePath, 'utf8');
|
|
160
|
+
} catch {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Make path relative for cleaner output
|
|
165
|
+
const normFilePath = filePath.replace(/\\/g, '/');
|
|
166
|
+
const relPath = normFilePath.replace(projectPath.replace(/\\/g, '/') + '/', '');
|
|
167
|
+
const fileIssues = validateNextComponent(content, relPath);
|
|
168
|
+
|
|
169
|
+
for (const issue of fileIssues) {
|
|
170
|
+
result.issues.push({
|
|
171
|
+
level: issue.type,
|
|
172
|
+
message: issue.message,
|
|
173
|
+
file: issue.file,
|
|
174
|
+
line: issue.line,
|
|
175
|
+
solution: issue.suggestion,
|
|
176
|
+
});
|
|
177
|
+
if (issue.type === 'error') result.errors++;
|
|
178
|
+
else if (issue.type === 'warning') result.warnings++;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Package Validators
|
|
3
|
-
*
|
|
4
|
-
* Package and dependency validation.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export * from './package-validator.js';
|
|
1
|
+
/**
|
|
2
|
+
* Package Validators
|
|
3
|
+
*
|
|
4
|
+
* Package and dependency validation.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export * from './package-validator.js';
|