@dewtech/dare-cli 3.11.0 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/__tests__/ensure-skills.test.js +5 -0
- package/dist/__tests__/ensure-skills.test.js.map +1 -1
- package/dist/__tests__/ide-command-parity.test.js +1 -0
- package/dist/__tests__/ide-command-parity.test.js.map +1 -1
- package/dist/__tests__/project-generator.test.js +17 -0
- package/dist/__tests__/project-generator.test.js.map +1 -1
- package/dist/__tests__/reverse-facts.test.js +1 -0
- package/dist/__tests__/reverse-facts.test.js.map +1 -1
- package/dist/__tests__/terminal-parity-regression.test.d.ts +2 -0
- package/dist/__tests__/terminal-parity-regression.test.d.ts.map +1 -0
- package/dist/__tests__/terminal-parity-regression.test.js +116 -0
- package/dist/__tests__/terminal-parity-regression.test.js.map +1 -0
- package/dist/__tests__/terminal-parity.test.d.ts +2 -0
- package/dist/__tests__/terminal-parity.test.d.ts.map +1 -0
- package/dist/__tests__/terminal-parity.test.js +81 -0
- package/dist/__tests__/terminal-parity.test.js.map +1 -0
- package/dist/agent/__tests__/antigravity-driver.test.d.ts +2 -0
- package/dist/agent/__tests__/antigravity-driver.test.d.ts.map +1 -0
- package/dist/agent/__tests__/antigravity-driver.test.js +52 -0
- package/dist/agent/__tests__/antigravity-driver.test.js.map +1 -0
- package/dist/agent/__tests__/codex-driver.test.d.ts +2 -0
- package/dist/agent/__tests__/codex-driver.test.d.ts.map +1 -0
- package/dist/agent/__tests__/codex-driver.test.js +68 -0
- package/dist/agent/__tests__/codex-driver.test.js.map +1 -0
- package/dist/agent/__tests__/cursor-driver.test.d.ts +2 -0
- package/dist/agent/__tests__/cursor-driver.test.d.ts.map +1 -0
- package/dist/agent/__tests__/cursor-driver.test.js +52 -0
- package/dist/agent/__tests__/cursor-driver.test.js.map +1 -0
- package/dist/agent/driver.d.ts +1 -1
- package/dist/agent/driver.d.ts.map +1 -1
- package/dist/agent/drivers/antigravity.d.ts +8 -0
- package/dist/agent/drivers/antigravity.d.ts.map +1 -0
- package/dist/agent/drivers/antigravity.js +99 -0
- package/dist/agent/drivers/antigravity.js.map +1 -0
- package/dist/agent/drivers/codex.d.ts +12 -0
- package/dist/agent/drivers/codex.d.ts.map +1 -0
- package/dist/agent/drivers/codex.js +137 -0
- package/dist/agent/drivers/codex.js.map +1 -0
- package/dist/agent/drivers/cursor.d.ts +8 -0
- package/dist/agent/drivers/cursor.d.ts.map +1 -0
- package/dist/agent/drivers/cursor.js +99 -0
- package/dist/agent/drivers/cursor.js.map +1 -0
- package/dist/ai/__tests__/ai-core.test.d.ts +2 -0
- package/dist/ai/__tests__/ai-core.test.d.ts.map +1 -0
- package/dist/ai/__tests__/ai-core.test.js +41 -0
- package/dist/ai/__tests__/ai-core.test.js.map +1 -0
- package/dist/ai/__tests__/parity.test.d.ts +2 -0
- package/dist/ai/__tests__/parity.test.d.ts.map +1 -0
- package/dist/ai/__tests__/parity.test.js +36 -0
- package/dist/ai/__tests__/parity.test.js.map +1 -0
- package/dist/ai/__tests__/pipeline.test.d.ts +2 -0
- package/dist/ai/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/ai/__tests__/pipeline.test.js +147 -0
- package/dist/ai/__tests__/pipeline.test.js.map +1 -0
- package/dist/ai/__tests__/refine-bridge.test.d.ts +2 -0
- package/dist/ai/__tests__/refine-bridge.test.d.ts.map +1 -0
- package/dist/ai/__tests__/refine-bridge.test.js +17 -0
- package/dist/ai/__tests__/refine-bridge.test.js.map +1 -0
- package/dist/ai/__tests__/resolve.test.d.ts +2 -0
- package/dist/ai/__tests__/resolve.test.d.ts.map +1 -0
- package/dist/ai/__tests__/resolve.test.js +42 -0
- package/dist/ai/__tests__/resolve.test.js.map +1 -0
- package/dist/ai/capabilities.d.ts +3 -0
- package/dist/ai/capabilities.d.ts.map +1 -0
- package/dist/ai/capabilities.js +11 -0
- package/dist/ai/capabilities.js.map +1 -0
- package/dist/ai/command-options.d.ts +10 -0
- package/dist/ai/command-options.d.ts.map +1 -0
- package/dist/ai/command-options.js +15 -0
- package/dist/ai/command-options.js.map +1 -0
- package/dist/ai/config.d.ts +27 -0
- package/dist/ai/config.d.ts.map +1 -0
- package/dist/ai/config.js +89 -0
- package/dist/ai/config.js.map +1 -0
- package/dist/ai/parity.d.ts +13 -0
- package/dist/ai/parity.d.ts.map +1 -0
- package/dist/ai/parity.js +87 -0
- package/dist/ai/parity.js.map +1 -0
- package/dist/ai/parse-json-output.d.ts +5 -0
- package/dist/ai/parse-json-output.d.ts.map +1 -0
- package/dist/ai/parse-json-output.js +25 -0
- package/dist/ai/parse-json-output.js.map +1 -0
- package/dist/ai/pipeline.d.ts +20 -0
- package/dist/ai/pipeline.d.ts.map +1 -0
- package/dist/ai/pipeline.js +303 -0
- package/dist/ai/pipeline.js.map +1 -0
- package/dist/ai/prompts.d.ts +6 -0
- package/dist/ai/prompts.d.ts.map +1 -0
- package/dist/ai/prompts.js +49 -0
- package/dist/ai/prompts.js.map +1 -0
- package/dist/ai/providers.d.ts +63 -0
- package/dist/ai/providers.d.ts.map +1 -0
- package/dist/ai/providers.js +297 -0
- package/dist/ai/providers.js.map +1 -0
- package/dist/ai/refine-bridge.d.ts +5 -0
- package/dist/ai/refine-bridge.d.ts.map +1 -0
- package/dist/ai/refine-bridge.js +14 -0
- package/dist/ai/refine-bridge.js.map +1 -0
- package/dist/ai/registry.d.ts +12 -0
- package/dist/ai/registry.d.ts.map +1 -0
- package/dist/ai/registry.js +43 -0
- package/dist/ai/registry.js.map +1 -0
- package/dist/ai/resolve.d.ts +28 -0
- package/dist/ai/resolve.d.ts.map +1 -0
- package/dist/ai/resolve.js +83 -0
- package/dist/ai/resolve.js.map +1 -0
- package/dist/ai/schemas.d.ts +175 -0
- package/dist/ai/schemas.d.ts.map +1 -0
- package/dist/ai/schemas.js +199 -0
- package/dist/ai/schemas.js.map +1 -0
- package/dist/ai/types.d.ts +52 -0
- package/dist/ai/types.d.ts.map +1 -0
- package/dist/ai/types.js +8 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/bin/dare.js +2 -0
- package/dist/bin/dare.js.map +1 -1
- package/dist/commands/__tests__/ai-command.test.d.ts +2 -0
- package/dist/commands/__tests__/ai-command.test.d.ts.map +1 -0
- package/dist/commands/__tests__/ai-command.test.js +68 -0
- package/dist/commands/__tests__/ai-command.test.js.map +1 -0
- package/dist/commands/__tests__/execute-agent.test.js +82 -0
- package/dist/commands/__tests__/execute-agent.test.js.map +1 -1
- package/dist/commands/ai.d.ts +3 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +141 -0
- package/dist/commands/ai.js.map +1 -0
- package/dist/commands/blueprint.d.ts.map +1 -1
- package/dist/commands/blueprint.js +17 -3
- package/dist/commands/blueprint.js.map +1 -1
- package/dist/commands/design.d.ts.map +1 -1
- package/dist/commands/design.js +21 -2
- package/dist/commands/design.js.map +1 -1
- package/dist/commands/discover.d.ts.map +1 -1
- package/dist/commands/discover.js +9 -1
- package/dist/commands/discover.js.map +1 -1
- package/dist/commands/dna.d.ts.map +1 -1
- package/dist/commands/dna.js +23 -3
- package/dist/commands/dna.js.map +1 -1
- package/dist/commands/execute.d.ts +11 -0
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +111 -4
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +14 -2
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/patterns.d.ts.map +1 -1
- package/dist/commands/patterns.js +14 -2
- package/dist/commands/patterns.js.map +1 -1
- package/dist/commands/refine.d.ts.map +1 -1
- package/dist/commands/refine.js +23 -2
- package/dist/commands/refine.js.map +1 -1
- package/dist/commands/reverse.d.ts.map +1 -1
- package/dist/commands/reverse.js +28 -3
- package/dist/commands/reverse.js.map +1 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +25 -3
- package/dist/commands/review.js.map +1 -1
- package/dist/core/types/project.d.ts +1 -1
- package/dist/core/types/project.d.ts.map +1 -1
- package/dist/dag-runner/run_dag.d.ts +1 -1
- package/dist/dag-runner/run_dag.d.ts.map +1 -1
- package/dist/exec/safe-spawn.d.ts.map +1 -1
- package/dist/exec/safe-spawn.js +6 -1
- package/dist/exec/safe-spawn.js.map +1 -1
- package/dist/skills/bundled.d.ts +5 -0
- package/dist/skills/bundled.d.ts.map +1 -0
- package/dist/skills/bundled.js +34 -0
- package/dist/skills/bundled.js.map +1 -0
- package/dist/skills/commands/add.d.ts +1 -3
- package/dist/skills/commands/add.d.ts.map +1 -1
- package/dist/skills/commands/add.js +20 -3
- package/dist/skills/commands/add.js.map +1 -1
- package/dist/skills/tests/bundled.spec.d.ts +2 -0
- package/dist/skills/tests/bundled.spec.d.ts.map +1 -0
- package/dist/skills/tests/bundled.spec.js +24 -0
- package/dist/skills/tests/bundled.spec.js.map +1 -0
- package/dist/types/UpdateManifest.types.d.ts +1 -1
- package/dist/types/UpdateManifest.types.d.ts.map +1 -1
- package/dist/utils/dag-converter.js +1 -1
- package/dist/utils/dag-converter.js.map +1 -1
- package/dist/utils/project-detector.d.ts +1 -0
- package/dist/utils/project-detector.d.ts.map +1 -1
- package/dist/utils/project-detector.js +8 -0
- package/dist/utils/project-detector.js.map +1 -1
- package/dist/utils/project-generator.d.ts +1 -1
- package/dist/utils/project-generator.d.ts.map +1 -1
- package/dist/utils/project-generator.js +23 -2
- package/dist/utils/project-generator.js.map +1 -1
- package/dist/utils/templates.d.ts +2 -0
- package/dist/utils/templates.d.ts.map +1 -1
- package/dist/utils/templates.js +74 -0
- package/dist/utils/templates.js.map +1 -1
- package/dist/verification/__tests__/safe-spawn.test.js +12 -0
- package/dist/verification/__tests__/safe-spawn.test.js.map +1 -1
- package/package.json +2 -1
- package/skills/dare-ax/generator.ts +325 -0
- package/skills/dare-ax/index.ts +19 -0
- package/skills/dare-ax/metrics.ts +352 -0
- package/skills/dare-ax/package-lock.json +1855 -0
- package/skills/dare-ax/package.json +50 -0
- package/skills/dare-ax/secret-detector.ts +123 -0
- package/skills/dare-ax/skill.yml +19 -0
- package/skills/dare-ax/templates/llms.txt.jinja2 +80 -0
- package/skills/dare-ax/tests/generator.spec.ts +193 -0
- package/skills/dare-ax/tests/metrics.spec.ts +394 -0
- package/skills/dare-ax/tests/validator.spec.ts +298 -0
- package/skills/dare-ax/tsconfig.json +18 -0
- package/skills/dare-ax/types.ts +79 -0
- package/skills/dare-ax/validator.ts +238 -0
- package/skills/dare-frontend-design/generator.ts +616 -0
- package/skills/dare-frontend-design/index.ts +25 -0
- package/skills/dare-frontend-design/linter.ts +227 -0
- package/skills/dare-frontend-design/metrics.ts +82 -0
- package/skills/dare-frontend-design/package-lock.json +1855 -0
- package/skills/dare-frontend-design/package.json +43 -0
- package/skills/dare-frontend-design/skill.yml +20 -0
- package/skills/dare-frontend-design/tests/frontend_design.spec.ts +435 -0
- package/skills/dare-frontend-design/tsconfig.json +18 -0
- package/skills/dare-frontend-design/types.ts +62 -0
- package/skills/dare-layered-design/generator.ts +740 -0
- package/skills/dare-layered-design/index.ts +17 -0
- package/skills/dare-layered-design/linter.ts +462 -0
- package/skills/dare-layered-design/metrics.ts +409 -0
- package/skills/dare-layered-design/package-lock.json +1855 -0
- package/skills/dare-layered-design/package.json +50 -0
- package/skills/dare-layered-design/skill.yml +35 -0
- package/skills/dare-layered-design/tests/generator.spec.ts +156 -0
- package/skills/dare-layered-design/tests/linter.spec.ts +255 -0
- package/skills/dare-layered-design/tests/metrics.spec.ts +286 -0
- package/skills/dare-layered-design/tsconfig.json +18 -0
- package/skills/dare-layered-design/types.ts +48 -0
- package/skills/dare-llm-integration/cache/llm_cache.ts +122 -0
- package/skills/dare-llm-integration/index.ts +49 -0
- package/skills/dare-llm-integration/metrics.ts +107 -0
- package/skills/dare-llm-integration/package-lock.json +1855 -0
- package/skills/dare-llm-integration/package.json +49 -0
- package/skills/dare-llm-integration/prompts/prompt_loader.ts +258 -0
- package/skills/dare-llm-integration/providers/anthropic_provider.ts +159 -0
- package/skills/dare-llm-integration/providers/dummy_provider.ts +113 -0
- package/skills/dare-llm-integration/providers/llm_provider.ts +6 -0
- package/skills/dare-llm-integration/providers/openai_provider.ts +215 -0
- package/skills/dare-llm-integration/rate_limit/token_bucket.ts +86 -0
- package/skills/dare-llm-integration/skill.yml +23 -0
- package/skills/dare-llm-integration/tests/fixtures/greet_v1.jinja2 +1 -0
- package/skills/dare-llm-integration/tests/fixtures/summarize_v1.jinja2 +1 -0
- package/skills/dare-llm-integration/tests/fixtures/summarize_v2.jinja2 +3 -0
- package/skills/dare-llm-integration/tests/llm_integration.spec.ts +657 -0
- package/skills/dare-llm-integration/tsconfig.json +23 -0
- package/skills/dare-llm-integration/types.ts +91 -0
- package/skills/dare-llm-integration/validators/output_validator.ts +200 -0
- package/skills/dare-quality-telemetry/collect.ts +134 -0
- package/skills/dare-quality-telemetry/collectors/dare_ax_collector.ts +301 -0
- package/skills/dare-quality-telemetry/collectors/dare_layered_design_collector.ts +406 -0
- package/skills/dare-quality-telemetry/collectors/index.ts +24 -0
- package/skills/dare-quality-telemetry/github_actions_template.ts +25 -0
- package/skills/dare-quality-telemetry/index.ts +18 -0
- package/skills/dare-quality-telemetry/metrics.ts +137 -0
- package/skills/dare-quality-telemetry/package-lock.json +1855 -0
- package/skills/dare-quality-telemetry/package.json +48 -0
- package/skills/dare-quality-telemetry/regression.ts +60 -0
- package/skills/dare-quality-telemetry/reporter.ts +132 -0
- package/skills/dare-quality-telemetry/skill.yml +18 -0
- package/skills/dare-quality-telemetry/tests/quality_telemetry.spec.ts +885 -0
- package/skills/dare-quality-telemetry/tsconfig.json +19 -0
- package/skills/dare-quality-telemetry/types.ts +41 -0
- package/skills/dare-realtime/event_registry.ts +101 -0
- package/skills/dare-realtime/index.ts +30 -0
- package/skills/dare-realtime/metrics.ts +84 -0
- package/skills/dare-realtime/package-lock.json +1855 -0
- package/skills/dare-realtime/package.json +43 -0
- package/skills/dare-realtime/reconnect_strategy.ts +85 -0
- package/skills/dare-realtime/schema_validator.ts +80 -0
- package/skills/dare-realtime/skill.yml +21 -0
- package/skills/dare-realtime/subscription_manager.ts +106 -0
- package/skills/dare-realtime/tests/realtime.spec.ts +482 -0
- package/skills/dare-realtime/tsconfig.json +18 -0
- package/skills/dare-realtime/types.ts +51 -0
- package/templates/ide/antigravity/.agents/skills/dare-ai/SKILL.md +17 -0
- package/templates/ide/antigravity/.agents/skills/dare-blueprint/SKILL.md +2 -0
- package/templates/ide/antigravity/.agents/skills/dare-design/SKILL.md +2 -0
- package/templates/ide/antigravity/.agents/skills/dare-dna/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-migrate/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-patterns/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-refine/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-reverse/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-review/SKILL.md +3 -0
- package/templates/ide/claude/.claude/commands/dare-ai.md +17 -0
- package/templates/ide/claude/.claude/commands/dare-blueprint.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-design.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-dna.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-migrate.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-patterns.md +3 -0
- package/templates/ide/claude/.claude/commands/dare-refine.md +3 -0
- package/templates/ide/claude/.claude/commands/dare-reverse.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-review.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-ai.md +17 -0
- package/templates/ide/cursor/.cursor/commands/dare-blueprint.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-design.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-dna.md +2 -0
- package/templates/ide/cursor/.cursor/commands/dare-migrate.md +2 -0
- package/templates/ide/cursor/.cursor/commands/dare-patterns.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-refine.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-reverse.md +2 -0
- package/templates/ide/cursor/.cursor/commands/dare-review.md +3 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dewtech/dare-frontend-design",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "dare-frontend-design — Frontend architecture patterns skill for DARE Method v3.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"skill.yml"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"test:watch": "vitest",
|
|
22
|
+
"typecheck": "tsc --noEmit"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^20.19.41",
|
|
26
|
+
"typescript": "^5.9.3",
|
|
27
|
+
"vitest": "^1.6.1"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"dare",
|
|
31
|
+
"dare-method",
|
|
32
|
+
"dare-frontend-design",
|
|
33
|
+
"react",
|
|
34
|
+
"vue",
|
|
35
|
+
"linter",
|
|
36
|
+
"scaffold"
|
|
37
|
+
],
|
|
38
|
+
"author": "Dewtech Technologies",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18.0.0"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: dare-frontend-design
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
license: MIT
|
|
4
|
+
description: >
|
|
5
|
+
Skill transversal DARE para arquitetura frontend. Detecta componentes grandes
|
|
6
|
+
(> 300 linhas), chamadas fetch inline em JSX/template, e gera scaffold DARE-compliant
|
|
7
|
+
para projetos React e Vue com error boundaries e loading states.
|
|
8
|
+
author: Dewtech Technologies
|
|
9
|
+
frameworks:
|
|
10
|
+
- react
|
|
11
|
+
- vue
|
|
12
|
+
metrics:
|
|
13
|
+
- id: M-01
|
|
14
|
+
description: 100% de componentes < 300 linhas
|
|
15
|
+
- id: M-02
|
|
16
|
+
description: 0 fetch() direto em JSX/template
|
|
17
|
+
- id: M-03
|
|
18
|
+
description: 100% de páginas com error boundary
|
|
19
|
+
- id: M-04
|
|
20
|
+
description: Bundle config presente para monitorar tamanho
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dare-frontend-design — test suite
|
|
3
|
+
* 35+ tests covering linter, generator, and metrics.
|
|
4
|
+
* License: MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import os from 'os';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
import { FrontendLinter } from '../linter.js';
|
|
14
|
+
import { FrontendGenerator } from '../generator.js';
|
|
15
|
+
import { collectFrontendMetrics } from '../metrics.js';
|
|
16
|
+
|
|
17
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Helpers
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
function createTempDir(): string {
|
|
24
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), 'dare-fe-'));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function removeTempDir(dir: string): void {
|
|
28
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function writeFile(dir: string, relPath: string, content: string): string {
|
|
32
|
+
const fullPath = path.join(dir, relPath);
|
|
33
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
34
|
+
fs.writeFileSync(fullPath, content, 'utf-8');
|
|
35
|
+
return fullPath;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function makeLargeComponent(lineCount: number): string {
|
|
39
|
+
const lines = ['import React from "react";', '', 'export function BigComponent() {', ' return (', ' <div>'];
|
|
40
|
+
for (let i = 0; i < lineCount - 8; i++) {
|
|
41
|
+
lines.push(` <p>Line ${i}</p>`);
|
|
42
|
+
}
|
|
43
|
+
lines.push(' </div>', ' );', '}');
|
|
44
|
+
return lines.join('\n');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// FrontendLinter — component-too-large
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
describe('FrontendLinter — component-too-large', () => {
|
|
52
|
+
let tmpDir: string;
|
|
53
|
+
let linter: FrontendLinter;
|
|
54
|
+
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
tmpDir = createTempDir();
|
|
57
|
+
linter = new FrontendLinter();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
afterEach(() => removeTempDir(tmpDir));
|
|
61
|
+
|
|
62
|
+
it('passes for a small component (< 300 lines)', () => {
|
|
63
|
+
const content = 'import React from "react";\nexport function Small() { return <div/>; }';
|
|
64
|
+
const file = writeFile(tmpDir, 'Small.tsx', content);
|
|
65
|
+
const violations = linter.lintFile(file);
|
|
66
|
+
expect(violations.filter((v) => v.rule === 'component-too-large')).toHaveLength(0);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('flags component with exactly 301 lines', () => {
|
|
70
|
+
const file = writeFile(tmpDir, 'Big.tsx', makeLargeComponent(301));
|
|
71
|
+
const violations = linter.lintFile(file);
|
|
72
|
+
const large = violations.filter((v) => v.rule === 'component-too-large');
|
|
73
|
+
expect(large).toHaveLength(1);
|
|
74
|
+
expect(large[0].severity).toBe('error');
|
|
75
|
+
expect(large[0].message).toContain('301 lines');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('does not flag non-component files (.ts)', () => {
|
|
79
|
+
const file = writeFile(tmpDir, 'utils.ts', makeLargeComponent(500));
|
|
80
|
+
const violations = linter.lintFile(file);
|
|
81
|
+
expect(violations).toHaveLength(0);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('lintDirectory finds violations in nested .tsx files', () => {
|
|
85
|
+
writeFile(tmpDir, 'src/Big.tsx', makeLargeComponent(400));
|
|
86
|
+
writeFile(tmpDir, 'src/Small.tsx', 'export function S() { return null; }');
|
|
87
|
+
const result = linter.lintDirectory(tmpDir);
|
|
88
|
+
expect(result.filesChecked).toBe(2);
|
|
89
|
+
expect(result.violations.some((v) => v.rule === 'component-too-large')).toBe(true);
|
|
90
|
+
expect(result.pass).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('lintDirectory passes when all components are small', () => {
|
|
94
|
+
writeFile(tmpDir, 'A.tsx', 'export function A() { return null; }');
|
|
95
|
+
writeFile(tmpDir, 'B.tsx', 'export function B() { return null; }');
|
|
96
|
+
const result = linter.lintDirectory(tmpDir);
|
|
97
|
+
expect(result.pass).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('lintFiles returns 0 violations for non-component files', () => {
|
|
101
|
+
const file = writeFile(tmpDir, 'utils.ts', makeLargeComponent(500));
|
|
102
|
+
const result = linter.lintFiles([file]);
|
|
103
|
+
expect(result.filesChecked).toBe(0);
|
|
104
|
+
expect(result.violations).toHaveLength(0);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('returns empty for non-existent file', () => {
|
|
108
|
+
const violations = linter.lintFile('/tmp/nonexistent.tsx');
|
|
109
|
+
expect(violations).toHaveLength(0);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
// FrontendLinter — fetch-in-jsx
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
describe('FrontendLinter — fetch-in-jsx', () => {
|
|
118
|
+
let tmpDir: string;
|
|
119
|
+
let linter: FrontendLinter;
|
|
120
|
+
|
|
121
|
+
beforeEach(() => {
|
|
122
|
+
tmpDir = createTempDir();
|
|
123
|
+
linter = new FrontendLinter();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
afterEach(() => removeTempDir(tmpDir));
|
|
127
|
+
|
|
128
|
+
it('flags fetch() in a non-hook .tsx file with JSX context', () => {
|
|
129
|
+
const content = `import React from 'react';
|
|
130
|
+
|
|
131
|
+
export function UserCard() {
|
|
132
|
+
const data = fetch('/api/users').then(r => r.json());
|
|
133
|
+
return <div>{JSON.stringify(data)}</div>;
|
|
134
|
+
}`;
|
|
135
|
+
const file = writeFile(tmpDir, 'UserCard.tsx', content);
|
|
136
|
+
const violations = linter.lintFile(file);
|
|
137
|
+
const fetchViolations = violations.filter((v) => v.rule === 'fetch-in-jsx');
|
|
138
|
+
expect(fetchViolations.length).toBeGreaterThan(0);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('does NOT flag fetch() in a useXxx hook file', () => {
|
|
142
|
+
const content = `export function useUserData(id: string) {
|
|
143
|
+
const data = fetch(\`/api/users/\${id}\`).then(r => r.json());
|
|
144
|
+
return data;
|
|
145
|
+
}`;
|
|
146
|
+
const file = writeFile(tmpDir, 'useUserData.ts', content);
|
|
147
|
+
const violations = linter.lintFile(file);
|
|
148
|
+
const fetchViolations = violations.filter((v) => v.rule === 'fetch-in-jsx');
|
|
149
|
+
expect(fetchViolations).toHaveLength(0);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('does NOT flag axios in a useXxx composable .ts file', () => {
|
|
153
|
+
const content = `export function useApi() {
|
|
154
|
+
return axios.get('/api').then(r => r.data);
|
|
155
|
+
}`;
|
|
156
|
+
const file = writeFile(tmpDir, 'useApi.ts', content);
|
|
157
|
+
const violations = linter.lintFile(file);
|
|
158
|
+
expect(violations.filter((v) => v.rule === 'fetch-in-jsx')).toHaveLength(0);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('flags fetch in Vue <template> section', () => {
|
|
162
|
+
const content = `<template>
|
|
163
|
+
<div>{{ fetch('/api') }}</div>
|
|
164
|
+
</template>
|
|
165
|
+
|
|
166
|
+
<script setup lang="ts">
|
|
167
|
+
</script>`;
|
|
168
|
+
const file = writeFile(tmpDir, 'Bad.vue', content);
|
|
169
|
+
const violations = linter.lintFile(file);
|
|
170
|
+
// template fetch is flagged
|
|
171
|
+
expect(violations.some((v) => v.rule === 'fetch-in-jsx')).toBe(true);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
// FrontendGenerator — React
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
describe('FrontendGenerator — React', () => {
|
|
180
|
+
let tmpDir: string;
|
|
181
|
+
let generator: FrontendGenerator;
|
|
182
|
+
|
|
183
|
+
beforeEach(() => {
|
|
184
|
+
tmpDir = createTempDir();
|
|
185
|
+
generator = new FrontendGenerator();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
afterEach(() => removeTempDir(tmpDir));
|
|
189
|
+
|
|
190
|
+
it('creates React scaffold with all expected directories', () => {
|
|
191
|
+
const result = generator.scaffold({ framework: 'react', outputDir: tmpDir });
|
|
192
|
+
const expectedDirs = ['components', 'hooks', 'pages', 'store', 'api'];
|
|
193
|
+
for (const dir of expectedDirs) {
|
|
194
|
+
expect(result.dirsCreated.some((d) => d.includes(dir))).toBe(true);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('creates ErrorBoundary.tsx', () => {
|
|
199
|
+
const result = generator.scaffold({ framework: 'react', outputDir: tmpDir });
|
|
200
|
+
expect(result.filesCreated.some((f) => f.includes('ErrorBoundary.tsx'))).toBe(true);
|
|
201
|
+
const content = fs.readFileSync(path.join(tmpDir, 'src/components/ErrorBoundary.tsx'), 'utf-8');
|
|
202
|
+
expect(content).toContain('ErrorBoundary');
|
|
203
|
+
expect(content).toContain('hasError');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('creates useFetch.ts hook', () => {
|
|
207
|
+
generator.scaffold({ framework: 'react', outputDir: tmpDir });
|
|
208
|
+
const content = fs.readFileSync(path.join(tmpDir, 'src/hooks/useFetch.ts'), 'utf-8');
|
|
209
|
+
expect(content).toContain('useFetch');
|
|
210
|
+
expect(content).toContain("status: 'loading'");
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('creates NotFoundPage.tsx with ErrorBoundary import', () => {
|
|
214
|
+
generator.scaffold({ framework: 'react', outputDir: tmpDir });
|
|
215
|
+
const content = fs.readFileSync(path.join(tmpDir, 'src/pages/NotFoundPage.tsx'), 'utf-8');
|
|
216
|
+
expect(content).toContain('ErrorBoundary');
|
|
217
|
+
expect(content).toContain('404');
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('creates API client with auth headers', () => {
|
|
221
|
+
generator.scaffold({ framework: 'react', outputDir: tmpDir });
|
|
222
|
+
const content = fs.readFileSync(path.join(tmpDir, 'src/api/client.ts'), 'utf-8');
|
|
223
|
+
expect(content).toContain('apiGet');
|
|
224
|
+
expect(content).toContain('Authorization');
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('creates App.tsx with ErrorBoundary', () => {
|
|
228
|
+
generator.scaffold({ framework: 'react', outputDir: tmpDir, projectName: 'MyApp' });
|
|
229
|
+
const content = fs.readFileSync(path.join(tmpDir, 'src/App.tsx'), 'utf-8');
|
|
230
|
+
expect(content).toContain('ErrorBoundary');
|
|
231
|
+
expect(content).toContain('MyApp');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('returns framework in result', () => {
|
|
235
|
+
const result = generator.scaffold({ framework: 'react', outputDir: tmpDir });
|
|
236
|
+
expect(result.framework).toBe('react');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('creates LoadingSpinner.tsx', () => {
|
|
240
|
+
generator.scaffold({ framework: 'react', outputDir: tmpDir });
|
|
241
|
+
const content = fs.readFileSync(path.join(tmpDir, 'src/components/LoadingSpinner.tsx'), 'utf-8');
|
|
242
|
+
expect(content).toContain('LoadingSpinner');
|
|
243
|
+
expect(content).toContain('aria-label');
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
// FrontendGenerator — Vue
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
|
|
251
|
+
describe('FrontendGenerator — Vue', () => {
|
|
252
|
+
let tmpDir: string;
|
|
253
|
+
let generator: FrontendGenerator;
|
|
254
|
+
|
|
255
|
+
beforeEach(() => {
|
|
256
|
+
tmpDir = createTempDir();
|
|
257
|
+
generator = new FrontendGenerator();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
afterEach(() => removeTempDir(tmpDir));
|
|
261
|
+
|
|
262
|
+
it('creates Vue scaffold with all expected directories', () => {
|
|
263
|
+
const result = generator.scaffold({ framework: 'vue', outputDir: tmpDir });
|
|
264
|
+
const expectedDirs = ['components', 'composables', 'pages', 'stores', 'api'];
|
|
265
|
+
for (const dir of expectedDirs) {
|
|
266
|
+
expect(result.dirsCreated.some((d) => d.includes(dir))).toBe(true);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('creates ErrorBoundary.vue with onErrorCaptured', () => {
|
|
271
|
+
generator.scaffold({ framework: 'vue', outputDir: tmpDir });
|
|
272
|
+
const content = fs.readFileSync(path.join(tmpDir, 'src/components/ErrorBoundary.vue'), 'utf-8');
|
|
273
|
+
expect(content).toContain('onErrorCaptured');
|
|
274
|
+
expect(content).toContain('hasError');
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('creates useFetch.ts composable', () => {
|
|
278
|
+
generator.scaffold({ framework: 'vue', outputDir: tmpDir });
|
|
279
|
+
const content = fs.readFileSync(path.join(tmpDir, 'src/composables/useFetch.ts'), 'utf-8');
|
|
280
|
+
expect(content).toContain('useFetch');
|
|
281
|
+
expect(content).toContain('watchEffect');
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('creates NotFoundPage.vue with ErrorBoundary', () => {
|
|
285
|
+
generator.scaffold({ framework: 'vue', outputDir: tmpDir });
|
|
286
|
+
const content = fs.readFileSync(path.join(tmpDir, 'src/pages/NotFoundPage.vue'), 'utf-8');
|
|
287
|
+
expect(content).toContain('ErrorBoundary');
|
|
288
|
+
expect(content).toContain('404');
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('creates Pinia store', () => {
|
|
292
|
+
generator.scaffold({ framework: 'vue', outputDir: tmpDir, projectName: 'MyVueApp' });
|
|
293
|
+
const content = fs.readFileSync(path.join(tmpDir, 'src/stores/app.ts'), 'utf-8');
|
|
294
|
+
expect(content).toContain('defineStore');
|
|
295
|
+
expect(content).toContain('MyVueApp');
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('throws for unknown framework', () => {
|
|
299
|
+
expect(() =>
|
|
300
|
+
generator.scaffold({ framework: 'svelte' as never, outputDir: tmpDir })
|
|
301
|
+
).toThrow('unknown framework');
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// ---------------------------------------------------------------------------
|
|
306
|
+
// Metrics
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
|
|
309
|
+
describe('collectFrontendMetrics', () => {
|
|
310
|
+
it('M-01 passes when no large components', () => {
|
|
311
|
+
const results = collectFrontendMetrics({
|
|
312
|
+
largeComponentCount: 0,
|
|
313
|
+
totalComponentsChecked: 10,
|
|
314
|
+
inlineFetchCount: 0,
|
|
315
|
+
pagesWithErrorBoundary: 3,
|
|
316
|
+
totalPages: 3,
|
|
317
|
+
bundleConfigExists: true,
|
|
318
|
+
});
|
|
319
|
+
expect(results.find((r) => r.id === 'M-01')!.pass).toBe(true);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('M-01 fails when large components exist', () => {
|
|
323
|
+
const results = collectFrontendMetrics({
|
|
324
|
+
largeComponentCount: 2,
|
|
325
|
+
totalComponentsChecked: 10,
|
|
326
|
+
inlineFetchCount: 0,
|
|
327
|
+
pagesWithErrorBoundary: 3,
|
|
328
|
+
totalPages: 3,
|
|
329
|
+
bundleConfigExists: true,
|
|
330
|
+
});
|
|
331
|
+
const m01 = results.find((r) => r.id === 'M-01')!;
|
|
332
|
+
expect(m01.pass).toBe(false);
|
|
333
|
+
expect(m01.detail).toContain('2/10');
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('M-02 passes with 0 inline fetch', () => {
|
|
337
|
+
const results = collectFrontendMetrics({
|
|
338
|
+
largeComponentCount: 0,
|
|
339
|
+
totalComponentsChecked: 5,
|
|
340
|
+
inlineFetchCount: 0,
|
|
341
|
+
pagesWithErrorBoundary: 2,
|
|
342
|
+
totalPages: 2,
|
|
343
|
+
bundleConfigExists: true,
|
|
344
|
+
});
|
|
345
|
+
expect(results.find((r) => r.id === 'M-02')!.pass).toBe(true);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('M-02 fails with inline fetch', () => {
|
|
349
|
+
const results = collectFrontendMetrics({
|
|
350
|
+
largeComponentCount: 0,
|
|
351
|
+
totalComponentsChecked: 5,
|
|
352
|
+
inlineFetchCount: 3,
|
|
353
|
+
pagesWithErrorBoundary: 2,
|
|
354
|
+
totalPages: 2,
|
|
355
|
+
bundleConfigExists: true,
|
|
356
|
+
});
|
|
357
|
+
const m02 = results.find((r) => r.id === 'M-02')!;
|
|
358
|
+
expect(m02.pass).toBe(false);
|
|
359
|
+
expect(m02.detail).toContain('3 inline');
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('M-03 passes when all pages have error boundaries', () => {
|
|
363
|
+
const results = collectFrontendMetrics({
|
|
364
|
+
largeComponentCount: 0,
|
|
365
|
+
totalComponentsChecked: 5,
|
|
366
|
+
inlineFetchCount: 0,
|
|
367
|
+
pagesWithErrorBoundary: 4,
|
|
368
|
+
totalPages: 4,
|
|
369
|
+
bundleConfigExists: true,
|
|
370
|
+
});
|
|
371
|
+
expect(results.find((r) => r.id === 'M-03')!.pass).toBe(true);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('M-03 fails when some pages missing error boundary', () => {
|
|
375
|
+
const results = collectFrontendMetrics({
|
|
376
|
+
largeComponentCount: 0,
|
|
377
|
+
totalComponentsChecked: 5,
|
|
378
|
+
inlineFetchCount: 0,
|
|
379
|
+
pagesWithErrorBoundary: 2,
|
|
380
|
+
totalPages: 4,
|
|
381
|
+
bundleConfigExists: true,
|
|
382
|
+
});
|
|
383
|
+
expect(results.find((r) => r.id === 'M-03')!.pass).toBe(false);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('M-04 passes when bundle config exists', () => {
|
|
387
|
+
const results = collectFrontendMetrics({
|
|
388
|
+
largeComponentCount: 0,
|
|
389
|
+
totalComponentsChecked: 3,
|
|
390
|
+
inlineFetchCount: 0,
|
|
391
|
+
pagesWithErrorBoundary: 1,
|
|
392
|
+
totalPages: 1,
|
|
393
|
+
bundleConfigExists: true,
|
|
394
|
+
});
|
|
395
|
+
expect(results.find((r) => r.id === 'M-04')!.pass).toBe(true);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it('M-04 fails when no bundle config', () => {
|
|
399
|
+
const results = collectFrontendMetrics({
|
|
400
|
+
largeComponentCount: 0,
|
|
401
|
+
totalComponentsChecked: 3,
|
|
402
|
+
inlineFetchCount: 0,
|
|
403
|
+
pagesWithErrorBoundary: 1,
|
|
404
|
+
totalPages: 1,
|
|
405
|
+
bundleConfigExists: false,
|
|
406
|
+
});
|
|
407
|
+
expect(results.find((r) => r.id === 'M-04')!.pass).toBe(false);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('returns exactly 4 metrics', () => {
|
|
411
|
+
const results = collectFrontendMetrics({
|
|
412
|
+
largeComponentCount: 0,
|
|
413
|
+
totalComponentsChecked: 0,
|
|
414
|
+
inlineFetchCount: 0,
|
|
415
|
+
pagesWithErrorBoundary: 0,
|
|
416
|
+
totalPages: 0,
|
|
417
|
+
bundleConfigExists: true,
|
|
418
|
+
});
|
|
419
|
+
expect(results).toHaveLength(4);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('all pass with ideal config', () => {
|
|
423
|
+
const results = collectFrontendMetrics({
|
|
424
|
+
largeComponentCount: 0,
|
|
425
|
+
totalComponentsChecked: 10,
|
|
426
|
+
inlineFetchCount: 0,
|
|
427
|
+
pagesWithErrorBoundary: 5,
|
|
428
|
+
totalPages: 5,
|
|
429
|
+
bundleConfigExists: true,
|
|
430
|
+
});
|
|
431
|
+
for (const r of results) {
|
|
432
|
+
expect(r.pass).toBe(true);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./dist",
|
|
5
|
+
"rootDir": ".",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"sourceMap": true
|
|
9
|
+
},
|
|
10
|
+
"include": [
|
|
11
|
+
"*.ts",
|
|
12
|
+
"tests/**/*.ts"
|
|
13
|
+
],
|
|
14
|
+
"exclude": [
|
|
15
|
+
"node_modules",
|
|
16
|
+
"dist"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dare-frontend-design — shared types
|
|
3
|
+
* License: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type Framework = 'react' | 'vue';
|
|
7
|
+
|
|
8
|
+
export interface LinterViolation {
|
|
9
|
+
file: string;
|
|
10
|
+
line: number;
|
|
11
|
+
rule: LinterRule;
|
|
12
|
+
message: string;
|
|
13
|
+
severity: 'error' | 'warning';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type LinterRule =
|
|
17
|
+
| 'component-too-large' // Component > 300 lines
|
|
18
|
+
| 'fetch-in-jsx'; // fetch() or axios. in JSX outside hooks/composables
|
|
19
|
+
|
|
20
|
+
export interface LinterResult {
|
|
21
|
+
violations: LinterViolation[];
|
|
22
|
+
filesChecked: number;
|
|
23
|
+
pass: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ScaffoldOptions {
|
|
27
|
+
framework: Framework;
|
|
28
|
+
/** Project root directory */
|
|
29
|
+
outputDir: string;
|
|
30
|
+
/** Project name (used in templates) */
|
|
31
|
+
projectName?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ScaffoldResult {
|
|
35
|
+
/** All files created */
|
|
36
|
+
filesCreated: string[];
|
|
37
|
+
/** Directories created */
|
|
38
|
+
dirsCreated: string[];
|
|
39
|
+
framework: Framework;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface MetricResult {
|
|
43
|
+
id: string;
|
|
44
|
+
pass: boolean;
|
|
45
|
+
description: string;
|
|
46
|
+
detail?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface FrontendMetricsInput {
|
|
50
|
+
/** M-01: number of components > 300 lines */
|
|
51
|
+
largeComponentCount: number;
|
|
52
|
+
/** M-01: total components checked */
|
|
53
|
+
totalComponentsChecked: number;
|
|
54
|
+
/** M-02: number of fetch() calls found inline in JSX/template */
|
|
55
|
+
inlineFetchCount: number;
|
|
56
|
+
/** M-03: number of pages with error boundaries */
|
|
57
|
+
pagesWithErrorBoundary: number;
|
|
58
|
+
/** M-03: total pages */
|
|
59
|
+
totalPages: number;
|
|
60
|
+
/** M-04: bundle config file exists */
|
|
61
|
+
bundleConfigExists: boolean;
|
|
62
|
+
}
|