@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,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dare-frontend-design — FrontendLinter
|
|
3
|
+
* Detects DARE frontend antipatterns in .tsx and .vue files.
|
|
4
|
+
*
|
|
5
|
+
* Rules:
|
|
6
|
+
* component-too-large — component file > 300 lines
|
|
7
|
+
* fetch-in-jsx — fetch() or axios. used directly in JSX/template
|
|
8
|
+
* (outside custom hooks/composables)
|
|
9
|
+
*
|
|
10
|
+
* License: MIT
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from 'fs';
|
|
14
|
+
import path from 'path';
|
|
15
|
+
import type { LinterViolation, LinterResult } from './types.js';
|
|
16
|
+
|
|
17
|
+
const COMPONENT_MAX_LINES = 300;
|
|
18
|
+
|
|
19
|
+
/** File extensions treated as frontend component files */
|
|
20
|
+
const COMPONENT_EXTENSIONS = new Set(['.tsx', '.vue']);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Patterns that indicate a fetch/axios call is inside a hook or composable
|
|
24
|
+
* (e.g. the function name starts with "use").
|
|
25
|
+
* We detect this via function scope heuristic.
|
|
26
|
+
*/
|
|
27
|
+
const HOOK_PATTERN = /function\s+use[A-Z]/;
|
|
28
|
+
const COMPOSABLE_PATTERN = /(?:export\s+(?:default\s+)?)?(?:const|function)\s+use[A-Z]/;
|
|
29
|
+
|
|
30
|
+
export class FrontendLinter {
|
|
31
|
+
/**
|
|
32
|
+
* Lint a single file and return any violations found.
|
|
33
|
+
*/
|
|
34
|
+
lintFile(filePath: string): LinterViolation[] {
|
|
35
|
+
if (!fs.existsSync(filePath)) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
40
|
+
const lines = content.split('\n');
|
|
41
|
+
const violations: LinterViolation[] = [];
|
|
42
|
+
|
|
43
|
+
const ext = path.extname(filePath);
|
|
44
|
+
if (!COMPONENT_EXTENSIONS.has(ext)) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Rule 1: component-too-large
|
|
49
|
+
if (lines.length > COMPONENT_MAX_LINES) {
|
|
50
|
+
violations.push({
|
|
51
|
+
file: filePath,
|
|
52
|
+
line: 1,
|
|
53
|
+
rule: 'component-too-large',
|
|
54
|
+
message: `Component has ${lines.length} lines (max ${COMPONENT_MAX_LINES}). Split into smaller components.`,
|
|
55
|
+
severity: 'error',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Rule 2: fetch-in-jsx — detect fetch() or axios. outside hooks/composables
|
|
60
|
+
const fetchViolations = detectInlineFetch(filePath, lines);
|
|
61
|
+
violations.push(...fetchViolations);
|
|
62
|
+
|
|
63
|
+
return violations;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Lint all .tsx and .vue files under a directory (recursively).
|
|
68
|
+
*/
|
|
69
|
+
lintDirectory(dirPath: string): LinterResult {
|
|
70
|
+
if (!fs.existsSync(dirPath)) {
|
|
71
|
+
return { violations: [], filesChecked: 0, pass: true };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const files = collectComponentFiles(dirPath);
|
|
75
|
+
const violations: LinterViolation[] = [];
|
|
76
|
+
|
|
77
|
+
for (const file of files) {
|
|
78
|
+
violations.push(...this.lintFile(file));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
violations,
|
|
83
|
+
filesChecked: files.length,
|
|
84
|
+
pass: violations.filter((v) => v.severity === 'error').length === 0,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Lint a list of specific files.
|
|
90
|
+
*/
|
|
91
|
+
lintFiles(filePaths: string[]): LinterResult {
|
|
92
|
+
const violations: LinterViolation[] = [];
|
|
93
|
+
let filesChecked = 0;
|
|
94
|
+
|
|
95
|
+
for (const filePath of filePaths) {
|
|
96
|
+
const ext = path.extname(filePath);
|
|
97
|
+
if (!COMPONENT_EXTENSIONS.has(ext)) continue;
|
|
98
|
+
filesChecked++;
|
|
99
|
+
violations.push(...this.lintFile(filePath));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
violations,
|
|
104
|
+
filesChecked,
|
|
105
|
+
pass: violations.filter((v) => v.severity === 'error').length === 0,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Collect all .tsx and .vue files in a directory tree.
|
|
112
|
+
*/
|
|
113
|
+
function collectComponentFiles(dirPath: string): string[] {
|
|
114
|
+
const results: string[] = [];
|
|
115
|
+
|
|
116
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '.git') continue;
|
|
119
|
+
|
|
120
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
121
|
+
if (entry.isDirectory()) {
|
|
122
|
+
results.push(...collectComponentFiles(fullPath));
|
|
123
|
+
} else if (COMPONENT_EXTENSIONS.has(path.extname(entry.name))) {
|
|
124
|
+
results.push(fullPath);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return results;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Detect fetch() or axios. calls that appear directly in JSX/template context
|
|
133
|
+
* (i.e., NOT inside a function named useXxx).
|
|
134
|
+
*/
|
|
135
|
+
function detectInlineFetch(filePath: string, lines: string[]): LinterViolation[] {
|
|
136
|
+
const violations: LinterViolation[] = [];
|
|
137
|
+
const ext = path.extname(filePath);
|
|
138
|
+
|
|
139
|
+
// Inline fetch pattern: fetch( or axios. appearing in JSX/return context
|
|
140
|
+
const FETCH_REGEX = /\bfetch\s*\(|axios\./;
|
|
141
|
+
|
|
142
|
+
// Track scope: are we inside a hook/composable function?
|
|
143
|
+
// Simple heuristic: scan for useXxx function declarations above each match.
|
|
144
|
+
const content = lines.join('\n');
|
|
145
|
+
const inHookOrComposable = HOOK_PATTERN.test(content) || COMPOSABLE_PATTERN.test(content);
|
|
146
|
+
|
|
147
|
+
// For Vue files, the <script setup> with composable is considered safe if
|
|
148
|
+
// the file itself is a composable (useXxx naming).
|
|
149
|
+
const filename = path.basename(filePath, path.extname(filePath));
|
|
150
|
+
const isHookFile = /^use[A-Z]/.test(filename);
|
|
151
|
+
|
|
152
|
+
if (inHookOrComposable || isHookFile) {
|
|
153
|
+
// The file itself is a hook/composable — fetch calls are expected
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Scan each line for direct fetch/axios usage outside of function blocks
|
|
158
|
+
for (let i = 0; i < lines.length; i++) {
|
|
159
|
+
const line = lines[i];
|
|
160
|
+
|
|
161
|
+
// Skip comments
|
|
162
|
+
if (line.trim().startsWith('//') || line.trim().startsWith('*')) continue;
|
|
163
|
+
|
|
164
|
+
if (FETCH_REGEX.test(line)) {
|
|
165
|
+
// Check if this line is inside a local function named useXxx
|
|
166
|
+
// by scanning backwards for the enclosing function signature
|
|
167
|
+
const context = lines.slice(Math.max(0, i - 20), i + 1).join('\n');
|
|
168
|
+
const inLocalHook = HOOK_PATTERN.test(context) || COMPOSABLE_PATTERN.test(context);
|
|
169
|
+
|
|
170
|
+
if (!inLocalHook) {
|
|
171
|
+
// Additional check: is this in a .tsx return block or template?
|
|
172
|
+
const isInReturn = isInJSXOrTemplate(lines, i, ext);
|
|
173
|
+
if (isInReturn) {
|
|
174
|
+
violations.push({
|
|
175
|
+
file: filePath,
|
|
176
|
+
line: i + 1,
|
|
177
|
+
rule: 'fetch-in-jsx',
|
|
178
|
+
message: `Direct fetch() or axios. call found outside custom hook/composable at line ${i + 1}. Move to useXxx hook or composable.`,
|
|
179
|
+
severity: 'error',
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return violations;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Heuristic: is line i inside a JSX/template context (not in a hook)?
|
|
191
|
+
* We check if:
|
|
192
|
+
* - For .tsx: the line is inside a return() block of a component function (not a useXxx)
|
|
193
|
+
* - For .vue: the line is in <template> or <script setup> (non-composable)
|
|
194
|
+
*/
|
|
195
|
+
function isInJSXOrTemplate(lines: string[], lineIdx: number, ext: string): boolean {
|
|
196
|
+
const line = lines[lineIdx];
|
|
197
|
+
|
|
198
|
+
if (ext === '.vue') {
|
|
199
|
+
// Check if we are in <template> section
|
|
200
|
+
let inTemplate = false;
|
|
201
|
+
for (let i = 0; i <= lineIdx; i++) {
|
|
202
|
+
const l = lines[i].trim();
|
|
203
|
+
if (l === '<template>' || l.startsWith('<template')) inTemplate = true;
|
|
204
|
+
if (l === '</template>') inTemplate = false;
|
|
205
|
+
}
|
|
206
|
+
if (inTemplate) return true;
|
|
207
|
+
|
|
208
|
+
// In <script setup> directly (not in composable)
|
|
209
|
+
let inScript = false;
|
|
210
|
+
for (let i = 0; i <= lineIdx; i++) {
|
|
211
|
+
const l = lines[i].trim();
|
|
212
|
+
if (l.startsWith('<script')) inScript = true;
|
|
213
|
+
if (l === '</script>') inScript = false;
|
|
214
|
+
}
|
|
215
|
+
return inScript;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// .tsx: check if the fetch is in any non-hook function
|
|
219
|
+
// Simple: if any nearby context has JSX (<, />, return (
|
|
220
|
+
const context = lines.slice(Math.max(0, lineIdx - 5), lineIdx + 5).join('\n');
|
|
221
|
+
const hasJSX = /<[A-Z]|<\/|jsx|return\s*\(/.test(context);
|
|
222
|
+
|
|
223
|
+
// Also flag if fetch appears as a direct statement (not in arrow fn named useXxx)
|
|
224
|
+
const isDirectStatement = /^\s*(const|let|var)?\s*(await\s+)?fetch\s*\(|^\s*(const|let|var)\s+\w+\s*=\s*(await\s+)?fetch\s*\(/.test(line);
|
|
225
|
+
|
|
226
|
+
return hasJSX || isDirectStatement;
|
|
227
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dare-frontend-design — Metrics collector
|
|
3
|
+
* Evaluates M-01 to M-04 for the dare-frontend-design skill.
|
|
4
|
+
* License: MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { MetricResult, FrontendMetricsInput } from './types.js';
|
|
8
|
+
|
|
9
|
+
export function collectFrontendMetrics(input: FrontendMetricsInput): MetricResult[] {
|
|
10
|
+
return [
|
|
11
|
+
checkM01(input),
|
|
12
|
+
checkM02(input),
|
|
13
|
+
checkM03(input),
|
|
14
|
+
checkM04(input),
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* M-01: 0 components > 300 lines.
|
|
20
|
+
*/
|
|
21
|
+
function checkM01(input: FrontendMetricsInput): MetricResult {
|
|
22
|
+
const pass = input.largeComponentCount === 0;
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
id: 'M-01',
|
|
26
|
+
pass,
|
|
27
|
+
description: '100% of components < 300 lines',
|
|
28
|
+
detail: pass
|
|
29
|
+
? `All ${input.totalComponentsChecked} component(s) within size limit`
|
|
30
|
+
: `${input.largeComponentCount}/${input.totalComponentsChecked} component(s) exceed 300 lines — split into smaller components`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* M-02: 0 fetch() calls inline in JSX/template.
|
|
36
|
+
*/
|
|
37
|
+
function checkM02(input: FrontendMetricsInput): MetricResult {
|
|
38
|
+
const pass = input.inlineFetchCount === 0;
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
id: 'M-02',
|
|
42
|
+
pass,
|
|
43
|
+
description: '0 fetch() calls inline in JSX/template',
|
|
44
|
+
detail: pass
|
|
45
|
+
? 'All API calls are in custom hooks/composables'
|
|
46
|
+
: `${input.inlineFetchCount} inline fetch() call(s) detected — move to useXxx hook or composable`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* M-03: 100% of pages have error boundaries.
|
|
52
|
+
*/
|
|
53
|
+
function checkM03(input: FrontendMetricsInput): MetricResult {
|
|
54
|
+
const pass = input.totalPages === 0 || input.pagesWithErrorBoundary === input.totalPages;
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
id: 'M-03',
|
|
58
|
+
pass,
|
|
59
|
+
description: '100% of pages have error boundary',
|
|
60
|
+
detail: input.totalPages === 0
|
|
61
|
+
? 'No pages found'
|
|
62
|
+
: pass
|
|
63
|
+
? `All ${input.totalPages} page(s) have error boundaries`
|
|
64
|
+
: `${input.totalPages - input.pagesWithErrorBoundary} page(s) missing error boundary`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* M-04: Bundle config (vite.config.ts / webpack.config.js / etc.) exists.
|
|
70
|
+
*/
|
|
71
|
+
function checkM04(input: FrontendMetricsInput): MetricResult {
|
|
72
|
+
const pass = input.bundleConfigExists;
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
id: 'M-04',
|
|
76
|
+
pass,
|
|
77
|
+
description: 'Bundle config exists (for size monitoring)',
|
|
78
|
+
detail: pass
|
|
79
|
+
? 'Bundle configuration found'
|
|
80
|
+
: 'No bundle config detected — add vite.config.ts or webpack.config.js to monitor bundle size',
|
|
81
|
+
};
|
|
82
|
+
}
|