@effected/workspaces 0.32.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,642 @@
1
+ import { isIdentifierChar, wordEndingAt } from "./sourceText.js";
2
+
3
+ //#region src/internal/importPosition.ts
4
+ const SPACE = /\s/;
5
+ /**
6
+ * Keywords that place the scan in value code: the first one a backward walk
7
+ * meets ends it on the runtime side.
8
+ */
9
+ const VALUE_KEYWORDS = /* @__PURE__ */ new Set([
10
+ "const",
11
+ "let",
12
+ "var",
13
+ "using",
14
+ "class",
15
+ "function",
16
+ "return",
17
+ "await",
18
+ "yield",
19
+ "new",
20
+ "delete",
21
+ "void",
22
+ "throw",
23
+ "case",
24
+ "default",
25
+ "if",
26
+ "while",
27
+ "for",
28
+ "switch",
29
+ "catch",
30
+ "do",
31
+ "else",
32
+ "try",
33
+ "finally",
34
+ "enum",
35
+ "namespace",
36
+ "module",
37
+ "of",
38
+ "in",
39
+ "instanceof",
40
+ "this",
41
+ "super",
42
+ "async",
43
+ "export"
44
+ ]);
45
+ /** Keywords a type (and nothing at runtime) can follow directly. */
46
+ const TYPE_OPERATORS = /* @__PURE__ */ new Set([
47
+ "typeof",
48
+ "keyof",
49
+ "infer",
50
+ "readonly",
51
+ "unique",
52
+ "as",
53
+ "satisfies",
54
+ "is",
55
+ "implements"
56
+ ]);
57
+ /** Modifiers that precede a member or binding name whose `:` is then an annotation. */
58
+ const MEMBER_MODIFIERS = /* @__PURE__ */ new Set([
59
+ "const",
60
+ "let",
61
+ "var",
62
+ "using",
63
+ "readonly",
64
+ "public",
65
+ "private",
66
+ "protected",
67
+ "static",
68
+ "declare",
69
+ "abstract",
70
+ "override",
71
+ "accessor"
72
+ ]);
73
+ /** Keywords before a method name that make its parenthesized list a signature, not a call. */
74
+ const METHOD_PREFIXES = /* @__PURE__ */ new Set([
75
+ "function",
76
+ "async",
77
+ "get",
78
+ "set",
79
+ "static",
80
+ "public",
81
+ "private",
82
+ "protected",
83
+ "abstract",
84
+ "override",
85
+ "readonly",
86
+ "declare",
87
+ "export"
88
+ ]);
89
+ /** The characters that complete a comparison or compound assignment when they precede `=`. */
90
+ const BEFORE_EQUALS = "=!<>+-*/%&|^";
91
+ const OPENER = {
92
+ ")": "(",
93
+ "]": "[",
94
+ "}": "{"
95
+ };
96
+ /** The last non-space offset at or before `j`; `-1` when there is none. */
97
+ const back = (code, j) => {
98
+ let k = j;
99
+ while (k >= 0 && SPACE.test(code[k] ?? "")) k--;
100
+ return k;
101
+ };
102
+ /** The start of the identifier (or number) ending at `end`. */
103
+ const wordStart = (code, end) => {
104
+ let start = end;
105
+ while (start >= 0 && isIdentifierChar(code[start])) start--;
106
+ return start + 1;
107
+ };
108
+ const isQuote = (char) => char === "\"" || char === "'" || char === "`";
109
+ /** The offset of the opening quote of the literal closing at `close`, or `-1`. */
110
+ const literalStart = (code, close) => code.lastIndexOf(code[close] ?? "", close - 1);
111
+ /** The opener matching the `)`, `]` or `}` at `close`, counting its own kind only; `-1` when unmatched. */
112
+ const matching = (code, close) => {
113
+ const closer = code[close] ?? "";
114
+ const opener = OPENER[closer];
115
+ if (opener === void 0) return -1;
116
+ let depth = 0;
117
+ for (let j = close; j >= 0; j--) {
118
+ const char = code[j];
119
+ if (char === closer) depth++;
120
+ else if (char === opener && --depth === 0) return j;
121
+ }
122
+ return -1;
123
+ };
124
+ /** The `<` matching the `>` at `close`, or `-1`; gives up at a statement boundary and steps over `=>`. */
125
+ const matchingAngle = (code, close) => {
126
+ let depth = 0;
127
+ for (let j = close; j >= 0; j--) {
128
+ const char = code[j];
129
+ if (char === ">") {
130
+ if (code[j - 1] === "=") {
131
+ j--;
132
+ continue;
133
+ }
134
+ depth++;
135
+ } else if (char === "<") {
136
+ if (--depth === 0) return j;
137
+ } else if (char === ";" || char === "{" || char === "}") return -1;
138
+ }
139
+ return -1;
140
+ };
141
+ /** Whether the `=` at `j` is the plain operator: not `==`, `!=`, `<=`, `>=` or a compound assignment. */
142
+ const isPlainEquals = (code, j) => !BEFORE_EQUALS.includes(code[j - 1] ?? "") && code[j + 1] !== "=";
143
+ /** Whether the `|` or `&` at `j` is a type operator: not `||`, `&&`, `|=` or `&=`. */
144
+ const isTypeJoin = (code, j) => {
145
+ const char = code[j] ?? "";
146
+ return code[j - 1] !== char && code[j + 1] !== char && code[j + 1] !== "=";
147
+ };
148
+ /**
149
+ * Whether the word at `start..end` is the declaration keyword (`type X`,
150
+ * `interface X`, `declare const`): not a member access, and followed by a
151
+ * name. A variable or property named `type` is not.
152
+ */
153
+ const isDeclarationKeyword = (code, start, end) => {
154
+ if (code[back(code, start - 1)] === ".") return false;
155
+ let n = end + 1;
156
+ while (n < code.length && SPACE.test(code[n] ?? "")) n++;
157
+ return isIdentifierChar(code[n]);
158
+ };
159
+ /** The unmatched `(`, `[` or `{` enclosing `from`, skipping balanced groups and crossing `;`; `-1` at top level. */
160
+ const enclosingOpener = (code, from) => {
161
+ let j = from;
162
+ while (j >= 0) {
163
+ const char = code[j] ?? "";
164
+ if (char === "(" || char === "[" || char === "{") return j;
165
+ if (char === ")" || char === "]" || char === "}") {
166
+ const open = matching(code, j);
167
+ if (open < 0) return -1;
168
+ j = open - 1;
169
+ continue;
170
+ }
171
+ j--;
172
+ }
173
+ return -1;
174
+ };
175
+ /** Whether `[` at `open` .. `]` at `close` holds a `:` or an `in` at its own depth: an index signature or a mapped type. */
176
+ const isSignatureBracket = (code, open, close) => {
177
+ let depth = 0;
178
+ for (let j = open + 1; j < close; j++) {
179
+ const char = code[j] ?? "";
180
+ if (char === "(" || char === "[" || char === "{") depth++;
181
+ else if (char === ")" || char === "]" || char === "}") depth--;
182
+ else if (depth === 0) {
183
+ if (char === ":") return true;
184
+ if (char === "n" && code[j - 1] === "i" && !isIdentifierChar(code[j - 2]) && !isIdentifierChar(code[j + 1])) return true;
185
+ }
186
+ }
187
+ return false;
188
+ };
189
+ /** Whether the token ending at `end` closes a decorator expression (`@dec`, `@dec()`, `@ns.dec()`). */
190
+ const isDecorator = (code, end) => {
191
+ let j = end;
192
+ for (;;) {
193
+ if (code[j] === ")") {
194
+ const open = matching(code, j);
195
+ if (open < 0) return false;
196
+ j = back(code, open - 1);
197
+ }
198
+ if (!isIdentifierChar(code[j])) return false;
199
+ const start = wordStart(code, j);
200
+ const before = back(code, start - 1);
201
+ if (code[before] === "@") return true;
202
+ if (code[before] !== ".") return false;
203
+ j = back(code, before - 1);
204
+ }
205
+ };
206
+ /** Whether the `extends` whose word ends just after `before` belongs to a class declaration: its clause is a value. */
207
+ const isClassHeritage = (code, before) => {
208
+ let j = back(code, before);
209
+ if (code[j] === ">") {
210
+ const open = matchingAngle(code, j);
211
+ if (open < 0) return false;
212
+ j = back(code, open - 1);
213
+ }
214
+ if (!isIdentifierChar(code[j])) return false;
215
+ if (code.slice(wordStart(code, j), j + 1) === "class") return true;
216
+ return wordEndingAt(code, wordStart(code, j) - 1) === "class";
217
+ };
218
+ /**
219
+ * The keyword governing the statement the identifier ending at `from` is
220
+ * part of: `type`, `interface` or `declare` is type, `class` is a class
221
+ * body, and any value keyword, a statement boundary or the file start is
222
+ * value.
223
+ */
224
+ const statementOf = (code, from) => {
225
+ let j = from;
226
+ while (j >= 0) {
227
+ j = back(code, j);
228
+ if (j < 0) return "value";
229
+ const char = code[j] ?? "";
230
+ if (char === ";" || char === "{" || char === "=" || char === ":" || char === "(" || char === "[" || char === "?") return "value";
231
+ if (char === ")" || char === "]" || char === "}") {
232
+ const open = matching(code, j);
233
+ if (open < 0) return "value";
234
+ j = open - 1;
235
+ continue;
236
+ }
237
+ if (char === ">") {
238
+ if (code[j - 1] === "=") return "value";
239
+ const open = matchingAngle(code, j);
240
+ if (open < 0) return "value";
241
+ j = open - 1;
242
+ continue;
243
+ }
244
+ if (isIdentifierChar(char)) {
245
+ const start = wordStart(code, j);
246
+ const word = code.slice(start, j + 1);
247
+ if (code[back(code, start - 1)] === ".") {
248
+ j = start - 1;
249
+ continue;
250
+ }
251
+ if ((word === "type" || word === "interface" || word === "declare") && isDeclarationKeyword(code, start, j)) return "type";
252
+ if (word === "class") return "class";
253
+ if (VALUE_KEYWORDS.has(word)) return "value";
254
+ j = start - 1;
255
+ continue;
256
+ }
257
+ j--;
258
+ }
259
+ return "value";
260
+ };
261
+ /**
262
+ * Whether the plain `=` whose left-hand side ends at `from` opens a type
263
+ * alias (`type T = `) or a type parameter default (`<T = `), as opposed to
264
+ * an initializer, an assignment or a default value.
265
+ */
266
+ const isAliasEquals = (code, from) => {
267
+ let j = from;
268
+ while (j >= 0) {
269
+ j = back(code, j);
270
+ if (j < 0) return false;
271
+ const char = code[j] ?? "";
272
+ if (char === "<") return true;
273
+ if (char === ")" || char === "]" || char === "}") {
274
+ const open = matching(code, j);
275
+ if (open < 0) return false;
276
+ j = open - 1;
277
+ continue;
278
+ }
279
+ if (char === ">") {
280
+ if (code[j - 1] === "=") return false;
281
+ const open = matchingAngle(code, j);
282
+ if (open < 0) return false;
283
+ j = open - 1;
284
+ continue;
285
+ }
286
+ if (char === ":" || char === ";" || char === "{" || char === "(" || char === "," || char === "[" || char === "=") return false;
287
+ if (isQuote(char)) return false;
288
+ if (isIdentifierChar(char)) {
289
+ const start = wordStart(code, j);
290
+ const word = code.slice(start, j + 1);
291
+ if (code[back(code, start - 1)] === ".") {
292
+ j = start - 1;
293
+ continue;
294
+ }
295
+ if (word === "type" && isDeclarationKeyword(code, start, j)) return true;
296
+ if (VALUE_KEYWORDS.has(word)) return false;
297
+ j = start - 1;
298
+ continue;
299
+ }
300
+ j--;
301
+ }
302
+ return false;
303
+ };
304
+ /**
305
+ * Whether the `=>` whose left-hand side ends at `from` returns a function
306
+ * type's result (type) or opens an arrow function's body (value): decided by
307
+ * what precedes the parameter list, past any return-type annotation.
308
+ */
309
+ const isTypeArrow = (code, from) => {
310
+ let j = from;
311
+ for (;;) {
312
+ j = back(code, j);
313
+ if (j < 0) return false;
314
+ const char = code[j] ?? "";
315
+ if (char === ":") {
316
+ const close = back(code, j - 1);
317
+ if (code[close] !== ")") return false;
318
+ const open = matching(code, close);
319
+ return open >= 0 && isTypeSignature(code, back(code, open - 1));
320
+ }
321
+ if (char === ")" || char === "]" || char === "}") {
322
+ const open = matching(code, j);
323
+ if (open < 0) return false;
324
+ const before = back(code, open - 1);
325
+ if (char === ")" && code[before] !== ":") return isTypeSignature(code, before);
326
+ j = open - 1;
327
+ continue;
328
+ }
329
+ if (char === ">") {
330
+ if (code[j - 1] === "=") return false;
331
+ const open = matchingAngle(code, j);
332
+ if (open < 0) return false;
333
+ j = open - 1;
334
+ continue;
335
+ }
336
+ if (char === ";" || char === "{" || char === "=" || char === "," || char === "(" || char === "[") return false;
337
+ if (isQuote(char)) return false;
338
+ j = isIdentifierChar(char) ? wordStart(code, j) - 1 : j - 1;
339
+ }
340
+ };
341
+ /** Whether the parameter list whose `(` follows the token at `from` belongs to a function type rather than an arrow function. */
342
+ const isTypeSignature = (code, from) => {
343
+ let j = from;
344
+ if (code[j] === ">") {
345
+ if (code[j - 1] === "=") return isTypeArrow(code, j - 2);
346
+ const open = matchingAngle(code, j);
347
+ if (open < 0) return false;
348
+ j = back(code, open - 1);
349
+ }
350
+ if (j < 0) return false;
351
+ const char = code[j] ?? "";
352
+ if (char === "=") return isPlainEquals(code, j) && isAliasEquals(code, j - 1);
353
+ if (char === ":") return isAnnotationColon(code, j);
354
+ if (char === "|" || char === "&") return isTypeJoin(code, j);
355
+ if (char === "<") return true;
356
+ if (char === "?") return isTypeContext(code, j - 1);
357
+ if (char === ",") {
358
+ const open = enclosingOpener(code, j - 1);
359
+ return open >= 0 && isTypeGroup(code, open);
360
+ }
361
+ if (char === "(" || char === "[") return isTypeGroup(code, j);
362
+ if (isIdentifierChar(char)) {
363
+ const start = wordStart(code, j);
364
+ const word = code.slice(start, j + 1);
365
+ if (code[back(code, start - 1)] === ".") return false;
366
+ if (word === "extends") return !isClassHeritage(code, start - 1);
367
+ if (word === "new" || word === "abstract") return true;
368
+ return TYPE_OPERATORS.has(word);
369
+ }
370
+ return false;
371
+ };
372
+ /**
373
+ * Whether the group opened by the `(`, `[`, `{` or `<` at `open` sits in a
374
+ * type: a parenthesized type, a tuple, a type literal or a generic argument
375
+ * list, as opposed to a call, an array, an object literal or a block.
376
+ */
377
+ const isTypeGroup = (code, open) => {
378
+ const opener = code[open] ?? "";
379
+ if (opener === "<") return true;
380
+ if (opener === "{") return isTypeMemberList(code, back(code, open - 1));
381
+ const v = back(code, open - 1);
382
+ if (v < 0) return false;
383
+ const char = code[v] ?? "";
384
+ if (char === ">") {
385
+ if (code[v - 1] === "=") return isTypeArrow(code, v - 2);
386
+ const angle = matchingAngle(code, v);
387
+ if (angle < 0 || opener === "(") return false;
388
+ return isTypeContext(code, back(code, angle - 1));
389
+ }
390
+ if (char === "=") return isPlainEquals(code, v) && isAliasEquals(code, v - 1);
391
+ if (char === ":") return isAnnotationColon(code, v);
392
+ if (char === "|" || char === "&") return isTypeJoin(code, v);
393
+ if (char === "<") return true;
394
+ if (char === "?") return isTypeContext(code, v - 1);
395
+ if (char === ",") {
396
+ const enclosing = enclosingOpener(code, v - 1);
397
+ return enclosing >= 0 && isTypeGroup(code, enclosing);
398
+ }
399
+ if (char === "(" || char === "[") return isTypeGroup(code, v);
400
+ if (char === "." && code[v - 1] === "." && code[v - 2] === ".") return isTypeContext(code, v - 3);
401
+ if (isIdentifierChar(char)) {
402
+ const start = wordStart(code, v);
403
+ const word = code.slice(start, v + 1);
404
+ if (code[back(code, start - 1)] === ".") return false;
405
+ if (word === "extends") return !isClassHeritage(code, start - 1);
406
+ if (TYPE_OPERATORS.has(word) && word !== "typeof") return true;
407
+ if (VALUE_KEYWORDS.has(word) || opener === "(") return false;
408
+ return isTypeContext(code, start - 1);
409
+ }
410
+ return false;
411
+ };
412
+ /**
413
+ * Whether the `{` preceded by the token at `v` opens a member list whose
414
+ * `name:` entries are annotations — an interface, class or type-literal
415
+ * body — rather than an object literal or a block.
416
+ */
417
+ const isTypeMemberList = (code, v) => {
418
+ if (v < 0) return false;
419
+ const char = code[v] ?? "";
420
+ if (char === ">") {
421
+ if (code[v - 1] === "=") return isTypeArrow(code, v - 2);
422
+ const open = matchingAngle(code, v);
423
+ return open >= 0 && isTypeMemberList(code, back(code, open - 1));
424
+ }
425
+ if (char === "=") return isPlainEquals(code, v) && isAliasEquals(code, v - 1);
426
+ if (char === ":") return isAnnotationColon(code, v);
427
+ if (char === "|" || char === "&") return isTypeJoin(code, v);
428
+ if (char === "<") return true;
429
+ if (char === "?") return isTypeContext(code, v - 1);
430
+ if (char === ",") {
431
+ const open = enclosingOpener(code, v - 1);
432
+ return open >= 0 && isTypeGroup(code, open);
433
+ }
434
+ if (char === "(" || char === "[") return isTypeGroup(code, v);
435
+ if (isIdentifierChar(char)) {
436
+ const start = wordStart(code, v);
437
+ const word = code.slice(start, v + 1);
438
+ if (code[back(code, start - 1)] === ".") return false;
439
+ if (TYPE_OPERATORS.has(word) && word !== "typeof") return true;
440
+ if (VALUE_KEYWORDS.has(word) && word !== "class") return false;
441
+ return statementOf(code, v) !== "value";
442
+ }
443
+ return false;
444
+ };
445
+ /**
446
+ * Whether the member or binding name preceded by the token at `u` carries
447
+ * an annotation — a parameter, a variable, an interface, class, tuple or
448
+ * type-literal member — rather than an object-literal key, a ternary branch,
449
+ * a label or a `case`.
450
+ */
451
+ const isAnnotatedName = (code, u) => {
452
+ if (u < 0) return false;
453
+ const char = code[u] ?? "";
454
+ if (char === "?") return isTypeContext(code, u - 1);
455
+ if (char === "(" || char === "[" || char === "<") return true;
456
+ if (char === "{") return isTypeMemberList(code, back(code, u - 1));
457
+ if (char === "," || char === ";" || char === "}") {
458
+ const open = enclosingOpener(code, char === "}" ? u : u - 1);
459
+ return open >= 0 && (code[open] !== "{" || isTypeMemberList(code, back(code, open - 1)));
460
+ }
461
+ if (char === ")") return isDecorator(code, u);
462
+ if (isIdentifierChar(char)) {
463
+ if (isDecorator(code, u)) return true;
464
+ return MEMBER_MODIFIERS.has(code.slice(wordStart(code, u), u + 1));
465
+ }
466
+ return false;
467
+ };
468
+ /**
469
+ * Whether the parenthesized list whose `(` follows the token at `w` is a
470
+ * signature — a function, method or arrow parameter list, whose following
471
+ * `:` is a return type — rather than a call expression in a ternary.
472
+ */
473
+ const isSignatureParens = (code, w) => {
474
+ if (w < 0) return false;
475
+ let j = w;
476
+ if (code[j] === ">") {
477
+ if (code[j - 1] === "=") return false;
478
+ const open = matchingAngle(code, j);
479
+ if (open < 0) return false;
480
+ j = back(code, open - 1);
481
+ if (j < 0) return false;
482
+ }
483
+ const char = code[j] ?? "";
484
+ if (char === "=") return isPlainEquals(code, j);
485
+ if (char === ":" || char === "," || char === "(" || char === "[") return true;
486
+ if (char === ")") return isDecorator(code, j);
487
+ if (char === "?") {
488
+ const name = back(code, j - 1);
489
+ return isIdentifierChar(code[name]) && isMemberStart(code, back(code, wordStart(code, name) - 1));
490
+ }
491
+ if (char === "]") {
492
+ const open = matching(code, j);
493
+ return open >= 0 && isMemberStart(code, back(code, open - 1));
494
+ }
495
+ if (isQuote(char)) {
496
+ const open = literalStart(code, j);
497
+ return open >= 0 && isMemberStart(code, back(code, open - 1));
498
+ }
499
+ if (isIdentifierChar(char)) {
500
+ const start = wordStart(code, j);
501
+ const word = code.slice(start, j + 1);
502
+ if (word === "function" || word === "async") return true;
503
+ if (VALUE_KEYWORDS.has(word)) return false;
504
+ const x = back(code, start - 1);
505
+ if (code[x] === "*") return isMemberStart(code, back(code, x - 1));
506
+ return isMemberStart(code, x);
507
+ }
508
+ return false;
509
+ };
510
+ /** Whether the token at `x` can precede a member or function name: a body boundary, a modifier or a decorator. */
511
+ const isMemberStart = (code, x) => {
512
+ if (x < 0) return false;
513
+ const char = code[x] ?? "";
514
+ if (char === "{" || char === ";" || char === "}" || char === ",") return true;
515
+ if (char === ")") return isDecorator(code, x);
516
+ if (!isIdentifierChar(char)) return false;
517
+ if (isDecorator(code, x)) return true;
518
+ return METHOD_PREFIXES.has(code.slice(wordStart(code, x), x + 1));
519
+ };
520
+ /**
521
+ * Whether the `:` at `c` opens a type annotation (`name: T`, `(): T`,
522
+ * `[k: string]: T`, `{ a }: T`) rather than separating an object-literal
523
+ * property, a ternary's branches, a label or a `case`.
524
+ */
525
+ const isAnnotationColon = (code, c) => {
526
+ let j = back(code, c - 1);
527
+ if (j < 0) return false;
528
+ if (code[j] === "?") j = back(code, j - 1);
529
+ const char = code[j] ?? "";
530
+ if (char === ")") {
531
+ const open = matching(code, j);
532
+ return open >= 0 && isSignatureParens(code, back(code, open - 1));
533
+ }
534
+ if (char === "]") {
535
+ const open = matching(code, j);
536
+ if (open < 0) return false;
537
+ return isSignatureBracket(code, open, j) || isAnnotatedName(code, back(code, open - 1));
538
+ }
539
+ if (char === "}") {
540
+ const open = matching(code, j);
541
+ if (open < 0) return false;
542
+ const b = back(code, open - 1);
543
+ if (code[b] === "(" || code[b] === ",") return true;
544
+ return isIdentifierChar(code[b]) && MEMBER_MODIFIERS.has(code.slice(wordStart(code, b), b + 1));
545
+ }
546
+ if (isQuote(char)) {
547
+ const open = literalStart(code, j);
548
+ return open >= 0 && isAnnotatedName(code, back(code, open - 1));
549
+ }
550
+ if (isIdentifierChar(char)) return isAnnotatedName(code, back(code, wordStart(code, j) - 1));
551
+ return false;
552
+ };
553
+ /**
554
+ * Whether the position after the token at `from` is inside a type: a
555
+ * backward walk, skipping balanced groups, to the first decisive token — a
556
+ * `:`, `=`, `=>`, `|`, `&`, a keyword, a `;`, or the opener of the group the
557
+ * position sits in, which is decided as a group so a walk inside a function
558
+ * body never reaches the signature in front of it.
559
+ */
560
+ const isTypeContext = (code, from) => {
561
+ let j = from;
562
+ while (j >= 0) {
563
+ j = back(code, j);
564
+ if (j < 0) return false;
565
+ const char = code[j] ?? "";
566
+ if (char === ";") return false;
567
+ if (char === "(" || char === "[" || char === "{") return isTypeGroup(code, j);
568
+ if (char === ")" || char === "]" || char === "}") {
569
+ const open = matching(code, j);
570
+ if (open < 0) return false;
571
+ j = open - 1;
572
+ continue;
573
+ }
574
+ if (char === ":") return isAnnotationColon(code, j);
575
+ if (char === "=") return isPlainEquals(code, j) && isAliasEquals(code, j - 1);
576
+ if (char === ">" && code[j - 1] === "=") return isTypeArrow(code, j - 2);
577
+ if (char === "|" || char === "&") return isTypeJoin(code, j);
578
+ if (isQuote(char)) {
579
+ j = literalStart(code, j) - 1;
580
+ continue;
581
+ }
582
+ if (isIdentifierChar(char)) {
583
+ const start = wordStart(code, j);
584
+ const word = code.slice(start, j + 1);
585
+ if (code[back(code, start - 1)] === ".") {
586
+ j = start - 1;
587
+ continue;
588
+ }
589
+ if (word === "extends") return !isClassHeritage(code, start - 1);
590
+ if (word === "keyof" || word === "infer" || word === "readonly" || word === "implements") return true;
591
+ if ((word === "type" || word === "interface" || word === "declare") && isDeclarationKeyword(code, start, j)) return true;
592
+ if (VALUE_KEYWORDS.has(word)) return false;
593
+ j = start - 1;
594
+ continue;
595
+ }
596
+ j--;
597
+ }
598
+ return false;
599
+ };
600
+ /** Whether the `in` ending at `end` is a mapped type's (`{ [K in ...]`) rather than the operator or a `for...in`. */
601
+ const isMappedIn = (code, end) => {
602
+ const open = enclosingOpener(code, wordStart(code, end) - 1);
603
+ if (open < 0 || code[open] !== "[") return false;
604
+ const b = back(code, open - 1);
605
+ return code[b] === "{" || code[b] === "+" || code[b] === "-" || wordEndingAt(code, b) === "readonly";
606
+ };
607
+ /**
608
+ * Whether the `import(` call whose `(` sits at `open` is in a type position,
609
+ * where the compiler erases it: `typeof import("x")`, an annotation, a
610
+ * union or intersection member, a generic argument, an `extends` heritage or
611
+ * bound, a type alias right-hand side, a function type's return, a tuple or
612
+ * type-literal member. A runtime dynamic import — `await import("x")`, an
613
+ * initializer, an object property value, a ternary branch, a call argument
614
+ * — and every `require()` are not.
615
+ */
616
+ const isTypePositionImport = (code, open) => {
617
+ const calleeEnd = back(code, open - 1);
618
+ const calleeStart = wordStart(code, calleeEnd);
619
+ if (code.slice(calleeStart, calleeEnd + 1) !== "import") return false;
620
+ const j = back(code, calleeStart - 1);
621
+ if (j < 0) return false;
622
+ const char = code[j] ?? "";
623
+ if (char === "<") return true;
624
+ if (char === "|" || char === "&") return isTypeJoin(code, j);
625
+ if (char === "?") return code[j - 1] !== "?" && isTypeContext(code, j - 1);
626
+ if (char === "(" || char === "[") return isTypeGroup(code, j);
627
+ if (char === ".") return code[j - 1] === "." && code[j - 2] === "." && isTypeContext(code, j - 3);
628
+ if (char === "!" || char === "{") return false;
629
+ if (isIdentifierChar(char)) {
630
+ const start = wordStart(code, j);
631
+ const word = code.slice(start, j + 1);
632
+ if (code[back(code, start - 1)] === ".") return false;
633
+ if (TYPE_OPERATORS.has(word)) return true;
634
+ if (word === "extends") return !isClassHeritage(code, start - 1);
635
+ if (word === "in") return isMappedIn(code, j);
636
+ if (VALUE_KEYWORDS.has(word)) return false;
637
+ }
638
+ return isTypeContext(code, j);
639
+ };
640
+
641
+ //#endregion
642
+ export { isTypePositionImport };
@@ -356,4 +356,4 @@ const locate = (text) => {
356
356
  };
357
357
 
358
358
  //#endregion
359
- export { isIdentifierChar, lex, locate, references, specifierLiterals };
359
+ export { isIdentifierChar, lex, locate, references, specifierLiterals, wordEndingAt };