@rstest/browser 0.7.9
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/browser-container/container-static/css/container.dc438e35.css +1 -0
- package/dist/browser-container/container-static/js/916.5aee8d2f.js +23549 -0
- package/dist/browser-container/container-static/js/916.5aee8d2f.js.LICENSE.txt +1 -0
- package/dist/browser-container/container-static/js/container.5ddd46c3.js +2281 -0
- package/dist/browser-container/container-static/js/lib-react.a9c9a89b.js +8464 -0
- package/dist/browser-container/container-static/js/lib-react.a9c9a89b.js.LICENSE.txt +1 -0
- package/dist/browser-container/container.html +14 -0
- package/dist/client/entry.d.ts +7 -0
- package/dist/client/fakeTimersStub.d.ts +25 -0
- package/dist/client/public.d.ts +9 -0
- package/dist/client/snapshot.d.ts +35 -0
- package/dist/client/sourceMapSupport.d.ts +45 -0
- package/dist/hostController.d.ts +17 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2576 -0
- package/dist/protocol.d.ts +134 -0
- package/dist/rslib-runtime.js +18 -0
- package/package.json +80 -0
- package/src/client/entry.ts +628 -0
- package/src/client/fakeTimersStub.ts +91 -0
- package/src/client/public.ts +12 -0
- package/src/client/snapshot.ts +177 -0
- package/src/client/sourceMapSupport.ts +178 -0
- package/src/env.d.ts +43 -0
- package/src/hostController.ts +1761 -0
- package/src/index.ts +18 -0
- package/src/manifest.d.ts +41 -0
- package/src/protocol.ts +132 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2576 @@
|
|
|
1
|
+
import { __webpack_require__ } from "./rslib-runtime.js";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import promises from "node:fs/promises";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { TEMP_RSTEST_OUTPUT_DIR, color, getSetupFiles, getTestEntries, isDebug, logger, rsbuild, serializableConfig } from "@rstest/core/browser";
|
|
6
|
+
import open_editor from "open-editor";
|
|
7
|
+
import { basename, dirname, join, normalize, relative, resolve as external_pathe_resolve } from "pathe";
|
|
8
|
+
import sirv from "sirv";
|
|
9
|
+
import { WebSocketServer } from "ws";
|
|
10
|
+
__webpack_require__.add({
|
|
11
|
+
"../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/index.js" (module, __unused_rspack_exports, __webpack_require__) {
|
|
12
|
+
const pico = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/picomatch.js");
|
|
13
|
+
const utils = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/utils.js");
|
|
14
|
+
function picomatch(glob, options, returnState = false) {
|
|
15
|
+
if (options && (null === options.windows || void 0 === options.windows)) options = {
|
|
16
|
+
...options,
|
|
17
|
+
windows: utils.isWindows()
|
|
18
|
+
};
|
|
19
|
+
return pico(glob, options, returnState);
|
|
20
|
+
}
|
|
21
|
+
Object.assign(picomatch, pico);
|
|
22
|
+
module.exports = picomatch;
|
|
23
|
+
},
|
|
24
|
+
"../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/constants.js" (module) {
|
|
25
|
+
const WIN_SLASH = '\\\\/';
|
|
26
|
+
const WIN_NO_SLASH = `[^${WIN_SLASH}]`;
|
|
27
|
+
const DOT_LITERAL = '\\.';
|
|
28
|
+
const PLUS_LITERAL = '\\+';
|
|
29
|
+
const QMARK_LITERAL = '\\?';
|
|
30
|
+
const SLASH_LITERAL = '\\/';
|
|
31
|
+
const ONE_CHAR = '(?=.)';
|
|
32
|
+
const QMARK = '[^/]';
|
|
33
|
+
const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`;
|
|
34
|
+
const START_ANCHOR = `(?:^|${SLASH_LITERAL})`;
|
|
35
|
+
const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`;
|
|
36
|
+
const NO_DOT = `(?!${DOT_LITERAL})`;
|
|
37
|
+
const NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`;
|
|
38
|
+
const NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`;
|
|
39
|
+
const NO_DOTS_SLASH = `(?!${DOTS_SLASH})`;
|
|
40
|
+
const QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`;
|
|
41
|
+
const STAR = `${QMARK}*?`;
|
|
42
|
+
const SEP = '/';
|
|
43
|
+
const POSIX_CHARS = {
|
|
44
|
+
DOT_LITERAL,
|
|
45
|
+
PLUS_LITERAL,
|
|
46
|
+
QMARK_LITERAL,
|
|
47
|
+
SLASH_LITERAL,
|
|
48
|
+
ONE_CHAR,
|
|
49
|
+
QMARK,
|
|
50
|
+
END_ANCHOR,
|
|
51
|
+
DOTS_SLASH,
|
|
52
|
+
NO_DOT,
|
|
53
|
+
NO_DOTS,
|
|
54
|
+
NO_DOT_SLASH,
|
|
55
|
+
NO_DOTS_SLASH,
|
|
56
|
+
QMARK_NO_DOT,
|
|
57
|
+
STAR,
|
|
58
|
+
START_ANCHOR,
|
|
59
|
+
SEP
|
|
60
|
+
};
|
|
61
|
+
const WINDOWS_CHARS = {
|
|
62
|
+
...POSIX_CHARS,
|
|
63
|
+
SLASH_LITERAL: `[${WIN_SLASH}]`,
|
|
64
|
+
QMARK: WIN_NO_SLASH,
|
|
65
|
+
STAR: `${WIN_NO_SLASH}*?`,
|
|
66
|
+
DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`,
|
|
67
|
+
NO_DOT: `(?!${DOT_LITERAL})`,
|
|
68
|
+
NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
|
|
69
|
+
NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`,
|
|
70
|
+
NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
|
|
71
|
+
QMARK_NO_DOT: `[^.${WIN_SLASH}]`,
|
|
72
|
+
START_ANCHOR: `(?:^|[${WIN_SLASH}])`,
|
|
73
|
+
END_ANCHOR: `(?:[${WIN_SLASH}]|$)`,
|
|
74
|
+
SEP: '\\'
|
|
75
|
+
};
|
|
76
|
+
const POSIX_REGEX_SOURCE = {
|
|
77
|
+
alnum: 'a-zA-Z0-9',
|
|
78
|
+
alpha: 'a-zA-Z',
|
|
79
|
+
ascii: '\\x00-\\x7F',
|
|
80
|
+
blank: ' \\t',
|
|
81
|
+
cntrl: '\\x00-\\x1F\\x7F',
|
|
82
|
+
digit: '0-9',
|
|
83
|
+
graph: '\\x21-\\x7E',
|
|
84
|
+
lower: 'a-z',
|
|
85
|
+
print: '\\x20-\\x7E ',
|
|
86
|
+
punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~',
|
|
87
|
+
space: ' \\t\\r\\n\\v\\f',
|
|
88
|
+
upper: 'A-Z',
|
|
89
|
+
word: 'A-Za-z0-9_',
|
|
90
|
+
xdigit: 'A-Fa-f0-9'
|
|
91
|
+
};
|
|
92
|
+
module.exports = {
|
|
93
|
+
MAX_LENGTH: 65536,
|
|
94
|
+
POSIX_REGEX_SOURCE,
|
|
95
|
+
REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g,
|
|
96
|
+
REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/,
|
|
97
|
+
REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/,
|
|
98
|
+
REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g,
|
|
99
|
+
REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g,
|
|
100
|
+
REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g,
|
|
101
|
+
REPLACEMENTS: {
|
|
102
|
+
__proto__: null,
|
|
103
|
+
'***': '*',
|
|
104
|
+
'**/**': '**',
|
|
105
|
+
'**/**/**': '**'
|
|
106
|
+
},
|
|
107
|
+
CHAR_0: 48,
|
|
108
|
+
CHAR_9: 57,
|
|
109
|
+
CHAR_UPPERCASE_A: 65,
|
|
110
|
+
CHAR_LOWERCASE_A: 97,
|
|
111
|
+
CHAR_UPPERCASE_Z: 90,
|
|
112
|
+
CHAR_LOWERCASE_Z: 122,
|
|
113
|
+
CHAR_LEFT_PARENTHESES: 40,
|
|
114
|
+
CHAR_RIGHT_PARENTHESES: 41,
|
|
115
|
+
CHAR_ASTERISK: 42,
|
|
116
|
+
CHAR_AMPERSAND: 38,
|
|
117
|
+
CHAR_AT: 64,
|
|
118
|
+
CHAR_BACKWARD_SLASH: 92,
|
|
119
|
+
CHAR_CARRIAGE_RETURN: 13,
|
|
120
|
+
CHAR_CIRCUMFLEX_ACCENT: 94,
|
|
121
|
+
CHAR_COLON: 58,
|
|
122
|
+
CHAR_COMMA: 44,
|
|
123
|
+
CHAR_DOT: 46,
|
|
124
|
+
CHAR_DOUBLE_QUOTE: 34,
|
|
125
|
+
CHAR_EQUAL: 61,
|
|
126
|
+
CHAR_EXCLAMATION_MARK: 33,
|
|
127
|
+
CHAR_FORM_FEED: 12,
|
|
128
|
+
CHAR_FORWARD_SLASH: 47,
|
|
129
|
+
CHAR_GRAVE_ACCENT: 96,
|
|
130
|
+
CHAR_HASH: 35,
|
|
131
|
+
CHAR_HYPHEN_MINUS: 45,
|
|
132
|
+
CHAR_LEFT_ANGLE_BRACKET: 60,
|
|
133
|
+
CHAR_LEFT_CURLY_BRACE: 123,
|
|
134
|
+
CHAR_LEFT_SQUARE_BRACKET: 91,
|
|
135
|
+
CHAR_LINE_FEED: 10,
|
|
136
|
+
CHAR_NO_BREAK_SPACE: 160,
|
|
137
|
+
CHAR_PERCENT: 37,
|
|
138
|
+
CHAR_PLUS: 43,
|
|
139
|
+
CHAR_QUESTION_MARK: 63,
|
|
140
|
+
CHAR_RIGHT_ANGLE_BRACKET: 62,
|
|
141
|
+
CHAR_RIGHT_CURLY_BRACE: 125,
|
|
142
|
+
CHAR_RIGHT_SQUARE_BRACKET: 93,
|
|
143
|
+
CHAR_SEMICOLON: 59,
|
|
144
|
+
CHAR_SINGLE_QUOTE: 39,
|
|
145
|
+
CHAR_SPACE: 32,
|
|
146
|
+
CHAR_TAB: 9,
|
|
147
|
+
CHAR_UNDERSCORE: 95,
|
|
148
|
+
CHAR_VERTICAL_LINE: 124,
|
|
149
|
+
CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279,
|
|
150
|
+
extglobChars (chars) {
|
|
151
|
+
return {
|
|
152
|
+
'!': {
|
|
153
|
+
type: 'negate',
|
|
154
|
+
open: '(?:(?!(?:',
|
|
155
|
+
close: `))${chars.STAR})`
|
|
156
|
+
},
|
|
157
|
+
'?': {
|
|
158
|
+
type: 'qmark',
|
|
159
|
+
open: '(?:',
|
|
160
|
+
close: ')?'
|
|
161
|
+
},
|
|
162
|
+
'+': {
|
|
163
|
+
type: 'plus',
|
|
164
|
+
open: '(?:',
|
|
165
|
+
close: ')+'
|
|
166
|
+
},
|
|
167
|
+
'*': {
|
|
168
|
+
type: 'star',
|
|
169
|
+
open: '(?:',
|
|
170
|
+
close: ')*'
|
|
171
|
+
},
|
|
172
|
+
'@': {
|
|
173
|
+
type: 'at',
|
|
174
|
+
open: '(?:',
|
|
175
|
+
close: ')'
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
},
|
|
179
|
+
globChars (win32) {
|
|
180
|
+
return true === win32 ? WINDOWS_CHARS : POSIX_CHARS;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
},
|
|
184
|
+
"../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/parse.js" (module, __unused_rspack_exports, __webpack_require__) {
|
|
185
|
+
const constants = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/constants.js");
|
|
186
|
+
const utils = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/utils.js");
|
|
187
|
+
const { MAX_LENGTH, POSIX_REGEX_SOURCE, REGEX_NON_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_BACKREF, REPLACEMENTS } = constants;
|
|
188
|
+
const expandRange = (args, options)=>{
|
|
189
|
+
if ('function' == typeof options.expandRange) return options.expandRange(...args, options);
|
|
190
|
+
args.sort();
|
|
191
|
+
const value = `[${args.join('-')}]`;
|
|
192
|
+
try {
|
|
193
|
+
new RegExp(value);
|
|
194
|
+
} catch (ex) {
|
|
195
|
+
return args.map((v)=>utils.escapeRegex(v)).join('..');
|
|
196
|
+
}
|
|
197
|
+
return value;
|
|
198
|
+
};
|
|
199
|
+
const syntaxError = (type, char)=>`Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`;
|
|
200
|
+
const parse = (input, options)=>{
|
|
201
|
+
if ('string' != typeof input) throw new TypeError('Expected a string');
|
|
202
|
+
input = REPLACEMENTS[input] || input;
|
|
203
|
+
const opts = {
|
|
204
|
+
...options
|
|
205
|
+
};
|
|
206
|
+
const max = 'number' == typeof opts.maxLength ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
|
|
207
|
+
let len = input.length;
|
|
208
|
+
if (len > max) throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`);
|
|
209
|
+
const bos = {
|
|
210
|
+
type: 'bos',
|
|
211
|
+
value: '',
|
|
212
|
+
output: opts.prepend || ''
|
|
213
|
+
};
|
|
214
|
+
const tokens = [
|
|
215
|
+
bos
|
|
216
|
+
];
|
|
217
|
+
const capture = opts.capture ? '' : '?:';
|
|
218
|
+
const PLATFORM_CHARS = constants.globChars(opts.windows);
|
|
219
|
+
const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS);
|
|
220
|
+
const { DOT_LITERAL, PLUS_LITERAL, SLASH_LITERAL, ONE_CHAR, DOTS_SLASH, NO_DOT, NO_DOT_SLASH, NO_DOTS_SLASH, QMARK, QMARK_NO_DOT, STAR, START_ANCHOR } = PLATFORM_CHARS;
|
|
221
|
+
const globstar = (opts)=>`(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
|
|
222
|
+
const nodot = opts.dot ? '' : NO_DOT;
|
|
223
|
+
const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT;
|
|
224
|
+
let star = true === opts.bash ? globstar(opts) : STAR;
|
|
225
|
+
if (opts.capture) star = `(${star})`;
|
|
226
|
+
if ('boolean' == typeof opts.noext) opts.noextglob = opts.noext;
|
|
227
|
+
const state = {
|
|
228
|
+
input,
|
|
229
|
+
index: -1,
|
|
230
|
+
start: 0,
|
|
231
|
+
dot: true === opts.dot,
|
|
232
|
+
consumed: '',
|
|
233
|
+
output: '',
|
|
234
|
+
prefix: '',
|
|
235
|
+
backtrack: false,
|
|
236
|
+
negated: false,
|
|
237
|
+
brackets: 0,
|
|
238
|
+
braces: 0,
|
|
239
|
+
parens: 0,
|
|
240
|
+
quotes: 0,
|
|
241
|
+
globstar: false,
|
|
242
|
+
tokens
|
|
243
|
+
};
|
|
244
|
+
input = utils.removePrefix(input, state);
|
|
245
|
+
len = input.length;
|
|
246
|
+
const extglobs = [];
|
|
247
|
+
const braces = [];
|
|
248
|
+
const stack = [];
|
|
249
|
+
let prev = bos;
|
|
250
|
+
let value;
|
|
251
|
+
const eos = ()=>state.index === len - 1;
|
|
252
|
+
const peek = state.peek = (n = 1)=>input[state.index + n];
|
|
253
|
+
const advance = state.advance = ()=>input[++state.index] || '';
|
|
254
|
+
const remaining = ()=>input.slice(state.index + 1);
|
|
255
|
+
const consume = (value = '', num = 0)=>{
|
|
256
|
+
state.consumed += value;
|
|
257
|
+
state.index += num;
|
|
258
|
+
};
|
|
259
|
+
const append = (token)=>{
|
|
260
|
+
state.output += null != token.output ? token.output : token.value;
|
|
261
|
+
consume(token.value);
|
|
262
|
+
};
|
|
263
|
+
const negate = ()=>{
|
|
264
|
+
let count = 1;
|
|
265
|
+
while('!' === peek() && ('(' !== peek(2) || '?' === peek(3))){
|
|
266
|
+
advance();
|
|
267
|
+
state.start++;
|
|
268
|
+
count++;
|
|
269
|
+
}
|
|
270
|
+
if (count % 2 === 0) return false;
|
|
271
|
+
state.negated = true;
|
|
272
|
+
state.start++;
|
|
273
|
+
return true;
|
|
274
|
+
};
|
|
275
|
+
const increment = (type)=>{
|
|
276
|
+
state[type]++;
|
|
277
|
+
stack.push(type);
|
|
278
|
+
};
|
|
279
|
+
const decrement = (type)=>{
|
|
280
|
+
state[type]--;
|
|
281
|
+
stack.pop();
|
|
282
|
+
};
|
|
283
|
+
const push = (tok)=>{
|
|
284
|
+
if ('globstar' === prev.type) {
|
|
285
|
+
const isBrace = state.braces > 0 && ('comma' === tok.type || 'brace' === tok.type);
|
|
286
|
+
const isExtglob = true === tok.extglob || extglobs.length && ('pipe' === tok.type || 'paren' === tok.type);
|
|
287
|
+
if ('slash' !== tok.type && 'paren' !== tok.type && !isBrace && !isExtglob) {
|
|
288
|
+
state.output = state.output.slice(0, -prev.output.length);
|
|
289
|
+
prev.type = 'star';
|
|
290
|
+
prev.value = '*';
|
|
291
|
+
prev.output = star;
|
|
292
|
+
state.output += prev.output;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (extglobs.length && 'paren' !== tok.type) extglobs[extglobs.length - 1].inner += tok.value;
|
|
296
|
+
if (tok.value || tok.output) append(tok);
|
|
297
|
+
if (prev && 'text' === prev.type && 'text' === tok.type) {
|
|
298
|
+
prev.output = (prev.output || prev.value) + tok.value;
|
|
299
|
+
prev.value += tok.value;
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
tok.prev = prev;
|
|
303
|
+
tokens.push(tok);
|
|
304
|
+
prev = tok;
|
|
305
|
+
};
|
|
306
|
+
const extglobOpen = (type, value)=>{
|
|
307
|
+
const token = {
|
|
308
|
+
...EXTGLOB_CHARS[value],
|
|
309
|
+
conditions: 1,
|
|
310
|
+
inner: ''
|
|
311
|
+
};
|
|
312
|
+
token.prev = prev;
|
|
313
|
+
token.parens = state.parens;
|
|
314
|
+
token.output = state.output;
|
|
315
|
+
const output = (opts.capture ? '(' : '') + token.open;
|
|
316
|
+
increment('parens');
|
|
317
|
+
push({
|
|
318
|
+
type,
|
|
319
|
+
value,
|
|
320
|
+
output: state.output ? '' : ONE_CHAR
|
|
321
|
+
});
|
|
322
|
+
push({
|
|
323
|
+
type: 'paren',
|
|
324
|
+
extglob: true,
|
|
325
|
+
value: advance(),
|
|
326
|
+
output
|
|
327
|
+
});
|
|
328
|
+
extglobs.push(token);
|
|
329
|
+
};
|
|
330
|
+
const extglobClose = (token)=>{
|
|
331
|
+
let output = token.close + (opts.capture ? ')' : '');
|
|
332
|
+
let rest;
|
|
333
|
+
if ('negate' === token.type) {
|
|
334
|
+
let extglobStar = star;
|
|
335
|
+
if (token.inner && token.inner.length > 1 && token.inner.includes('/')) extglobStar = globstar(opts);
|
|
336
|
+
if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) output = token.close = `)$))${extglobStar}`;
|
|
337
|
+
if (token.inner.includes('*') && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) {
|
|
338
|
+
const expression = parse(rest, {
|
|
339
|
+
...options,
|
|
340
|
+
fastpaths: false
|
|
341
|
+
}).output;
|
|
342
|
+
output = token.close = `)${expression})${extglobStar})`;
|
|
343
|
+
}
|
|
344
|
+
if ('bos' === token.prev.type) state.negatedExtglob = true;
|
|
345
|
+
}
|
|
346
|
+
push({
|
|
347
|
+
type: 'paren',
|
|
348
|
+
extglob: true,
|
|
349
|
+
value,
|
|
350
|
+
output
|
|
351
|
+
});
|
|
352
|
+
decrement('parens');
|
|
353
|
+
};
|
|
354
|
+
if (false !== opts.fastpaths && !/(^[*!]|[/()[\]{}"])/.test(input)) {
|
|
355
|
+
let backslashes = false;
|
|
356
|
+
let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index)=>{
|
|
357
|
+
if ('\\' === first) {
|
|
358
|
+
backslashes = true;
|
|
359
|
+
return m;
|
|
360
|
+
}
|
|
361
|
+
if ('?' === first) {
|
|
362
|
+
if (esc) return esc + first + (rest ? QMARK.repeat(rest.length) : '');
|
|
363
|
+
if (0 === index) return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : '');
|
|
364
|
+
return QMARK.repeat(chars.length);
|
|
365
|
+
}
|
|
366
|
+
if ('.' === first) return DOT_LITERAL.repeat(chars.length);
|
|
367
|
+
if ('*' === first) {
|
|
368
|
+
if (esc) return esc + first + (rest ? star : '');
|
|
369
|
+
return star;
|
|
370
|
+
}
|
|
371
|
+
return esc ? m : `\\${m}`;
|
|
372
|
+
});
|
|
373
|
+
if (true === backslashes) output = true === opts.unescape ? output.replace(/\\/g, '') : output.replace(/\\+/g, (m)=>m.length % 2 === 0 ? '\\\\' : m ? '\\' : '');
|
|
374
|
+
if (output === input && true === opts.contains) {
|
|
375
|
+
state.output = input;
|
|
376
|
+
return state;
|
|
377
|
+
}
|
|
378
|
+
state.output = utils.wrapOutput(output, state, options);
|
|
379
|
+
return state;
|
|
380
|
+
}
|
|
381
|
+
while(!eos()){
|
|
382
|
+
value = advance();
|
|
383
|
+
if ('\u0000' === value) continue;
|
|
384
|
+
if ('\\' === value) {
|
|
385
|
+
const next = peek();
|
|
386
|
+
if ('/' === next && true !== opts.bash) continue;
|
|
387
|
+
if ('.' === next || ';' === next) continue;
|
|
388
|
+
if (!next) {
|
|
389
|
+
value += '\\';
|
|
390
|
+
push({
|
|
391
|
+
type: 'text',
|
|
392
|
+
value
|
|
393
|
+
});
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
const match = /^\\+/.exec(remaining());
|
|
397
|
+
let slashes = 0;
|
|
398
|
+
if (match && match[0].length > 2) {
|
|
399
|
+
slashes = match[0].length;
|
|
400
|
+
state.index += slashes;
|
|
401
|
+
if (slashes % 2 !== 0) value += '\\';
|
|
402
|
+
}
|
|
403
|
+
if (true === opts.unescape) value = advance();
|
|
404
|
+
else value += advance();
|
|
405
|
+
if (0 === state.brackets) {
|
|
406
|
+
push({
|
|
407
|
+
type: 'text',
|
|
408
|
+
value
|
|
409
|
+
});
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (state.brackets > 0 && (']' !== value || '[' === prev.value || '[^' === prev.value)) {
|
|
414
|
+
if (false !== opts.posix && ':' === value) {
|
|
415
|
+
const inner = prev.value.slice(1);
|
|
416
|
+
if (inner.includes('[')) {
|
|
417
|
+
prev.posix = true;
|
|
418
|
+
if (inner.includes(':')) {
|
|
419
|
+
const idx = prev.value.lastIndexOf('[');
|
|
420
|
+
const pre = prev.value.slice(0, idx);
|
|
421
|
+
const rest = prev.value.slice(idx + 2);
|
|
422
|
+
const posix = POSIX_REGEX_SOURCE[rest];
|
|
423
|
+
if (posix) {
|
|
424
|
+
prev.value = pre + posix;
|
|
425
|
+
state.backtrack = true;
|
|
426
|
+
advance();
|
|
427
|
+
if (!bos.output && 1 === tokens.indexOf(prev)) bos.output = ONE_CHAR;
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if ('[' === value && ':' !== peek() || '-' === value && ']' === peek()) value = `\\${value}`;
|
|
434
|
+
if (']' === value && ('[' === prev.value || '[^' === prev.value)) value = `\\${value}`;
|
|
435
|
+
if (true === opts.posix && '!' === value && '[' === prev.value) value = '^';
|
|
436
|
+
prev.value += value;
|
|
437
|
+
append({
|
|
438
|
+
value
|
|
439
|
+
});
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (1 === state.quotes && '"' !== value) {
|
|
443
|
+
value = utils.escapeRegex(value);
|
|
444
|
+
prev.value += value;
|
|
445
|
+
append({
|
|
446
|
+
value
|
|
447
|
+
});
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if ('"' === value) {
|
|
451
|
+
state.quotes = 1 === state.quotes ? 0 : 1;
|
|
452
|
+
if (true === opts.keepQuotes) push({
|
|
453
|
+
type: 'text',
|
|
454
|
+
value
|
|
455
|
+
});
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if ('(' === value) {
|
|
459
|
+
increment('parens');
|
|
460
|
+
push({
|
|
461
|
+
type: 'paren',
|
|
462
|
+
value
|
|
463
|
+
});
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
if (')' === value) {
|
|
467
|
+
if (0 === state.parens && true === opts.strictBrackets) throw new SyntaxError(syntaxError('opening', '('));
|
|
468
|
+
const extglob = extglobs[extglobs.length - 1];
|
|
469
|
+
if (extglob && state.parens === extglob.parens + 1) {
|
|
470
|
+
extglobClose(extglobs.pop());
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
push({
|
|
474
|
+
type: 'paren',
|
|
475
|
+
value,
|
|
476
|
+
output: state.parens ? ')' : '\\)'
|
|
477
|
+
});
|
|
478
|
+
decrement('parens');
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
if ('[' === value) {
|
|
482
|
+
if (true !== opts.nobracket && remaining().includes(']')) increment('brackets');
|
|
483
|
+
else {
|
|
484
|
+
if (true !== opts.nobracket && true === opts.strictBrackets) throw new SyntaxError(syntaxError('closing', ']'));
|
|
485
|
+
value = `\\${value}`;
|
|
486
|
+
}
|
|
487
|
+
push({
|
|
488
|
+
type: 'bracket',
|
|
489
|
+
value
|
|
490
|
+
});
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
if (']' === value) {
|
|
494
|
+
if (true === opts.nobracket || prev && 'bracket' === prev.type && 1 === prev.value.length) {
|
|
495
|
+
push({
|
|
496
|
+
type: 'text',
|
|
497
|
+
value,
|
|
498
|
+
output: `\\${value}`
|
|
499
|
+
});
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
if (0 === state.brackets) {
|
|
503
|
+
if (true === opts.strictBrackets) throw new SyntaxError(syntaxError('opening', '['));
|
|
504
|
+
push({
|
|
505
|
+
type: 'text',
|
|
506
|
+
value,
|
|
507
|
+
output: `\\${value}`
|
|
508
|
+
});
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
decrement('brackets');
|
|
512
|
+
const prevValue = prev.value.slice(1);
|
|
513
|
+
if (true !== prev.posix && '^' === prevValue[0] && !prevValue.includes('/')) value = `/${value}`;
|
|
514
|
+
prev.value += value;
|
|
515
|
+
append({
|
|
516
|
+
value
|
|
517
|
+
});
|
|
518
|
+
if (false === opts.literalBrackets || utils.hasRegexChars(prevValue)) continue;
|
|
519
|
+
const escaped = utils.escapeRegex(prev.value);
|
|
520
|
+
state.output = state.output.slice(0, -prev.value.length);
|
|
521
|
+
if (true === opts.literalBrackets) {
|
|
522
|
+
state.output += escaped;
|
|
523
|
+
prev.value = escaped;
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
prev.value = `(${capture}${escaped}|${prev.value})`;
|
|
527
|
+
state.output += prev.value;
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
if ('{' === value && true !== opts.nobrace) {
|
|
531
|
+
increment('braces');
|
|
532
|
+
const open = {
|
|
533
|
+
type: 'brace',
|
|
534
|
+
value,
|
|
535
|
+
output: '(',
|
|
536
|
+
outputIndex: state.output.length,
|
|
537
|
+
tokensIndex: state.tokens.length
|
|
538
|
+
};
|
|
539
|
+
braces.push(open);
|
|
540
|
+
push(open);
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
if ('}' === value) {
|
|
544
|
+
const brace = braces[braces.length - 1];
|
|
545
|
+
if (true === opts.nobrace || !brace) {
|
|
546
|
+
push({
|
|
547
|
+
type: 'text',
|
|
548
|
+
value,
|
|
549
|
+
output: value
|
|
550
|
+
});
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
let output = ')';
|
|
554
|
+
if (true === brace.dots) {
|
|
555
|
+
const arr = tokens.slice();
|
|
556
|
+
const range = [];
|
|
557
|
+
for(let i = arr.length - 1; i >= 0; i--){
|
|
558
|
+
tokens.pop();
|
|
559
|
+
if ('brace' === arr[i].type) break;
|
|
560
|
+
if ('dots' !== arr[i].type) range.unshift(arr[i].value);
|
|
561
|
+
}
|
|
562
|
+
output = expandRange(range, opts);
|
|
563
|
+
state.backtrack = true;
|
|
564
|
+
}
|
|
565
|
+
if (true !== brace.comma && true !== brace.dots) {
|
|
566
|
+
const out = state.output.slice(0, brace.outputIndex);
|
|
567
|
+
const toks = state.tokens.slice(brace.tokensIndex);
|
|
568
|
+
brace.value = brace.output = '\\{';
|
|
569
|
+
value = output = '\\}';
|
|
570
|
+
state.output = out;
|
|
571
|
+
for (const t of toks)state.output += t.output || t.value;
|
|
572
|
+
}
|
|
573
|
+
push({
|
|
574
|
+
type: 'brace',
|
|
575
|
+
value,
|
|
576
|
+
output
|
|
577
|
+
});
|
|
578
|
+
decrement('braces');
|
|
579
|
+
braces.pop();
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
if ('|' === value) {
|
|
583
|
+
if (extglobs.length > 0) extglobs[extglobs.length - 1].conditions++;
|
|
584
|
+
push({
|
|
585
|
+
type: 'text',
|
|
586
|
+
value
|
|
587
|
+
});
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
if (',' === value) {
|
|
591
|
+
let output = value;
|
|
592
|
+
const brace = braces[braces.length - 1];
|
|
593
|
+
if (brace && 'braces' === stack[stack.length - 1]) {
|
|
594
|
+
brace.comma = true;
|
|
595
|
+
output = '|';
|
|
596
|
+
}
|
|
597
|
+
push({
|
|
598
|
+
type: 'comma',
|
|
599
|
+
value,
|
|
600
|
+
output
|
|
601
|
+
});
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
if ('/' === value) {
|
|
605
|
+
if ('dot' === prev.type && state.index === state.start + 1) {
|
|
606
|
+
state.start = state.index + 1;
|
|
607
|
+
state.consumed = '';
|
|
608
|
+
state.output = '';
|
|
609
|
+
tokens.pop();
|
|
610
|
+
prev = bos;
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
push({
|
|
614
|
+
type: 'slash',
|
|
615
|
+
value,
|
|
616
|
+
output: SLASH_LITERAL
|
|
617
|
+
});
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
if ('.' === value) {
|
|
621
|
+
if (state.braces > 0 && 'dot' === prev.type) {
|
|
622
|
+
if ('.' === prev.value) prev.output = DOT_LITERAL;
|
|
623
|
+
const brace = braces[braces.length - 1];
|
|
624
|
+
prev.type = 'dots';
|
|
625
|
+
prev.output += value;
|
|
626
|
+
prev.value += value;
|
|
627
|
+
brace.dots = true;
|
|
628
|
+
continue;
|
|
629
|
+
}
|
|
630
|
+
if (state.braces + state.parens === 0 && 'bos' !== prev.type && 'slash' !== prev.type) {
|
|
631
|
+
push({
|
|
632
|
+
type: 'text',
|
|
633
|
+
value,
|
|
634
|
+
output: DOT_LITERAL
|
|
635
|
+
});
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
push({
|
|
639
|
+
type: 'dot',
|
|
640
|
+
value,
|
|
641
|
+
output: DOT_LITERAL
|
|
642
|
+
});
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
if ('?' === value) {
|
|
646
|
+
const isGroup = prev && '(' === prev.value;
|
|
647
|
+
if (!isGroup && true !== opts.noextglob && '(' === peek() && '?' !== peek(2)) {
|
|
648
|
+
extglobOpen('qmark', value);
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
if (prev && 'paren' === prev.type) {
|
|
652
|
+
const next = peek();
|
|
653
|
+
let output = value;
|
|
654
|
+
if ('(' === prev.value && !/[!=<:]/.test(next) || '<' === next && !/<([!=]|\w+>)/.test(remaining())) output = `\\${value}`;
|
|
655
|
+
push({
|
|
656
|
+
type: 'text',
|
|
657
|
+
value,
|
|
658
|
+
output
|
|
659
|
+
});
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
if (true !== opts.dot && ('slash' === prev.type || 'bos' === prev.type)) {
|
|
663
|
+
push({
|
|
664
|
+
type: 'qmark',
|
|
665
|
+
value,
|
|
666
|
+
output: QMARK_NO_DOT
|
|
667
|
+
});
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
push({
|
|
671
|
+
type: 'qmark',
|
|
672
|
+
value,
|
|
673
|
+
output: QMARK
|
|
674
|
+
});
|
|
675
|
+
continue;
|
|
676
|
+
}
|
|
677
|
+
if ('!' === value) {
|
|
678
|
+
if (true !== opts.noextglob && '(' === peek()) {
|
|
679
|
+
if ('?' !== peek(2) || !/[!=<:]/.test(peek(3))) {
|
|
680
|
+
extglobOpen('negate', value);
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
if (true !== opts.nonegate && 0 === state.index) {
|
|
685
|
+
negate();
|
|
686
|
+
continue;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
if ('+' === value) {
|
|
690
|
+
if (true !== opts.noextglob && '(' === peek() && '?' !== peek(2)) {
|
|
691
|
+
extglobOpen('plus', value);
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
if (prev && '(' === prev.value || false === opts.regex) {
|
|
695
|
+
push({
|
|
696
|
+
type: 'plus',
|
|
697
|
+
value,
|
|
698
|
+
output: PLUS_LITERAL
|
|
699
|
+
});
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
if (prev && ('bracket' === prev.type || 'paren' === prev.type || 'brace' === prev.type) || state.parens > 0) {
|
|
703
|
+
push({
|
|
704
|
+
type: 'plus',
|
|
705
|
+
value
|
|
706
|
+
});
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
push({
|
|
710
|
+
type: 'plus',
|
|
711
|
+
value: PLUS_LITERAL
|
|
712
|
+
});
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
if ('@' === value) {
|
|
716
|
+
if (true !== opts.noextglob && '(' === peek() && '?' !== peek(2)) {
|
|
717
|
+
push({
|
|
718
|
+
type: 'at',
|
|
719
|
+
extglob: true,
|
|
720
|
+
value,
|
|
721
|
+
output: ''
|
|
722
|
+
});
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
push({
|
|
726
|
+
type: 'text',
|
|
727
|
+
value
|
|
728
|
+
});
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
731
|
+
if ('*' !== value) {
|
|
732
|
+
if ('$' === value || '^' === value) value = `\\${value}`;
|
|
733
|
+
const match = REGEX_NON_SPECIAL_CHARS.exec(remaining());
|
|
734
|
+
if (match) {
|
|
735
|
+
value += match[0];
|
|
736
|
+
state.index += match[0].length;
|
|
737
|
+
}
|
|
738
|
+
push({
|
|
739
|
+
type: 'text',
|
|
740
|
+
value
|
|
741
|
+
});
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
if (prev && ('globstar' === prev.type || true === prev.star)) {
|
|
745
|
+
prev.type = 'star';
|
|
746
|
+
prev.star = true;
|
|
747
|
+
prev.value += value;
|
|
748
|
+
prev.output = star;
|
|
749
|
+
state.backtrack = true;
|
|
750
|
+
state.globstar = true;
|
|
751
|
+
consume(value);
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
let rest = remaining();
|
|
755
|
+
if (true !== opts.noextglob && /^\([^?]/.test(rest)) {
|
|
756
|
+
extglobOpen('star', value);
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
if ('star' === prev.type) {
|
|
760
|
+
if (true === opts.noglobstar) {
|
|
761
|
+
consume(value);
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
const prior = prev.prev;
|
|
765
|
+
const before = prior.prev;
|
|
766
|
+
const isStart = 'slash' === prior.type || 'bos' === prior.type;
|
|
767
|
+
const afterStar = before && ('star' === before.type || 'globstar' === before.type);
|
|
768
|
+
if (true === opts.bash && (!isStart || rest[0] && '/' !== rest[0])) {
|
|
769
|
+
push({
|
|
770
|
+
type: 'star',
|
|
771
|
+
value,
|
|
772
|
+
output: ''
|
|
773
|
+
});
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
const isBrace = state.braces > 0 && ('comma' === prior.type || 'brace' === prior.type);
|
|
777
|
+
const isExtglob = extglobs.length && ('pipe' === prior.type || 'paren' === prior.type);
|
|
778
|
+
if (!isStart && 'paren' !== prior.type && !isBrace && !isExtglob) {
|
|
779
|
+
push({
|
|
780
|
+
type: 'star',
|
|
781
|
+
value,
|
|
782
|
+
output: ''
|
|
783
|
+
});
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
while('/**' === rest.slice(0, 3)){
|
|
787
|
+
const after = input[state.index + 4];
|
|
788
|
+
if (after && '/' !== after) break;
|
|
789
|
+
rest = rest.slice(3);
|
|
790
|
+
consume('/**', 3);
|
|
791
|
+
}
|
|
792
|
+
if ('bos' === prior.type && eos()) {
|
|
793
|
+
prev.type = 'globstar';
|
|
794
|
+
prev.value += value;
|
|
795
|
+
prev.output = globstar(opts);
|
|
796
|
+
state.output = prev.output;
|
|
797
|
+
state.globstar = true;
|
|
798
|
+
consume(value);
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
if ('slash' === prior.type && 'bos' !== prior.prev.type && !afterStar && eos()) {
|
|
802
|
+
state.output = state.output.slice(0, -(prior.output + prev.output).length);
|
|
803
|
+
prior.output = `(?:${prior.output}`;
|
|
804
|
+
prev.type = 'globstar';
|
|
805
|
+
prev.output = globstar(opts) + (opts.strictSlashes ? ')' : '|$)');
|
|
806
|
+
prev.value += value;
|
|
807
|
+
state.globstar = true;
|
|
808
|
+
state.output += prior.output + prev.output;
|
|
809
|
+
consume(value);
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
if ('slash' === prior.type && 'bos' !== prior.prev.type && '/' === rest[0]) {
|
|
813
|
+
const end = void 0 !== rest[1] ? '|$' : '';
|
|
814
|
+
state.output = state.output.slice(0, -(prior.output + prev.output).length);
|
|
815
|
+
prior.output = `(?:${prior.output}`;
|
|
816
|
+
prev.type = 'globstar';
|
|
817
|
+
prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`;
|
|
818
|
+
prev.value += value;
|
|
819
|
+
state.output += prior.output + prev.output;
|
|
820
|
+
state.globstar = true;
|
|
821
|
+
consume(value + advance());
|
|
822
|
+
push({
|
|
823
|
+
type: 'slash',
|
|
824
|
+
value: '/',
|
|
825
|
+
output: ''
|
|
826
|
+
});
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
if ('bos' === prior.type && '/' === rest[0]) {
|
|
830
|
+
prev.type = 'globstar';
|
|
831
|
+
prev.value += value;
|
|
832
|
+
prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`;
|
|
833
|
+
state.output = prev.output;
|
|
834
|
+
state.globstar = true;
|
|
835
|
+
consume(value + advance());
|
|
836
|
+
push({
|
|
837
|
+
type: 'slash',
|
|
838
|
+
value: '/',
|
|
839
|
+
output: ''
|
|
840
|
+
});
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
state.output = state.output.slice(0, -prev.output.length);
|
|
844
|
+
prev.type = 'globstar';
|
|
845
|
+
prev.output = globstar(opts);
|
|
846
|
+
prev.value += value;
|
|
847
|
+
state.output += prev.output;
|
|
848
|
+
state.globstar = true;
|
|
849
|
+
consume(value);
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
const token = {
|
|
853
|
+
type: 'star',
|
|
854
|
+
value,
|
|
855
|
+
output: star
|
|
856
|
+
};
|
|
857
|
+
if (true === opts.bash) {
|
|
858
|
+
token.output = '.*?';
|
|
859
|
+
if ('bos' === prev.type || 'slash' === prev.type) token.output = nodot + token.output;
|
|
860
|
+
push(token);
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
if (prev && ('bracket' === prev.type || 'paren' === prev.type) && true === opts.regex) {
|
|
864
|
+
token.output = value;
|
|
865
|
+
push(token);
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
868
|
+
if (state.index === state.start || 'slash' === prev.type || 'dot' === prev.type) {
|
|
869
|
+
if ('dot' === prev.type) {
|
|
870
|
+
state.output += NO_DOT_SLASH;
|
|
871
|
+
prev.output += NO_DOT_SLASH;
|
|
872
|
+
} else if (true === opts.dot) {
|
|
873
|
+
state.output += NO_DOTS_SLASH;
|
|
874
|
+
prev.output += NO_DOTS_SLASH;
|
|
875
|
+
} else {
|
|
876
|
+
state.output += nodot;
|
|
877
|
+
prev.output += nodot;
|
|
878
|
+
}
|
|
879
|
+
if ('*' !== peek()) {
|
|
880
|
+
state.output += ONE_CHAR;
|
|
881
|
+
prev.output += ONE_CHAR;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
push(token);
|
|
885
|
+
}
|
|
886
|
+
while(state.brackets > 0){
|
|
887
|
+
if (true === opts.strictBrackets) throw new SyntaxError(syntaxError('closing', ']'));
|
|
888
|
+
state.output = utils.escapeLast(state.output, '[');
|
|
889
|
+
decrement('brackets');
|
|
890
|
+
}
|
|
891
|
+
while(state.parens > 0){
|
|
892
|
+
if (true === opts.strictBrackets) throw new SyntaxError(syntaxError('closing', ')'));
|
|
893
|
+
state.output = utils.escapeLast(state.output, '(');
|
|
894
|
+
decrement('parens');
|
|
895
|
+
}
|
|
896
|
+
while(state.braces > 0){
|
|
897
|
+
if (true === opts.strictBrackets) throw new SyntaxError(syntaxError('closing', '}'));
|
|
898
|
+
state.output = utils.escapeLast(state.output, '{');
|
|
899
|
+
decrement('braces');
|
|
900
|
+
}
|
|
901
|
+
if (true !== opts.strictSlashes && ('star' === prev.type || 'bracket' === prev.type)) push({
|
|
902
|
+
type: 'maybe_slash',
|
|
903
|
+
value: '',
|
|
904
|
+
output: `${SLASH_LITERAL}?`
|
|
905
|
+
});
|
|
906
|
+
if (true === state.backtrack) {
|
|
907
|
+
state.output = '';
|
|
908
|
+
for (const token of state.tokens){
|
|
909
|
+
state.output += null != token.output ? token.output : token.value;
|
|
910
|
+
if (token.suffix) state.output += token.suffix;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
return state;
|
|
914
|
+
};
|
|
915
|
+
parse.fastpaths = (input, options)=>{
|
|
916
|
+
const opts = {
|
|
917
|
+
...options
|
|
918
|
+
};
|
|
919
|
+
const max = 'number' == typeof opts.maxLength ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
|
|
920
|
+
const len = input.length;
|
|
921
|
+
if (len > max) throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`);
|
|
922
|
+
input = REPLACEMENTS[input] || input;
|
|
923
|
+
const { DOT_LITERAL, SLASH_LITERAL, ONE_CHAR, DOTS_SLASH, NO_DOT, NO_DOTS, NO_DOTS_SLASH, STAR, START_ANCHOR } = constants.globChars(opts.windows);
|
|
924
|
+
const nodot = opts.dot ? NO_DOTS : NO_DOT;
|
|
925
|
+
const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT;
|
|
926
|
+
const capture = opts.capture ? '' : '?:';
|
|
927
|
+
const state = {
|
|
928
|
+
negated: false,
|
|
929
|
+
prefix: ''
|
|
930
|
+
};
|
|
931
|
+
let star = true === opts.bash ? '.*?' : STAR;
|
|
932
|
+
if (opts.capture) star = `(${star})`;
|
|
933
|
+
const globstar = (opts)=>{
|
|
934
|
+
if (true === opts.noglobstar) return star;
|
|
935
|
+
return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
|
|
936
|
+
};
|
|
937
|
+
const create = (str)=>{
|
|
938
|
+
switch(str){
|
|
939
|
+
case '*':
|
|
940
|
+
return `${nodot}${ONE_CHAR}${star}`;
|
|
941
|
+
case '.*':
|
|
942
|
+
return `${DOT_LITERAL}${ONE_CHAR}${star}`;
|
|
943
|
+
case '*.*':
|
|
944
|
+
return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`;
|
|
945
|
+
case '*/*':
|
|
946
|
+
return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`;
|
|
947
|
+
case '**':
|
|
948
|
+
return nodot + globstar(opts);
|
|
949
|
+
case '**/*':
|
|
950
|
+
return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`;
|
|
951
|
+
case '**/*.*':
|
|
952
|
+
return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`;
|
|
953
|
+
case '**/.*':
|
|
954
|
+
return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`;
|
|
955
|
+
default:
|
|
956
|
+
{
|
|
957
|
+
const match = /^(.*?)\.(\w+)$/.exec(str);
|
|
958
|
+
if (!match) return;
|
|
959
|
+
const source = create(match[1]);
|
|
960
|
+
if (!source) return;
|
|
961
|
+
return source + DOT_LITERAL + match[2];
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
const output = utils.removePrefix(input, state);
|
|
966
|
+
let source = create(output);
|
|
967
|
+
if (source && true !== opts.strictSlashes) source += `${SLASH_LITERAL}?`;
|
|
968
|
+
return source;
|
|
969
|
+
};
|
|
970
|
+
module.exports = parse;
|
|
971
|
+
},
|
|
972
|
+
"../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/picomatch.js" (module, __unused_rspack_exports, __webpack_require__) {
|
|
973
|
+
const scan = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/scan.js");
|
|
974
|
+
const parse = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/parse.js");
|
|
975
|
+
const utils = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/utils.js");
|
|
976
|
+
const constants = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/constants.js");
|
|
977
|
+
const isObject = (val)=>val && 'object' == typeof val && !Array.isArray(val);
|
|
978
|
+
const picomatch = (glob, options, returnState = false)=>{
|
|
979
|
+
if (Array.isArray(glob)) {
|
|
980
|
+
const fns = glob.map((input)=>picomatch(input, options, returnState));
|
|
981
|
+
const arrayMatcher = (str)=>{
|
|
982
|
+
for (const isMatch of fns){
|
|
983
|
+
const state = isMatch(str);
|
|
984
|
+
if (state) return state;
|
|
985
|
+
}
|
|
986
|
+
return false;
|
|
987
|
+
};
|
|
988
|
+
return arrayMatcher;
|
|
989
|
+
}
|
|
990
|
+
const isState = isObject(glob) && glob.tokens && glob.input;
|
|
991
|
+
if ('' === glob || 'string' != typeof glob && !isState) throw new TypeError('Expected pattern to be a non-empty string');
|
|
992
|
+
const opts = options || {};
|
|
993
|
+
const posix = opts.windows;
|
|
994
|
+
const regex = isState ? picomatch.compileRe(glob, options) : picomatch.makeRe(glob, options, false, true);
|
|
995
|
+
const state = regex.state;
|
|
996
|
+
delete regex.state;
|
|
997
|
+
let isIgnored = ()=>false;
|
|
998
|
+
if (opts.ignore) {
|
|
999
|
+
const ignoreOpts = {
|
|
1000
|
+
...options,
|
|
1001
|
+
ignore: null,
|
|
1002
|
+
onMatch: null,
|
|
1003
|
+
onResult: null
|
|
1004
|
+
};
|
|
1005
|
+
isIgnored = picomatch(opts.ignore, ignoreOpts, returnState);
|
|
1006
|
+
}
|
|
1007
|
+
const matcher = (input, returnObject = false)=>{
|
|
1008
|
+
const { isMatch, match, output } = picomatch.test(input, regex, options, {
|
|
1009
|
+
glob,
|
|
1010
|
+
posix
|
|
1011
|
+
});
|
|
1012
|
+
const result = {
|
|
1013
|
+
glob,
|
|
1014
|
+
state,
|
|
1015
|
+
regex,
|
|
1016
|
+
posix,
|
|
1017
|
+
input,
|
|
1018
|
+
output,
|
|
1019
|
+
match,
|
|
1020
|
+
isMatch
|
|
1021
|
+
};
|
|
1022
|
+
if ('function' == typeof opts.onResult) opts.onResult(result);
|
|
1023
|
+
if (false === isMatch) {
|
|
1024
|
+
result.isMatch = false;
|
|
1025
|
+
return returnObject ? result : false;
|
|
1026
|
+
}
|
|
1027
|
+
if (isIgnored(input)) {
|
|
1028
|
+
if ('function' == typeof opts.onIgnore) opts.onIgnore(result);
|
|
1029
|
+
result.isMatch = false;
|
|
1030
|
+
return returnObject ? result : false;
|
|
1031
|
+
}
|
|
1032
|
+
if ('function' == typeof opts.onMatch) opts.onMatch(result);
|
|
1033
|
+
return returnObject ? result : true;
|
|
1034
|
+
};
|
|
1035
|
+
if (returnState) matcher.state = state;
|
|
1036
|
+
return matcher;
|
|
1037
|
+
};
|
|
1038
|
+
picomatch.test = (input, regex, options, { glob, posix } = {})=>{
|
|
1039
|
+
if ('string' != typeof input) throw new TypeError('Expected input to be a string');
|
|
1040
|
+
if ('' === input) return {
|
|
1041
|
+
isMatch: false,
|
|
1042
|
+
output: ''
|
|
1043
|
+
};
|
|
1044
|
+
const opts = options || {};
|
|
1045
|
+
const format = opts.format || (posix ? utils.toPosixSlashes : null);
|
|
1046
|
+
let match = input === glob;
|
|
1047
|
+
let output = match && format ? format(input) : input;
|
|
1048
|
+
if (false === match) {
|
|
1049
|
+
output = format ? format(input) : input;
|
|
1050
|
+
match = output === glob;
|
|
1051
|
+
}
|
|
1052
|
+
if (false === match || true === opts.capture) match = true === opts.matchBase || true === opts.basename ? picomatch.matchBase(input, regex, options, posix) : regex.exec(output);
|
|
1053
|
+
return {
|
|
1054
|
+
isMatch: Boolean(match),
|
|
1055
|
+
match,
|
|
1056
|
+
output
|
|
1057
|
+
};
|
|
1058
|
+
};
|
|
1059
|
+
picomatch.matchBase = (input, glob, options)=>{
|
|
1060
|
+
const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options);
|
|
1061
|
+
return regex.test(utils.basename(input));
|
|
1062
|
+
};
|
|
1063
|
+
picomatch.isMatch = (str, patterns, options)=>picomatch(patterns, options)(str);
|
|
1064
|
+
picomatch.parse = (pattern, options)=>{
|
|
1065
|
+
if (Array.isArray(pattern)) return pattern.map((p)=>picomatch.parse(p, options));
|
|
1066
|
+
return parse(pattern, {
|
|
1067
|
+
...options,
|
|
1068
|
+
fastpaths: false
|
|
1069
|
+
});
|
|
1070
|
+
};
|
|
1071
|
+
picomatch.scan = (input, options)=>scan(input, options);
|
|
1072
|
+
picomatch.compileRe = (state, options, returnOutput = false, returnState = false)=>{
|
|
1073
|
+
if (true === returnOutput) return state.output;
|
|
1074
|
+
const opts = options || {};
|
|
1075
|
+
const prepend = opts.contains ? '' : '^';
|
|
1076
|
+
const append = opts.contains ? '' : '$';
|
|
1077
|
+
let source = `${prepend}(?:${state.output})${append}`;
|
|
1078
|
+
if (state && true === state.negated) source = `^(?!${source}).*$`;
|
|
1079
|
+
const regex = picomatch.toRegex(source, options);
|
|
1080
|
+
if (true === returnState) regex.state = state;
|
|
1081
|
+
return regex;
|
|
1082
|
+
};
|
|
1083
|
+
picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false)=>{
|
|
1084
|
+
if (!input || 'string' != typeof input) throw new TypeError('Expected a non-empty string');
|
|
1085
|
+
let parsed = {
|
|
1086
|
+
negated: false,
|
|
1087
|
+
fastpaths: true
|
|
1088
|
+
};
|
|
1089
|
+
if (false !== options.fastpaths && ('.' === input[0] || '*' === input[0])) parsed.output = parse.fastpaths(input, options);
|
|
1090
|
+
if (!parsed.output) parsed = parse(input, options);
|
|
1091
|
+
return picomatch.compileRe(parsed, options, returnOutput, returnState);
|
|
1092
|
+
};
|
|
1093
|
+
picomatch.toRegex = (source, options)=>{
|
|
1094
|
+
try {
|
|
1095
|
+
const opts = options || {};
|
|
1096
|
+
return new RegExp(source, opts.flags || (opts.nocase ? 'i' : ''));
|
|
1097
|
+
} catch (err) {
|
|
1098
|
+
if (options && true === options.debug) throw err;
|
|
1099
|
+
return /$^/;
|
|
1100
|
+
}
|
|
1101
|
+
};
|
|
1102
|
+
picomatch.constants = constants;
|
|
1103
|
+
module.exports = picomatch;
|
|
1104
|
+
},
|
|
1105
|
+
"../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/scan.js" (module, __unused_rspack_exports, __webpack_require__) {
|
|
1106
|
+
const utils = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/utils.js");
|
|
1107
|
+
const { CHAR_ASTERISK, CHAR_AT, CHAR_BACKWARD_SLASH, CHAR_COMMA, CHAR_DOT, CHAR_EXCLAMATION_MARK, CHAR_FORWARD_SLASH, CHAR_LEFT_CURLY_BRACE, CHAR_LEFT_PARENTHESES, CHAR_LEFT_SQUARE_BRACKET, CHAR_PLUS, CHAR_QUESTION_MARK, CHAR_RIGHT_CURLY_BRACE, CHAR_RIGHT_PARENTHESES, CHAR_RIGHT_SQUARE_BRACKET } = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/constants.js");
|
|
1108
|
+
const isPathSeparator = (code)=>code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
|
|
1109
|
+
const depth = (token)=>{
|
|
1110
|
+
if (true !== token.isPrefix) token.depth = token.isGlobstar ? 1 / 0 : 1;
|
|
1111
|
+
};
|
|
1112
|
+
const scan = (input, options)=>{
|
|
1113
|
+
const opts = options || {};
|
|
1114
|
+
const length = input.length - 1;
|
|
1115
|
+
const scanToEnd = true === opts.parts || true === opts.scanToEnd;
|
|
1116
|
+
const slashes = [];
|
|
1117
|
+
const tokens = [];
|
|
1118
|
+
const parts = [];
|
|
1119
|
+
let str = input;
|
|
1120
|
+
let index = -1;
|
|
1121
|
+
let start = 0;
|
|
1122
|
+
let lastIndex = 0;
|
|
1123
|
+
let isBrace = false;
|
|
1124
|
+
let isBracket = false;
|
|
1125
|
+
let isGlob = false;
|
|
1126
|
+
let isExtglob = false;
|
|
1127
|
+
let isGlobstar = false;
|
|
1128
|
+
let braceEscaped = false;
|
|
1129
|
+
let backslashes = false;
|
|
1130
|
+
let negated = false;
|
|
1131
|
+
let negatedExtglob = false;
|
|
1132
|
+
let finished = false;
|
|
1133
|
+
let braces = 0;
|
|
1134
|
+
let prev;
|
|
1135
|
+
let code;
|
|
1136
|
+
let token = {
|
|
1137
|
+
value: '',
|
|
1138
|
+
depth: 0,
|
|
1139
|
+
isGlob: false
|
|
1140
|
+
};
|
|
1141
|
+
const eos = ()=>index >= length;
|
|
1142
|
+
const peek = ()=>str.charCodeAt(index + 1);
|
|
1143
|
+
const advance = ()=>{
|
|
1144
|
+
prev = code;
|
|
1145
|
+
return str.charCodeAt(++index);
|
|
1146
|
+
};
|
|
1147
|
+
while(index < length){
|
|
1148
|
+
code = advance();
|
|
1149
|
+
let next;
|
|
1150
|
+
if (code === CHAR_BACKWARD_SLASH) {
|
|
1151
|
+
backslashes = token.backslashes = true;
|
|
1152
|
+
code = advance();
|
|
1153
|
+
if (code === CHAR_LEFT_CURLY_BRACE) braceEscaped = true;
|
|
1154
|
+
continue;
|
|
1155
|
+
}
|
|
1156
|
+
if (true === braceEscaped || code === CHAR_LEFT_CURLY_BRACE) {
|
|
1157
|
+
braces++;
|
|
1158
|
+
while(true !== eos() && (code = advance())){
|
|
1159
|
+
if (code === CHAR_BACKWARD_SLASH) {
|
|
1160
|
+
backslashes = token.backslashes = true;
|
|
1161
|
+
advance();
|
|
1162
|
+
continue;
|
|
1163
|
+
}
|
|
1164
|
+
if (code === CHAR_LEFT_CURLY_BRACE) {
|
|
1165
|
+
braces++;
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
1168
|
+
if (true !== braceEscaped && code === CHAR_DOT && (code = advance()) === CHAR_DOT) {
|
|
1169
|
+
isBrace = token.isBrace = true;
|
|
1170
|
+
isGlob = token.isGlob = true;
|
|
1171
|
+
finished = true;
|
|
1172
|
+
if (true === scanToEnd) continue;
|
|
1173
|
+
break;
|
|
1174
|
+
}
|
|
1175
|
+
if (true !== braceEscaped && code === CHAR_COMMA) {
|
|
1176
|
+
isBrace = token.isBrace = true;
|
|
1177
|
+
isGlob = token.isGlob = true;
|
|
1178
|
+
finished = true;
|
|
1179
|
+
if (true === scanToEnd) continue;
|
|
1180
|
+
break;
|
|
1181
|
+
}
|
|
1182
|
+
if (code === CHAR_RIGHT_CURLY_BRACE) {
|
|
1183
|
+
braces--;
|
|
1184
|
+
if (0 === braces) {
|
|
1185
|
+
braceEscaped = false;
|
|
1186
|
+
isBrace = token.isBrace = true;
|
|
1187
|
+
finished = true;
|
|
1188
|
+
break;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
if (true === scanToEnd) continue;
|
|
1193
|
+
break;
|
|
1194
|
+
}
|
|
1195
|
+
if (code === CHAR_FORWARD_SLASH) {
|
|
1196
|
+
slashes.push(index);
|
|
1197
|
+
tokens.push(token);
|
|
1198
|
+
token = {
|
|
1199
|
+
value: '',
|
|
1200
|
+
depth: 0,
|
|
1201
|
+
isGlob: false
|
|
1202
|
+
};
|
|
1203
|
+
if (true === finished) continue;
|
|
1204
|
+
if (prev === CHAR_DOT && index === start + 1) {
|
|
1205
|
+
start += 2;
|
|
1206
|
+
continue;
|
|
1207
|
+
}
|
|
1208
|
+
lastIndex = index + 1;
|
|
1209
|
+
continue;
|
|
1210
|
+
}
|
|
1211
|
+
if (true !== opts.noext) {
|
|
1212
|
+
const isExtglobChar = code === CHAR_PLUS || code === CHAR_AT || code === CHAR_ASTERISK || code === CHAR_QUESTION_MARK || code === CHAR_EXCLAMATION_MARK;
|
|
1213
|
+
if (true === isExtglobChar && peek() === CHAR_LEFT_PARENTHESES) {
|
|
1214
|
+
isGlob = token.isGlob = true;
|
|
1215
|
+
isExtglob = token.isExtglob = true;
|
|
1216
|
+
finished = true;
|
|
1217
|
+
if (code === CHAR_EXCLAMATION_MARK && index === start) negatedExtglob = true;
|
|
1218
|
+
if (true === scanToEnd) {
|
|
1219
|
+
while(true !== eos() && (code = advance())){
|
|
1220
|
+
if (code === CHAR_BACKWARD_SLASH) {
|
|
1221
|
+
backslashes = token.backslashes = true;
|
|
1222
|
+
code = advance();
|
|
1223
|
+
continue;
|
|
1224
|
+
}
|
|
1225
|
+
if (code === CHAR_RIGHT_PARENTHESES) {
|
|
1226
|
+
isGlob = token.isGlob = true;
|
|
1227
|
+
finished = true;
|
|
1228
|
+
break;
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
break;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
if (code === CHAR_ASTERISK) {
|
|
1237
|
+
if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true;
|
|
1238
|
+
isGlob = token.isGlob = true;
|
|
1239
|
+
finished = true;
|
|
1240
|
+
if (true === scanToEnd) continue;
|
|
1241
|
+
break;
|
|
1242
|
+
}
|
|
1243
|
+
if (code === CHAR_QUESTION_MARK) {
|
|
1244
|
+
isGlob = token.isGlob = true;
|
|
1245
|
+
finished = true;
|
|
1246
|
+
if (true === scanToEnd) continue;
|
|
1247
|
+
break;
|
|
1248
|
+
}
|
|
1249
|
+
if (code === CHAR_LEFT_SQUARE_BRACKET) {
|
|
1250
|
+
while(true !== eos() && (next = advance())){
|
|
1251
|
+
if (next === CHAR_BACKWARD_SLASH) {
|
|
1252
|
+
backslashes = token.backslashes = true;
|
|
1253
|
+
advance();
|
|
1254
|
+
continue;
|
|
1255
|
+
}
|
|
1256
|
+
if (next === CHAR_RIGHT_SQUARE_BRACKET) {
|
|
1257
|
+
isBracket = token.isBracket = true;
|
|
1258
|
+
isGlob = token.isGlob = true;
|
|
1259
|
+
finished = true;
|
|
1260
|
+
break;
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
if (true === scanToEnd) continue;
|
|
1264
|
+
break;
|
|
1265
|
+
}
|
|
1266
|
+
if (true !== opts.nonegate && code === CHAR_EXCLAMATION_MARK && index === start) {
|
|
1267
|
+
negated = token.negated = true;
|
|
1268
|
+
start++;
|
|
1269
|
+
continue;
|
|
1270
|
+
}
|
|
1271
|
+
if (true !== opts.noparen && code === CHAR_LEFT_PARENTHESES) {
|
|
1272
|
+
isGlob = token.isGlob = true;
|
|
1273
|
+
if (true === scanToEnd) {
|
|
1274
|
+
while(true !== eos() && (code = advance())){
|
|
1275
|
+
if (code === CHAR_LEFT_PARENTHESES) {
|
|
1276
|
+
backslashes = token.backslashes = true;
|
|
1277
|
+
code = advance();
|
|
1278
|
+
continue;
|
|
1279
|
+
}
|
|
1280
|
+
if (code === CHAR_RIGHT_PARENTHESES) {
|
|
1281
|
+
finished = true;
|
|
1282
|
+
break;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
continue;
|
|
1286
|
+
}
|
|
1287
|
+
break;
|
|
1288
|
+
}
|
|
1289
|
+
if (true === isGlob) {
|
|
1290
|
+
finished = true;
|
|
1291
|
+
if (true === scanToEnd) continue;
|
|
1292
|
+
break;
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
if (true === opts.noext) {
|
|
1296
|
+
isExtglob = false;
|
|
1297
|
+
isGlob = false;
|
|
1298
|
+
}
|
|
1299
|
+
let base = str;
|
|
1300
|
+
let prefix = '';
|
|
1301
|
+
let glob = '';
|
|
1302
|
+
if (start > 0) {
|
|
1303
|
+
prefix = str.slice(0, start);
|
|
1304
|
+
str = str.slice(start);
|
|
1305
|
+
lastIndex -= start;
|
|
1306
|
+
}
|
|
1307
|
+
if (base && true === isGlob && lastIndex > 0) {
|
|
1308
|
+
base = str.slice(0, lastIndex);
|
|
1309
|
+
glob = str.slice(lastIndex);
|
|
1310
|
+
} else if (true === isGlob) {
|
|
1311
|
+
base = '';
|
|
1312
|
+
glob = str;
|
|
1313
|
+
} else base = str;
|
|
1314
|
+
if (base && '' !== base && '/' !== base && base !== str) {
|
|
1315
|
+
if (isPathSeparator(base.charCodeAt(base.length - 1))) base = base.slice(0, -1);
|
|
1316
|
+
}
|
|
1317
|
+
if (true === opts.unescape) {
|
|
1318
|
+
if (glob) glob = utils.removeBackslashes(glob);
|
|
1319
|
+
if (base && true === backslashes) base = utils.removeBackslashes(base);
|
|
1320
|
+
}
|
|
1321
|
+
const state = {
|
|
1322
|
+
prefix,
|
|
1323
|
+
input,
|
|
1324
|
+
start,
|
|
1325
|
+
base,
|
|
1326
|
+
glob,
|
|
1327
|
+
isBrace,
|
|
1328
|
+
isBracket,
|
|
1329
|
+
isGlob,
|
|
1330
|
+
isExtglob,
|
|
1331
|
+
isGlobstar,
|
|
1332
|
+
negated,
|
|
1333
|
+
negatedExtglob
|
|
1334
|
+
};
|
|
1335
|
+
if (true === opts.tokens) {
|
|
1336
|
+
state.maxDepth = 0;
|
|
1337
|
+
if (!isPathSeparator(code)) tokens.push(token);
|
|
1338
|
+
state.tokens = tokens;
|
|
1339
|
+
}
|
|
1340
|
+
if (true === opts.parts || true === opts.tokens) {
|
|
1341
|
+
let prevIndex;
|
|
1342
|
+
for(let idx = 0; idx < slashes.length; idx++){
|
|
1343
|
+
const n = prevIndex ? prevIndex + 1 : start;
|
|
1344
|
+
const i = slashes[idx];
|
|
1345
|
+
const value = input.slice(n, i);
|
|
1346
|
+
if (opts.tokens) {
|
|
1347
|
+
if (0 === idx && 0 !== start) {
|
|
1348
|
+
tokens[idx].isPrefix = true;
|
|
1349
|
+
tokens[idx].value = prefix;
|
|
1350
|
+
} else tokens[idx].value = value;
|
|
1351
|
+
depth(tokens[idx]);
|
|
1352
|
+
state.maxDepth += tokens[idx].depth;
|
|
1353
|
+
}
|
|
1354
|
+
if (0 !== idx || '' !== value) parts.push(value);
|
|
1355
|
+
prevIndex = i;
|
|
1356
|
+
}
|
|
1357
|
+
if (prevIndex && prevIndex + 1 < input.length) {
|
|
1358
|
+
const value = input.slice(prevIndex + 1);
|
|
1359
|
+
parts.push(value);
|
|
1360
|
+
if (opts.tokens) {
|
|
1361
|
+
tokens[tokens.length - 1].value = value;
|
|
1362
|
+
depth(tokens[tokens.length - 1]);
|
|
1363
|
+
state.maxDepth += tokens[tokens.length - 1].depth;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
state.slashes = slashes;
|
|
1367
|
+
state.parts = parts;
|
|
1368
|
+
}
|
|
1369
|
+
return state;
|
|
1370
|
+
};
|
|
1371
|
+
module.exports = scan;
|
|
1372
|
+
},
|
|
1373
|
+
"../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/utils.js" (__unused_rspack_module, exports, __webpack_require__) {
|
|
1374
|
+
const { REGEX_BACKSLASH, REGEX_REMOVE_BACKSLASH, REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL } = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/constants.js");
|
|
1375
|
+
exports.isObject = (val)=>null !== val && 'object' == typeof val && !Array.isArray(val);
|
|
1376
|
+
exports.hasRegexChars = (str)=>REGEX_SPECIAL_CHARS.test(str);
|
|
1377
|
+
exports.isRegexChar = (str)=>1 === str.length && exports.hasRegexChars(str);
|
|
1378
|
+
exports.escapeRegex = (str)=>str.replace(REGEX_SPECIAL_CHARS_GLOBAL, '\\$1');
|
|
1379
|
+
exports.toPosixSlashes = (str)=>str.replace(REGEX_BACKSLASH, '/');
|
|
1380
|
+
exports.isWindows = ()=>{
|
|
1381
|
+
if ("u" > typeof navigator && navigator.platform) {
|
|
1382
|
+
const platform = navigator.platform.toLowerCase();
|
|
1383
|
+
return 'win32' === platform || 'windows' === platform;
|
|
1384
|
+
}
|
|
1385
|
+
if ("u" > typeof process && process.platform) return 'win32' === process.platform;
|
|
1386
|
+
return false;
|
|
1387
|
+
};
|
|
1388
|
+
exports.removeBackslashes = (str)=>str.replace(REGEX_REMOVE_BACKSLASH, (match)=>'\\' === match ? '' : match);
|
|
1389
|
+
exports.escapeLast = (input, char, lastIdx)=>{
|
|
1390
|
+
const idx = input.lastIndexOf(char, lastIdx);
|
|
1391
|
+
if (-1 === idx) return input;
|
|
1392
|
+
if ('\\' === input[idx - 1]) return exports.escapeLast(input, char, idx - 1);
|
|
1393
|
+
return `${input.slice(0, idx)}\\${input.slice(idx)}`;
|
|
1394
|
+
};
|
|
1395
|
+
exports.removePrefix = (input, state = {})=>{
|
|
1396
|
+
let output = input;
|
|
1397
|
+
if (output.startsWith('./')) {
|
|
1398
|
+
output = output.slice(2);
|
|
1399
|
+
state.prefix = './';
|
|
1400
|
+
}
|
|
1401
|
+
return output;
|
|
1402
|
+
};
|
|
1403
|
+
exports.wrapOutput = (input, state = {}, options = {})=>{
|
|
1404
|
+
const prepend = options.contains ? '' : '^';
|
|
1405
|
+
const append = options.contains ? '' : '$';
|
|
1406
|
+
let output = `${prepend}(?:${input})${append}`;
|
|
1407
|
+
if (true === state.negated) output = `(?:^(?!${output}).*$)`;
|
|
1408
|
+
return output;
|
|
1409
|
+
};
|
|
1410
|
+
exports.basename = (path, { windows } = {})=>{
|
|
1411
|
+
const segs = path.split(windows ? /[\\/]/ : '/');
|
|
1412
|
+
const last = segs[segs.length - 1];
|
|
1413
|
+
if ('' === last) return segs[segs.length - 2];
|
|
1414
|
+
return last;
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1417
|
+
});
|
|
1418
|
+
const TYPE_REQUEST = "q";
|
|
1419
|
+
const TYPE_RESPONSE = "s";
|
|
1420
|
+
const DEFAULT_TIMEOUT = 6e4;
|
|
1421
|
+
function defaultSerialize(i) {
|
|
1422
|
+
return i;
|
|
1423
|
+
}
|
|
1424
|
+
const defaultDeserialize = defaultSerialize;
|
|
1425
|
+
const { clearTimeout: dist_clearTimeout, setTimeout: dist_setTimeout } = globalThis;
|
|
1426
|
+
const random = Math.random.bind(Math);
|
|
1427
|
+
function createBirpc($functions, options) {
|
|
1428
|
+
const { post, on, off = ()=>{}, eventNames = [], serialize = defaultSerialize, deserialize = defaultDeserialize, resolver, bind = "rpc", timeout = DEFAULT_TIMEOUT } = options;
|
|
1429
|
+
let $closed = false;
|
|
1430
|
+
const _rpcPromiseMap = /* @__PURE__ */ new Map();
|
|
1431
|
+
let _promiseInit;
|
|
1432
|
+
let rpc;
|
|
1433
|
+
async function _call(method, args, event, optional) {
|
|
1434
|
+
if ($closed) throw new Error(`[birpc] rpc is closed, cannot call "${method}"`);
|
|
1435
|
+
const req = {
|
|
1436
|
+
m: method,
|
|
1437
|
+
a: args,
|
|
1438
|
+
t: TYPE_REQUEST
|
|
1439
|
+
};
|
|
1440
|
+
if (optional) req.o = true;
|
|
1441
|
+
const send = async (_req)=>post(serialize(_req));
|
|
1442
|
+
if (event) return void await send(req);
|
|
1443
|
+
if (_promiseInit) try {
|
|
1444
|
+
await _promiseInit;
|
|
1445
|
+
} finally{
|
|
1446
|
+
_promiseInit = void 0;
|
|
1447
|
+
}
|
|
1448
|
+
let { promise, resolve, reject } = createPromiseWithResolvers();
|
|
1449
|
+
const id = nanoid();
|
|
1450
|
+
req.i = id;
|
|
1451
|
+
let timeoutId;
|
|
1452
|
+
async function handler(newReq = req) {
|
|
1453
|
+
if (timeout >= 0) {
|
|
1454
|
+
timeoutId = dist_setTimeout(()=>{
|
|
1455
|
+
try {
|
|
1456
|
+
const handleResult = options.onTimeoutError?.call(rpc, method, args);
|
|
1457
|
+
if (true !== handleResult) throw new Error(`[birpc] timeout on calling "${method}"`);
|
|
1458
|
+
} catch (e) {
|
|
1459
|
+
reject(e);
|
|
1460
|
+
}
|
|
1461
|
+
_rpcPromiseMap.delete(id);
|
|
1462
|
+
}, timeout);
|
|
1463
|
+
if ("object" == typeof timeoutId) timeoutId = timeoutId.unref?.();
|
|
1464
|
+
}
|
|
1465
|
+
_rpcPromiseMap.set(id, {
|
|
1466
|
+
resolve,
|
|
1467
|
+
reject,
|
|
1468
|
+
timeoutId,
|
|
1469
|
+
method
|
|
1470
|
+
});
|
|
1471
|
+
await send(newReq);
|
|
1472
|
+
return promise;
|
|
1473
|
+
}
|
|
1474
|
+
try {
|
|
1475
|
+
if (options.onRequest) await options.onRequest.call(rpc, req, handler, resolve);
|
|
1476
|
+
else await handler();
|
|
1477
|
+
} catch (e) {
|
|
1478
|
+
if (options.onGeneralError?.call(rpc, e) !== true) throw e;
|
|
1479
|
+
return;
|
|
1480
|
+
} finally{
|
|
1481
|
+
dist_clearTimeout(timeoutId);
|
|
1482
|
+
_rpcPromiseMap.delete(id);
|
|
1483
|
+
}
|
|
1484
|
+
return promise;
|
|
1485
|
+
}
|
|
1486
|
+
const $call = (method, ...args)=>_call(method, args, false);
|
|
1487
|
+
const $callOptional = (method, ...args)=>_call(method, args, false, true);
|
|
1488
|
+
const $callEvent = (method, ...args)=>_call(method, args, true);
|
|
1489
|
+
const $callRaw = (options2)=>_call(options2.method, options2.args, options2.event, options2.optional);
|
|
1490
|
+
const builtinMethods = {
|
|
1491
|
+
$call,
|
|
1492
|
+
$callOptional,
|
|
1493
|
+
$callEvent,
|
|
1494
|
+
$callRaw,
|
|
1495
|
+
$rejectPendingCalls,
|
|
1496
|
+
get $closed () {
|
|
1497
|
+
return $closed;
|
|
1498
|
+
},
|
|
1499
|
+
get $meta () {
|
|
1500
|
+
return options.meta;
|
|
1501
|
+
},
|
|
1502
|
+
$close,
|
|
1503
|
+
$functions
|
|
1504
|
+
};
|
|
1505
|
+
rpc = new Proxy({}, {
|
|
1506
|
+
get (_, method) {
|
|
1507
|
+
if (Object.prototype.hasOwnProperty.call(builtinMethods, method)) return builtinMethods[method];
|
|
1508
|
+
if ("then" === method && !eventNames.includes("then") && !("then" in $functions)) return;
|
|
1509
|
+
const sendEvent = (...args)=>_call(method, args, true);
|
|
1510
|
+
if (eventNames.includes(method)) {
|
|
1511
|
+
sendEvent.asEvent = sendEvent;
|
|
1512
|
+
return sendEvent;
|
|
1513
|
+
}
|
|
1514
|
+
const sendCall = (...args)=>_call(method, args, false);
|
|
1515
|
+
sendCall.asEvent = sendEvent;
|
|
1516
|
+
return sendCall;
|
|
1517
|
+
}
|
|
1518
|
+
});
|
|
1519
|
+
function $close(customError) {
|
|
1520
|
+
$closed = true;
|
|
1521
|
+
_rpcPromiseMap.forEach(({ reject, method })=>{
|
|
1522
|
+
const error = new Error(`[birpc] rpc is closed, cannot call "${method}"`);
|
|
1523
|
+
if (customError) {
|
|
1524
|
+
customError.cause ??= error;
|
|
1525
|
+
return reject(customError);
|
|
1526
|
+
}
|
|
1527
|
+
reject(error);
|
|
1528
|
+
});
|
|
1529
|
+
_rpcPromiseMap.clear();
|
|
1530
|
+
off(onMessage);
|
|
1531
|
+
}
|
|
1532
|
+
function $rejectPendingCalls(handler) {
|
|
1533
|
+
const entries = Array.from(_rpcPromiseMap.values());
|
|
1534
|
+
const handlerResults = entries.map(({ method, reject })=>{
|
|
1535
|
+
if (!handler) return reject(new Error(`[birpc]: rejected pending call "${method}".`));
|
|
1536
|
+
return handler({
|
|
1537
|
+
method,
|
|
1538
|
+
reject
|
|
1539
|
+
});
|
|
1540
|
+
});
|
|
1541
|
+
_rpcPromiseMap.clear();
|
|
1542
|
+
return handlerResults;
|
|
1543
|
+
}
|
|
1544
|
+
async function onMessage(data, ...extra) {
|
|
1545
|
+
let msg;
|
|
1546
|
+
try {
|
|
1547
|
+
msg = deserialize(data);
|
|
1548
|
+
} catch (e) {
|
|
1549
|
+
if (options.onGeneralError?.call(rpc, e) !== true) throw e;
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
if (msg.t === TYPE_REQUEST) {
|
|
1553
|
+
const { m: method, a: args, o: optional } = msg;
|
|
1554
|
+
let result, error;
|
|
1555
|
+
let fn = await (resolver ? resolver.call(rpc, method, $functions[method]) : $functions[method]);
|
|
1556
|
+
if (optional) fn ||= ()=>void 0;
|
|
1557
|
+
if (fn) try {
|
|
1558
|
+
result = await fn.apply("rpc" === bind ? rpc : $functions, args);
|
|
1559
|
+
} catch (e) {
|
|
1560
|
+
error = e;
|
|
1561
|
+
}
|
|
1562
|
+
else error = new Error(`[birpc] function "${method}" not found`);
|
|
1563
|
+
if (msg.i) {
|
|
1564
|
+
if (error && options.onError) options.onError.call(rpc, error, method, args);
|
|
1565
|
+
if (error && options.onFunctionError) {
|
|
1566
|
+
if (true === options.onFunctionError.call(rpc, error, method, args)) return;
|
|
1567
|
+
}
|
|
1568
|
+
if (!error) try {
|
|
1569
|
+
await post(serialize({
|
|
1570
|
+
t: TYPE_RESPONSE,
|
|
1571
|
+
i: msg.i,
|
|
1572
|
+
r: result
|
|
1573
|
+
}), ...extra);
|
|
1574
|
+
return;
|
|
1575
|
+
} catch (e) {
|
|
1576
|
+
error = e;
|
|
1577
|
+
if (options.onGeneralError?.call(rpc, e, method, args) !== true) throw e;
|
|
1578
|
+
}
|
|
1579
|
+
try {
|
|
1580
|
+
await post(serialize({
|
|
1581
|
+
t: TYPE_RESPONSE,
|
|
1582
|
+
i: msg.i,
|
|
1583
|
+
e: error
|
|
1584
|
+
}), ...extra);
|
|
1585
|
+
} catch (e) {
|
|
1586
|
+
if (options.onGeneralError?.call(rpc, e, method, args) !== true) throw e;
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
} else {
|
|
1590
|
+
const { i: ack, r: result, e: error } = msg;
|
|
1591
|
+
const promise = _rpcPromiseMap.get(ack);
|
|
1592
|
+
if (promise) {
|
|
1593
|
+
dist_clearTimeout(promise.timeoutId);
|
|
1594
|
+
if (error) promise.reject(error);
|
|
1595
|
+
else promise.resolve(result);
|
|
1596
|
+
}
|
|
1597
|
+
_rpcPromiseMap.delete(ack);
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
_promiseInit = on(onMessage);
|
|
1601
|
+
return rpc;
|
|
1602
|
+
}
|
|
1603
|
+
function createPromiseWithResolvers() {
|
|
1604
|
+
let resolve;
|
|
1605
|
+
let reject;
|
|
1606
|
+
const promise = new Promise((res, rej)=>{
|
|
1607
|
+
resolve = res;
|
|
1608
|
+
reject = rej;
|
|
1609
|
+
});
|
|
1610
|
+
return {
|
|
1611
|
+
promise,
|
|
1612
|
+
resolve,
|
|
1613
|
+
reject
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
1617
|
+
function nanoid(size = 21) {
|
|
1618
|
+
let id = "";
|
|
1619
|
+
let i = size;
|
|
1620
|
+
while(i--)id += urlAlphabet[64 * random() | 0];
|
|
1621
|
+
return id;
|
|
1622
|
+
}
|
|
1623
|
+
const picomatch = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/index.js");
|
|
1624
|
+
const { createRsbuild: createRsbuild, rspack: rspack } = rsbuild;
|
|
1625
|
+
const hostController_dirname = dirname(fileURLToPath(import.meta.url));
|
|
1626
|
+
class ContainerRpcManager {
|
|
1627
|
+
wss;
|
|
1628
|
+
ws = null;
|
|
1629
|
+
rpc = null;
|
|
1630
|
+
methods;
|
|
1631
|
+
constructor(wss, methods){
|
|
1632
|
+
this.wss = wss;
|
|
1633
|
+
this.methods = methods;
|
|
1634
|
+
this.setupConnectionHandler();
|
|
1635
|
+
}
|
|
1636
|
+
updateMethods(methods) {
|
|
1637
|
+
this.methods = methods;
|
|
1638
|
+
if (this.ws && this.ws.readyState === this.ws.OPEN) this.attachWebSocket(this.ws);
|
|
1639
|
+
}
|
|
1640
|
+
setupConnectionHandler() {
|
|
1641
|
+
this.wss.on('connection', (ws)=>{
|
|
1642
|
+
logger.log(color.gray('[Browser UI] Container WebSocket connected'));
|
|
1643
|
+
logger.log(color.gray(`[Browser UI] Current ws: ${this.ws ? 'exists' : 'null'}, new ws: ${ws ? 'exists' : 'null'}`));
|
|
1644
|
+
this.attachWebSocket(ws);
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
attachWebSocket(ws) {
|
|
1648
|
+
this.ws = ws;
|
|
1649
|
+
this.rpc = createBirpc(this.methods, {
|
|
1650
|
+
post: (data)=>{
|
|
1651
|
+
if (ws.readyState === ws.OPEN) ws.send(JSON.stringify(data));
|
|
1652
|
+
},
|
|
1653
|
+
on: (fn)=>{
|
|
1654
|
+
ws.on('message', (message)=>{
|
|
1655
|
+
try {
|
|
1656
|
+
const data = JSON.parse(message.toString());
|
|
1657
|
+
fn(data);
|
|
1658
|
+
} catch {}
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
});
|
|
1662
|
+
ws.on('close', ()=>{
|
|
1663
|
+
if (this.ws === ws) {
|
|
1664
|
+
this.ws = null;
|
|
1665
|
+
this.rpc = null;
|
|
1666
|
+
}
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1669
|
+
get isConnected() {
|
|
1670
|
+
return null !== this.ws && this.ws.readyState === this.ws.OPEN;
|
|
1671
|
+
}
|
|
1672
|
+
get currentWebSocket() {
|
|
1673
|
+
return this.ws;
|
|
1674
|
+
}
|
|
1675
|
+
reattach(ws) {
|
|
1676
|
+
this.attachWebSocket(ws);
|
|
1677
|
+
}
|
|
1678
|
+
async notifyTestFileUpdate(files) {
|
|
1679
|
+
await this.rpc?.onTestFileUpdate(files);
|
|
1680
|
+
}
|
|
1681
|
+
async reloadTestFile(testFile, testNamePattern) {
|
|
1682
|
+
logger.log(color.gray(`[Browser UI] reloadTestFile called, rpc: ${this.rpc ? 'exists' : 'null'}, ws: ${this.ws ? 'exists' : 'null'}`));
|
|
1683
|
+
if (!this.rpc) return void logger.log(color.yellow('[Browser UI] RPC not available, skipping reloadTestFile'));
|
|
1684
|
+
logger.log(color.gray(`[Browser UI] Calling reloadTestFile: ${testFile}`));
|
|
1685
|
+
await this.rpc.reloadTestFile(testFile, testNamePattern);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
const watchContext = {
|
|
1689
|
+
runtime: null,
|
|
1690
|
+
lastTestFiles: [],
|
|
1691
|
+
hooksEnabled: false,
|
|
1692
|
+
cleanupRegistered: false
|
|
1693
|
+
};
|
|
1694
|
+
const ensureProcessExitCode = (code)=>{
|
|
1695
|
+
if (void 0 === process.exitCode || 0 === process.exitCode) process.exitCode = code;
|
|
1696
|
+
};
|
|
1697
|
+
const globToRegexp = (glob)=>{
|
|
1698
|
+
const regex = picomatch.makeRe(glob, {
|
|
1699
|
+
fastpaths: false,
|
|
1700
|
+
noglobstar: false,
|
|
1701
|
+
bash: false
|
|
1702
|
+
});
|
|
1703
|
+
if (!regex) throw new Error(`Invalid glob pattern: ${glob}`);
|
|
1704
|
+
if (!glob.startsWith('./')) return regex;
|
|
1705
|
+
return new RegExp([
|
|
1706
|
+
'^\\.',
|
|
1707
|
+
glob.startsWith('./**') ? '' : '[\\\\/]',
|
|
1708
|
+
regex.source.substring(1)
|
|
1709
|
+
].join(''));
|
|
1710
|
+
};
|
|
1711
|
+
const globPatternsToRegExp = (patterns)=>{
|
|
1712
|
+
const regexParts = patterns.map((pattern)=>{
|
|
1713
|
+
const regex = globToRegexp(pattern);
|
|
1714
|
+
let source = regex.source;
|
|
1715
|
+
if (source.startsWith('^')) source = source.substring(1);
|
|
1716
|
+
if (source.endsWith('$')) source = source.substring(0, source.length - 1);
|
|
1717
|
+
return source;
|
|
1718
|
+
});
|
|
1719
|
+
return new RegExp(`(?:${regexParts.join('|')})$`);
|
|
1720
|
+
};
|
|
1721
|
+
const excludePatternsToRegExp = (patterns)=>{
|
|
1722
|
+
const keywords = [];
|
|
1723
|
+
for (const pattern of patterns){
|
|
1724
|
+
const match = pattern.match(/\*\*\/\.?\{?([^/*{}]+(?:,[^/*{}]+)*)\}?\/?\*?\*?/);
|
|
1725
|
+
if (match) {
|
|
1726
|
+
const parts = match[1].split(',');
|
|
1727
|
+
for (const part of parts){
|
|
1728
|
+
const cleaned = part.replace(/^\./, '');
|
|
1729
|
+
if (cleaned && !keywords.includes(cleaned)) keywords.push(cleaned);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
if (0 === keywords.length) return null;
|
|
1734
|
+
return new RegExp(`[\\\\/](${keywords.join('|')})[\\\\/]`);
|
|
1735
|
+
};
|
|
1736
|
+
const getRuntimeConfigFromProject = (project)=>{
|
|
1737
|
+
const { testNamePattern, testTimeout, passWithNoTests, retry, globals, clearMocks, resetMocks, restoreMocks, unstubEnvs, unstubGlobals, maxConcurrency, printConsoleTrace, disableConsoleIntercept, testEnvironment, hookTimeout, isolate, coverage, snapshotFormat, env, bail, logHeapUsage, chaiConfig, includeTaskLocation } = project.normalizedConfig;
|
|
1738
|
+
return {
|
|
1739
|
+
env,
|
|
1740
|
+
testNamePattern,
|
|
1741
|
+
testTimeout,
|
|
1742
|
+
hookTimeout,
|
|
1743
|
+
passWithNoTests,
|
|
1744
|
+
retry,
|
|
1745
|
+
globals,
|
|
1746
|
+
clearMocks,
|
|
1747
|
+
resetMocks,
|
|
1748
|
+
restoreMocks,
|
|
1749
|
+
unstubEnvs,
|
|
1750
|
+
unstubGlobals,
|
|
1751
|
+
maxConcurrency,
|
|
1752
|
+
printConsoleTrace,
|
|
1753
|
+
disableConsoleIntercept,
|
|
1754
|
+
testEnvironment,
|
|
1755
|
+
isolate,
|
|
1756
|
+
coverage,
|
|
1757
|
+
snapshotFormat,
|
|
1758
|
+
bail,
|
|
1759
|
+
logHeapUsage,
|
|
1760
|
+
chaiConfig,
|
|
1761
|
+
includeTaskLocation
|
|
1762
|
+
};
|
|
1763
|
+
};
|
|
1764
|
+
const getBrowserProjects = (context)=>context.projects.filter((project)=>project.normalizedConfig.browser.enabled);
|
|
1765
|
+
const collectProjectEntries = async (context)=>{
|
|
1766
|
+
const projectEntries = [];
|
|
1767
|
+
const browserProjects = getBrowserProjects(context);
|
|
1768
|
+
for (const project of browserProjects){
|
|
1769
|
+
const { normalizedConfig: { include, exclude, includeSource, setupFiles } } = project;
|
|
1770
|
+
const tests = await getTestEntries({
|
|
1771
|
+
include,
|
|
1772
|
+
exclude: exclude.patterns,
|
|
1773
|
+
includeSource,
|
|
1774
|
+
rootPath: context.rootPath,
|
|
1775
|
+
projectRoot: project.rootPath,
|
|
1776
|
+
fileFilters: context.fileFilters || []
|
|
1777
|
+
});
|
|
1778
|
+
const setup = getSetupFiles(setupFiles, project.rootPath);
|
|
1779
|
+
projectEntries.push({
|
|
1780
|
+
project,
|
|
1781
|
+
setupFiles: Object.values(setup),
|
|
1782
|
+
testFiles: Object.values(tests)
|
|
1783
|
+
});
|
|
1784
|
+
}
|
|
1785
|
+
return projectEntries;
|
|
1786
|
+
};
|
|
1787
|
+
const resolveBrowserFile = (relativePath)=>{
|
|
1788
|
+
const candidates = [
|
|
1789
|
+
external_pathe_resolve(hostController_dirname, '../src', relativePath),
|
|
1790
|
+
external_pathe_resolve(hostController_dirname, relativePath)
|
|
1791
|
+
];
|
|
1792
|
+
for (const candidate of candidates)if (existsSync(candidate)) return candidate;
|
|
1793
|
+
throw new Error(`Unable to resolve browser client file: ${relativePath}`);
|
|
1794
|
+
};
|
|
1795
|
+
const resolveContainerDist = ()=>{
|
|
1796
|
+
const distPath = external_pathe_resolve(hostController_dirname, 'browser-container');
|
|
1797
|
+
if (existsSync(distPath)) return distPath;
|
|
1798
|
+
throw new Error(`Browser container build not found at ${distPath}. Please run "pnpm --filter @rstest/browser build".`);
|
|
1799
|
+
};
|
|
1800
|
+
const toSafeVarName = (name)=>name.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
1801
|
+
const generateManifestModule = ({ manifestPath, entries })=>{
|
|
1802
|
+
const manifestDirPosix = normalize(dirname(manifestPath));
|
|
1803
|
+
const toRelativeImport = (filePath)=>{
|
|
1804
|
+
const posixPath = normalize(filePath);
|
|
1805
|
+
let relativePath = relative(manifestDirPosix, posixPath);
|
|
1806
|
+
if (!relativePath.startsWith('.')) relativePath = `./${relativePath}`;
|
|
1807
|
+
return relativePath;
|
|
1808
|
+
};
|
|
1809
|
+
const lines = [];
|
|
1810
|
+
lines.push('// All projects configuration');
|
|
1811
|
+
lines.push('export const projects = [');
|
|
1812
|
+
for (const { project } of entries){
|
|
1813
|
+
lines.push(' {');
|
|
1814
|
+
lines.push(` name: ${JSON.stringify(project.name)},`);
|
|
1815
|
+
lines.push(` environmentName: ${JSON.stringify(project.environmentName)},`);
|
|
1816
|
+
lines.push(` projectRoot: ${JSON.stringify(normalize(project.rootPath))},`);
|
|
1817
|
+
lines.push(' },');
|
|
1818
|
+
}
|
|
1819
|
+
lines.push('];');
|
|
1820
|
+
lines.push('');
|
|
1821
|
+
lines.push('// Setup loaders for each project');
|
|
1822
|
+
lines.push('export const projectSetupLoaders = {');
|
|
1823
|
+
for (const { project, setupFiles } of entries){
|
|
1824
|
+
lines.push(` ${JSON.stringify(project.name)}: [`);
|
|
1825
|
+
for (const filePath of setupFiles){
|
|
1826
|
+
const relativePath = toRelativeImport(filePath);
|
|
1827
|
+
lines.push(` () => import(${JSON.stringify(relativePath)}),`);
|
|
1828
|
+
}
|
|
1829
|
+
lines.push(' ],');
|
|
1830
|
+
}
|
|
1831
|
+
lines.push('};');
|
|
1832
|
+
lines.push('');
|
|
1833
|
+
lines.push('// Test context for each project');
|
|
1834
|
+
for (const { project } of entries){
|
|
1835
|
+
const varName = `context_${toSafeVarName(project.environmentName)}`;
|
|
1836
|
+
const projectRootPosix = normalize(project.rootPath);
|
|
1837
|
+
const includeRegExp = globPatternsToRegExp(project.normalizedConfig.include);
|
|
1838
|
+
const excludePatterns = project.normalizedConfig.exclude.patterns;
|
|
1839
|
+
const excludeRegExp = excludePatternsToRegExp(excludePatterns);
|
|
1840
|
+
lines.push(`const ${varName} = import.meta.webpackContext(${JSON.stringify(projectRootPosix)}, {`);
|
|
1841
|
+
lines.push(' recursive: true,');
|
|
1842
|
+
lines.push(` regExp: ${includeRegExp.toString()},`);
|
|
1843
|
+
if (excludeRegExp) lines.push(` exclude: ${excludeRegExp.toString()},`);
|
|
1844
|
+
lines.push(" mode: 'lazy',");
|
|
1845
|
+
lines.push('});');
|
|
1846
|
+
lines.push('');
|
|
1847
|
+
}
|
|
1848
|
+
lines.push('export const projectTestContexts = {');
|
|
1849
|
+
for (const { project } of entries){
|
|
1850
|
+
const varName = `context_${toSafeVarName(project.environmentName)}`;
|
|
1851
|
+
lines.push(` ${JSON.stringify(project.name)}: {`);
|
|
1852
|
+
lines.push(` getTestKeys: () => ${varName}.keys(),`);
|
|
1853
|
+
lines.push(` loadTest: (key) => ${varName}(key),`);
|
|
1854
|
+
lines.push(` projectRoot: ${JSON.stringify(normalize(project.rootPath))},`);
|
|
1855
|
+
lines.push(' },');
|
|
1856
|
+
}
|
|
1857
|
+
lines.push('};');
|
|
1858
|
+
lines.push('');
|
|
1859
|
+
lines.push('// Backward compatibility: export first project as default');
|
|
1860
|
+
lines.push('export const projectConfig = projects[0];');
|
|
1861
|
+
lines.push('export const setupLoaders = projectSetupLoaders[projects[0].name] || [];');
|
|
1862
|
+
lines.push('const _defaultCtx = projectTestContexts[projects[0].name];');
|
|
1863
|
+
lines.push('export const getTestKeys = () => _defaultCtx ? _defaultCtx.getTestKeys() : [];');
|
|
1864
|
+
lines.push('export const loadTest = (key) => _defaultCtx ? _defaultCtx.loadTest(key) : Promise.reject(new Error("No project found"));');
|
|
1865
|
+
return `${lines.join('\n')}\n`;
|
|
1866
|
+
};
|
|
1867
|
+
const htmlTemplate = `<!DOCTYPE html>
|
|
1868
|
+
<html lang="en">
|
|
1869
|
+
<head>
|
|
1870
|
+
<meta charset="UTF-8" />
|
|
1871
|
+
<title>Rstest Browser Runner</title>
|
|
1872
|
+
</head>
|
|
1873
|
+
<body>
|
|
1874
|
+
<script type="module" src="/static/js/runner.js"></script>
|
|
1875
|
+
</body>
|
|
1876
|
+
</html>
|
|
1877
|
+
`;
|
|
1878
|
+
const destroyBrowserRuntime = async (runtime)=>{
|
|
1879
|
+
try {
|
|
1880
|
+
await runtime.browser?.close?.();
|
|
1881
|
+
} catch {}
|
|
1882
|
+
try {
|
|
1883
|
+
await runtime.devServer?.close?.();
|
|
1884
|
+
} catch {}
|
|
1885
|
+
try {
|
|
1886
|
+
runtime.wss?.close();
|
|
1887
|
+
} catch {}
|
|
1888
|
+
await promises.rm(runtime.tempDir, {
|
|
1889
|
+
recursive: true,
|
|
1890
|
+
force: true
|
|
1891
|
+
}).catch(()=>{});
|
|
1892
|
+
};
|
|
1893
|
+
const registerWatchCleanup = ()=>{
|
|
1894
|
+
if (watchContext.cleanupRegistered) return;
|
|
1895
|
+
const cleanup = async ()=>{
|
|
1896
|
+
if (!watchContext.runtime) return;
|
|
1897
|
+
await destroyBrowserRuntime(watchContext.runtime);
|
|
1898
|
+
watchContext.runtime = null;
|
|
1899
|
+
};
|
|
1900
|
+
for (const signal of [
|
|
1901
|
+
'SIGINT',
|
|
1902
|
+
'SIGTERM'
|
|
1903
|
+
])process.once(signal, ()=>{
|
|
1904
|
+
cleanup();
|
|
1905
|
+
});
|
|
1906
|
+
process.once('exit', ()=>{
|
|
1907
|
+
cleanup();
|
|
1908
|
+
});
|
|
1909
|
+
watchContext.cleanupRegistered = true;
|
|
1910
|
+
};
|
|
1911
|
+
const createBrowserRuntime = async ({ context, manifestPath, manifestSource, tempDir, isWatchMode, onTriggerRerun, containerDistPath, containerDevServer, forceHeadless })=>{
|
|
1912
|
+
const virtualManifestPlugin = new rspack.experiments.VirtualModulesPlugin({
|
|
1913
|
+
[manifestPath]: manifestSource
|
|
1914
|
+
});
|
|
1915
|
+
const optionsPlaceholder = '__RSTEST_OPTIONS_PLACEHOLDER__';
|
|
1916
|
+
const containerHtmlTemplate = containerDistPath ? await promises.readFile(join(containerDistPath, 'container.html'), 'utf-8') : null;
|
|
1917
|
+
let injectedContainerHtml = null;
|
|
1918
|
+
let serializedOptions = 'null';
|
|
1919
|
+
const setContainerOptions = (options)=>{
|
|
1920
|
+
serializedOptions = JSON.stringify(options).replace(/</g, '\\u003c');
|
|
1921
|
+
if (containerHtmlTemplate) injectedContainerHtml = containerHtmlTemplate.replace(optionsPlaceholder, serializedOptions);
|
|
1922
|
+
};
|
|
1923
|
+
const browserProjects = getBrowserProjects(context);
|
|
1924
|
+
const firstProject = browserProjects[0];
|
|
1925
|
+
const userPlugins = firstProject?.normalizedConfig.plugins || [];
|
|
1926
|
+
const userRsbuildConfig = firstProject?.normalizedConfig ?? {};
|
|
1927
|
+
const browserRuntimePath = fileURLToPath(import.meta.resolve('@rstest/core/browser-runtime'));
|
|
1928
|
+
const rstestInternalAliases = {
|
|
1929
|
+
'@rstest/browser-manifest': manifestPath,
|
|
1930
|
+
'@rstest/core': resolveBrowserFile('client/public.ts'),
|
|
1931
|
+
'@rstest/core/browser-runtime': browserRuntimePath,
|
|
1932
|
+
'@sinonjs/fake-timers': resolveBrowserFile('client/fakeTimersStub.ts')
|
|
1933
|
+
};
|
|
1934
|
+
const rsbuildInstance = await createRsbuild({
|
|
1935
|
+
callerName: 'rstest-browser',
|
|
1936
|
+
rsbuildConfig: {
|
|
1937
|
+
root: context.rootPath,
|
|
1938
|
+
mode: 'development',
|
|
1939
|
+
plugins: userPlugins,
|
|
1940
|
+
server: {
|
|
1941
|
+
printUrls: false,
|
|
1942
|
+
port: context.normalizedConfig.browser.port,
|
|
1943
|
+
strictPort: void 0 !== context.normalizedConfig.browser.port
|
|
1944
|
+
},
|
|
1945
|
+
dev: {
|
|
1946
|
+
client: {
|
|
1947
|
+
logLevel: 'error'
|
|
1948
|
+
}
|
|
1949
|
+
},
|
|
1950
|
+
environments: {
|
|
1951
|
+
web: {}
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
});
|
|
1955
|
+
rsbuildInstance.addPlugins([
|
|
1956
|
+
{
|
|
1957
|
+
name: 'rstest:browser-user-config',
|
|
1958
|
+
setup (api) {
|
|
1959
|
+
api.modifyEnvironmentConfig((config, { mergeEnvironmentConfig })=>{
|
|
1960
|
+
const merged = mergeEnvironmentConfig(config, userRsbuildConfig, {
|
|
1961
|
+
source: {
|
|
1962
|
+
entry: {
|
|
1963
|
+
runner: resolveBrowserFile('client/entry.ts')
|
|
1964
|
+
}
|
|
1965
|
+
},
|
|
1966
|
+
resolve: {
|
|
1967
|
+
alias: rstestInternalAliases
|
|
1968
|
+
},
|
|
1969
|
+
output: {
|
|
1970
|
+
target: 'web',
|
|
1971
|
+
sourceMap: {
|
|
1972
|
+
js: 'source-map'
|
|
1973
|
+
}
|
|
1974
|
+
},
|
|
1975
|
+
tools: {
|
|
1976
|
+
rspack: (rspackConfig)=>{
|
|
1977
|
+
rspackConfig.mode = 'development';
|
|
1978
|
+
rspackConfig.lazyCompilation = {
|
|
1979
|
+
imports: true,
|
|
1980
|
+
entries: false
|
|
1981
|
+
};
|
|
1982
|
+
rspackConfig.plugins = rspackConfig.plugins || [];
|
|
1983
|
+
rspackConfig.plugins.push(virtualManifestPlugin);
|
|
1984
|
+
const browserRuntimeDir = dirname(browserRuntimePath);
|
|
1985
|
+
rspackConfig.module = rspackConfig.module || {};
|
|
1986
|
+
rspackConfig.module.rules = rspackConfig.module.rules || [];
|
|
1987
|
+
rspackConfig.module.rules.unshift({
|
|
1988
|
+
test: /\.js$/,
|
|
1989
|
+
include: browserRuntimeDir,
|
|
1990
|
+
extractSourceMap: true
|
|
1991
|
+
});
|
|
1992
|
+
if (isDebug()) logger.log(`[rstest:browser] extractSourceMap rule added for: ${browserRuntimeDir}`);
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
});
|
|
1996
|
+
return merged;
|
|
1997
|
+
});
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
]);
|
|
2001
|
+
if (isWatchMode && onTriggerRerun) rsbuildInstance.addPlugins([
|
|
2002
|
+
{
|
|
2003
|
+
name: 'rstest:browser-watch',
|
|
2004
|
+
setup (api) {
|
|
2005
|
+
api.onBeforeDevCompile(()=>{
|
|
2006
|
+
if (!watchContext.hooksEnabled) return;
|
|
2007
|
+
logger.log(color.cyan('\nFile changed, re-running tests...\n'));
|
|
2008
|
+
});
|
|
2009
|
+
api.onAfterDevCompile(async ()=>{
|
|
2010
|
+
if (!watchContext.hooksEnabled) return;
|
|
2011
|
+
await onTriggerRerun();
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
]);
|
|
2016
|
+
const devServer = await rsbuildInstance.createDevServer({
|
|
2017
|
+
getPortSilently: true
|
|
2018
|
+
});
|
|
2019
|
+
const serveContainer = containerDistPath ? sirv(containerDistPath, {
|
|
2020
|
+
dev: false,
|
|
2021
|
+
single: 'container.html'
|
|
2022
|
+
}) : null;
|
|
2023
|
+
const containerDevBase = containerDevServer ? new URL(containerDevServer) : null;
|
|
2024
|
+
const respondWithDevServerHtml = async (url, res)=>{
|
|
2025
|
+
if (!containerDevBase) return false;
|
|
2026
|
+
try {
|
|
2027
|
+
const target = new URL(url.pathname + url.search, containerDevBase);
|
|
2028
|
+
const response = await fetch(target);
|
|
2029
|
+
if (!response.ok) return false;
|
|
2030
|
+
let html = await response.text();
|
|
2031
|
+
html = html.replace(optionsPlaceholder, serializedOptions);
|
|
2032
|
+
res.statusCode = response.status;
|
|
2033
|
+
response.headers.forEach((value, key)=>{
|
|
2034
|
+
if ('content-length' === key.toLowerCase()) return;
|
|
2035
|
+
res.setHeader(key, value);
|
|
2036
|
+
});
|
|
2037
|
+
res.setHeader('Content-Type', 'text/html');
|
|
2038
|
+
res.end(html);
|
|
2039
|
+
return true;
|
|
2040
|
+
} catch (error) {
|
|
2041
|
+
logger.log(color.yellow(`[Browser UI] Failed to fetch container HTML from dev server: ${String(error)}`));
|
|
2042
|
+
return false;
|
|
2043
|
+
}
|
|
2044
|
+
};
|
|
2045
|
+
const proxyDevServerAsset = async (req, res)=>{
|
|
2046
|
+
if (!containerDevBase || !req.url) return false;
|
|
2047
|
+
try {
|
|
2048
|
+
const target = new URL(req.url, containerDevBase);
|
|
2049
|
+
const response = await fetch(target);
|
|
2050
|
+
if (!response.ok) return false;
|
|
2051
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
2052
|
+
res.statusCode = response.status;
|
|
2053
|
+
response.headers.forEach((value, key)=>{
|
|
2054
|
+
if ('content-length' === key.toLowerCase()) return;
|
|
2055
|
+
res.setHeader(key, value);
|
|
2056
|
+
});
|
|
2057
|
+
res.end(buffer);
|
|
2058
|
+
return true;
|
|
2059
|
+
} catch (error) {
|
|
2060
|
+
logger.log(color.yellow(`[Browser UI] Failed to proxy asset from dev server: ${String(error)}`));
|
|
2061
|
+
return false;
|
|
2062
|
+
}
|
|
2063
|
+
};
|
|
2064
|
+
devServer.middlewares.use(async (req, res, next)=>{
|
|
2065
|
+
if (!req.url) return void next();
|
|
2066
|
+
const url = new URL(req.url, 'http://localhost');
|
|
2067
|
+
if ('/__open-in-editor' === url.pathname) {
|
|
2068
|
+
const file = url.searchParams.get('file');
|
|
2069
|
+
if (!file) {
|
|
2070
|
+
res.statusCode = 400;
|
|
2071
|
+
res.end('Missing file');
|
|
2072
|
+
return;
|
|
2073
|
+
}
|
|
2074
|
+
try {
|
|
2075
|
+
await open_editor([
|
|
2076
|
+
{
|
|
2077
|
+
file
|
|
2078
|
+
}
|
|
2079
|
+
]);
|
|
2080
|
+
res.statusCode = 204;
|
|
2081
|
+
res.end();
|
|
2082
|
+
} catch (error) {
|
|
2083
|
+
logger.log(color.yellow(`[Browser UI] Failed to open editor: ${String(error)}`));
|
|
2084
|
+
res.statusCode = 500;
|
|
2085
|
+
res.end('Failed to open editor');
|
|
2086
|
+
}
|
|
2087
|
+
return;
|
|
2088
|
+
}
|
|
2089
|
+
if ('/' === url.pathname || '/container.html' === url.pathname) {
|
|
2090
|
+
if (await respondWithDevServerHtml(url, res)) return;
|
|
2091
|
+
const html = injectedContainerHtml || containerHtmlTemplate?.replace(optionsPlaceholder, 'null');
|
|
2092
|
+
if (html) {
|
|
2093
|
+
res.setHeader('Content-Type', 'text/html');
|
|
2094
|
+
res.end(html);
|
|
2095
|
+
return;
|
|
2096
|
+
}
|
|
2097
|
+
res.statusCode = 502;
|
|
2098
|
+
res.end('Container UI is not available.');
|
|
2099
|
+
return;
|
|
2100
|
+
}
|
|
2101
|
+
if (url.pathname.startsWith('/container-static/')) {
|
|
2102
|
+
if (await proxyDevServerAsset(req, res)) return;
|
|
2103
|
+
if (serveContainer) return void serveContainer(req, res, next);
|
|
2104
|
+
res.statusCode = 502;
|
|
2105
|
+
res.end('Container assets are not available.');
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2108
|
+
if ('/runner.html' === url.pathname) {
|
|
2109
|
+
res.setHeader('Content-Type', 'text/html');
|
|
2110
|
+
res.end(htmlTemplate);
|
|
2111
|
+
return;
|
|
2112
|
+
}
|
|
2113
|
+
next();
|
|
2114
|
+
});
|
|
2115
|
+
const { port } = await devServer.listen();
|
|
2116
|
+
const wsPort = port + 1;
|
|
2117
|
+
const wss = new WebSocketServer({
|
|
2118
|
+
port: wsPort
|
|
2119
|
+
});
|
|
2120
|
+
logger.log(color.gray(`[Browser UI] WebSocket server started on port ${wsPort}`));
|
|
2121
|
+
let browserLauncher;
|
|
2122
|
+
const browserName = context.normalizedConfig.browser.browser;
|
|
2123
|
+
try {
|
|
2124
|
+
const playwright = await import("playwright");
|
|
2125
|
+
browserLauncher = playwright[browserName];
|
|
2126
|
+
} catch (_error) {
|
|
2127
|
+
wss.close();
|
|
2128
|
+
await devServer.close();
|
|
2129
|
+
throw _error;
|
|
2130
|
+
}
|
|
2131
|
+
let browser;
|
|
2132
|
+
try {
|
|
2133
|
+
browser = await browserLauncher.launch({
|
|
2134
|
+
headless: forceHeadless ?? context.normalizedConfig.browser.headless,
|
|
2135
|
+
args: 'chromium' === browserName ? [
|
|
2136
|
+
'--disable-popup-blocking',
|
|
2137
|
+
'--no-first-run',
|
|
2138
|
+
'--no-default-browser-check'
|
|
2139
|
+
] : void 0
|
|
2140
|
+
});
|
|
2141
|
+
} catch (_error) {
|
|
2142
|
+
wss.close();
|
|
2143
|
+
await devServer.close();
|
|
2144
|
+
throw _error;
|
|
2145
|
+
}
|
|
2146
|
+
return {
|
|
2147
|
+
rsbuildInstance,
|
|
2148
|
+
devServer,
|
|
2149
|
+
browser,
|
|
2150
|
+
port,
|
|
2151
|
+
wsPort,
|
|
2152
|
+
manifestPath,
|
|
2153
|
+
tempDir,
|
|
2154
|
+
manifestPlugin: virtualManifestPlugin,
|
|
2155
|
+
setContainerOptions,
|
|
2156
|
+
wss
|
|
2157
|
+
};
|
|
2158
|
+
};
|
|
2159
|
+
const runBrowserController = async (context)=>{
|
|
2160
|
+
const buildStart = Date.now();
|
|
2161
|
+
const containerDevServerEnv = process.env.RSTEST_CONTAINER_DEV_SERVER;
|
|
2162
|
+
let containerDevServer;
|
|
2163
|
+
let containerDistPath;
|
|
2164
|
+
if (containerDevServerEnv) try {
|
|
2165
|
+
containerDevServer = new URL(containerDevServerEnv).toString();
|
|
2166
|
+
logger.log(color.gray(`[Browser UI] Using dev server for container: ${containerDevServer}`));
|
|
2167
|
+
} catch (error) {
|
|
2168
|
+
logger.error(color.red(`Invalid RSTEST_CONTAINER_DEV_SERVER value: ${String(error)}`));
|
|
2169
|
+
ensureProcessExitCode(1);
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
if (!containerDevServer) try {
|
|
2173
|
+
containerDistPath = resolveContainerDist();
|
|
2174
|
+
} catch (error) {
|
|
2175
|
+
logger.error(color.red(String(error)));
|
|
2176
|
+
ensureProcessExitCode(1);
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
const projectEntries = await collectProjectEntries(context);
|
|
2180
|
+
const totalTests = projectEntries.reduce((total, item)=>total + item.testFiles.length, 0);
|
|
2181
|
+
if (0 === totalTests) {
|
|
2182
|
+
const code = context.normalizedConfig.passWithNoTests ? 0 : 1;
|
|
2183
|
+
logger.log(color[code ? 'red' : 'yellow'](`No test files found, exiting with code ${code}.`));
|
|
2184
|
+
if (0 !== code) ensureProcessExitCode(code);
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
2187
|
+
const isWatchMode = 'watch' === context.command;
|
|
2188
|
+
const tempDir = isWatchMode && watchContext.runtime ? watchContext.runtime.tempDir : isWatchMode ? join(context.rootPath, TEMP_RSTEST_OUTPUT_DIR, 'browser', 'watch') : join(context.rootPath, TEMP_RSTEST_OUTPUT_DIR, 'browser', Date.now().toString());
|
|
2189
|
+
const manifestPath = join(tempDir, 'manifest.ts');
|
|
2190
|
+
const manifestSource = generateManifestModule({
|
|
2191
|
+
manifestPath,
|
|
2192
|
+
entries: projectEntries
|
|
2193
|
+
});
|
|
2194
|
+
if (isWatchMode) watchContext.lastTestFiles = projectEntries.flatMap((entry)=>entry.testFiles.map((testPath)=>({
|
|
2195
|
+
testPath,
|
|
2196
|
+
projectName: entry.project.name
|
|
2197
|
+
})));
|
|
2198
|
+
let runtime = isWatchMode ? watchContext.runtime : null;
|
|
2199
|
+
let triggerRerun;
|
|
2200
|
+
if (!runtime) {
|
|
2201
|
+
try {
|
|
2202
|
+
runtime = await createBrowserRuntime({
|
|
2203
|
+
context,
|
|
2204
|
+
manifestPath,
|
|
2205
|
+
manifestSource,
|
|
2206
|
+
tempDir,
|
|
2207
|
+
isWatchMode,
|
|
2208
|
+
onTriggerRerun: isWatchMode ? async ()=>{
|
|
2209
|
+
await triggerRerun?.();
|
|
2210
|
+
} : void 0,
|
|
2211
|
+
containerDistPath,
|
|
2212
|
+
containerDevServer
|
|
2213
|
+
});
|
|
2214
|
+
} catch (error) {
|
|
2215
|
+
logger.error(error instanceof Error ? error : new Error(String(error)));
|
|
2216
|
+
ensureProcessExitCode(1);
|
|
2217
|
+
await promises.rm(tempDir, {
|
|
2218
|
+
recursive: true,
|
|
2219
|
+
force: true
|
|
2220
|
+
}).catch(()=>{});
|
|
2221
|
+
return;
|
|
2222
|
+
}
|
|
2223
|
+
if (isWatchMode) {
|
|
2224
|
+
watchContext.runtime = runtime;
|
|
2225
|
+
registerWatchCleanup();
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
const { browser, port, wsPort, wss } = runtime;
|
|
2229
|
+
const buildTime = Date.now() - buildStart;
|
|
2230
|
+
const allTestFiles = projectEntries.flatMap((entry)=>entry.testFiles.map((testPath)=>({
|
|
2231
|
+
testPath: normalize(testPath),
|
|
2232
|
+
projectName: entry.project.name
|
|
2233
|
+
})));
|
|
2234
|
+
const browserProjectsForRuntime = getBrowserProjects(context);
|
|
2235
|
+
const projectRuntimeConfigs = browserProjectsForRuntime.map((project)=>({
|
|
2236
|
+
name: project.name,
|
|
2237
|
+
environmentName: project.environmentName,
|
|
2238
|
+
projectRoot: normalize(project.rootPath),
|
|
2239
|
+
runtimeConfig: serializableConfig(getRuntimeConfigFromProject(project))
|
|
2240
|
+
}));
|
|
2241
|
+
const maxTestTimeoutForRpc = Math.max(...browserProjectsForRuntime.map((p)=>p.normalizedConfig.testTimeout ?? 5000));
|
|
2242
|
+
const hostOptions = {
|
|
2243
|
+
rootPath: normalize(context.rootPath),
|
|
2244
|
+
projects: projectRuntimeConfigs,
|
|
2245
|
+
snapshot: {
|
|
2246
|
+
updateSnapshot: context.snapshotManager.options.updateSnapshot
|
|
2247
|
+
},
|
|
2248
|
+
runnerUrl: `http://localhost:${port}`,
|
|
2249
|
+
wsPort,
|
|
2250
|
+
debug: isDebug(),
|
|
2251
|
+
rpcTimeout: maxTestTimeoutForRpc
|
|
2252
|
+
};
|
|
2253
|
+
runtime.setContainerOptions(hostOptions);
|
|
2254
|
+
const reporterResults = [];
|
|
2255
|
+
const caseResults = [];
|
|
2256
|
+
let completedTests = 0;
|
|
2257
|
+
let fatalError = null;
|
|
2258
|
+
let resolveAllTests;
|
|
2259
|
+
const allTestsPromise = new Promise((resolve)=>{
|
|
2260
|
+
resolveAllTests = resolve;
|
|
2261
|
+
});
|
|
2262
|
+
let containerContext;
|
|
2263
|
+
let containerPage;
|
|
2264
|
+
let isNewPage = false;
|
|
2265
|
+
if (isWatchMode && runtime.containerPage && runtime.containerContext) {
|
|
2266
|
+
containerContext = runtime.containerContext;
|
|
2267
|
+
containerPage = runtime.containerPage;
|
|
2268
|
+
logger.log(color.gray('\n[Watch] Reusing existing container page\n'));
|
|
2269
|
+
} else {
|
|
2270
|
+
isNewPage = true;
|
|
2271
|
+
containerContext = await browser.newContext({
|
|
2272
|
+
viewport: null
|
|
2273
|
+
});
|
|
2274
|
+
containerPage = await containerContext.newPage();
|
|
2275
|
+
containerPage.on('popup', async (popup)=>{
|
|
2276
|
+
await popup.close().catch(()=>{});
|
|
2277
|
+
});
|
|
2278
|
+
containerContext.on('page', async (page)=>{
|
|
2279
|
+
if (page !== containerPage) await page.close().catch(()=>{});
|
|
2280
|
+
});
|
|
2281
|
+
if (isWatchMode) {
|
|
2282
|
+
runtime.containerPage = containerPage;
|
|
2283
|
+
runtime.containerContext = containerContext;
|
|
2284
|
+
}
|
|
2285
|
+
containerPage.on('console', (msg)=>{
|
|
2286
|
+
const text = msg.text();
|
|
2287
|
+
if (text.includes('[Container]') || text.includes('[Runner]')) logger.log(color.gray(`[Browser Console] ${text}`));
|
|
2288
|
+
});
|
|
2289
|
+
}
|
|
2290
|
+
const createRpcMethods = ()=>({
|
|
2291
|
+
async rerunTest (testFile, testNamePattern) {
|
|
2292
|
+
logger.log(color.cyan(`\nRe-running test: ${testFile}${testNamePattern ? ` (pattern: ${testNamePattern})` : ''}\n`));
|
|
2293
|
+
await rpcManager.reloadTestFile(testFile, testNamePattern);
|
|
2294
|
+
},
|
|
2295
|
+
async getTestFiles () {
|
|
2296
|
+
return allTestFiles;
|
|
2297
|
+
},
|
|
2298
|
+
async onTestFileStart (payload) {
|
|
2299
|
+
await Promise.all(context.reporters.map((reporter)=>reporter.onTestFileStart?.({
|
|
2300
|
+
testPath: payload.testPath,
|
|
2301
|
+
tests: []
|
|
2302
|
+
})));
|
|
2303
|
+
},
|
|
2304
|
+
async onTestCaseResult (payload) {
|
|
2305
|
+
caseResults.push(payload);
|
|
2306
|
+
await Promise.all(context.reporters.map((reporter)=>reporter.onTestCaseResult?.(payload)));
|
|
2307
|
+
},
|
|
2308
|
+
async onTestFileComplete (payload) {
|
|
2309
|
+
reporterResults.push(payload);
|
|
2310
|
+
if (payload.snapshotResult) context.snapshotManager.add(payload.snapshotResult);
|
|
2311
|
+
await Promise.all(context.reporters.map((reporter)=>reporter.onTestFileResult?.(payload)));
|
|
2312
|
+
completedTests++;
|
|
2313
|
+
if (completedTests >= allTestFiles.length && resolveAllTests) resolveAllTests();
|
|
2314
|
+
},
|
|
2315
|
+
async onLog (payload) {
|
|
2316
|
+
const log = {
|
|
2317
|
+
content: payload.content,
|
|
2318
|
+
name: payload.level,
|
|
2319
|
+
testPath: payload.testPath,
|
|
2320
|
+
type: payload.type,
|
|
2321
|
+
trace: payload.trace
|
|
2322
|
+
};
|
|
2323
|
+
const shouldLog = context.normalizedConfig.onConsoleLog?.(log.content) ?? true;
|
|
2324
|
+
if (shouldLog) await Promise.all(context.reporters.map((reporter)=>reporter.onUserConsoleLog?.(log)));
|
|
2325
|
+
},
|
|
2326
|
+
async onFatal (payload) {
|
|
2327
|
+
fatalError = new Error(payload.message);
|
|
2328
|
+
fatalError.stack = payload.stack;
|
|
2329
|
+
if (resolveAllTests) resolveAllTests();
|
|
2330
|
+
},
|
|
2331
|
+
async resolveSnapshotPath (testPath) {
|
|
2332
|
+
const snapExtension = '.snap';
|
|
2333
|
+
const resolver = context.normalizedConfig.resolveSnapshotPath || (()=>join(dirname(testPath), '__snapshots__', `${basename(testPath)}${snapExtension}`));
|
|
2334
|
+
return resolver(testPath, snapExtension);
|
|
2335
|
+
},
|
|
2336
|
+
async readSnapshotFile (filepath) {
|
|
2337
|
+
try {
|
|
2338
|
+
return await promises.readFile(filepath, 'utf-8');
|
|
2339
|
+
} catch {
|
|
2340
|
+
return null;
|
|
2341
|
+
}
|
|
2342
|
+
},
|
|
2343
|
+
async saveSnapshotFile (filepath, content) {
|
|
2344
|
+
const dir = dirname(filepath);
|
|
2345
|
+
await promises.mkdir(dir, {
|
|
2346
|
+
recursive: true
|
|
2347
|
+
});
|
|
2348
|
+
await promises.writeFile(filepath, content, 'utf-8');
|
|
2349
|
+
},
|
|
2350
|
+
async removeSnapshotFile (filepath) {
|
|
2351
|
+
try {
|
|
2352
|
+
await promises.unlink(filepath);
|
|
2353
|
+
} catch {}
|
|
2354
|
+
}
|
|
2355
|
+
});
|
|
2356
|
+
let rpcManager;
|
|
2357
|
+
if (isWatchMode && runtime.rpcManager) {
|
|
2358
|
+
rpcManager = runtime.rpcManager;
|
|
2359
|
+
rpcManager.updateMethods(createRpcMethods());
|
|
2360
|
+
const existingWs = rpcManager.currentWebSocket;
|
|
2361
|
+
if (existingWs) rpcManager.reattach(existingWs);
|
|
2362
|
+
} else {
|
|
2363
|
+
rpcManager = new ContainerRpcManager(wss, createRpcMethods());
|
|
2364
|
+
if (isWatchMode) runtime.rpcManager = rpcManager;
|
|
2365
|
+
}
|
|
2366
|
+
if (isNewPage) {
|
|
2367
|
+
await containerPage.goto(`http://localhost:${port}/container.html`, {
|
|
2368
|
+
waitUntil: 'load'
|
|
2369
|
+
});
|
|
2370
|
+
logger.log(color.cyan(`\nContainer page opened at http://localhost:${port}/container.html\n`));
|
|
2371
|
+
}
|
|
2372
|
+
const maxTestTimeout = Math.max(...browserProjectsForRuntime.map((p)=>p.normalizedConfig.testTimeout ?? 5000));
|
|
2373
|
+
const totalTimeoutMs = maxTestTimeout * allTestFiles.length + 30000;
|
|
2374
|
+
let timeoutId;
|
|
2375
|
+
const testTimeout = new Promise((resolve)=>{
|
|
2376
|
+
timeoutId = setTimeout(()=>{
|
|
2377
|
+
logger.log(color.yellow(`\nTest execution timeout after ${totalTimeoutMs / 1000}s. Completed: ${completedTests}/${allTestFiles.length}\n`));
|
|
2378
|
+
resolve();
|
|
2379
|
+
}, totalTimeoutMs);
|
|
2380
|
+
});
|
|
2381
|
+
const testStart = Date.now();
|
|
2382
|
+
await Promise.race([
|
|
2383
|
+
allTestsPromise,
|
|
2384
|
+
testTimeout
|
|
2385
|
+
]);
|
|
2386
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2387
|
+
const testTime = Date.now() - testStart;
|
|
2388
|
+
if (isWatchMode) triggerRerun = async ()=>{
|
|
2389
|
+
const newProjectEntries = await collectProjectEntries(context);
|
|
2390
|
+
const currentTestFiles = newProjectEntries.flatMap((entry)=>entry.testFiles.map((testPath)=>({
|
|
2391
|
+
testPath: normalize(testPath),
|
|
2392
|
+
projectName: entry.project.name
|
|
2393
|
+
})));
|
|
2394
|
+
const serialize = (files)=>JSON.stringify(files.map((f)=>`${f.projectName}:${f.testPath}`).sort());
|
|
2395
|
+
const filesChanged = serialize(currentTestFiles) !== serialize(watchContext.lastTestFiles);
|
|
2396
|
+
if (filesChanged) {
|
|
2397
|
+
watchContext.lastTestFiles = currentTestFiles;
|
|
2398
|
+
await rpcManager.notifyTestFileUpdate(currentTestFiles);
|
|
2399
|
+
}
|
|
2400
|
+
logger.log(color.cyan('Tests will be re-executed automatically\n'));
|
|
2401
|
+
};
|
|
2402
|
+
if (!isWatchMode) await destroyBrowserRuntime(runtime);
|
|
2403
|
+
if (fatalError) {
|
|
2404
|
+
logger.error(color.red(`Browser test run failed: ${fatalError.message}`));
|
|
2405
|
+
ensureProcessExitCode(1);
|
|
2406
|
+
return;
|
|
2407
|
+
}
|
|
2408
|
+
const duration = {
|
|
2409
|
+
totalTime: buildTime + testTime,
|
|
2410
|
+
buildTime,
|
|
2411
|
+
testTime
|
|
2412
|
+
};
|
|
2413
|
+
context.updateReporterResultState(reporterResults, caseResults);
|
|
2414
|
+
const isFailure = reporterResults.some((result)=>'fail' === result.status);
|
|
2415
|
+
if (isFailure) ensureProcessExitCode(1);
|
|
2416
|
+
for (const reporter of context.reporters)await reporter.onTestRunEnd?.({
|
|
2417
|
+
results: context.reporterResults.results,
|
|
2418
|
+
testResults: context.reporterResults.testResults,
|
|
2419
|
+
duration,
|
|
2420
|
+
snapshotSummary: context.snapshotManager.summary,
|
|
2421
|
+
getSourcemap: async ()=>null
|
|
2422
|
+
});
|
|
2423
|
+
if (isWatchMode && triggerRerun) {
|
|
2424
|
+
watchContext.hooksEnabled = true;
|
|
2425
|
+
logger.log(color.cyan('\nWatch mode enabled - will re-run tests on file changes\n'));
|
|
2426
|
+
}
|
|
2427
|
+
};
|
|
2428
|
+
const listBrowserTests = async (context)=>{
|
|
2429
|
+
const projectEntries = await collectProjectEntries(context);
|
|
2430
|
+
const totalTests = projectEntries.reduce((total, item)=>total + item.testFiles.length, 0);
|
|
2431
|
+
if (0 === totalTests) return {
|
|
2432
|
+
list: [],
|
|
2433
|
+
close: async ()=>{}
|
|
2434
|
+
};
|
|
2435
|
+
const tempDir = join(context.rootPath, TEMP_RSTEST_OUTPUT_DIR, 'browser', `list-${Date.now()}`);
|
|
2436
|
+
const manifestPath = join(tempDir, 'manifest.ts');
|
|
2437
|
+
const manifestSource = generateManifestModule({
|
|
2438
|
+
manifestPath,
|
|
2439
|
+
entries: projectEntries
|
|
2440
|
+
});
|
|
2441
|
+
let runtime;
|
|
2442
|
+
try {
|
|
2443
|
+
runtime = await createBrowserRuntime({
|
|
2444
|
+
context,
|
|
2445
|
+
manifestPath,
|
|
2446
|
+
manifestSource,
|
|
2447
|
+
tempDir,
|
|
2448
|
+
isWatchMode: false,
|
|
2449
|
+
containerDistPath: void 0,
|
|
2450
|
+
containerDevServer: void 0,
|
|
2451
|
+
forceHeadless: true
|
|
2452
|
+
});
|
|
2453
|
+
} catch (error) {
|
|
2454
|
+
logger.error(color.red('Failed to load Playwright. Please install "playwright" to use browser mode.'), error);
|
|
2455
|
+
throw error;
|
|
2456
|
+
}
|
|
2457
|
+
const { browser, port } = runtime;
|
|
2458
|
+
const browserProjects = getBrowserProjects(context);
|
|
2459
|
+
const projectRuntimeConfigs = browserProjects.map((project)=>({
|
|
2460
|
+
name: project.name,
|
|
2461
|
+
environmentName: project.environmentName,
|
|
2462
|
+
projectRoot: normalize(project.rootPath),
|
|
2463
|
+
runtimeConfig: serializableConfig(getRuntimeConfigFromProject(project))
|
|
2464
|
+
}));
|
|
2465
|
+
const maxTestTimeoutForRpc = Math.max(...browserProjects.map((p)=>p.normalizedConfig.testTimeout ?? 5000));
|
|
2466
|
+
const hostOptions = {
|
|
2467
|
+
rootPath: normalize(context.rootPath),
|
|
2468
|
+
projects: projectRuntimeConfigs,
|
|
2469
|
+
snapshot: {
|
|
2470
|
+
updateSnapshot: context.snapshotManager.options.updateSnapshot
|
|
2471
|
+
},
|
|
2472
|
+
mode: 'collect',
|
|
2473
|
+
debug: isDebug(),
|
|
2474
|
+
rpcTimeout: maxTestTimeoutForRpc
|
|
2475
|
+
};
|
|
2476
|
+
runtime.setContainerOptions(hostOptions);
|
|
2477
|
+
const collectResults = [];
|
|
2478
|
+
let fatalError = null;
|
|
2479
|
+
let collectCompleted = false;
|
|
2480
|
+
let resolveCollect;
|
|
2481
|
+
const collectPromise = new Promise((resolve)=>{
|
|
2482
|
+
resolveCollect = resolve;
|
|
2483
|
+
});
|
|
2484
|
+
const browserContext = await browser.newContext({
|
|
2485
|
+
viewport: null
|
|
2486
|
+
});
|
|
2487
|
+
const page = await browserContext.newPage();
|
|
2488
|
+
await page.exposeFunction('__rstest_dispatch__', (message)=>{
|
|
2489
|
+
switch(message.type){
|
|
2490
|
+
case 'collect-result':
|
|
2491
|
+
{
|
|
2492
|
+
const payload = message.payload;
|
|
2493
|
+
collectResults.push({
|
|
2494
|
+
testPath: payload.testPath,
|
|
2495
|
+
project: payload.project,
|
|
2496
|
+
tests: payload.tests
|
|
2497
|
+
});
|
|
2498
|
+
break;
|
|
2499
|
+
}
|
|
2500
|
+
case 'collect-complete':
|
|
2501
|
+
collectCompleted = true;
|
|
2502
|
+
resolveCollect?.();
|
|
2503
|
+
break;
|
|
2504
|
+
case 'fatal':
|
|
2505
|
+
{
|
|
2506
|
+
const payload = message.payload;
|
|
2507
|
+
fatalError = new Error(payload.message);
|
|
2508
|
+
fatalError.stack = payload.stack;
|
|
2509
|
+
resolveCollect?.();
|
|
2510
|
+
break;
|
|
2511
|
+
}
|
|
2512
|
+
case 'ready':
|
|
2513
|
+
case 'log':
|
|
2514
|
+
break;
|
|
2515
|
+
default:
|
|
2516
|
+
logger.debug(`[List] Unexpected message: ${message.type}`);
|
|
2517
|
+
}
|
|
2518
|
+
});
|
|
2519
|
+
const serializedOptions = JSON.stringify(hostOptions).replace(/</g, '\\u003c');
|
|
2520
|
+
await page.addInitScript(`window.__RSTEST_BROWSER_OPTIONS__ = ${serializedOptions};`);
|
|
2521
|
+
await page.goto(`http://localhost:${port}/runner.html`, {
|
|
2522
|
+
waitUntil: 'load'
|
|
2523
|
+
});
|
|
2524
|
+
const timeoutMs = 30000;
|
|
2525
|
+
let timeoutId;
|
|
2526
|
+
const timeoutPromise = new Promise((resolve)=>{
|
|
2527
|
+
timeoutId = setTimeout(()=>{
|
|
2528
|
+
if (!collectCompleted) logger.warn(color.yellow(`[List] Browser test collection timed out after ${timeoutMs}ms`));
|
|
2529
|
+
resolve();
|
|
2530
|
+
}, timeoutMs);
|
|
2531
|
+
});
|
|
2532
|
+
await Promise.race([
|
|
2533
|
+
collectPromise,
|
|
2534
|
+
timeoutPromise
|
|
2535
|
+
]);
|
|
2536
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2537
|
+
const cleanup = async ()=>{
|
|
2538
|
+
try {
|
|
2539
|
+
await page.close();
|
|
2540
|
+
await browserContext.close();
|
|
2541
|
+
} catch {}
|
|
2542
|
+
await destroyBrowserRuntime(runtime);
|
|
2543
|
+
};
|
|
2544
|
+
if (fatalError) {
|
|
2545
|
+
await cleanup();
|
|
2546
|
+
const errorResult = {
|
|
2547
|
+
testPath: '',
|
|
2548
|
+
project: '',
|
|
2549
|
+
tests: [],
|
|
2550
|
+
errors: [
|
|
2551
|
+
{
|
|
2552
|
+
name: 'BrowserCollectError',
|
|
2553
|
+
message: fatalError.message,
|
|
2554
|
+
stack: fatalError.stack
|
|
2555
|
+
}
|
|
2556
|
+
]
|
|
2557
|
+
};
|
|
2558
|
+
return {
|
|
2559
|
+
list: [
|
|
2560
|
+
errorResult
|
|
2561
|
+
],
|
|
2562
|
+
close: async ()=>{}
|
|
2563
|
+
};
|
|
2564
|
+
}
|
|
2565
|
+
return {
|
|
2566
|
+
list: collectResults,
|
|
2567
|
+
close: cleanup
|
|
2568
|
+
};
|
|
2569
|
+
};
|
|
2570
|
+
async function runBrowserTests(context) {
|
|
2571
|
+
await runBrowserController(context);
|
|
2572
|
+
}
|
|
2573
|
+
async function src_listBrowserTests(context) {
|
|
2574
|
+
return listBrowserTests(context);
|
|
2575
|
+
}
|
|
2576
|
+
export { runBrowserTests, src_listBrowserTests as listBrowserTests };
|