tsc-ts 0.0.8 → 0.0.10

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 (68) hide show
  1. package/README.md +1 -1
  2. package/dist/api/compiler/ast.d.ts +2 -0
  3. package/dist/api/compiler/ast.js +3 -0
  4. package/dist/api/compiler/checker-calls.js +5 -1
  5. package/dist/api/compiler/checker-grammar.js +3 -2
  6. package/dist/api/compiler/checker-inference.js +8 -2
  7. package/dist/api/compiler/checker-members.js +8 -3
  8. package/dist/api/compiler/checker-source-file.d.ts +12 -0
  9. package/dist/api/compiler/checker-source-file.js +35 -0
  10. package/dist/api/compiler/checker-symbols.js +2 -1
  11. package/dist/api/compiler/checker-type-factory.js +8 -2
  12. package/dist/api/compiler/checker.d.ts +17 -1
  13. package/dist/api/compiler/checker.js +49 -1
  14. package/dist/api/compiler/compile-program.d.ts +26 -2
  15. package/dist/api/compiler/compile-program.js +100 -7
  16. package/dist/api/compiler/diagnostics.d.ts +22 -0
  17. package/dist/api/compiler/diagnostics.js +86 -4
  18. package/dist/api/compiler/file-loader.d.ts +13 -3
  19. package/dist/api/compiler/file-loader.js +106 -30
  20. package/dist/api/compiler/link-tables.d.ts +7 -0
  21. package/dist/api/compiler/link-tables.js +22 -0
  22. package/dist/api/compiler/parallel-parse.d.ts +3 -2
  23. package/dist/api/compiler/parallel-parse.js +17 -5
  24. package/dist/api/compiler/parser.js +15 -14
  25. package/dist/api/compiler/printer/emit-context.d.ts +2 -0
  26. package/dist/api/compiler/printer/emit-context.js +4 -0
  27. package/dist/api/compiler/printer/printer.d.ts +48 -2
  28. package/dist/api/compiler/printer/printer.js +238 -14
  29. package/dist/api/compiler/program-checker.d.ts +22 -0
  30. package/dist/api/compiler/program-checker.js +39 -2
  31. package/dist/api/compiler/program.d.ts +2 -6
  32. package/dist/api/compiler/program.js +5 -30
  33. package/dist/api/compiler/relations.js +10 -0
  34. package/dist/api/compiler/symbol.d.ts +1 -1
  35. package/dist/api/compiler/symbol.js +8 -10
  36. package/dist/api/library/diagnostics.d.ts +10 -0
  37. package/dist/api/library/diagnostics.js +20 -0
  38. package/package.json +9 -9
  39. package/src/compiler/ast.ts +3 -0
  40. package/src/compiler/checker-calls.ts +3 -1
  41. package/src/compiler/checker-grammar.ts +3 -2
  42. package/src/compiler/checker-inference.ts +7 -2
  43. package/src/compiler/checker-members.ts +9 -3
  44. package/src/compiler/checker-source-file.ts +34 -0
  45. package/src/compiler/checker-symbols.ts +2 -1
  46. package/src/compiler/checker-type-factory.ts +4 -2
  47. package/src/compiler/checker.ts +45 -1
  48. package/src/compiler/compile-program.ts +121 -7
  49. package/src/compiler/diagnostics.ts +84 -3
  50. package/src/compiler/file-loader.ts +106 -29
  51. package/src/compiler/incremental.ts +80 -13
  52. package/src/compiler/link-tables.ts +32 -0
  53. package/src/compiler/parallel-check.ts +326 -62
  54. package/src/compiler/parallel-emit.ts +320 -0
  55. package/src/compiler/parallel-parse.ts +16 -5
  56. package/src/compiler/parse-thread.ts +3 -0
  57. package/src/compiler/parser.ts +15 -16
  58. package/src/compiler/printer/emit-context.ts +5 -0
  59. package/src/compiler/printer/printer.ts +231 -16
  60. package/src/compiler/program-checker.ts +40 -2
  61. package/src/compiler/program.ts +5 -25
  62. package/src/compiler/relations.ts +7 -0
  63. package/src/compiler/symbol.ts +15 -10
  64. package/src/compiler/thread-entry.ts +61 -3
  65. package/src/compiler/tsc-build.ts +63 -9
  66. package/src/compiler/tsc-cli.ts +5 -0
  67. package/src/compiler/tsc-compile.ts +278 -3
  68. package/src/library/diagnostics.ts +23 -0
@@ -7,7 +7,7 @@
7
7
  * ```
8
8
  */
9
9
  import { ArrayLiteralExpression, Decorator, getText, NodeVisitor, ObjectLiteralExpression, ParameterDeclaration, } from './ast.js';
10
- import { compareDiagnostics, createCompilerDiagnostic, Diagnostic, equalDiagnostics, equalDiagnosticsNoRelatedInfo, useThreadTextCaches, } from './diagnostics.js';
10
+ import { compareDiagnostics, createCompilerDiagnostic, Diagnostic, equalDiagnostics, equalDiagnosticsNoRelatedInfo, sortDiagnostics, useThreadTextCaches, } from './diagnostics.js';
11
11
  import { canReplaceFile, FileLoader, jsxRuntimeImportOf, modeAwareKey, needsImportHelpersImportSpecifier, withReplacedFile, } from './file-loader.js';
12
12
  import { PackageJsonLookup } from './package-json.js';
13
13
  import { ProcessingDiagnostic } from './file-include.js';
@@ -325,34 +325,9 @@ export class Program {
325
325
  getResolvedModule(file, moduleName, mode) {
326
326
  return this.processed.resolvedModules.get(file.path)?.get(modeAwareKey(moduleName, mode));
327
327
  }
328
- /** The program files that the imports and type reference directives of `file` resolved to. */
329
- /**
330
- * The files `file` imports and references through type directives. `byFileName`, the program's
331
- * files by file name, finds a file without computing its path when it is resolved by that name.
332
- */
333
- getResolvedFilesOf(file, byFileName) {
334
- const result = [];
335
- const modules = this.processed.resolvedModules.get(file.path);
336
- if (modules !== undefined)
337
- for (const resolved of modules.values()) {
338
- const name = resolved.resolvedFileName;
339
- if (name === '')
340
- continue;
341
- const target = byFileName?.get(name) ?? this.getSourceFileForResolvedModule(name);
342
- if (target !== undefined)
343
- result.push(target);
344
- }
345
- const types = this.processed.typeResolutionsInFile.get(file.path);
346
- if (types !== undefined)
347
- for (const resolved of types.values()) {
348
- const name = resolved.resolvedFileName;
349
- if (name === '')
350
- continue;
351
- const target = byFileName?.get(name) ?? this.getSourceFile(name);
352
- if (target !== undefined)
353
- result.push(target);
354
- }
355
- return result;
328
+ /** The files each file added to the program (`ProcessedFiles.addedFiles`), as indices of `getSourceFiles`. */
329
+ addedFiles() {
330
+ return this.processed.addedFiles;
356
331
  }
357
332
  /** The specifier of the implicit JSX runtime import of `file`, if any. */
358
333
  jsxRuntimeImportSpecifier(path) {
@@ -986,7 +961,7 @@ class NodeCollector extends NodeVisitor {
986
961
  }
987
962
  /** Sorts diagnostics and merges duplicates, combining their related information. */
988
963
  export function sortAndDeduplicateDiagnostics(diagnostics) {
989
- const sorted = [...diagnostics].sort(compareDiagnostics);
964
+ const sorted = sortDiagnostics(diagnostics);
990
965
  if (sorted.length < 2)
991
966
  return sorted;
992
967
  const result = [];
@@ -232,6 +232,16 @@ export function isTypeRelatedTo(c, source, target, relation) {
232
232
  if ((source.flags & 394239 /* TypeFlags.Singleton */) !== 0)
233
233
  return true;
234
234
  }
235
+ else {
236
+ // What the relater decides first for types that may simplify (Relater.isRelatedToEx): their normal forms are
237
+ // one type, or can be identical only with identical flags.
238
+ const s = getNormalizedType(c, source, false);
239
+ const t = getNormalizedType(c, target, true);
240
+ if (s === t)
241
+ return true;
242
+ if (s.flags !== t.flags)
243
+ return false;
244
+ }
235
245
  if ((source.flags & 1048576 /* TypeFlags.Object */) !== 0 && (target.flags & 1048576 /* TypeFlags.Object */) !== 0) {
236
246
  const related = relation.get(getRelationKey(c, source, target, 0 /* IntersectionState.None */, relation === c.identityRelation, false).key);
237
247
  if (related !== 0 /* RelationComparisonResult.None */)
@@ -139,7 +139,7 @@ export declare class SymbolRareFields implements ProgramLifetime {
139
139
  }
140
140
  export declare class Symbol implements ProgramLifetime {
141
141
  flags: SymbolFlags;
142
- /** The check flags, and `valueDeclarationIsFirst`. */
142
+ /** The check flags, and `SymbolBits.ValueDeclarationIsFirst`. */
143
143
  private bits;
144
144
  readonly name: string;
145
145
  /**
@@ -8,11 +8,9 @@ export class SymbolRareFields {
8
8
  /** The value declaration where it is not the first declaration (`Symbol.valueDeclaration`). */
9
9
  valueDeclaration = undefined;
10
10
  }
11
- /** In `Symbol.bits`, above the check flags: the value declaration is the first declaration. */
12
- const valueDeclarationIsFirst = 1 << 30;
13
11
  export class Symbol {
14
12
  flags;
15
- /** The check flags, and `valueDeclarationIsFirst`. */
13
+ /** The check flags, and `SymbolBits.ValueDeclarationIsFirst`. */
16
14
  bits;
17
15
  name;
18
16
  /**
@@ -35,17 +33,17 @@ export class Symbol {
35
33
  this.id = idsAtCreation ? takeSymbolId() : 0;
36
34
  }
37
35
  get checkFlags() {
38
- return (this.bits & ~valueDeclarationIsFirst);
36
+ return (this.bits & ~1073741824 /* SymbolBits.ValueDeclarationIsFirst */);
39
37
  }
40
38
  set checkFlags(flags) {
41
- this.bits = (this.bits & valueDeclarationIsFirst) | flags;
39
+ this.bits = (this.bits & 1073741824 /* SymbolBits.ValueDeclarationIsFirst */) | flags;
42
40
  }
43
41
  /** Makes `node` the first declaration, keeping the value declaration. */
44
42
  setFirst(node) {
45
- if ((this.bits & valueDeclarationIsFirst) !== 0 && node !== this.declaration) {
43
+ if ((this.bits & 1073741824 /* SymbolBits.ValueDeclarationIsFirst */) !== 0 && node !== this.declaration) {
46
44
  // The value declaration stays what it was.
47
45
  this.rareFieldsOrNew().valueDeclaration = this.declaration;
48
- this.bits &= ~valueDeclarationIsFirst;
46
+ this.bits &= ~1073741824 /* SymbolBits.ValueDeclarationIsFirst */;
49
47
  }
50
48
  this.declaration = node;
51
49
  }
@@ -54,7 +52,7 @@ export class Symbol {
54
52
  * says; any other is kept in the rare fields.
55
53
  */
56
54
  get valueDeclaration() {
57
- if ((this.bits & valueDeclarationIsFirst) !== 0)
55
+ if ((this.bits & 1073741824 /* SymbolBits.ValueDeclarationIsFirst */) !== 0)
58
56
  return this.declaration;
59
57
  const group = this.rareFields;
60
58
  return group === undefined ? undefined : group.valueDeclaration;
@@ -62,12 +60,12 @@ export class Symbol {
62
60
  set valueDeclaration(node) {
63
61
  const group = this.rareFields;
64
62
  if (node !== undefined && node === this.declaration) {
65
- this.bits |= valueDeclarationIsFirst;
63
+ this.bits |= 1073741824 /* SymbolBits.ValueDeclarationIsFirst */;
66
64
  if (group !== undefined)
67
65
  group.valueDeclaration = undefined;
68
66
  return;
69
67
  }
70
- this.bits &= ~valueDeclarationIsFirst;
68
+ this.bits &= ~1073741824 /* SymbolBits.ValueDeclarationIsFirst */;
71
69
  if (node !== undefined || group !== undefined)
72
70
  this.rareFieldsOrNew().valueDeclaration = node;
73
71
  }
@@ -11,3 +11,13 @@ export declare function diagnosticOf(d: CompilerDiagnostic): Diagnostic;
11
11
  * them. Declaration diagnostics are empty, as tsgo's API returns them.
12
12
  */
13
13
  export declare function programDiagnostics(checked: CheckedProgram, kind: DiagnosticKind, file: SourceFile | undefined): readonly CompilerDiagnostic[];
14
+ /**
15
+ * What `semantic` answers for each of `files` and what `global` answers, after checking `files` and
16
+ * no other file. Checking one file can add diagnostics located in another, so every file is checked
17
+ * before any file's diagnostics are collected. With every file of the program listed, the answers are
18
+ * those of the full check.
19
+ */
20
+ export declare function checkedFileDiagnostics(checked: CheckedProgram, files: readonly SourceFile[]): {
21
+ files: (readonly CompilerDiagnostic[])[];
22
+ global: readonly CompilerDiagnostic[];
23
+ };
@@ -48,3 +48,23 @@ export function programDiagnostics(checked, kind, file) {
48
48
  return [];
49
49
  }
50
50
  }
51
+ /**
52
+ * What `semantic` answers for each of `files` and what `global` answers, after checking `files` and
53
+ * no other file. Checking one file can add diagnostics located in another, so every file is checked
54
+ * before any file's diagnostics are collected. With every file of the program listed, the answers are
55
+ * those of the full check.
56
+ */
57
+ export function checkedFileDiagnostics(checked, files) {
58
+ for (const file of files)
59
+ checked.getSemanticDiagnostics(file);
60
+ const program = checked.program;
61
+ const global = [
62
+ ...program.getConfigFileParsingDiagnostics(),
63
+ ...program.getProgramDiagnostics(),
64
+ ...checked.getGlobalDiagnostics(),
65
+ ];
66
+ return {
67
+ files: files.map((file) => checked.getSemanticDiagnostics(file)),
68
+ global: sortAndDeduplicateDiagnostics(global).filter((d) => d.file === undefined),
69
+ };
70
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsc-ts",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "description": "The TypeScript 7 compiler, ported to TypeScript and compiled to a native executable.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -29,18 +29,18 @@
29
29
  "./package.json": "./package.json"
30
30
  },
31
31
  "dependencies": {
32
- "@scriptc/threads": "0.4.2"
32
+ "@scriptc/threads": "0.4.5"
33
33
  },
34
34
  "engines": {
35
35
  "node": ">=20"
36
36
  },
37
37
  "optionalDependencies": {
38
- "@tsc-ts/darwin-arm64": "0.0.8",
39
- "@tsc-ts/darwin-x64": "0.0.8",
40
- "@tsc-ts/linux-arm64-gnu": "0.0.8",
41
- "@tsc-ts/linux-arm64-musl": "0.0.8",
42
- "@tsc-ts/linux-x64-gnu": "0.0.8",
43
- "@tsc-ts/linux-x64-musl": "0.0.8",
44
- "@tsc-ts/win32-x64-msvc": "0.0.8"
38
+ "@tsc-ts/darwin-arm64": "0.0.10",
39
+ "@tsc-ts/darwin-x64": "0.0.10",
40
+ "@tsc-ts/linux-arm64-gnu": "0.0.10",
41
+ "@tsc-ts/linux-arm64-musl": "0.0.10",
42
+ "@tsc-ts/linux-x64-gnu": "0.0.10",
43
+ "@tsc-ts/linux-x64-musl": "0.0.10",
44
+ "@tsc-ts/win32-x64-msvc": "0.0.10"
45
45
  }
46
46
  }
@@ -8258,6 +8258,8 @@ export class SourceFile extends Node {
8258
8258
  identifierCount: number;
8259
8259
  nodeCount: number;
8260
8260
  textCount: number;
8261
+ /** The API's binary encoding of this file, when an API server keeps it (`keptEncodingOf`). */
8262
+ apiEncoding: Uint8Array | undefined;
8261
8263
  /** Module specifiers of imports, exports, `require` calls and `import()` types, in source order. */
8262
8264
  imports: Node[];
8263
8265
  /** Names of `declare module "x"` augmentations and `declare global` blocks in modules. */
@@ -8325,6 +8327,7 @@ export class SourceFile extends Node {
8325
8327
  this.identifierCount = 0;
8326
8328
  this.nodeCount = 0;
8327
8329
  this.textCount = 0;
8330
+ this.apiEncoding = undefined;
8328
8331
  this.imports = [];
8329
8332
  this.moduleAugmentations = emptyNodes;
8330
8333
  this.ambientModuleNames = emptyStrings;
@@ -975,7 +975,9 @@ export function reorderCandidates(c: Checker, signatures: Signature[], callChain
975
975
  cutoffIndex++;
976
976
  } else spliceIndex = index;
977
977
  if (callChainFlags !== 0) signature = getOptionalCallSignature(c, signature, callChainFlags);
978
- result.splice(spliceIndex, 0, signature);
978
+ // Most signatures go at the end, where a push is cheaper than an inserting splice.
979
+ if (spliceIndex === result.length) result.push(signature);
980
+ else result.splice(spliceIndex, 0, signature);
979
981
  }
980
982
  return result;
981
983
  }
@@ -3143,7 +3143,8 @@ function checkGrammarObjectLiteralExpression(
3143
3143
  node: ObjectLiteralExpression,
3144
3144
  inDestructuring: boolean,
3145
3145
  ): boolean {
3146
- const seen = new Map<string, DeclarationMeaning>();
3146
+ // An object literal with one property can't repeat a name, so it needs no table of names.
3147
+ const seen = node.properties.nodes.length > 1 ? new Map<string, DeclarationMeaning>() : undefined;
3147
3148
  for (const property of node.properties.nodes) {
3148
3149
  if (property.kind === SyntaxKind.SpreadAssignment) {
3149
3150
  if (inDestructuring) {
@@ -3222,7 +3223,7 @@ function checkGrammarObjectLiteralExpression(
3222
3223
  }
3223
3224
  if (inDestructuring) continue;
3224
3225
  const effectiveName = getEffectivePropertyNameForPropertyNameNode(g.c, name);
3225
- if (effectiveName === undefined) continue;
3226
+ if (effectiveName === undefined || seen === undefined) continue;
3226
3227
  const existingKind = seen.get(effectiveName);
3227
3228
  if (existingKind === undefined) {
3228
3229
  seen.set(effectiveName, currentKind);
@@ -733,8 +733,13 @@ class InferenceState {
733
733
  // Keep candidates with the highest depth first.
734
734
  index = 0;
735
735
  while (index < inference.candidateDepths.length && inference.candidateDepths[index] >= this.depth) index++;
736
- inference.candidates.splice(index, 0, candidate);
737
- inference.candidateDepths.splice(index, 0, this.depth);
736
+ if (index === inference.candidates.length) {
737
+ inference.candidates.push(candidate);
738
+ inference.candidateDepths.push(this.depth);
739
+ } else {
740
+ inference.candidates.splice(index, 0, candidate);
741
+ inference.candidateDepths.splice(index, 0, this.depth);
742
+ }
738
743
  clearCachedInferences(this.inferences);
739
744
  }
740
745
  }
@@ -923,8 +923,12 @@ export function resolveDeclaredMembers(c: Checker, t: InterfaceType): InterfaceT
923
923
  const members = getMembersOfSymbol(c, t.symbol!);
924
924
  declared.declaredMembersResolved = true;
925
925
  declared.declaredMembers = members;
926
- declared.declaredCallSignatures = getSignaturesOfSymbol(c, members.get(InternalSymbolName.Call));
927
- declared.declaredConstructSignatures = getSignaturesOfSymbol(c, members.get(InternalSymbolName.New));
926
+ // Most interfaces declare neither, and share one empty list.
927
+ const call = members.get(InternalSymbolName.Call);
928
+ const construct = members.get(InternalSymbolName.New);
929
+ declared.declaredCallSignatures = call !== undefined ? getSignaturesOfSymbol(c, call) : emptySignatures;
930
+ declared.declaredConstructSignatures =
931
+ construct !== undefined ? getSignaturesOfSymbol(c, construct) : emptySignatures;
928
932
  declared.declaredIndexInfos = getIndexInfosOfSymbol(c, t.symbol!);
929
933
  }
930
934
  return declared;
@@ -1570,7 +1574,9 @@ export function getPropertyOfTypeEx(
1570
1574
  ): Symbol | undefined {
1571
1575
  if (!isOwnReducedApparentType(t)) t = getReducedApparentTypeWorker(c, t);
1572
1576
  if ((t.flags & TypeFlags.Object) !== 0) {
1573
- const resolved = resolveStructuredTypeMembers(c, t);
1577
+ // The first test of resolveStructuredTypeMembers, here where most lookups find the members resolved.
1578
+ const resolved =
1579
+ (t.objectFlags & ObjectFlags.MembersResolved) !== 0 ? (t as StructuredType) : resolveStructuredTypeMembers(c, t);
1574
1580
  const symbol = getResolvedMember(resolved, name);
1575
1581
  if (symbol !== undefined && includeTypeOnlyMembers) return symbol;
1576
1582
  if (symbol !== undefined) {
@@ -161,6 +161,40 @@ function checkSourceFileWorker(c: Checker, file: SourceFile): void {
161
161
  links.deferredNodes = undefined;
162
162
  }
163
163
 
164
+ /**
165
+ * Checks the statements `start` to `end` of the declaration file `file`, one of the ranges that several
166
+ * checkers check it in (parallel-check.ts), and returns whether the checker then has diagnostics in the
167
+ * file. The range ending the file also runs the checks of the whole file. The file is not marked checked.
168
+ *
169
+ * When no range finds a diagnostic in the file, checking it whole would find none either: each statement
170
+ * is checked by the checker of its range. Otherwise the file is checked whole (`startParallelCheck`): the
171
+ * diagnostics of the ranges can differ from the file's, where they depend on the order the file's
172
+ * declarations are resolved in. A union type prints its members in the order the checker created them,
173
+ * and a check that a symbol's first declaration runs reports on all its declarations.
174
+ */
175
+ export function checkSourceFileRange(c: Checker, file: SourceFile, start: number, end: number): boolean {
176
+ const statements = getStatements(file)!.nodes;
177
+ c.fileBeingChecked = file;
178
+ c.saveDeferredDiagnostics = true;
179
+ for (let i = start; i < end; i++) checkSourceElement(c, statements[i]!);
180
+ checkDeferredNodes(c, file);
181
+ const whole = end === statements.length;
182
+ if (whole && isExternalOrCommonJSModule(file)) checkExternalModuleExports(c, file);
183
+ c.renamedBindingElementsInTypes.length = 0;
184
+ if (whole) {
185
+ checkGrammarSourceFile(c, file);
186
+ checkDeferredNodes(c, file);
187
+ checkPendingExternalEmitHelpers(c, file);
188
+ }
189
+ c.fileBeingChecked = undefined;
190
+ c.saveDeferredDiagnostics = false;
191
+ produceDeferredIterationErrors(c);
192
+ produceDeferredCollisionChecks(c);
193
+ // Deferred nodes of a later range are queued again (checkNodeDeferred).
194
+ c.nodeLinks(file).deferredNodes = undefined;
195
+ return c.diagnostics.forFile(file.fileName).length > 0;
196
+ }
197
+
164
198
  /** Checks a statement, declaration, class member or type node. */
165
199
  export function checkSourceElement(c: Checker, node: Node | undefined): void {
166
200
  if (node === undefined) return;
@@ -957,6 +957,7 @@ export function setGlobal(c: Checker, name: string, symbol: Symbol): void {
957
957
  function setMergedSymbol(c: Checker, id: number, target: Symbol): void {
958
958
  if (c.mergedSymbolChanges !== undefined) c.mergedSymbolChanges.set(c.mergedSymbols, id, target);
959
959
  else c.mergedSymbols.set(id, target);
960
+ c.mergedSymbolIds.add(id);
960
961
  }
961
962
 
962
963
  /** Merges script locals into globals; ambient modules and augmentations wait for global types. */
@@ -1298,7 +1299,7 @@ function recordMergedSymbol(c: Checker, target: Symbol, source: Symbol): void {
1298
1299
 
1299
1300
  export function getMergedSymbol(c: Checker, symbol: Symbol): Symbol {
1300
1301
  const id = symbol.id;
1301
- if (id !== 0) {
1302
+ if (id !== 0 && c.mergedSymbolIds.has(id)) {
1302
1303
  const merged = c.mergedSymbols.get(id);
1303
1304
  if (merged !== undefined) return merged;
1304
1305
  }
@@ -998,7 +998,8 @@ export function unionContainsSortedTypes(c: Checker, union: UnionType, types: re
998
998
  export function insertType(c: Checker, types: Type[], t: Type): boolean {
999
999
  const index = binarySearchTypes(c, types, t);
1000
1000
  if (index >= 0) return false;
1001
- types.splice(~index, 0, t);
1001
+ if (~index === types.length) types.push(t);
1002
+ else types.splice(~index, 0, t);
1002
1003
  return true;
1003
1004
  }
1004
1005
 
@@ -1214,7 +1215,8 @@ function insertTypeNearLast(c: Checker, set: TypeSet, t: Type): void {
1214
1215
  if (r < 0) low = middle + 1;
1215
1216
  else high = middle - 1;
1216
1217
  }
1217
- types.splice(low, 0, t);
1218
+ if (low === types.length) types.push(t);
1219
+ else types.splice(low, 0, t);
1218
1220
  set.last = low;
1219
1221
  }
1220
1222
 
@@ -33,7 +33,7 @@ import type { DeferredCollisionCheck } from './checker-generated-code.js';
33
33
  import type { EmitHelperFileLinks } from './checker-emit-helpers.js';
34
34
  import { sideOf } from './side-state.js';
35
35
  import type { Node, PatternAmbientModule, SourceFile } from './ast.js';
36
- import { CompactSymbolLinkTable, IntLinkTable, LinkTable, SparseLinkTable } from './link-tables.js';
36
+ import { CompactSymbolLinkTable, IntLinkTable, LinkTable, PagedIdSet, SparseLinkTable } from './link-tables.js';
37
37
  import { getErrorRangeForNode, getSourceFileOfNode } from './binder-utilities.js';
38
38
  import { Diagnostic, type DiagnosticMessage, Diagnostics, compareDiagnostics } from './diagnostics.js';
39
39
  import {
@@ -245,6 +245,30 @@ class DiagnosticCollection {
245
245
  return this.global.slice();
246
246
  }
247
247
 
248
+ /** The global diagnostics `withoutNewGlobals` removed. */
249
+ removedGlobals = 0;
250
+
251
+ /** Runs `run`, then removes the global diagnostics it added. */
252
+ withoutNewGlobals<T>(run: () => T): T {
253
+ const outer = this.log;
254
+ const added: Diagnostic[] = [];
255
+ this.log = added;
256
+ try {
257
+ return run();
258
+ } finally {
259
+ this.log = outer;
260
+ if (outer !== undefined) for (const d of added) outer.push(d);
261
+ const globals = new Set<Diagnostic>();
262
+ for (const d of added) if (d.file === undefined) globals.add(d);
263
+ if (globals.size > 0) {
264
+ this.removedGlobals += globals.size;
265
+ let kept = 0;
266
+ for (const d of this.global) if (!globals.has(d)) this.global[kept++] = d;
267
+ this.global.length = kept;
268
+ }
269
+ }
270
+ }
271
+
248
272
  /** Records the diagnostics added from now on. */
249
273
  startLog(): void {
250
274
  this.log = [];
@@ -402,6 +426,11 @@ export class Checker implements TypeChecking {
402
426
  readonly resolvedSignatures = new SparseLinkTable<Signature>();
403
427
  /** Merged symbols, indexed by the id of the symbol merged into them. */
404
428
  readonly mergedSymbols: Map<number, Symbol>;
429
+ /**
430
+ * The ids that `mergedSymbols` has, or had: most symbols are merged into none, and `getMergedSymbol`
431
+ * finds that here without probing the map.
432
+ */
433
+ readonly mergedSymbolIds = new PagedIdSet();
405
434
  /** Globals merged from the default libraries, shared with other checkers. */
406
435
  libGlobals: LibGlobals | undefined;
407
436
  /** The program's globals and module augmentations as another checker merged them, shared with it. */
@@ -746,6 +775,7 @@ export class Checker implements TypeChecking {
746
775
  this.globals = libs.copyGlobals();
747
776
  this.mergedSymbols = libs.copyMergedSymbols();
748
777
  }
778
+ for (const id of this.mergedSymbols.keys()) this.mergedSymbolIds.add(id);
749
779
  this.globalThisSymbol = this.newSymbol(SymbolFlags.Module, 'globalThis');
750
780
  this.globalThisSymbol.checkFlags = CheckFlags.Readonly;
751
781
  this.globalThisSymbol.exports = this.globals;
@@ -934,6 +964,19 @@ export class Checker implements TypeChecking {
934
964
  return this.diagnostics.globals();
935
965
  }
936
966
 
967
+ /**
968
+ * Runs `run` (such as emitting declarations), then forgets the global diagnostics it reported:
969
+ * the program collects global diagnostics before it emits.
970
+ */
971
+ withoutGlobalDiagnostics<T>(run: () => T): T {
972
+ return this.diagnostics.withoutNewGlobals(run);
973
+ }
974
+
975
+ /** The global diagnostics `withoutGlobalDiagnostics` forgot so far. */
976
+ forgottenGlobalDiagnostics(): number {
977
+ return this.diagnostics.removedGlobals;
978
+ }
979
+
937
980
  // ---- Links
938
981
 
939
982
  /** The rarely used links of `symbol`, created on first use. */
@@ -1087,6 +1130,7 @@ export function disposeChecker(c: Checker): void {
1087
1130
  c.mergedSymbols.clear();
1088
1131
  c.globals.clear();
1089
1132
  }
1133
+ c.mergedSymbolIds.clear();
1090
1134
  c.sharedSymbolCopies.clear();
1091
1135
  c.memberPositionsInLists.clear();
1092
1136
  c.memberNamesOfLists.clear();
@@ -13,7 +13,11 @@ import { CheckerDeclarationEmitResolver, CheckerEmitResolver } from './checker-e
13
13
  import { getEmitModuleFormatOfFile } from './program-utilities.js';
14
14
  import { DeclarationGlobals, SyntacticDeclarationEmitResolver } from './transformers/declarations-syntactic.js';
15
15
  import { mark } from './timeline.js';
16
- import type { EmitResolver } from './transformers/emit-resolver.js';
16
+ import { DecoratorMetadata } from './transformers/decorator-metadata.js';
17
+ import { SyntaxFacts } from './transformers/syntax-facts.js';
18
+ import type { TypeReferenceSerializationKind } from './transformers/type-serialization.js';
19
+ import { EmitContext } from './printer/emit-context.js';
20
+ import { type EmitResolver, SyntacticEmitResolver } from './transformers/emit-resolver.js';
17
21
  import { ModuleFormatHost } from './transformers/module.js';
18
22
  import { type MergedGlobals, ModuleLookup, ReferenceResolver } from './transformers/reference-resolver.js';
19
23
 
@@ -193,6 +197,100 @@ export class EmitIndexes {
193
197
  }
194
198
  }
195
199
 
200
+ /**
201
+ * The JavaScript emit of a program's files without a checker, for emit threads (parallel-emit.ts).
202
+ * JavaScript emit asks the checker only how decorator metadata serializes a type
203
+ * (`CheckerEmitResolver`): with `kinds`, the answers the main thread's checker gave
204
+ * (`recordMetadataKinds`), else files with decorator metadata are emitted by a `ProgramEmitter`.
205
+ */
206
+ export class JavaScriptEmitter {
207
+ private readonly host: ProgramEmitHost;
208
+ private readonly recorded: RecordedKindsEmitResolver | undefined;
209
+
210
+ constructor(program: Program, kinds: readonly number[] = []) {
211
+ const options = program.options;
212
+ const indexes = program.emitIndexes;
213
+ const references = new ReferenceResolver(
214
+ options,
215
+ program.getSourceFiles(),
216
+ new ProgramModules(program),
217
+ indexes !== undefined ? indexes.globals : undefined,
218
+ );
219
+ this.recorded = kinds.length > 0 ? new RecordedKindsEmitResolver(options, references, kinds) : undefined;
220
+ this.host = new ProgramEmitHost(program, this.recorded ?? new SyntacticEmitResolver(options, references));
221
+ }
222
+
223
+ /**
224
+ * Emits `file`, or returns undefined if its decorator metadata asked for a kind that wasn't recorded: what
225
+ * was written is then not the file's emit, and it must be emitted with the checker.
226
+ */
227
+ emit(file: SourceFile, writer: OutputWriter): EmitResult | undefined {
228
+ const host = this.host;
229
+ const recorded = this.recorded;
230
+ if (recorded !== undefined) recorded.missed = false;
231
+ host.writer = writer;
232
+ const result = emitFiles(host, [file], EmitOnly.Js);
233
+ host.writer = undefined;
234
+ return recorded !== undefined && recorded.missed ? undefined : result;
235
+ }
236
+ }
237
+
238
+ /**
239
+ * The serialization kinds of decorator metadata that the checker of `checked` gives the type references of
240
+ * `files`, in the order JavaScript emit asks for them: the type name's node id, the location's node id (-1 for
241
+ * none) and the kind, for each. Only the decorator metadata transform runs, as the first transform of emit.
242
+ */
243
+ export function recordMetadataKinds(checked: CheckedProgram, files: readonly SourceFile[]): number[] {
244
+ const program = checked.program;
245
+ const options = program.options;
246
+ const indexes = program.emitIndexes;
247
+ const references = new ReferenceResolver(
248
+ options,
249
+ program.getSourceFiles(),
250
+ new ProgramModules(program),
251
+ indexes !== undefined ? indexes.globals : undefined,
252
+ );
253
+ const resolver = new RecordingEmitResolver(options, references, checked.checker);
254
+ for (const file of files)
255
+ new DecoratorMetadata(new EmitContext(), options, resolver, new SyntaxFacts()).transformSourceFile(file);
256
+ return resolver.kinds;
257
+ }
258
+
259
+ /** A `CheckerEmitResolver` that records the serialization kinds it gives (`recordMetadataKinds`). */
260
+ class RecordingEmitResolver extends CheckerEmitResolver {
261
+ readonly kinds: number[] = [];
262
+
263
+ getTypeReferenceSerializationKind(typeName: Node, location: Node | undefined): TypeReferenceSerializationKind {
264
+ const kind = super.getTypeReferenceSerializationKind(typeName, location);
265
+ this.kinds.push(typeName.id, location === undefined ? -1 : location.id, kind);
266
+ return kind;
267
+ }
268
+ }
269
+
270
+ /** Answers serialization kinds from those recorded on the main thread; `missed` once one wasn't. */
271
+ class RecordedKindsEmitResolver extends SyntacticEmitResolver {
272
+ /** The kind and location of each type name, by the type name's node id. */
273
+ private readonly byName = new Map<number, number>();
274
+ missed = false;
275
+
276
+ constructor(
277
+ options: CompilerOptions,
278
+ referenceResolver: ReferenceResolver,
279
+ private readonly kinds: readonly number[],
280
+ ) {
281
+ super(options, referenceResolver);
282
+ for (let i = 0; i < kinds.length; i += 3) if (!this.byName.has(kinds[i]!)) this.byName.set(kinds[i]!, i);
283
+ }
284
+
285
+ getTypeReferenceSerializationKind(typeName: Node, location: Node | undefined): TypeReferenceSerializationKind {
286
+ const at = this.byName.get(typeName.id);
287
+ if (at !== undefined && this.kinds[at + 1] === (location === undefined ? -1 : location.id))
288
+ return this.kinds[at + 2]! as TypeReferenceSerializationKind;
289
+ this.missed = true;
290
+ return super.getTypeReferenceSerializationKind(typeName, location);
291
+ }
292
+ }
293
+
196
294
  /** A checked program and the emit of its files. */
197
295
  export class ProgramEmitter {
198
296
  private host: ProgramEmitHost | undefined = undefined;
@@ -354,16 +452,25 @@ export class PlainProgram extends CompilerProgram {
354
452
  const program = this.program;
355
453
  const files = program.getSourceFiles();
356
454
  const parallel = this.checked.emittedInParallel;
357
- // Checker threads emitted the files they checked (parallel-check.ts); the others are emitted here, in order.
455
+ // Checker threads emitted the files they checked (parallel-check.ts), or emit threads their JavaScript
456
+ // (parallel-emit.ts); the rest is emitted here, in order.
358
457
  const result =
359
458
  parallel === undefined
360
459
  ? this.emitter.emit(files, EmitOnly.All, writer)
361
460
  : combineEmitResults(
362
461
  files.map((file, i) => {
363
462
  const emitted = parallel[i];
364
- return emitted !== undefined
365
- ? writeHeldOutputs(emitted, writer)
366
- : this.emitter.emit([file], EmitOnly.All, writer);
463
+ // The declaration thread emitted the declarations of the files it could (parallel-emit.ts).
464
+ const ahead = this.checked.declarationsEmittedAhead;
465
+ const declarations = (): EmitResult => ahead?.[i] ?? this.emitter.emit([file], EmitOnly.Dts, writer);
466
+ if (emitted === undefined)
467
+ return ahead !== undefined
468
+ ? combineEmitResults([this.emitter.emit([file], EmitOnly.Js, writer), declarations()])
469
+ : this.emitter.emit([file], EmitOnly.All, writer);
470
+ const written = writeHeldOutputs(emitted, writer);
471
+ // Emit threads emit JavaScript only.
472
+ if (!this.checked.javaScriptEmittedOnThreads || !getEmitDeclarations(options)) return written;
473
+ return combineEmitResults([written, declarations()]);
367
474
  }),
368
475
  );
369
476
  if (options.noEmit === true) {
@@ -390,9 +497,15 @@ export function handleNoEmitOnError(program: CompilerProgram): EmitResult | unde
390
497
 
391
498
  /**
392
499
  * Config and syntax errors; without syntax errors, program, global and semantic errors too, and
393
- * declaration errors under `noEmit` (or `withDeclarationDiagnostics`).
500
+ * declaration errors under `noEmit` (or `withDeclarationDiagnostics`). With `declarationsLater`, the
501
+ * declaration errors are left to `program.emit`, which reports them under `noEmit` (without
502
+ * `noEmitOnError`) whether or not other errors were found.
394
503
  */
395
- export function getDiagnosticsOfProgram(program: CompilerProgram, withDeclarationDiagnostics: boolean): Diagnostic[] {
504
+ export function getDiagnosticsOfProgram(
505
+ program: CompilerProgram,
506
+ withDeclarationDiagnostics: boolean,
507
+ declarationsLater: boolean = false,
508
+ ): Diagnostic[] {
396
509
  const result: Diagnostic[] = [...program.getConfigFileParsingDiagnostics()];
397
510
  const configCount = result.length;
398
511
  for (const d of program.getSyntacticDiagnostics()) result.push(d);
@@ -414,6 +527,7 @@ export function getDiagnosticsOfProgram(program: CompilerProgram, withDeclaratio
414
527
  mark('semantic diagnostics');
415
528
  }
416
529
  if (
530
+ !declarationsLater &&
417
531
  (withDeclarationDiagnostics || options.noEmit === true) &&
418
532
  getEmitDeclarations(options) &&
419
533
  result.length === configCount