@gate-forge/pack-playwright 0.8.0 → 0.9.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.
@@ -0,0 +1,638 @@
1
+ /**
2
+ * Runner file scopes: WHICH files a runner's own configuration collects.
3
+ *
4
+ * The static scan is seeded from Gateforge's own include globs, so it
5
+ * can find test-shaped calls anywhere in the repository — including
6
+ * files the configured runner would never run (a vitest suite in a
7
+ * playwright-configured repository, a spec under a `testDir` the config
8
+ * excludes). Cataloguing those as that runner's tests is wrong twice
9
+ * over: the runner will not execute them, and their assertions were
10
+ * never proved.
11
+ *
12
+ * This module answers the file question from DATA, never from a guess:
13
+ * - Playwright: the enumeration's own project-graph reporter hands us
14
+ * the resolved per-project `testDir`/`testMatch`/`testIgnore` (see
15
+ * `reporter/project-graph-reporter.ts`) — the runner's own words.
16
+ * - Vitest: a bounded, AST-only read of the repository's own
17
+ * `vitest.config.*`/`vite.config.*` literal `test.include` /
18
+ * `test.exclude` / `test.globals` values. No consumer code runs and
19
+ * no expression is evaluated.
20
+ *
21
+ * Every scope is FAIL-OPEN: when a selection cannot be read faithfully
22
+ * (a computed glob, a function-valued selector, a missing config), the
23
+ * scope is not authoritative and selects every file, which is exactly
24
+ * the pre-existing behaviour. Narrowing a catalog on a mis-read glob is
25
+ * the one failure mode this module must never have.
26
+ */
27
+ import { readdirSync, readFileSync } from 'node:fs';
28
+ import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
29
+ import ts from 'typescript';
30
+ /** Vitest's documented default `test.include`. */
31
+ const VITEST_DEFAULT_INCLUDE = ['**/*.{test,spec}.?(c|m)[jt]s?(x)'];
32
+ /** Vitest's documented default `test.exclude`. */
33
+ const VITEST_DEFAULT_EXCLUDE = [
34
+ '**/node_modules/**',
35
+ '**/dist/**',
36
+ '**/cypress/**',
37
+ '**/.{idea,git,cache,output,temp}/**',
38
+ '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*',
39
+ ];
40
+ /** Config file names a vitest repository may declare, in resolution order. */
41
+ const VITEST_CONFIG_NAMES = [
42
+ 'vitest.config.ts',
43
+ 'vitest.config.mts',
44
+ 'vitest.config.cts',
45
+ 'vitest.config.js',
46
+ 'vitest.config.mjs',
47
+ 'vitest.config.cjs',
48
+ 'vite.config.ts',
49
+ 'vite.config.mts',
50
+ 'vite.config.cts',
51
+ 'vite.config.js',
52
+ 'vite.config.mjs',
53
+ 'vite.config.cjs',
54
+ ];
55
+ /** Escapes one character for use inside a regular expression. */
56
+ function escapeRegExp(character) {
57
+ return character.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
58
+ }
59
+ /**
60
+ * Reads a parenthesized group starting at `openIndex` (which must hold
61
+ * `(`), honouring nesting and backslash escapes.
62
+ *
63
+ * Args:
64
+ * pattern: the glob being translated.
65
+ * openIndex: index of the group's opening parenthesis.
66
+ *
67
+ * Returns:
68
+ * {body, end}: the group's body and the index just past its `)`; null
69
+ * when the group is unbalanced.
70
+ */
71
+ function readGroup(pattern, openIndex) {
72
+ let depth = 0;
73
+ for (let index = openIndex; index < pattern.length; index += 1) {
74
+ const character = pattern[index];
75
+ if (character === '\\') {
76
+ index += 1;
77
+ continue;
78
+ }
79
+ if (character === '(')
80
+ depth += 1;
81
+ else if (character === ')') {
82
+ depth -= 1;
83
+ if (depth === 0) {
84
+ return { body: pattern.slice(openIndex + 1, index), end: index + 1 };
85
+ }
86
+ }
87
+ }
88
+ return null;
89
+ }
90
+ /**
91
+ * Finds the `}` closing the `{` at `openIndex`, honouring nesting.
92
+ *
93
+ * Args:
94
+ * pattern: the glob being translated.
95
+ * openIndex: index of the brace group's opening `{`.
96
+ *
97
+ * Returns:
98
+ * number: the index of the matching `}`, or -1 when unbalanced.
99
+ */
100
+ function findClosingBrace(pattern, openIndex) {
101
+ let depth = 0;
102
+ for (let index = openIndex; index < pattern.length; index += 1) {
103
+ const character = pattern[index];
104
+ if (character === '\\') {
105
+ index += 1;
106
+ continue;
107
+ }
108
+ if (character === '{')
109
+ depth += 1;
110
+ else if (character === '}') {
111
+ depth -= 1;
112
+ if (depth === 0)
113
+ return index;
114
+ }
115
+ }
116
+ return -1;
117
+ }
118
+ /**
119
+ * Splits a brace group body on its top-level commas.
120
+ *
121
+ * Args:
122
+ * body: the text between the braces.
123
+ *
124
+ * Returns:
125
+ * string[]: the alternatives; null when a nested group is unbalanced.
126
+ */
127
+ function splitAlternatives(body) {
128
+ const parts = [];
129
+ let depth = 0;
130
+ let current = '';
131
+ for (let index = 0; index < body.length; index += 1) {
132
+ const character = body[index] ?? '';
133
+ if (character === '\\') {
134
+ current += character + (body[index + 1] ?? '');
135
+ index += 1;
136
+ continue;
137
+ }
138
+ if (character === '(' || character === '[')
139
+ depth += 1;
140
+ else if (character === ')' || character === ']')
141
+ depth -= 1;
142
+ else if (character === ',' && depth === 0) {
143
+ parts.push(current);
144
+ current = '';
145
+ continue;
146
+ }
147
+ if (depth < 0)
148
+ return null;
149
+ current += character;
150
+ }
151
+ if (depth !== 0)
152
+ return null;
153
+ parts.push(current);
154
+ return parts;
155
+ }
156
+ /**
157
+ * Translates one glob into a regular-expression source.
158
+ *
159
+ * Supported: `**`, `*`, `?`, character classes, brace alternation
160
+ * (`{a,b}`), and the extglob forms `@(a|b)`, `?(a|b)`, `+(a|b)` — which
161
+ * together cover the runners' own documented selection globs, such as
162
+ * playwright's default test glob (a globstar, `*.@(spec|test)` and an
163
+ * optional `.(c|m)[jt]s?(x)` extension). Anything else (a negated
164
+ * class or extglob, an unbalanced group) yields null, and null means
165
+ * "this pattern cannot narrow" — never "this pattern matches
166
+ * nothing".
167
+ *
168
+ * Args:
169
+ * pattern: the glob to translate.
170
+ *
171
+ * Returns:
172
+ * string: the regexp source, or null when it cannot be translated.
173
+ */
174
+ function translateGlob(pattern) {
175
+ let source = '';
176
+ let index = 0;
177
+ while (index < pattern.length) {
178
+ const character = pattern[index] ?? '';
179
+ if (character === '\\') {
180
+ const next = pattern[index + 1];
181
+ if (next === undefined)
182
+ return null;
183
+ source += escapeRegExp(next);
184
+ index += 2;
185
+ continue;
186
+ }
187
+ if (character === '*') {
188
+ let stars = 0;
189
+ while (pattern[index] === '*') {
190
+ stars += 1;
191
+ index += 1;
192
+ }
193
+ if (stars >= 2) {
194
+ if (pattern[index] === '/') {
195
+ source += '(?:.*/)?';
196
+ index += 1;
197
+ }
198
+ else {
199
+ source += '.*';
200
+ }
201
+ }
202
+ else {
203
+ source += '[^/]*';
204
+ }
205
+ continue;
206
+ }
207
+ if (character === '?') {
208
+ if (pattern[index + 1] === '(') {
209
+ const group = readGroup(pattern, index + 1);
210
+ if (group === null)
211
+ return null;
212
+ const inner = translateGlob(group.body);
213
+ if (inner === null)
214
+ return null;
215
+ source += `(?:${inner})?`;
216
+ index = group.end;
217
+ continue;
218
+ }
219
+ source += '[^/]';
220
+ index += 1;
221
+ continue;
222
+ }
223
+ if ((character === '@' || character === '+') && pattern[index + 1] === '(') {
224
+ const group = readGroup(pattern, index + 1);
225
+ if (group === null)
226
+ return null;
227
+ const inner = translateGlob(group.body);
228
+ if (inner === null)
229
+ return null;
230
+ source += character === '@' ? `(?:${inner})` : `(?:${inner})+`;
231
+ index = group.end;
232
+ continue;
233
+ }
234
+ if (character === '[') {
235
+ const end = pattern.indexOf(']', index + 1);
236
+ if (end === -1)
237
+ return null;
238
+ source += pattern.slice(index, end + 1);
239
+ index = end + 1;
240
+ continue;
241
+ }
242
+ if (character === '{') {
243
+ const end = findClosingBrace(pattern, index);
244
+ if (end === -1)
245
+ return null;
246
+ const alternatives = splitAlternatives(pattern.slice(index + 1, end));
247
+ if (alternatives === null)
248
+ return null;
249
+ const parts = [];
250
+ for (const alternative of alternatives) {
251
+ const fragment = translateGlob(alternative);
252
+ if (fragment === null)
253
+ return null;
254
+ parts.push(fragment);
255
+ }
256
+ source += `(?:${parts.join('|')})`;
257
+ index = end + 1;
258
+ continue;
259
+ }
260
+ // A `!` introduces a negated class or a `!(…)` extglob; neither has a
261
+ // sound translation here, and a wrong one would drop real tests.
262
+ if (character === '!')
263
+ return null;
264
+ source += escapeRegExp(character);
265
+ index += 1;
266
+ }
267
+ return source;
268
+ }
269
+ /** Compiled selection globs, memoized per pattern. */
270
+ const compiledGlobs = new Map();
271
+ /**
272
+ * Compiles one selection glob, memoized per pattern.
273
+ *
274
+ * Args:
275
+ glob: the selection glob.
276
+ *
277
+ * Returns:
278
+ RegExp | null: the compiled matcher, or null when the pattern cannot
279
+ be translated faithfully (never "matches nothing").
280
+ */
281
+ function compileGlobPattern(glob) {
282
+ if (compiledGlobs.has(glob))
283
+ return compiledGlobs.get(glob) ?? null;
284
+ const source = translateGlob(glob);
285
+ let compiled = null;
286
+ if (source !== null) {
287
+ try {
288
+ compiled = new RegExp(`^${source}$`);
289
+ }
290
+ catch {
291
+ compiled = null;
292
+ }
293
+ }
294
+ compiledGlobs.set(glob, compiled);
295
+ return compiled;
296
+ }
297
+ /**
298
+ * Whether one glob selects a repo-relative path.
299
+ *
300
+ * Args:
301
+ * glob: the selection glob.
302
+ * file: repo-relative posix path, relative to the scope root.
303
+ *
304
+ * Returns:
305
+ * boolean: false only when the pattern is translatable and does NOT
306
+ * match. An untranslatable pattern returns true so a mis-read glob can
307
+ * never narrow the catalog.
308
+ */
309
+ function globSelectsFile(glob, file) {
310
+ const compiled = compileGlobPattern(glob);
311
+ if (compiled === null)
312
+ return true;
313
+ return compiled.test(file);
314
+ }
315
+ /**
316
+ * Whether a runner's own configuration collects this file.
317
+ *
318
+ * Args:
319
+ * scope: the runner's selection.
320
+ * cwd: absolute repo root.
321
+ * repoRelativeFile: repo-relative posix path.
322
+ *
323
+ * Returns:
324
+ * boolean: true when the file is one of the runner's test files. A
325
+ * non-authoritative scope always returns true (fail open).
326
+ */
327
+ export function scopeSelectsFile(scope, cwd, repoRelativeFile) {
328
+ if (!scope.authoritative)
329
+ return true;
330
+ const absolute = isAbsolute(repoRelativeFile) ? repoRelativeFile : resolve(cwd, repoRelativeFile);
331
+ const file = relative(scope.root, absolute).split('\\').join('/');
332
+ if (file === '' || file.startsWith('../'))
333
+ return false;
334
+ if (scope.include.length > 0 && !scope.include.some((glob) => globSelectsFile(glob, file))) {
335
+ return false;
336
+ }
337
+ return !scope.exclude.some((glob) => globSelectsFile(glob, file));
338
+ }
339
+ /**
340
+ * Whether ANY of the runner's scopes collects the file — the union over
341
+ * projects, which is how a runner collects a file at all.
342
+ */
343
+ export function anyScopeSelectsFile(scopes, cwd, repoRelativeFile) {
344
+ if (scopes.length === 0)
345
+ return true;
346
+ return scopes.some((scope) => scopeSelectsFile(scope, cwd, repoRelativeFile));
347
+ }
348
+ /**
349
+ * The playwright scopes the enumeration's runner resolved.
350
+ *
351
+ * Args:
352
+ * projects: the per-project selection the project-graph reporter read.
353
+ *
354
+ * Returns:
355
+ * RunnerFileScope[]: one scope per project whose selection was data.
356
+ * Empty when the runner resolved none, which selects nothing away.
357
+ */
358
+ export function playwrightFileScopes(projects) {
359
+ const scopes = [];
360
+ for (const project of projects) {
361
+ const include = project.testMatch.map((glob) => glob.replace(/^\.\//, ''));
362
+ const exclude = project.testIgnore.map((glob) => glob.replace(/^\.\//, ''));
363
+ // A selection this boundary cannot read as DATA — a root that is not
364
+ // a location, a pattern with no sound translation — makes the whole
365
+ // scope fail open (every file) rather than narrow on a guess, and
366
+ // says so in `note` so a wide selection is never mistaken for a
367
+ // narrow one.
368
+ const unreadable = [
369
+ ...(isAbsolute(project.testDir) ? [] : [`testDir '${project.testDir}' is not an absolute path`]),
370
+ ...[...include, ...exclude]
371
+ .filter((glob) => compileGlobPattern(glob) === null)
372
+ .map((glob) => `selection pattern '${glob}' is not a translatable glob`),
373
+ ];
374
+ scopes.push({
375
+ runner: 'playwright',
376
+ root: project.testDir,
377
+ include,
378
+ exclude,
379
+ globals: false,
380
+ authoritative: unreadable.length === 0,
381
+ ...(unreadable.length > 0 ? { note: `${unreadable.join('; ')} — this project collects every file` } : {}),
382
+ });
383
+ }
384
+ return scopes;
385
+ }
386
+ /** Reads a string-literal node, else undefined. */
387
+ function literalString(node) {
388
+ if (node === undefined)
389
+ return undefined;
390
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
391
+ return node.text;
392
+ return undefined;
393
+ }
394
+ /** Reads a boolean-literal node, else undefined. */
395
+ function literalBoolean(node) {
396
+ if (node === undefined)
397
+ return undefined;
398
+ if (node.kind === ts.SyntaxKind.TrueKeyword)
399
+ return true;
400
+ if (node.kind === ts.SyntaxKind.FalseKeyword)
401
+ return false;
402
+ return undefined;
403
+ }
404
+ /** Reads a string or string-array literal, else undefined. */
405
+ function literalStrings(node) {
406
+ if (node === undefined)
407
+ return undefined;
408
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
409
+ return [node.text];
410
+ if (!ts.isArrayLiteralExpression(node))
411
+ return undefined;
412
+ const values = [];
413
+ for (const element of node.elements) {
414
+ const value = literalString(element);
415
+ if (value === undefined)
416
+ return undefined;
417
+ values.push(value);
418
+ }
419
+ return values;
420
+ }
421
+ /** The `test` property of an object literal, when it is one itself. */
422
+ function propertyOf(object, name) {
423
+ for (const member of object.properties) {
424
+ if (!ts.isPropertyAssignment(member))
425
+ continue;
426
+ const key = member.name;
427
+ const keyName = ts.isIdentifier(key) || ts.isStringLiteral(key) ? key.text : undefined;
428
+ if (keyName === name)
429
+ return member.initializer;
430
+ }
431
+ return undefined;
432
+ }
433
+ /** Directories never descended into when looking for runner configs. */
434
+ const CONFIG_SEARCH_PRUNED_DIRS = new Set([
435
+ '.git',
436
+ 'node_modules',
437
+ 'dist',
438
+ 'build',
439
+ 'out',
440
+ 'coverage',
441
+ 'test-results',
442
+ 'playwright-report',
443
+ ]);
444
+ /** How deep below the repo root a runner config is still read from. */
445
+ const MAX_CONFIG_SEARCH_DEPTH = 4;
446
+ /**
447
+ * Every vitest configuration the repository declares, at any depth a
448
+ * monorepo keeps one (a repo whose frontend owns `frontend/vitest.config.js`
449
+ * selects files under `frontend/`, not under the repo root).
450
+ *
451
+ * Args:
452
+ * cwd: absolute repo root.
453
+ *
454
+ * Returns:
455
+ * string[]: absolute config paths, sorted; empty when the repository
456
+ * declares none (the scope is then unknown, never empty).
457
+ */
458
+ function findVitestConfigFiles(cwd) {
459
+ const found = [];
460
+ const walk = (dir, depth) => {
461
+ if (depth > MAX_CONFIG_SEARCH_DEPTH)
462
+ return;
463
+ let entries;
464
+ try {
465
+ entries = readdirSync(dir, { withFileTypes: true });
466
+ }
467
+ catch {
468
+ return; // unreadable dir: invisible, never a failure of other configs
469
+ }
470
+ for (const entry of [...entries].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))) {
471
+ // Symlinked directories are NOT followed: a link can leave the
472
+ // repository, and a config outside the repo root selects nothing here.
473
+ if (entry.isDirectory()) {
474
+ if (!CONFIG_SEARCH_PRUNED_DIRS.has(entry.name))
475
+ walk(join(dir, entry.name), depth + 1);
476
+ }
477
+ else if (entry.isFile() && VITEST_CONFIG_NAMES.includes(entry.name)) {
478
+ found.push(join(dir, entry.name));
479
+ }
480
+ }
481
+ };
482
+ walk(cwd, 0);
483
+ return found.sort();
484
+ }
485
+ /**
486
+ * The config object literal of a runner config file, or null when it is
487
+ * not one literal (`module.exports = merge(a, b)`).
488
+ */
489
+ function configObjectOf(parsed) {
490
+ let config = null;
491
+ const visit = (node) => {
492
+ if (config !== null)
493
+ return;
494
+ // `export default { … }` — the object literal is the config itself.
495
+ if (ts.isExportAssignment(node) && ts.isObjectLiteralExpression(node.expression)) {
496
+ config = node.expression;
497
+ return;
498
+ }
499
+ // `export default defineConfig({ … })` / `mergeConfig(a, { … })`:
500
+ // unwrap the first object literal argument.
501
+ if (ts.isExportAssignment(node) &&
502
+ ts.isCallExpression(node.expression) &&
503
+ node.expression.arguments[0] !== undefined &&
504
+ ts.isObjectLiteralExpression(node.expression.arguments[0])) {
505
+ config = node.expression.arguments[0];
506
+ return;
507
+ }
508
+ // The CommonJS transpiled shape: `exports.default = { … }`. `=` is the
509
+ // only assignment operator; anything else is a comparison.
510
+ if (ts.isBinaryExpression(node) &&
511
+ node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
512
+ ts.isObjectLiteralExpression(node.right) &&
513
+ ts.isPropertyAccessExpression(node.left) &&
514
+ node.left.name.text === 'default') {
515
+ config = node.right;
516
+ return;
517
+ }
518
+ ts.forEachChild(node, visit);
519
+ };
520
+ visit(parsed);
521
+ return config;
522
+ }
523
+ /**
524
+ * The vitest selection one config file declares, read with the
525
+ * TypeScript AST only — the same bounded, non-executing read the static
526
+ * test scan already performs on candidate source. No consumer code runs
527
+ * and no expression is evaluated.
528
+ *
529
+ * A config that cannot be read as DATA (unreadable file, a computed
530
+ * `include: buildGlobs()`, `mergeConfig(a, b)`) yields NO scope at all:
531
+ * for a foreign runner, claiming nothing leaves the file with the
532
+ * configured runner exactly as before, which is the fail-open answer.
533
+ *
534
+ * Args:
535
+ * absolute: absolute path of the config file.
536
+ *
537
+ * Returns:
538
+ * RunnerFileScope | null: the declared selection, or null when this
539
+ * file declares none that can be read (a `vite.config.*` without a
540
+ * `test` block is a BUILD config: it configures no test runner).
541
+ */
542
+ function vitestScopeOf(absolute) {
543
+ const name = basename(absolute);
544
+ // `vitest.config.*` declares a vitest run even without a `test` block
545
+ // (its defaults then apply); `vite.config.*` only does once it
546
+ // configures `test`.
547
+ const declaresVitest = name.startsWith('vitest.config.');
548
+ let source;
549
+ try {
550
+ source = readFileSync(absolute, 'utf8');
551
+ }
552
+ catch {
553
+ return null;
554
+ }
555
+ const configRoot = dirname(absolute);
556
+ const defaultScope = {
557
+ runner: 'vitest',
558
+ root: configRoot,
559
+ include: [...VITEST_DEFAULT_INCLUDE],
560
+ exclude: [...VITEST_DEFAULT_EXCLUDE],
561
+ globals: false,
562
+ authoritative: true,
563
+ };
564
+ const config = configObjectOf(ts.createSourceFile(name, source, ts.ScriptTarget.Latest, true));
565
+ const testNode = config === null ? undefined : propertyOf(config, 'test');
566
+ if (testNode === undefined || !ts.isObjectLiteralExpression(testNode)) {
567
+ // A config with no readable `test` block still runs vitest's
568
+ // documented defaults when the file names vitest itself; a
569
+ // `vite.config.*` without `test` configures a bundler, not a runner.
570
+ return declaresVitest ? defaultScope : null;
571
+ }
572
+ const root = literalString(config === null ? undefined : propertyOf(config, 'root'));
573
+ const base = root === undefined ? configRoot : isAbsolute(root) ? root : resolve(configRoot, root);
574
+ const dir = literalString(propertyOf(testNode, 'dir'));
575
+ const scopeRoot = dir === undefined ? base : isAbsolute(dir) ? dir : resolve(base, dir);
576
+ const includeNode = propertyOf(testNode, 'include');
577
+ const excludeNode = propertyOf(testNode, 'exclude');
578
+ const globalsNode = propertyOf(testNode, 'globals');
579
+ const include = includeNode === undefined ? [...VITEST_DEFAULT_INCLUDE] : literalStrings(includeNode);
580
+ const exclude = excludeNode === undefined ? [...VITEST_DEFAULT_EXCLUDE] : literalStrings(excludeNode);
581
+ const globals = globalsNode === undefined ? false : literalBoolean(globalsNode);
582
+ // A computed selection is not data this boundary may narrow on.
583
+ if (include === undefined || exclude === undefined || globals === undefined)
584
+ return null;
585
+ return { runner: 'vitest', root: scopeRoot, include, exclude, globals, authoritative: true };
586
+ }
587
+ /**
588
+ * The repository's own vitest test-file selections, read from every
589
+ * vitest config it declares (root first in resolution order, then nested
590
+ * configs, each scoped to its own directory).
591
+ *
592
+ * Args:
593
+ * cwd: absolute repo root.
594
+ *
595
+ * Returns:
596
+ * RunnerFileScope[]: one scope per readable config; empty when the
597
+ * repository declares no vitest selection this boundary can read.
598
+ */
599
+ export function vitestFileScopes(cwd) {
600
+ const scopes = [];
601
+ for (const absolute of findVitestConfigFiles(cwd)) {
602
+ const scope = vitestScopeOf(absolute);
603
+ if (scope !== null)
604
+ scopes.push(scope);
605
+ }
606
+ return scopes;
607
+ }
608
+ /**
609
+ * Which runner owns a statically discovered file.
610
+ *
611
+ * A foreign scope that is not authoritative claims NOTHING: fail-open
612
+ * for the configured runner means "keep today's behaviour", and a
613
+ * mis-read foreign config must never steal that runner's files.
614
+ *
615
+ * Args:
616
+ * configuredRunner: the repository's configured runner.
617
+ * configuredScopes: that runner's own scopes (empty ⇒ unknown).
618
+ * otherScopes: the other runners' own scopes.
619
+ * cwd: absolute repo root.
620
+ * file: repo-relative posix path.
621
+ *
622
+ * Returns:
623
+ * string | null: the owning runner, or null when no runner's own
624
+ * configuration claims the file — the file is then not that runner's
625
+ * test, and the caller reports it instead of inventing a runner.
626
+ */
627
+ export function runnerForFile(configuredRunner, configuredScopes, otherScopes, cwd, file) {
628
+ if (anyScopeSelectsFile(configuredScopes, cwd, file))
629
+ return configuredRunner;
630
+ for (const scope of otherScopes) {
631
+ if (!scope.authoritative)
632
+ continue;
633
+ if (scopeSelectsFile(scope, cwd, file))
634
+ return scope.runner;
635
+ }
636
+ return null;
637
+ }
638
+ //# sourceMappingURL=runner-file-scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-file-scope.js","sourceRoot":"","sources":["../../src/discovery/runner-file-scope.ts"],"names":[],"mappings":"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"}
@@ -39,6 +39,17 @@ export interface StaticScanOptions {
39
39
  * harvested back as a test the repository never declared.
40
40
  */
41
41
  excludeFile?: RepoRelativeFileFilter;
42
+ /**
43
+ * Per-file runner-globals predicate (repo-relative posix paths).
44
+ *
45
+ * TRUE when the file's own runner injects `test`/`it`/`describe` as
46
+ * GLOBALS (vitest `globals: true`): in such a file a bare
47
+ * `it('…')`/`test('…')` IS a test registration the runner owns, not
48
+ * an alias the scan could not resolve. Without this predicate every
49
+ * unbound call stays `unresolved-test-alias`, which is what a file
50
+ * without runner globals must keep.
51
+ */
52
+ testGlobals?: RepoRelativeFileFilter;
42
53
  /** Traversal budgets (defaults documented on {@link ScanBudget}). */
43
54
  budget?: ScanBudget;
44
55
  }