tsc-ts 0.0.6 → 0.0.8

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 (85) hide show
  1. package/README.md +1 -1
  2. package/dist/api/compiler/ast.d.ts +57 -18
  3. package/dist/api/compiler/ast.js +134 -39
  4. package/dist/api/compiler/binder-utilities.js +2 -2
  5. package/dist/api/compiler/binder.js +6 -6
  6. package/dist/api/compiler/checker-calls.js +4 -4
  7. package/dist/api/compiler/checker-contextual.js +1 -1
  8. package/dist/api/compiler/checker-declaration-order.js +3 -2
  9. package/dist/api/compiler/checker-declared-types.js +6 -3
  10. package/dist/api/compiler/checker-deprecation.js +14 -13
  11. package/dist/api/compiler/checker-destructuring.js +3 -2
  12. package/dist/api/compiler/checker-emit-helpers.d.ts +2 -1
  13. package/dist/api/compiler/checker-emit-helpers.js +5 -4
  14. package/dist/api/compiler/checker-grammar.js +5 -5
  15. package/dist/api/compiler/checker-inference.d.ts +20 -10
  16. package/dist/api/compiler/checker-inference.js +48 -25
  17. package/dist/api/compiler/checker-members.d.ts +27 -0
  18. package/dist/api/compiler/checker-members.js +165 -0
  19. package/dist/api/compiler/checker-modules.d.ts +14 -0
  20. package/dist/api/compiler/checker-modules.js +73 -1
  21. package/dist/api/compiler/checker-symbol-types.js +5 -5
  22. package/dist/api/compiler/checker-symbols.js +3 -1
  23. package/dist/api/compiler/checker.d.ts +16 -0
  24. package/dist/api/compiler/checker.js +17 -0
  25. package/dist/api/compiler/diagnostics.js +9 -5
  26. package/dist/api/compiler/parallel-parse.js +3 -1
  27. package/dist/api/compiler/parser.d.ts +7 -0
  28. package/dist/api/compiler/parser.js +73 -41
  29. package/dist/api/compiler/printer/factory-base.js +8 -8
  30. package/dist/api/compiler/printer/factory.js +4 -4
  31. package/dist/api/compiler/printer/printer.js +6 -6
  32. package/dist/api/compiler/program-checker.d.ts +5 -0
  33. package/dist/api/compiler/program-checker.js +11 -1
  34. package/dist/api/compiler/relations.js +8 -3
  35. package/dist/api/compiler/reparser.js +13 -5
  36. package/dist/api/compiler/transformers/class-fields.js +3 -3
  37. package/dist/api/compiler/transformers/commonjs-module.js +3 -3
  38. package/dist/api/compiler/transformers/declarations-pseudo.js +2 -2
  39. package/dist/api/compiler/transformers/module.js +2 -2
  40. package/dist/api/compiler/transformers/optional-chain.js +3 -3
  41. package/dist/api/compiler/transformers/subtree-facts.js +2 -2
  42. package/dist/api/compiler/transformers/tagged-template.js +2 -2
  43. package/dist/api/compiler/transformers/type-eraser.js +2 -2
  44. package/dist/api/compiler/transformers/visitor-base.js +3 -3
  45. package/dist/api/compiler/tsconfig.js +2 -2
  46. package/package.json +9 -9
  47. package/src/compiler/ast.ts +184 -59
  48. package/src/compiler/binder-utilities.ts +2 -1
  49. package/src/compiler/binder.ts +11 -5
  50. package/src/compiler/checker-calls.ts +5 -3
  51. package/src/compiler/checker-contextual.ts +1 -1
  52. package/src/compiler/checker-declaration-order.ts +3 -2
  53. package/src/compiler/checker-declared-types.ts +6 -3
  54. package/src/compiler/checker-deprecation.ts +13 -11
  55. package/src/compiler/checker-destructuring.ts +11 -2
  56. package/src/compiler/checker-emit-helpers.ts +5 -4
  57. package/src/compiler/checker-grammar.ts +5 -3
  58. package/src/compiler/checker-inference.ts +49 -25
  59. package/src/compiler/checker-members.ts +164 -0
  60. package/src/compiler/checker-modules.ts +92 -1
  61. package/src/compiler/checker-symbol-types.ts +14 -4
  62. package/src/compiler/checker-symbols.ts +4 -1
  63. package/src/compiler/checker.ts +19 -0
  64. package/src/compiler/decoder.ts +5 -4
  65. package/src/compiler/diagnostics.ts +10 -5
  66. package/src/compiler/encoder.ts +10 -5
  67. package/src/compiler/parallel-parse.ts +3 -1
  68. package/src/compiler/parser.ts +93 -39
  69. package/src/compiler/printer/factory-base.ts +14 -7
  70. package/src/compiler/printer/factory.ts +5 -3
  71. package/src/compiler/printer/printer.ts +8 -6
  72. package/src/compiler/program-checker.ts +10 -1
  73. package/src/compiler/relations.ts +10 -2
  74. package/src/compiler/reparser.ts +14 -4
  75. package/src/compiler/transformers/class-fields.ts +12 -2
  76. package/src/compiler/transformers/commonjs-module.ts +3 -2
  77. package/src/compiler/transformers/declarations-pseudo.ts +2 -1
  78. package/src/compiler/transformers/declarations.ts +1 -1
  79. package/src/compiler/transformers/module.ts +2 -1
  80. package/src/compiler/transformers/optional-chain.ts +5 -2
  81. package/src/compiler/transformers/subtree-facts.ts +2 -1
  82. package/src/compiler/transformers/tagged-template.ts +2 -2
  83. package/src/compiler/transformers/type-eraser.ts +2 -1
  84. package/src/compiler/transformers/visitor-base.ts +10 -5
  85. package/src/compiler/tsconfig.ts +2 -1
@@ -174,12 +174,15 @@ function threadCopyOf(file: DiagnosticFile): DiagnosticFile | undefined {
174
174
  return copy;
175
175
  }
176
176
 
177
+ /** The empty value of a file's position tables until they are computed. */
178
+ const noNumbers: readonly number[] = [];
179
+
177
180
  /** The text of a file diagnostics point into. */
178
181
  export class DiagnosticFile {
179
182
  readonly fileName: string;
180
183
  readonly text: string;
181
184
  // Computed on first use; never empty once computed.
182
- private lineStarts: number[] = [];
185
+ private lineStarts: readonly number[] = noNumbers;
183
186
  /** Whether this is a thread's copy of a possibly shared file (`threadCopyOf`). */
184
187
  isThreadCopy = false;
185
188
 
@@ -210,8 +213,8 @@ export class DiagnosticFile {
210
213
  // The non-ASCII characters, in order: their UTF-16 offsets, UTF-8 lengths, and the UTF-8 bytes
211
214
  // beyond one per UTF-16 code unit of the text up to and including each. Unset until first used.
212
215
  private nonASCIIStarts: number[] | undefined = undefined;
213
- private nonASCIILengths: number[] = [];
214
- private extraBytes: number[] = [];
216
+ private nonASCIILengths: readonly number[] = noNumbers;
217
+ private extraBytes: readonly number[] = noNumbers;
215
218
 
216
219
  private getNonASCIIStarts(): number[] {
217
220
  let starts = this.nonASCIIStarts;
@@ -221,8 +224,8 @@ export class DiagnosticFile {
221
224
  if (Buffer.byteLength(text, 'utf8') !== text.length) {
222
225
  // Scanning the UTF-8 encoding is faster than reading each code unit.
223
226
  const bytes = Buffer.from(text, 'utf8');
224
- const lengths = this.nonASCIILengths;
225
- const extraBytes = this.extraBytes;
227
+ const lengths: number[] = [];
228
+ const extraBytes: number[] = [];
226
229
  let extra = 0;
227
230
  for (let i = 0; i < bytes.length; i++) {
228
231
  const b = bytes[i]!;
@@ -235,6 +238,8 @@ export class DiagnosticFile {
235
238
  extraBytes.push(extra);
236
239
  i += length - 1;
237
240
  }
241
+ this.nonASCIILengths = lengths;
242
+ this.extraBytes = extraBytes;
238
243
  }
239
244
  this.nonASCIIStarts = starts;
240
245
  }
@@ -186,6 +186,11 @@ import {
186
186
  YieldExpression,
187
187
  BigIntLiteral,
188
188
  Identifier,
189
+ propertyAssignmentPostfixToken,
190
+ propertyAssignmentType,
191
+ callQuestionDotToken,
192
+ propertyAssignmentModifiers,
193
+ callTypeArguments,
189
194
  } from './ast.js';
190
195
  import { getJSDoc } from './parser.js';
191
196
  import { SyntaxKind } from './syntax-kind.js';
@@ -883,8 +888,8 @@ function childrenPropertyMask(node: Node): number {
883
888
  case SyntaxKind.CallExpression:
884
889
  return node instanceof CallExpression
885
890
  ? ((node as CallExpression).expression !== undefined ? 1 : 0) |
886
- ((node as CallExpression).questionDotToken !== undefined ? 2 : 0) |
887
- ((node as CallExpression).typeArguments !== undefined ? 4 : 0) |
891
+ (callQuestionDotToken(node as CallExpression) !== undefined ? 2 : 0) |
892
+ (callTypeArguments(node as CallExpression) !== undefined ? 4 : 0) |
888
893
  ((node as CallExpression).arguments !== undefined ? 8 : 0)
889
894
  : 0;
890
895
  case SyntaxKind.NewExpression:
@@ -938,10 +943,10 @@ function childrenPropertyMask(node: Node): number {
938
943
  return node instanceof SpreadAssignment ? ((node as SpreadAssignment).expression !== undefined ? 1 : 0) : 0;
939
944
  case SyntaxKind.PropertyAssignment:
940
945
  return node instanceof PropertyAssignment
941
- ? (hasModifiers((node as PropertyAssignment).modifiers) ? 1 : 0) |
946
+ ? (hasModifiers(propertyAssignmentModifiers(node as PropertyAssignment)) ? 1 : 0) |
942
947
  ((node as PropertyAssignment).name !== undefined ? 2 : 0) |
943
- ((node as PropertyAssignment).postfixToken !== undefined ? 4 : 0) |
944
- ((node as PropertyAssignment).type !== undefined ? 8 : 0) |
948
+ (propertyAssignmentPostfixToken(node as PropertyAssignment) !== undefined ? 4 : 0) |
949
+ (propertyAssignmentType(node as PropertyAssignment) !== undefined ? 8 : 0) |
945
950
  ((node as PropertyAssignment).initializer !== undefined ? 16 : 0)
946
951
  : 0;
947
952
  case SyntaxKind.ShorthandPropertyAssignment:
@@ -494,7 +494,9 @@ export class ParseSession {
494
494
  }
495
495
 
496
496
  private post(i: number, chunk: LoadMessage): void {
497
- this.workers[i]!.postMessage(chunk);
497
+ // Published, the chunk reaches the thread by pointer: copying its names into the message took the
498
+ // main thread 25-45 us per chunk, which during config read delays the matching of root files.
499
+ this.workers[i]!.postMessage(publish(chunk));
498
500
  this.outstanding[i] = this.outstanding[i]! + 1;
499
501
  if (chunk.kinds[0] === LoadKind.Library && largeLibraries.has(chunk.names[0]!)) this.blocked[i] = true;
500
502
  this.busy++;
@@ -6,6 +6,7 @@
6
6
  // for (const statement of file.statements.nodes) console.log(statement.kind);
7
7
  // ```
8
8
  import {
9
+ appendToList,
9
10
  ArrayLiteralExpression,
10
11
  ArrayTypeNode,
11
12
  ArrowFunction,
@@ -38,6 +39,11 @@ import {
38
39
  DeleteExpression,
39
40
  DoStatement,
40
41
  ElementAccessExpression,
42
+ emptyCommentDirectives,
43
+ emptyDiagnostics,
44
+ emptyFileReferences,
45
+ emptyNodes,
46
+ emptyPragmas,
41
47
  EmptyStatement,
42
48
  EnumDeclaration,
43
49
  EnumMember,
@@ -193,6 +199,8 @@ import {
193
199
  type Expression,
194
200
  type Statement,
195
201
  type TypeNode,
202
+ newCallExpression,
203
+ newPropertyAssignment,
196
204
  } from './ast.js';
197
205
  import {
198
206
  OperatorPrecedence,
@@ -392,6 +400,11 @@ function appendRange(to: Node[], from: readonly Node[], start: number, end: numb
392
400
  for (let i = start; i < end; i += 1) to.push(from[i]!);
393
401
  }
394
402
 
403
+ // The nodes of every empty parsed list. A list's nodes are pushed into a new `[]`, which natively
404
+ // reserves room for its first elements (112 bytes); a parsed list never changes, so an empty one
405
+ // keeps this shared array instead and its builder is freed.
406
+ const emptyListNodes: Node[] = [];
407
+
395
408
  /** Parses `text` as the file `fileName`. The script kind defaults to the one implied by the extension. */
396
409
  export function parseSourceFile(fileName: string, text: string, options?: ParseOptions): SourceFile {
397
410
  const path = options === undefined ? fileName : options.path;
@@ -490,6 +503,10 @@ export class Parser extends ScannerErrorReporter {
490
503
  // receives reparsed nodes, and a fresh array per parsed list would be one more allocation each.
491
504
  private readonly reparseLists: Node[][];
492
505
  private reparseDepth: number;
506
+ // One scratch array per nesting depth of the lists being parsed. A list collects its elements in
507
+ // its depth's array and is copied out at its exact length, so the tree keeps no spare capacity.
508
+ private readonly listScratch: Node[][];
509
+ private listDepth: number;
493
510
  reparsedClones: Node[];
494
511
  /** Nodes with eagerly parsed JSDoc, for rewinding. */
495
512
  jsDocInfos: Node[];
@@ -549,6 +566,8 @@ export class Parser extends ScannerErrorReporter {
549
566
  this.reparseList = [];
550
567
  this.reparseLists = [this.reparseList];
551
568
  this.reparseDepth = 0;
569
+ this.listScratch = [];
570
+ this.listDepth = -1;
552
571
  this.reparsedClones = [];
553
572
  this.jsDocInfos = [];
554
573
  this.jsDocTable = undefined;
@@ -759,11 +778,11 @@ export class Parser extends ScannerErrorReporter {
759
778
  // Nodes
760
779
 
761
780
  newNodeList(pos: number, end: number, nodes: Node[]): NodeList {
762
- return new NodeList(pos, end, nodes);
781
+ return new NodeList(pos, end, nodes.length === 0 ? emptyListNodes : nodes);
763
782
  }
764
783
 
765
784
  newModifierList(pos: number, end: number, nodes: Node[]): ModifierList {
766
- return new ModifierList(pos, end, nodes, modifiersToFlags(nodes));
785
+ return new ModifierList(pos, end, nodes.length === 0 ? emptyListNodes : nodes, modifiersToFlags(nodes));
767
786
  }
768
787
 
769
788
  finishNode(node: Node, pos: number): Node {
@@ -839,7 +858,8 @@ export class Parser extends ScannerErrorReporter {
839
858
  }
840
859
 
841
860
  finishSourceFile(result: SourceFile, isDeclarationFile: boolean): void {
842
- result.commentDirectives = this.scanner.getCommentDirectives();
861
+ const commentDirectives = this.scanner.getCommentDirectives();
862
+ result.commentDirectives = commentDirectives.length === 0 ? emptyCommentDirectives : commentDirectives;
843
863
  result.pragmas = getCommentPragmas(this.sourceText);
844
864
  this.processPragmasIntoFields(result);
845
865
  result.diagnostics = attachFileToDiagnostics(this.diagnostics, result);
@@ -857,9 +877,12 @@ export class Parser extends ScannerErrorReporter {
857
877
  // The table stays with the parser: when top-level await reparsing produces a second source
858
878
  // file, it finishes with the same table, which still holds the JSDoc of retained statements.
859
879
  result.jsDocs = this.jsDocTable;
860
- const clones = this.reparsedClones.slice();
861
- clones.sort(compareNodePositions);
862
- result.reparsedClones = clones;
880
+ if (this.reparsedClones.length === 0) result.reparsedClones = emptyNodes;
881
+ else {
882
+ const clones = this.reparsedClones.slice();
883
+ clones.sort(compareNodePositions);
884
+ result.reparsedClones = clones;
885
+ }
863
886
  setExternalModuleIndicator(result);
864
887
  }
865
888
 
@@ -1007,7 +1030,7 @@ export class Parser extends ScannerErrorReporter {
1007
1030
  if (depth === this.reparseLists.length) this.reparseLists.push([]);
1008
1031
  const reparseList = this.reparseLists[depth]!;
1009
1032
  this.reparseList = reparseList;
1010
- const list: Node[] = [];
1033
+ const list = this.openList();
1011
1034
  while (!this.isListTerminator(kind)) {
1012
1035
  if (this.isListElement(kind, false)) {
1013
1036
  const elt = this.parseListElement(element, list.length);
@@ -1032,7 +1055,7 @@ export class Parser extends ScannerErrorReporter {
1032
1055
  this.reparseDepth = depth - 1;
1033
1056
  this.reparseList = outerReparseList;
1034
1057
  this.parsingContexts = saveParsingContexts;
1035
- return list;
1058
+ return this.closeList(list);
1036
1059
  }
1037
1060
 
1038
1061
  parseList(kind: ParsingContext, element: ListElement): NodeList {
@@ -1046,12 +1069,13 @@ export class Parser extends ScannerErrorReporter {
1046
1069
  const pos = this.nodePos();
1047
1070
  const saveParsingContexts = this.parsingContexts;
1048
1071
  this.parsingContexts |= 1 << kind;
1049
- const list: Node[] = [];
1072
+ const list = this.openList();
1050
1073
  for (;;) {
1051
1074
  if (this.isListElement(kind, false)) {
1052
1075
  const startPos = this.nodePos();
1053
1076
  const result = this.parseListElement(element, list.length);
1054
1077
  if (result === this.noElement) {
1078
+ this.closeList(list);
1055
1079
  this.parsingContexts = saveParsingContexts;
1056
1080
  return undefined;
1057
1081
  }
@@ -1077,7 +1101,24 @@ export class Parser extends ScannerErrorReporter {
1077
1101
  if (this.abortParsingListOrMoveToNextToken(kind)) break;
1078
1102
  }
1079
1103
  this.parsingContexts = saveParsingContexts;
1080
- return this.newNodeList(pos, this.nodePos(), list);
1104
+ return this.newNodeList(pos, this.nodePos(), this.closeList(list));
1105
+ }
1106
+
1107
+ /** The scratch array for a list at the next nesting depth, empty. */
1108
+ private openList(): Node[] {
1109
+ const depth = ++this.listDepth;
1110
+ if (depth === this.listScratch.length) this.listScratch.push([]);
1111
+ return this.listScratch[depth]!;
1112
+ }
1113
+
1114
+ /** The elements collected in `list` (the scratch array from `openList`) as an array of their exact
1115
+ * length, or the shared empty array; empties the scratch array for the next list at its depth. */
1116
+ private closeList(list: Node[]): Node[] {
1117
+ this.listDepth--;
1118
+ if (list.length === 0) return emptyListNodes;
1119
+ const nodes = list.slice();
1120
+ list.length = 0;
1121
+ return nodes;
1081
1122
  }
1082
1123
 
1083
1124
  /** The list; a missing list if `opening` is absent; undefined if an element parser returned undefined. */
@@ -3106,9 +3147,10 @@ export class Parser extends ScannerErrorReporter {
3106
3147
  ? this.parseFunctionOrConstructorTypeToError(isUnionType)
3107
3148
  : this.parseConstituentType(isUnionType);
3108
3149
  if (this.token() === operator || hasLeadingOperator) {
3109
- const types: Node[] = [type];
3150
+ const types = this.openList();
3151
+ types.push(type);
3110
3152
  while (this.parseOptional(operator)) types.push(this.parseFunctionOrConstructorTypeToError(isUnionType));
3111
- const list = this.newNodeList(pos, this.nodePos(), types);
3153
+ const list = this.newNodeList(pos, this.nodePos(), this.closeList(types));
3112
3154
  type = isUnionType ? new UnionTypeNode(list) : new IntersectionTypeNode(list);
3113
3155
  this.finishNode(type, pos);
3114
3156
  }
@@ -4042,13 +4084,13 @@ export class Parser extends ScannerErrorReporter {
4042
4084
 
4043
4085
  parseTemplateTypeSpans(): NodeList {
4044
4086
  const pos = this.nodePos();
4045
- const list: Node[] = [];
4087
+ const list = this.openList();
4046
4088
  for (;;) {
4047
4089
  const span = this.parseTemplateTypeSpan();
4048
4090
  list.push(span);
4049
4091
  if (span.literal.kind !== SyntaxKind.TemplateMiddle) break;
4050
4092
  }
4051
- return this.newNodeList(pos, this.nodePos(), list);
4093
+ return this.newNodeList(pos, this.nodePos(), this.closeList(list));
4052
4094
  }
4053
4095
 
4054
4096
  parseTemplateTypeSpan(): TemplateLiteralTypeSpan {
@@ -4172,24 +4214,23 @@ export class Parser extends ScannerErrorReporter {
4172
4214
  let hasTrailingModifier = false;
4173
4215
  let hasStaticModifier = false;
4174
4216
  const pos = this.nodePos();
4175
- let list: Node[] | undefined;
4217
+ const list = this.openList();
4176
4218
  for (;;) {
4177
4219
  if (allowDecorators && this.token() === SyntaxKind.AtToken && !hasTrailingModifier) {
4178
4220
  const decorator = this.parseDecorator();
4179
- if (list === undefined) list = [];
4180
4221
  list.push(decorator);
4181
4222
  if (hasLeadingModifier) hasTrailingDecorator = true;
4182
4223
  } else {
4183
4224
  const modifier = this.tryParseModifier(hasStaticModifier, permitConstAsModifier, stopOnStartOfClassStaticBlock);
4184
4225
  if (modifier === undefined) break;
4185
4226
  if (modifier.kind === SyntaxKind.StaticKeyword) hasStaticModifier = true;
4186
- if (list === undefined) list = [];
4187
4227
  list.push(modifier);
4188
4228
  if (hasTrailingDecorator) hasTrailingModifier = true;
4189
4229
  else hasLeadingModifier = true;
4190
4230
  }
4191
4231
  }
4192
- return list === undefined ? undefined : this.newModifierList(pos, this.nodePos(), list);
4232
+ const nodes = this.closeList(list);
4233
+ return nodes.length === 0 ? undefined : this.newModifierList(pos, this.nodePos(), nodes);
4193
4234
  }
4194
4235
 
4195
4236
  parseDecorator(): Node {
@@ -4921,7 +4962,7 @@ export class Parser extends ScannerErrorReporter {
4921
4962
  const pos = this.nodePos();
4922
4963
  const saveParsingContexts = this.parsingContexts;
4923
4964
  this.parsingContexts |= 1 << ParsingContext.JsxChildren;
4924
- const list: Node[] = [];
4965
+ const list = this.openList();
4925
4966
  for (;;) {
4926
4967
  // Like tsgo, this leaves the parser's current token unchanged.
4927
4968
  const currentToken = this.scanner.reScanJsxToken(true);
@@ -4941,7 +4982,7 @@ export class Parser extends ScannerErrorReporter {
4941
4982
  break;
4942
4983
  }
4943
4984
  this.parsingContexts = saveParsingContexts;
4944
- return this.newNodeList(pos, this.nodePos(), list);
4985
+ return this.newNodeList(pos, this.nodePos(), this.closeList(list));
4945
4986
  }
4946
4987
 
4947
4988
  parseJsxChild(openingTag: Node, token: SyntaxKind): Expression | undefined {
@@ -5520,12 +5561,13 @@ export class Parser extends ScannerErrorReporter {
5520
5561
  const isOptionalChain = questionDotToken !== undefined || this.tryReparseOptionalChain(expression);
5521
5562
  expression = this.checkJSSyntax(
5522
5563
  this.finishNode(
5523
- new CallExpression(
5564
+ newCallExpression(
5524
5565
  expression,
5525
5566
  questionDotToken,
5526
5567
  typeArguments,
5527
5568
  argumentList,
5528
5569
  isOptionalChain ? NodeFlags.OptionalChain : NodeFlags.None,
5570
+ (this.contextFlags & NodeFlags.JavaScriptFile) !== 0,
5529
5571
  ),
5530
5572
  pos,
5531
5573
  ),
@@ -5615,13 +5657,13 @@ export class Parser extends ScannerErrorReporter {
5615
5657
 
5616
5658
  parseTemplateSpans(isTaggedTemplate: boolean): NodeList {
5617
5659
  const pos = this.nodePos();
5618
- const list: Node[] = [];
5660
+ const list = this.openList();
5619
5661
  for (;;) {
5620
5662
  const span = this.parseTemplateSpan(isTaggedTemplate);
5621
5663
  list.push(span);
5622
5664
  if (span.literal.kind !== SyntaxKind.TemplateMiddle) break;
5623
5665
  }
5624
- return this.newNodeList(pos, this.nodePos(), list);
5666
+ return this.newNodeList(pos, this.nodePos(), this.closeList(list));
5625
5667
  }
5626
5668
 
5627
5669
  parseTemplateSpan(isTaggedTemplate: boolean): TemplateSpan {
@@ -5753,7 +5795,14 @@ export class Parser extends ScannerErrorReporter {
5753
5795
  } else {
5754
5796
  this.parseExpected(SyntaxKind.ColonToken);
5755
5797
  const initializer = this.parseAssignmentExpressionOrHigherAllowIn();
5756
- node = new PropertyAssignment(modifiers, name, postfixToken, undefined, initializer);
5798
+ node = newPropertyAssignment(
5799
+ modifiers,
5800
+ name,
5801
+ postfixToken,
5802
+ undefined,
5803
+ initializer,
5804
+ (this.contextFlags & NodeFlags.JavaScriptFile) !== 0,
5805
+ );
5757
5806
  }
5758
5807
  return this.finishWithJSDoc(node, pos, jsdoc);
5759
5808
  }
@@ -6802,9 +6851,9 @@ export class Parser extends ScannerErrorReporter {
6802
6851
 
6803
6852
  processPragmasIntoFields(file: SourceFile): void {
6804
6853
  file.checkJsDirective = undefined;
6805
- file.referencedFiles = [];
6806
- file.typeReferenceDirectives = [];
6807
- file.libReferenceDirectives = [];
6854
+ file.referencedFiles = emptyFileReferences;
6855
+ file.typeReferenceDirectives = emptyFileReferences;
6856
+ file.libReferenceDirectives = emptyFileReferences;
6808
6857
  for (const pragma of file.pragmas) {
6809
6858
  switch (pragma.name) {
6810
6859
  case 'reference': {
@@ -6823,15 +6872,20 @@ export class Parser extends ScannerErrorReporter {
6823
6872
  resolutionMode !== undefined
6824
6873
  ? this.parseResolutionMode(resolutionMode.value, resolutionMode.pos, resolutionMode.end)
6825
6874
  : ModuleKind.None;
6826
- file.typeReferenceDirectives.push(
6875
+ file.typeReferenceDirectives = appendToList(
6876
+ file.typeReferenceDirectives,
6827
6877
  new FileReference(types.pos, types.end, types.value, parsed, isPreserved),
6828
6878
  );
6829
6879
  } else if (lib !== undefined) {
6830
- file.libReferenceDirectives.push(
6880
+ file.libReferenceDirectives = appendToList(
6881
+ file.libReferenceDirectives,
6831
6882
  new FileReference(lib.pos, lib.end, lib.value, ModuleKind.None, isPreserved),
6832
6883
  );
6833
6884
  } else if (path !== undefined) {
6834
- file.referencedFiles.push(new FileReference(path.pos, path.end, path.value, ModuleKind.None, isPreserved));
6885
+ file.referencedFiles = appendToList(
6886
+ file.referencedFiles,
6887
+ new FileReference(path.pos, path.end, path.value, ModuleKind.None, isPreserved),
6888
+ );
6835
6889
  } else {
6836
6890
  this.parseErrorAt(pragma.range.pos, pragma.range.end, Diagnostics.Invalid_reference_directive_syntax);
6837
6891
  }
@@ -7042,8 +7096,8 @@ function typeHasBlockingError(node: TypeNode | undefined): boolean {
7042
7096
  return node !== undefined && typeHasArrowFunctionBlockingParseError(node);
7043
7097
  }
7044
7098
 
7045
- function attachFileToDiagnostics(diagnostics: Diagnostic[], file: SourceFile): Diagnostic[] {
7046
- if (diagnostics.length === 0) return diagnostics;
7099
+ function attachFileToDiagnostics(diagnostics: Diagnostic[], file: SourceFile): readonly Diagnostic[] {
7100
+ if (diagnostics.length === 0) return emptyDiagnostics;
7047
7101
  const diagnosticFile = file.diagnosticFile();
7048
7102
  for (const d of diagnostics) {
7049
7103
  d.file = diagnosticFile;
@@ -7354,12 +7408,12 @@ function nextTokenIsNotColon(p: Parser): boolean {
7354
7408
 
7355
7409
  // Pragmas
7356
7410
 
7357
- function getCommentPragmas(sourceText: string): Pragma[] {
7358
- const pragmas: Pragma[] = [];
7411
+ function getCommentPragmas(sourceText: string): readonly Pragma[] {
7359
7412
  const ranges = getLeadingCommentRanges(sourceText, 0);
7360
- if (ranges !== undefined)
7361
- for (const range of ranges) extractPragmas(range, sourceText.substring(range.pos, range.end), pragmas);
7362
- return pragmas;
7413
+ if (ranges === undefined) return emptyPragmas;
7414
+ const pragmas: Pragma[] = [];
7415
+ for (const range of ranges) extractPragmas(range, sourceText.substring(range.pos, range.end), pragmas);
7416
+ return pragmas.length === 0 ? emptyPragmas : pragmas;
7363
7417
  }
7364
7418
 
7365
7419
  function extractPragmas(range: CommentRange, text: string, pragmas: Pragma[]): void {
@@ -7528,9 +7582,9 @@ function collectModuleReferences(file: SourceFile, node: Statement, inAmbientMod
7528
7582
  ) {
7529
7583
  const nameText = getText((node as ModuleDeclaration).name);
7530
7584
  if (file.externalModuleIndicator !== undefined || (inAmbientModule && !isExternalModuleNameRelative(nameText))) {
7531
- file.moduleAugmentations.push((node as ModuleDeclaration).name);
7585
+ file.moduleAugmentations = appendToList(file.moduleAugmentations, (node as ModuleDeclaration).name);
7532
7586
  } else if (!inAmbientModule) {
7533
- file.ambientModuleNames.push(nameText);
7587
+ file.ambientModuleNames = appendToList(file.ambientModuleNames, nameText);
7534
7588
  const body = (node as ModuleDeclaration).body;
7535
7589
  if (body !== undefined) {
7536
7590
  const statements = getStatements(body);
@@ -199,6 +199,13 @@ import {
199
199
  type Node,
200
200
  type NodeList,
201
201
  type Token,
202
+ propertyAssignmentPostfixToken,
203
+ propertyAssignmentType,
204
+ callQuestionDotToken,
205
+ propertyAssignmentModifiers,
206
+ callTypeArguments,
207
+ newCallExpression,
208
+ newPropertyAssignment,
202
209
  } from '../ast.js';
203
210
  import type { SyntaxKind } from '../syntax-kind.js';
204
211
 
@@ -359,12 +366,12 @@ export abstract class NodeFactoryBase {
359
366
  ): CallExpression {
360
367
  if (
361
368
  expression !== node.expression ||
362
- questionDotToken !== node.questionDotToken ||
363
- typeArguments !== node.typeArguments ||
369
+ questionDotToken !== callQuestionDotToken(node) ||
370
+ typeArguments !== callTypeArguments(node) ||
364
371
  args !== node.arguments ||
365
372
  flags !== node.flags
366
373
  ) {
367
- const updated = new CallExpression(expression, questionDotToken, typeArguments, args, flags);
374
+ const updated = newCallExpression(expression, questionDotToken, typeArguments, args, flags, false);
368
375
  this.finishUpdate(updated, node);
369
376
  return updated;
370
377
  }
@@ -2057,13 +2064,13 @@ export abstract class NodeFactoryBase {
2057
2064
  initializer: Expression,
2058
2065
  ): PropertyAssignment {
2059
2066
  if (
2060
- modifiers !== node.modifiers ||
2067
+ modifiers !== propertyAssignmentModifiers(node) ||
2061
2068
  name !== node.name ||
2062
- postfixToken !== node.postfixToken ||
2063
- typeNode !== node.type ||
2069
+ postfixToken !== propertyAssignmentPostfixToken(node) ||
2070
+ typeNode !== propertyAssignmentType(node) ||
2064
2071
  initializer !== node.initializer
2065
2072
  ) {
2066
- const updated = new PropertyAssignment(modifiers, name, postfixToken, typeNode, initializer);
2073
+ const updated = newPropertyAssignment(modifiers, name, postfixToken, typeNode, initializer, false);
2067
2074
  this.finishUpdate(updated, node);
2068
2075
  return updated;
2069
2076
  }
@@ -45,6 +45,8 @@ import {
45
45
  VariableDeclarationList,
46
46
  VariableStatement,
47
47
  VoidExpression,
48
+ newCallExpression,
49
+ newPropertyAssignment,
48
50
  } from '../ast.js';
49
51
  import { modifierToFlag } from '../ast-utilities.js';
50
52
  import { getNameOfDeclaration, getNonAssignedNameOfDeclaration, hasSyntacticModifier } from '../binder-utilities.js';
@@ -166,7 +168,7 @@ export abstract class NodeFactory extends NodeFactoryBase {
166
168
 
167
169
  createCallExpression(expression: Node, args: Node[]): CallExpression {
168
170
  return this.created(
169
- new CallExpression(expression, undefined, undefined, this.createNodeList(args), NodeFlags.None),
171
+ newCallExpression(expression, undefined, undefined, this.createNodeList(args), NodeFlags.None, false),
170
172
  );
171
173
  }
172
174
 
@@ -265,7 +267,7 @@ export abstract class NodeFactory extends NodeFactoryBase {
265
267
  }
266
268
 
267
269
  createPropertyAssignment(name: Node, initializer: Node): PropertyAssignment {
268
- return this.created(new PropertyAssignment(undefined, name, undefined, undefined, initializer));
270
+ return this.created(newPropertyAssignment(undefined, name, undefined, undefined, initializer, false));
269
271
  }
270
272
 
271
273
  createParameter(name: Node): ParameterDeclaration {
@@ -546,7 +548,7 @@ export abstract class NodeFactory extends NodeFactoryBase {
546
548
  createMethodCall(object: Node, method: string, args: Node[]): CallExpression {
547
549
  const callee = this.createPropertyAccessExpression(object, this.createIdentifier(method));
548
550
  const flags = object instanceof CallExpression ? object.flags & NodeFlags.OptionalChain : NodeFlags.None;
549
- return this.created(new CallExpression(callee, undefined, undefined, this.createNodeList(args), flags));
551
+ return this.created(newCallExpression(callee, undefined, undefined, this.createNodeList(args), flags, false));
550
552
  }
551
553
 
552
554
  /** `Global.method(...args)`, such as `Math.pow(a, b)`. */
@@ -157,6 +157,8 @@ import {
157
157
  getProperties,
158
158
  getStatements,
159
159
  getModifiers,
160
+ callQuestionDotToken,
161
+ callTypeArguments,
160
162
  } from '../ast.js';
161
163
  import { OperatorPrecedence } from '../ast-utilities.js';
162
164
  import {
@@ -2248,8 +2250,8 @@ export class Printer extends NameGeneratorHost {
2248
2250
  private emitCallExpression(node: CallExpression): boolean {
2249
2251
  const state = this.enterNode(node);
2250
2252
  this.emitCallee(node.expression, node);
2251
- this.emitOptionalToken(node.questionDotToken);
2252
- this.emitTypeArguments(node, node.typeArguments);
2253
+ this.emitOptionalToken(callQuestionDotToken(node));
2254
+ this.emitTypeArguments(node, callTypeArguments(node));
2253
2255
  this.emitList(ListItem.Argument, node, node.arguments, ListFormat.CallExpressionArguments);
2254
2256
  this.exitNode(node, state);
2255
2257
  return true;
@@ -2653,8 +2655,8 @@ export class Printer extends NameGeneratorHost {
2653
2655
  return factory.updateCallExpression(
2654
2656
  node,
2655
2657
  this.parenthesizeExpressionForNoAsi(node.expression),
2656
- node.questionDotToken,
2657
- node.typeArguments,
2658
+ callQuestionDotToken(node),
2659
+ callTypeArguments(node),
2658
2660
  node.arguments,
2659
2661
  node.flags,
2660
2662
  );
@@ -2921,8 +2923,8 @@ export class Printer extends NameGeneratorHost {
2921
2923
  this.write1('(');
2922
2924
  this.emitExpression(call.expression, OperatorPrecedence.Lowest);
2923
2925
  this.write1(')');
2924
- this.emitOptionalToken(call.questionDotToken);
2925
- this.emitTypeArguments(call, call.typeArguments);
2926
+ this.emitOptionalToken(callQuestionDotToken(call));
2927
+ this.emitTypeArguments(call, callTypeArguments(call));
2926
2928
  this.emitList(ListItem.Argument, call, call.arguments, ListFormat.CallExpressionArguments);
2927
2929
  this.exitNode(call, callState);
2928
2930
  }
@@ -32,6 +32,11 @@ import { CommentDirectiveKind } from './types.js';
32
32
  /** What the checker needs, answered by a program. */
33
33
  export class ProgramCheckerHost extends CheckerHost {
34
34
  private specifierHost: ProgramModuleSpecifierHost | undefined = undefined;
35
+ /**
36
+ * `getSourceFileForResolvedModule` by resolved file name (null when there is none): every import of
37
+ * a module asks again, and the program finds a file by its path, which it computes from the name.
38
+ */
39
+ private readonly resolvedSourceFiles = new Map<string, SourceFile | null>();
35
40
 
36
41
  constructor(readonly program: Program) {
37
42
  super();
@@ -54,7 +59,11 @@ export class ProgramCheckerHost extends CheckerHost {
54
59
  }
55
60
 
56
61
  getSourceFileForResolvedModule(fileName: string): SourceFile | undefined {
57
- return this.program.getSourceFileForResolvedModule(fileName);
62
+ const known = this.resolvedSourceFiles.get(fileName);
63
+ if (known !== undefined) return known ?? undefined;
64
+ const file = this.program.getSourceFileForResolvedModule(fileName);
65
+ this.resolvedSourceFiles.set(fileName, file ?? null);
66
+ return file;
58
67
  }
59
68
 
60
69
  getModeForUsageLocation(file: SourceFile, location: Node): ModuleKind {
@@ -129,6 +129,8 @@ import {
129
129
  getResolvedMember,
130
130
  hasResolvedProperties,
131
131
  resolvedPropertiesAreOptional,
132
+ positionsOfNamesFoundInSource,
133
+ firstUnmatchedProperty,
132
134
  } from './checker-members.js';
133
135
  import {
134
136
  getParentOfSymbol,
@@ -2822,7 +2824,13 @@ export class Relater extends TypeComparer {
2822
2824
  !isObjectLiteralType(source) &&
2823
2825
  !isEmptyArrayLiteralType(c, source) &&
2824
2826
  !isTupleType(source);
2825
- const unmatchedProperty = getUnmatchedProperty(c, source, target, requireOptionalProperties, false);
2827
+ const targetProperties = getPropertiesOfType(c, target);
2828
+ // Where it is known which target names the source has, nothing else is looked up by name.
2829
+ const found = positionsOfNamesFoundInSource(c, source, target, targetProperties);
2830
+ const unmatchedProperty =
2831
+ found !== undefined
2832
+ ? firstUnmatchedProperty(c, targetProperties, found, requireOptionalProperties)
2833
+ : getUnmatchedProperty(c, source, target, requireOptionalProperties, false);
2826
2834
  if (unmatchedProperty !== undefined) {
2827
2835
  if (reportErrors && shouldReportUnmatchedPropertyError(c, source, target))
2828
2836
  this.reportUnmatchedProperty(source, target, unmatchedProperty, requireOptionalProperties);
@@ -2842,7 +2850,7 @@ export class Relater extends TypeComparer {
2842
2850
  }
2843
2851
  }
2844
2852
  const numericNamesOnly = isTupleType(source) && isTupleType(target);
2845
- for (const targetProp of getPropertiesOfType(c, target)) {
2853
+ for (const targetProp of found !== undefined ? found.map((i) => targetProperties[i]!) : targetProperties) {
2846
2854
  if (excludedProperties !== undefined && excludedProperties.has(targetProp.name)) continue;
2847
2855
  const name = targetProp.name;
2848
2856
  if (