@slowcook-ai/cli 0.17.0-alpha.4 → 0.17.0-alpha.6
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/dist/commands/recon/index.d.ts.map +1 -1
- package/dist/commands/recon/index.js +29 -1
- package/dist/commands/recon/index.js.map +1 -1
- package/dist/commands/recon/shape-emit.d.ts +55 -0
- package/dist/commands/recon/shape-emit.d.ts.map +1 -0
- package/dist/commands/recon/shape-emit.js +350 -0
- package/dist/commands/recon/shape-emit.js.map +1 -0
- package/dist/commands/recon/shape-preserve.d.ts +63 -0
- package/dist/commands/recon/shape-preserve.d.ts.map +1 -0
- package/dist/commands/recon/shape-preserve.js +343 -0
- package/dist/commands/recon/shape-preserve.js.map +1 -0
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/recon/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/recon/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAclF,UAAU,cAAc;IACtB,IAAI,EAAE,WAAW,GAAG,aAAa,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,SAAS;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,UAAU,aAAa;IACrB,IAAI,EAAE,mBAAmB,GAAG,eAAe,GAAG,qBAAqB,GAAG,wBAAwB,CAAC;IAC/F,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,uBAAuB,CAAC;IACnC,MAAM,EAAE,OAAO,GAAG,eAAe,GAAG,UAAU,CAAC;IAC/C,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,WAAW,EAAE,SAAS,EAAE,CAAC;IACzB,eAAe,EAAE,aAAa,EAAE,CAAC;IACjC,wBAAwB,EAAE,MAAM,CAAC;IACjC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAqDD,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgJ9E;AAID,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAU5E;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAMrD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAYrD;AA8CD,YAAY,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
25
25
|
import { dirname, join, relative } from "node:path";
|
|
26
26
|
import { buildHistoryIndex } from "../refine/history-index.js";
|
|
27
|
+
import { extractShape, synthesiseShapeTestFile, findMockFilesForStory } from "./shape-preserve.js";
|
|
27
28
|
function parseArgs(argv) {
|
|
28
29
|
const args = {
|
|
29
30
|
story: "",
|
|
@@ -159,6 +160,33 @@ export async function recon(argv, _cliVersion) {
|
|
|
159
160
|
// a different story's tests.
|
|
160
161
|
// (This is a heavier check; skip for v1. Recorded as a TODO.)
|
|
161
162
|
result.warnings.push("brownfield-rename-safety check is not yet implemented in 0.17.6 (recorded by simulation; defer to 0.17.7)");
|
|
163
|
+
// 0.17.6+ — shape-emit. Read mock UI for the story; emit
|
|
164
|
+
// tests/integration/story-N-shape.test.tsx with structural assertions
|
|
165
|
+
// (testids, visual tokens, semantic landmarks). Mock-chrome subtrees
|
|
166
|
+
// are stripped via the data-mock-chrome="true" marker.
|
|
167
|
+
let shapeFile = null;
|
|
168
|
+
try {
|
|
169
|
+
const mockFiles = findMockFilesForStory(args.repoRoot, args.story);
|
|
170
|
+
if (mockFiles.length > 0) {
|
|
171
|
+
const shapes = mockFiles
|
|
172
|
+
.map((f) => extractShape(join(args.repoRoot, f), args.repoRoot))
|
|
173
|
+
.filter((s) => s !== null);
|
|
174
|
+
if (shapes.length > 0) {
|
|
175
|
+
const body = synthesiseShapeTestFile({ story: args.story, shapes });
|
|
176
|
+
shapeFile = `tests/integration/story-${args.story}-shape.test.tsx`;
|
|
177
|
+
const outAbs = join(args.repoRoot, shapeFile);
|
|
178
|
+
mkdirSync(dirname(outAbs), { recursive: true });
|
|
179
|
+
writeFileSync(outAbs, body, "utf8");
|
|
180
|
+
console.log(` shape-emit: wrote ${shapeFile} (from ${mockFiles.length} mock file(s); ${shapes.flatMap((s) => s.testids).length} testid assertion(s))`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
result.warnings.push(`shape-emit: no mock files discovered for story-${args.story} (no scenarios reference it; no test imports point at mock/src)`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch (e) {
|
|
188
|
+
result.warnings.push(`shape-emit failed: ${e.message.slice(0, 200)}`);
|
|
189
|
+
}
|
|
162
190
|
// Decide status
|
|
163
191
|
if (result.structural_gaps.length > 0) {
|
|
164
192
|
result.status = "escalate";
|
|
@@ -171,7 +199,7 @@ export async function recon(argv, _cliVersion) {
|
|
|
171
199
|
}
|
|
172
200
|
// Write output
|
|
173
201
|
mkdirSync(dirname(args.outPath), { recursive: true });
|
|
174
|
-
writeFileSync(args.outPath, JSON.stringify(result, null, 2), "utf8");
|
|
202
|
+
writeFileSync(args.outPath, JSON.stringify({ ...result, shape_file: shapeFile }, null, 2), "utf8");
|
|
175
203
|
// Print summary
|
|
176
204
|
console.log(` status: ${result.status.toUpperCase()}`);
|
|
177
205
|
console.log(` ${testFiles.length} test file(s) · ${result.structural_gaps.length} structural gap(s) · ${result.testid_gaps.length} testid gap(s) · ${result.renames.length} rename(s)`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/recon/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACpG,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAqB,MAAM,4BAA4B,CAAC;AA+
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/recon/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @slowcook 0.17.6+ — recon shape-test emission.
|
|
3
|
+
*
|
|
4
|
+
* Reads mock UI files for a story and emits structural assertions to
|
|
5
|
+
* `tests/integration/story-N-shape.test.tsx`. Pure deterministic; no LLM.
|
|
6
|
+
*
|
|
7
|
+
* Asserts SHAPE only (per `feedback_shape_tests_belong_to_recon_not_testgen`):
|
|
8
|
+
* - testid presence + cardinality
|
|
9
|
+
* - DOM containment (e.g. badge inside <header>)
|
|
10
|
+
* - className tokens (visual/a11y signals like `rounded-full`, `min-h-[44px]`)
|
|
11
|
+
* - CSS variable tokens (no inline hex/rgb)
|
|
12
|
+
*
|
|
13
|
+
* Never asserts WIRING (per `feedback_testgen_must_not_over_assert_mock`):
|
|
14
|
+
* - No spied-on hooks, no import existence, no readFileSync of source
|
|
15
|
+
* - No mock-only chrome (subtrees marked `data-mock-chrome="true"` are
|
|
16
|
+
* mechanically excluded from the testid+class extraction)
|
|
17
|
+
*
|
|
18
|
+
* Test rendering uses plain props (no scenario-runtime), so mock-chrome
|
|
19
|
+
* never actually renders in the test even if it weren't filtered.
|
|
20
|
+
*/
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
export interface ExtractedShape {
|
|
23
|
+
/** Component file scanned (mock-side). */
|
|
24
|
+
file: string;
|
|
25
|
+
/** Testids found on non-mock-chrome JSX (deduped). */
|
|
26
|
+
testids: string[];
|
|
27
|
+
/** Visual className tokens worth preserving (subset; see TOKENS_OF_INTEREST). */
|
|
28
|
+
visualTokens: string[];
|
|
29
|
+
/** Whether the component renders any element with a `<header>` ancestor. */
|
|
30
|
+
hasHeader: boolean;
|
|
31
|
+
/** Component name (best-effort). */
|
|
32
|
+
componentName: string | null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Scan a mock UI file, return the SHAPE-relevant facts after stripping
|
|
36
|
+
* mock-only chrome subtrees.
|
|
37
|
+
*/
|
|
38
|
+
export declare function extractShape(absFile: string, repoRoot: string): ExtractedShape | null;
|
|
39
|
+
export declare function stripMockChromeSubtrees(source: string): string;
|
|
40
|
+
interface SynthOpts {
|
|
41
|
+
story: string;
|
|
42
|
+
shapes: ExtractedShape[];
|
|
43
|
+
}
|
|
44
|
+
export declare function synthesiseShapeTestFile(opts: SynthOpts): string;
|
|
45
|
+
/**
|
|
46
|
+
* Walk mock/src/ + return any file that contains `story-N` in source
|
|
47
|
+
* OR is referenced by a scenario file for the story.
|
|
48
|
+
*
|
|
49
|
+
* For 0.17.6 first cut: simple — any file under mock/src/components/
|
|
50
|
+
* that lives in a directory referenced by the story's scenario.
|
|
51
|
+
* Conservative — caller can pass explicit list via --mock-files.
|
|
52
|
+
*/
|
|
53
|
+
export declare function findMockFilesForStory(repoRoot: string, story: string): string[];
|
|
54
|
+
export { join as _join };
|
|
55
|
+
//# sourceMappingURL=shape-emit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape-emit.d.ts","sourceRoot":"","sources":["../../../src/commands/recon/shape-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,IAAI,EAAY,MAAM,WAAW,CAAC;AAE3C,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iFAAiF;IACjF,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,4EAA4E;IAC5E,SAAS,EAAE,OAAO,CAAC;IACnB,oCAAoC;IACpC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAWD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAiBrF;AAaD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAY9D;AA0GD,UAAU,SAAS;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,cAAc,EAAE,CAAC;CAC1B;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CA6E/D;AAaD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAsD/E;AAGD,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @slowcook 0.17.6+ — recon shape-test emission.
|
|
3
|
+
*
|
|
4
|
+
* Reads mock UI files for a story and emits structural assertions to
|
|
5
|
+
* `tests/integration/story-N-shape.test.tsx`. Pure deterministic; no LLM.
|
|
6
|
+
*
|
|
7
|
+
* Asserts SHAPE only (per `feedback_shape_tests_belong_to_recon_not_testgen`):
|
|
8
|
+
* - testid presence + cardinality
|
|
9
|
+
* - DOM containment (e.g. badge inside <header>)
|
|
10
|
+
* - className tokens (visual/a11y signals like `rounded-full`, `min-h-[44px]`)
|
|
11
|
+
* - CSS variable tokens (no inline hex/rgb)
|
|
12
|
+
*
|
|
13
|
+
* Never asserts WIRING (per `feedback_testgen_must_not_over_assert_mock`):
|
|
14
|
+
* - No spied-on hooks, no import existence, no readFileSync of source
|
|
15
|
+
* - No mock-only chrome (subtrees marked `data-mock-chrome="true"` are
|
|
16
|
+
* mechanically excluded from the testid+class extraction)
|
|
17
|
+
*
|
|
18
|
+
* Test rendering uses plain props (no scenario-runtime), so mock-chrome
|
|
19
|
+
* never actually renders in the test even if it weren't filtered.
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
22
|
+
import { join, relative } from "node:path";
|
|
23
|
+
const TOKENS_OF_INTEREST = [
|
|
24
|
+
// Shape tokens
|
|
25
|
+
"rounded-full", "rounded-2xl", "rounded-xl", "rounded-lg", "rounded-md", "rounded-sm",
|
|
26
|
+
// Tap-target / a11y mandates
|
|
27
|
+
"min-h-[44px]", "min-h-11", "h-11", "min-w-[44px]",
|
|
28
|
+
// Common layout primitives that imply structural intent
|
|
29
|
+
"flex", "grid", "inline-flex",
|
|
30
|
+
];
|
|
31
|
+
/**
|
|
32
|
+
* Scan a mock UI file, return the SHAPE-relevant facts after stripping
|
|
33
|
+
* mock-only chrome subtrees.
|
|
34
|
+
*/
|
|
35
|
+
export function extractShape(absFile, repoRoot) {
|
|
36
|
+
if (!existsSync(absFile))
|
|
37
|
+
return null;
|
|
38
|
+
const body = readFileSync(absFile, "utf8");
|
|
39
|
+
const fileRel = relative(repoRoot, absFile).replace(/\\/g, "/");
|
|
40
|
+
// Strip mock-only chrome subtrees BEFORE extraction.
|
|
41
|
+
const cleaned = stripMockChromeSubtrees(body);
|
|
42
|
+
// Component name: simplest extraction.
|
|
43
|
+
const nameMatch = cleaned.match(/export\s+(?:default\s+)?function\s+([A-Z]\w*)/);
|
|
44
|
+
const componentName = nameMatch ? nameMatch[1] ?? null : null;
|
|
45
|
+
const testids = [...new Set(extractTestids(cleaned))];
|
|
46
|
+
const visualTokens = [...new Set(extractTokens(cleaned, TOKENS_OF_INTEREST))];
|
|
47
|
+
const hasHeader = /<header\b/.test(cleaned);
|
|
48
|
+
return { file: fileRel, testids, visualTokens, hasHeader, componentName };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Strip JSX subtrees marked as mock-only chrome. Three conventions
|
|
52
|
+
* recognised (any one is enough):
|
|
53
|
+
* 1. `data-mock-chrome="true"` attribute on the wrapping element
|
|
54
|
+
* 2. JSX comment immediately preceding: `{/* Mock-only ... */ `}`
|
|
55
|
+
* 3.;
|
|
56
|
+
Source;
|
|
57
|
+
comment;
|
|
58
|
+
immediately;
|
|
59
|
+
preceding;
|
|
60
|
+
the;
|
|
61
|
+
element: `// Mock-only ...`
|
|
62
|
+
*
|
|
63
|
+
* Conservative;
|
|
64
|
+
false;
|
|
65
|
+
positives(treating, real, shape);
|
|
66
|
+
are
|
|
67
|
+
* worse;
|
|
68
|
+
than;
|
|
69
|
+
false;
|
|
70
|
+
negatives.We;
|
|
71
|
+
only;
|
|
72
|
+
strip;
|
|
73
|
+
elements;
|
|
74
|
+
with (the)
|
|
75
|
+
explicit
|
|
76
|
+
* marker;
|
|
77
|
+
the;
|
|
78
|
+
comment - based;
|
|
79
|
+
heuristics;
|
|
80
|
+
are;
|
|
81
|
+
best - effort;
|
|
82
|
+
fallback.
|
|
83
|
+
* /;
|
|
84
|
+
export function stripMockChromeSubtrees(source) {
|
|
85
|
+
let out = source;
|
|
86
|
+
// Pass 1: strip elements opened with data-mock-chrome="true" (and their
|
|
87
|
+
// children up to the matching closing tag).
|
|
88
|
+
out = stripBalancedJsxBlock(out, /<(\w+)\b[^>]*\bdata-mock-chrome\s*=\s*["']true["'][^>]*>/);
|
|
89
|
+
// Pass 2: best-effort — strip the JSX block that immediately follows
|
|
90
|
+
// a `{/* Mock-only ... */}` JSX comment.
|
|
91
|
+
out = stripBalancedJsxBlockAfter(out, /\{\/\*\s*Mock-only[^*]*\*\/\s*\}\s*/);
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Repeatedly find a JSX opening tag matching `openRe` and remove the
|
|
96
|
+
* balanced subtree (open tag + children + close tag).
|
|
97
|
+
*
|
|
98
|
+
* Self-closing tags (e.g. `<Foo />`) are also handled. Tag-name balanced.
|
|
99
|
+
*/
|
|
100
|
+
function stripBalancedJsxBlock(source, openRe) {
|
|
101
|
+
let s = source;
|
|
102
|
+
for (let safety = 0; safety < 64; safety++) {
|
|
103
|
+
const m = openRe.exec(s);
|
|
104
|
+
if (!m)
|
|
105
|
+
break;
|
|
106
|
+
const start = m.index;
|
|
107
|
+
const tagName = m[1];
|
|
108
|
+
const openTag = m[0];
|
|
109
|
+
// Self-closing? remove just that.
|
|
110
|
+
if (openTag.endsWith("/>")) {
|
|
111
|
+
s = s.slice(0, start) + s.slice(start + openTag.length);
|
|
112
|
+
openRe.lastIndex = 0;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
// Walk forward to find balanced close.
|
|
116
|
+
let depth = 1;
|
|
117
|
+
let i = start + openTag.length;
|
|
118
|
+
const openTagRe = new RegExp(`<${tagName}\\b[^>]*?>`, "g");
|
|
119
|
+
const closeTagRe = new RegExp(`</${tagName}\\s*>`, "g");
|
|
120
|
+
while (i < s.length && depth > 0) {
|
|
121
|
+
openTagRe.lastIndex = i;
|
|
122
|
+
closeTagRe.lastIndex = i;
|
|
123
|
+
const oNext = openTagRe.exec(s);
|
|
124
|
+
const cNext = closeTagRe.exec(s);
|
|
125
|
+
if (!cNext)
|
|
126
|
+
break;
|
|
127
|
+
if (oNext && oNext.index < cNext.index) {
|
|
128
|
+
// skip self-closing inside (rare for compound tag)
|
|
129
|
+
if (!oNext[0].endsWith("/>"))
|
|
130
|
+
depth++;
|
|
131
|
+
i = oNext.index + oNext[0].length;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
depth--;
|
|
135
|
+
i = cNext.index + cNext[0].length;
|
|
136
|
+
if (depth === 0) {
|
|
137
|
+
s = s.slice(0, start) + s.slice(i);
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
openRe.lastIndex = 0;
|
|
143
|
+
}
|
|
144
|
+
return s;
|
|
145
|
+
}
|
|
146
|
+
function stripBalancedJsxBlockAfter(source, markerRe) {
|
|
147
|
+
let s = source;
|
|
148
|
+
for (let safety = 0; safety < 32; safety++) {
|
|
149
|
+
const m = markerRe.exec(s);
|
|
150
|
+
if (!m)
|
|
151
|
+
break;
|
|
152
|
+
// Find the JSX opening tag immediately after the marker.
|
|
153
|
+
const after = s.slice(m.index + m[0].length);
|
|
154
|
+
const tagMatch = /^\s*<(\w+)\b[^>]*>/.exec(after);
|
|
155
|
+
if (!tagMatch) {
|
|
156
|
+
// Marker not followed by a JSX tag; just remove the marker comment.
|
|
157
|
+
s = s.slice(0, m.index) + s.slice(m.index + m[0].length);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
// Use stripBalancedJsxBlock at the marker position with the tag-specific regex.
|
|
161
|
+
const openTagAbsoluteIdx = m.index + m[0].length + (after.indexOf("<"));
|
|
162
|
+
const tagName = tagMatch[1];
|
|
163
|
+
if (!tagName) {
|
|
164
|
+
s = s.slice(0, m.index) + s.slice(m.index + m[0].length);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const openTagRe = new RegExp(`<${tagName}\\b[^>]*?>`);
|
|
168
|
+
const sliced = s.slice(openTagAbsoluteIdx);
|
|
169
|
+
const stripped = stripBalancedJsxBlock(sliced, openTagRe);
|
|
170
|
+
s = s.slice(0, m.index) + stripped;
|
|
171
|
+
markerRe.lastIndex = 0;
|
|
172
|
+
}
|
|
173
|
+
return s;
|
|
174
|
+
}
|
|
175
|
+
function extractTestids(source) {
|
|
176
|
+
const out = [];
|
|
177
|
+
for (const m of source.matchAll(/data-testid\s*=\s*["']([^"']+)["']/g)) {
|
|
178
|
+
if (m[1])
|
|
179
|
+
out.push(m[1]);
|
|
180
|
+
}
|
|
181
|
+
return out;
|
|
182
|
+
}
|
|
183
|
+
function extractTokens(source, tokens) {
|
|
184
|
+
const out = [];
|
|
185
|
+
for (const t of tokens) {
|
|
186
|
+
// Look in className strings/template literals.
|
|
187
|
+
const re = new RegExp(`className\\s*=\\s*["'\`][^"'\`]*\\b${escapeRegExp(t)}\\b`);
|
|
188
|
+
if (re.test(source))
|
|
189
|
+
out.push(t);
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
function escapeRegExp(s) {
|
|
194
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
195
|
+
}
|
|
196
|
+
export function synthesiseShapeTestFile(opts) {
|
|
197
|
+
const { story, shapes } = opts;
|
|
198
|
+
const lines = [];
|
|
199
|
+
lines.push("// @vitest-environment jsdom");
|
|
200
|
+
lines.push("//");
|
|
201
|
+
lines.push(`// AUTO-GENERATED by slowcook recon (story-${story}). Do not edit by hand.`);
|
|
202
|
+
lines.push("// Asserts STRUCTURAL SHAPE only (DOM containment, testid cardinality,");
|
|
203
|
+
lines.push("// className tokens, CSS-variable usage). Behavioral assertions live in");
|
|
204
|
+
lines.push(`// the testgen-emitted story-${story}-*.test.{ts,tsx} files.`);
|
|
205
|
+
lines.push("//");
|
|
206
|
+
lines.push("// Mock-only chrome (data-mock-chrome=\"true\" subtrees) is excluded.");
|
|
207
|
+
lines.push("// Re-running recon regenerates this file.");
|
|
208
|
+
lines.push("");
|
|
209
|
+
lines.push(`import { describe, it, expect } from "vitest";`);
|
|
210
|
+
lines.push("");
|
|
211
|
+
lines.push(`describe("story-${story} structural shape (recon-emitted)", () => {`);
|
|
212
|
+
// For each unique testid across all shapes, emit cardinality + presence assertions
|
|
213
|
+
// backed by source-greppable checks (NOT a render — recon doesn't have a
|
|
214
|
+
// render-helper available without the consumer's test setup).
|
|
215
|
+
// The render-based assertions need the consumer's renderWithProviders helper +
|
|
216
|
+
// Component import + props; recon emits a TODO scaffold the operator (or a
|
|
217
|
+
// future iteration of brew) fills in.
|
|
218
|
+
const allTestids = [...new Set(shapes.flatMap((s) => s.testids))];
|
|
219
|
+
if (allTestids.length === 0) {
|
|
220
|
+
lines.push(` it.skip("no testids found in mock UI — nothing to assert", () => {});`);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
// Source-grep based assertions: cheap + always work without a render.
|
|
224
|
+
lines.push(``);
|
|
225
|
+
lines.push(` // Source-grep assertions: testids the mock exposes must remain in src/`);
|
|
226
|
+
lines.push(` // after brew's edits. These don't require a render.`);
|
|
227
|
+
lines.push(` import { readFileSync, existsSync } from "node:fs";`);
|
|
228
|
+
lines.push(``);
|
|
229
|
+
for (const shape of shapes) {
|
|
230
|
+
const srcPath = mockToSrcPath(shape.file);
|
|
231
|
+
lines.push(` describe("from mock ${shape.file}", () => {`);
|
|
232
|
+
lines.push(` const srcPath = "${srcPath}";`);
|
|
233
|
+
lines.push(` const src = existsSync(srcPath) ? readFileSync(srcPath, "utf8") : "";`);
|
|
234
|
+
lines.push(``);
|
|
235
|
+
for (const tid of shape.testids) {
|
|
236
|
+
lines.push(` it("preserves data-testid='${tid}' (mock-only chrome excluded)", () => {`);
|
|
237
|
+
lines.push(` expect(src).toMatch(/data-testid\\s*=\\s*["']${escapeRegexForTest(tid)}["']/);`);
|
|
238
|
+
lines.push(` });`);
|
|
239
|
+
}
|
|
240
|
+
for (const token of shape.visualTokens) {
|
|
241
|
+
lines.push(` it("preserves visual token '${token}'", () => {`);
|
|
242
|
+
lines.push(` expect(src).toMatch(/className=[^>]*\\b${escapeRegexForTest(token)}\\b/);`);
|
|
243
|
+
lines.push(` });`);
|
|
244
|
+
}
|
|
245
|
+
if (shape.hasHeader) {
|
|
246
|
+
lines.push(` it("retains a <header> element (semantic landmark)", () => {`);
|
|
247
|
+
lines.push(` expect(src).toMatch(/<header\\b/);`);
|
|
248
|
+
lines.push(` });`);
|
|
249
|
+
}
|
|
250
|
+
lines.push(` });`);
|
|
251
|
+
lines.push(``);
|
|
252
|
+
}
|
|
253
|
+
// Anti-wiring assertion: no inline hex colors (force token-family preservation).
|
|
254
|
+
lines.push(` describe("token-family preservation", () => {`);
|
|
255
|
+
for (const shape of shapes) {
|
|
256
|
+
const srcPath = mockToSrcPath(shape.file);
|
|
257
|
+
lines.push(` it("${shape.componentName ?? shape.file}: no inline hex/rgb colors (use var(--) tokens)", () => {`);
|
|
258
|
+
lines.push(` const src = existsSync("${srcPath}") ? readFileSync("${srcPath}", "utf8") : "";`);
|
|
259
|
+
lines.push(` // Allow hex in JSDoc/comments; only flag in className/style attribute values.`);
|
|
260
|
+
lines.push(` const styled = src.match(/(?:className|style)\\s*=\\s*[\"\`'][^\"\`']*?[#][0-9a-fA-F]{3,8}[^\"\`']*?[\"\`']/g) || [];`);
|
|
261
|
+
lines.push(` expect(styled, "no inline hex in className/style").toEqual([]);`);
|
|
262
|
+
lines.push(` });`);
|
|
263
|
+
}
|
|
264
|
+
lines.push(` });`);
|
|
265
|
+
}
|
|
266
|
+
lines.push(`});`);
|
|
267
|
+
return lines.join("\n") + "\n";
|
|
268
|
+
}
|
|
269
|
+
function mockToSrcPath(mockPath) {
|
|
270
|
+
// mock/src/components/X.tsx → src/components/X.tsx
|
|
271
|
+
return mockPath.replace(/^mock\/src\//, "src/");
|
|
272
|
+
}
|
|
273
|
+
function escapeRegexForTest(s) {
|
|
274
|
+
return s.replace(/[.*+?^${}()|[\]\\\/]/g, "\\\\$&");
|
|
275
|
+
}
|
|
276
|
+
// ---- Discovery: which mock files belong to a story ----
|
|
277
|
+
/**
|
|
278
|
+
* Walk mock/src/ + return any file that contains `story-N` in source
|
|
279
|
+
* OR is referenced by a scenario file for the story.
|
|
280
|
+
*
|
|
281
|
+
* For 0.17.6 first cut: simple — any file under mock/src/components/
|
|
282
|
+
* that lives in a directory referenced by the story's scenario.
|
|
283
|
+
* Conservative — caller can pass explicit list via --mock-files.
|
|
284
|
+
*/
|
|
285
|
+
export function findMockFilesForStory(repoRoot, story) {
|
|
286
|
+
const out = new Set();
|
|
287
|
+
// Look for mock/scenarios/story-N*.ts
|
|
288
|
+
const scenariosDir = join(repoRoot, "mock/scenarios");
|
|
289
|
+
if (existsSync(scenariosDir)) {
|
|
290
|
+
for (const name of readdirSync(scenariosDir)) {
|
|
291
|
+
if (!name.startsWith(`story-${story}`))
|
|
292
|
+
continue;
|
|
293
|
+
const body = readFileSync(join(scenariosDir, name), "utf8");
|
|
294
|
+
// Pull `from "@/components/X/Y"` imports
|
|
295
|
+
for (const m of body.matchAll(/from\s+["']@\/(components\/[\w\-./[\]]+)["']/g)) {
|
|
296
|
+
if (m[1]) {
|
|
297
|
+
const candidates = [
|
|
298
|
+
`mock/src/${m[1]}.tsx`,
|
|
299
|
+
`mock/src/${m[1]}.ts`,
|
|
300
|
+
`mock/src/${m[1]}/index.tsx`,
|
|
301
|
+
];
|
|
302
|
+
for (const c of candidates) {
|
|
303
|
+
if (existsSync(join(repoRoot, c)))
|
|
304
|
+
out.add(c);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// Also: any mock component referenced by a story-N test file
|
|
311
|
+
const testsDir = join(repoRoot, "tests/integration");
|
|
312
|
+
if (existsSync(testsDir)) {
|
|
313
|
+
for (const name of readdirSync(testsDir)) {
|
|
314
|
+
if (!name.startsWith(`story-${story}`))
|
|
315
|
+
continue;
|
|
316
|
+
const body = readFileSync(join(testsDir, name), "utf8");
|
|
317
|
+
for (const m of body.matchAll(/from\s+["']@\/(components\/[\w\-./[\]]+)["']/g)) {
|
|
318
|
+
if (m[1]) {
|
|
319
|
+
const candidates = [
|
|
320
|
+
`mock/src/${m[1]}.tsx`,
|
|
321
|
+
`mock/src/${m[1]}.ts`,
|
|
322
|
+
];
|
|
323
|
+
for (const c of candidates) {
|
|
324
|
+
if (existsSync(join(repoRoot, c)))
|
|
325
|
+
out.add(c);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
// Recurse: include direct imports of the discovered files
|
|
332
|
+
for (const file of [...out]) {
|
|
333
|
+
const body = readFileSync(join(repoRoot, file), "utf8");
|
|
334
|
+
for (const m of body.matchAll(/from\s+["']\.\/(\w+)["']/g)) {
|
|
335
|
+
if (m[1]) {
|
|
336
|
+
const dir = file.split("/").slice(0, -1).join("/");
|
|
337
|
+
const candidate = `${dir}/${m[1]}.tsx`;
|
|
338
|
+
if (existsSync(join(repoRoot, candidate)))
|
|
339
|
+
out.add(candidate);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return [...out];
|
|
344
|
+
}
|
|
345
|
+
// Re-export for tests
|
|
346
|
+
export { join as _join };
|
|
347
|
+
// re-export to placate tsc unused-import warnings
|
|
348
|
+
void readdirSync;
|
|
349
|
+
void statSync;
|
|
350
|
+
//# sourceMappingURL=shape-emit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape-emit.js","sourceRoot":"","sources":["../../../src/commands/recon/shape-emit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @slowcook 0.17.6+ — recon shape-preservation tests.
|
|
3
|
+
*
|
|
4
|
+
* Reads mock UI files for a story and emits structural assertions to
|
|
5
|
+
* tests/integration/story-N-shape.test.tsx. Pure deterministic; no LLM.
|
|
6
|
+
*
|
|
7
|
+
* Asserts SHAPE only (per `feedback_shape_tests_belong_to_recon_not_testgen`):
|
|
8
|
+
* - testid presence + cardinality
|
|
9
|
+
* - DOM containment (e.g. badge inside <header>)
|
|
10
|
+
* - className tokens (visual/a11y signals like `rounded-full`, `min-h-[44px]`)
|
|
11
|
+
* - CSS variable tokens (no inline hex/rgb)
|
|
12
|
+
*
|
|
13
|
+
* Never asserts WIRING (per `feedback_testgen_must_not_over_assert_mock`):
|
|
14
|
+
* - No spied-on hooks, no import existence, no readFileSync of source
|
|
15
|
+
* - No mock-only chrome (subtrees marked `data-mock-chrome="true"` are
|
|
16
|
+
* mechanically excluded from the testid+class extraction)
|
|
17
|
+
*
|
|
18
|
+
* Test rendering uses plain props (no scenario-runtime), so mock-chrome
|
|
19
|
+
* never actually renders in the test even if it weren't filtered.
|
|
20
|
+
*/
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
export interface ExtractedShape {
|
|
23
|
+
/** Component file scanned (mock-side). */
|
|
24
|
+
file: string;
|
|
25
|
+
/** Testids found on non-mock-chrome JSX (deduped). */
|
|
26
|
+
testids: string[];
|
|
27
|
+
/** Visual className tokens worth preserving (subset; see TOKENS_OF_INTEREST). */
|
|
28
|
+
visualTokens: string[];
|
|
29
|
+
/** Whether the component renders any element with a `<header>` ancestor. */
|
|
30
|
+
hasHeader: boolean;
|
|
31
|
+
/** Component name (best-effort). */
|
|
32
|
+
componentName: string | null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Scan a mock UI file, return the SHAPE-relevant facts after stripping
|
|
36
|
+
* mock-only chrome subtrees.
|
|
37
|
+
*/
|
|
38
|
+
export declare function extractShape(absFile: string, repoRoot: string): ExtractedShape | null;
|
|
39
|
+
/**
|
|
40
|
+
* Strip JSX subtrees marked as mock-only chrome. Two conventions:
|
|
41
|
+
* 1. data-mock-chrome="true" attribute on the wrapping element
|
|
42
|
+
* 2. JSX comment immediately preceding (matching /Mock-only/i)
|
|
43
|
+
*
|
|
44
|
+
* Conservative — false positives (treating real shape as chrome) are
|
|
45
|
+
* worse than false negatives.
|
|
46
|
+
*/
|
|
47
|
+
export declare function stripMockChromeSubtrees(source: string): string;
|
|
48
|
+
interface SynthOpts {
|
|
49
|
+
story: string;
|
|
50
|
+
shapes: ExtractedShape[];
|
|
51
|
+
}
|
|
52
|
+
export declare function synthesiseShapeTestFile(opts: SynthOpts): string;
|
|
53
|
+
/**
|
|
54
|
+
* Walk mock/src/ + return any file that contains `story-N` in source
|
|
55
|
+
* OR is referenced by a scenario file for the story.
|
|
56
|
+
*
|
|
57
|
+
* For 0.17.6 first cut: simple — any file under mock/src/components/
|
|
58
|
+
* that lives in a directory referenced by the story's scenario.
|
|
59
|
+
* Conservative — caller can pass explicit list via --mock-files.
|
|
60
|
+
*/
|
|
61
|
+
export declare function findMockFilesForStory(repoRoot: string, story: string): string[];
|
|
62
|
+
export { join as _join };
|
|
63
|
+
//# sourceMappingURL=shape-preserve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape-preserve.d.ts","sourceRoot":"","sources":["../../../src/commands/recon/shape-preserve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,IAAI,EAAY,MAAM,WAAW,CAAC;AAE3C,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iFAAiF;IACjF,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,4EAA4E;IAC5E,SAAS,EAAE,OAAO,CAAC;IACnB,oCAAoC;IACpC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAWD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAiBrF;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAY9D;AA0GD,UAAU,SAAS;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,cAAc,EAAE,CAAC;CAC1B;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAmE/D;AAaD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA+E/E;AAGD,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @slowcook 0.17.6+ — recon shape-preservation tests.
|
|
3
|
+
*
|
|
4
|
+
* Reads mock UI files for a story and emits structural assertions to
|
|
5
|
+
* tests/integration/story-N-shape.test.tsx. Pure deterministic; no LLM.
|
|
6
|
+
*
|
|
7
|
+
* Asserts SHAPE only (per `feedback_shape_tests_belong_to_recon_not_testgen`):
|
|
8
|
+
* - testid presence + cardinality
|
|
9
|
+
* - DOM containment (e.g. badge inside <header>)
|
|
10
|
+
* - className tokens (visual/a11y signals like `rounded-full`, `min-h-[44px]`)
|
|
11
|
+
* - CSS variable tokens (no inline hex/rgb)
|
|
12
|
+
*
|
|
13
|
+
* Never asserts WIRING (per `feedback_testgen_must_not_over_assert_mock`):
|
|
14
|
+
* - No spied-on hooks, no import existence, no readFileSync of source
|
|
15
|
+
* - No mock-only chrome (subtrees marked `data-mock-chrome="true"` are
|
|
16
|
+
* mechanically excluded from the testid+class extraction)
|
|
17
|
+
*
|
|
18
|
+
* Test rendering uses plain props (no scenario-runtime), so mock-chrome
|
|
19
|
+
* never actually renders in the test even if it weren't filtered.
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
22
|
+
import { join, relative } from "node:path";
|
|
23
|
+
const TOKENS_OF_INTEREST = [
|
|
24
|
+
// Shape tokens
|
|
25
|
+
"rounded-full", "rounded-2xl", "rounded-xl", "rounded-lg", "rounded-md", "rounded-sm",
|
|
26
|
+
// Tap-target / a11y mandates
|
|
27
|
+
"min-h-[44px]", "min-h-11", "h-11", "min-w-[44px]",
|
|
28
|
+
// Common layout primitives that imply structural intent
|
|
29
|
+
"flex", "grid", "inline-flex",
|
|
30
|
+
];
|
|
31
|
+
/**
|
|
32
|
+
* Scan a mock UI file, return the SHAPE-relevant facts after stripping
|
|
33
|
+
* mock-only chrome subtrees.
|
|
34
|
+
*/
|
|
35
|
+
export function extractShape(absFile, repoRoot) {
|
|
36
|
+
if (!existsSync(absFile))
|
|
37
|
+
return null;
|
|
38
|
+
const body = readFileSync(absFile, "utf8");
|
|
39
|
+
const fileRel = relative(repoRoot, absFile).replace(/\\/g, "/");
|
|
40
|
+
// Strip mock-only chrome subtrees BEFORE extraction.
|
|
41
|
+
const cleaned = stripMockChromeSubtrees(body);
|
|
42
|
+
// Component name: simplest extraction.
|
|
43
|
+
const nameMatch = cleaned.match(/export\s+(?:default\s+)?function\s+([A-Z]\w*)/);
|
|
44
|
+
const componentName = nameMatch ? nameMatch[1] ?? null : null;
|
|
45
|
+
const testids = [...new Set(extractTestids(cleaned))];
|
|
46
|
+
const visualTokens = [...new Set(extractTokens(cleaned, TOKENS_OF_INTEREST))];
|
|
47
|
+
const hasHeader = /<header\b/.test(cleaned);
|
|
48
|
+
return { file: fileRel, testids, visualTokens, hasHeader, componentName };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Strip JSX subtrees marked as mock-only chrome. Two conventions:
|
|
52
|
+
* 1. data-mock-chrome="true" attribute on the wrapping element
|
|
53
|
+
* 2. JSX comment immediately preceding (matching /Mock-only/i)
|
|
54
|
+
*
|
|
55
|
+
* Conservative — false positives (treating real shape as chrome) are
|
|
56
|
+
* worse than false negatives.
|
|
57
|
+
*/
|
|
58
|
+
export function stripMockChromeSubtrees(source) {
|
|
59
|
+
let out = source;
|
|
60
|
+
// Pass 1: strip elements opened with data-mock-chrome="true" (and their
|
|
61
|
+
// children up to the matching closing tag).
|
|
62
|
+
out = stripBalancedJsxBlock(out, /<(\w+)\b[^>]*\bdata-mock-chrome\s*=\s*["']true["'][^>]*>/);
|
|
63
|
+
// Pass 2: best-effort — strip the JSX block that immediately follows
|
|
64
|
+
// a `{/* Mock-only ... */}` JSX comment.
|
|
65
|
+
out = stripBalancedJsxBlockAfter(out, /\{\/\*\s*Mock-only[^*]*\*\/\s*\}\s*/);
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Repeatedly find a JSX opening tag matching `openRe` and remove the
|
|
70
|
+
* balanced subtree (open tag + children + close tag).
|
|
71
|
+
*
|
|
72
|
+
* Self-closing tags (e.g. `<Foo />`) are also handled. Tag-name balanced.
|
|
73
|
+
*/
|
|
74
|
+
function stripBalancedJsxBlock(source, openRe) {
|
|
75
|
+
let s = source;
|
|
76
|
+
for (let safety = 0; safety < 64; safety++) {
|
|
77
|
+
const m = openRe.exec(s);
|
|
78
|
+
if (!m)
|
|
79
|
+
break;
|
|
80
|
+
const start = m.index;
|
|
81
|
+
const tagName = m[1];
|
|
82
|
+
const openTag = m[0];
|
|
83
|
+
// Self-closing? remove just that.
|
|
84
|
+
if (openTag.endsWith("/>")) {
|
|
85
|
+
s = s.slice(0, start) + s.slice(start + openTag.length);
|
|
86
|
+
openRe.lastIndex = 0;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
// Walk forward to find balanced close.
|
|
90
|
+
let depth = 1;
|
|
91
|
+
let i = start + openTag.length;
|
|
92
|
+
const openTagRe = new RegExp(`<${tagName}\\b[^>]*?>`, "g");
|
|
93
|
+
const closeTagRe = new RegExp(`</${tagName}\\s*>`, "g");
|
|
94
|
+
while (i < s.length && depth > 0) {
|
|
95
|
+
openTagRe.lastIndex = i;
|
|
96
|
+
closeTagRe.lastIndex = i;
|
|
97
|
+
const oNext = openTagRe.exec(s);
|
|
98
|
+
const cNext = closeTagRe.exec(s);
|
|
99
|
+
if (!cNext)
|
|
100
|
+
break;
|
|
101
|
+
if (oNext && oNext.index < cNext.index) {
|
|
102
|
+
// skip self-closing inside (rare for compound tag)
|
|
103
|
+
if (!oNext[0].endsWith("/>"))
|
|
104
|
+
depth++;
|
|
105
|
+
i = oNext.index + oNext[0].length;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
depth--;
|
|
109
|
+
i = cNext.index + cNext[0].length;
|
|
110
|
+
if (depth === 0) {
|
|
111
|
+
s = s.slice(0, start) + s.slice(i);
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
openRe.lastIndex = 0;
|
|
117
|
+
}
|
|
118
|
+
return s;
|
|
119
|
+
}
|
|
120
|
+
function stripBalancedJsxBlockAfter(source, markerRe) {
|
|
121
|
+
let s = source;
|
|
122
|
+
for (let safety = 0; safety < 32; safety++) {
|
|
123
|
+
const m = markerRe.exec(s);
|
|
124
|
+
if (!m)
|
|
125
|
+
break;
|
|
126
|
+
// Find the JSX opening tag immediately after the marker.
|
|
127
|
+
const after = s.slice(m.index + m[0].length);
|
|
128
|
+
const tagMatch = /^\s*<(\w+)\b[^>]*>/.exec(after);
|
|
129
|
+
if (!tagMatch) {
|
|
130
|
+
// Marker not followed by a JSX tag; just remove the marker comment.
|
|
131
|
+
s = s.slice(0, m.index) + s.slice(m.index + m[0].length);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
// Use stripBalancedJsxBlock at the marker position with the tag-specific regex.
|
|
135
|
+
const openTagAbsoluteIdx = m.index + m[0].length + (after.indexOf("<"));
|
|
136
|
+
const tagName = tagMatch[1];
|
|
137
|
+
if (!tagName) {
|
|
138
|
+
s = s.slice(0, m.index) + s.slice(m.index + m[0].length);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const openTagRe = new RegExp(`<${tagName}\\b[^>]*?>`);
|
|
142
|
+
const sliced = s.slice(openTagAbsoluteIdx);
|
|
143
|
+
const stripped = stripBalancedJsxBlock(sliced, openTagRe);
|
|
144
|
+
s = s.slice(0, m.index) + stripped;
|
|
145
|
+
markerRe.lastIndex = 0;
|
|
146
|
+
}
|
|
147
|
+
return s;
|
|
148
|
+
}
|
|
149
|
+
function extractTestids(source) {
|
|
150
|
+
const out = [];
|
|
151
|
+
for (const m of source.matchAll(/data-testid\s*=\s*["']([^"']+)["']/g)) {
|
|
152
|
+
if (m[1])
|
|
153
|
+
out.push(m[1]);
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
function extractTokens(source, tokens) {
|
|
158
|
+
const out = [];
|
|
159
|
+
for (const t of tokens) {
|
|
160
|
+
// Look in className strings/template literals.
|
|
161
|
+
const re = new RegExp(`className\\s*=\\s*["'\`][^"'\`]*\\b${escapeRegExp(t)}\\b`);
|
|
162
|
+
if (re.test(source))
|
|
163
|
+
out.push(t);
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
167
|
+
function escapeRegExp(s) {
|
|
168
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
169
|
+
}
|
|
170
|
+
export function synthesiseShapeTestFile(opts) {
|
|
171
|
+
const { story, shapes } = opts;
|
|
172
|
+
const lines = [];
|
|
173
|
+
lines.push("// @vitest-environment jsdom");
|
|
174
|
+
lines.push("//");
|
|
175
|
+
lines.push(`// AUTO-GENERATED by slowcook recon (story-${story}). Do not edit by hand.`);
|
|
176
|
+
lines.push("// Asserts STRUCTURAL SHAPE only (DOM containment, testid cardinality,");
|
|
177
|
+
lines.push("// className tokens, CSS-variable usage). Behavioral assertions live in");
|
|
178
|
+
lines.push(`// the testgen-emitted story-${story}-*.test.{ts,tsx} files.`);
|
|
179
|
+
lines.push("//");
|
|
180
|
+
lines.push("// Mock-only chrome (data-mock-chrome=\"true\" subtrees) is excluded.");
|
|
181
|
+
lines.push("// Re-running recon regenerates this file.");
|
|
182
|
+
lines.push("");
|
|
183
|
+
lines.push(`import { describe, it, expect } from "vitest";`);
|
|
184
|
+
lines.push(`import { readFileSync, existsSync } from "node:fs";`);
|
|
185
|
+
lines.push("");
|
|
186
|
+
lines.push(`describe("story-${story} structural shape (recon-emitted)", () => {`);
|
|
187
|
+
// Source-grep based assertions: cheap + always work without a render.
|
|
188
|
+
// The mock's testids/tokens must persist in src/ after brew's edits.
|
|
189
|
+
const allTestids = [...new Set(shapes.flatMap((s) => s.testids))];
|
|
190
|
+
if (allTestids.length === 0 && shapes.every((s) => s.visualTokens.length === 0)) {
|
|
191
|
+
lines.push(` it.skip("no testids/tokens found in mock UI — nothing to assert", () => {});`);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
for (const shape of shapes) {
|
|
195
|
+
const srcPath = mockToSrcPath(shape.file);
|
|
196
|
+
lines.push(` describe("from mock ${shape.file}", () => {`);
|
|
197
|
+
lines.push(` const srcPath = "${srcPath}";`);
|
|
198
|
+
lines.push(` const src = existsSync(srcPath) ? readFileSync(srcPath, "utf8") : "";`);
|
|
199
|
+
lines.push(``);
|
|
200
|
+
for (const tid of shape.testids) {
|
|
201
|
+
lines.push(` it("preserves data-testid='${tid}' (mock-only chrome excluded)", () => {`);
|
|
202
|
+
lines.push(` expect(src).toMatch(/data-testid\\s*=\\s*["']${escapeRegexForTest(tid)}["']/);`);
|
|
203
|
+
lines.push(` });`);
|
|
204
|
+
}
|
|
205
|
+
for (const token of shape.visualTokens) {
|
|
206
|
+
lines.push(` it("preserves visual token '${token}'", () => {`);
|
|
207
|
+
lines.push(` expect(src).toMatch(/className=[^>]*\\b${escapeRegexForTest(token)}\\b/);`);
|
|
208
|
+
lines.push(` });`);
|
|
209
|
+
}
|
|
210
|
+
if (shape.hasHeader) {
|
|
211
|
+
lines.push(` it("retains a <header> element (semantic landmark)", () => {`);
|
|
212
|
+
lines.push(` expect(src).toMatch(/<header\\b/);`);
|
|
213
|
+
lines.push(` });`);
|
|
214
|
+
}
|
|
215
|
+
lines.push(` });`);
|
|
216
|
+
lines.push(``);
|
|
217
|
+
}
|
|
218
|
+
// Anti-wiring assertion: no inline hex colors (force token-family preservation).
|
|
219
|
+
lines.push(` describe("token-family preservation", () => {`);
|
|
220
|
+
for (const shape of shapes) {
|
|
221
|
+
const srcPath = mockToSrcPath(shape.file);
|
|
222
|
+
lines.push(` it("${shape.componentName ?? shape.file}: no inline hex/rgb colors (use var(--) tokens)", () => {`);
|
|
223
|
+
lines.push(` const src = existsSync("${srcPath}") ? readFileSync("${srcPath}", "utf8") : "";`);
|
|
224
|
+
lines.push(` // Allow hex in JSDoc/comments; only flag in className/style attribute values.`);
|
|
225
|
+
lines.push(` const styled = src.match(/(?:className|style)\\s*=\\s*[\"\`'][^\"\`']*?[#][0-9a-fA-F]{3,8}[^\"\`']*?[\"\`']/g) || [];`);
|
|
226
|
+
lines.push(` expect(styled, "no inline hex in className/style").toEqual([]);`);
|
|
227
|
+
lines.push(` });`);
|
|
228
|
+
}
|
|
229
|
+
lines.push(` });`);
|
|
230
|
+
}
|
|
231
|
+
lines.push(`});`);
|
|
232
|
+
return lines.join("\n") + "\n";
|
|
233
|
+
}
|
|
234
|
+
function mockToSrcPath(mockPath) {
|
|
235
|
+
// mock/src/components/X.tsx → src/components/X.tsx
|
|
236
|
+
return mockPath.replace(/^mock\/src\//, "src/");
|
|
237
|
+
}
|
|
238
|
+
function escapeRegexForTest(s) {
|
|
239
|
+
return s.replace(/[.*+?^${}()|[\]\\\/]/g, "\\\\$&");
|
|
240
|
+
}
|
|
241
|
+
// ---- Discovery: which mock files belong to a story ----
|
|
242
|
+
/**
|
|
243
|
+
* Walk mock/src/ + return any file that contains `story-N` in source
|
|
244
|
+
* OR is referenced by a scenario file for the story.
|
|
245
|
+
*
|
|
246
|
+
* For 0.17.6 first cut: simple — any file under mock/src/components/
|
|
247
|
+
* that lives in a directory referenced by the story's scenario.
|
|
248
|
+
* Conservative — caller can pass explicit list via --mock-files.
|
|
249
|
+
*/
|
|
250
|
+
export function findMockFilesForStory(repoRoot, story) {
|
|
251
|
+
const out = new Set();
|
|
252
|
+
// Look for mock/scenarios/story-N*.ts
|
|
253
|
+
const scenariosDir = join(repoRoot, "mock/scenarios");
|
|
254
|
+
if (existsSync(scenariosDir)) {
|
|
255
|
+
for (const name of readdirSync(scenariosDir)) {
|
|
256
|
+
if (!name.startsWith(`story-${story}`))
|
|
257
|
+
continue;
|
|
258
|
+
const body = readFileSync(join(scenariosDir, name), "utf8");
|
|
259
|
+
// Pull `from "@/components/X/Y"` imports
|
|
260
|
+
for (const m of body.matchAll(/from\s+["']@\/(components\/[\w\-./[\]]+)["']/g)) {
|
|
261
|
+
if (m[1]) {
|
|
262
|
+
const candidates = [
|
|
263
|
+
`mock/src/${m[1]}.tsx`,
|
|
264
|
+
`mock/src/${m[1]}.ts`,
|
|
265
|
+
`mock/src/${m[1]}/index.tsx`,
|
|
266
|
+
];
|
|
267
|
+
for (const c of candidates) {
|
|
268
|
+
if (existsSync(join(repoRoot, c)))
|
|
269
|
+
out.add(c);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// Also: any mock component referenced by a story-N test file
|
|
276
|
+
const testsDir = join(repoRoot, "tests/integration");
|
|
277
|
+
if (existsSync(testsDir)) {
|
|
278
|
+
for (const name of readdirSync(testsDir)) {
|
|
279
|
+
if (!name.startsWith(`story-${story}`))
|
|
280
|
+
continue;
|
|
281
|
+
const body = readFileSync(join(testsDir, name), "utf8");
|
|
282
|
+
for (const m of body.matchAll(/from\s+["']@\/(components\/[\w\-./[\]]+)["']/g)) {
|
|
283
|
+
if (m[1]) {
|
|
284
|
+
const candidates = [
|
|
285
|
+
`mock/src/${m[1]}.tsx`,
|
|
286
|
+
`mock/src/${m[1]}.ts`,
|
|
287
|
+
];
|
|
288
|
+
for (const c of candidates) {
|
|
289
|
+
if (existsSync(join(repoRoot, c)))
|
|
290
|
+
out.add(c);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// Transitive walk: keep expanding until no new files added.
|
|
297
|
+
const visited = new Set();
|
|
298
|
+
for (let safety = 0; safety < 20; safety++) {
|
|
299
|
+
let added = false;
|
|
300
|
+
for (const file of [...out]) {
|
|
301
|
+
if (visited.has(file))
|
|
302
|
+
continue;
|
|
303
|
+
visited.add(file);
|
|
304
|
+
const body = readFileSync(join(repoRoot, file), "utf8");
|
|
305
|
+
const dir = file.split("/").slice(0, -1).join("/");
|
|
306
|
+
for (const m of body.matchAll(/from\s+["']\.\/([\w\-./]+)["']/g)) {
|
|
307
|
+
if (!m[1])
|
|
308
|
+
continue;
|
|
309
|
+
const candidates = [
|
|
310
|
+
`${dir}/${m[1]}.tsx`,
|
|
311
|
+
`${dir}/${m[1]}.ts`,
|
|
312
|
+
`${dir}/${m[1]}/index.tsx`,
|
|
313
|
+
];
|
|
314
|
+
for (const c of candidates) {
|
|
315
|
+
if (existsSync(join(repoRoot, c)) && !out.has(c)) {
|
|
316
|
+
out.add(c);
|
|
317
|
+
added = true;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
for (const m of body.matchAll(/from\s+["']@\/(components\/[\w\-./[\]]+)["']/g)) {
|
|
322
|
+
if (!m[1])
|
|
323
|
+
continue;
|
|
324
|
+
const candidates = [`mock/src/${m[1]}.tsx`, `mock/src/${m[1]}.ts`];
|
|
325
|
+
for (const c of candidates) {
|
|
326
|
+
if (existsSync(join(repoRoot, c)) && !out.has(c)) {
|
|
327
|
+
out.add(c);
|
|
328
|
+
added = true;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (!added)
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
return [...out];
|
|
337
|
+
}
|
|
338
|
+
// Re-export for tests
|
|
339
|
+
export { join as _join };
|
|
340
|
+
// re-export to placate tsc unused-import warnings
|
|
341
|
+
void readdirSync;
|
|
342
|
+
void statSync;
|
|
343
|
+
//# sourceMappingURL=shape-preserve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape-preserve.js","sourceRoot":"","sources":["../../../src/commands/recon/shape-preserve.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slowcook-ai/cli",
|
|
3
|
-
"version": "0.17.0-alpha.
|
|
3
|
+
"version": "0.17.0-alpha.6",
|
|
4
4
|
"description": "CLI for the slowcook brewing harness",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "aminazar",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
"zod": "^3.23.8",
|
|
41
41
|
"@slowcook-ai/core": "^0.13.0",
|
|
42
42
|
"@slowcook-ai/stack-ts": "^0.9.8",
|
|
43
|
-
"@slowcook-ai/
|
|
44
|
-
"@slowcook-ai/
|
|
45
|
-
"@slowcook-ai/
|
|
46
|
-
"@slowcook-ai/
|
|
43
|
+
"@slowcook-ai/llm-anthropic": "^0.13.5",
|
|
44
|
+
"@slowcook-ai/forge-github": "^0.11.6",
|
|
45
|
+
"@slowcook-ai/recorder": "^0.9.1",
|
|
46
|
+
"@slowcook-ai/review-overlay": "^0.5.3"
|
|
47
47
|
},
|
|
48
48
|
"publishConfig": {
|
|
49
49
|
"access": "public"
|