@molecule/api-ai-tools 1.16.2 → 1.16.3
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 +37 -1
- package/dist/browser-probe.d.ts.map +1 -1
- package/dist/browser-probe.js +1 -211
- package/dist/source-code.d.ts +17 -0
- package/dist/source-code.d.ts.map +1 -0
- package/dist/source-code.js +214 -0
- package/dist/suite-overlap.d.ts +24 -0
- package/dist/suite-overlap.d.ts.map +1 -1
- package/dist/suite-overlap.js +53 -17
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +3 -7
- package/dist/trace-artifacts.d.ts +6 -0
- package/dist/trace-artifacts.d.ts.map +1 -1
- package/dist/trace-artifacts.js +196 -106
- package/dist/utilities.d.ts.map +1 -1
- package/dist/utilities.js +8 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
|
3
3
|
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
4
|
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
5
|
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
-
Generated: 2026-11-
|
|
6
|
+
Generated: 2026-11-04T11:19:09.206Z
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
9
|
# @molecule/api-ai-tools
|
|
@@ -2719,6 +2719,20 @@ function suiteCommandTarget(command: string): string
|
|
|
2719
2719
|
|
|
2720
2720
|
**Returns:** The target key.
|
|
2721
2721
|
|
|
2722
|
+
#### `playwrightCommandTarget(command, last)`
|
|
2723
|
+
|
|
2724
|
+
The target of the actual Playwright step, including inherited exports. URLs and option names
|
|
2725
|
+
in another program or a quoted title cannot decide whether this step is deployed.
|
|
2726
|
+
|
|
2727
|
+
```typescript
|
|
2728
|
+
function playwrightCommandTarget(command: string, last?: boolean): string
|
|
2729
|
+
```
|
|
2730
|
+
|
|
2731
|
+
- `command` — The shell command.
|
|
2732
|
+
- `last` — Read the last Playwright invocation, for the background launch rewrite.
|
|
2733
|
+
|
|
2734
|
+
**Returns:** The comparable target key, or local when no target setting is present.
|
|
2735
|
+
|
|
2722
2736
|
#### `namesSuiteConfig(command)`
|
|
2723
2737
|
|
|
2724
2738
|
Whether a Playwright statement names its own config — the reading {@link withSuiteConfig} and
|
|
@@ -2822,6 +2836,27 @@ function withRunEnv(command: string, settings?: readonly RunEnvSetting[]): strin
|
|
|
2822
2836
|
|
|
2823
2837
|
**Returns:** The command, or the command unchanged when it already carries every setting.
|
|
2824
2838
|
|
|
2839
|
+
#### `suiteRunDirectory(command, cwd)`
|
|
2840
|
+
|
|
2841
|
+
The absolute directory the Playwright statement in a command runs in, or null when the command
|
|
2842
|
+
does not say.
|
|
2843
|
+
|
|
2844
|
+
The reporter prints a failing test's file relative to the process directory the runner started
|
|
2845
|
+
in (`../fixtures/…` from a run whose suite lives outside its own tree), so the directory has to
|
|
2846
|
+
come from the command itself: `exec_command`'s `cwd` when it was given, then each `cd` before
|
|
2847
|
+
the runner. Only a literal `cd` is followed — a target holding a variable, a glob or `~`
|
|
2848
|
+
resolves nowhere useful, and a guessed absolute path is worse than the relative one the
|
|
2849
|
+
reporter printed.
|
|
2850
|
+
|
|
2851
|
+
```typescript
|
|
2852
|
+
function suiteRunDirectory(command: string, cwd?: string | null): string | null
|
|
2853
|
+
```
|
|
2854
|
+
|
|
2855
|
+
- `command` — The shell command.
|
|
2856
|
+
- `cwd` — The `cwd` the tool was given, when it was.
|
|
2857
|
+
|
|
2858
|
+
**Returns:** The absolute directory, or null.
|
|
2859
|
+
|
|
2825
2860
|
#### `resolvePlaywrightSelectionPaths(command, cwd)`
|
|
2826
2861
|
|
|
2827
2862
|
Resolve literal `./`/`../` Playwright file selections against the runner's own directory.
|
|
@@ -3410,6 +3445,7 @@ function diagnosisTraceCommand(args: {
|
|
|
3410
3445
|
command: string
|
|
3411
3446
|
cwd: string
|
|
3412
3447
|
readFile: (path: string) => Promise<string>
|
|
3448
|
+
readDir?: ReadConfigDirectory
|
|
3413
3449
|
}): Promise<DiagnosisTraceRewrite>
|
|
3414
3450
|
```
|
|
3415
3451
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-probe.d.ts","sourceRoot":"","sources":["../src/browser-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;
|
|
1
|
+
{"version":3,"file":"browser-probe.d.ts","sourceRoot":"","sources":["../src/browser-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAIH,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED,iEAAiE;AACjE,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;IAC9D,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AA8ND;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAyB7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,sBAAsB,EAC7B,aAAa,EAAE,MAAM,EACrB,KAAK,UAAQ,GACZ,MAAM,CAaR;AAED;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,sBAAsB,GAAG,MAAM,CASlF"}
|
package/dist/browser-probe.js
CHANGED
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
*
|
|
37
37
|
* @module
|
|
38
38
|
*/
|
|
39
|
+
import { codeWithModuleSpecifiers as codeOnly } from './source-code.js';
|
|
39
40
|
/** A `const|let|var <name> = await [qualifier.]chromium|firefox|webkit.launch(` binding. */
|
|
40
41
|
const LAUNCH = /(?<![\w$.])(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*(?::[^=\n]+)?=\s*await\s+(?:([A-Za-z_$][\w$]*)\s*\.\s*)?(chromium|firefox|webkit)\s*\.\s*launch\s*\(/g;
|
|
41
42
|
/** A browser method that does not close the browser, so calling it leaves the handle alive. */
|
|
@@ -57,217 +58,6 @@ const ANY_CLEANUP = /\.\s*(?:close|disconnect|dispose)\s*\(|[A-Za-z_$][\w$]*\s*\
|
|
|
57
58
|
const CLEANUP_HINT = /cleanup|teardown|close|dispose|shutdown|quit/i;
|
|
58
59
|
/** The packages whose `chromium`/`firefox`/`webkit` export really launches a browser. */
|
|
59
60
|
const PLAYWRIGHT_PACKAGE = '(?:playwright-core|playwright|@playwright/test)';
|
|
60
|
-
/** The characters after which a `/` starts a regex literal rather than a division. */
|
|
61
|
-
const REGEX_AFTER = new Set([
|
|
62
|
-
'',
|
|
63
|
-
'(',
|
|
64
|
-
'[',
|
|
65
|
-
'{',
|
|
66
|
-
',',
|
|
67
|
-
';',
|
|
68
|
-
':',
|
|
69
|
-
'=',
|
|
70
|
-
'!',
|
|
71
|
-
'&',
|
|
72
|
-
'|',
|
|
73
|
-
'?',
|
|
74
|
-
'+',
|
|
75
|
-
'-',
|
|
76
|
-
'*',
|
|
77
|
-
'%',
|
|
78
|
-
'^',
|
|
79
|
-
'~',
|
|
80
|
-
'<',
|
|
81
|
-
'>',
|
|
82
|
-
'}',
|
|
83
|
-
]);
|
|
84
|
-
/**
|
|
85
|
-
* A source with every comment and every string, template and regex literal blanked to spaces,
|
|
86
|
-
* preserving offsets and newlines so a match's position still indexes the original source.
|
|
87
|
-
*
|
|
88
|
-
* A launch mentioned in prose is not a launch: a review proved scripts that only PRINT
|
|
89
|
-
* `const browser = await chromium.launch()` — in a string, a comment and a template literal — and
|
|
90
|
-
* a raw substring match refused all of them. Template `${…}` substitutions stay code (a `close()`
|
|
91
|
-
* written there is a real cleanup path), nested braces are tracked so the substitution's end is
|
|
92
|
-
* found, and regex literals are recognised by the token before them so a quote inside a character
|
|
93
|
-
* class cannot blank the rest of the file.
|
|
94
|
-
*
|
|
95
|
-
* @param source - The script's source.
|
|
96
|
-
* @returns The same text with literal/comment spans replaced by spaces.
|
|
97
|
-
*/
|
|
98
|
-
function codeOnly(source) {
|
|
99
|
-
const out = source.split('');
|
|
100
|
-
const blank = (from, to) => {
|
|
101
|
-
for (let i = from; i < to; i++)
|
|
102
|
-
if (out[i] !== '\n')
|
|
103
|
-
out[i] = ' ';
|
|
104
|
-
};
|
|
105
|
-
const stack = [{ mode: 'code', braces: 0 }];
|
|
106
|
-
const top = () => stack[stack.length - 1];
|
|
107
|
-
/** The last non-whitespace CODE character: decides whether a `/` opens a regex. */
|
|
108
|
-
let lastCode = '';
|
|
109
|
-
/** The last identifier word and the word being read — a module specifier follows `from`. */
|
|
110
|
-
let lastWord = '';
|
|
111
|
-
let word = '';
|
|
112
|
-
/** A specifier (`from 'playwright'`, `require('playwright')`) stays readable to provenance. */
|
|
113
|
-
const specifierAhead = () => lastWord === 'from' || lastWord === 'require' || lastWord === 'import';
|
|
114
|
-
let i = 0;
|
|
115
|
-
while (i < source.length) {
|
|
116
|
-
const frame = top();
|
|
117
|
-
const char = source[i] ?? '';
|
|
118
|
-
const next = source[i + 1];
|
|
119
|
-
if (frame.mode === 'code') {
|
|
120
|
-
if (char === '/' && next === '/') {
|
|
121
|
-
stack.push({ mode: 'line' });
|
|
122
|
-
blank(i, i + 2);
|
|
123
|
-
i += 2;
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
if (char === '/' && next === '*') {
|
|
127
|
-
stack.push({ mode: 'block' });
|
|
128
|
-
blank(i, i + 2);
|
|
129
|
-
i += 2;
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
if (char === "'" || char === '"') {
|
|
133
|
-
stack.push({ mode: 'string', quote: char, keep: specifierAhead() });
|
|
134
|
-
if (!specifierAhead())
|
|
135
|
-
out[i] = ' ';
|
|
136
|
-
i += 1;
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
if (char === '`') {
|
|
140
|
-
stack.push({ mode: 'template' });
|
|
141
|
-
out[i] = ' ';
|
|
142
|
-
i += 1;
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
if (char === '/' && REGEX_AFTER.has(lastCode)) {
|
|
146
|
-
stack.push({ mode: 'regex', inClass: false });
|
|
147
|
-
out[i] = ' ';
|
|
148
|
-
i += 1;
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
if (frame.braces > 0) {
|
|
152
|
-
if (char === '{')
|
|
153
|
-
frame.braces += 1;
|
|
154
|
-
else if (char === '}') {
|
|
155
|
-
frame.braces -= 1;
|
|
156
|
-
if (frame.braces === 0) {
|
|
157
|
-
stack.pop();
|
|
158
|
-
out[i] = ' ';
|
|
159
|
-
i += 1;
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
if (/[\w$]/.test(char))
|
|
165
|
-
word += char;
|
|
166
|
-
else if (word !== '') {
|
|
167
|
-
lastWord = word;
|
|
168
|
-
word = '';
|
|
169
|
-
}
|
|
170
|
-
if (!/\s/.test(char))
|
|
171
|
-
lastCode = char;
|
|
172
|
-
i += 1;
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
if (frame.mode === 'line') {
|
|
176
|
-
if (char === '\n') {
|
|
177
|
-
stack.pop();
|
|
178
|
-
i += 1;
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
out[i] = ' ';
|
|
182
|
-
i += 1;
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
if (frame.mode === 'block') {
|
|
186
|
-
if (char === '*' && next === '/') {
|
|
187
|
-
blank(i, i + 2);
|
|
188
|
-
stack.pop();
|
|
189
|
-
i += 2;
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (char !== '\n')
|
|
193
|
-
out[i] = ' ';
|
|
194
|
-
i += 1;
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
if (frame.mode === 'string') {
|
|
198
|
-
if (char === '\\') {
|
|
199
|
-
if (!frame.keep)
|
|
200
|
-
blank(i, i + 2);
|
|
201
|
-
i += 2;
|
|
202
|
-
continue;
|
|
203
|
-
}
|
|
204
|
-
if (char === frame.quote) {
|
|
205
|
-
if (!frame.keep)
|
|
206
|
-
out[i] = ' ';
|
|
207
|
-
stack.pop();
|
|
208
|
-
// A string is an operand: the next `/` is division, not a regex.
|
|
209
|
-
lastCode = 'x';
|
|
210
|
-
i += 1;
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
if (!frame.keep && char !== '\n')
|
|
214
|
-
out[i] = ' ';
|
|
215
|
-
i += 1;
|
|
216
|
-
continue;
|
|
217
|
-
}
|
|
218
|
-
if (frame.mode === 'template') {
|
|
219
|
-
if (char === '\\') {
|
|
220
|
-
blank(i, i + 2);
|
|
221
|
-
i += 2;
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
if (char === '`') {
|
|
225
|
-
out[i] = ' ';
|
|
226
|
-
stack.pop();
|
|
227
|
-
lastCode = 'x';
|
|
228
|
-
i += 1;
|
|
229
|
-
continue;
|
|
230
|
-
}
|
|
231
|
-
if (char === '$' && next === '{') {
|
|
232
|
-
// The substitution is code: keep it visible and track its closing brace.
|
|
233
|
-
blank(i, i + 2);
|
|
234
|
-
stack.push({ mode: 'code', braces: 1 });
|
|
235
|
-
lastCode = '{';
|
|
236
|
-
i += 2;
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
if (char !== '\n')
|
|
240
|
-
out[i] = ' ';
|
|
241
|
-
i += 1;
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
// Regex literal: blank it, honour escapes and character classes, then its flags.
|
|
245
|
-
if (char === '\\') {
|
|
246
|
-
blank(i, i + 2);
|
|
247
|
-
i += 2;
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
if (char === '[')
|
|
251
|
-
frame.inClass = true;
|
|
252
|
-
else if (char === ']')
|
|
253
|
-
frame.inClass = false;
|
|
254
|
-
else if (char === '/' && !frame.inClass) {
|
|
255
|
-
out[i] = ' ';
|
|
256
|
-
stack.pop();
|
|
257
|
-
lastCode = 'x';
|
|
258
|
-
i += 1;
|
|
259
|
-
while (i < source.length && /[a-z]/i.test(source[i] ?? '')) {
|
|
260
|
-
out[i] = ' ';
|
|
261
|
-
i += 1;
|
|
262
|
-
}
|
|
263
|
-
continue;
|
|
264
|
-
}
|
|
265
|
-
if (char !== '\n')
|
|
266
|
-
out[i] = ' ';
|
|
267
|
-
i += 1;
|
|
268
|
-
}
|
|
269
|
-
return out.join('');
|
|
270
|
-
}
|
|
271
61
|
/**
|
|
272
62
|
* How many `{ … }` blocks are open at one offset of the code-only text.
|
|
273
63
|
*
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Shared lexical masking for source inspection without executing a module. */
|
|
2
|
+
/**
|
|
3
|
+
* A source with every comment and every string, template and regex literal blanked to spaces,
|
|
4
|
+
* preserving offsets and newlines so a match's position still indexes the original source.
|
|
5
|
+
*
|
|
6
|
+
* A launch mentioned in prose is not a launch: a review proved scripts that only PRINT
|
|
7
|
+
* `const browser = await chromium.launch()` — in a string, a comment and a template literal — and
|
|
8
|
+
* a raw substring match refused all of them. Template `${…}` substitutions stay code (a `close()`
|
|
9
|
+
* written there is a real cleanup path), nested braces are tracked so the substitution's end is
|
|
10
|
+
* found, and regex literals are recognised by the token before them so a quote inside a character
|
|
11
|
+
* class cannot blank the rest of the file.
|
|
12
|
+
*
|
|
13
|
+
* @param source - The script's source.
|
|
14
|
+
* @returns The same text with literal/comment spans replaced by spaces.
|
|
15
|
+
*/
|
|
16
|
+
export declare function codeWithModuleSpecifiers(source: string): string;
|
|
17
|
+
//# sourceMappingURL=source-code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-code.d.ts","sourceRoot":"","sources":["../src/source-code.ts"],"names":[],"mappings":"AAAA,+EAA+E;AA2B/E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAwK/D"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/** Shared lexical masking for source inspection without executing a module. */
|
|
2
|
+
/** The characters after which a `/` starts a regex literal rather than a division. */
|
|
3
|
+
const REGEX_AFTER = new Set([
|
|
4
|
+
'',
|
|
5
|
+
'(',
|
|
6
|
+
'[',
|
|
7
|
+
'{',
|
|
8
|
+
',',
|
|
9
|
+
';',
|
|
10
|
+
':',
|
|
11
|
+
'=',
|
|
12
|
+
'!',
|
|
13
|
+
'&',
|
|
14
|
+
'|',
|
|
15
|
+
'?',
|
|
16
|
+
'+',
|
|
17
|
+
'-',
|
|
18
|
+
'*',
|
|
19
|
+
'%',
|
|
20
|
+
'^',
|
|
21
|
+
'~',
|
|
22
|
+
'<',
|
|
23
|
+
'>',
|
|
24
|
+
'}',
|
|
25
|
+
]);
|
|
26
|
+
/**
|
|
27
|
+
* A source with every comment and every string, template and regex literal blanked to spaces,
|
|
28
|
+
* preserving offsets and newlines so a match's position still indexes the original source.
|
|
29
|
+
*
|
|
30
|
+
* A launch mentioned in prose is not a launch: a review proved scripts that only PRINT
|
|
31
|
+
* `const browser = await chromium.launch()` — in a string, a comment and a template literal — and
|
|
32
|
+
* a raw substring match refused all of them. Template `${…}` substitutions stay code (a `close()`
|
|
33
|
+
* written there is a real cleanup path), nested braces are tracked so the substitution's end is
|
|
34
|
+
* found, and regex literals are recognised by the token before them so a quote inside a character
|
|
35
|
+
* class cannot blank the rest of the file.
|
|
36
|
+
*
|
|
37
|
+
* @param source - The script's source.
|
|
38
|
+
* @returns The same text with literal/comment spans replaced by spaces.
|
|
39
|
+
*/
|
|
40
|
+
export function codeWithModuleSpecifiers(source) {
|
|
41
|
+
const out = source.split('');
|
|
42
|
+
const blank = (from, to) => {
|
|
43
|
+
for (let i = from; i < to; i++)
|
|
44
|
+
if (out[i] !== '\n')
|
|
45
|
+
out[i] = ' ';
|
|
46
|
+
};
|
|
47
|
+
const stack = [{ mode: 'code', braces: 0 }];
|
|
48
|
+
const top = () => stack[stack.length - 1];
|
|
49
|
+
/** The last non-whitespace CODE character: decides whether a `/` opens a regex. */
|
|
50
|
+
let lastCode = '';
|
|
51
|
+
/** The last identifier word and the word being read — a module specifier follows `from`. */
|
|
52
|
+
let lastWord = '';
|
|
53
|
+
let word = '';
|
|
54
|
+
/** A specifier (`from 'playwright'`, `require('playwright')`) stays readable to provenance. */
|
|
55
|
+
const specifierAhead = () => (lastWord === 'from' && lastCode === 'm') ||
|
|
56
|
+
(lastWord === 'import' && lastCode === 't') ||
|
|
57
|
+
((lastWord === 'require' || lastWord === 'import') && lastCode === '(');
|
|
58
|
+
let i = 0;
|
|
59
|
+
while (i < source.length) {
|
|
60
|
+
const frame = top();
|
|
61
|
+
const char = source[i] ?? '';
|
|
62
|
+
const next = source[i + 1];
|
|
63
|
+
if (frame.mode === 'code') {
|
|
64
|
+
if (char === '/' && next === '/') {
|
|
65
|
+
stack.push({ mode: 'line' });
|
|
66
|
+
blank(i, i + 2);
|
|
67
|
+
i += 2;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (char === '/' && next === '*') {
|
|
71
|
+
stack.push({ mode: 'block' });
|
|
72
|
+
blank(i, i + 2);
|
|
73
|
+
i += 2;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (char === "'" || char === '"') {
|
|
77
|
+
stack.push({ mode: 'string', quote: char, keep: specifierAhead() });
|
|
78
|
+
if (!specifierAhead())
|
|
79
|
+
out[i] = ' ';
|
|
80
|
+
i += 1;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (char === '`') {
|
|
84
|
+
stack.push({ mode: 'template' });
|
|
85
|
+
out[i] = ' ';
|
|
86
|
+
i += 1;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (char === '/' && REGEX_AFTER.has(lastCode)) {
|
|
90
|
+
stack.push({ mode: 'regex', inClass: false });
|
|
91
|
+
out[i] = ' ';
|
|
92
|
+
i += 1;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (frame.braces > 0) {
|
|
96
|
+
if (char === '{')
|
|
97
|
+
frame.braces += 1;
|
|
98
|
+
else if (char === '}') {
|
|
99
|
+
frame.braces -= 1;
|
|
100
|
+
if (frame.braces === 0) {
|
|
101
|
+
stack.pop();
|
|
102
|
+
out[i] = ' ';
|
|
103
|
+
i += 1;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (/[\w$]/.test(char))
|
|
109
|
+
word += char;
|
|
110
|
+
else if (word !== '') {
|
|
111
|
+
lastWord = word;
|
|
112
|
+
word = '';
|
|
113
|
+
}
|
|
114
|
+
if (!/\s/.test(char))
|
|
115
|
+
lastCode = char;
|
|
116
|
+
i += 1;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (frame.mode === 'line') {
|
|
120
|
+
if (char === '\n') {
|
|
121
|
+
stack.pop();
|
|
122
|
+
i += 1;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
out[i] = ' ';
|
|
126
|
+
i += 1;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (frame.mode === 'block') {
|
|
130
|
+
if (char === '*' && next === '/') {
|
|
131
|
+
blank(i, i + 2);
|
|
132
|
+
stack.pop();
|
|
133
|
+
i += 2;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (char !== '\n')
|
|
137
|
+
out[i] = ' ';
|
|
138
|
+
i += 1;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (frame.mode === 'string') {
|
|
142
|
+
if (char === '\\') {
|
|
143
|
+
if (!frame.keep)
|
|
144
|
+
blank(i, i + 2);
|
|
145
|
+
i += 2;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (char === frame.quote) {
|
|
149
|
+
if (!frame.keep)
|
|
150
|
+
out[i] = ' ';
|
|
151
|
+
stack.pop();
|
|
152
|
+
// A string is an operand: the next `/` is division, not a regex.
|
|
153
|
+
lastCode = 'x';
|
|
154
|
+
i += 1;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (!frame.keep && char !== '\n')
|
|
158
|
+
out[i] = ' ';
|
|
159
|
+
i += 1;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (frame.mode === 'template') {
|
|
163
|
+
if (char === '\\') {
|
|
164
|
+
blank(i, i + 2);
|
|
165
|
+
i += 2;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (char === '`') {
|
|
169
|
+
out[i] = ' ';
|
|
170
|
+
stack.pop();
|
|
171
|
+
lastCode = 'x';
|
|
172
|
+
i += 1;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (char === '$' && next === '{') {
|
|
176
|
+
// The substitution is code: keep it visible and track its closing brace.
|
|
177
|
+
blank(i, i + 2);
|
|
178
|
+
stack.push({ mode: 'code', braces: 1 });
|
|
179
|
+
lastCode = '{';
|
|
180
|
+
i += 2;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (char !== '\n')
|
|
184
|
+
out[i] = ' ';
|
|
185
|
+
i += 1;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
// Regex literal: blank it, honour escapes and character classes, then its flags.
|
|
189
|
+
if (char === '\\') {
|
|
190
|
+
blank(i, i + 2);
|
|
191
|
+
i += 2;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (char === '[')
|
|
195
|
+
frame.inClass = true;
|
|
196
|
+
else if (char === ']')
|
|
197
|
+
frame.inClass = false;
|
|
198
|
+
else if (char === '/' && !frame.inClass) {
|
|
199
|
+
out[i] = ' ';
|
|
200
|
+
stack.pop();
|
|
201
|
+
lastCode = 'x';
|
|
202
|
+
i += 1;
|
|
203
|
+
while (i < source.length && /[a-z]/i.test(source[i] ?? '')) {
|
|
204
|
+
out[i] = ' ';
|
|
205
|
+
i += 1;
|
|
206
|
+
}
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (char !== '\n')
|
|
210
|
+
out[i] = ' ';
|
|
211
|
+
i += 1;
|
|
212
|
+
}
|
|
213
|
+
return out.join('');
|
|
214
|
+
}
|
package/dist/suite-overlap.d.ts
CHANGED
|
@@ -50,6 +50,14 @@ export declare function withoutHeredocs(command: string): string;
|
|
|
50
50
|
* @returns The target key.
|
|
51
51
|
*/
|
|
52
52
|
export declare function suiteCommandTarget(command: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* The target of the actual Playwright step, including inherited exports. URLs and option names
|
|
55
|
+
* in another program or a quoted title cannot decide whether this step is deployed.
|
|
56
|
+
* @param command - The shell command.
|
|
57
|
+
* @param last - Read the last Playwright invocation, for the background launch rewrite.
|
|
58
|
+
* @returns The comparable target key, or local when no target setting is present.
|
|
59
|
+
*/
|
|
60
|
+
export declare function playwrightCommandTarget(command: string, last?: boolean): string;
|
|
53
61
|
/**
|
|
54
62
|
* Whether a Playwright statement names its own config — the reading {@link withSuiteConfig} and
|
|
55
63
|
* {@link scopedRerunCommand} share, so neither adds a second `--config` to a command that has one.
|
|
@@ -147,6 +155,22 @@ export interface RunEnvSetting {
|
|
|
147
155
|
* @returns The command, or the command unchanged when it already carries every setting.
|
|
148
156
|
*/
|
|
149
157
|
export declare function withRunEnv(command: string, settings?: readonly RunEnvSetting[]): string;
|
|
158
|
+
/**
|
|
159
|
+
* The absolute directory the Playwright statement in a command runs in, or null when the command
|
|
160
|
+
* does not say.
|
|
161
|
+
*
|
|
162
|
+
* The reporter prints a failing test's file relative to the process directory the runner started
|
|
163
|
+
* in (`../fixtures/…` from a run whose suite lives outside its own tree), so the directory has to
|
|
164
|
+
* come from the command itself: `exec_command`'s `cwd` when it was given, then each `cd` before
|
|
165
|
+
* the runner. Only a literal `cd` is followed — a target holding a variable, a glob or `~`
|
|
166
|
+
* resolves nowhere useful, and a guessed absolute path is worse than the relative one the
|
|
167
|
+
* reporter printed.
|
|
168
|
+
*
|
|
169
|
+
* @param command - The shell command.
|
|
170
|
+
* @param cwd - The `cwd` the tool was given, when it was.
|
|
171
|
+
* @returns The absolute directory, or null.
|
|
172
|
+
*/
|
|
173
|
+
export declare function suiteRunDirectory(command: string, cwd?: string | null): string | null;
|
|
150
174
|
/**
|
|
151
175
|
* Resolve literal `./`/`../` Playwright file selections against the runner's own directory.
|
|
152
176
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"suite-overlap.d.ts","sourceRoot":"","sources":["../src/suite-overlap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAaH;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoB1D;AAKD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAS1F;AA2VD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,QAAQ,GAAE,SAAS,MAAM,EAAO,EAChC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,EACtB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,EACnB,GAAG,GAAE,SAAS,aAAa,EAAO,EAClC,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,MAAM,CA2DR;AAED,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd;AAmCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,SAAS,aAAa,EAAO,GAAG,MAAM,CAe3F;
|
|
1
|
+
{"version":3,"file":"suite-overlap.d.ts","sourceRoot":"","sources":["../src/suite-overlap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAaH;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoB1D;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,UAAQ,GAAG,MAAM,CAwB7E;AAKD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAS1F;AA2VD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,QAAQ,GAAE,SAAS,MAAM,EAAO,EAChC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,EACtB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,EACnB,GAAG,GAAE,SAAS,aAAa,EAAO,EAClC,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,MAAM,CA2DR;AAED,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd;AAmCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,SAAS,aAAa,EAAO,GAAG,MAAM,CAe3F;AAQD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAiCrF;AAkND;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAa5F;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,SAAS,EAAE,OAAO,CAAA;IAClB,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,gBAAgB,EACzD,SAAS,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EAC7B,QAAQ,EAAE,SAAS,CAAC,EAAE,GACrB,CAAC,GAAG,IAAI,CAMV;AAqHD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC;;;;;;;;;;;;;OAaG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,EAAE,OAAO,MAA4D,CAAA;AAEhG;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAM7D;AAmCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CA2BtF"}
|
package/dist/suite-overlap.js
CHANGED
|
@@ -75,6 +75,31 @@ export function suiteCommandTarget(command) {
|
|
|
75
75
|
}
|
|
76
76
|
return 'local';
|
|
77
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* The target of the actual Playwright step, including inherited exports. URLs and option names
|
|
80
|
+
* in another program or a quoted title cannot decide whether this step is deployed.
|
|
81
|
+
* @param command - The shell command.
|
|
82
|
+
* @param last - Read the last Playwright invocation, for the background launch rewrite.
|
|
83
|
+
* @returns The comparable target key, or local when no target setting is present.
|
|
84
|
+
*/
|
|
85
|
+
export function playwrightCommandTarget(command, last = false) {
|
|
86
|
+
const steps = locatedCommandSegments(command);
|
|
87
|
+
const runners = steps.filter((step) => step.segment?.program === 'playwright' && step.segment.args[0] === 'test');
|
|
88
|
+
const runner = runners.at(last ? -1 : 0);
|
|
89
|
+
if (!runner)
|
|
90
|
+
return suiteCommandTarget(command);
|
|
91
|
+
const inherited = steps
|
|
92
|
+
.slice(0, steps.indexOf(runner))
|
|
93
|
+
.filter((step) => (step.shellScope ?? []).every((scope, i) => scope === runner.shellScope?.[i]) &&
|
|
94
|
+
(step.segment === null || step.segment.program === 'export'));
|
|
95
|
+
const prefix = runner.start !== null && runner.programOffset !== null
|
|
96
|
+
? command.slice(runner.start, runner.programOffset)
|
|
97
|
+
: runner.assignments.join(' ');
|
|
98
|
+
return suiteCommandTarget([
|
|
99
|
+
prefix,
|
|
100
|
+
...inherited.reverse().map((step) => step.segment?.text ?? step.assignments.join(' ')),
|
|
101
|
+
].join('; '));
|
|
102
|
+
}
|
|
78
103
|
/** Whether a Playwright statement already names its own config (`-c`, `--config`, `--config=`). */
|
|
79
104
|
const NAMES_CONFIG = /(?:^|\s)(?:-c|--config)(?:\s+|=)/;
|
|
80
105
|
/**
|
|
@@ -619,29 +644,40 @@ const unquoted = (word) => word.replace(/^['"]|['"]$/g, '');
|
|
|
619
644
|
* @param cwd - The `cwd` the tool was given, when it was.
|
|
620
645
|
* @returns The absolute directory, or null.
|
|
621
646
|
*/
|
|
622
|
-
function suiteRunDirectory(command, cwd) {
|
|
623
|
-
|
|
624
|
-
|
|
647
|
+
export function suiteRunDirectory(command, cwd) {
|
|
648
|
+
const dirs = new Map([
|
|
649
|
+
['', typeof cwd === 'string' && cwd.startsWith('/') ? posix.normalize(cwd) : null],
|
|
650
|
+
]);
|
|
651
|
+
for (const located of locatedCommandSegments(command)) {
|
|
652
|
+
const scope = located.shellScope ?? [];
|
|
653
|
+
const key = scope.join('\0');
|
|
654
|
+
if (!dirs.has(key)) {
|
|
655
|
+
let parent = scope.slice(0, -1);
|
|
656
|
+
while (!dirs.has(parent.join('\0')))
|
|
657
|
+
parent = parent.slice(0, -1);
|
|
658
|
+
dirs.set(key, dirs.get(parent.join('\0')) ?? null);
|
|
659
|
+
}
|
|
660
|
+
const { segment } = located;
|
|
625
661
|
if (segment === null)
|
|
626
662
|
continue;
|
|
627
663
|
if (segment.program === 'cd') {
|
|
628
|
-
const
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
664
|
+
const words = segment.args.map(unquoted);
|
|
665
|
+
while (words[0] === '--' || words[0] === '-L' || words[0] === '-P')
|
|
666
|
+
words.shift();
|
|
667
|
+
const target = words[0] ?? '';
|
|
668
|
+
const dir = dirs.get(key) ?? null;
|
|
669
|
+
dirs.set(key, !target || target.startsWith('-') || target.startsWith('~') || !LITERAL_DIR.test(target)
|
|
670
|
+
? null
|
|
671
|
+
: target.startsWith('/')
|
|
672
|
+
? posix.normalize(target)
|
|
673
|
+
: dir === null
|
|
674
|
+
? null
|
|
675
|
+
: posix.normalize(posix.join(dir, target)));
|
|
640
676
|
}
|
|
641
677
|
if (segment.program === 'playwright' && segment.args[0] === 'test')
|
|
642
|
-
|
|
678
|
+
return dirs.get(key) ?? null;
|
|
643
679
|
}
|
|
644
|
-
return
|
|
680
|
+
return null;
|
|
645
681
|
}
|
|
646
682
|
/** A failure location's path and its `:line[:column]` suffix, split the way Playwright does. */
|
|
647
683
|
const LOCATION_LINE = /^(.*?)(:\d+(?::\d+)?)$/;
|