depvitals 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +39 -61
  2. package/dist/cli/entry.d.ts +59 -0
  3. package/dist/cli/entry.d.ts.map +1 -0
  4. package/dist/cli/entry.js +95 -0
  5. package/dist/cli/entry.js.map +1 -0
  6. package/dist/cli/index.js +14 -18
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/cli/notifier/eligibility.d.ts +65 -0
  9. package/dist/cli/notifier/eligibility.d.ts.map +1 -0
  10. package/dist/cli/notifier/eligibility.js +123 -0
  11. package/dist/cli/notifier/eligibility.js.map +1 -0
  12. package/dist/cli/notifier/registryLookup.d.ts +111 -0
  13. package/dist/cli/notifier/registryLookup.d.ts.map +1 -0
  14. package/dist/cli/notifier/registryLookup.js +211 -0
  15. package/dist/cli/notifier/registryLookup.js.map +1 -0
  16. package/dist/cli/notifier/updateCache.d.ts +101 -0
  17. package/dist/cli/notifier/updateCache.d.ts.map +1 -0
  18. package/dist/cli/notifier/updateCache.js +352 -0
  19. package/dist/cli/notifier/updateCache.js.map +1 -0
  20. package/dist/cli/notifier/updateCheck.d.ts +88 -0
  21. package/dist/cli/notifier/updateCheck.d.ts.map +1 -0
  22. package/dist/cli/notifier/updateCheck.js +166 -0
  23. package/dist/cli/notifier/updateCheck.js.map +1 -0
  24. package/dist/cli/notifier/versionCompare.d.ts +48 -0
  25. package/dist/cli/notifier/versionCompare.d.ts.map +1 -0
  26. package/dist/cli/notifier/versionCompare.js +89 -0
  27. package/dist/cli/notifier/versionCompare.js.map +1 -0
  28. package/dist/cli/runCli.d.ts +2 -0
  29. package/dist/cli/runCli.d.ts.map +1 -1
  30. package/dist/cli/runCli.js +36 -4
  31. package/dist/cli/runCli.js.map +1 -1
  32. package/dist/config/ignoreDependencies.d.ts +7 -0
  33. package/dist/config/ignoreDependencies.d.ts.map +1 -0
  34. package/dist/config/ignoreDependencies.js +18 -0
  35. package/dist/config/ignoreDependencies.js.map +1 -0
  36. package/dist/config/ignorePaths.d.ts +13 -0
  37. package/dist/config/ignorePaths.d.ts.map +1 -0
  38. package/dist/config/ignorePaths.js +47 -0
  39. package/dist/config/ignorePaths.js.map +1 -0
  40. package/dist/config/projectConfigError.d.ts +7 -0
  41. package/dist/config/projectConfigError.d.ts.map +1 -0
  42. package/dist/config/projectConfigError.js +10 -0
  43. package/dist/config/projectConfigError.js.map +1 -0
  44. package/dist/config/readProjectConfig.d.ts +23 -0
  45. package/dist/config/readProjectConfig.d.ts.map +1 -0
  46. package/dist/config/readProjectConfig.js +106 -0
  47. package/dist/config/readProjectConfig.js.map +1 -0
  48. package/dist/core/analyzeProject.d.ts +48 -6
  49. package/dist/core/analyzeProject.d.ts.map +1 -1
  50. package/dist/core/analyzeProject.js +123 -16
  51. package/dist/core/analyzeProject.js.map +1 -1
  52. package/dist/detectors/eslintManifestConfig.d.ts +111 -0
  53. package/dist/detectors/eslintManifestConfig.d.ts.map +1 -0
  54. package/dist/detectors/eslintManifestConfig.js +351 -0
  55. package/dist/detectors/eslintManifestConfig.js.map +1 -0
  56. package/dist/detectors/eslintRcJsonConfig.d.ts +85 -0
  57. package/dist/detectors/eslintRcJsonConfig.d.ts.map +1 -0
  58. package/dist/detectors/eslintRcJsonConfig.js +189 -0
  59. package/dist/detectors/eslintRcJsonConfig.js.map +1 -0
  60. package/dist/detectors/jestManifestConfig.d.ts +76 -0
  61. package/dist/detectors/jestManifestConfig.d.ts.map +1 -0
  62. package/dist/detectors/jestManifestConfig.js +294 -0
  63. package/dist/detectors/jestManifestConfig.js.map +1 -0
  64. package/dist/reporters/terminalReporter.js +27 -1
  65. package/dist/reporters/terminalReporter.js.map +1 -1
  66. package/dist/scanner/moduleSpecifiers.d.ts +9 -1
  67. package/dist/scanner/moduleSpecifiers.d.ts.map +1 -1
  68. package/dist/scanner/moduleSpecifiers.js +3317 -205
  69. package/dist/scanner/moduleSpecifiers.js.map +1 -1
  70. package/dist/scanner/packageSpecifier.d.ts +26 -2
  71. package/dist/scanner/packageSpecifier.d.ts.map +1 -1
  72. package/dist/scanner/packageSpecifier.js +70 -2
  73. package/dist/scanner/packageSpecifier.js.map +1 -1
  74. package/dist/scanner/projectManifest.d.ts +31 -1
  75. package/dist/scanner/projectManifest.d.ts.map +1 -1
  76. package/dist/scanner/projectManifest.js +83 -4
  77. package/dist/scanner/projectManifest.js.map +1 -1
  78. package/dist/scanner/scriptUsage.d.ts +2 -1
  79. package/dist/scanner/scriptUsage.d.ts.map +1 -1
  80. package/dist/scanner/scriptUsage.js +216 -39
  81. package/dist/scanner/scriptUsage.js.map +1 -1
  82. package/dist/scanner/sourceFiles.d.ts +16 -2
  83. package/dist/scanner/sourceFiles.d.ts.map +1 -1
  84. package/dist/scanner/sourceFiles.js +12 -6
  85. package/dist/scanner/sourceFiles.js.map +1 -1
  86. package/package.json +7 -7
@@ -42,15 +42,22 @@ export class ModuleSpecifierError extends Error {
42
42
  * @throws {ModuleSpecifierError} when the source reaches an unterminated or malformed lexical state
43
43
  * from which the scanner cannot safely continue.
44
44
  */
45
- export function extractModuleSpecifiers(source) {
46
- return interpret(tokenize(source));
45
+ export function extractModuleSpecifiers(source, options = {}) {
46
+ const jsx = options.jsx ?? false;
47
+ // Ambiguous input gets one non-interpreting lexical probe and one definitive extraction. No
48
+ // source reread, parser retry after an exception, or repeated per-statement suffix scans.
49
+ const module = options.module === 'detect'
50
+ ? tokenize(source, jsx, false, true).some((token) => token.moduleSignal === true)
51
+ : (options.module ?? false);
52
+ return interpret(tokenize(source, jsx, module));
47
53
  }
48
54
  /**
49
55
  * Turn source into a stream of significant tokens, skipping whitespace, line/block comments, regex
50
56
  * literals and template literals.
51
57
  */
52
- function tokenize(source) {
58
+ function tokenize(source, jsx = false, module = false, detectModule = false) {
53
59
  const tokens = [];
60
+ const typeArgumentBounds = new Map();
54
61
  const n = source.length;
55
62
  let i = 0;
56
63
  // Whether the previous identifier token is an expression-starting keyword (return, typeof, ...).
@@ -70,22 +77,77 @@ function tokenize(source) {
70
77
  // The value of the previous identifier token, when the previous token was an identifier. Which
71
78
  // identifier it was matters for one decision: `return {` opens an object literal, `else {` not.
72
79
  let prevIdentifier;
80
+ // Preserve the position before an `async` modifier for a following `function` keyword.
81
+ let asyncFunctionIsExpression = false;
82
+ // A line terminator in trivia may end an expression-bodied arrow through ASI. It is consumed
83
+ // only when the next significant token cannot continue the preceding expression.
84
+ let lineTerminatorSinceLastToken = false;
85
+ let tokenLineBreakBefore = false;
86
+ let htmlCloseCommentAllowed = true;
87
+ const emit = (token) => {
88
+ const significant = tokenLineBreakBefore ? { ...token, lineBreakBefore: true } : token;
89
+ if (memberAtToken !== undefined) {
90
+ advanceMember(memberAtToken, significant, tokens[tokens.length - 1]);
91
+ }
92
+ const scope = requireScopes[requireScopes.length - 1];
93
+ tokens.push(scope?.inParameters === true
94
+ ? {
95
+ ...significant,
96
+ parameterFrame: frames.length,
97
+ ...(typeAnnotation === undefined ? {} : { parameterType: true }),
98
+ }
99
+ : significant);
100
+ if (token.kind !== 'punct' || token.syntheticBoundary !== true) {
101
+ htmlCloseCommentAllowed = false;
102
+ }
103
+ tokenLineBreakBefore = false;
104
+ lineTerminatorSinceLastToken = false;
105
+ };
73
106
  // Template literal state. `inText` is true while scanning template text that must not be lexed as
74
107
  // JavaScript; a backtick toggles it, so nested templates are handled by the toggle. Every open
75
- // `${...}` expression pushes its own brace depth onto `braceDepths`, which is what distinguishes
76
- // the `}` that closes the interpolation from a `}` belonging to an object literal, a block or a
77
- // function body inside it. JavaScript inside `${...}` is lexed like ordinary source.
78
- const braceDepths = [];
108
+ // `${...}` expression pushes a `template-expr` frame carrying its own brace depth, which is what
109
+ // distinguishes the `}` that closes the interpolation from a `}` belonging to an object literal, a
110
+ // block or a function body inside it. JavaScript inside `${...}` is lexed like ordinary source.
111
+ //
112
+ // The same stack carries the JSX overlay frames (`jsx-tag`, `jsx-text` and the `jsx-expr` of a
113
+ // `{...}` container), so markup, expression containers, templates and nested elements interleave
114
+ // in a single pass without any state leaking between them.
115
+ const frames = [];
79
116
  let inText = false;
80
117
  // Offset of the opening backtick of the current template, for unterminated-template errors.
81
118
  let templateStart = 0;
82
- // Brace context. Each `{` pushes whether it opened an expression (an object literal or a
83
- // destructuring pattern) or a statement block / function body, and its matching `}` pops that
84
- // entry. A `}` only ends an expression when it closed an object literal, which is what separates
85
- // `{}` in `{} / require('pkg') / 2`, where both slashes divide, from the `}` of
86
- // `if (ok) { ... }`, after which a `/` may legitimately start a regex. Anything unrecognized is
87
- // treated as a block.
119
+ const templateStarts = [];
88
120
  const braceContexts = [];
121
+ let memberAtToken;
122
+ // Function context of every open function body, tracked independently of expression frame resets
123
+ // so `${}` boundaries cannot change the nearest await/yield grammar.
124
+ const functionContextStack = [];
125
+ const requireScopes = [];
126
+ const closeRequireScope = (scope) => {
127
+ if (scope !== undefined && requireScopes[requireScopes.length - 1] === scope) {
128
+ requireScopes.pop();
129
+ }
130
+ };
131
+ const recordRequireBinding = (parameters, end) => {
132
+ const scope = parameters.signature?.context.requireScope;
133
+ const bindingEnd = parameters.bindingEnd ?? end;
134
+ if (scope !== undefined && parameters.bindingStart < bindingEnd) {
135
+ scope.bindings.push({ start: parameters.bindingStart, end: bindingEnd });
136
+ }
137
+ };
138
+ // The nearest function body owns the await/yield grammar. An ordinary nested function therefore
139
+ // suspends an async/generator context from an outer function instead of inheriting it.
140
+ // A default callback may open and finish while an outer signature is still reading parameters.
141
+ // Only a signature with closed parameters at its original depth can own the next body brace.
142
+ const pendingFunctionContexts = [];
143
+ const pendingClassContexts = [];
144
+ const conditionalContexts = [];
145
+ let colonIsExpression = false;
146
+ const expressionFunctionContexts = [];
147
+ let typeAnnotation;
148
+ const parameterContexts = [];
149
+ let closedParameterSignature;
150
+ let bracketDepth = 0;
89
151
  // Whether the most recent `}` closed an object literal. It is only consulted while that `}` is
90
152
  // the previous token, exactly like `prevPunct`.
91
153
  let closedObjectBrace = false;
@@ -95,10 +157,25 @@ function tokenize(source) {
95
157
  // The control keyword pending a `(` (if/while/for/with), so the matching `)` can be recognised.
96
158
  // Only whitespace and comments may appear between the keyword and the `(`.
97
159
  let pendingControlKeyword;
98
- // How many `(` are currently open, and the depth at which the open control-statement head sits,
99
- // so only the `)` that closes the head itself (not a nested one) enables the heuristic.
160
+ // Parameter/control parentheses carry the signature they opened, rather than recovering one
161
+ // from an arbitrary `)` when a colon or body brace appears later.
100
162
  let parenDepth = 0;
101
- let controlParenDepth;
163
+ const atLexicalDepth = (context) => parenDepth === context.parenDepth &&
164
+ bracketDepth === context.bracketDepth &&
165
+ braceContexts.length === context.braceDepth &&
166
+ frames.length === context.frameDepth;
167
+ const insideObjectLiteral = () => braceContexts[braceContexts.length - 1]?.role === 'object';
168
+ const insideClassBody = () => braceContexts[braceContexts.length - 1]?.role === 'class';
169
+ const atMethodContainer = () => {
170
+ const brace = braceContexts[braceContexts.length - 1];
171
+ return brace?.role === 'object'
172
+ ? atLexicalDepth(brace.depth)
173
+ : brace?.role === 'class' && atLexicalDepth(brace.state);
174
+ };
175
+ const currentClass = () => {
176
+ const brace = braceContexts[braceContexts.length - 1];
177
+ return brace?.role === 'class' ? brace.state : undefined;
178
+ };
102
179
  // Reset the expression-start state at the beginning of a fresh `${...}` expression.
103
180
  const resetExpressionState = () => {
104
181
  prevKind = 'start';
@@ -108,13 +185,54 @@ function tokenize(source) {
108
185
  afterControlHead = false;
109
186
  pendingControlKeyword = undefined;
110
187
  closedObjectBrace = false;
188
+ lineTerminatorSinceLastToken = false;
189
+ // Pending signatures retain their own depth across template/JSX expressions in parameters.
190
+ };
191
+ // A finished JSX element leaves an expression value behind: a following `/` divides, and the
192
+ // markup itself never opens a statement context.
193
+ const endJsxElement = () => {
194
+ prevKind = 'template';
195
+ prevPunct = undefined;
196
+ prevIdentifier = undefined;
197
+ prevIsExpressionKeyword = false;
198
+ afterControlHead = false;
199
+ pendingControlKeyword = undefined;
200
+ closedObjectBrace = false;
201
+ };
202
+ // Template interpolations, JSX markup and sibling containers are separate expressions. An
203
+ // explicit boundary keeps the token stream from gluing them into an import/require sequence that
204
+ // does not exist in source (for example `<p>{import}{('pkg')}</p>`).
205
+ const pushBoundary = (offset) => {
206
+ emit({
207
+ kind: 'punct',
208
+ value: ';',
209
+ offset,
210
+ endOffset: offset + 1,
211
+ syntheticBoundary: true,
212
+ });
111
213
  };
112
214
  while (i < n) {
215
+ memberAtToken = undefined;
113
216
  const ch = source[i];
217
+ let typedArrowContext;
218
+ let inType = typeAnnotation !== undefined;
219
+ let closedClassMemberBoundary = false;
220
+ let closedExpressionBrace = false;
221
+ let opensClassMemberBoundary = false;
222
+ let matchingOpen;
114
223
  if (ch === undefined) {
115
224
  i++;
116
225
  continue;
117
226
  }
227
+ let conditional = conditionalContexts[conditionalContexts.length - 1];
228
+ while (conditional !== undefined &&
229
+ (conditional.parenDepth > parenDepth ||
230
+ conditional.bracketDepth > bracketDepth ||
231
+ conditional.braceDepth > braceContexts.length ||
232
+ conditional.frameDepth > frames.length)) {
233
+ conditionalContexts.pop();
234
+ conditional = conditionalContexts[conditionalContexts.length - 1];
235
+ }
118
236
  // Template literal text: skip everything until `${`, a nested `` ` ``, or the closing `` ` ``.
119
237
  // A backtick toggles out of text mode; the matching toggle happens below in code mode.
120
238
  if (inText) {
@@ -125,6 +243,9 @@ function tokenize(source) {
125
243
  if (ch === '`') {
126
244
  // Closing backtick of the current (or nested) template literal.
127
245
  inText = false;
246
+ const start = templateStarts.pop() ?? templateStart;
247
+ emit({ kind: 'template', offset: start, endOffset: i + 1 });
248
+ templateStart = templateStarts[templateStarts.length - 1] ?? start;
128
249
  prevKind = 'template';
129
250
  i++;
130
251
  continue;
@@ -132,7 +253,16 @@ function tokenize(source) {
132
253
  if (ch === '$' && source[i + 1] === '{') {
133
254
  // Start of a `${...}` expression: lex the JavaScript inside like ordinary source. The frame
134
255
  // starts at brace depth 0, so the next unmatched `}` closes the interpolation.
135
- braceDepths.push(0);
256
+ frames.push({
257
+ kind: 'template-expr',
258
+ depth: 0,
259
+ offset: i,
260
+ ...(typeAnnotation !== undefined
261
+ ? { typeOwner: { annotation: typeAnnotation, depth: { ...typeAnnotation } } }
262
+ : {}),
263
+ });
264
+ // The preceding tag is not the callee of tokens inside this interpolation.
265
+ pushBoundary(i);
136
266
  inText = false;
137
267
  resetExpressionState();
138
268
  i += 2;
@@ -141,161 +271,2690 @@ function tokenize(source) {
141
271
  i++;
142
272
  continue;
143
273
  }
144
- if (isWhitespace(ch)) {
274
+ // The innermost frame decides how the next character is read.
275
+ const frame = frames[frames.length - 1];
276
+ // JSX children: inert text up to the matching close tag. Quotes, backticks and comment-looking
277
+ // sequences are text here, so only `<` (markup) and `{` (an expression container) leave it.
278
+ if (frame?.kind === 'jsx-text') {
279
+ if (ch === '{') {
280
+ frames.push({ kind: 'jsx-expr', depth: 0, offset: i });
281
+ pushBoundary(i);
282
+ resetExpressionState();
283
+ i++;
284
+ continue;
285
+ }
286
+ if (ch === '<') {
287
+ const after = source[i + 1];
288
+ // Close tag `</name>` or fragment close `</>`: this element ends here.
289
+ if (after === '/') {
290
+ let k = i + 2;
291
+ while (k < n && source[k] !== '>') {
292
+ k++;
293
+ }
294
+ if (k >= n) {
295
+ throw new ModuleSpecifierError('Unterminated JSX element', 'invalid-source', frame.offset);
296
+ }
297
+ frames.pop();
298
+ pushBoundary(i);
299
+ endJsxElement();
300
+ i = k + 1;
301
+ continue;
302
+ }
303
+ // Nested fragment `<>`.
304
+ if (after === '>') {
305
+ frames.push({ kind: 'jsx-text', name: null, offset: i });
306
+ i += 2;
307
+ continue;
308
+ }
309
+ // Nested element `<name ...`.
310
+ if (isIdentifierStartAt(source, i + 1)) {
311
+ const tag = readJsxTagName(source, i + 1, n);
312
+ frames.push({ kind: 'jsx-tag', name: tag.name, offset: i, nameEnd: tag.end });
313
+ i = tag.end;
314
+ continue;
315
+ }
316
+ // A `<` that is not markup (`a < b` written inside JSX text) is ordinary text.
317
+ i++;
318
+ continue;
319
+ }
320
+ i++;
321
+ continue;
322
+ }
323
+ // JSX open tag: attribute names, `=` and namespaces are inert; quoted values are inert text and
324
+ // `{...}` returns to real JavaScript.
325
+ if (frame?.kind === 'jsx-tag') {
326
+ if (ch === '<' && skipTrivia(source, frame.nameEnd, n) === i) {
327
+ pushBoundary(i);
328
+ i = readJsxTypeArguments(source, i, n, tokens);
329
+ pushBoundary(i);
330
+ continue;
331
+ }
332
+ if (ch === '{') {
333
+ frames.push({ kind: 'jsx-expr', depth: 0, offset: i });
334
+ pushBoundary(i);
335
+ resetExpressionState();
336
+ i++;
337
+ continue;
338
+ }
339
+ if (ch === '"' || ch === "'") {
340
+ i = skipJsxAttributeValue(source, i, n);
341
+ continue;
342
+ }
343
+ // Self-closing tag `<name ... />`: the element ends without children.
344
+ if (ch === '/' && source[i + 1] === '>') {
345
+ frames.pop();
346
+ pushBoundary(i);
347
+ endJsxElement();
348
+ i += 2;
349
+ continue;
350
+ }
351
+ // End of the open tag: its children follow until the matching close tag.
352
+ if (ch === '>') {
353
+ frames.pop();
354
+ frames.push({ kind: 'jsx-text', name: frame.name, offset: frame.offset });
355
+ i++;
356
+ continue;
357
+ }
145
358
  i++;
146
359
  continue;
147
360
  }
148
- // Line comment: it ends at any ECMAScript line terminator (LF, CR, LS or PS), not only `\n`.
149
- if (ch === '/' && source[i + 1] === '/') {
150
- i += 2;
361
+ if (!module &&
362
+ (source.startsWith('<!--', i) || (source.startsWith('-->', i) && htmlCloseCommentAllowed))) {
151
363
  while (i < n && !isLineTerminator(source[i])) {
152
364
  i++;
153
365
  }
154
366
  continue;
155
367
  }
156
- // Block comment. Comments are transparent to the regex/division heuristic, so a comment between
157
- // a control keyword and its `(` behaves exactly like whitespace.
158
- if (ch === '/' && source[i + 1] === '*') {
159
- i = skipBlockComment(source, i);
368
+ if (isWhitespace(ch) || (ch === '/' && (source[i + 1] === '/' || source[i + 1] === '*'))) {
369
+ const trivia = readTrivia(source, i, n);
370
+ i = trivia.end;
371
+ lineTerminatorSinceLastToken ||= trivia.lineBreak;
372
+ htmlCloseCommentAllowed ||= trivia.lineBreak;
160
373
  continue;
161
374
  }
162
- // A backtick in code mode opens a (possibly nested) template literal.
375
+ tokenLineBreakBefore = lineTerminatorSinceLastToken;
376
+ const previousSignature = closedParameterSignature;
377
+ closedParameterSignature = undefined;
378
+ memberAtToken = undefined;
379
+ const classState = currentClass();
380
+ const identifier = isIdentifierStartAt(source, i) || isUnicodeEscapeStart(source, i)
381
+ ? readIdentifier(source, i)
382
+ : undefined;
383
+ if (typeAnnotation?.role === 'assertion' &&
384
+ atLexicalDepth(typeAnnotation.owner) &&
385
+ atTypeRoot(typeAnnotation) &&
386
+ !(ch === '>' && isArrowHead(tokens[tokens.length - 1], i)) &&
387
+ !assertionTypeContinues(typeAnnotation, ch, identifier?.value, tokenLineBreakBefore, source[i + 1])) {
388
+ // Complete the expression before class-field ASI and slash classification consume it.
389
+ // The token keeps its type delimiter identity; no runtime brace is invented or popped.
390
+ const previous = tokens[tokens.length - 1];
391
+ if (previous !== undefined) {
392
+ tokens[tokens.length - 1] = { ...previous, assertionEnd: true };
393
+ }
394
+ prevIsExpressionKeyword = false;
395
+ afterControlHead = false;
396
+ typeAnnotation = undefined;
397
+ inType = false;
398
+ }
399
+ const expressionOwner = expressionFunctionContexts[expressionFunctionContexts.length - 1];
400
+ if (typeAnnotation === undefined &&
401
+ ch === '<' &&
402
+ prevKind === 'identifier' &&
403
+ expressionOwner !== undefined &&
404
+ atLexicalDepth(expressionOwner)) {
405
+ const end = expressionTypeArgumentEnd(source, i, n, typeArgumentBounds);
406
+ if (end !== undefined) {
407
+ // Use the ordinary type overlay: it preserves template imports and lexical scopes.
408
+ typeAnnotation = {
409
+ role: 'assertion',
410
+ conditionalDepth: 0,
411
+ conditionalOperand: false,
412
+ importCallPending: false,
413
+ owner: {
414
+ parenDepth,
415
+ bracketDepth,
416
+ braceDepth: braceContexts.length,
417
+ frameDepth: frames.length,
418
+ },
419
+ parenDepth: 0,
420
+ bracketDepth: 0,
421
+ braceDepth: 0,
422
+ angleDepth: 0,
423
+ typeOperandExpected: true,
424
+ };
425
+ inType = true;
426
+ }
427
+ }
428
+ // An assertion must release its type context before the same runtime delimiter can retire
429
+ // an enclosing arrow. Consume the delimiter only after restoring the nearest function grammar.
430
+ let expressionFunctionContext = expressionFunctionContexts[expressionFunctionContexts.length - 1];
431
+ while (expressionFunctionContext !== undefined &&
432
+ typeAnnotation === undefined &&
433
+ frames[frames.length - 1]?.kind !== 'jsx-text' &&
434
+ frames[frames.length - 1]?.kind !== 'jsx-tag' &&
435
+ expressionFunctionBodyEnded(ch, parenDepth, bracketDepth, braceContexts.length, frames.length, functionContextStack.length, conditionalContexts.length, expressionFunctionContext)) {
436
+ closeRequireScope(expressionFunctionContexts.pop()?.requireScope);
437
+ expressionFunctionContext = expressionFunctionContexts[expressionFunctionContexts.length - 1];
438
+ }
439
+ if (typeAnnotation?.role === 'return' &&
440
+ tokenLineBreakBefore &&
441
+ atLexicalDepth(typeAnnotation.signature.owner) &&
442
+ atTypeRoot(typeAnnotation) &&
443
+ !typeAnnotation.typeOperandExpected &&
444
+ ((identifier !== undefined &&
445
+ (!TYPE_OPERAND_KEYWORDS.has(identifier.value) || identifier.value === 'const')) ||
446
+ (ch === '[' &&
447
+ typeAnnotation.signature.context.isClassMember === true &&
448
+ computedMethodFollows(source, i, n)))) {
449
+ const signature = typeAnnotation.signature;
450
+ if (pendingFunctionContexts[pendingFunctionContexts.length - 1]?.requireScope ===
451
+ signature.context.requireScope) {
452
+ closeRequireScope(pendingFunctionContexts.pop()?.requireScope);
453
+ }
454
+ if (classState !== undefined && signature.context.isClassMember === true) {
455
+ classState.member = undefined;
456
+ }
457
+ typeAnnotation = undefined;
458
+ inType = false;
459
+ }
460
+ const pendingBodylessSignature = pendingFunctionContexts[pendingFunctionContexts.length - 1];
461
+ if (pendingBodylessSignature?.phase === 'awaiting-body' &&
462
+ atLexicalDepth(pendingBodylessSignature) &&
463
+ ((typeAnnotation === undefined && tokenLineBreakBefore && ch !== '{' && ch !== ':') ||
464
+ (ch === '}' &&
465
+ (typeAnnotation === undefined ||
466
+ (typeAnnotation.role === 'return' && atTypeRoot(typeAnnotation)))))) {
467
+ closeRequireScope(pendingFunctionContexts.pop()?.requireScope);
468
+ typeAnnotation = undefined;
469
+ inType = false;
470
+ resetExpressionState();
471
+ }
472
+ if (classState !== undefined &&
473
+ atLexicalDepth(classState) &&
474
+ (typeAnnotation === undefined ||
475
+ (typeAnnotation.role === 'field' && atTypeRoot(typeAnnotation)) ||
476
+ (typeAnnotation.role === 'return' &&
477
+ atTypeRoot(typeAnnotation) &&
478
+ (ch === ';' || ch === '}')))) {
479
+ if (tokenLineBreakBefore &&
480
+ classState.member !== undefined &&
481
+ typeAnnotation?.typeOperandExpected !== true &&
482
+ classFieldCanEnd(classState.member, tokens[tokens.length - 1], ch, identifier?.value)) {
483
+ classState.member = undefined;
484
+ typeAnnotation = undefined;
485
+ inType = false;
486
+ }
487
+ if (ch === ';' || ch === '}') {
488
+ classState.member = undefined;
489
+ if (typeAnnotation?.role === 'field') {
490
+ typeAnnotation = undefined;
491
+ inType = false;
492
+ }
493
+ }
494
+ else {
495
+ classState.member ??= {
496
+ start: tokens.length,
497
+ functionDepth: functionContextStack.length,
498
+ phase: 'head',
499
+ };
500
+ memberAtToken = classState.member;
501
+ }
502
+ }
503
+ if (lineTerminatorSinceLastToken && typeAnnotation === undefined) {
504
+ let context = expressionFunctionContexts[expressionFunctionContexts.length - 1];
505
+ while (context !== undefined &&
506
+ frames[frames.length - 1]?.kind !== 'jsx-text' &&
507
+ frames[frames.length - 1]?.kind !== 'jsx-tag' &&
508
+ lineTerminatorEndsExpressionFunctionBody(ch, prevKind, prevPunct, tokens[tokens.length - 1], identifier?.value, (ch === '+' || ch === '-') && source[i + 1] === ch, parenDepth, bracketDepth, braceContexts.length, frames.length, functionContextStack.length, context)) {
509
+ closeRequireScope(expressionFunctionContexts.pop()?.requireScope);
510
+ context = expressionFunctionContexts[expressionFunctionContexts.length - 1];
511
+ }
512
+ lineTerminatorSinceLastToken = false;
513
+ }
514
+ const equalsOperator = ch === '='
515
+ ? classifyEquals(source, i, typeAnnotation !== undefined && atTypeRoot(typeAnnotation))
516
+ : undefined;
517
+ if (ch === '<' && typeAnnotation === undefined) {
518
+ const classHeader = pendingClassContexts[pendingClassContexts.length - 1];
519
+ if (classHeader !== undefined &&
520
+ atLexicalDepth(classHeader) &&
521
+ (tokens[tokens.length - 1]?.kind === 'identifier' ||
522
+ isPunct(tokens[tokens.length - 1], ')') ||
523
+ isPunct(tokens[tokens.length - 1], ']') ||
524
+ isPunct(tokens[tokens.length - 1], '>'))) {
525
+ typeAnnotation = {
526
+ role: 'class-header',
527
+ owner: classHeader,
528
+ parenDepth: 0,
529
+ bracketDepth: 0,
530
+ braceDepth: 0,
531
+ angleDepth: 0,
532
+ typeOperandExpected: true,
533
+ };
534
+ inType = true;
535
+ }
536
+ }
537
+ // A backtick in code mode opens a (possibly nested) template literal. Inside an annotation
538
+ // that is a template-literal type: its text stays inert and the annotation keeps running until the
539
+ // type itself ends.
163
540
  if (ch === '`') {
164
541
  templateStart = i;
542
+ templateStarts.push(i);
165
543
  inText = true;
166
544
  prevKind = 'template';
545
+ if (typeAnnotation !== undefined) {
546
+ typeAnnotation.typeOperandExpected = false;
547
+ }
548
+ i++;
549
+ continue;
550
+ }
551
+ // JSX overlay (opt-in via the `jsx` option). A `<` in expression position that is followed by a
552
+ // tag name (or `>` for a fragment) opens JSX markup: its text, tags and attributes are inert,
553
+ // while the JavaScript inside `{...}` containers is lexed normally. Expression position is the
554
+ // same test that decides a regex literal, so a comparison (`a < b`, `f() < x`) is never JSX, and
555
+ // a TypeScript type-argument list (`<T,>`, `<T extends U>`) is excluded explicitly.
556
+ if (jsx &&
557
+ typeAnnotation === undefined &&
558
+ ch === '<' &&
559
+ i + 1 < n &&
560
+ isRegexStart(prevKind, prevPunct, prevIsExpressionKeyword, afterControlHead, closedObjectBrace, tokens[tokens.length - 1])) {
561
+ const after = source[i + 1];
562
+ if (after === '>') {
563
+ frames.push({ kind: 'jsx-text', name: null, offset: i });
564
+ pushBoundary(i);
565
+ i += 2;
566
+ continue;
567
+ }
568
+ if (isIdentifierStartAt(source, i + 1) && !jsxTagIsGeneric(source, i + 1, n)) {
569
+ const tag = readJsxTagName(source, i + 1, n);
570
+ frames.push({ kind: 'jsx-tag', name: tag.name, offset: i, nameEnd: tag.end });
571
+ pushBoundary(i);
572
+ i = tag.end;
573
+ continue;
574
+ }
575
+ }
576
+ // Type delimiters belong to the field, parameter or return signature, never to runtime code.
577
+ // Only that owner's boundary or initializer/body can end the annotation.
578
+ if (typeAnnotation !== undefined) {
579
+ const annotation = typeAnnotation;
580
+ const typeFrame = frame?.kind === 'template-expr' && frame.typeOwner?.annotation === annotation
581
+ ? frame.typeOwner
582
+ : undefined;
583
+ const atRoot = typeFrame === undefined && atTypeRoot(annotation);
584
+ const isTypeArrow = ch === '>' && isArrowHead(tokens[tokens.length - 1], i);
585
+ if (annotation.role === 'assertion' && atRoot) {
586
+ if (ch === '?' && annotation.conditionalOperand) {
587
+ annotation.conditionalDepth++;
588
+ annotation.conditionalOperand = false;
589
+ }
590
+ else if (ch === ':' && annotation.conditionalDepth > 0) {
591
+ annotation.conditionalDepth--;
592
+ }
593
+ if (ch === '(') {
594
+ annotation.importCallPending = false;
595
+ }
596
+ }
597
+ // Interpolations own depths relative to the surrounding type, which may itself be an object.
598
+ if (ch === '}' &&
599
+ (typeFrame !== undefined
600
+ ? sameTypeDepth(annotation, typeFrame.depth)
601
+ : annotation.braceDepth === 0) &&
602
+ (frame?.kind === 'template-expr' || frame?.kind === 'jsx-expr') &&
603
+ frame.depth === 0) {
604
+ frames.pop();
605
+ pushBoundary(i);
606
+ i++;
607
+ if (frame.kind === 'template-expr') {
608
+ annotation.typeOperandExpected = false;
609
+ inText = true;
610
+ resetExpressionState();
611
+ }
612
+ else {
613
+ // Back to the enclosing JSX tag or text frame, which reads the next character itself.
614
+ endJsxElement();
615
+ }
616
+ continue;
617
+ }
618
+ if ((annotation.role === 'field' || annotation.role === 'parameter') &&
619
+ atRoot &&
620
+ ch === '=' &&
621
+ equalsOperator?.operator === 'assignment') {
622
+ if (annotation.role === 'field') {
623
+ annotation.member.phase = 'initializer';
624
+ }
625
+ else {
626
+ annotation.owner.phase = 'initializer';
627
+ }
628
+ typeAnnotation = undefined;
629
+ inType = false;
630
+ }
631
+ else if (annotation.role === 'return' &&
632
+ ch === '{' &&
633
+ atRoot &&
634
+ !annotation.typeOperandExpected) {
635
+ // A complete type was read, so this brace opens the runtime body.
636
+ typeAnnotation = undefined;
637
+ inType = false;
638
+ lineTerminatorSinceLastToken = false;
639
+ if (!annotation.signature.isFunctionKeyword &&
640
+ pendingFunctionContexts[pendingFunctionContexts.length - 1]?.requireScope !==
641
+ annotation.signature.context.requireScope) {
642
+ pendingFunctionContexts.push({
643
+ ...annotation.signature.context,
644
+ ...annotation.signature.owner,
645
+ parameterDepth: undefined,
646
+ phase: 'awaiting-body',
647
+ });
648
+ }
649
+ }
650
+ else if (isTypeArrow &&
651
+ atRoot &&
652
+ annotation.role === 'return' &&
653
+ annotation.signature.isArrowSignature &&
654
+ !functionTypeArrowContinues(source, i, n, conditional !== undefined && atLexicalDepth(conditional))) {
655
+ // The signature's own `=>`: everything after it is the body, not the return type.
656
+ typedArrowContext = annotation.signature.context;
657
+ typeAnnotation = undefined;
658
+ inType = false;
659
+ lineTerminatorSinceLastToken = false;
660
+ }
661
+ else if (atRoot && (ch === ';' || ch === ',' || ch === ')' || ch === ']' || ch === '}')) {
662
+ // A signature with no body of its own. Neither the annotation nor a pending signature may
663
+ // outlive it, otherwise later code (a JSX text `<`, another method body, an async arrow) is
664
+ // misread.
665
+ typeAnnotation = undefined;
666
+ inType = false;
667
+ }
668
+ else {
669
+ if (ch === '{') {
670
+ annotation.braceDepth++;
671
+ annotation.typeOperandExpected = true;
672
+ }
673
+ else if (ch === '}') {
674
+ annotation.braceDepth = Math.max(0, annotation.braceDepth - 1);
675
+ annotation.typeOperandExpected = false;
676
+ }
677
+ else if (ch === '(') {
678
+ annotation.parenDepth++;
679
+ annotation.typeOperandExpected = true;
680
+ }
681
+ else if (ch === ')') {
682
+ annotation.parenDepth = Math.max(0, annotation.parenDepth - 1);
683
+ annotation.typeOperandExpected = false;
684
+ }
685
+ else if (ch === '[') {
686
+ annotation.bracketDepth++;
687
+ annotation.typeOperandExpected = true;
688
+ }
689
+ else if (ch === ']') {
690
+ annotation.bracketDepth = Math.max(0, annotation.bracketDepth - 1);
691
+ annotation.typeOperandExpected = false;
692
+ }
693
+ else if (ch === '<') {
694
+ annotation.angleDepth++;
695
+ annotation.typeOperandExpected = true;
696
+ }
697
+ else if (isTypeArrow) {
698
+ // A function type's own arrow (`(x: A) => B`) still expects a type on its right.
699
+ annotation.typeOperandExpected = true;
700
+ }
701
+ else if (ch === '>' && annotation.angleDepth > 0) {
702
+ annotation.angleDepth--;
703
+ annotation.typeOperandExpected = false;
704
+ }
705
+ else if (ch === ':' || ch === '?' || ch === '.' || ch === '|' || ch === '&') {
706
+ annotation.typeOperandExpected = true;
707
+ }
708
+ if (annotation.role === 'class-header' &&
709
+ ch === '>' &&
710
+ !isTypeArrow &&
711
+ atTypeRoot(annotation)) {
712
+ typeAnnotation = undefined;
713
+ }
714
+ }
715
+ }
716
+ // A `}` in code mode closes the innermost open `${...}` or JSX `{...}` expression only when that
717
+ // expression has no open braces of its own; otherwise it is an ordinary brace inside it.
718
+ if (ch === '}' && typeAnnotation === undefined) {
719
+ if (frame?.kind === 'template-expr' || frame?.kind === 'jsx-expr') {
720
+ if (frame.depth === 0) {
721
+ frames.pop();
722
+ pushBoundary(i);
723
+ i++;
724
+ if (frame.kind === 'template-expr') {
725
+ inText = true;
726
+ resetExpressionState();
727
+ }
728
+ else {
729
+ // Back to the enclosing JSX tag or text frame, which reads the next character itself.
730
+ endJsxElement();
731
+ }
732
+ continue;
733
+ }
734
+ frame.depth -= 1;
735
+ }
736
+ // Operand completion for class-field ASI is separate from the regex/division decision.
737
+ // A function/class body can finish an initializer without closing an object literal.
738
+ const closed = braceContexts.pop();
739
+ const closedMethod = closed?.role === 'function' && closed.context.isClassMember === true;
740
+ closedClassMemberBoundary = closedMethod || closed?.role === 'static-block';
741
+ closedObjectBrace =
742
+ closed?.role === 'object' ||
743
+ (closed?.role === 'function' && closed.context.isExpression) ||
744
+ (closed?.role === 'class' && closed.isExpression);
745
+ closedExpressionBrace =
746
+ closedObjectBrace ||
747
+ closedMethod ||
748
+ closed?.role === 'function' ||
749
+ closed?.role === 'class';
750
+ if (closed?.role === 'function') {
751
+ closeRequireScope(closed.context.requireScope);
752
+ functionContextStack.pop();
753
+ }
754
+ if ((closed?.role === 'function' || closed?.role === 'static-block') &&
755
+ closed.owner !== undefined) {
756
+ closed.owner.member = undefined;
757
+ }
758
+ }
759
+ // `/` is a regex when it can start an expression, otherwise it is division.
760
+ if (ch === '/') {
761
+ if (isRegexStart(prevKind, prevPunct, prevIsExpressionKeyword ||
762
+ (tokenLineBreakBefore &&
763
+ (isIdentifier(tokens[tokens.length - 1], 'break') ||
764
+ isIdentifier(tokens[tokens.length - 1], 'continue'))), afterControlHead, closedObjectBrace, tokens[tokens.length - 1])) {
765
+ const start = i;
766
+ i = skipRegex(source, i);
767
+ emit({ kind: 'regex', offset: start, endOffset: i });
768
+ prevKind = 'regex';
769
+ afterControlHead = false;
770
+ pendingControlKeyword = undefined;
771
+ continue;
772
+ }
773
+ emit({ kind: 'punct', value: '/', offset: i, endOffset: i + 1 });
774
+ prevKind = 'punct';
775
+ prevPunct = '/';
776
+ afterControlHead = false;
777
+ pendingControlKeyword = undefined;
778
+ i++;
779
+ continue;
780
+ }
781
+ // String literals (single or double quoted).
782
+ if (ch === "'" || ch === '"') {
783
+ const { value, next } = readString(source, i);
784
+ emit({ kind: 'string', value, offset: i, endOffset: next });
785
+ if (typeAnnotation !== undefined) {
786
+ typeAnnotation.typeOperandExpected = false;
787
+ }
788
+ prevKind = 'string';
789
+ afterControlHead = false;
790
+ pendingControlKeyword = undefined;
791
+ i = next;
792
+ continue;
793
+ }
794
+ // Identifiers.
795
+ if (identifier !== undefined) {
796
+ const start = i;
797
+ i = identifier.end;
798
+ const value = identifier.value;
799
+ if (typeAnnotation !== undefined) {
800
+ if (typeAnnotation.role === 'assertion') {
801
+ if (atLexicalDepth(typeAnnotation.owner) &&
802
+ atTypeRoot(typeAnnotation) &&
803
+ value === 'extends' &&
804
+ !typeAnnotation.typeOperandExpected) {
805
+ typeAnnotation.conditionalOperand = true;
806
+ }
807
+ typeAnnotation.importCallPending = value === 'import';
808
+ }
809
+ // `as const` completes an assertion, while prefix keywords still expect an operand.
810
+ typeAnnotation.typeOperandExpected =
811
+ TYPE_OPERAND_KEYWORDS.has(value) &&
812
+ !(typeAnnotation.role === 'assertion' && value === 'const');
813
+ }
814
+ const previousIdentifier = prevIdentifier;
815
+ const startsAssertion = (value === 'as' || value === 'satisfies') &&
816
+ !inType &&
817
+ // TypeScript permits trivia after these operators, but a preceding newline ends them.
818
+ !tokenLineBreakBefore &&
819
+ !(prevKind === 'punct' && prevPunct === '.') &&
820
+ !(prevKind === 'punct' &&
821
+ prevPunct === '}' &&
822
+ !closedObjectBrace &&
823
+ tokens[tokens.length - 1]?.assertionEnd !== true) &&
824
+ !afterControlHead &&
825
+ !prevIsExpressionKeyword &&
826
+ canStartTypeAssertion(tokens[tokens.length - 1]);
827
+ // Capture the enclosing operand grammar before this identifier replaces it. Contextual
828
+ // await/yield, an arrow head and a fresh embedded expression all expect an expression.
829
+ const expressionPosition = functionIsExpressionPosition(prevKind, prevPunct, colonIsExpression, (prevKind === 'identifier' &&
830
+ prevIsExpressionKeyword &&
831
+ !BLOCK_AFTER_KEYWORD.has(previousIdentifier ?? '') &&
832
+ !((previousIdentifier === 'return' || previousIdentifier === 'yield') &&
833
+ tokenLineBreakBefore)) ||
834
+ (isPunct(tokens[tokens.length - 1], '>') && isArrowHead(tokens[tokens.length - 2])) ||
835
+ (prevKind === 'start' && (frame?.kind === 'template-expr' || frame?.kind === 'jsx-expr')));
836
+ if (value === 'async') {
837
+ asyncFunctionIsExpression = expressionPosition;
838
+ if (isIdentifier(tokens[tokens.length - 1], 'default') &&
839
+ isIdentifier(tokens[tokens.length - 2], 'export')) {
840
+ asyncFunctionIsExpression = false;
841
+ }
842
+ }
843
+ prevIdentifier = value;
844
+ const isMemberName = prevKind === 'punct' && prevPunct === '.';
845
+ const atRoot = braceContexts.length === 0 && parenDepth === 0 && bracketDepth === 0 && frames.length === 0;
846
+ let moduleSignal = detectModule &&
847
+ !isMemberName &&
848
+ ((value === 'import' && importSignalsModule(source, i, atRoot)) ||
849
+ (value === 'export' && atRoot && exportSignalsModule(source, i)));
850
+ const classMemberName = value === 'class' &&
851
+ atMethodContainer() &&
852
+ memberDeclarationHead(tokens, tokens.length, insideClassBody(), currentClass()?.member?.start) !== undefined;
853
+ const nextSignificant = skipTrivia(source, i, n);
854
+ const classPropertyName = value === 'class' &&
855
+ (insideObjectLiteral() || insideClassBody()) &&
856
+ [':', '=', '(', ',', '}'].includes(source[nextSignificant] ?? '');
857
+ if (value === 'class' &&
858
+ !isMemberName &&
859
+ !classMemberName &&
860
+ !classPropertyName &&
861
+ typeAnnotation === undefined) {
862
+ const isExportDefault = isIdentifier(tokens[tokens.length - 1], 'default') &&
863
+ isIdentifier(tokens[tokens.length - 2], 'export');
864
+ pendingClassContexts.push({
865
+ parenDepth,
866
+ bracketDepth,
867
+ braceDepth: braceContexts.length,
868
+ frameDepth: frames.length,
869
+ isExpression: !isExportDefault && (previousIdentifier === 'extends' || expressionPosition),
870
+ });
871
+ }
872
+ // A keyword after `.` is a property name (e.g. `x.return`), not an expression start.
873
+ const forIterationSeparator = (value === 'of' || value === 'in') &&
874
+ !isMemberName &&
875
+ consumeForIterationSeparator(tokens, parameterContexts, {
876
+ parenDepth,
877
+ bracketDepth,
878
+ braceDepth: braceContexts.length,
879
+ frameDepth: frames.length,
880
+ });
881
+ let isExpressionKeyword = (REGEX_PRECEDING_KEYWORDS.has(value) || forIterationSeparator) && !isMemberName;
882
+ // `await`/`yield` are keywords only inside an async / generator function body. A member name
883
+ // (`obj.await`) is always a property access, never a keyword, so a following `/` divides.
884
+ if (!isMemberName) {
885
+ const expressionContext = expressionFunctionContexts[expressionFunctionContexts.length - 1];
886
+ const bodyContext = functionContextStack[functionContextStack.length - 1];
887
+ const parameterOwner = nearestFunctionParameter(parameterContexts);
888
+ const classMemberOwner = nearestClassField(braceContexts);
889
+ const parameterGrammarSuspended = parameterOwner !== undefined &&
890
+ functionContextStack.length <= parameterOwner.functionDepth &&
891
+ expressionContext?.parameterOwner !== parameterOwner;
892
+ const fieldGrammarSuspended = classMemberOwner !== undefined &&
893
+ functionContextStack.length <= classMemberOwner.functionDepth &&
894
+ expressionContext?.classMemberOwner !== classMemberOwner;
895
+ const expressionContextIsCurrent = expressionContext !== undefined &&
896
+ functionContextStack.length <= expressionContext.functionDepth
897
+ ? expressionContext
898
+ : undefined;
899
+ const functionContext = parameterGrammarSuspended || fieldGrammarSuspended
900
+ ? undefined
901
+ : (expressionContextIsCurrent ?? bodyContext);
902
+ const probe = detectModule &&
903
+ value === 'await' &&
904
+ !(prevKind === 'identifier' &&
905
+ ['class', 'function', 'var', 'let', 'const', 'extends'].includes(previousIdentifier ?? '')) &&
906
+ functionContext === undefined &&
907
+ !parameterGrammarSuspended &&
908
+ !fieldGrammarSuspended
909
+ ? probeAwaitOperand(source, i, parameterContexts[parameterContexts.length - 1]?.forPhase !== undefined)
910
+ : undefined;
911
+ moduleSignal ||= probe?.module === true;
912
+ if (value === 'await' &&
913
+ (functionContext?.async === true ||
914
+ probe?.keyword === true ||
915
+ (functionContext === undefined &&
916
+ module &&
917
+ !parameterGrammarSuspended &&
918
+ !fieldGrammarSuspended))) {
919
+ isExpressionKeyword = true;
920
+ }
921
+ else if (value === 'yield' && functionContext?.generator === true) {
922
+ isExpressionKeyword = true;
923
+ }
924
+ }
925
+ prevIsExpressionKeyword = isExpressionKeyword;
926
+ // Remember a control-statement keyword until its `(` is seen so only that head's `)` may be
927
+ // followed by a regex literal.
928
+ pendingControlKeyword = pendingControlKeywordAfter(value, isMemberName || insideObjectLiteral() || insideClassBody(), pendingControlKeyword);
929
+ // A `function` keyword (not a property name) starts a function signature. Track whether it is
930
+ // an expression (`const f = function(){}`) or a declaration (`function f(){}`), and whether it
931
+ // is async (`async function`, i.e. preceded by `async`) or a generator (`function*`, resolved
932
+ // when a following `*` is seen). The context is consumed at the body `{`.
933
+ const methodName = value === 'function' &&
934
+ atMethodContainer() &&
935
+ memberDeclarationHead(tokens, tokens.length, insideClassBody(), currentClass()?.member?.start) !== undefined;
936
+ if (value === 'function' && !isMemberName && !methodName && typeAnnotation === undefined) {
937
+ // `async function` is only async without a line terminator between the two words: a line
938
+ // break ends the `async` statement, so the declaration that follows is an ordinary one.
939
+ const isAsync = previousIdentifier === 'async' && prevKind === 'identifier' && !tokenLineBreakBefore;
940
+ pendingFunctionContexts.push({
941
+ requireScope: {
942
+ parent: requireScopes[requireScopes.length - 1],
943
+ bindings: [],
944
+ inParameters: true,
945
+ },
946
+ async: isAsync,
947
+ generator: false,
948
+ isExpression: isAsync
949
+ ? asyncFunctionIsExpression
950
+ : !(isIdentifier(tokens[tokens.length - 1], 'default') &&
951
+ isIdentifier(tokens[tokens.length - 2], 'export')) && expressionPosition,
952
+ parenDepth,
953
+ bracketDepth,
954
+ braceDepth: braceContexts.length,
955
+ frameDepth: frames.length,
956
+ parameterDepth: undefined,
957
+ phase: 'awaiting-parameters',
958
+ });
959
+ }
960
+ // A bare arrow parameter has no opening parenthesis to own its binding range.
961
+ const next = !inType && !isMemberName ? skipTrivia(source, i, n) : n;
962
+ const singleArrowContext = source[next] === '=' && source[next + 1] === '>'
963
+ ? {
964
+ async: prevKind === 'identifier' &&
965
+ previousIdentifier === 'async' &&
966
+ !tokenLineBreakBefore,
967
+ generator: false,
968
+ isExpression: false,
969
+ requireScope: {
970
+ parent: requireScopes[requireScopes.length - 1],
971
+ bindings: [{ start: tokens.length, end: tokens.length + 1 }],
972
+ inParameters: false,
973
+ },
974
+ }
975
+ : undefined;
976
+ if (singleArrowContext?.requireScope !== undefined) {
977
+ requireScopes.push(singleArrowContext.requireScope);
978
+ }
979
+ emit({
980
+ kind: 'identifier',
981
+ value,
982
+ offset: start,
983
+ ...(singleArrowContext === undefined ? {} : { signatureContext: singleArrowContext }),
984
+ endOffset: i,
985
+ ...(value === 'require' && requireScopes.length > 0
986
+ ? { requireScope: requireScopes[requireScopes.length - 1] }
987
+ : {}),
988
+ ...(isMemberName ? { propertyName: true } : {}),
989
+ ...(moduleSignal ? { moduleSignal: true } : {}),
990
+ });
991
+ // Once the mode is known, only the definitive module pass should validate the remainder.
992
+ // Continuing this probe with script grammar could reject valid newline await operands.
993
+ if (detectModule && moduleSignal) {
994
+ return tokens;
995
+ }
996
+ if (startsAssertion) {
997
+ typeAnnotation = {
998
+ role: 'assertion',
999
+ conditionalDepth: 0,
1000
+ conditionalOperand: false,
1001
+ importCallPending: false,
1002
+ owner: {
1003
+ parenDepth,
1004
+ bracketDepth,
1005
+ braceDepth: braceContexts.length,
1006
+ frameDepth: frames.length,
1007
+ },
1008
+ parenDepth: 0,
1009
+ bracketDepth: 0,
1010
+ braceDepth: 0,
1011
+ angleDepth: 0,
1012
+ typeOperandExpected: true,
1013
+ };
1014
+ }
1015
+ prevKind = 'identifier';
1016
+ afterControlHead = false;
1017
+ continue;
1018
+ }
1019
+ // Numbers end an expression, so they affect the regex/division decision.
1020
+ if (isNumberStart(ch)) {
1021
+ const start = i;
167
1022
  i++;
1023
+ while (i < n) {
1024
+ const part = source[i];
1025
+ if (part === undefined || !(isIdentifierPartAt(source, i) || part === '.')) {
1026
+ break;
1027
+ }
1028
+ i++;
1029
+ }
1030
+ emit({ kind: 'number', offset: start, endOffset: i });
1031
+ if (typeAnnotation !== undefined) {
1032
+ typeAnnotation.typeOperandExpected = false;
1033
+ }
1034
+ prevKind = 'number';
1035
+ afterControlHead = false;
1036
+ pendingControlKeyword = undefined;
1037
+ continue;
1038
+ }
1039
+ if (ch === ':' && typeAnnotation === undefined) {
1040
+ const parameter = parameterContexts[parameterContexts.length - 1];
1041
+ if (previousSignature !== undefined && atLexicalDepth(previousSignature.owner)) {
1042
+ typeAnnotation = {
1043
+ role: 'return',
1044
+ signature: previousSignature,
1045
+ parenDepth: 0,
1046
+ bracketDepth: 0,
1047
+ braceDepth: 0,
1048
+ angleDepth: 0,
1049
+ typeOperandExpected: true,
1050
+ };
1051
+ inType = true;
1052
+ }
1053
+ else if (classState !== undefined &&
1054
+ memberAtToken !== undefined &&
1055
+ (memberAtToken.phase === 'field' || memberAtToken.phase === 'head')) {
1056
+ typeAnnotation = {
1057
+ role: 'field',
1058
+ owner: classState,
1059
+ member: memberAtToken,
1060
+ parenDepth: 0,
1061
+ bracketDepth: 0,
1062
+ braceDepth: 0,
1063
+ angleDepth: 0,
1064
+ typeOperandExpected: true,
1065
+ };
1066
+ memberAtToken.phase = 'type';
1067
+ inType = true;
1068
+ }
1069
+ else if (parameter?.signature !== undefined &&
1070
+ parameter.phase === 'binding' &&
1071
+ atLexicalDepth(parameter.depth)) {
1072
+ typeAnnotation = {
1073
+ role: 'parameter',
1074
+ owner: parameter,
1075
+ parenDepth: 0,
1076
+ bracketDepth: 0,
1077
+ braceDepth: 0,
1078
+ angleDepth: 0,
1079
+ typeOperandExpected: true,
1080
+ };
1081
+ parameter.bindingEnd ??= tokens.length;
1082
+ parameter.phase = 'type';
1083
+ inType = true;
1084
+ }
1085
+ }
1086
+ // Any other single character is punctuation. This (and the `:`/`;`/`}` handlers below) is the
1087
+ // only place that pushes a conditionalContext, and it is reached solely while scanning real
1088
+ // JavaScript: template text, JSX markup and `${...}`/`{...}` expression bodies are consumed by
1089
+ // the text-mode gates above, so inert punctuation inside them never contributes a conditional.
1090
+ // A `?` in template text is a plain character, not a ternary, so it must not leak here.
1091
+ if (!inType &&
1092
+ ch === '?' &&
1093
+ (memberAtToken === undefined || memberAtToken.phase === 'initializer') &&
1094
+ source[i + 1] !== '?' &&
1095
+ source[i - 1] !== '?' &&
1096
+ source[i + 1] !== '.') {
1097
+ conditionalContexts.push({
1098
+ parenDepth,
1099
+ bracketDepth,
1100
+ braceDepth: braceContexts.length,
1101
+ frameDepth: frames.length,
1102
+ });
1103
+ }
1104
+ else if (!inType && ch === ':') {
1105
+ const isConditional = conditional !== undefined && atLexicalDepth(conditional);
1106
+ if (isConditional) {
1107
+ conditionalContexts.pop();
1108
+ }
1109
+ colonIsExpression = isConditional || insideObjectLiteral();
1110
+ }
1111
+ else if (!inType && ch === ';' && conditional !== undefined && atLexicalDepth(conditional)) {
1112
+ conditionalContexts.pop();
1113
+ }
1114
+ if (!inType && ch === '{') {
1115
+ // Record what this brace opens, so its `}` knows whether an expression or a statement ended.
1116
+ let isObject = opensObjectLiteral(prevKind, prevPunct, prevIdentifier, prevIsExpressionKeyword, frame?.kind === 'template-expr' || frame?.kind === 'jsx-expr', colonIsExpression, tokenLineBreakBefore);
1117
+ const pending = pendingFunctionContexts[pendingFunctionContexts.length - 1];
1118
+ let context;
1119
+ if (pending?.phase === 'awaiting-body' && atLexicalDepth(pending)) {
1120
+ context = pendingFunctionContexts.pop();
1121
+ }
1122
+ if (context !== undefined) {
1123
+ isObject = false;
1124
+ }
1125
+ const classExpression = !isObject && context === undefined
1126
+ ? takeClassBodyContext(pendingClassContexts, {
1127
+ parenDepth,
1128
+ bracketDepth,
1129
+ braceDepth: braceContexts.length,
1130
+ frameDepth: frames.length,
1131
+ })
1132
+ : undefined;
1133
+ const isClassBody = classExpression !== undefined;
1134
+ opensClassMemberBoundary = isClassBody;
1135
+ const isStaticBlock = !isObject &&
1136
+ !isClassBody &&
1137
+ insideClassBody() &&
1138
+ isIdentifier(tokens[tokens.length - 1], 'static');
1139
+ // A statement-block brace that consumes a pending function signature is a function body. A
1140
+ // function expression used as a value ends an expression (so a following `/` divides), while a
1141
+ // declaration in statement position starts a block (so a following `/` may be a regex).
1142
+ //
1143
+ // Methods consume the signature captured at their parameter opener. Arrows use their
1144
+ // contiguous operator; neither a control head nor an unrelated call can claim this brace.
1145
+ if (context === undefined && !isObject && !isClassBody) {
1146
+ const last = tokens[tokens.length - 1];
1147
+ context =
1148
+ isPunct(last, '>') && isArrowHead(tokens[tokens.length - 2])
1149
+ ? inferArrowContext(tokens, tokens.length - 2)
1150
+ : previousSignature !== undefined &&
1151
+ !previousSignature.isArrowSignature &&
1152
+ atLexicalDepth(previousSignature.owner)
1153
+ ? previousSignature.context
1154
+ : undefined;
1155
+ }
1156
+ if (!isObject && context !== undefined) {
1157
+ functionContextStack.push(context);
1158
+ }
1159
+ const owner = currentClass();
1160
+ if (context !== undefined) {
1161
+ braceContexts.push({
1162
+ role: 'function',
1163
+ context,
1164
+ ...(context.isClassMember === true && owner !== undefined ? { owner } : {}),
1165
+ });
1166
+ }
1167
+ else if (isClassBody) {
1168
+ braceContexts.push({
1169
+ role: 'class',
1170
+ isExpression: classExpression.isExpression,
1171
+ state: {
1172
+ parenDepth,
1173
+ bracketDepth,
1174
+ braceDepth: braceContexts.length + 1,
1175
+ frameDepth: frames.length,
1176
+ member: undefined,
1177
+ },
1178
+ });
1179
+ }
1180
+ else if (isStaticBlock && owner !== undefined) {
1181
+ braceContexts.push({ role: 'static-block', owner });
1182
+ }
1183
+ else {
1184
+ braceContexts.push(isObject
1185
+ ? {
1186
+ role: 'object',
1187
+ depth: {
1188
+ parenDepth,
1189
+ bracketDepth,
1190
+ braceDepth: braceContexts.length + 1,
1191
+ frameDepth: frames.length,
1192
+ },
1193
+ }
1194
+ : { role: 'block' });
1195
+ }
1196
+ // Count the brace inside the innermost open `${...}` or JSX `{...}` expression so its closing
1197
+ // `}` is found.
1198
+ if (frame?.kind === 'template-expr' || frame?.kind === 'jsx-expr') {
1199
+ frame.depth += 1;
1200
+ }
1201
+ }
1202
+ else if (!inType && ch === '(') {
1203
+ // A `(` after a control keyword opens a control head; only its matching `)` may then be
1204
+ // followed by a regex literal, so the nesting depth of every `(` has to be tracked.
1205
+ const pending = pendingFunctionContexts[pendingFunctionContexts.length - 1];
1206
+ const signatureCandidate = parameterSignatureContext(tokens, pending !== undefined && atLexicalDepth(pending) ? pending : undefined, atMethodContainer(), insideClassBody(), tokenLineBreakBefore, currentClass()?.member?.start);
1207
+ // A ternary's alternate may itself be an arrow. Its => is not proof that
1208
+ // the consequent's parentheses were parameters: a typed arrow must still
1209
+ // leave the enclosing conditional's colon after its own body.
1210
+ const needsConditionalEnd = conditional !== undefined && atLexicalDepth(conditional);
1211
+ const selectedSignature = signatureCandidate?.isArrowSignature === true &&
1212
+ !arrowFollowsParametersSafely(source, i, n, jsx, needsConditionalEnd)
1213
+ ? undefined
1214
+ : signatureCandidate;
1215
+ const signature = selectedSignature === undefined
1216
+ ? undefined
1217
+ : {
1218
+ ...selectedSignature,
1219
+ context: selectedSignature.context.requireScope !== undefined
1220
+ ? selectedSignature.context
1221
+ : {
1222
+ ...selectedSignature.context,
1223
+ requireScope: {
1224
+ parent: requireScopes[requireScopes.length - 1],
1225
+ bindings: [],
1226
+ inParameters: true,
1227
+ },
1228
+ },
1229
+ };
1230
+ if (signature?.context.requireScope !== undefined) {
1231
+ requireScopes.push(signature.context.requireScope);
1232
+ }
1233
+ parameterContexts.push({
1234
+ openIndex: tokens.length,
1235
+ bindingStart: tokens.length + 1,
1236
+ bindingEnd: undefined,
1237
+ bindingCommas: [],
1238
+ signature: signature === undefined
1239
+ ? undefined
1240
+ : {
1241
+ ...signature,
1242
+ owner: {
1243
+ parenDepth,
1244
+ bracketDepth,
1245
+ braceDepth: braceContexts.length,
1246
+ frameDepth: frames.length,
1247
+ },
1248
+ },
1249
+ control: pendingControlKeyword !== undefined,
1250
+ controlKeyword: pendingControlKeyword,
1251
+ functionDepth: functionContextStack.length,
1252
+ depth: {
1253
+ parenDepth: parenDepth + 1,
1254
+ bracketDepth,
1255
+ braceDepth: braceContexts.length,
1256
+ frameDepth: frames.length,
1257
+ },
1258
+ phase: 'binding',
1259
+ ...(pendingControlKeyword === 'for' ? { forPhase: 'binding' } : {}),
1260
+ });
1261
+ if (pending?.phase === 'awaiting-parameters' &&
1262
+ atLexicalDepth(pending) &&
1263
+ signature?.isFunctionKeyword === true) {
1264
+ pending.parameterDepth = parenDepth + 1;
1265
+ pending.phase = 'parameters';
1266
+ }
1267
+ parenDepth++;
1268
+ pendingControlKeyword = undefined;
1269
+ afterControlHead = false;
1270
+ }
1271
+ else if (!inType && ch === '[') {
1272
+ bracketDepth++;
1273
+ }
1274
+ else if (!inType && ch === ')') {
1275
+ const pending = pendingFunctionContexts[pendingFunctionContexts.length - 1];
1276
+ if (pending?.parameterDepth === parenDepth &&
1277
+ bracketDepth === pending.bracketDepth &&
1278
+ braceContexts.length === pending.braceDepth &&
1279
+ frames.length === pending.frameDepth) {
1280
+ pending.phase = 'awaiting-body';
1281
+ }
1282
+ const parameters = parameterContexts.pop();
1283
+ if (parameters?.signature !== undefined) {
1284
+ recordRequireBinding(parameters, tokens.length);
1285
+ const scope = parameters.signature.context.requireScope;
1286
+ if (scope !== undefined) {
1287
+ scope.inParameters = false;
1288
+ }
1289
+ }
1290
+ matchingOpen = parameters?.openIndex;
1291
+ closedParameterSignature = parameters?.signature;
1292
+ if (closedParameterSignature !== undefined &&
1293
+ !closedParameterSignature.isFunctionKeyword &&
1294
+ !closedParameterSignature.isArrowSignature) {
1295
+ pendingFunctionContexts.push({
1296
+ ...closedParameterSignature.context,
1297
+ ...closedParameterSignature.owner,
1298
+ parameterDepth: undefined,
1299
+ phase: 'awaiting-body',
1300
+ });
1301
+ }
1302
+ afterControlHead = parameters?.control === true;
1303
+ if (parenDepth > 0) {
1304
+ parenDepth--;
1305
+ }
1306
+ }
1307
+ else if (!inType && ch === ']') {
1308
+ if (bracketDepth > 0) {
1309
+ bracketDepth--;
1310
+ }
1311
+ }
1312
+ if (!inType && (ch === '+' || ch === '-') && source[i + 1] === ch) {
1313
+ const previous = tokens[tokens.length - 1];
1314
+ const postfix = !tokenLineBreakBefore &&
1315
+ previous !== undefined &&
1316
+ isMemberExpressionEnd(previous) &&
1317
+ !prevIsExpressionKeyword;
1318
+ emit({
1319
+ kind: 'punct',
1320
+ value: ch + ch,
1321
+ offset: i,
1322
+ endOffset: i + 2,
1323
+ expressionEnd: postfix,
1324
+ });
1325
+ prevKind = 'punct';
1326
+ prevPunct = ch + ch;
1327
+ i += 2;
1328
+ continue;
1329
+ }
1330
+ // An arrow's expression body has no brace where its function context can be consumed. Record
1331
+ // its bounded context after `=>`; a following delimiter removes it again.
1332
+ if (!inType && ch === '>' && isArrowHead(tokens[tokens.length - 1], i)) {
1333
+ const context = typedArrowContext ?? inferArrowContext(tokens, tokens.length - 1);
1334
+ const next = skipTrivia(source, i + 1, n);
1335
+ if (context !== undefined) {
1336
+ if (source[next] === '{') {
1337
+ pendingFunctionContexts.push({
1338
+ ...context,
1339
+ parenDepth,
1340
+ bracketDepth,
1341
+ braceDepth: braceContexts.length,
1342
+ frameDepth: frames.length,
1343
+ parameterDepth: undefined,
1344
+ phase: 'awaiting-body',
1345
+ });
1346
+ }
1347
+ else {
1348
+ const parameterOwner = nearestFunctionParameter(parameterContexts);
1349
+ const classMemberOwner = nearestClassField(braceContexts);
1350
+ expressionFunctionContexts.push({
1351
+ ...context,
1352
+ isExpression: true,
1353
+ parenDepth,
1354
+ bracketDepth,
1355
+ braceDepth: braceContexts.length,
1356
+ frameDepth: frames.length,
1357
+ functionDepth: functionContextStack.length,
1358
+ conditionalDepth: conditionalContexts.length,
1359
+ ...(parameterOwner === undefined ? {} : { parameterOwner }),
1360
+ ...(classMemberOwner === undefined ? {} : { classMemberOwner }),
1361
+ });
1362
+ }
1363
+ }
1364
+ }
1365
+ // A `*` right after `function` (whitespace/comments aside) marks a generator: `function*` and
1366
+ // `function *`. It is recorded on the pending signature, consumed at the body `{`.
1367
+ const pending = pendingFunctionContexts[pendingFunctionContexts.length - 1];
1368
+ const parameter = parameterContexts[parameterContexts.length - 1];
1369
+ if (ch === ';' && parameter?.forPhase !== undefined && atLexicalDepth(parameter.depth)) {
1370
+ parameter.forPhase = 'regular';
1371
+ }
1372
+ if (!inType && parameter !== undefined && atLexicalDepth(parameter.depth)) {
1373
+ if (ch === ',') {
1374
+ const scope = parameter.signature?.context.requireScope;
1375
+ if (scope !== undefined) {
1376
+ parameter.bindingCommas.push({
1377
+ index: tokens.length,
1378
+ bindingStart: parameter.bindingStart,
1379
+ bindingEnd: parameter.bindingEnd,
1380
+ phase: parameter.phase,
1381
+ bindingCount: scope.bindings.length,
1382
+ });
1383
+ }
1384
+ recordRequireBinding(parameter, tokens.length);
1385
+ parameter.bindingStart = tokens.length + 1;
1386
+ parameter.bindingEnd = undefined;
1387
+ parameter.phase = 'binding';
1388
+ }
1389
+ else if (equalsOperator?.operator === 'assignment') {
1390
+ parameter.bindingEnd ??= tokens.length;
1391
+ parameter.phase = 'initializer';
1392
+ }
1393
+ }
1394
+ if (!inType &&
1395
+ ch === '*' &&
1396
+ prevKind === 'identifier' &&
1397
+ prevIdentifier === 'function' &&
1398
+ pending !== undefined) {
1399
+ pendingFunctionContexts[pendingFunctionContexts.length - 1] = {
1400
+ ...pending,
1401
+ generator: true,
1402
+ };
1403
+ }
1404
+ // A callback's statements cannot invalidate a signature outside its body or parameter list.
1405
+ if (!inType && ch === ';' && pending !== undefined && atLexicalDepth(pending)) {
1406
+ closeRequireScope(pendingFunctionContexts.pop()?.requireScope);
1407
+ }
1408
+ const previousToken = tokens[tokens.length - 1];
1409
+ const openingSignature = ch === '(' ? parameterContexts[parameterContexts.length - 1]?.signature?.context : undefined;
1410
+ emit({
1411
+ kind: 'punct',
1412
+ value: ch,
1413
+ offset: i,
1414
+ endOffset: i + 1,
1415
+ ...(ch === '!' &&
1416
+ source[i + 1] !== '=' &&
1417
+ previousToken !== undefined &&
1418
+ isMemberExpressionEnd(previousToken) &&
1419
+ !prevIsExpressionKeyword
1420
+ ? { expressionEnd: true }
1421
+ : {}),
1422
+ ...equalsOperator,
1423
+ ...(openingSignature === undefined ? {} : { signatureContext: openingSignature }),
1424
+ ...(matchingOpen !== undefined ? { matchingOpen } : {}),
1425
+ ...(closedClassMemberBoundary || opensClassMemberBoundary || (ch === ';' && insideClassBody())
1426
+ ? { memberSeparator: true }
1427
+ : {}),
1428
+ ...(closedExpressionBrace ? { expressionEnd: true } : {}),
1429
+ });
1430
+ if (!inType &&
1431
+ ch === '>' &&
1432
+ requireScopes[requireScopes.length - 1]?.inParameters === true &&
1433
+ !isArrowHead(tokens[tokens.length - 2], i)) {
1434
+ const start = markParameterTypeArguments(tokens, tokens.length);
1435
+ const owner = parameterContexts[parameterContexts.length - 1];
1436
+ const scope = owner?.signature?.context.requireScope;
1437
+ if (start !== undefined && owner !== undefined && scope !== undefined) {
1438
+ // Commas were provisional until the closing > established a type-argument list.
1439
+ // Restore the initializer state as well as its binding ranges; do not re-lex source.
1440
+ let first = owner.bindingCommas.length;
1441
+ while (first > 0 && (owner.bindingCommas[first - 1]?.index ?? -1) >= start) {
1442
+ first--;
1443
+ }
1444
+ const checkpoint = owner.bindingCommas[first];
1445
+ if (checkpoint !== undefined) {
1446
+ owner.bindingStart = checkpoint.bindingStart;
1447
+ owner.bindingEnd = checkpoint.bindingEnd;
1448
+ owner.phase = checkpoint.phase;
1449
+ scope.bindings.length = checkpoint.bindingCount;
1450
+ owner.bindingCommas.length = first;
1451
+ }
1452
+ }
1453
+ }
1454
+ prevKind = 'punct';
1455
+ prevPunct = ch;
1456
+ i++;
1457
+ }
1458
+ // Reaching the end of the source with an open frame means the template literal, the JSX element or
1459
+ // the JSX expression container was never closed. The innermost open frame is the one that failed.
1460
+ const openFrame = frames[frames.length - 1];
1461
+ if (inText || openFrame?.kind === 'template-expr') {
1462
+ throw new ModuleSpecifierError('Unterminated template literal', 'invalid-source', inText ? templateStart : (openFrame?.offset ?? templateStart));
1463
+ }
1464
+ if (openFrame?.kind === 'jsx-expr') {
1465
+ throw new ModuleSpecifierError('Unterminated JSX expression', 'invalid-source', openFrame.offset);
1466
+ }
1467
+ if (openFrame !== undefined) {
1468
+ throw new ModuleSpecifierError('Unterminated JSX element', 'invalid-source', openFrame.offset);
1469
+ }
1470
+ return tokens;
1471
+ }
1472
+ // A `<` followed by an identifier that begins a generic type-argument list (e.g. `<T,>`, `<T = X>`,
1473
+ // `<T extends U>`, `<const T,>`) or a generic function signature (`<T>(x: T) => T`) is not a JSX
1474
+ // element. `<T>` alone in expression position remains ambiguous and is treated as JSX.
1475
+ function jsxTagIsGeneric(source, i, n) {
1476
+ try {
1477
+ if (!isIdentifierStartAt(source, i)) {
1478
+ return false;
1479
+ }
1480
+ const identifier = readIdentifier(source, i);
1481
+ let k = identifier.end;
1482
+ const name = identifier.value;
1483
+ k = skipTrivia(source, k, n);
1484
+ const next = source[k];
1485
+ if (next === ',' || next === '=') {
1486
+ return true;
1487
+ }
1488
+ if (next === '>') {
1489
+ const j = skipTrivia(source, k + 1, n);
1490
+ if (source[j] === '(') {
1491
+ return arrowFollowsParameters(source, j, n);
1492
+ }
1493
+ }
1494
+ let end = k;
1495
+ while (end < n && isIdentifierPartAt(source, end)) {
1496
+ end++;
1497
+ }
1498
+ if (source.slice(k, end) === 'extends') {
1499
+ // `extends` is a generic constraint only when it introduces a constraint type that closes
1500
+ // the type-parameter list. `implements` is not a TypeScript type-parameter constraint and
1501
+ // remains a JSX boolean attribute.
1502
+ const j = skipTrivia(source, end, n);
1503
+ const after = source[j];
1504
+ if (after !== '=' &&
1505
+ after !== '"' &&
1506
+ after !== "'" &&
1507
+ after !== '`' &&
1508
+ after !== '>' &&
1509
+ after !== '/') {
1510
+ return true;
1511
+ }
1512
+ }
1513
+ // `const T, ...`: a TypeScript const type parameter, whose real name follows the modifier.
1514
+ if (name === 'const' && k < n && isIdentifierStartAt(source, k)) {
1515
+ return jsxTagIsGeneric(source, k, n);
1516
+ }
1517
+ return false;
1518
+ }
1519
+ catch (error) {
1520
+ if (error instanceof ModuleSpecifierError) {
1521
+ // This is speculative: an apostrophe or `/*` may be inert JSX children, not broken JS.
1522
+ // Let the normal JSX path scan it; genuinely incomplete markup will still fail there.
1523
+ return false;
1524
+ }
1525
+ throw error;
1526
+ }
1527
+ }
1528
+ // Scan a generic function signature's parameter list starting at its opening `(` (offset `open`).
1529
+ // Returns true only when the list is closed and `=>` follows, so callers can treat `<T>(...) =>` as
1530
+ // a generic signature rather than a JSX element.
1531
+ //
1532
+ // Brackets inside strings, template literals and regex literals are inert: a `)` inside any of them
1533
+ // must not be counted as the parameter-list close. Template interpolations are skipped opaquely
1534
+ // because a template literal is a single argument value, so its internal parentheses never shape
1535
+ // the parameter list. Regex-vs-division tracking mirrors the main tokenizer so a regex default
1536
+ // (for example `= /a/)`) is skipped instead of read as division.
1537
+ function arrowFollowsParameters(source, open, n, jsx = false, needsConditionalEnd = false, mode = 'parameters') {
1538
+ let depth = 0;
1539
+ let bracketDepth = 0;
1540
+ let braceDepth = 0;
1541
+ let conditionalDepth = 0;
1542
+ let assertionOwner;
1543
+ let prevKind = 'start';
1544
+ let prevPunct;
1545
+ let k = open;
1546
+ const jsxFrames = [];
1547
+ const typeArgumentBounds = new Map();
1548
+ while (k < n) {
1549
+ let ch = source[k];
1550
+ const frame = jsxFrames[jsxFrames.length - 1];
1551
+ if (frame?.kind === 'jsx-text') {
1552
+ if (ch === '{') {
1553
+ jsxFrames.push({ kind: 'jsx-expr', depth: 0, offset: k });
1554
+ prevKind = 'start';
1555
+ prevPunct = undefined;
1556
+ k++;
1557
+ continue;
1558
+ }
1559
+ if (ch === '<' && source[k + 1] === '/') {
1560
+ const end = source.indexOf('>', k + 2);
1561
+ if (end < 0) {
1562
+ return false;
1563
+ }
1564
+ jsxFrames.pop();
1565
+ prevKind = 'template';
1566
+ prevPunct = undefined;
1567
+ k = end + 1;
1568
+ continue;
1569
+ }
1570
+ // Quotes, parentheses, backticks and comment markers in children are inert.
1571
+ if (ch !== '<') {
1572
+ k++;
1573
+ continue;
1574
+ }
1575
+ }
1576
+ else if (frame?.kind === 'jsx-tag') {
1577
+ if (ch === '<' && skipTrivia(source, frame.nameEnd, n) === k) {
1578
+ k = readJsxTypeArguments(source, k, n, []);
1579
+ }
1580
+ else if (ch === '{') {
1581
+ jsxFrames.push({ kind: 'jsx-expr', depth: 0, offset: k });
1582
+ prevKind = 'start';
1583
+ prevPunct = undefined;
1584
+ k++;
1585
+ }
1586
+ else if (ch === '"' || ch === "'") {
1587
+ k = skipJsxAttributeValue(source, k, n);
1588
+ }
1589
+ else if (ch === '/' && source[k + 1] === '>') {
1590
+ jsxFrames.pop();
1591
+ prevKind = 'template';
1592
+ prevPunct = undefined;
1593
+ k += 2;
1594
+ }
1595
+ else if (ch === '>') {
1596
+ jsxFrames.pop();
1597
+ jsxFrames.push({ kind: 'jsx-text', name: frame.name, offset: frame.offset });
1598
+ k++;
1599
+ }
1600
+ else {
1601
+ k++;
1602
+ }
1603
+ continue;
1604
+ }
1605
+ else {
1606
+ k = skipTrivia(source, k, n);
1607
+ ch = source[k];
1608
+ }
1609
+ if (ch === undefined) {
1610
+ break;
1611
+ }
1612
+ if (jsx &&
1613
+ ch === '<' &&
1614
+ (frame?.kind === 'jsx-text' || isRegexStart(prevKind, prevPunct, false, false, false))) {
1615
+ if (source[k + 1] === '>') {
1616
+ jsxFrames.push({ kind: 'jsx-text', name: null, offset: k });
1617
+ k += 2;
1618
+ continue;
1619
+ }
1620
+ if (isIdentifierStartAt(source, k + 1) &&
1621
+ (frame?.kind === 'jsx-text' || !jsxTagIsGeneric(source, k + 1, n))) {
1622
+ const tag = readJsxTagName(source, k + 1, n);
1623
+ jsxFrames.push({ kind: 'jsx-tag', name: tag.name, offset: k, nameEnd: tag.end });
1624
+ k = tag.end;
1625
+ continue;
1626
+ }
1627
+ }
1628
+ if (frame?.kind === 'jsx-text') {
1629
+ k++;
1630
+ continue;
1631
+ }
1632
+ if (frame?.kind === 'jsx-expr') {
1633
+ if (ch === '}') {
1634
+ if (frame.depth === 0) {
1635
+ jsxFrames.pop();
1636
+ prevKind = 'template';
1637
+ prevPunct = undefined;
1638
+ k++;
1639
+ continue;
1640
+ }
1641
+ frame.depth--;
1642
+ }
1643
+ else if (ch === '{') {
1644
+ frame.depth++;
1645
+ }
1646
+ }
1647
+ if (mode === 'conditional-body' && ch === '<' && prevKind === 'identifier') {
1648
+ const inAssertion = assertionOwner?.paren === depth &&
1649
+ assertionOwner.bracket === bracketDepth &&
1650
+ assertionOwner.brace === braceDepth &&
1651
+ assertionOwner.frame === jsxFrames.length;
1652
+ const end = expressionTypeArgumentEnd(source, k, n, typeArgumentBounds, inAssertion);
1653
+ if (end !== undefined) {
1654
+ k = end;
1655
+ prevKind = 'identifier';
1656
+ prevPunct = undefined;
1657
+ continue;
1658
+ }
1659
+ }
1660
+ if (assertionOwner?.paren === depth &&
1661
+ assertionOwner.bracket === bracketDepth &&
1662
+ assertionOwner.brace === braceDepth &&
1663
+ assertionOwner.frame === jsxFrames.length &&
1664
+ ['+', '-', '*', '/', '%', '=', '?', '!'].includes(ch)) {
1665
+ assertionOwner = undefined;
1666
+ }
1667
+ // Reuse the same opaque regions to confirm a typed nested arrow in a ternary.
1668
+ // Colons in JSX, object literals, nested delimiters or inner conditionals do not
1669
+ // close the enclosing conditional. Stop at this expression's own boundary.
1670
+ if (mode === 'conditional-body') {
1671
+ if (jsxFrames.length === 0 && depth === 0 && bracketDepth === 0 && braceDepth === 0) {
1672
+ if (ch === ':') {
1673
+ if (conditionalDepth === 0) {
1674
+ return true;
1675
+ }
1676
+ conditionalDepth--;
1677
+ }
1678
+ else if (ch === '?' &&
1679
+ source[k + 1] !== '?' &&
1680
+ source[k - 1] !== '?' &&
1681
+ source[k + 1] !== '.') {
1682
+ conditionalDepth++;
1683
+ }
1684
+ else if ([';', ',', ')', ']', '}'].includes(ch)) {
1685
+ return false;
1686
+ }
1687
+ }
1688
+ if (ch === '[')
1689
+ bracketDepth++;
1690
+ else if (ch === ']')
1691
+ bracketDepth--;
1692
+ else if (ch === '{')
1693
+ braceDepth++;
1694
+ else if (ch === '}')
1695
+ braceDepth--;
1696
+ }
1697
+ // String literals: their brackets and slashes are inert text, so skip the whole value.
1698
+ if (ch === "'" || ch === '"') {
1699
+ k = readString(source, k).next;
1700
+ prevKind = 'string';
1701
+ continue;
1702
+ }
1703
+ // Template literals: skip their text and interpolation so a `)` inside a template is not
1704
+ // counted as the parameter-list close.
1705
+ if (ch === '`') {
1706
+ k = skipTemplateLiteral(source, k);
1707
+ prevKind = 'template';
1708
+ continue;
1709
+ }
1710
+ // Regex literals: a `/` that begins a regex must be skipped so its `)` stays inside the pattern
1711
+ // rather than closing the parameter list.
1712
+ if (ch === '/' && isRegexStart(prevKind, prevPunct, false, false, false)) {
1713
+ k = skipRegex(source, k);
1714
+ prevKind = 'regex';
1715
+ continue;
1716
+ }
1717
+ if (ch === '(') {
1718
+ depth++;
1719
+ prevKind = 'punct';
1720
+ prevPunct = '(';
1721
+ }
1722
+ else if (ch === ')') {
1723
+ depth--;
1724
+ prevKind = 'punct';
1725
+ prevPunct = ')';
1726
+ if (depth === 0 && mode === 'parameters') {
1727
+ const j = skipTrivia(source, k + 1, n);
1728
+ if (source[j] === '=' && source[j + 1] === '>') {
1729
+ return true;
1730
+ }
1731
+ const arrow = source[j] === ':' ? arrowAfterReturnType(source, j + 1, n) : undefined;
1732
+ return (arrow !== undefined &&
1733
+ (!needsConditionalEnd ||
1734
+ arrowFollowsParameters(source, arrow + 2, n, jsx, false, 'conditional-body')));
1735
+ }
1736
+ }
1737
+ else if (isIdentifierStartAt(source, k)) {
1738
+ const identifier = readIdentifier(source, k);
1739
+ if (mode === 'conditional-body' &&
1740
+ ['as', 'satisfies'].includes(identifier.value) &&
1741
+ prevKind !== 'start' &&
1742
+ !(prevKind === 'punct' && prevPunct === '.')) {
1743
+ assertionOwner = {
1744
+ paren: depth,
1745
+ bracket: bracketDepth,
1746
+ brace: braceDepth,
1747
+ frame: jsxFrames.length,
1748
+ };
1749
+ }
1750
+ k = identifier.end - 1;
1751
+ prevKind = 'identifier';
1752
+ prevPunct = undefined;
1753
+ }
1754
+ else {
1755
+ prevKind = 'punct';
1756
+ prevPunct = ch;
1757
+ }
1758
+ k++;
1759
+ }
1760
+ return false;
1761
+ }
1762
+ /** Skip a balanced type-argument list only when its suffix can continue an expression. */
1763
+ function expressionTypeArgumentEnd(source, open, n, bounds, typeAssertion = false) {
1764
+ let end = bounds.get(open);
1765
+ if (end === undefined) {
1766
+ try {
1767
+ end = readJsxTypeArguments(source, open, n, [], true, bounds);
1768
+ }
1769
+ catch (error) {
1770
+ if (error instanceof ModuleSpecifierError) {
1771
+ bounds.set(open, null);
1772
+ return undefined;
1773
+ }
1774
+ throw error;
1775
+ }
1776
+ }
1777
+ if (end === null)
1778
+ return undefined;
1779
+ const next = skipTrivia(source, end, n);
1780
+ const ch = source[next];
1781
+ if (!typeAssertion)
1782
+ return ch === '(' ? end : undefined;
1783
+ if (isIdentifierStartAt(source, next) || isUnicodeEscapeStart(source, next)) {
1784
+ return ['as', 'satisfies', 'in', 'instanceof'].includes(readIdentifier(source, next).value)
1785
+ ? end
1786
+ : undefined;
1787
+ }
1788
+ return ch !== undefined && (isNumberStart(ch) || ch === '"' || ch === "'") ? undefined : end;
1789
+ }
1790
+ /** A newline computed method head ends a complete bodyless member return annotation. */
1791
+ function computedMethodFollows(source, start, n) {
1792
+ let depth = 0;
1793
+ let index = start;
1794
+ let previous = 'start';
1795
+ let punct;
1796
+ while (index < n) {
1797
+ index = skipTrivia(source, index, n);
1798
+ const ch = source[index];
1799
+ if (ch === undefined) {
1800
+ break;
1801
+ }
1802
+ if (ch === "'" || ch === '"') {
1803
+ index = readString(source, index).next;
1804
+ previous = 'string';
1805
+ continue;
1806
+ }
1807
+ if (ch === '`') {
1808
+ index = skipTemplateLiteral(source, index);
1809
+ previous = 'template';
1810
+ continue;
1811
+ }
1812
+ if (ch === '/' && isRegexStart(previous, punct, false, false, false)) {
1813
+ index = skipRegex(source, index);
1814
+ previous = 'regex';
1815
+ continue;
1816
+ }
1817
+ if (ch === '[') {
1818
+ depth++;
1819
+ }
1820
+ else if (ch === ']') {
1821
+ depth--;
1822
+ if (depth === 0) {
1823
+ return source[skipTrivia(source, index + 1, n)] === '(';
1824
+ }
1825
+ }
1826
+ if (isIdentifierStartAt(source, index) || isUnicodeEscapeStart(source, index)) {
1827
+ index = readIdentifier(source, index).end;
1828
+ previous = 'identifier';
1829
+ }
1830
+ else {
1831
+ previous = isNumberStart(ch) ? 'number' : 'punct';
1832
+ punct = ch;
1833
+ index++;
1834
+ }
1835
+ }
1836
+ return false;
1837
+ }
1838
+ function arrowAfterReturnType(source, start, n) {
1839
+ let parenDepth = 0;
1840
+ let bracketDepth = 0;
1841
+ let braceDepth = 0;
1842
+ let angleDepth = 0;
1843
+ let index = start;
1844
+ while (index < n) {
1845
+ index = skipTrivia(source, index, n);
1846
+ const ch = source[index];
1847
+ if (ch === undefined) {
1848
+ return undefined;
1849
+ }
1850
+ if (ch === "'" || ch === '"') {
1851
+ index = readString(source, index).next;
1852
+ continue;
1853
+ }
1854
+ if (ch === '`') {
1855
+ index = skipTemplateLiteral(source, index);
1856
+ continue;
1857
+ }
1858
+ if (ch === '=' && source[index + 1] === '>') {
1859
+ if (parenDepth === 0 && bracketDepth === 0 && braceDepth === 0 && angleDepth === 0) {
1860
+ return index;
1861
+ }
1862
+ index += 2;
1863
+ continue;
1864
+ }
1865
+ if (parenDepth === 0 &&
1866
+ bracketDepth === 0 &&
1867
+ braceDepth === 0 &&
1868
+ angleDepth === 0 &&
1869
+ (isIdentifierStartAt(source, index) || isUnicodeEscapeStart(source, index))) {
1870
+ const identifier = readIdentifier(source, index);
1871
+ const next = skipTrivia(source, identifier.end, n);
1872
+ // An operand followed by a call is runtime code (not a return type). In particular,
1873
+ // a conditional's `: async () =>` must not claim the preceding sequence expression.
1874
+ if (source[next] === '(' &&
1875
+ identifier.value !== 'import' &&
1876
+ !TYPE_OPERAND_KEYWORDS.has(identifier.value)) {
1877
+ return undefined;
1878
+ }
1879
+ index = identifier.end;
1880
+ continue;
1881
+ }
1882
+ if (ch === '(') {
1883
+ parenDepth++;
1884
+ }
1885
+ else if (ch === ')') {
1886
+ if (parenDepth === 0) {
1887
+ return undefined;
1888
+ }
1889
+ parenDepth--;
1890
+ }
1891
+ else if (ch === '[') {
1892
+ bracketDepth++;
1893
+ }
1894
+ else if (ch === ']') {
1895
+ if (bracketDepth === 0) {
1896
+ return undefined;
1897
+ }
1898
+ bracketDepth--;
1899
+ }
1900
+ else if (ch === '{') {
1901
+ braceDepth++;
1902
+ }
1903
+ else if (ch === '}') {
1904
+ if (braceDepth === 0) {
1905
+ return undefined;
1906
+ }
1907
+ braceDepth--;
1908
+ }
1909
+ else if (ch === '<') {
1910
+ angleDepth++;
1911
+ }
1912
+ else if (ch === '>') {
1913
+ if (angleDepth === 0) {
1914
+ return undefined;
1915
+ }
1916
+ angleDepth--;
1917
+ }
1918
+ else if (parenDepth === 0 &&
1919
+ bracketDepth === 0 &&
1920
+ braceDepth === 0 &&
1921
+ angleDepth === 0 &&
1922
+ (ch === ';' || ch === ',')) {
1923
+ return undefined;
1924
+ }
1925
+ index++;
1926
+ }
1927
+ return undefined;
1928
+ }
1929
+ function arrowFollowsParametersSafely(source, open, n, jsx = false, needsConditionalEnd = false) {
1930
+ try {
1931
+ return arrowFollowsParameters(source, open, n, jsx, needsConditionalEnd);
1932
+ }
1933
+ catch (error) {
1934
+ if (error instanceof ModuleSpecifierError) {
1935
+ return false;
1936
+ }
1937
+ throw error;
1938
+ }
1939
+ }
1940
+ // Skip a template literal starting at its opening backtick (offset `start`). Returns the index just
1941
+ // past the closing backtick.
1942
+ //
1943
+ // `${...}` interpolations are skipped opaquely by brace depth: the caller only needs this region to
1944
+ // be invisible so its brackets never reach the parameter-list accounting, which is why the body is
1945
+ // not re-lexed here.
1946
+ //
1947
+ // @throws {ModuleSpecifierError} on an unterminated template literal.
1948
+ function skipTemplateLiteral(source, start) {
1949
+ const n = source.length;
1950
+ let i = start + 1; // past the opening backtick
1951
+ while (i < n) {
1952
+ const ch = source[i];
1953
+ // A backslash escapes the next character (including a nested backtick or `${`).
1954
+ if (ch === '\\') {
1955
+ i += 2;
1956
+ continue;
1957
+ }
1958
+ if (ch === '`') {
1959
+ return i + 1;
1960
+ }
1961
+ if (ch === '$' && source[i + 1] === '{') {
1962
+ // Skip the interpolation until its braces balance. Nested templates/braces are handled by the
1963
+ // depth counter; a raw line terminator is invalid inside a template and stops the scan.
1964
+ let depth = 1;
1965
+ i += 2;
1966
+ while (i < n && depth > 0) {
1967
+ const inner = source[i];
1968
+ if (inner === '\n' || inner === '\r') {
1969
+ throw new ModuleSpecifierError('Unterminated template literal', 'invalid-source', start);
1970
+ }
1971
+ if (inner === '\\') {
1972
+ i += 2;
1973
+ continue;
1974
+ }
1975
+ if (inner === '{') {
1976
+ depth++;
1977
+ }
1978
+ else if (inner === '}') {
1979
+ depth--;
1980
+ }
1981
+ i++;
1982
+ }
1983
+ continue;
1984
+ }
1985
+ i++;
1986
+ }
1987
+ throw new ModuleSpecifierError('Unterminated template literal', 'invalid-source', start);
1988
+ }
1989
+ /** Probe template types with the same type boundaries as their interpolated operands. */
1990
+ function skipTemplateType(source, start, n, bounds) {
1991
+ let i = start + 1;
1992
+ while (i < n) {
1993
+ if (source[i] === '\\')
1994
+ i += 2;
1995
+ else if (source[i] === '`')
1996
+ return i + 1;
1997
+ else if (source[i] === '$' && source[i + 1] === '{') {
1998
+ i = readJsxTypeArguments(source, i + 1, n, [], true, bounds, '}');
1999
+ }
2000
+ else
2001
+ i++;
2002
+ }
2003
+ throw new ModuleSpecifierError('Unterminated template literal', 'invalid-source', start);
2004
+ }
2005
+ /** Only a complete type followed by a line break can begin another bodyless type member. */
2006
+ function typeMemberFollowsLineBreak(tokens, lineBreak) {
2007
+ const complete = (token) => {
2008
+ if (token === undefined)
2009
+ return false;
2010
+ if (token.kind === 'identifier')
2011
+ return !TYPE_OPERAND_KEYWORDS.has(token.value);
2012
+ return (token.kind === 'string' ||
2013
+ token.kind === 'number' ||
2014
+ token.kind === 'template' ||
2015
+ (token.kind === 'punct' &&
2016
+ ([')', ']', '}', '>'].includes(token.value) || /^[0-9]$/.test(token.value))));
2017
+ };
2018
+ let index = tokens.length - 1;
2019
+ if (lineBreak && complete(tokens[index]))
2020
+ return true;
2021
+ // The line break may precede a modifier rather than the method name (e.g. get b()).
2022
+ while (index >= 0) {
2023
+ const token = tokens[index];
2024
+ if (token?.kind !== 'identifier' || !MEMBER_MODIFIERS.has(token.value))
2025
+ break;
2026
+ if (token.lineBreakBefore === true && complete(tokens[index - 1]))
2027
+ return true;
2028
+ index--;
2029
+ }
2030
+ return false;
2031
+ }
2032
+ /**
2033
+ * Lex only the boundaries of a component's type arguments. These tokens preserve literal imports
2034
+ * without changing the surrounding JavaScript function/brace state or treating type braces as JSX.
2035
+ */
2036
+ function readJsxTypeArguments(source, open, n, tokens, expressionProbe = false, bounds, rootClose = '>') {
2037
+ const closers = [rootClose];
2038
+ const opens = [open];
2039
+ const conditions = [0];
2040
+ let i = open + 1;
2041
+ try {
2042
+ while (i < n) {
2043
+ const trivia = readTrivia(source, i, n);
2044
+ i = trivia.end;
2045
+ const ch = source[i];
2046
+ if (ch === undefined) {
2047
+ break;
2048
+ }
2049
+ // A speculative expression probe must not borrow a > from a later statement
2050
+ // or conditional branch. Colons in conditional types still belong to the list.
2051
+ if (expressionProbe &&
2052
+ (closers[closers.length - 1] === '>' || (rootClose === '}' && closers.length === 1))) {
2053
+ const level = conditions.length - 1;
2054
+ if (ch === '?')
2055
+ conditions[level] = (conditions[level] ?? 0) + 1;
2056
+ else if (ch === ':' && (conditions[level] ?? 0) > 0)
2057
+ conditions[level] = (conditions[level] ?? 0) - 1;
2058
+ else if (ch === ';' ||
2059
+ ch === ':' ||
2060
+ (ch === '=' && source[i + 1] !== '>') ||
2061
+ ([')', ']', '}'].includes(ch) && ch !== closers[closers.length - 1])) {
2062
+ throw new ModuleSpecifierError('Invalid type argument boundary', 'invalid-source', i);
2063
+ }
2064
+ }
2065
+ if (expressionProbe && ch === '/') {
2066
+ throw new ModuleSpecifierError('Runtime slash in type argument probe', 'invalid-source', i);
2067
+ }
2068
+ if (ch === "'" || ch === '"') {
2069
+ const callee = tokens[tokens.length - 2];
2070
+ if (expressionProbe &&
2071
+ isPunct(tokens[tokens.length - 1], '(') &&
2072
+ callee?.kind === 'identifier' &&
2073
+ callee.value !== 'import' &&
2074
+ !TYPE_OPERAND_KEYWORDS.has(callee.value)) {
2075
+ throw new ModuleSpecifierError('Literal method parameter in type argument probe', 'invalid-source', i);
2076
+ }
2077
+ const { value, next } = readString(source, i);
2078
+ tokens.push({
2079
+ kind: 'string',
2080
+ value,
2081
+ offset: i,
2082
+ endOffset: next,
2083
+ lineBreakBefore: trivia.lineBreak,
2084
+ });
2085
+ i = next;
2086
+ continue;
2087
+ }
2088
+ if (ch === '`') {
2089
+ if (expressionProbe) {
2090
+ const end = skipTemplateType(source, i, n, bounds);
2091
+ tokens.push({
2092
+ kind: 'template',
2093
+ offset: i,
2094
+ endOffset: end,
2095
+ lineBreakBefore: trivia.lineBreak,
2096
+ });
2097
+ i = end;
2098
+ continue;
2099
+ }
2100
+ throw new ModuleSpecifierError('Template literal types in JSX type arguments are not supported', 'invalid-source', i);
2101
+ }
2102
+ if (isIdentifierStartAt(source, i)) {
2103
+ const start = i;
2104
+ const identifier = readIdentifier(source, i);
2105
+ const next = skipTrivia(source, identifier.end, n);
2106
+ if (expressionProbe &&
2107
+ source[next] === '(' &&
2108
+ identifier.value !== 'import' &&
2109
+ !TYPE_OPERAND_KEYWORDS.has(identifier.value) &&
2110
+ !(closers[closers.length - 1] === '}' &&
2111
+ closers.length > 1 &&
2112
+ (memberDeclarationHead(tokens, tokens.length, false) !== undefined ||
2113
+ typeMemberFollowsLineBreak(tokens, trivia.lineBreak)))) {
2114
+ throw new ModuleSpecifierError('Runtime call in type argument probe', 'invalid-source', i);
2115
+ }
2116
+ i = identifier.end;
2117
+ tokens.push({
2118
+ kind: 'identifier',
2119
+ value: identifier.value,
2120
+ offset: start,
2121
+ endOffset: i,
2122
+ lineBreakBefore: trivia.lineBreak,
2123
+ });
2124
+ continue;
2125
+ }
2126
+ if (expressionProbe && ch === '{' && isPunct(tokens[tokens.length - 1], ')')) {
2127
+ throw new ModuleSpecifierError('Runtime body in type argument probe', 'invalid-source', i);
2128
+ }
2129
+ if (ch === '<' || ch === '(' || ch === '{' || ch === '[') {
2130
+ closers.push(ch === '<' ? '>' : ch === '(' ? ')' : ch === '{' ? '}' : ']');
2131
+ opens.push(i);
2132
+ conditions.push(0);
2133
+ }
2134
+ else if (ch === closers[closers.length - 1] && !(ch === '>' && source[i - 1] === '=')) {
2135
+ closers.pop();
2136
+ const opened = opens.pop();
2137
+ conditions.pop();
2138
+ if (expressionProbe && ch === '>' && opened !== undefined)
2139
+ bounds?.set(opened, i + 1);
2140
+ if (closers.length === 0) {
2141
+ return i + 1;
2142
+ }
2143
+ }
2144
+ tokens.push({
2145
+ kind: 'punct',
2146
+ value: ch,
2147
+ offset: i,
2148
+ endOffset: i + 1,
2149
+ lineBreakBefore: trivia.lineBreak,
2150
+ ...(ch === '=' ? classifyEquals(source, i, true) : {}),
2151
+ });
2152
+ i++;
2153
+ }
2154
+ throw new ModuleSpecifierError('Unterminated JSX type arguments', 'invalid-source', open);
2155
+ }
2156
+ catch (error) {
2157
+ if (expressionProbe && bounds !== undefined) {
2158
+ // One failed walk classifies every still-open angle encountered along the way.
2159
+ for (let level = 0; level < closers.length; level++) {
2160
+ const opened = opens[level];
2161
+ if (closers[level] === '>' && opened !== undefined)
2162
+ bounds.set(opened, null);
2163
+ }
2164
+ }
2165
+ throw error;
2166
+ }
2167
+ }
2168
+ /**
2169
+ * Whether the source between two offsets contains a line terminator.
2170
+ */
2171
+ function hasLineTerminator(source, from, to) {
2172
+ for (let k = from; k < to; k++) {
2173
+ if (isLineTerminator(source[k])) {
2174
+ return true;
2175
+ }
2176
+ }
2177
+ return false;
2178
+ }
2179
+ /** Skip whitespace and comments without interpreting any target-project code. */
2180
+ function skipTrivia(source, start, n) {
2181
+ return readTrivia(source, start, n).end;
2182
+ }
2183
+ function readTrivia(source, start, n) {
2184
+ let i = start;
2185
+ let lineBreak = false;
2186
+ while (i < n) {
2187
+ if (isWhitespace(source[i])) {
2188
+ lineBreak ||= isLineTerminator(source[i]);
2189
+ i++;
2190
+ continue;
2191
+ }
2192
+ if (source[i] === '/' && source[i + 1] === '/') {
2193
+ i += 2;
2194
+ while (i < n && !isLineTerminator(source[i])) {
2195
+ i++;
2196
+ }
2197
+ continue;
2198
+ }
2199
+ if (source[i] === '/' && source[i + 1] === '*') {
2200
+ const start = i;
2201
+ i = skipBlockComment(source, i);
2202
+ lineBreak ||= hasLineTerminator(source, start, i);
2203
+ continue;
2204
+ }
2205
+ break;
2206
+ }
2207
+ return { end: i, lineBreak };
2208
+ }
2209
+ /**
2210
+ * Type keywords that still expect a type after them, so a `{` following one opens an object type and
2211
+ * not a function body (`T extends { ok: boolean } ? A : B`).
2212
+ */
2213
+ const TYPE_OPERAND_KEYWORDS = new Set([
2214
+ 'abstract',
2215
+ 'as',
2216
+ 'asserts',
2217
+ 'await',
2218
+ 'const',
2219
+ 'extends',
2220
+ 'in',
2221
+ 'infer',
2222
+ 'is',
2223
+ 'keyof',
2224
+ 'new',
2225
+ 'readonly',
2226
+ 'satisfies',
2227
+ 'typeof',
2228
+ ]);
2229
+ /**
2230
+ * Whether a root-level `=>` inside a return annotation is an arrow of the return type itself rather
2231
+ * than the signature's runtime arrow. A function type keeps a second arrow at the same nesting level
2232
+ * (`(): (x: number) => void => {}`); once the return type is over, the next significant token is the
2233
+ * body (`{`), a statement terminator or a delimiter that closes the surrounding code.
2234
+ */
2235
+ function functionTypeArrowContinues(source, arrowEnd, n, conditionalEnd = false) {
2236
+ let depth = 0;
2237
+ for (let i = arrowEnd + 1; i < n; i++) {
2238
+ const ch = source[i];
2239
+ if (ch === '(' || ch === '[' || ch === '<') {
2240
+ depth++;
2241
+ }
2242
+ else if (ch === '>' && depth > 0) {
2243
+ depth--;
2244
+ }
2245
+ else if (ch === ')' || ch === ']') {
2246
+ if (depth === 0) {
2247
+ return false;
2248
+ }
2249
+ depth--;
2250
+ }
2251
+ else if (depth === 0) {
2252
+ if (ch === '{' || ch === ';' || ch === ',' || (conditionalEnd && ch === ':')) {
2253
+ return false;
2254
+ }
2255
+ if (ch === '=' && source[i + 1] === '>') {
2256
+ return true;
2257
+ }
2258
+ }
2259
+ }
2260
+ return false;
2261
+ }
2262
+ /** Preserve type-argument identity while inspecting a parameter binding pattern. */
2263
+ function markParameterTypeArguments(tokens, openIndex) {
2264
+ if (!isPunct(tokens[openIndex - 1], '>')) {
2265
+ return;
2266
+ }
2267
+ const start = signatureNameIndex(tokens, openIndex) + 1;
2268
+ if (!isPunct(tokens[start], '<')) {
2269
+ return;
2270
+ }
2271
+ // A real JS destructuring entry between comparison operators needs a default or property
2272
+ // separator. Do not mistake that binding for a generic argument. Nested type members may
2273
+ // contain their own separators inside balanced (), [] and {}.
2274
+ let depth = 0;
2275
+ for (let index = start + 1; index < openIndex - 1; index++) {
2276
+ const token = tokens[index];
2277
+ if (token?.kind !== 'punct') {
2278
+ continue;
2279
+ }
2280
+ if (['(', '[', '{'].includes(token.value)) {
2281
+ depth++;
2282
+ }
2283
+ else if ([')', ']', '}'].includes(token.value)) {
2284
+ depth--;
2285
+ }
2286
+ else if (depth === 0 &&
2287
+ (token.operator === 'assignment' || token.value === ':' || token.value === ';')) {
2288
+ return;
2289
+ }
2290
+ }
2291
+ for (let index = start; index < openIndex; index++) {
2292
+ const token = tokens[index];
2293
+ if (token !== undefined) {
2294
+ tokens[index] = { ...token, parameterType: true };
2295
+ }
2296
+ }
2297
+ return start;
2298
+ }
2299
+ /**
2300
+ * Index of the token that names a `(` parameter list. A generic signature keeps its type-parameter
2301
+ * list between the name and the `(` (`async function f<T>(): T {}`, `async <T,>(value: T) => {}`,
2302
+ * `async check<T>(): T {}`), so a balanced `<...>` directly before the `(` is skipped. The index of
2303
+ * the token directly before the `(` is returned unchanged when no such list precedes it.
2304
+ */
2305
+ function signatureNameIndex(tokens, openIndex) {
2306
+ let index = openIndex - 1;
2307
+ const angle = tokens[index];
2308
+ if (angle?.kind !== 'punct' || angle.value !== '>') {
2309
+ return index;
2310
+ }
2311
+ const closers = [];
2312
+ while (index >= 0) {
2313
+ const token = tokens[index];
2314
+ if (token === undefined) {
2315
+ break;
2316
+ }
2317
+ if (token.kind !== 'punct') {
2318
+ index--;
2319
+ continue;
2320
+ }
2321
+ if (token.value === '>' && isArrowHead(tokens[index - 1], token.offset)) {
2322
+ index -= 2;
2323
+ continue;
2324
+ }
2325
+ if (['>', ')', ']', '}'].includes(token.value)) {
2326
+ closers.push(token.value);
2327
+ }
2328
+ else if (['<', '(', '[', '{'].includes(token.value)) {
2329
+ const expected = token.value === '<' ? '>' : token.value === '(' ? ')' : token.value === '[' ? ']' : '}';
2330
+ if (closers.pop() !== expected) {
2331
+ break;
2332
+ }
2333
+ if (closers.length === 0) {
2334
+ return index - 1;
2335
+ }
2336
+ }
2337
+ else if (token.value === ';' &&
2338
+ token.syntheticBoundary !== true &&
2339
+ closers[closers.length - 1] === '>') {
2340
+ break;
2341
+ }
2342
+ index--;
2343
+ }
2344
+ // The `>` does not close a balanced type-parameter list, so the token before `(` names the list.
2345
+ return openIndex - 1;
2346
+ }
2347
+ /**
2348
+ * Modifiers that may sit between a member separator and a method name.
2349
+ *
2350
+ * `new`, `typeof`, `void` and `await` are absent on purpose: they are operators, so an identifier
2351
+ * after them belongs to an expression and never starts a member declaration.
2352
+ */
2353
+ const MEMBER_MODIFIERS = new Set([
2354
+ 'abstract',
2355
+ 'async',
2356
+ 'get',
2357
+ 'override',
2358
+ 'private',
2359
+ 'protected',
2360
+ 'public',
2361
+ 'set',
2362
+ 'static',
2363
+ ]);
2364
+ /** Punctuation that can start a member declaration inside an object literal, class body or interface. */
2365
+ const MEMBER_SEPARATORS = new Set(['{', ',', ';']);
2366
+ /**
2367
+ * Index of the `@` that starts the decorator ending at `index`, or -1 when the tokens ending there
2368
+ * are not a decorator. Only the tail of a decorator is known from the member side, so the
2369
+ * LeftHandSideExpression of `@dec`, `@dec(1)`, `@a.b` and `@(dec)` is scanned backwards.
2370
+ */
2371
+ function decoratorStartIndex(tokens, index) {
2372
+ let depth = 0;
2373
+ for (let k = index; k >= 0; k--) {
2374
+ const token = tokens[k];
2375
+ if (token === undefined) {
2376
+ return -1;
2377
+ }
2378
+ if (token.kind !== 'punct') {
2379
+ continue;
2380
+ }
2381
+ if (token.value === ')' || token.value === ']' || token.value === '}') {
2382
+ depth++;
2383
+ }
2384
+ else if (token.value === '(' || token.value === '[' || token.value === '{') {
2385
+ // An unmatched opener means the scan left the decorator expression.
2386
+ if (depth === 0) {
2387
+ return -1;
2388
+ }
2389
+ depth--;
2390
+ }
2391
+ else if (depth === 0) {
2392
+ if (token.value === '@') {
2393
+ return k;
2394
+ }
2395
+ // Only property access (`@a.b`, `@a?.b`) continues a decorator name.
2396
+ if (token.value !== '.' && token.value !== '?') {
2397
+ return -1;
2398
+ }
2399
+ }
2400
+ }
2401
+ return -1;
2402
+ }
2403
+ /**
2404
+ * The async/generator flags of the member declaration whose name is the token at `nameIndex`, or
2405
+ * `undefined` when no member declaration starts there.
2406
+ *
2407
+ * Class headers are bounded by the current member's forward-tracked start. Objects use explicit
2408
+ * separators, never class-field ASI. Only modifiers and decorators may precede the name.
2409
+ */
2410
+ function memberDeclarationHead(tokens, nameIndex, insideClassBody, memberStart) {
2411
+ // A computed member (`[key]() {}`) is named by the `]` that closes its key.
2412
+ let index = nameIndex - 1;
2413
+ if (isPunct(tokens[nameIndex], ']')) {
2414
+ const open = matchingOpenBracket(tokens, nameIndex);
2415
+ if (open < 0) {
2416
+ return undefined;
2417
+ }
2418
+ index = open - 1;
2419
+ }
2420
+ let async = false;
2421
+ let generator = false;
2422
+ for (;;) {
2423
+ while (index >= (memberStart ?? 0)) {
2424
+ const token = tokens[index];
2425
+ if (isPunct(token, '*') || isPunct(token, '#')) {
2426
+ generator ||= isPunct(token, '*');
2427
+ index--;
2428
+ continue;
2429
+ }
2430
+ if (token?.kind === 'identifier') {
2431
+ const nextToken = tokens[index + 1];
2432
+ const startsMethodModifier = nextToken !== undefined && nextToken.lineBreakBefore !== true;
2433
+ if (token.value === 'async' && startsMethodModifier) {
2434
+ async = true;
2435
+ index--;
2436
+ continue;
2437
+ }
2438
+ if (MEMBER_MODIFIERS.has(token.value) && startsMethodModifier) {
2439
+ index--;
2440
+ continue;
2441
+ }
2442
+ }
2443
+ break;
2444
+ }
2445
+ if (insideClassBody && memberStart !== undefined && index < memberStart) {
2446
+ return { async, generator };
2447
+ }
2448
+ const start = tokens[index];
2449
+ if (start?.kind === 'punct' &&
2450
+ (MEMBER_SEPARATORS.has(start.value) ||
2451
+ (start.value === '}' && start.memberSeparator === true))) {
2452
+ return { async, generator };
2453
+ }
2454
+ // Decorators precede the modifiers they apply to, and each one ends just before them.
2455
+ const decorator = decoratorStartIndex(tokens, index);
2456
+ if (decorator < 0) {
2457
+ return undefined;
2458
+ }
2459
+ index = decorator - 1;
2460
+ }
2461
+ }
2462
+ function isPunct(token, value) {
2463
+ return token?.kind === 'punct' && token.value === value;
2464
+ }
2465
+ function classifyEquals(source, offset, atTypeBoundary = false) {
2466
+ const next = source[offset + 1];
2467
+ const previous = source[offset - 1];
2468
+ if (next === '>') {
2469
+ return { operator: 'arrow', operatorEndOffset: offset + 2 };
2470
+ }
2471
+ if (next === '=' ||
2472
+ (previous !== undefined &&
2473
+ '=!<>+-*/%&|^?'.includes(previous) &&
2474
+ !(atTypeBoundary && previous === '>'))) {
2475
+ let end = offset + 1;
2476
+ while (source[end] === '=') {
2477
+ end++;
2478
+ }
2479
+ return { operator: 'other', operatorEndOffset: end };
2480
+ }
2481
+ return { operator: 'assignment', operatorEndOffset: offset + 1 };
2482
+ }
2483
+ function readIdentifier(source, start) {
2484
+ let end = start;
2485
+ let value = '';
2486
+ let first = true;
2487
+ while (end < source.length) {
2488
+ const escaped = isUnicodeEscapeStart(source, end);
2489
+ const segment = escaped ? readUnicodeEscape(source, end) : readCodePoint(source, end);
2490
+ if (segment === undefined) {
2491
+ break;
2492
+ }
2493
+ const allowed = first
2494
+ ? isIdentifierStartCharacter(segment.character)
2495
+ : isIdentifierPartCharacter(segment.character);
2496
+ if (!allowed) {
2497
+ if (escaped) {
2498
+ throw new ModuleSpecifierError('Invalid Unicode escape in identifier', 'invalid-source', end);
2499
+ }
2500
+ break;
2501
+ }
2502
+ value += segment.character;
2503
+ end = segment.end;
2504
+ first = false;
2505
+ }
2506
+ if (first) {
2507
+ throw new ModuleSpecifierError('Invalid identifier', 'invalid-source', start);
2508
+ }
2509
+ return { value, end };
2510
+ }
2511
+ function isArrowHead(token, greaterOffset) {
2512
+ return (token?.kind === 'punct' &&
2513
+ token.operator === 'arrow' &&
2514
+ (greaterOffset === undefined || token.operatorEndOffset === greaterOffset + 1));
2515
+ }
2516
+ function lineBreakBetween(tokens, start, end) {
2517
+ for (let index = start + 1; index <= end; index++) {
2518
+ if (tokens[index]?.lineBreakBefore === true) {
2519
+ return true;
2520
+ }
2521
+ }
2522
+ return false;
2523
+ }
2524
+ function isMemberExpressionEnd(token) {
2525
+ if (token.assertionEnd === true) {
2526
+ return true;
2527
+ }
2528
+ if (token.kind === 'identifier') {
2529
+ return (token.propertyName === true ||
2530
+ (!REGEX_PRECEDING_KEYWORDS.has(token.value) && !['await', 'yield'].includes(token.value)));
2531
+ }
2532
+ return (EXPRESSION_ENDING.has(token.kind) ||
2533
+ isPunct(token, ')') ||
2534
+ isPunct(token, ']') ||
2535
+ (token.kind === 'punct' && token.expressionEnd === true));
2536
+ }
2537
+ function advanceMember(member, token, previous) {
2538
+ if (member.phase === 'initializer' || member.phase === 'parameters') {
2539
+ return;
2540
+ }
2541
+ if (isPunct(token, '@')) {
2542
+ member.phase = 'decorator';
2543
+ return;
2544
+ }
2545
+ if (member.phase === 'decorator') {
2546
+ if (token.lineBreakBefore !== true &&
2547
+ !isPunct(previous, ')') &&
2548
+ !(previous?.kind === 'identifier' && token.kind === 'identifier')) {
2549
+ return;
2550
+ }
2551
+ member.phase = 'head';
2552
+ }
2553
+ if (token.kind === 'punct' && token.operator === 'assignment') {
2554
+ member.phase = 'initializer';
2555
+ }
2556
+ else if (isPunct(token, ':')) {
2557
+ member.phase = 'type';
2558
+ }
2559
+ else if (isPunct(token, '(') && member.phase !== 'type') {
2560
+ member.phase = 'parameters';
2561
+ }
2562
+ else if (member.phase === 'head' &&
2563
+ ((token.kind === 'identifier' &&
2564
+ !MEMBER_MODIFIERS.has(token.value) &&
2565
+ !['declare', 'readonly'].includes(token.value)) ||
2566
+ token.kind === 'string' ||
2567
+ token.kind === 'number' ||
2568
+ isPunct(token, '['))) {
2569
+ member.phase = 'field';
2570
+ }
2571
+ }
2572
+ function classFieldCanEnd(member, previous, next, nextIdentifier) {
2573
+ if (previous === undefined || member.phase === 'parameters' || member.phase === 'decorator') {
2574
+ return false;
2575
+ }
2576
+ const canEnd = isMemberExpressionEnd(previous) ||
2577
+ ((member.phase === 'field' || member.phase === 'head') &&
2578
+ (previous.kind === 'identifier' || isPunct(previous, '?') || isPunct(previous, '!'))) ||
2579
+ (member.phase === 'type' &&
2580
+ ((previous.kind === 'identifier' && !TYPE_OPERAND_KEYWORDS.has(previous.value)) ||
2581
+ isPunct(previous, '>') ||
2582
+ isPunct(previous, '}')));
2583
+ if (!canEnd) {
2584
+ return false;
2585
+ }
2586
+ if (member.phase === 'initializer') {
2587
+ return !expressionContinues(next, nextIdentifier);
2588
+ }
2589
+ // An uninitialized field cannot continue by multiplication. Type operators and suffixes can.
2590
+ return !UNINITIALIZED_FIELD_CONTINUATIONS.has(next);
2591
+ }
2592
+ const UNINITIALIZED_FIELD_CONTINUATIONS = new Set([
2593
+ '(',
2594
+ '[',
2595
+ '.',
2596
+ '?',
2597
+ '!',
2598
+ '=',
2599
+ ':',
2600
+ '<',
2601
+ '>',
2602
+ '|',
2603
+ '&',
2604
+ ',',
2605
+ ]);
2606
+ function atTypeRoot(type) {
2607
+ return (type.parenDepth === 0 &&
2608
+ type.bracketDepth === 0 &&
2609
+ type.braceDepth === 0 &&
2610
+ type.angleDepth === 0);
2611
+ }
2612
+ function sameTypeDepth(left, right) {
2613
+ return (left.parenDepth === right.parenDepth &&
2614
+ left.bracketDepth === right.bracketDepth &&
2615
+ left.braceDepth === right.braceDepth &&
2616
+ left.angleDepth === right.angleDepth);
2617
+ }
2618
+ /**
2619
+ * Infer an arrow signature using the index of `=` in `=>`, regardless of whether `>` is emitted.
2620
+ */
2621
+ function inferArrowContext(tokens, arrowIndex) {
2622
+ const arrow = tokens[arrowIndex];
2623
+ if (!isArrowHead(arrow)) {
2624
+ return undefined;
2625
+ }
2626
+ const beforeArrow = arrowIndex - 1;
2627
+ const parameters = tokens[beforeArrow];
2628
+ let parametersIndex;
2629
+ if (parameters?.kind === 'punct' && parameters.value === ')') {
2630
+ parametersIndex = matchingOpenParen(tokens, beforeArrow);
2631
+ }
2632
+ else if (parameters?.kind === 'identifier') {
2633
+ parametersIndex = beforeArrow;
2634
+ }
2635
+ else {
2636
+ return undefined;
2637
+ }
2638
+ const parametersStart = parametersIndex >= 0 ? tokens[parametersIndex] : undefined;
2639
+ if (parametersStart?.kind !== 'identifier' && parametersStart?.kind !== 'punct') {
2640
+ return undefined;
2641
+ }
2642
+ // Only trivia before the parameters can break the async modifier; newlines inside `(...)` cannot.
2643
+ // A generic arrow keeps its type-parameter list between `async` and the parameter list.
2644
+ const markerIndex = parametersStart.kind === 'punct' && parametersStart.value === '('
2645
+ ? signatureNameIndex(tokens, parametersIndex)
2646
+ : parametersIndex - 1;
2647
+ const markerToken = tokens[markerIndex];
2648
+ const async = markerToken?.kind === 'identifier' &&
2649
+ markerToken.value === 'async' &&
2650
+ asyncModifierIsContinuous(tokens, markerIndex, parametersIndex);
2651
+ return (parametersStart.signatureContext ?? {
2652
+ async,
2653
+ generator: false,
2654
+ isExpression: false,
2655
+ });
2656
+ }
2657
+ /** Parameter lists can start at expression/statement boundaries, never after a call's name. */
2658
+ function arrowParameterPrefix(token) {
2659
+ if (token === undefined) {
2660
+ return true;
2661
+ }
2662
+ if (token.kind === 'punct') {
2663
+ return (EXPRESSION_BEFORE_BRACE.has(token.value) || [':', '>', ';', '{', '/'].includes(token.value));
2664
+ }
2665
+ return (token.kind === 'identifier' &&
2666
+ ['return', 'throw', 'yield', 'await', 'void', 'typeof', 'delete', 'default'].includes(token.value));
2667
+ }
2668
+ function parameterSignatureContext(tokens, pending, insideMethodContainer, insideClassBody, lineBreakBefore, memberStart) {
2669
+ const openIndex = tokens.length;
2670
+ const nameIndex = signatureNameIndex(tokens, openIndex);
2671
+ const name = tokens[nameIndex];
2672
+ const head = insideMethodContainer && isMethodName(name)
2673
+ ? memberDeclarationHead(tokens, nameIndex, insideClassBody, memberStart)
2674
+ : undefined;
2675
+ if (head !== undefined) {
2676
+ return {
2677
+ context: {
2678
+ ...head,
2679
+ isExpression: false,
2680
+ isClassMember: insideClassBody,
2681
+ },
2682
+ isFunctionKeyword: false,
2683
+ isArrowSignature: false,
2684
+ };
2685
+ }
2686
+ if (pending?.phase === 'awaiting-parameters' &&
2687
+ (name?.kind === 'identifier' || isPunct(name, '*'))) {
2688
+ return { context: pending, isFunctionKeyword: true, isArrowSignature: false };
2689
+ }
2690
+ const marker = tokens[nameIndex - 1];
2691
+ // A generic signature has no name of its own before `(` when it is anonymous (`async <T,>(v: T) => {}`).
2692
+ const startsArrowParameters = arrowParameterPrefix(name) || (isIdentifier(name, 'async') && arrowParameterPrefix(marker));
2693
+ if (startsArrowParameters) {
2694
+ const asyncToken = name?.kind === 'identifier' && name.value === 'async' ? name : undefined;
2695
+ const async = asyncToken !== undefined &&
2696
+ !lineBreakBefore &&
2697
+ asyncModifierIsContinuous(tokens, nameIndex, openIndex - 1);
2698
+ return {
2699
+ context: { async, generator: false, isExpression: false },
2700
+ isFunctionKeyword: false,
2701
+ isArrowSignature: true,
2702
+ };
2703
+ }
2704
+ // Without a `function` keyword the parameter list must start a real member declaration, so the
2705
+ // `:` of a conditional expression or of an unrelated expression never claims a return annotation.
2706
+ return undefined;
2707
+ }
2708
+ function asyncModifierIsContinuous(tokens, modifierIndex, parameterIndex) {
2709
+ const genericOpen = isPunct(tokens[modifierIndex + 1], '<') ? modifierIndex + 1 : undefined;
2710
+ return !lineBreakBetween(tokens, modifierIndex, genericOpen ?? parameterIndex);
2711
+ }
2712
+ function nearestFunctionParameter(parameters) {
2713
+ for (let index = parameters.length - 1; index >= 0; index--) {
2714
+ const parameter = parameters[index];
2715
+ if (parameter?.signature !== undefined) {
2716
+ return parameter;
2717
+ }
2718
+ }
2719
+ return undefined;
2720
+ }
2721
+ function nearestClassField(braces) {
2722
+ for (let index = braces.length - 1; index >= 0; index--) {
2723
+ const brace = braces[index];
2724
+ if (brace?.role === 'class' && brace.state.member?.phase === 'initializer') {
2725
+ return brace.state.member;
2726
+ }
2727
+ }
2728
+ return undefined;
2729
+ }
2730
+ function consumeForIterationSeparator(tokens, parameters, depth) {
2731
+ const control = parameters[parameters.length - 1];
2732
+ if (control?.forPhase !== 'binding' ||
2733
+ control.depth.parenDepth !== depth.parenDepth ||
2734
+ control.depth.bracketDepth !== depth.bracketDepth ||
2735
+ control.depth.braceDepth !== depth.braceDepth ||
2736
+ control.depth.frameDepth !== depth.frameDepth ||
2737
+ tokens.length <= control.openIndex) {
2738
+ return false;
2739
+ }
2740
+ const previous = tokens[tokens.length - 1];
2741
+ const isSeparator = previous !== undefined &&
2742
+ isMemberExpressionEnd(previous) &&
2743
+ !isIdentifier(previous, 'const') &&
2744
+ !isIdentifier(previous, 'let') &&
2745
+ !isIdentifier(previous, 'var');
2746
+ if (isSeparator) {
2747
+ control.forPhase = 'iteration';
2748
+ }
2749
+ return isSeparator;
2750
+ }
2751
+ // At the owner's root, a finished operand can still have a suffix or compose with another
2752
+ // type. Only a runtime boundary retires the assertion; internal closers never do so themselves.
2753
+ function assertionTypeContinues(annotation, next, identifier, lineBreak, after) {
2754
+ if (next === '-' && annotation.typeOperandExpected && after !== '-') {
2755
+ return true;
2756
+ }
2757
+ if (identifier !== undefined) {
2758
+ return annotation.typeOperandExpected || (identifier === 'extends' && !lineBreak);
2759
+ }
2760
+ if (next === '?') {
2761
+ return annotation.conditionalOperand;
2762
+ }
2763
+ if (next === ':') {
2764
+ return annotation.conditionalDepth > 0;
2765
+ }
2766
+ if (next === '(') {
2767
+ return annotation.typeOperandExpected || annotation.importCallPending;
2768
+ }
2769
+ if (next === '{' || next === '`' || next === "'" || next === '"') {
2770
+ return annotation.typeOperandExpected;
2771
+ }
2772
+ if (next === '<' || next === '[') {
2773
+ return after !== next && after !== '=' && (annotation.typeOperandExpected || !lineBreak);
2774
+ }
2775
+ if (next === '|' || next === '&') {
2776
+ return after !== next && after !== '=';
2777
+ }
2778
+ if (next === '.') {
2779
+ return true;
2780
+ }
2781
+ if (next === '=' && after === '>') {
2782
+ return true;
2783
+ }
2784
+ return (annotation.typeOperandExpected &&
2785
+ !['/', '+', '-', '*', '%', '!', ';', ',', ')', ']', '}', '=', '>'].includes(next));
2786
+ }
2787
+ function canStartTypeAssertion(previous) {
2788
+ if (previous?.assertionEnd === true) {
2789
+ return true;
2790
+ }
2791
+ return (previous !== undefined &&
2792
+ isMemberExpressionEnd(previous) &&
2793
+ !(previous.kind === 'identifier' &&
2794
+ previous.propertyName !== true &&
2795
+ [
2796
+ 'const',
2797
+ 'let',
2798
+ 'var',
2799
+ 'function',
2800
+ 'class',
2801
+ 'type',
2802
+ 'interface',
2803
+ 'namespace',
2804
+ 'import',
2805
+ 'export',
2806
+ 'extends',
2807
+ 'implements',
2808
+ ].includes(previous.value)));
2809
+ }
2810
+ function isMethodName(token) {
2811
+ return (token?.kind === 'identifier' ||
2812
+ token?.kind === 'string' ||
2813
+ token?.kind === 'number' ||
2814
+ isPunct(token, ']'));
2815
+ }
2816
+ function expressionFunctionBodyEnded(ch, parenDepth, bracketDepth, braceDepth, frameDepth, functionDepth, conditionalDepth, context) {
2817
+ if (functionDepth > context.functionDepth ||
2818
+ braceDepth > context.braceDepth ||
2819
+ frameDepth !== context.frameDepth) {
2820
+ return false;
2821
+ }
2822
+ if (ch === ':' &&
2823
+ context.conditionalDepth > 0 &&
2824
+ conditionalDepth === context.conditionalDepth &&
2825
+ parenDepth === context.parenDepth &&
2826
+ bracketDepth === context.bracketDepth &&
2827
+ braceDepth === context.braceDepth) {
2828
+ return true;
2829
+ }
2830
+ if (ch === ';' || ch === ',') {
2831
+ return (parenDepth === context.parenDepth &&
2832
+ bracketDepth === context.bracketDepth &&
2833
+ braceDepth === context.braceDepth);
2834
+ }
2835
+ if (ch === ')' || ch === ']' || ch === '}') {
2836
+ return (parenDepth === context.parenDepth &&
2837
+ bracketDepth === context.bracketDepth &&
2838
+ braceDepth === context.braceDepth);
2839
+ }
2840
+ return false;
2841
+ }
2842
+ function lineTerminatorEndsExpressionFunctionBody(next, prevKind, prevPunct, previous, nextIdentifier, nextIsUpdate, parenDepth, bracketDepth, braceDepth, frameDepth, functionDepth, context) {
2843
+ if (functionDepth > context.functionDepth ||
2844
+ parenDepth !== context.parenDepth ||
2845
+ bracketDepth !== context.bracketDepth ||
2846
+ braceDepth !== context.braceDepth ||
2847
+ frameDepth !== context.frameDepth) {
2848
+ return false;
2849
+ }
2850
+ const previousEndsExpression = previous?.assertionEnd === true ||
2851
+ EXPRESSION_ENDING.has(prevKind) ||
2852
+ (prevKind === 'punct' &&
2853
+ ([')', ']', '}'].includes(prevPunct ?? '') ||
2854
+ (previous?.kind === 'punct' && previous.expressionEnd === true)));
2855
+ return previousEndsExpression && (nextIsUpdate || !expressionContinues(next, nextIdentifier));
2856
+ }
2857
+ function expressionContinues(next, identifier) {
2858
+ return (EXPRESSION_CONTINUATION_CHARACTERS.has(next) ||
2859
+ identifier === 'in' ||
2860
+ identifier === 'instanceof');
2861
+ }
2862
+ /** Whether a token is the given identifier. */
2863
+ function isIdentifier(token, value) {
2864
+ return token?.kind === 'identifier' && token.value === value;
2865
+ }
2866
+ /**
2867
+ * The index of the `[` matching the `]` at `closeIndex`, or `-1` when the source has no match.
2868
+ */
2869
+ function matchingOpenBracket(tokens, closeIndex) {
2870
+ let depth = 0;
2871
+ for (let k = closeIndex; k >= 0; k--) {
2872
+ const token = tokens[k];
2873
+ if (token?.kind !== 'punct') {
168
2874
  continue;
169
2875
  }
170
- // A `}` in code mode closes the innermost open `${...}` expression only when that expression has
171
- // no open braces of its own; otherwise it is an ordinary brace inside the expression.
172
- if (ch === '}') {
173
- const depth = braceDepths[braceDepths.length - 1];
174
- if (depth !== undefined) {
175
- if (depth === 0) {
176
- braceDepths.pop();
177
- i++;
178
- inText = true;
179
- resetExpressionState();
180
- continue;
181
- }
182
- braceDepths[braceDepths.length - 1] = depth - 1;
183
- }
184
- // Otherwise `}` is an ordinary brace: it ends an expression only when it closed an object
185
- // literal. An unmatched `}` (an empty context) keeps the statement-block behaviour.
186
- closedObjectBrace = braceContexts.pop() === true;
2876
+ if (token.value === ']') {
2877
+ depth++;
187
2878
  }
188
- // `/` is a regex when it can start an expression, otherwise it is division.
189
- if (ch === '/') {
190
- if (isRegexStart(prevKind, prevPunct, prevIsExpressionKeyword, afterControlHead, closedObjectBrace)) {
191
- i = skipRegex(source, i);
192
- prevKind = 'regex';
193
- afterControlHead = false;
194
- pendingControlKeyword = undefined;
195
- continue;
2879
+ else if (token.value === '[') {
2880
+ depth--;
2881
+ if (depth === 0) {
2882
+ return k;
196
2883
  }
197
- tokens.push({ kind: 'punct', value: '/', offset: i });
198
- prevKind = 'punct';
199
- prevPunct = '/';
200
- afterControlHead = false;
201
- pendingControlKeyword = undefined;
202
- i++;
203
- continue;
204
- }
205
- // String literals (single or double quoted).
206
- if (ch === "'" || ch === '"') {
207
- const { value, next } = readString(source, i);
208
- tokens.push({ kind: 'string', value, offset: i });
209
- prevKind = 'string';
210
- afterControlHead = false;
211
- pendingControlKeyword = undefined;
212
- i = next;
213
- continue;
214
2884
  }
215
- // Identifiers.
216
- if (isIdentifierStart(ch)) {
217
- const start = i;
218
- i++;
219
- while (i < n) {
220
- const part = source[i];
221
- if (part === undefined || !isIdentifierPart(part)) {
222
- break;
223
- }
224
- i++;
225
- }
226
- const value = source.slice(start, i);
227
- prevIdentifier = value;
228
- // A keyword after `.` is a property name (e.g. `x.return`), not an expression start.
229
- prevIsExpressionKeyword =
230
- REGEX_PRECEDING_KEYWORDS.has(value) && !(prevKind === 'punct' && prevPunct === '.');
231
- // Remember a control-statement keyword until its `(` is seen so only that head's `)` may be
232
- // followed by a regex literal.
233
- pendingControlKeyword = pendingControlKeywordAfter(value,
234
- // After `.` the identifier is a property name (e.g. `obj.if(...)`), never a control head.
235
- prevKind === 'punct' && prevPunct === '.', pendingControlKeyword);
236
- tokens.push({ kind: 'identifier', value, offset: start });
237
- prevKind = 'identifier';
238
- afterControlHead = false;
2885
+ }
2886
+ return -1;
2887
+ }
2888
+ /**
2889
+ * The index of the `(` matching the `)` at `closeIndex`, or `-1` when the source has no match.
2890
+ */
2891
+ function matchingOpenParen(tokens, closeIndex) {
2892
+ const close = tokens[closeIndex];
2893
+ if (close?.kind === 'punct' && close.matchingOpen !== undefined) {
2894
+ return close.matchingOpen;
2895
+ }
2896
+ let depth = 0;
2897
+ for (let k = closeIndex; k >= 0; k--) {
2898
+ const token = tokens[k];
2899
+ if (token?.kind !== 'punct') {
239
2900
  continue;
240
2901
  }
241
- // Numbers end an expression, so they affect the regex/division decision.
242
- if (isNumberStart(ch)) {
243
- const start = i;
244
- i++;
245
- while (i < n) {
246
- const part = source[i];
247
- if (part === undefined || !(isIdentifierPart(part) || part === '.')) {
248
- break;
249
- }
250
- i++;
251
- }
252
- tokens.push({ kind: 'number', offset: start });
253
- prevKind = 'number';
254
- afterControlHead = false;
255
- pendingControlKeyword = undefined;
256
- continue;
2902
+ if (token.value === ')') {
2903
+ depth++;
257
2904
  }
258
- // Any other single character is punctuation.
259
- if (ch === '{') {
260
- // Record what this brace opens, so its `}` knows whether an expression or a statement ended.
261
- // The enclosing entry is inspected before pushing, so a `:` knows if it separates a property.
262
- braceContexts.push(opensObjectLiteral(prevKind, prevPunct, prevIdentifier, prevIsExpressionKeyword, braceDepths.length > 0, braceContexts[braceContexts.length - 1] === true));
263
- // Count the brace inside the innermost open `${...}` expression so its closing `}` is found.
264
- const depth = braceDepths[braceDepths.length - 1];
265
- if (depth !== undefined) {
266
- braceDepths[braceDepths.length - 1] = depth + 1;
2905
+ else if (token.value === '(') {
2906
+ depth--;
2907
+ if (depth === 0) {
2908
+ return k;
267
2909
  }
268
2910
  }
269
- else if (ch === '(') {
270
- // A `(` after a control keyword opens a control head; only its matching `)` may then be
271
- // followed by a regex literal, so the nesting depth of every `(` has to be tracked.
272
- parenDepth++;
273
- if (pendingControlKeyword !== undefined) {
274
- controlParenDepth = parenDepth;
275
- pendingControlKeyword = undefined;
276
- }
277
- afterControlHead = false;
2911
+ }
2912
+ return -1;
2913
+ }
2914
+ /** Claim the most recent class header at this exact depth; nested heritage classes claim first. */
2915
+ function takeClassBodyContext(contexts, depth) {
2916
+ for (let index = contexts.length - 1; index >= 0; index--) {
2917
+ const context = contexts[index];
2918
+ if (context?.parenDepth === depth.parenDepth &&
2919
+ context.bracketDepth === depth.bracketDepth &&
2920
+ context.braceDepth === depth.braceDepth &&
2921
+ context.frameDepth === depth.frameDepth) {
2922
+ contexts.splice(index, 1);
2923
+ return context;
278
2924
  }
279
- else if (ch === ')') {
280
- afterControlHead = controlParenDepth !== undefined && parenDepth === controlParenDepth;
281
- if (afterControlHead) {
282
- controlParenDepth = undefined;
283
- }
284
- if (parenDepth > 0) {
285
- parenDepth--;
286
- }
2925
+ }
2926
+ return undefined;
2927
+ }
2928
+ /**
2929
+ * Read a JSX tag name starting at `i` (the first character after `<`). Member expressions
2930
+ * (`<UI.Panel>`), namespaced names (`<svg:rect>`) and dashed names (`<my-tag>`) are part of the name.
2931
+ */
2932
+ function readJsxTagName(source, i, n) {
2933
+ const start = i;
2934
+ while (i < n) {
2935
+ const ch = source[i];
2936
+ if (ch === undefined ||
2937
+ !(isIdentifierPartAt(source, i) || ch === '.' || ch === '-' || ch === ':')) {
2938
+ break;
287
2939
  }
288
- tokens.push({ kind: 'punct', value: ch, offset: i });
289
- prevKind = 'punct';
290
- prevPunct = ch;
291
- i++;
2940
+ i += codePointLength(source, i);
292
2941
  }
293
- // Reaching the end of the source while inside a template text or an open `${...}` expression means
294
- // the template literal was never closed.
295
- if (inText || braceDepths.length > 0) {
296
- throw new ModuleSpecifierError('Unterminated template literal', 'invalid-source', templateStart);
2942
+ return { name: source.slice(start, i), end: i };
2943
+ }
2944
+ /**
2945
+ * Skip a quoted JSX attribute value starting at its opening quote. The value is inert text: it ends
2946
+ * at the matching quote and backslashes are not escapes, so it can never contribute a specifier.
2947
+ */
2948
+ function skipJsxAttributeValue(source, i, n) {
2949
+ const quote = source[i];
2950
+ let k = i + 1;
2951
+ while (k < n && source[k] !== quote) {
2952
+ k++;
297
2953
  }
298
- return tokens;
2954
+ if (k >= n) {
2955
+ throw new ModuleSpecifierError('Unterminated JSX attribute value', 'invalid-source', i);
2956
+ }
2957
+ return k + 1;
299
2958
  }
300
2959
  /**
301
2960
  * Significant token kinds that clearly terminate an expression. After one of these, `/` is
@@ -308,6 +2967,29 @@ const EXPRESSION_ENDING = new Set([
308
2967
  'template',
309
2968
  'regex',
310
2969
  ]);
2970
+ // A token beginning with one of these characters may continue an expression across a line
2971
+ // terminator, so ASI must not retire an expression-bodied arrow before it.
2972
+ const EXPRESSION_CONTINUATION_CHARACTERS = new Set([
2973
+ '.',
2974
+ '(',
2975
+ '[',
2976
+ '?',
2977
+ ':',
2978
+ ',',
2979
+ '+',
2980
+ '-',
2981
+ '*',
2982
+ '/',
2983
+ '%',
2984
+ '<',
2985
+ '>',
2986
+ '=',
2987
+ '!',
2988
+ '&',
2989
+ '|',
2990
+ '^',
2991
+ '`',
2992
+ ]);
311
2993
  /**
312
2994
  * Expression-starting keywords. After one of these, a `/` begins a regex literal even though the
313
2995
  * keyword itself is tokenized as an identifier. `of`, `yield` and `await` are intentionally
@@ -355,9 +3037,8 @@ function pendingControlKeywordAfter(value, isMemberName, pending) {
355
3037
  * expression on its right: assignment (`= {`), an argument or element list (`f({`, `[{`) and the
356
3038
  * separators and binary/logical operators. `>` is absent because it usually ends an arrow function
357
3039
  * (`() => {}`), whose `{` opens a function body, and `)`/`}` are absent because they close
358
- * statement heads and blocks. `:` is decided separately, in {@link opensObjectLiteral}, because it
359
- * is a property separator inside an object literal but a label or `case`/`default` separator
360
- * otherwise.
3040
+ * statement heads and blocks. `:` is classified separately using property/conditional depth,
3041
+ * because labels and `case`/`default` clauses instead introduce statements.
361
3042
  */
362
3043
  const EXPRESSION_BEFORE_BRACE = new Set([
363
3044
  '=',
@@ -377,26 +3058,41 @@ const EXPRESSION_BEFORE_BRACE = new Set([
377
3058
  ]);
378
3059
  // Keywords that start an expression but are still followed by a statement block.
379
3060
  const BLOCK_AFTER_KEYWORD = new Set(['else', 'do']);
3061
+ // Whether a `function` keyword sits in expression position (a function expression used as a value,
3062
+ // so a slash after its body `{}` divides) rather than statement position (a declaration, so a slash
3063
+ // after its body may start a regex). The previous significant token must expect an expression on
3064
+ // its right: an expression-starting punctuation, an expression keyword, or a regex.
3065
+ function functionIsExpressionPosition(prevKind, prevPunct, colonIsExpression, expressionExpected) {
3066
+ if (expressionExpected) {
3067
+ return true;
3068
+ }
3069
+ if (prevKind === 'punct') {
3070
+ return prevPunct === ':' ? colonIsExpression : EXPRESSION_BEFORE_BRACE.has(prevPunct ?? '');
3071
+ }
3072
+ return prevKind === 'regex';
3073
+ }
380
3074
  /**
381
3075
  * Whether a `{` opens an expression (an object literal or a destructuring pattern) instead of a
382
- * statement block or function body. `enclosingIsObjectLiteral` reports what the enclosing `{`, if
383
- * any, opened. Statement position is the default, so an unknown context keeps the existing
3076
+ * statement block or function body. `colonIsExpression` distinguishes property/conditional colons
3077
+ * from statement separators. Statement position is the default, so an unknown context keeps the existing
384
3078
  * behaviour of allowing a regex literal after the matching `}`.
385
3079
  */
386
- function opensObjectLiteral(prevKind, prevPunct, prevIdentifier, prevIsExpressionKeyword, inTemplateExpression, enclosingIsObjectLiteral) {
3080
+ function opensObjectLiteral(prevKind, prevPunct, prevIdentifier, prevIsExpressionKeyword, inTemplateExpression, colonIsExpression, lineBreakBefore) {
387
3081
  if (prevKind === 'punct') {
388
3082
  // Inside an object literal a `:` separates a property from its value (`{ scale: {} }`, where a
389
3083
  // following `/` divides), while at statement level the same `:` follows a label
390
3084
  // (`label: {}`) or a `case`/`default` clause, whose `{` opens a block.
391
3085
  if (prevPunct === ':') {
392
- return enclosingIsObjectLiteral;
3086
+ return colonIsExpression;
393
3087
  }
394
3088
  return EXPRESSION_BEFORE_BRACE.has(prevPunct ?? '');
395
3089
  }
396
3090
  if (prevKind === 'identifier') {
397
3091
  // `return {` is an expression, `else {`/`do {` are blocks and every other identifier
398
3092
  // (`function f`, `class C`) sits in statement position.
399
- return prevIsExpressionKeyword && !BLOCK_AFTER_KEYWORD.has(prevIdentifier ?? '');
3093
+ return (prevIsExpressionKeyword &&
3094
+ !BLOCK_AFTER_KEYWORD.has(prevIdentifier ?? '') &&
3095
+ !(prevIdentifier === 'return' && lineBreakBefore));
400
3096
  }
401
3097
  // Right after `${` an expression is expected, so a `{` opens an object literal.
402
3098
  return inTemplateExpression && prevKind === 'start';
@@ -409,7 +3105,16 @@ function opensObjectLiteral(prevKind, prevPunct, prevIdentifier, prevIsExpressio
409
3105
  * what it closed: an object literal ends an expression, so the `/` divides, while a statement block
410
3106
  * leaves the expression-start position, where a regex literal may begin.
411
3107
  */
412
- function isRegexStart(prevKind, prevPunct, prevIsExpressionKeyword, afterControlHead, closedObjectBrace) {
3108
+ function isRegexStart(prevKind, prevPunct, prevIsExpressionKeyword, afterControlHead, closedObjectBrace, previous) {
3109
+ if (previous?.assertionEnd === true) {
3110
+ return false;
3111
+ }
3112
+ if (previous?.kind === 'punct' && previous.value !== '}' && previous.expressionEnd === true) {
3113
+ return false;
3114
+ }
3115
+ if (previous?.kind === 'punct' && (previous.value === '++' || previous.value === '--')) {
3116
+ return previous.expressionEnd !== true;
3117
+ }
413
3118
  if (prevKind === 'identifier' && prevIsExpressionKeyword) {
414
3119
  return true;
415
3120
  }
@@ -436,6 +3141,29 @@ function isRegexStart(prevKind, prevPunct, prevIsExpressionKeyword, afterControl
436
3141
  function interpret(tokens) {
437
3142
  const occurrences = [];
438
3143
  const n = tokens.length;
3144
+ let closingDelimiters;
3145
+ const closingDelimiter = (openIndex) => {
3146
+ closingDelimiters ??= indexClosingDelimiters(tokens);
3147
+ return closingDelimiters.get(openIndex);
3148
+ };
3149
+ const resolvedRequireScopes = new Map();
3150
+ const requireIsShadowed = (scope) => {
3151
+ const path = [];
3152
+ let current = scope;
3153
+ while (current !== undefined && !resolvedRequireScopes.has(current)) {
3154
+ path.push(current);
3155
+ current = current.parent;
3156
+ }
3157
+ let shadowed = current === undefined ? false : (resolvedRequireScopes.get(current) ?? false);
3158
+ for (let index = path.length - 1; index >= 0; index--) {
3159
+ const owner = path[index];
3160
+ if (owner !== undefined) {
3161
+ shadowed ||= owner.bindings.some((range) => bindingHasRequire(tokens, range.start, range.end, closingDelimiter));
3162
+ resolvedRequireScopes.set(owner, shadowed);
3163
+ }
3164
+ }
3165
+ return shadowed;
3166
+ };
439
3167
  let i = 0;
440
3168
  while (i < n) {
441
3169
  const token = tokens[i];
@@ -444,7 +3172,7 @@ function interpret(tokens) {
444
3172
  continue;
445
3173
  }
446
3174
  if (token.value === 'import') {
447
- i = handleImport(tokens, i, occurrences);
3175
+ i = handleImport(tokens, i, occurrences, closingDelimiter);
448
3176
  continue;
449
3177
  }
450
3178
  if (token.value === 'export') {
@@ -452,6 +3180,10 @@ function interpret(tokens) {
452
3180
  continue;
453
3181
  }
454
3182
  if (token.value === 'require') {
3183
+ if (token.requireScope !== undefined && requireIsShadowed(token.requireScope)) {
3184
+ i++;
3185
+ continue;
3186
+ }
455
3187
  i = handleRequire(tokens, i, occurrences);
456
3188
  continue;
457
3189
  }
@@ -465,13 +3197,32 @@ function interpret(tokens) {
465
3197
  * - `obj.import(...)` is ignored (a member call, preceded by `.`).
466
3198
  * - `import.meta` is ignored (treated as `import` followed by `.`).
467
3199
  * - `import 'pkg'` and `import ... from 'pkg'` are `import`.
468
- * - `import('pkg')` is `dynamic-import` only when the sole argument is a static string.
3200
+ * - Dynamic import accepts a literal source with optional bounded object-literal options.
3201
+ */
3202
+ /**
3203
+ * Whether the identifier at `i` is a property name (`obj.require`), which is never the global
3204
+ * `require`/`import`. A `.` that belongs to a spread (`...require('pkg')`) is not member access.
469
3205
  */
470
- function handleImport(tokens, i, occurrences) {
3206
+ function isMemberName(tokens, i) {
471
3207
  const prev = tokens[i - 1];
3208
+ if (prev?.kind === 'punct' && prev.value === '#') {
3209
+ return true;
3210
+ }
3211
+ if (prev?.kind !== 'punct' || prev.value !== '.') {
3212
+ return false;
3213
+ }
3214
+ const second = tokens[i - 2];
3215
+ const third = tokens[i - 3];
3216
+ const isSpread = second?.kind === 'punct' &&
3217
+ second.value === '.' &&
3218
+ third?.kind === 'punct' &&
3219
+ third.value === '.';
3220
+ return !isSpread;
3221
+ }
3222
+ function handleImport(tokens, i, occurrences, closingDelimiter) {
472
3223
  const next = tokens[i + 1];
473
3224
  // `obj.import(...)`: a member call, not the import keyword.
474
- if (prev?.kind === 'punct' && prev.value === '.') {
3225
+ if (isMemberName(tokens, i)) {
475
3226
  return i + 1;
476
3227
  }
477
3228
  // `import.meta` / `import .foo`: not a module import.
@@ -483,80 +3234,164 @@ function handleImport(tokens, i, occurrences) {
483
3234
  occurrences.push({ specifier: next.value, kind: 'import' });
484
3235
  return i + 2;
485
3236
  }
486
- // `import('pkg')` / `import ('pkg')`: only a sole string argument closed by `)` is static.
3237
+ // Keep literal specifiers with sole-argument or bounded object-literal options forms.
487
3238
  if (next?.kind === 'punct' && next.value === '(') {
488
- const specifier = soleStringSpecifier(tokens, i + 2);
3239
+ const specifier = dynamicImportSpecifier(tokens, i + 2, closingDelimiter);
489
3240
  if (specifier !== undefined) {
490
3241
  occurrences.push({ specifier, kind: 'dynamic-import' });
491
3242
  return i + 3;
492
3243
  }
493
3244
  return i + 1;
494
3245
  }
495
- // Static `import ... from 'pkg'`: scan forward for `from` followed by a string.
3246
+ // Static `import ... from 'pkg'`: consume only the supported clause.
496
3247
  return scanImportSource(tokens, i, occurrences, 'import');
497
3248
  }
498
3249
  /**
499
3250
  * Handle an `export` identifier. `export ... from 'pkg'` is `export-from`.
500
3251
  */
501
3252
  function handleExport(tokens, i, occurrences) {
3253
+ // Member names share the same boundary rule as import and require.
3254
+ if (isMemberName(tokens, i)) {
3255
+ return i + 1;
3256
+ }
502
3257
  return scanImportSource(tokens, i, occurrences, 'export-from');
503
3258
  }
3259
+ /** Read only the supported clause; unrelated statements cannot supply its source. */
3260
+ function scanImportSource(tokens, i, occurrences, kind) {
3261
+ const sourceIndex = kind === 'export-from' ? exportSourceIndex(tokens, i) : importSourceIndex(tokens, i);
3262
+ const source = sourceIndex === undefined ? undefined : tokens[sourceIndex];
3263
+ if (source?.kind === 'string' && sourceIndex !== undefined) {
3264
+ occurrences.push({ specifier: source.value, kind });
3265
+ return sourceIndex + 1;
3266
+ }
3267
+ return i + 1;
3268
+ }
504
3269
  /**
505
- * Scan forward from `i` looking for the `from` keyword followed by a string, recording it with
506
- * `kind`. Stops early at a nested `import`/`export`/`require` so a following construct is handled on
507
- * its own, except for `import`/`export` used as binding names inside the `{ ... }` clause of the
508
- * current statement (e.g. `import { export as v } from 'pkg'`).
3270
+ * Consume a default binding and/or a namespace/named clause, then require `from` + literal.
3271
+ * Each property lookalike fails locally. Named clauses accept only specifiers, never arbitrary
3272
+ * brace contents, so failed candidates cannot repeatedly search an unrelated suffix.
509
3273
  */
510
- function scanImportSource(tokens, i, occurrences, kind) {
511
- const n = tokens.length;
512
- let j = i + 1;
513
- // How many `{ ... }` named clauses of the current statement are open. `import`/`export` are legal
514
- // binding names inside such a clause (e.g. `import { export as v } from 'pkg'`), so they may only
515
- // start a new construct at depth 0, otherwise the search stops before the real `from` source.
516
- let braceDepth = 0;
517
- while (j < n) {
518
- const token = tokens[j];
519
- if (token?.kind === 'punct') {
520
- if (token.value === '{') {
521
- braceDepth++;
3274
+ function importSourceIndex(tokens, importIndex) {
3275
+ let index = importIndex + 1;
3276
+ if (isIdentifier(tokens[index], 'type') &&
3277
+ (isPunct(tokens[index + 1], '{') ||
3278
+ isPunct(tokens[index + 1], '*') ||
3279
+ (tokens[index + 1]?.kind === 'identifier' &&
3280
+ (!isIdentifier(tokens[index + 1], 'from') || tokens[index + 2]?.kind !== 'string') &&
3281
+ (!isIdentifier(tokens[index + 1], 'as') || isIdentifier(tokens[index + 2], 'from'))))) {
3282
+ index++;
3283
+ }
3284
+ if (tokens[index]?.kind === 'identifier') {
3285
+ index++;
3286
+ // Preserve the historical synthetic default-alias form supported by this scanner.
3287
+ if (isIdentifier(tokens[index], 'as') && tokens[index + 1]?.kind === 'identifier') {
3288
+ index += 2;
3289
+ }
3290
+ if (isPunct(tokens[index], ',')) {
3291
+ index++;
3292
+ }
3293
+ else {
3294
+ return importFromSourceIndex(tokens, index);
3295
+ }
3296
+ }
3297
+ if (isPunct(tokens[index], '*')) {
3298
+ if (!isIdentifier(tokens[index + 1], 'as') || tokens[index + 2]?.kind !== 'identifier') {
3299
+ return undefined;
3300
+ }
3301
+ index += 3;
3302
+ }
3303
+ else if (isPunct(tokens[index], '{')) {
3304
+ index++;
3305
+ while (!isPunct(tokens[index], '}')) {
3306
+ // `type as T` imports the value `type`, but `type as` imports the type `as`.
3307
+ // A second `as` plus an alias distinguishes `type as as Alias` from `type as as`.
3308
+ if (isIdentifier(tokens[index], 'type') &&
3309
+ tokens[index + 1]?.kind === 'identifier' &&
3310
+ (!isIdentifier(tokens[index + 1], 'as') ||
3311
+ isPunct(tokens[index + 2], ',') ||
3312
+ isPunct(tokens[index + 2], '}') ||
3313
+ (isIdentifier(tokens[index + 2], 'as') && tokens[index + 3]?.kind === 'identifier'))) {
3314
+ index++;
522
3315
  }
523
- else if (token.value === '}') {
524
- // Clamped so an unmatched `}` cannot leave a negative depth that hides later boundaries.
525
- braceDepth = braceDepth > 0 ? braceDepth - 1 : 0;
3316
+ const imported = tokens[index];
3317
+ if (imported?.kind !== 'identifier' && imported?.kind !== 'string') {
3318
+ return undefined;
526
3319
  }
527
- }
528
- if (token?.kind === 'identifier') {
529
- const next = tokens[j + 1];
530
- // `import`/`export` only start a new construct outside the named clause. A `require`
531
- // identifier only starts one when it begins a direct call: a binding named `require` (e.g.
532
- // `import { require } from 'pkg'`) must not stop the search for the source that follows
533
- // `from`.
534
- if ((braceDepth === 0 && (token.value === 'import' || token.value === 'export')) ||
535
- (token.value === 'require' && next?.kind === 'punct' && next.value === '(')) {
3320
+ index++;
3321
+ if (isIdentifier(tokens[index], 'as')) {
3322
+ if (tokens[index + 1]?.kind !== 'identifier') {
3323
+ return undefined;
3324
+ }
3325
+ index += 2;
3326
+ }
3327
+ else if (imported.kind === 'string') {
3328
+ return undefined;
3329
+ }
3330
+ if (!isPunct(tokens[index], ',')) {
536
3331
  break;
537
3332
  }
538
- if (token.value === 'from' && next?.kind === 'string') {
539
- occurrences.push({ specifier: next.value, kind });
540
- return j + 2;
3333
+ index++;
3334
+ }
3335
+ if (!isPunct(tokens[index], '}')) {
3336
+ return undefined;
3337
+ }
3338
+ index++;
3339
+ }
3340
+ else {
3341
+ return undefined;
3342
+ }
3343
+ return importFromSourceIndex(tokens, index);
3344
+ }
3345
+ function importFromSourceIndex(tokens, index) {
3346
+ return isIdentifier(tokens[index], 'from') && tokens[index + 1]?.kind === 'string'
3347
+ ? index + 1
3348
+ : undefined;
3349
+ }
3350
+ function exportSourceIndex(tokens, exportIndex) {
3351
+ let index = exportIndex + 1;
3352
+ if (isIdentifier(tokens[index], 'type')) {
3353
+ index++;
3354
+ }
3355
+ if (isPunct(tokens[index], '*')) {
3356
+ index++;
3357
+ if (isIdentifier(tokens[index], 'as')) {
3358
+ index += 2;
3359
+ }
3360
+ }
3361
+ else if (isPunct(tokens[index], '{')) {
3362
+ let depth = 1;
3363
+ index++;
3364
+ while (index < tokens.length && depth > 0) {
3365
+ if (isPunct(tokens[index], '{')) {
3366
+ depth++;
3367
+ }
3368
+ else if (isPunct(tokens[index], '}')) {
3369
+ depth--;
541
3370
  }
3371
+ index++;
3372
+ }
3373
+ if (depth !== 0) {
3374
+ return undefined;
542
3375
  }
543
- j++;
544
3376
  }
545
- // No source found; keep scanning from the next token.
546
- return i + 1;
3377
+ else {
3378
+ return undefined;
3379
+ }
3380
+ return isIdentifier(tokens[index], 'from') && tokens[index + 1]?.kind === 'string'
3381
+ ? index + 1
3382
+ : undefined;
547
3383
  }
548
3384
  /**
549
3385
  * Handle a `require` identifier. `obj.require(...)` is ignored (preceded by `.`).
550
3386
  * `require('pkg')` is `require` only when the sole argument is a static string.
551
3387
  */
552
3388
  function handleRequire(tokens, i, occurrences) {
553
- const prev = tokens[i - 1];
554
3389
  // `obj.require(...)`: not the global CommonJS require.
555
- if (prev?.kind === 'punct' && prev.value === '.') {
3390
+ if (isMemberName(tokens, i)) {
556
3391
  return i + 1;
557
3392
  }
558
3393
  const next = tokens[i + 1];
559
- // `require('pkg')`: only a sole string argument closed by `)` is static.
3394
+ // A sole string argument, with an optional trailing comma, is static; options are not supported.
560
3395
  if (next?.kind === 'punct' && next.value === '(') {
561
3396
  const specifier = soleStringSpecifier(tokens, i + 2);
562
3397
  if (specifier !== undefined) {
@@ -566,15 +3401,160 @@ function handleRequire(tokens, i, occurrences) {
566
3401
  }
567
3402
  return i + 1;
568
3403
  }
3404
+ /** Inspect binding targets only, skipping property keys, types and initializer expressions. */
3405
+ function bindingHasRequire(tokens, start, end, closingDelimiter) {
3406
+ const targets = [{ start, end }];
3407
+ while (targets.length > 0) {
3408
+ const target = targets.pop();
3409
+ if (target === undefined) {
3410
+ break;
3411
+ }
3412
+ let index = bindingAfterRest(tokens, target.start);
3413
+ // Constructor parameter properties prefix the binding, rather than naming it.
3414
+ let modifier = tokens[index];
3415
+ while (index + 1 < target.end &&
3416
+ modifier?.kind === 'identifier' &&
3417
+ ['public', 'private', 'protected', 'readonly', 'override'].includes(modifier.value)) {
3418
+ index++;
3419
+ modifier = tokens[index];
3420
+ }
3421
+ const token = tokens[index];
3422
+ if (token?.kind === 'identifier') {
3423
+ const next = tokens[index + 1];
3424
+ if (token.value === 'require' &&
3425
+ (index + 1 >= target.end ||
3426
+ isPunct(next, ',') ||
3427
+ isPunct(next, '?') ||
3428
+ (next?.kind === 'punct' && next.operator === 'assignment'))) {
3429
+ return true;
3430
+ }
3431
+ continue;
3432
+ }
3433
+ const object = isPunct(token, '{');
3434
+ if (!object && !isPunct(token, '[')) {
3435
+ continue;
3436
+ }
3437
+ const close = closingDelimiter(index);
3438
+ if (close === undefined || close >= target.end) {
3439
+ continue;
3440
+ }
3441
+ const frame = token?.parameterFrame;
3442
+ index++;
3443
+ while (index < close) {
3444
+ if (isPunct(tokens[index], ',')) {
3445
+ index++;
3446
+ continue;
3447
+ }
3448
+ index = bindingAfterRest(tokens, index);
3449
+ let binding = index;
3450
+ if (object) {
3451
+ const keyClose = isPunct(tokens[index], '[') ? closingDelimiter(index) : index;
3452
+ if (keyClose === undefined) {
3453
+ break;
3454
+ }
3455
+ if (isPunct(tokens[keyClose + 1], ':')) {
3456
+ binding = keyClose + 2;
3457
+ }
3458
+ }
3459
+ targets.push({ start: binding, end: close });
3460
+ index = bindingElementEnd(tokens, binding, close, frame, closingDelimiter);
3461
+ }
3462
+ }
3463
+ return false;
3464
+ }
3465
+ function bindingAfterRest(tokens, index) {
3466
+ return isPunct(tokens[index], '.') &&
3467
+ isPunct(tokens[index + 1], '.') &&
3468
+ isPunct(tokens[index + 2], '.')
3469
+ ? index + 3
3470
+ : index;
3471
+ }
3472
+ function bindingElementEnd(tokens, start, end, frame, closingDelimiter) {
3473
+ let index = start;
3474
+ while (index < end) {
3475
+ const token = tokens[index];
3476
+ if (isPunct(token, ',') && token?.parameterFrame === frame && token?.parameterType !== true) {
3477
+ return index + 1;
3478
+ }
3479
+ if (isPunct(token, '(') || isPunct(token, '[') || isPunct(token, '{')) {
3480
+ const close = closingDelimiter(index);
3481
+ if (close !== undefined && close < end) {
3482
+ index = close + 1;
3483
+ continue;
3484
+ }
3485
+ }
3486
+ index++;
3487
+ }
3488
+ return end;
3489
+ }
3490
+ /** Index balanced delimiters once, lazily, so nested option calls do not rescan suffixes. */
3491
+ function indexClosingDelimiters(tokens) {
3492
+ const closing = new Map();
3493
+ const stack = [];
3494
+ for (let index = 0; index < tokens.length; index++) {
3495
+ const token = tokens[index];
3496
+ if (token?.kind !== 'punct') {
3497
+ continue;
3498
+ }
3499
+ if (token.value === '(' || token.value === '[' || token.value === '{') {
3500
+ stack.push(index);
3501
+ continue;
3502
+ }
3503
+ const opener = closerFor(token.value);
3504
+ if (opener === undefined) {
3505
+ continue;
3506
+ }
3507
+ const open = stack.pop();
3508
+ if (open === undefined || !isPunct(tokens[open], opener)) {
3509
+ // Do not pair an enclosing option object across a malformed nested delimiter.
3510
+ stack.length = 0;
3511
+ }
3512
+ else {
3513
+ closing.set(open, index);
3514
+ }
3515
+ }
3516
+ return closing;
3517
+ }
3518
+ /** The opener a closer matches, or undefined when the token is not a closer. */
3519
+ function closerFor(value) {
3520
+ if (value === ')') {
3521
+ return '(';
3522
+ }
3523
+ return value === ']' ? '[' : value === '}' ? '{' : undefined;
3524
+ }
3525
+ /**
3526
+ * Recognize the supported dynamic import arguments: a literal source plus either no further
3527
+ * argument or one balanced object-literal options expression. Option contents are never evaluated
3528
+ * and never contribute evidence. `require` keeps the stricter sole-literal rule.
3529
+ */
3530
+ function dynamicImportSpecifier(tokens, argIndex, closingDelimiter) {
3531
+ const sole = soleStringSpecifier(tokens, argIndex);
3532
+ if (sole !== undefined) {
3533
+ return sole;
3534
+ }
3535
+ const specifier = tokens[argIndex];
3536
+ if (specifier?.kind !== 'string' ||
3537
+ !isPunct(tokens[argIndex + 1], ',') ||
3538
+ !isPunct(tokens[argIndex + 2], '{')) {
3539
+ return undefined;
3540
+ }
3541
+ const optionsClose = closingDelimiter(argIndex + 2);
3542
+ if (optionsClose === undefined) {
3543
+ return undefined;
3544
+ }
3545
+ const next = isPunct(tokens[optionsClose + 1], ',') ? optionsClose + 2 : optionsClose + 1;
3546
+ return isPunct(tokens[next], ')') ? specifier.value : undefined;
3547
+ }
569
3548
  /**
570
3549
  * The decoded string at `argIndex` when it is the only argument of the surrounding call, that is
571
- * when it is a string and the very next token closes the call with `)`. Returns undefined otherwise,
3550
+ * when it is a string followed by an optional trailing comma and `)`. Returns undefined otherwise,
572
3551
  * so a dynamic argument (e.g. `'pkg-' + name`) or a call with extra arguments (e.g.
573
3552
  * `require('pkg', ctx)`) never yields a static specifier.
574
3553
  */
575
3554
  function soleStringSpecifier(tokens, argIndex) {
576
3555
  const arg = tokens[argIndex];
577
- const closing = tokens[argIndex + 1];
3556
+ const closingIndex = isPunct(tokens[argIndex + 1], ',') ? argIndex + 2 : argIndex + 1;
3557
+ const closing = tokens[closingIndex];
578
3558
  if (arg?.kind !== 'string') {
579
3559
  return undefined;
580
3560
  }
@@ -597,7 +3577,88 @@ function skipBlockComment(source, start) {
597
3577
  }
598
3578
  throw new ModuleSpecifierError('Unterminated block comment', 'invalid-source', start);
599
3579
  }
3580
+ /** A dynamic import remains valid script syntax; only static forms and meta signal a module. */
3581
+ function importSignalsModule(source, end, atRoot) {
3582
+ const next = skipTrivia(source, end, source.length);
3583
+ if (source[next] === '.') {
3584
+ const meta = skipTrivia(source, next + 1, source.length);
3585
+ return ((isIdentifierStartAt(source, meta) || isUnicodeEscapeStart(source, meta)) &&
3586
+ readIdentifier(source, meta).value === 'meta');
3587
+ }
3588
+ return (atRoot &&
3589
+ (source[next] === "'" ||
3590
+ source[next] === '"' ||
3591
+ source[next] === '{' ||
3592
+ source[next] === '*' ||
3593
+ isIdentifierStartAt(source, next) ||
3594
+ isUnicodeEscapeStart(source, next)));
3595
+ }
3596
+ function exportSignalsModule(source, end) {
3597
+ const next = skipTrivia(source, end, source.length);
3598
+ return (source[next] === '{' ||
3599
+ source[next] === '*' ||
3600
+ (isIdentifierStartAt(source, next) &&
3601
+ ['default', 'const', 'let', 'var', 'function', 'async', 'class'].includes(readIdentifier(source, next).value)));
3602
+ }
3603
+ /**
3604
+ * Probe top-level await without treating every identifier named await as a keyword. A complete
3605
+ * regex may be read opaquely during this pass even when its following operand makes it script
3606
+ * division. That protects apostrophes until a later static declaration settles the whole file.
3607
+ * Calls, indexing, tags, unary +/- and flag-like identifier divisors remain ambiguous script
3608
+ * expressions absent another signal.
3609
+ */
3610
+ function probeAwaitOperand(source, end, inForHead) {
3611
+ const trivia = readTrivia(source, end, source.length);
3612
+ const next = trivia.end;
3613
+ const ch = source[next];
3614
+ if (ch === '/') {
3615
+ const finish = regexEnd(source, next);
3616
+ if (finish === undefined) {
3617
+ return { keyword: false, module: false };
3618
+ }
3619
+ const following = skipTrivia(source, finish, source.length);
3620
+ const tail = source[following];
3621
+ // Characters consumed as flags can instead be a complete identifier divisor (including a
3622
+ // valid flag such as g). Its following operator/terminator cannot establish module grammar.
3623
+ const hasIdentifierDivisor = source[finish - 1] !== '/';
3624
+ const comparisonKeyword = (isIdentifierStartAt(source, following) || isUnicodeEscapeStart(source, following)) &&
3625
+ ['in', 'instanceof'].includes(readIdentifier(source, following).value);
3626
+ const unambiguous = !hasIdentifierDivisor &&
3627
+ (tail === undefined ||
3628
+ [';', ',', ')', ']', '}', '?'].includes(tail) ||
3629
+ (tail === '.' && !isNumberStart(source[following + 1] ?? '')) ||
3630
+ // These binary operators cannot start the missing right-hand divisor. Unary +/-,
3631
+ // calls, indexing, templates and decimal starts still admit script expressions.
3632
+ ['<', '>', '*', '%', '&', '|', '^'].includes(tail) ||
3633
+ source.startsWith('==', following) ||
3634
+ source.startsWith('!=', following) ||
3635
+ comparisonKeyword);
3636
+ return { keyword: true, module: unambiguous };
3637
+ }
3638
+ // A newline also permits a bare await identifier statement followed by another statement.
3639
+ if (trivia.lineBreak) {
3640
+ return { keyword: false, module: false };
3641
+ }
3642
+ if (isIdentifierStartAt(source, next) || isUnicodeEscapeStart(source, next)) {
3643
+ const name = readIdentifier(source, next).value;
3644
+ const operand = !['in', 'instanceof'].includes(name) && !(name === 'of' && inForHead);
3645
+ return { keyword: operand, module: operand };
3646
+ }
3647
+ const operand = ch !== undefined &&
3648
+ (isNumberStart(ch) ||
3649
+ (ch === '.' && isNumberStart(source[next + 1] ?? '')) ||
3650
+ ["'", '"', '{', '~'].includes(ch) ||
3651
+ (ch === '!' && source[next + 1] !== '='));
3652
+ return { keyword: operand, module: operand };
3653
+ }
600
3654
  function skipRegex(source, start) {
3655
+ const end = regexEnd(source, start);
3656
+ if (end === undefined) {
3657
+ throw new ModuleSpecifierError('Unterminated regex literal', 'invalid-source', start);
3658
+ }
3659
+ return end;
3660
+ }
3661
+ function regexEnd(source, start) {
601
3662
  const n = source.length;
602
3663
  let i = start + 1;
603
3664
  let inClass = false;
@@ -606,7 +3667,7 @@ function skipRegex(source, start) {
606
3667
  // A regex literal cannot contain a raw line terminator. Rejecting it here stops a wrong regex
607
3668
  // start from swallowing the following lines.
608
3669
  if (ch === '\n' || ch === '\r' || ch === '\u2028' || ch === '\u2029') {
609
- throw new ModuleSpecifierError('Unterminated regex literal', 'invalid-source', start);
3670
+ return undefined;
610
3671
  }
611
3672
  if (ch === '\\') {
612
3673
  i += 2;
@@ -629,7 +3690,7 @@ function skipRegex(source, start) {
629
3690
  }
630
3691
  i++;
631
3692
  }
632
- throw new ModuleSpecifierError('Unterminated regex literal', 'invalid-source', start);
3693
+ return undefined;
633
3694
  }
634
3695
  function skipRegexFlags(source, i) {
635
3696
  const n = source.length;
@@ -803,7 +3864,7 @@ function isWhitespace(ch) {
803
3864
  ch === '\f' ||
804
3865
  ch === '\uFEFF' ||
805
3866
  isLineTerminator(ch) ||
806
- SPACE_SEPARATOR.test(ch));
3867
+ (ch !== undefined && SPACE_SEPARATOR.test(ch)));
807
3868
  }
808
3869
  /**
809
3870
  * The ECMAScript `LineTerminator` characters. Line comments and the hashbang end at any of them,
@@ -812,11 +3873,62 @@ function isWhitespace(ch) {
812
3873
  function isLineTerminator(ch) {
813
3874
  return ch === '\n' || ch === '\r' || ch === '\u2028' || ch === '\u2029';
814
3875
  }
815
- function isIdentifierStart(ch) {
816
- return /[A-Za-z_$]/.test(ch);
3876
+ const IDENTIFIER_START = /^[$_\p{ID_Start}]$/u;
3877
+ const IDENTIFIER_PART = /^[$_\u200C\u200D\p{ID_Continue}]$/u;
3878
+ function isIdentifierStartAt(source, index) {
3879
+ const codePoint = readCodePoint(source, index);
3880
+ return codePoint !== undefined && isIdentifierStartCharacter(codePoint.character);
3881
+ }
3882
+ function isIdentifierPartAt(source, index) {
3883
+ const codePoint = readCodePoint(source, index);
3884
+ return codePoint !== undefined && isIdentifierPartCharacter(codePoint.character);
3885
+ }
3886
+ function isIdentifierStartCharacter(character) {
3887
+ return IDENTIFIER_START.test(character);
3888
+ }
3889
+ function isIdentifierPartCharacter(character) {
3890
+ return IDENTIFIER_PART.test(character);
3891
+ }
3892
+ function codePointLength(source, index) {
3893
+ return (source.codePointAt(index) ?? 0) > 0xffff ? 2 : 1;
3894
+ }
3895
+ function readCodePoint(source, index) {
3896
+ const point = source.codePointAt(index);
3897
+ if (point === undefined) {
3898
+ return undefined;
3899
+ }
3900
+ const end = index + (point > 0xffff ? 2 : 1);
3901
+ return { character: String.fromCodePoint(point), end };
3902
+ }
3903
+ function isUnicodeEscapeStart(source, index) {
3904
+ return source[index] === '\\' && source[index + 1] === 'u';
817
3905
  }
818
- function isIdentifierPart(ch) {
819
- return /[A-Za-z0-9_$]/.test(ch);
3906
+ function readUnicodeEscape(source, start) {
3907
+ let end = start + 2;
3908
+ let digits;
3909
+ if (source[end] === '{') {
3910
+ const digitsStart = ++end;
3911
+ while (end < source.length && /[0-9A-Fa-f]/.test(source[end] ?? '')) {
3912
+ end++;
3913
+ }
3914
+ if (end === digitsStart || source[end] !== '}' || end - digitsStart > 6) {
3915
+ throw new ModuleSpecifierError('Invalid Unicode escape in identifier', 'invalid-source', start);
3916
+ }
3917
+ digits = source.slice(digitsStart, end);
3918
+ end++;
3919
+ }
3920
+ else {
3921
+ digits = source.slice(end, end + 4);
3922
+ if (digits.length !== 4 || !/^[0-9A-Fa-f]{4}$/.test(digits)) {
3923
+ throw new ModuleSpecifierError('Invalid Unicode escape in identifier', 'invalid-source', start);
3924
+ }
3925
+ end += 4;
3926
+ }
3927
+ const value = Number.parseInt(digits, 16);
3928
+ if (value > 0x10ffff) {
3929
+ throw new ModuleSpecifierError('Invalid Unicode escape in identifier', 'invalid-source', start);
3930
+ }
3931
+ return { character: String.fromCodePoint(value), end };
820
3932
  }
821
3933
  function isNumberStart(ch) {
822
3934
  return /[0-9]/.test(ch);