@afokapu/atdd-bun 0.1.4 → 0.3.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 +51 -1
- package/conventions/coder.bun/coder.bun.design-connected.convention.yaml +34 -0
- package/conventions/coder.bun/coder.bun.design-dependency-flow.convention.yaml +37 -0
- package/conventions/coder.bun/coder.bun.design-orphan-export.convention.yaml +36 -0
- package/conventions/coder.bun/coder.bun.design-primitives.convention.yaml +35 -0
- package/conventions/coder.bun/coder.bun.design-token-color.convention.yaml +38 -0
- package/conventions/coder.bun/coder.bun.design-token-hardcoded.convention.yaml +40 -0
- package/conventions/coder.bun/coder.bun.design-tokens-pure.convention.yaml +35 -0
- package/conventions/coder.bun/coder.bun.design-wagons-import.convention.yaml +35 -0
- package/detectors/bun_design_system_detector/atdd.implementation.yaml +28 -0
- package/detectors/bun_design_system_detector/checks/_design.mjs +112 -0
- package/detectors/bun_design_system_detector/checks/_map.json +10 -0
- package/detectors/bun_design_system_detector/checks/design_connected.mjs +19 -0
- package/detectors/bun_design_system_detector/checks/design_dependency_flow.mjs +20 -0
- package/detectors/bun_design_system_detector/checks/design_orphan_export.mjs +33 -0
- package/detectors/bun_design_system_detector/checks/design_primitives.mjs +16 -0
- package/detectors/bun_design_system_detector/checks/design_token_color.mjs +22 -0
- package/detectors/bun_design_system_detector/checks/design_token_hardcoded.mjs +29 -0
- package/detectors/bun_design_system_detector/checks/design_tokens_pure.mjs +23 -0
- package/detectors/bun_design_system_detector/checks/design_wagons_import.mjs +18 -0
- package/detectors/bun_design_system_detector/detect.mjs +46 -0
- package/detectors/bun_design_system_detector/fixtures/clean/app/public/index.html +6 -0
- package/detectors/bun_design_system_detector/fixtures/clean/app/public/site.css +9 -0
- package/detectors/bun_design_system_detector/fixtures/clean/app/src/design/components/Card.tsx +6 -0
- package/detectors/bun_design_system_detector/fixtures/clean/app/src/design/primitives/Button.tsx +6 -0
- package/detectors/bun_design_system_detector/fixtures/clean/app/src/design/primitives/Text.tsx +5 -0
- package/detectors/bun_design_system_detector/fixtures/clean/app/src/design/primitives/index.ts +2 -0
- package/detectors/bun_design_system_detector/fixtures/clean/app/src/design/templates/Page.tsx +5 -0
- package/detectors/bun_design_system_detector/fixtures/clean/app/src/design/tokens/colors.ts +1 -0
- package/detectors/bun_design_system_detector/fixtures/clean/app/src/design/tokens/spacing.ts +1 -0
- package/detectors/bun_design_system_detector/fixtures/clean/app/src/design/tokens/theme.css +8 -0
- package/detectors/bun_design_system_detector/fixtures/clean/app/src/wagons/orders/presentation/orders-view.tsx +6 -0
- package/detectors/bun_design_system_detector/fixtures/clean/app/src/wagons/orders/presentation/orders.css +8 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/connected/src/design/tokens/colors.ts +1 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/connected/src/views/plain.tsx +3 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/dependency_flow/src/design/components/Card.tsx +3 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/dependency_flow/src/design/primitives/Badge.tsx +5 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/orphan_export/src/design/primitives/Unused.tsx +3 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/primitives/src/design/primitives/Button.tsx +3 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/primitives/src/views/form.tsx +5 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/token_color/src/app.css +4 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/token_color/src/design/tokens/colors.css +1 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/token_color/src/views/alert.tsx +3 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/token_hardcoded/public/list.html +1 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/token_hardcoded/src/design/tokens/spacing.css +1 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/token_hardcoded/src/views/list.tsx +3 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/tokens_pure/src/design/tokens/swatch.tsx +9 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/wagons_import/src/design/components/OrderBadge.tsx +5 -0
- package/detectors/bun_design_system_detector/fixtures/dirty/wagons_import/src/wagons/orders/order-status.ts +1 -0
- package/integrity.json +933 -0
- package/package.json +3 -2
- package/src/cli.ts +9 -0
- package/src/enforce.ts +3 -2
- package/src/index.ts +2 -0
- package/src/integrity.ts +165 -0
- package/src/register.ts +9 -0
- package/src/setup.ts +7 -2
- package/templates/agents/AGENTS.block.md +1 -1
- package/templates/agents/atdd/SKILL.md +3 -1
- package/templates/agents/atdd-bun.integrity.test.ts +5 -0
- package/templates/github/atdd-bun.yml +1 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// Detector: coder.bun.design-token-color
|
|
3
|
+
// Outside the tokens layer, UI files (.tsx/.jsx/.html/.css) take colors from tokens
|
|
4
|
+
// (`var(--…)`), never raw hex, rgb(), or hsl() literals. Defining a custom
|
|
5
|
+
// property (`--danger: #b91c1c`) is defining a token, not using a raw value.
|
|
6
|
+
import { readRoots, readExcludes, readText, emit, locate } from "../../../lib/scan.mjs";
|
|
7
|
+
import { scope, designOf, isUi, declarations } from "./_design.mjs";
|
|
8
|
+
|
|
9
|
+
const RULE_ID = "coder.bun.design-token-color";
|
|
10
|
+
const COLOR_RE = /#[0-9a-fA-F]{3,8}\b|\b(?:rgba?|hsla?)\s*\(/;
|
|
11
|
+
const violations = [];
|
|
12
|
+
for (const file of scope(readRoots(), readExcludes())) {
|
|
13
|
+
if (!isUi(file) || designOf(file)?.layer === 0) continue;
|
|
14
|
+
const text = readText(file);
|
|
15
|
+
for (const d of declarations(file, text ?? "")) {
|
|
16
|
+
if (d.prop.startsWith("--")) continue;
|
|
17
|
+
const hit = d.value.match(COLOR_RE);
|
|
18
|
+
if (hit) violations.push({ rule_id: RULE_ID, file, ...locate(text, d.index), evidence: `${d.prop} uses the raw color ${hit[0].replace(/\s*\($/, "()")}; reference a color token (var(--…)) instead` });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
process.stderr.write(`bun-detector[design-token-color]: ${violations.length} violation(s)\n`);
|
|
22
|
+
emit(violations);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// Detector: coder.bun.design-token-hardcoded
|
|
3
|
+
// Outside the tokens layer, spacing, radii, and durations come from foundations.
|
|
4
|
+
// 0 and 1px (hairline borders) are not design decisions and are allowed. Custom
|
|
5
|
+
// property definitions (`--space-3: 12px`) define tokens and are never judged.
|
|
6
|
+
import { readRoots, readExcludes, readText, emit, locate } from "../../../lib/scan.mjs";
|
|
7
|
+
import { scope, designOf, isUi, declarations } from "./_design.mjs";
|
|
8
|
+
|
|
9
|
+
const RULE_ID = "coder.bun.design-token-hardcoded";
|
|
10
|
+
const LENGTH_PROP = /^(margin|padding|gap|row-gap|column-gap|inset|top|right|bottom|left|border-radius)(-|$)/;
|
|
11
|
+
const DURATION_PROP = /^(transition|animation)(-|$)/;
|
|
12
|
+
const violations = [];
|
|
13
|
+
const report = (file, text, d, value) => violations.push({ rule_id: RULE_ID, file, ...locate(text, d.index), evidence: `${d.prop}: ${value} is hardcoded; use a foundations token (var(--…))` });
|
|
14
|
+
for (const file of scope(readRoots(), readExcludes())) {
|
|
15
|
+
if (!isUi(file) || designOf(file)?.layer === 0) continue;
|
|
16
|
+
const text = readText(file);
|
|
17
|
+
for (const d of declarations(file, text ?? "")) {
|
|
18
|
+
if (LENGTH_PROP.test(d.prop)) {
|
|
19
|
+
if (d.numeric) { if (Math.abs(Number(d.value)) > 1) report(file, text, d, d.value); continue; }
|
|
20
|
+
const px = [...d.value.matchAll(/(-?\d*\.?\d+)px\b/g)].find((m) => Math.abs(Number(m[1])) > 1);
|
|
21
|
+
if (px) report(file, text, d, px[0]);
|
|
22
|
+
} else if (DURATION_PROP.test(d.prop)) {
|
|
23
|
+
const time = [...d.value.matchAll(/(\d*\.?\d+)(ms|s)\b/g)].find((m) => Number(m[1]) > 0);
|
|
24
|
+
if (time) report(file, text, d, time[0]);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
process.stderr.write(`bun-detector[design-token-hardcoded]: ${violations.length} violation(s)\n`);
|
|
29
|
+
emit(violations);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// Detector: coder.bun.design-tokens-pure
|
|
3
|
+
// Files in the tokens/foundations layer hold values only: no JSX runtime import,
|
|
4
|
+
// no rendered markup, no control flow.
|
|
5
|
+
import { readRoots, readExcludes, readText, emit, locate } from "../../../lib/scan.mjs";
|
|
6
|
+
import { extname } from "node:path";
|
|
7
|
+
import { scope, designOf, importsOf, masked, CODE_EXT } from "./_design.mjs";
|
|
8
|
+
|
|
9
|
+
const RULE_ID = "coder.bun.design-tokens-pure";
|
|
10
|
+
const RENDERERS = /^(react|react-dom|preact|hono\/jsx|hono\/jsx\/.*|@kitajs\/html)$/;
|
|
11
|
+
const violations = [];
|
|
12
|
+
const report = (file, text, index, evidence) => violations.push({ rule_id: RULE_ID, file, ...locate(text, index), evidence });
|
|
13
|
+
for (const file of scope(readRoots(), readExcludes())) {
|
|
14
|
+
if (designOf(file)?.layer !== 0 || !CODE_EXT.has(extname(file))) continue;
|
|
15
|
+
const m = masked(file);
|
|
16
|
+
if (!m) continue;
|
|
17
|
+
for (const imp of importsOf(m.text)) if (RENDERERS.test(imp.specifier)) report(file, m.text, imp.index, `token file imports the renderer "${imp.specifier}"; tokens are values, not widgets`);
|
|
18
|
+
const jsx = m.code.search(/<\/[A-Za-z]|\/>/);
|
|
19
|
+
if (jsx !== -1) report(file, m.text, jsx, "token file renders markup; move it to primitives");
|
|
20
|
+
for (const c of m.code.matchAll(/\b(if|for|while|switch)\s*\(/g)) report(file, m.text, c.index, `token file branches with ${c[1]}; tokens are pure values`);
|
|
21
|
+
}
|
|
22
|
+
process.stderr.write(`bun-detector[design-tokens-pure]: ${violations.length} violation(s)\n`);
|
|
23
|
+
emit(violations);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// Detector: coder.bun.design-wagons-import
|
|
3
|
+
// The design system is wagon-agnostic: a file under a design root never imports
|
|
4
|
+
// application or wagon code. Wagons import FROM the design system, never the reverse.
|
|
5
|
+
import { readRoots, readExcludes, readText, emit, locate } from "../../../lib/scan.mjs";
|
|
6
|
+
import { scope, designOf, importsOf, target, designTarget } from "./_design.mjs";
|
|
7
|
+
|
|
8
|
+
const RULE_ID = "coder.bun.design-wagons-import";
|
|
9
|
+
const violations = [];
|
|
10
|
+
for (const file of scope(readRoots(), readExcludes())) {
|
|
11
|
+
if (!designOf(file)) continue;
|
|
12
|
+
const text = readText(file);
|
|
13
|
+
for (const imp of importsOf(text ?? "")) {
|
|
14
|
+
if (target(file, imp.specifier)?.path && !designTarget(file, imp.specifier)) violations.push({ rule_id: RULE_ID, file, ...locate(text, imp.index), evidence: `design-system file imports "${imp.specifier}", outside the design root; the design system must not depend on app code` });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
process.stderr.write(`bun-detector[design-wagons-import]: ${violations.length} violation(s)\n`);
|
|
18
|
+
emit(violations);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// FAMILY validator: bun_design_system_detector
|
|
3
|
+
// Runs each member check (checks/*.mjs) VERBATIM as a subprocess and merges their
|
|
4
|
+
// RAW v1.1 reports into one — ONE implementation realizing a family of rule_ids
|
|
5
|
+
// (the Core pattern; see frontend.workspace.runtime's families).
|
|
6
|
+
//
|
|
7
|
+
// Members are spawned with `process.execPath`, which under this provider IS the
|
|
8
|
+
// bun binary, so a member may be authored as .mjs OR .ts with no build step.
|
|
9
|
+
// Files whose name begins with `_` are skipped: they are shared helpers, not
|
|
10
|
+
// checks. (The node-runtime families have no such convention because each of
|
|
11
|
+
// their checks re-implements its own walker; this provider factors the walk into
|
|
12
|
+
// lib/scan.mjs instead.)
|
|
13
|
+
import { execFileSync } from "node:child_process";
|
|
14
|
+
import { readFileSync, writeFileSync, mkdtempSync, readdirSync } from "node:fs";
|
|
15
|
+
import { tmpdir } from "node:os";
|
|
16
|
+
import { join, dirname } from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
|
|
19
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
const reportPath = process.env.ATDD_VIOLATIONS_REPORT;
|
|
21
|
+
if (!reportPath) {
|
|
22
|
+
process.stderr.write("family: ATDD_VIOLATIONS_REPORT not set\n");
|
|
23
|
+
process.exit(2);
|
|
24
|
+
}
|
|
25
|
+
const checks = readdirSync(join(here, "checks"))
|
|
26
|
+
.filter((f) => (f.endsWith(".mjs") || f.endsWith(".ts")) && !f.startsWith("_"))
|
|
27
|
+
.sort();
|
|
28
|
+
const td = mkdtempSync(join(tmpdir(), "atdd-bun-fam-"));
|
|
29
|
+
const out = [];
|
|
30
|
+
for (const c of checks) {
|
|
31
|
+
const rep = join(td, c + ".json");
|
|
32
|
+
try {
|
|
33
|
+
execFileSync(process.execPath, [join(here, "checks", c)], {
|
|
34
|
+
env: { ...process.env, ATDD_VIOLATIONS_REPORT: rep },
|
|
35
|
+
stdio: ["ignore", "ignore", "inherit"],
|
|
36
|
+
});
|
|
37
|
+
} catch {
|
|
38
|
+
/* a member may exit non-zero; still try to read its report */
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
out.push(...JSON.parse(readFileSync(rep, "utf8")).violations);
|
|
42
|
+
} catch {}
|
|
43
|
+
}
|
|
44
|
+
writeFileSync(reportPath, JSON.stringify({ violations: out }, null, 2), "utf8");
|
|
45
|
+
process.stderr.write("family bun_design_system_detector: " + out.length + " violation(s)\n");
|
|
46
|
+
process.exit(0);
|
package/detectors/bun_design_system_detector/fixtures/clean/app/src/design/components/Card.tsx
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Text } from "../primitives/Text";
|
|
2
|
+
import { space } from "../tokens/spacing";
|
|
3
|
+
|
|
4
|
+
export function Card(props: { title: string; children: unknown }) {
|
|
5
|
+
return <section class="card" style={{ padding: space[4], borderRadius: "var(--radius)" }}><Text>{props.title}</Text>{props.children}</section>;
|
|
6
|
+
}
|
package/detectors/bun_design_system_detector/fixtures/clean/app/src/design/primitives/Button.tsx
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { colors } from "../tokens/colors";
|
|
2
|
+
import { space } from "../tokens/spacing";
|
|
3
|
+
|
|
4
|
+
export function Button(props: { children: unknown; [attr: string]: unknown }) {
|
|
5
|
+
return <button class="btn" style={{ color: colors.ink, padding: space[2] }} {...props}>{props.children}</button>;
|
|
6
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const colors = { ink: "var(--ink)", line: "var(--line)" } as const;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const space = { 2: "var(--space-2)", 4: "var(--space-4)" } as const;
|
package/detectors/bun_design_system_detector/fixtures/dirty/connected/src/design/tokens/colors.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const colors = { ink: "var(--ink)" } as const;
|
package/detectors/bun_design_system_detector/fixtures/dirty/token_color/src/design/tokens/colors.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root { --danger: #b91c1c; }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<ul style="margin: 16px; border-radius: 6px"><li>row</li></ul>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root { --space-3: 12px; }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const orderStatus = ["open", "closed"] as const;
|