Skip to content

Commit 5dc6c3b

Browse files
committed
[compiler][snap] Support pattern of files to test as CLI argument
Vibe-coded. When trying to use vibe coding on actual compiler work, the agent kept wanting to do `yarn snap <test>` to get the output of a file. It was tedious to tell it to use the testfilter file. This lets us quickly run a set of files or a specific path and get debug output. Open to feedback!
1 parent 1254ecf commit 5dc6c3b

File tree

2 files changed

+66
-11
lines changed

2 files changed

+66
-11
lines changed

compiler/packages/snap/src/fixture-utils.ts

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,21 @@ function stripExtension(filename: string, extensions: Array<string>): string {
4444
return filename;
4545
}
4646

47+
/**
48+
* Strip all extensions from a filename
49+
* e.g., "foo.expect.md" -> "foo"
50+
*/
51+
function stripAllExtensions(filename: string): string {
52+
let result = filename;
53+
while (true) {
54+
const extension = path.extname(result);
55+
if (extension === '') {
56+
return result;
57+
}
58+
result = path.basename(result, extension);
59+
}
60+
}
61+
4762
export async function readTestFilter(): Promise<TestFilter | null> {
4863
if (!(await exists(FILTER_PATH))) {
4964
throw new Error(`testfilter file not found at \`${FILTER_PATH}\``);
@@ -111,11 +126,25 @@ async function readInputFixtures(
111126
} else {
112127
inputFiles = (
113128
await Promise.all(
114-
filter.paths.map(pattern =>
115-
glob.glob(`${pattern}{${INPUT_EXTENSIONS.join(',')}}`, {
129+
filter.paths.map(pattern => {
130+
// If the pattern already has an extension other than .expect.md,
131+
// search for the pattern directly. Otherwise, search for the
132+
// pattern with the expected input extensions added.
133+
// Eg
134+
// `alias-while` => search for `alias-while{.js,.jsx,.ts,.tsx}`
135+
// `alias-while.js` => search as-is
136+
// `alias-while.expect.md` => search for `alias-while{.js,.jsx,.ts,.tsx}`
137+
const basename = path.basename(pattern);
138+
const basenameWithoutExt = stripAllExtensions(basename);
139+
const hasExtension = basename !== basenameWithoutExt;
140+
const globPattern =
141+
hasExtension && !pattern.endsWith(SNAPSHOT_EXTENSION)
142+
? pattern
143+
: `${basenameWithoutExt}{${INPUT_EXTENSIONS.join(',')}}`;
144+
return glob.glob(globPattern, {
116145
cwd: rootDir,
117-
}),
118-
),
146+
});
147+
}),
119148
)
120149
).flat();
121150
}
@@ -150,11 +179,13 @@ async function readOutputFixtures(
150179
} else {
151180
outputFiles = (
152181
await Promise.all(
153-
filter.paths.map(pattern =>
154-
glob.glob(`${pattern}${SNAPSHOT_EXTENSION}`, {
182+
filter.paths.map(pattern => {
183+
// Strip all extensions and find matching .expect.md files
184+
const basenameWithoutExt = stripAllExtensions(pattern);
185+
return glob.glob(`${basenameWithoutExt}${SNAPSHOT_EXTENSION}`, {
155186
cwd: rootDir,
156-
}),
157-
),
187+
});
188+
}),
158189
)
159190
).flat();
160191
}

compiler/packages/snap/src/runner.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type RunnerOptions = {
3535
watch: boolean;
3636
filter: boolean;
3737
update: boolean;
38+
pattern?: string;
3839
};
3940

4041
const opts: RunnerOptions = yargs
@@ -62,9 +63,15 @@ const opts: RunnerOptions = yargs
6263
'Only run fixtures which match the contents of testfilter.txt',
6364
)
6465
.default('filter', false)
66+
.string('pattern')
67+
.alias('p', 'pattern')
68+
.describe(
69+
'pattern',
70+
'Optional glob pattern to filter fixtures (e.g., "error.*", "use-memo")',
71+
)
6572
.help('help')
6673
.strict()
67-
.parseSync(hideBin(process.argv));
74+
.parseSync(hideBin(process.argv)) as RunnerOptions;
6875

6976
/**
7077
* Do a test run and return the test results
@@ -171,7 +178,13 @@ export async function main(opts: RunnerOptions): Promise<void> {
171178
worker.getStderr().pipe(process.stderr);
172179
worker.getStdout().pipe(process.stdout);
173180

174-
if (opts.watch) {
181+
// If pattern is provided, force watch mode off and use pattern filter
182+
const shouldWatch = opts.watch && opts.pattern == null;
183+
if (opts.watch && opts.pattern != null) {
184+
console.warn('NOTE: --watch is ignored when a --pattern is supplied');
185+
}
186+
187+
if (shouldWatch) {
175188
makeWatchRunner(state => onChange(worker, state), opts.filter);
176189
if (opts.filter) {
177190
/**
@@ -216,7 +229,18 @@ export async function main(opts: RunnerOptions): Promise<void> {
216229
try {
217230
execSync('yarn build', {cwd: PROJECT_ROOT});
218231
console.log('Built compiler successfully with tsup');
219-
const testFilter = opts.filter ? await readTestFilter() : null;
232+
233+
// Determine which filter to use
234+
let testFilter: TestFilter | null = null;
235+
if (opts.pattern) {
236+
testFilter = {
237+
debug: true,
238+
paths: [opts.pattern],
239+
};
240+
} else if (opts.filter) {
241+
testFilter = await readTestFilter();
242+
}
243+
220244
const results = await runFixtures(worker, testFilter, 0);
221245
if (opts.update) {
222246
update(results);

0 commit comments

Comments
 (0)