@polymorphism-tech/morph-spec 8.46.0 → 9.0.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/CLAUDE.md +53 -44
- package/README.md +3 -4
- package/bin/morph-spec.js +38 -55
- package/bin/validate.js +5 -134
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +2 -2
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +41 -101
- package/framework/MORPH.md +95 -252
- package/framework/agents.json +75 -18
- package/framework/commands/create-umbraco-lp.md +2 -0
- package/framework/commands/migrate-lp-to-umbraco.md +2 -0
- package/framework/commands/morph-apply.md +5 -28
- package/framework/commands/morph-archive.md +2 -2
- package/framework/commands/morph-hotfix.md +8 -8
- package/framework/commands/morph-infra.md +17 -203
- package/framework/commands/morph-media.md +3 -0
- package/framework/commands/morph-preflight.md +7 -31
- package/framework/commands/morph-proposal.md +2 -50
- package/framework/commands/morph-status.md +3 -3
- package/framework/commands/morph-troubleshoot.md +2 -14
- package/framework/docs/CLI.md +585 -286
- package/framework/evals/review-checklist.md +82 -41
- package/framework/hooks/README.md +10 -11
- package/framework/hooks/claude-code/core/comment-guard.js +1 -1
- package/framework/hooks/claude-code/core/denylist.json +33 -3
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +16 -20
- package/framework/hooks/claude-code/core/gate-guard.js +2 -11
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +38 -0
- package/framework/hooks/claude-code/core/protect-spec-files.js +30 -242
- package/framework/hooks/claude-code/core/state-sync.js +17 -19
- package/framework/hooks/claude-code/core/telemetry-log.js +3 -3
- package/framework/hooks/claude-code/core/validate-completion.js +33 -97
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +31 -0
- package/framework/hooks/claude-code/core/worktree-context.js +5 -4
- package/framework/hooks/claude-code/statusline.py +55 -41
- package/framework/hooks/package.json +3 -0
- package/framework/hooks/shared/activity-logger.js +1 -1
- package/framework/hooks/shared/build-check.js +10 -6
- package/framework/hooks/shared/command-tokens.js +169 -0
- package/framework/hooks/shared/comment-rules.js +1 -1
- package/framework/hooks/shared/comment-scanner.js +10 -23
- package/framework/hooks/shared/comment-stripper.js +1 -1
- package/framework/hooks/shared/e2e-check.js +67 -31
- package/framework/hooks/shared/errata.js +72 -0
- package/framework/hooks/shared/feature-locator.js +1 -1
- package/framework/hooks/shared/frontend-locations.js +6 -0
- package/framework/hooks/shared/gate-question.js +18 -0
- package/framework/hooks/shared/phase-utils.js +15 -32
- package/framework/hooks/shared/slice-progress.js +118 -0
- package/framework/hooks/shared/spawn-command.js +23 -0
- package/framework/hooks/shared/state-reader.js +61 -87
- package/framework/hooks/shared/state-writer.js +3 -3
- package/framework/mcp/registry.json +3 -4
- package/framework/phases.json +23 -39
- package/framework/rules/ai-agents-standards.md +18 -16
- package/framework/rules/asaas-standards.md +3 -1
- package/framework/rules/csharp-standards.md +10 -8
- package/framework/rules/data-standards.md +5 -5
- package/framework/rules/dotnet-templates.md +1 -1
- package/framework/rules/frontend-standards.md +4 -2
- package/framework/rules/gsap-standards.md +10 -5
- package/framework/rules/hangfire-standards.md +3 -1
- package/framework/rules/infrastructure-standards.md +4 -2
- package/framework/rules/neon-auth-standards.md +3 -1
- package/framework/rules/neon-standards.md +6 -4
- package/framework/rules/nextjs-standards.md +13 -11
- package/framework/rules/nextjs-templates.md +1 -1
- package/framework/rules/resend-standards.md +3 -1
- package/framework/schemas/config.schema.json +14 -9
- package/framework/schemas/feature.schema.json +15 -52
- package/framework/skills/README.md +4 -5
- package/framework/skills/core/create-umbraco-lp/SKILL.md +3 -3
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +3 -3
- package/framework/skills/core/morph-apply/SKILL.md +150 -223
- package/framework/skills/core/morph-hotfix/SKILL.md +34 -43
- package/framework/skills/core/morph-machine/SKILL.md +4 -4
- package/framework/skills/core/morph-media/SKILL.md +3 -3
- package/framework/skills/core/morph-plan/SKILL.md +138 -300
- package/framework/skills/core/morph-review/SKILL.md +112 -137
- package/framework/skills/core/morph-standards/SKILL.md +29 -41
- package/framework/skills/core/morph-uiux/SKILL.md +78 -153
- package/framework/standards/STANDARDS.json +90 -1219
- package/framework/standards/STANDARDS.meta.json +1305 -0
- package/framework/standards/ai-agents/evals-with-cache.md +2 -3
- package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
- package/framework/standards/ai-agents/modalities-speech-to-text.md +36 -26
- package/framework/standards/ai-agents/prompt-sources.md +6 -4
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +67 -39
- package/framework/standards/ai-agents/setup.md +2 -2
- package/framework/standards/ai-agents/structured-output.md +1 -1
- package/framework/standards/ai-agents/sweet-spot.md +1 -1
- package/framework/standards/architecture/code-comments.md +8 -8
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +18 -9
- package/framework/standards/backend/api/validation.md +135 -47
- package/framework/standards/backend/database/postgresql/database.md +11 -7
- package/framework/standards/backend/database/vector-search-rag.md +207 -324
- package/framework/standards/backend/dotnet/fluent-validation-vsa.md +14 -8
- package/framework/standards/backend/dotnet/result-pattern.md +40 -6
- package/framework/standards/backend/dotnet/scalar-ui.md +13 -7
- package/framework/standards/backend/dotnet/scrutor.md +1 -1
- package/framework/standards/backend/dotnet/testing.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +7 -7
- package/framework/standards/backend/integrations/asaas/asaas-api.md +309 -165
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +134 -154
- package/framework/standards/data/nosql/cache/redis.md +120 -44
- package/framework/standards/data/nosql/cosmos-db.md +114 -71
- package/framework/standards/frontend/design-system/animations.md +1 -1
- package/framework/standards/frontend/design-system/naming.md +1 -1
- package/framework/standards/frontend/nextjs/motion-patterns.md +45 -29
- package/framework/standards/frontend/nextjs/naming-conventions.md +1 -1
- package/framework/standards/infrastructure/docker/coolify-deploy.md +8 -6
- package/framework/standards/infrastructure/neon/neon-pgvector.md +120 -75
- package/framework/standards/infrastructure/neon/neon-setup.md +53 -31
- package/framework/standards/integration/mcp/mcp-tools.md +7 -7
- package/framework/templates/README.md +2 -3
- package/framework/templates/REGISTRY.json +6 -170
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
- package/framework/templates/code/dotnet/extensions/database-extensions.cs +1 -1
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +27 -0
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +2 -2
- package/framework/templates/docs/plan.md +72 -0
- package/framework/templates/dotnet/ai-kit/README.md +1 -9
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +1 -1
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
- package/framework/templates/infrastructure/docker/coolify.template.json +3 -3
- package/framework/templates/infrastructure/github/README.md +2 -1
- package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +2 -1
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
- package/package.json +1 -1
- package/src/commands/e2e/e2e.js +2 -2
- package/src/commands/observability/cost.js +18 -60
- package/src/commands/observability/telemetry.js +3 -5
- package/src/commands/project/doctor-procs.js +1 -1
- package/src/commands/project/doctor.js +9 -9
- package/src/commands/project/fleet.js +4 -4
- package/src/commands/project/heavy.js +3 -3
- package/src/commands/project/init.js +6 -1
- package/src/commands/project/setup-infra-cmd.js +2 -1
- package/src/commands/project/stack-scan.js +58 -0
- package/src/commands/project/standards.js +79 -25
- package/src/commands/project/status.js +29 -69
- package/src/commands/project/update.js +33 -19
- package/src/commands/project/worktree.js +1 -1
- package/src/commands/state/advance.js +62 -46
- package/src/commands/state/approve.js +95 -138
- package/src/commands/state/archive.js +20 -84
- package/src/commands/state/auto-approval.js +68 -0
- package/src/commands/state/create.js +28 -1
- package/src/commands/state/delete.js +1 -1
- package/src/commands/state/finish.js +11 -23
- package/src/commands/state/gate-check.js +86 -95
- package/src/commands/state/gate-decision.js +6 -4
- package/src/commands/state/handoff.js +1 -1
- package/src/commands/state/list.js +9 -9
- package/src/commands/state/report-check.js +157 -311
- package/src/commands/state/retype.js +9 -3
- package/src/commands/validation/mutate.js +77 -14
- package/src/commands/validation/plan-check.js +81 -0
- package/src/commands/validation/validate-feature.js +60 -135
- package/src/commands/validation/verify.js +41 -82
- package/src/core/paths/output-schema.js +20 -67
- package/src/core/state/gate-order.js +5 -6
- package/src/core/state/lifecycle-audit.js +2 -2
- package/src/core/state/state-manager.js +17 -98
- package/src/core/templates/template-registry.js +2 -2
- package/src/core/templates/template-seed.js +22 -0
- package/src/core/work-types.js +10 -10
- package/src/dashboard/app/App.jsx +0 -2
- package/src/dashboard/app/components/ActivityFeed.jsx +2 -3
- package/src/dashboard/app/components/CommandPalette.jsx +7 -8
- package/src/dashboard/app/components/EntityLink.jsx +1 -2
- package/src/dashboard/app/components/Markdown.jsx +1 -1
- package/src/dashboard/app/components/Sidebar.jsx +1 -2
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -1
- package/src/dashboard/app/lib/phases.js +2 -4
- package/src/dashboard/app/pages/Activity.jsx +1 -1
- package/src/dashboard/app/pages/FeatureDetail.jsx +139 -129
- package/src/dashboard/app/pages/Features.jsx +9 -9
- package/src/dashboard/app/pages/Guide.jsx +2 -2
- package/src/dashboard/app/pages/PhasePipeline.jsx +3 -4
- package/src/dashboard/app/pages/RulesMap.jsx +3 -3
- package/src/dashboard/app/pages/SkillsLibrary.jsx +3 -3
- package/src/dashboard/app/pages/Standards.jsx +1 -1
- package/src/dashboard/scanner/activity-scanner.js +6 -16
- package/src/dashboard/scanner/agents-scanner.js +1 -1
- package/src/dashboard/scanner/docs-scanner.js +5 -23
- package/src/dashboard/scanner/features-scanner.js +95 -60
- package/src/dashboard/scanner/index.js +0 -2
- package/src/dashboard/scanner/project-api.js +1 -1
- package/src/dashboard/scanner/rules-scanner.js +15 -10
- package/src/dashboard/scanner/skills-scanner.js +2 -3
- package/src/dashboard/scanner/standards-scanner.js +2 -1
- package/src/lib/ci/ci-verify.js +17 -1
- package/src/lib/ci/github-ci.js +1 -1
- package/src/lib/detectors/index.js +3 -8
- package/src/lib/generators/handoff-generator.js +34 -65
- package/src/lib/generators/settings-generator.js +11 -89
- package/src/lib/git/evidence-staleness.js +1 -1
- package/src/lib/git/tree-fingerprint.js +19 -7
- package/src/lib/git/tree-state.js +2 -2
- package/src/lib/heavy/cleanup.js +1 -5
- package/src/lib/installers/memory-installer.js +6 -8
- package/src/lib/mutation/commands.js +3 -2
- package/src/lib/{tasks/dotnet-test-introspection.js → mutation/dotnet-introspection.js} +17 -282
- package/src/lib/mutation/plan.js +14 -6
- package/src/lib/mutation/runner.js +1 -1
- package/src/lib/mutation/subset.js +46 -9
- package/src/lib/plan/artifact-vocabulary.js +60 -0
- package/src/lib/plan/checks.js +234 -0
- package/src/lib/plan/plan-parser.js +209 -0
- package/src/lib/plan/plan-template.js +18 -0
- package/src/lib/review-reports.js +222 -0
- package/src/lib/risk-detector.js +134 -135
- package/src/lib/specs/adr-index.js +50 -69
- package/src/lib/specs/design-seed.js +18 -0
- package/src/lib/specs/frontmatter.js +44 -0
- package/src/lib/specs/gfm-table.js +17 -0
- package/src/lib/specs/old-format.js +68 -0
- package/src/lib/specs/revocations.js +37 -95
- package/src/lib/specs/screen-checklist.js +18 -17
- package/src/lib/stack/dotnet-build-ignores.js +47 -0
- package/src/lib/stack/stack-profile.js +1 -1
- package/src/lib/stack/stack-scan.js +267 -0
- package/src/lib/standards/registry.js +43 -0
- package/src/lib/standards/sections.js +58 -0
- package/src/lib/tasks/eval-node.js +4 -12
- package/src/lib/tasks/test-nodes.js +1 -74
- package/src/lib/tasks/test-runner.js +75 -117
- package/src/lib/telemetry/metrics.js +8 -69
- package/src/lib/telemetry/telemetry-reader.js +5 -118
- package/src/lib/telemetry/verify-event.js +5 -7
- package/src/lib/validators/code-budget/code-budget-validator.js +1 -1
- package/src/lib/validators/contracts/contract-compliance-validator.js +2 -2
- package/src/lib/validators/reuse/reuse-validator.js +24 -48
- package/src/lib/validators/reuse/symbol-scanner.js +2 -4
- package/src/lib/validators/traceability-check.js +85 -129
- package/src/lib/validators/validation-runner.js +8 -88
- package/src/lib/validators/verify-runner.js +134 -301
- package/src/lib/worktree/docker-audit.js +1 -2
- package/src/lib/worktree/fleet-scanner.js +25 -38
- package/src/scripts/setup-infra.js +42 -22
- package/src/utils/agents-installer.js +92 -13
- package/src/utils/claude-settings-manager.js +0 -35
- package/src/utils/file-copier.js +34 -6
- package/src/utils/hooks-installer.js +16 -9
- package/src/utils/skills-installer.js +1 -2
- package/framework/commands/commit.md +0 -166
- package/framework/evals/trace-schema.md +0 -67
- package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
- package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
- package/framework/hooks/git/pre-commit/specs.sh +0 -50
- package/framework/hooks/shared/tasks-validator.js +0 -111
- package/framework/rubrics/backend.json +0 -41
- package/framework/rubrics/default.json +0 -36
- package/framework/rubrics/frontend.json +0 -36
- package/framework/rules/morph-task-tracking.md +0 -54
- package/framework/rules/morph-workflow.md +0 -244
- package/framework/schemas/tasks.schema.json +0 -79
- package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
- package/framework/skills/core/morph-implement/SKILL.md +0 -194
- package/framework/skills/core/morph-proposal/SKILL.md +0 -152
- package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
- package/framework/templates/docs/proposal.md +0 -192
- package/framework/templates/docs/spec.md +0 -57
- package/framework/templates/docs/ui-components.md +0 -52
- package/framework/templates/docs/ui-design-system.md +0 -76
- package/framework/templates/docs/ui-flows.md +0 -167
- package/framework/templates/examples/spec-examples.md +0 -91
- package/framework/templates/feature/decisions.md +0 -124
- package/framework/templates/feature/recap.md +0 -145
- package/framework/templates/project/validate.js +0 -97
- package/framework/templates/tasks.template.json +0 -17
- package/src/commands/state/usecase.js +0 -143
- package/src/commands/tasks/dag.js +0 -181
- package/src/dashboard/app/pages/Rubrics.jsx +0 -97
- package/src/dashboard/scanner/rubrics-scanner.js +0 -12
- package/src/lib/agents/output-collector.js +0 -76
- package/src/lib/evaluator-report.js +0 -91
- package/src/lib/specs/acervo-format.js +0 -70
- package/src/lib/specs/acervo-lifecycle.js +0 -264
- package/src/lib/specs/done-criteria-check.js +0 -200
- package/src/lib/specs/usecase-check.js +0 -301
- package/src/lib/specs/usecase-promoter.js +0 -301
- package/src/lib/specs/usecase-registry.js +0 -237
- package/src/lib/tasks/dispatch-planner.js +0 -319
- package/src/lib/tasks/mermaid-renderer.js +0 -132
- package/src/lib/tasks/task-classifier.js +0 -30
- package/src/lib/tasks/task-parser.js +0 -209
- package/src/lib/tasks/tasks-json-parser.js +0 -296
- package/src/lib/tasks/test-scope.js +0 -597
- package/src/lib/validators/architecture/architecture-validator.js +0 -270
- package/src/lib/validators/architecture/index.js +0 -7
- package/src/lib/validators/css/css-validator.js +0 -340
- package/src/lib/validators/css/index.js +0 -7
- package/src/lib/validators/design-system/design-system-validator.js +0 -237
- package/src/lib/validators/design-system/index.js +0 -7
- package/src/lib/validators/score-aggregator.js +0 -244
- package/src/lib/validators/ui/index.js +0 -7
- package/src/lib/validators/ui/ui-contrast-validator.js +0 -422
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Architecture Pattern Validator
|
|
3
|
-
*
|
|
4
|
-
* Validates architectural patterns and detects anti-patterns in .NET apps.
|
|
5
|
-
* Checks Program.cs configuration, middleware order, and common DI issues.
|
|
6
|
-
*
|
|
7
|
-
* MORPH-SPEC 3.0 - Sprint 4
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { readFileSync } from 'fs';
|
|
11
|
-
import { join } from 'path';
|
|
12
|
-
import chalk from 'chalk';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Architecture Validation Rules
|
|
16
|
-
*/
|
|
17
|
-
const VALIDATION_RULES = {
|
|
18
|
-
// Program.cs Issues
|
|
19
|
-
missingHttpClient: {
|
|
20
|
-
pattern: /AddHttpClient\(\)/,
|
|
21
|
-
inverse: true, // Rule triggers if pattern NOT found
|
|
22
|
-
level: 'error',
|
|
23
|
-
message: 'AddHttpClient() missing in Program.cs',
|
|
24
|
-
reference: 'framework/standards/program-cs-checklist.md',
|
|
25
|
-
solution: 'Add builder.Services.AddHttpClient() before builder.Build()'
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
missingHttpContextAccessor: {
|
|
29
|
-
pattern: /AddHttpContextAccessor\(\)/,
|
|
30
|
-
inverse: true,
|
|
31
|
-
level: 'warning',
|
|
32
|
-
message: 'AddHttpContextAccessor() missing (needed for auth contexts)',
|
|
33
|
-
reference: 'framework/standards/program-cs-checklist.md',
|
|
34
|
-
solution: 'Add builder.Services.AddHttpContextAccessor()'
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
missingStaticFiles: {
|
|
38
|
-
pattern: /app\.UseStaticFiles\(\)/,
|
|
39
|
-
inverse: true,
|
|
40
|
-
level: 'error',
|
|
41
|
-
message: 'app.UseStaticFiles() missing (CSS/JS won\'t load)',
|
|
42
|
-
reference: 'framework/standards/program-cs-checklist.md',
|
|
43
|
-
solution: 'Add app.UseStaticFiles() before app.UseAntiforgery()'
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
wrongMiddlewareOrder: {
|
|
47
|
-
// Custom validator (checked in validateProgramCs)
|
|
48
|
-
level: 'error',
|
|
49
|
-
message: 'app.UseStaticFiles() must come BEFORE app.UseAntiforgery()',
|
|
50
|
-
reference: 'framework/standards/program-cs-checklist.md',
|
|
51
|
-
solution: 'Reorder: UseStaticFiles() → UseAntiforgery() → UseAuthorization()'
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Architecture Validator Class
|
|
57
|
-
*/
|
|
58
|
-
export class ArchitectureValidator {
|
|
59
|
-
constructor(projectPath = '.') {
|
|
60
|
-
this.projectPath = projectPath;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Validate all architecture rules
|
|
65
|
-
*/
|
|
66
|
-
async validateAll() {
|
|
67
|
-
const results = {
|
|
68
|
-
programCs: await this.validateProgramCs()
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const allIssues = [
|
|
72
|
-
...results.programCs.issues
|
|
73
|
-
];
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
status: allIssues.length === 0 ? 'ok' : 'error',
|
|
77
|
-
totalIssues: allIssues.length,
|
|
78
|
-
errors: allIssues.filter(i => i.level === 'error').length,
|
|
79
|
-
warnings: allIssues.filter(i => i.level === 'warning').length,
|
|
80
|
-
// Top-level too, not only nested under `results`: the runner reports
|
|
81
|
-
// "❌ 2 error(s)" from the counts but prints the issue list, and reading
|
|
82
|
-
// only the top level meant a failure arrived with no file, no line and
|
|
83
|
-
// no fix — impossible to act on.
|
|
84
|
-
issues: allIssues,
|
|
85
|
-
results
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Validate Program.cs
|
|
91
|
-
*/
|
|
92
|
-
async validateProgramCs() {
|
|
93
|
-
// Resolved against `projectPath`, not the cwd. The constructor stored
|
|
94
|
-
// `projectPath` and nothing ever read it, so this validator inspected
|
|
95
|
-
// whatever directory the process happened to be in — in a worktree or a
|
|
96
|
-
// fleet run that is a different project, and reporting "no Program.cs,
|
|
97
|
-
// nothing to check" reads exactly like a pass.
|
|
98
|
-
const programCsPath = join(this.projectPath, 'Program.cs');
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
const content = readFileSync(programCsPath, 'utf-8');
|
|
102
|
-
return this.validateProgramCsContent(content);
|
|
103
|
-
} catch (error) {
|
|
104
|
-
// No Program.cs found (not a .NET project)
|
|
105
|
-
return {
|
|
106
|
-
exists: false,
|
|
107
|
-
issues: []
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Validate Program.cs content
|
|
114
|
-
*/
|
|
115
|
-
validateProgramCsContent(content) {
|
|
116
|
-
const issues = [];
|
|
117
|
-
|
|
118
|
-
// Check missing patterns (inverse rules)
|
|
119
|
-
const inverseRules = ['missingHttpClient', 'missingHttpContextAccessor', 'missingStaticFiles'];
|
|
120
|
-
|
|
121
|
-
for (const ruleId of inverseRules) {
|
|
122
|
-
const rule = VALIDATION_RULES[ruleId];
|
|
123
|
-
const found = rule.pattern.test(content);
|
|
124
|
-
|
|
125
|
-
if (!found) {
|
|
126
|
-
issues.push({
|
|
127
|
-
file: 'Program.cs',
|
|
128
|
-
rule: ruleId,
|
|
129
|
-
level: rule.level,
|
|
130
|
-
message: rule.message,
|
|
131
|
-
reference: rule.reference,
|
|
132
|
-
solution: rule.solution
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Check middleware order
|
|
138
|
-
const staticFilesIndex = content.indexOf('UseStaticFiles');
|
|
139
|
-
const antiforgeryIndex = content.indexOf('UseAntiforgery');
|
|
140
|
-
|
|
141
|
-
if (staticFilesIndex > 0 && antiforgeryIndex > 0 && staticFilesIndex > antiforgeryIndex) {
|
|
142
|
-
const rule = VALIDATION_RULES.wrongMiddlewareOrder;
|
|
143
|
-
issues.push({
|
|
144
|
-
file: 'Program.cs',
|
|
145
|
-
rule: 'wrongMiddlewareOrder',
|
|
146
|
-
level: rule.level,
|
|
147
|
-
message: rule.message,
|
|
148
|
-
reference: rule.reference,
|
|
149
|
-
solution: rule.solution,
|
|
150
|
-
lineNumber: this.findLineNumber(content, /UseStaticFiles/)
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return {
|
|
155
|
-
exists: true,
|
|
156
|
-
issues
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Find line number of pattern match
|
|
162
|
-
*/
|
|
163
|
-
findLineNumber(content, pattern) {
|
|
164
|
-
const lines = content.split('\n');
|
|
165
|
-
for (let i = 0; i < lines.length; i++) {
|
|
166
|
-
if (pattern.test(lines[i])) {
|
|
167
|
-
return i + 1;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Format validation results for console output
|
|
175
|
-
*/
|
|
176
|
-
formatResults(results) {
|
|
177
|
-
if (results.status === 'ok') {
|
|
178
|
-
console.log(chalk.green('✅ No architecture issues found'));
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
console.log(chalk.yellow(`\n⚠️ Found ${results.totalIssues} architecture issue(s):\n`));
|
|
183
|
-
|
|
184
|
-
// Group by file
|
|
185
|
-
const issuesByFile = new Map();
|
|
186
|
-
const allIssues = [
|
|
187
|
-
...results.results.programCs.issues
|
|
188
|
-
];
|
|
189
|
-
|
|
190
|
-
for (const issue of allIssues) {
|
|
191
|
-
if (!issuesByFile.has(issue.file)) {
|
|
192
|
-
issuesByFile.set(issue.file, []);
|
|
193
|
-
}
|
|
194
|
-
issuesByFile.get(issue.file).push(issue);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Display by file
|
|
198
|
-
for (const [file, issues] of issuesByFile) {
|
|
199
|
-
console.log(chalk.cyan(`\n📄 ${file}`));
|
|
200
|
-
|
|
201
|
-
const errors = issues.filter(i => i.level === 'error');
|
|
202
|
-
const warnings = issues.filter(i => i.level === 'warning');
|
|
203
|
-
|
|
204
|
-
if (errors.length > 0) {
|
|
205
|
-
console.log(chalk.red(` ❌ ${errors.length} error(s):`));
|
|
206
|
-
errors.forEach(e => {
|
|
207
|
-
const location = e.lineNumber ? `:${e.lineNumber}` : '';
|
|
208
|
-
console.log(chalk.red(` ${file}${location}`));
|
|
209
|
-
console.log(chalk.red(` - ${e.message}`));
|
|
210
|
-
console.log(chalk.gray(` Solution: ${e.solution}`));
|
|
211
|
-
console.log(chalk.blue(` Reference: ${e.reference}`));
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (warnings.length > 0) {
|
|
216
|
-
console.log(chalk.yellow(` ⚠️ ${warnings.length} warning(s):`));
|
|
217
|
-
warnings.forEach(w => {
|
|
218
|
-
const location = w.lineNumber ? `:${w.lineNumber}` : '';
|
|
219
|
-
console.log(chalk.yellow(` ${file}${location}`));
|
|
220
|
-
console.log(chalk.yellow(` - ${w.message}`));
|
|
221
|
-
console.log(chalk.gray(` Solution: ${w.solution}`));
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
console.log('');
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Get suggested fixes
|
|
231
|
-
*/
|
|
232
|
-
getSuggestedFixes(results) {
|
|
233
|
-
const fixes = [];
|
|
234
|
-
const allIssues = [
|
|
235
|
-
...results.results.programCs.issues
|
|
236
|
-
];
|
|
237
|
-
|
|
238
|
-
for (const issue of allIssues) {
|
|
239
|
-
fixes.push({
|
|
240
|
-
file: issue.file,
|
|
241
|
-
line: issue.lineNumber,
|
|
242
|
-
issue: issue.message,
|
|
243
|
-
solution: issue.solution,
|
|
244
|
-
reference: issue.reference,
|
|
245
|
-
severity: issue.level
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
return fixes;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Quick validation function (for imports)
|
|
255
|
-
*/
|
|
256
|
-
export async function validateArchitecture(projectPath = '.', options = {}) {
|
|
257
|
-
const validator = new ArchitectureValidator(projectPath);
|
|
258
|
-
const results = await validator.validateAll();
|
|
259
|
-
|
|
260
|
-
if (options.verbose) {
|
|
261
|
-
validator.formatResults(results);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (options.fixes) {
|
|
265
|
-
const fixes = validator.getSuggestedFixes(results);
|
|
266
|
-
return { ...results, fixes };
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return results;
|
|
270
|
-
}
|
|
@@ -1,340 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MORPH-SPEC CSS Validator Library
|
|
3
|
-
*
|
|
4
|
-
* Validates that CSS classes used in HTML/JSX/TSX files exist in CSS files.
|
|
5
|
-
* Helps catch missing CSS definitions before runtime.
|
|
6
|
-
*
|
|
7
|
-
* @module css-validator
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { readFileSync, existsSync } from 'fs';
|
|
11
|
-
import { glob } from 'glob';
|
|
12
|
-
import { join, relative } from 'path';
|
|
13
|
-
|
|
14
|
-
// ============================================================================
|
|
15
|
-
// CSS CLASS EXTRACTION
|
|
16
|
-
// ============================================================================
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Extract CSS class names from a CSS file content
|
|
20
|
-
* @param {string} content - CSS file content
|
|
21
|
-
* @returns {Set<string>} Set of class names defined in the CSS
|
|
22
|
-
*/
|
|
23
|
-
export function extractCssClasses(content) {
|
|
24
|
-
const classes = new Set();
|
|
25
|
-
|
|
26
|
-
// Match class selectors: .class-name
|
|
27
|
-
// Handles: .class, .class:hover, .class::before, .class.other-class
|
|
28
|
-
const classRegex = /\.([a-zA-Z_-][a-zA-Z0-9_-]*)/g;
|
|
29
|
-
|
|
30
|
-
let match;
|
|
31
|
-
while ((match = classRegex.exec(content)) !== null) {
|
|
32
|
-
classes.add(match[1]);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return classes;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Extract CSS classes used in HTML/JSX/TSX file content
|
|
40
|
-
* @param {string} content - HTML or JSX/TSX file content
|
|
41
|
-
* @returns {Array<{className: string, line: number}>} Array of class usages with line numbers
|
|
42
|
-
*/
|
|
43
|
-
export function extractRazorClasses(content) {
|
|
44
|
-
const usages = [];
|
|
45
|
-
const lines = content.split('\n');
|
|
46
|
-
|
|
47
|
-
lines.forEach((line, index) => {
|
|
48
|
-
// Match class="..." or class='...'
|
|
49
|
-
const classAttrRegex = /class\s*=\s*["']([^"']+)["']/gi;
|
|
50
|
-
|
|
51
|
-
// Match className="..." (React/Next.js JSX)
|
|
52
|
-
const componentClassRegex = /\bclassName\s*=\s*["']([^"']+)["']/gi;
|
|
53
|
-
|
|
54
|
-
// Match Style with class-like patterns in CSS modules
|
|
55
|
-
const cssModuleRegex = /styles\.([a-zA-Z_-][a-zA-Z0-9_-]*)/gi;
|
|
56
|
-
|
|
57
|
-
for (const regex of [classAttrRegex, componentClassRegex]) {
|
|
58
|
-
let match;
|
|
59
|
-
while ((match = regex.exec(line)) !== null) {
|
|
60
|
-
const classString = match[1];
|
|
61
|
-
|
|
62
|
-
// Split by whitespace to get individual classes
|
|
63
|
-
const classes = classString.split(/\s+/).filter(c => c.length > 0);
|
|
64
|
-
|
|
65
|
-
for (const className of classes) {
|
|
66
|
-
// Skip interpolated strings
|
|
67
|
-
if (className.includes('{') || className.includes('}')) continue;
|
|
68
|
-
|
|
69
|
-
// Skip conditional ternary results (partial matches)
|
|
70
|
-
if (className === '?' || className === ':') continue;
|
|
71
|
-
|
|
72
|
-
usages.push({
|
|
73
|
-
className: className.trim(),
|
|
74
|
-
line: index + 1
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// CSS modules
|
|
81
|
-
let cssModuleMatch;
|
|
82
|
-
while ((cssModuleMatch = cssModuleRegex.exec(line)) !== null) {
|
|
83
|
-
usages.push({
|
|
84
|
-
className: cssModuleMatch[1],
|
|
85
|
-
line: index + 1
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
return usages;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// ============================================================================
|
|
94
|
-
// VALIDATION
|
|
95
|
-
// ============================================================================
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Known utility class prefixes from common frameworks
|
|
99
|
-
* These are excluded from validation as they come from external libraries
|
|
100
|
-
*/
|
|
101
|
-
const UTILITY_PREFIXES = [
|
|
102
|
-
// Tailwind CSS
|
|
103
|
-
'bg-', 'text-', 'font-', 'p-', 'px-', 'py-', 'pt-', 'pb-', 'pl-', 'pr-',
|
|
104
|
-
'm-', 'mx-', 'my-', 'mt-', 'mb-', 'ml-', 'mr-', 'w-', 'h-', 'min-', 'max-',
|
|
105
|
-
'flex', 'grid', 'block', 'inline', 'hidden', 'overflow-', 'rounded-',
|
|
106
|
-
'border-', 'shadow-', 'opacity-', 'z-', 'gap-', 'space-', 'justify-',
|
|
107
|
-
'items-', 'content-', 'self-', 'col-', 'row-', 'order-', 'animate-',
|
|
108
|
-
'transition-', 'duration-', 'ease-', 'delay-', 'scale-', 'rotate-',
|
|
109
|
-
'translate-', 'skew-', 'origin-', 'cursor-', 'select-', 'resize-',
|
|
110
|
-
'list-', 'appearance-', 'pointer-', 'fill-', 'stroke-', 'object-',
|
|
111
|
-
'aspect-', 'columns-', 'break-', 'box-', 'float-', 'clear-', 'isolation-',
|
|
112
|
-
'mix-blend-', 'bg-blend-', 'filter-', 'backdrop-', 'blur-', 'brightness-',
|
|
113
|
-
'contrast-', 'drop-shadow-', 'grayscale-', 'hue-rotate-', 'invert-',
|
|
114
|
-
'saturate-', 'sepia-', 'table-', 'caption-', 'border-collapse-', 'outline-',
|
|
115
|
-
'ring-', 'placeholder-', 'caret-', 'accent-', 'scroll-', 'snap-', 'touch-',
|
|
116
|
-
'will-change-', 'contain-',
|
|
117
|
-
|
|
118
|
-
// Bootstrap
|
|
119
|
-
'container', 'row', 'col-', 'btn-', 'alert-', 'badge-', 'card-', 'nav-',
|
|
120
|
-
'navbar-', 'dropdown-', 'modal-', 'form-', 'input-', 'table-', 'list-',
|
|
121
|
-
'd-', 'align-', 'justify-', 'order-', 'offset-', 'g-', 'gy-', 'gx-',
|
|
122
|
-
|
|
123
|
-
// Common framework classes
|
|
124
|
-
'sr-only', 'not-sr-only', 'disabled', 'active', 'show', 'fade', 'collapse'
|
|
125
|
-
];
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Check if a class name is a utility class from a known framework
|
|
129
|
-
* @param {string} className - Class name to check
|
|
130
|
-
* @returns {boolean} True if it's a utility class
|
|
131
|
-
*/
|
|
132
|
-
function isUtilityClass(className) {
|
|
133
|
-
return UTILITY_PREFIXES.some(prefix => className.startsWith(prefix));
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Validate CSS classes in a project directory
|
|
138
|
-
* @param {string} projectPath - Path to the project directory
|
|
139
|
-
* @param {Object} options - Validation options
|
|
140
|
-
* @param {boolean} options.verbose - Show detailed output
|
|
141
|
-
* @param {boolean} options.includeUtilities - Include utility classes in validation
|
|
142
|
-
* @param {string[]} options.cssGlob - Glob patterns for CSS files
|
|
143
|
-
* @param {string[]} options.razorGlob - Glob patterns for HTML/JSX/TSX files
|
|
144
|
-
* @returns {Promise<Object>} Validation results
|
|
145
|
-
*/
|
|
146
|
-
export async function validateCssClasses(projectPath, options = {}) {
|
|
147
|
-
const {
|
|
148
|
-
verbose = false,
|
|
149
|
-
includeUtilities = false,
|
|
150
|
-
cssGlob = ['**/*.css'],
|
|
151
|
-
razorGlob = ['**/*.html', '**/*.jsx', '**/*.tsx']
|
|
152
|
-
} = options;
|
|
153
|
-
|
|
154
|
-
// Find all CSS files
|
|
155
|
-
const cssFiles = [];
|
|
156
|
-
for (const pattern of cssGlob) {
|
|
157
|
-
const files = await glob(pattern, {
|
|
158
|
-
cwd: projectPath,
|
|
159
|
-
ignore: ['**/node_modules/**', '**/bin/**', '**/obj/**', '**/dist/**', '**/.next/**']
|
|
160
|
-
});
|
|
161
|
-
cssFiles.push(...files);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Find all source files (HTML/JSX/TSX)
|
|
165
|
-
const razorFiles = [];
|
|
166
|
-
for (const pattern of razorGlob) {
|
|
167
|
-
const files = await glob(pattern, {
|
|
168
|
-
cwd: projectPath,
|
|
169
|
-
ignore: ['**/node_modules/**', '**/bin/**', '**/obj/**']
|
|
170
|
-
});
|
|
171
|
-
razorFiles.push(...files);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Extract all defined CSS classes
|
|
175
|
-
const definedClasses = new Set();
|
|
176
|
-
const cssFileDetails = [];
|
|
177
|
-
|
|
178
|
-
for (const file of cssFiles) {
|
|
179
|
-
const fullPath = join(projectPath, file);
|
|
180
|
-
if (!existsSync(fullPath)) continue;
|
|
181
|
-
|
|
182
|
-
const content = readFileSync(fullPath, 'utf8');
|
|
183
|
-
const classes = extractCssClasses(content);
|
|
184
|
-
|
|
185
|
-
cssFileDetails.push({
|
|
186
|
-
file,
|
|
187
|
-
classCount: classes.size
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
for (const cls of classes) {
|
|
191
|
-
definedClasses.add(cls);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Extract all used CSS classes from source files
|
|
196
|
-
const usedClasses = new Map(); // className -> [{file, line}]
|
|
197
|
-
const razorFileDetails = [];
|
|
198
|
-
|
|
199
|
-
for (const file of razorFiles) {
|
|
200
|
-
const fullPath = join(projectPath, file);
|
|
201
|
-
if (!existsSync(fullPath)) continue;
|
|
202
|
-
|
|
203
|
-
const content = readFileSync(fullPath, 'utf8');
|
|
204
|
-
const usages = extractRazorClasses(content);
|
|
205
|
-
|
|
206
|
-
razorFileDetails.push({
|
|
207
|
-
file,
|
|
208
|
-
usageCount: usages.length
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
for (const usage of usages) {
|
|
212
|
-
if (!usedClasses.has(usage.className)) {
|
|
213
|
-
usedClasses.set(usage.className, []);
|
|
214
|
-
}
|
|
215
|
-
usedClasses.get(usage.className).push({
|
|
216
|
-
file,
|
|
217
|
-
line: usage.line
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Find missing classes
|
|
223
|
-
const missingClasses = [];
|
|
224
|
-
|
|
225
|
-
for (const [className, locations] of usedClasses) {
|
|
226
|
-
// Skip utility classes unless explicitly included
|
|
227
|
-
if (!includeUtilities && isUtilityClass(className)) continue;
|
|
228
|
-
|
|
229
|
-
// Skip if class is defined
|
|
230
|
-
if (definedClasses.has(className)) continue;
|
|
231
|
-
|
|
232
|
-
missingClasses.push({
|
|
233
|
-
className,
|
|
234
|
-
locations
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Sort missing classes by number of usages (most used first)
|
|
239
|
-
missingClasses.sort((a, b) => b.locations.length - a.locations.length);
|
|
240
|
-
|
|
241
|
-
return {
|
|
242
|
-
summary: {
|
|
243
|
-
cssFilesScanned: cssFiles.length,
|
|
244
|
-
razorFilesScanned: razorFiles.length,
|
|
245
|
-
definedClasses: definedClasses.size,
|
|
246
|
-
usedClasses: usedClasses.size,
|
|
247
|
-
missingClasses: missingClasses.length
|
|
248
|
-
},
|
|
249
|
-
cssFiles: cssFileDetails,
|
|
250
|
-
razorFiles: razorFileDetails,
|
|
251
|
-
definedClasses: Array.from(definedClasses).sort(),
|
|
252
|
-
missingClasses,
|
|
253
|
-
valid: missingClasses.length === 0
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Format validation results for CLI output
|
|
259
|
-
* @param {Object} results - Validation results from validateCssClasses
|
|
260
|
-
* @param {boolean} verbose - Show detailed output
|
|
261
|
-
* @returns {string} Formatted output
|
|
262
|
-
*/
|
|
263
|
-
export function formatValidationResults(results, verbose = false) {
|
|
264
|
-
const lines = [];
|
|
265
|
-
|
|
266
|
-
// Header
|
|
267
|
-
lines.push('');
|
|
268
|
-
lines.push('CSS Validation Results');
|
|
269
|
-
lines.push('='.repeat(50));
|
|
270
|
-
lines.push('');
|
|
271
|
-
|
|
272
|
-
// Summary
|
|
273
|
-
lines.push(`CSS files scanned: ${results.summary.cssFilesScanned}`);
|
|
274
|
-
lines.push(`Source files scanned: ${results.summary.razorFilesScanned}`);
|
|
275
|
-
lines.push(`Classes defined: ${results.summary.definedClasses}`);
|
|
276
|
-
lines.push(`Classes used: ${results.summary.usedClasses}`);
|
|
277
|
-
lines.push(`Missing classes: ${results.summary.missingClasses}`);
|
|
278
|
-
lines.push('');
|
|
279
|
-
|
|
280
|
-
if (results.valid) {
|
|
281
|
-
lines.push('All CSS classes are defined.');
|
|
282
|
-
return lines.join('\n');
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Missing classes
|
|
286
|
-
lines.push('Missing CSS Classes:');
|
|
287
|
-
lines.push('-'.repeat(50));
|
|
288
|
-
|
|
289
|
-
for (const missing of results.missingClasses) {
|
|
290
|
-
lines.push(`\n .${missing.className}`);
|
|
291
|
-
|
|
292
|
-
if (verbose) {
|
|
293
|
-
for (const loc of missing.locations) {
|
|
294
|
-
lines.push(` - ${loc.file}:${loc.line}`);
|
|
295
|
-
}
|
|
296
|
-
} else {
|
|
297
|
-
// Show first 3 locations
|
|
298
|
-
const shown = missing.locations.slice(0, 3);
|
|
299
|
-
for (const loc of shown) {
|
|
300
|
-
lines.push(` - ${loc.file}:${loc.line}`);
|
|
301
|
-
}
|
|
302
|
-
if (missing.locations.length > 3) {
|
|
303
|
-
lines.push(` ... and ${missing.locations.length - 3} more`);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
lines.push('');
|
|
309
|
-
|
|
310
|
-
return lines.join('\n');
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Generate a CSS stub file with missing class definitions
|
|
315
|
-
* @param {Object} results - Validation results
|
|
316
|
-
* @returns {string} CSS file content with stub classes
|
|
317
|
-
*/
|
|
318
|
-
export function generateCssStub(results) {
|
|
319
|
-
if (results.valid) {
|
|
320
|
-
return '/* No missing classes */';
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const lines = [
|
|
324
|
-
'/* ==========================================================================',
|
|
325
|
-
' MISSING CSS CLASSES - Generated by MORPH-SPEC validate-css',
|
|
326
|
-
' Add these classes to your design-system.css or appropriate CSS file',
|
|
327
|
-
' ========================================================================== */',
|
|
328
|
-
''
|
|
329
|
-
];
|
|
330
|
-
|
|
331
|
-
for (const missing of results.missingClasses) {
|
|
332
|
-
lines.push(`/* Used in: ${missing.locations.map(l => `${l.file}:${l.line}`).slice(0, 3).join(', ')}${missing.locations.length > 3 ? '...' : ''} */`);
|
|
333
|
-
lines.push(`.${missing.className} {`);
|
|
334
|
-
lines.push(' /* TODO: Add styles */');
|
|
335
|
-
lines.push('}');
|
|
336
|
-
lines.push('');
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
return lines.join('\n');
|
|
340
|
-
}
|