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
@@ -347,6 +347,11 @@ export class Diagnostic {
347
347
  readonly reportsDeprecated: boolean;
348
348
  skippedOnNoEmit = false;
349
349
  repopulateInfo: RepopulateInfo | undefined = undefined;
350
+ /**
351
+ * What `formatDiagnostic` gives for this diagnostic in `formattedReports.format`, where the checker
352
+ * thread that found it formatted it (`packResult`); `''` otherwise.
353
+ */
354
+ formatted = '';
350
355
 
351
356
  constructor(
352
357
  file: DiagnosticFile | undefined,
@@ -388,6 +393,7 @@ export class Diagnostic {
388
393
  result.relatedInformation = this.relatedInformation;
389
394
  result.skippedOnNoEmit = this.skippedOnNoEmit;
390
395
  result.repopulateInfo = this.repopulateInfo;
396
+ result.formatted = this.formatted;
391
397
  return result;
392
398
  }
393
399
  }
@@ -510,9 +516,13 @@ function compareRelatedInfo(a: readonly Diagnostic[], b: readonly Diagnostic[]):
510
516
  /** The order tsc reports diagnostics in: by file, position, code, arguments, then elaboration. */
511
517
  export function compareDiagnostics(a: Diagnostic, b: Diagnostic): number {
512
518
  if (a === b) return 0;
513
- let c = compareCodePoints(fileNameOf(a), fileNameOf(b));
514
- if (c !== 0) return c;
515
- c = a.pos - b.pos;
519
+ const c = compareCodePoints(fileNameOf(a), fileNameOf(b));
520
+ return c !== 0 ? c : compareDiagnosticsInFile(a, b);
521
+ }
522
+
523
+ /** `compareDiagnostics` of two diagnostics whose files have the same name. */
524
+ function compareDiagnosticsInFile(a: Diagnostic, b: Diagnostic): number {
525
+ let c = a.pos - b.pos;
516
526
  if (c !== 0) return c;
517
527
  c = a.end - b.end;
518
528
  if (c !== 0) return c;
@@ -527,6 +537,61 @@ export function compareDiagnostics(a: Diagnostic, b: Diagnostic): number {
527
537
  return compareRelatedInfo(a.relatedInformation, b.relatedInformation);
528
538
  }
529
539
 
540
+ /**
541
+ * `diagnostics` in the order of `compareDiagnostics`, equal ones in their order in `diagnostics`. File
542
+ * names are compared once per pair of files rather than once per pair of diagnostics: the distinct
543
+ * files are sorted by name, and each diagnostic sorts by its file's place in that order, then by the
544
+ * rest of `compareDiagnostics`. A program's diagnostics come a file at a time, so the files are found
545
+ * from the runs of one file.
546
+ */
547
+ export function sortDiagnostics(diagnostics: readonly Diagnostic[]): Diagnostic[] {
548
+ const n = diagnostics.length;
549
+ if (n < 2) return diagnostics.slice();
550
+ const fileIndices = new Map<DiagnosticFile | undefined, number>();
551
+ const files: (DiagnosticFile | undefined)[] = [];
552
+ const fileOf: number[] = [];
553
+ let last: DiagnosticFile | undefined = undefined;
554
+ let lastIndex = -1;
555
+ for (let i = 0; i < n; i++) {
556
+ const file = diagnostics[i]!.file;
557
+ if (lastIndex < 0 || file !== last) {
558
+ let k = fileIndices.get(file);
559
+ if (k === undefined) {
560
+ k = files.length;
561
+ fileIndices.set(file, k);
562
+ files.push(file);
563
+ }
564
+ last = file;
565
+ lastIndex = k;
566
+ }
567
+ fileOf.push(lastIndex);
568
+ }
569
+ // Files with the same name share a rank, as they compare equal.
570
+ const nameOf = (k: number): string => {
571
+ const file = files[k];
572
+ return file === undefined ? '' : file.fileName;
573
+ };
574
+ const byName: number[] = [];
575
+ for (let k = 0; k < files.length; k++) byName.push(k);
576
+ byName.sort((x, y) => compareCodePoints(nameOf(x), nameOf(y)));
577
+ const rank: number[] = [];
578
+ for (let k = 0; k < files.length; k++) rank.push(0);
579
+ for (let k = 1; k < byName.length; k++)
580
+ rank[byName[k]!] = rank[byName[k - 1]!]! + (nameOf(byName[k]!) === nameOf(byName[k - 1]!) ? 0 : 1);
581
+ const order: number[] = [];
582
+ for (let i = 0; i < n; i++) order.push(i);
583
+ order.sort((x, y) => {
584
+ const c = rank[fileOf[x]!]! - rank[fileOf[y]!]!;
585
+ if (c !== 0) return c;
586
+ const a = diagnostics[x]!;
587
+ const b = diagnostics[y]!;
588
+ return a === b ? 0 : compareDiagnosticsInFile(a, b);
589
+ });
590
+ const sorted: Diagnostic[] = [];
591
+ for (const i of order) sorted.push(diagnostics[i]!);
592
+ return sorted;
593
+ }
594
+
530
595
  function equalChain(a: readonly Diagnostic[], b: readonly Diagnostic[]): boolean {
531
596
  if (a.length !== b.length) return false;
532
597
  for (let i = 0; i < a.length; i++) {
@@ -625,13 +690,29 @@ function formatDiagnosticIn(diagnostic: Diagnostic, fileName: string, options: D
625
690
  return result + flattenDiagnosticMessage(diagnostic, options.newLine, options.locale) + options.newLine;
626
691
  }
627
692
 
693
+ /**
694
+ * The plain format the main thread reports a compilation's diagnostics in (`DiagnosticReporter.plainFormat`),
695
+ * which its checker threads receive a copy of and format the diagnostics they send back in
696
+ * (`Diagnostic.formatted`). Unset where diagnostics are reported with color and context, or not at all.
697
+ */
698
+ export class FormattedReports {
699
+ format: DiagnosticFormatOptions | undefined = undefined;
700
+ }
701
+
702
+ export const formattedReports = new FormattedReports();
703
+
628
704
  /** `formatDiagnostic` of each diagnostic. */
629
705
  export function formatDiagnostics(diagnostics: readonly Diagnostic[], options: DiagnosticFormatOptions): string {
630
706
  let result = '';
707
+ const formatted = options === formattedReports.format;
631
708
  // Sorted diagnostics come a file at a time: the relative name of the last file is kept.
632
709
  let lastFile: DiagnosticFile | undefined = undefined;
633
710
  let lastName = '';
634
711
  for (const diagnostic of diagnostics) {
712
+ if (formatted && diagnostic.formatted !== '') {
713
+ result += diagnostic.formatted;
714
+ continue;
715
+ }
635
716
  const file = diagnostic.file;
636
717
  if (file !== undefined && file !== lastFile) {
637
718
  lastFile = file;
@@ -216,7 +216,7 @@ export class LoadedFile {
216
216
  readonly references: LoadedReference[],
217
217
  readonly resolutionsInFile: ModeAwareCache<ResolvedModule> | undefined,
218
218
  readonly typeResolutionsInFile: ModeAwareCache<ResolvedTypeReferenceDirective> | undefined,
219
- readonly processingDiagnostics: ProcessingDiagnostic[],
219
+ readonly processingDiagnostics: readonly ProcessingDiagnostic[],
220
220
  readonly importHelpersImportSpecifier: StringLiteral | undefined,
221
221
  readonly jsxRuntimeImport: JsxRuntimeImport | undefined,
222
222
  ) {}
@@ -230,23 +230,34 @@ class SubTasksAhead {
230
230
  ) {}
231
231
  }
232
232
 
233
+ /** Read in place of a task's lists it has not created. */
234
+ const noParseTasks: readonly ParseTask[] = [];
235
+ const noTraces: readonly MessageWithArgs[] = [];
236
+ const noProcessingDiagnostics: readonly ProcessingDiagnostic[] = [];
237
+
238
+ /**
239
+ * A file to load. Its lists are created on first use: most tasks never get sub-tasks, traces or processing
240
+ * diagnostics, and large programs create hundreds of thousands of tasks.
241
+ */
233
242
  class ParseTask {
234
243
  path = '';
235
244
  file: SourceFile | undefined = undefined;
236
245
  redirectedParseTask: ParseTask | undefined = undefined;
237
- subTasks: ParseTask[] = [];
246
+ subTasks: ParseTask[] | undefined = undefined;
238
247
  loaded = false;
239
248
  startedSubTasks = false;
240
249
  metadata: SourceFileMetadata = libFileMetadata;
241
250
  resolutionsInFile: ModeAwareCache<ResolvedModule> | undefined = undefined;
242
- readonly resolutionsTrace: MessageWithArgs[] = [];
251
+ resolutionsTrace: MessageWithArgs[] | undefined = undefined;
243
252
  typeResolutionsInFile: ModeAwareCache<ResolvedTypeReferenceDirective> | undefined = undefined;
244
- readonly typeResolutionsTrace: MessageWithArgs[] = [];
245
- readonly processingDiagnostics: ProcessingDiagnostic[] = [];
253
+ typeResolutionsTrace: MessageWithArgs[] | undefined = undefined;
254
+ processingDiagnostics: ProcessingDiagnostic[] | undefined = undefined;
246
255
  importHelpersImportSpecifier: StringLiteral | undefined = undefined;
247
256
  jsxRuntimeImport: JsxRuntimeImport | undefined = undefined;
248
257
  /** The task of the same file name that was loaded instead of this one. */
249
258
  loadedTask: ParseTask | undefined = undefined;
259
+ /** Whether an import or a type reference directive adds the file (`ProcessedFiles.addedFiles`). */
260
+ readonly byImport: boolean;
250
261
 
251
262
  constructor(
252
263
  readonly normalizedFilePath: string,
@@ -257,7 +268,10 @@ class ParseTask {
257
268
  /** A JavaScript file found in `node_modules`, loaded only within `maxNodeModuleJsDepth`. */
258
269
  readonly elideOnDepth: boolean,
259
270
  readonly packageId: PackageId | undefined,
260
- ) {}
271
+ ) {
272
+ const kind = includeReason !== undefined ? includeReason.kind : -1;
273
+ this.byImport = kind === FileIncludeKind.Import || kind === FileIncludeKind.TypeReferenceDirective;
274
+ }
261
275
 
262
276
  addSubTask(
263
277
  fileName: string,
@@ -267,18 +281,40 @@ class ParseTask {
267
281
  elideOnDepth: boolean,
268
282
  packageId: PackageId | undefined,
269
283
  ): void {
270
- this.subTasks.push(
284
+ (this.subTasks ??= []).push(
271
285
  new ParseTask(normalizePath(fileName), libFile, reason, false, increaseDepth, elideOnDepth, packageId),
272
286
  );
273
287
  }
288
+
289
+ addProcessingDiagnostic(diagnostic: ProcessingDiagnostic): void {
290
+ (this.processingDiagnostics ??= []).push(diagnostic);
291
+ }
292
+
293
+ /** Where module resolution traces go under `--traceResolution`; undefined without it. */
294
+ moduleTraces(trace: boolean): MessageWithArgs[] | undefined {
295
+ return trace ? (this.resolutionsTrace ??= []) : undefined;
296
+ }
297
+
298
+ /** Where type reference resolution traces go under `--traceResolution`; undefined without it. */
299
+ typeTraces(trace: boolean): MessageWithArgs[] | undefined {
300
+ return trace ? (this.typeResolutionsTrace ??= []) : undefined;
301
+ }
274
302
  }
275
303
 
304
+ const noData: readonly ParseTaskData[] = [];
305
+
276
306
  /** The tasks of one path, by file name casing. */
277
307
  class ParseTaskData {
278
308
  readonly tasks = new Map<string, ParseTask>();
279
309
  lowestDepth = Number.MAX_SAFE_INTEGER;
280
310
  startedSubTasks = false;
281
311
  packageId: PackageId | undefined = undefined;
312
+ /** The index of this path's file in the program's files, once collected; -1 for none. */
313
+ index = -1;
314
+ /** For a deduplicated package's path, the tasks of the copy kept (`collect`). */
315
+ keptCopy: ParseTaskData | undefined = undefined;
316
+ /** The paths the file's imports and type reference directives add, once collected. */
317
+ added: readonly ParseTaskData[] = noData;
282
318
 
283
319
  constructor(task: ParseTask) {
284
320
  this.tasks.set(task.normalizedFilePath, task);
@@ -396,6 +432,11 @@ export class ProcessedFiles {
396
432
  * threads (`preparedPackageJsonLookups`); `undefined` where they may miss some.
397
433
  */
398
434
  readonly preparedPackageJsons: readonly PackageJsonLookup[] | undefined,
435
+ /**
436
+ * The files each file's imports and type reference directives added to the program, by index into
437
+ * `files`, without the file itself.
438
+ */
439
+ readonly addedFiles: readonly (readonly number[])[],
399
440
  ) {}
400
441
  }
401
442
 
@@ -423,6 +464,8 @@ export function withReplacedFile(processed: ProcessedFiles, file: SourceFile): P
423
464
  processed.redirectFilesByPath,
424
465
  processed.projectReferenceMapper,
425
466
  processed.preparedPackageJsons,
467
+ // The file has the same references (`canReplaceFile`), and keeps its index.
468
+ processed.addedFiles,
426
469
  );
427
470
  }
428
471
 
@@ -742,7 +785,7 @@ export class FileLoader {
742
785
  return undefined;
743
786
  }
744
787
  const references: LoadedReference[] = [];
745
- for (const sub of task.subTasks) {
788
+ for (const sub of task.subTasks ?? noParseTasks) {
746
789
  references.push(
747
790
  new LoadedReference(
748
791
  sub.normalizedFilePath,
@@ -769,7 +812,7 @@ export class FileLoader {
769
812
  references,
770
813
  task.resolutionsInFile,
771
814
  task.typeResolutionsInFile,
772
- task.processingDiagnostics,
815
+ task.processingDiagnostics ?? noProcessingDiagnostics,
773
816
  task.importHelpersImportSpecifier,
774
817
  task.jsxRuntimeImport,
775
818
  );
@@ -857,7 +900,7 @@ export class FileLoader {
857
900
  return;
858
901
  }
859
902
  const task = new ParseTask(absolute, undefined, reason, false, false, false, undefined);
860
- task.processingDiagnostics.push(
903
+ task.addProcessingDiagnostic(
861
904
  ProcessingDiagnostic.explainingFileInclude('', reason, resolved.message, resolved.args),
862
905
  );
863
906
  this.rootTasks.push(task);
@@ -1031,7 +1074,7 @@ export class FileLoader {
1031
1074
  }
1032
1075
  if (!taskByFileName.startedSubTasks && loadSubTasks) {
1033
1076
  taskByFileName.startedSubTasks = true;
1034
- this.start(taskByFileName.subTasks, data.lowestDepth);
1077
+ if (taskByFileName.subTasks !== undefined) this.start(taskByFileName.subTasks, data.lowestDepth);
1035
1078
  }
1036
1079
  }
1037
1080
  }
@@ -1073,7 +1116,7 @@ export class FileLoader {
1073
1116
  if (hasExtension(task.normalizedFilePath) && this.options.allowNonTsExtensions !== true) {
1074
1117
  const canonicalFileName = getCanonicalFileName(task.normalizedFilePath, fs.useCaseSensitiveFileNames);
1075
1118
  if (!this.isSupportedExtension(canonicalFileName)) {
1076
- task.processingDiagnostics.push(
1119
+ task.addProcessingDiagnostic(
1077
1120
  hasJSFileExtension(canonicalFileName)
1078
1121
  ? ProcessingDiagnostic.explainingFileInclude(
1079
1122
  '',
@@ -1122,7 +1165,7 @@ export class FileLoader {
1122
1165
  const reason = FileIncludeReason.reference(FileIncludeKind.LibReferenceDirective, task.path, i, undefined);
1123
1166
  const name = getLibFileName(file.libReferenceDirectives[i].fileName);
1124
1167
  if (name === '') {
1125
- task.processingDiagnostics.push(ProcessingDiagnostic.unknownReference(reason));
1168
+ task.addProcessingDiagnostic(ProcessingDiagnostic.unknownReference(reason));
1126
1169
  continue;
1127
1170
  }
1128
1171
  const libFile = this.pathForLibFile(name);
@@ -1140,7 +1183,7 @@ export class FileLoader {
1140
1183
  this.subTasksAhead.delete(task.normalizedFilePath);
1141
1184
  task.subTasks = ahead.subTasks;
1142
1185
  } else task.subTasks = this.subTasksOf(loaded);
1143
- for (const diagnostic of loaded.processingDiagnostics) task.processingDiagnostics.push(diagnostic.copy());
1186
+ for (const diagnostic of loaded.processingDiagnostics) task.addProcessingDiagnostic(diagnostic.copy());
1144
1187
  task.resolutionsInFile = loaded.resolutionsInFile;
1145
1188
  task.typeResolutionsInFile = loaded.typeResolutionsInFile;
1146
1189
  task.importHelpersImportSpecifier = loaded.importHelpersImportSpecifier;
@@ -1190,7 +1233,7 @@ export class FileLoader {
1190
1233
  reason,
1191
1234
  );
1192
1235
  if (typeof resolved !== 'string') {
1193
- task.processingDiagnostics.push(
1236
+ task.addProcessingDiagnostic(
1194
1237
  ProcessingDiagnostic.explainingFileInclude('', reason, resolved.message, resolved.args),
1195
1238
  );
1196
1239
  return;
@@ -1216,7 +1259,7 @@ export class FileLoader {
1216
1259
  containingFile,
1217
1260
  mode,
1218
1261
  redirect?.resolved,
1219
- task.typeResolutionsTrace,
1262
+ task.typeTraces(this.options.traceResolution === true),
1220
1263
  );
1221
1264
  resolutions.set(modeAwareKey(reference.fileName, mode), resolved);
1222
1265
  const reason = FileIncludeReason.reference(FileIncludeKind.TypeReferenceDirective, task.path, i, undefined);
@@ -1230,7 +1273,7 @@ export class FileLoader {
1230
1273
  resolved.packageId,
1231
1274
  );
1232
1275
  } else {
1233
- task.processingDiagnostics.push(ProcessingDiagnostic.unknownReference(reason));
1276
+ task.addProcessingDiagnostic(ProcessingDiagnostic.unknownReference(reason));
1234
1277
  }
1235
1278
  }
1236
1279
  task.typeResolutionsInFile = resolutions;
@@ -1247,7 +1290,7 @@ export class FileLoader {
1247
1290
  task.normalizedFilePath,
1248
1291
  ModuleKind.None,
1249
1292
  undefined,
1250
- task.typeResolutionsTrace,
1293
+ task.typeTraces(this.options.traceResolution === true),
1251
1294
  );
1252
1295
  resolutions.set(modeAwareKey(name, ModuleKind.None), resolved);
1253
1296
  if (resolved.resolvedFileName !== '') {
@@ -1260,7 +1303,7 @@ export class FileLoader {
1260
1303
  resolved.packageId,
1261
1304
  );
1262
1305
  } else {
1263
- task.processingDiagnostics.push(
1306
+ task.addProcessingDiagnostic(
1264
1307
  ProcessingDiagnostic.explainingFileInclude(
1265
1308
  '',
1266
1309
  FileIncludeReason.automaticTypeDirective(name, undefined),
@@ -1308,7 +1351,7 @@ export class FileLoader {
1308
1351
  containingFile,
1309
1352
  mode,
1310
1353
  redirect?.resolved,
1311
- task.resolutionsTrace,
1354
+ task.moduleTraces(this.options.traceResolution === true),
1312
1355
  );
1313
1356
  resolutions.set(modeAwareKey(moduleName, mode), resolved);
1314
1357
  const resolvedFileName = resolved.resolvedFileName;
@@ -1370,16 +1413,24 @@ export class FileLoader {
1370
1413
  const packageIdToSourceFile = new Map<string, SourceFile>();
1371
1414
  const checkCasing = this.options.forceConsistentCasingInFileNames !== false;
1372
1415
  const seen = new Map<ParseTaskData, string>();
1373
-
1374
- const collectFiles = (tasks: readonly ParseTask[]): void => {
1416
+ // The paths each file's imports and type reference directives add (the tasks of each path), for
1417
+ // `ProcessedFiles.addedFiles`: recorded while collecting visits them, as their files are only known
1418
+ // once every file is collected.
1419
+ const fileData: ParseTaskData[] = [];
1420
+ const libData = new Map<SourceFile, ParseTaskData>();
1421
+ const packageIdToData = new Map<string, ParseTaskData>();
1422
+
1423
+ const collectFiles = (tasks: readonly ParseTask[], added: ParseTaskData[] | undefined): void => {
1375
1424
  for (let task of tasks) {
1376
1425
  const includeReason = task.includeReason;
1426
+ const byImport = task.byImport;
1377
1427
  // Automatic type directive tasks are an implementation detail, not a reason.
1378
1428
  if (task.redirectedParseTask === undefined && !task.isForAutomaticTypeDirective) {
1379
1429
  if (task.loadedTask !== undefined) task = task.loadedTask;
1380
1430
  if (includeReason !== undefined) addIncludeReason(includeProcessor, task, includeReason);
1381
1431
  }
1382
1432
  const data = this.taskDataByPath.get(task.path);
1433
+ if (added !== undefined && data !== undefined && byImport) added.push(data);
1383
1434
  if (!task.loaded || data === undefined) continue;
1384
1435
 
1385
1436
  const checkedName = seen.get(data);
@@ -1412,8 +1463,8 @@ export class FileLoader {
1412
1463
  }
1413
1464
  }
1414
1465
 
1415
- for (const trace of task.typeResolutionsTrace) host.trace(trace);
1416
- for (const trace of task.resolutionsTrace) host.trace(trace);
1466
+ for (const trace of task.typeResolutionsTrace ?? noTraces) host.trace(trace);
1467
+ for (const trace of task.resolutionsTrace ?? noTraces) host.trace(trace);
1417
1468
 
1418
1469
  const file = task.file;
1419
1470
  const packageId = data.packageId;
@@ -1434,13 +1485,18 @@ export class FileLoader {
1434
1485
  ),
1435
1486
  );
1436
1487
  filesByPath.set(task.path, packageIdFile);
1488
+ data.keptCopy = packageIdToData.get(key);
1437
1489
  if (data.lowestDepth > 0) sourceFilesFoundSearchingNodeModules.add(task.path);
1438
1490
  continue;
1439
1491
  }
1440
- if (file !== undefined) packageIdToSourceFile.set(key, file);
1492
+ if (file !== undefined) {
1493
+ packageIdToSourceFile.set(key, file);
1494
+ packageIdToData.set(key, data);
1495
+ }
1441
1496
  }
1442
1497
 
1443
- if (task.subTasks.length > 0) collectFiles(task.subTasks);
1498
+ let own: ParseTaskData[] | undefined = undefined;
1499
+ if (task.subTasks !== undefined && task.subTasks.length > 0) collectFiles(task.subTasks, (own = []));
1444
1500
 
1445
1501
  const redirected = task.redirectedParseTask;
1446
1502
  if (redirected !== undefined) {
@@ -1449,21 +1505,26 @@ export class FileLoader {
1449
1505
  }
1450
1506
  if (task.isForAutomaticTypeDirective) {
1451
1507
  typeResolutionsInFile.set(task.path, task.typeResolutionsInFile);
1452
- for (const diagnostic of task.processingDiagnostics) includeProcessor.addProcessingDiagnostic(diagnostic);
1508
+ for (const diagnostic of task.processingDiagnostics ?? noProcessingDiagnostics)
1509
+ includeProcessor.addProcessingDiagnostic(diagnostic);
1453
1510
  continue;
1454
1511
  }
1455
1512
  const path = task.path;
1456
- for (const diagnostic of task.processingDiagnostics) includeProcessor.addProcessingDiagnostic(diagnostic);
1513
+ for (const diagnostic of task.processingDiagnostics ?? noProcessingDiagnostics)
1514
+ includeProcessor.addProcessingDiagnostic(diagnostic);
1457
1515
  if (file === undefined) {
1458
1516
  missingFiles.push(task.normalizedFilePath);
1459
1517
  continue;
1460
1518
  }
1461
1519
  if (task.libFile !== undefined) {
1462
1520
  libFiles.push(file);
1521
+ libData.set(file, data);
1463
1522
  libFilesByPath.set(path, task.libFile);
1464
1523
  } else {
1465
1524
  files.push(file);
1525
+ fileData.push(data);
1466
1526
  }
1527
+ if (own !== undefined) data.added = own;
1467
1528
  filesByPath.set(path, file);
1468
1529
  resolvedModules.set(path, task.resolutionsInFile);
1469
1530
  typeResolutionsInFile.set(path, task.typeResolutionsInFile);
@@ -1475,13 +1536,28 @@ export class FileLoader {
1475
1536
  if (data.lowestDepth > 0) sourceFilesFoundSearchingNodeModules.add(path);
1476
1537
  }
1477
1538
  };
1478
- collectFiles(this.rootTasks);
1539
+ collectFiles(this.rootTasks, undefined);
1479
1540
 
1480
1541
  const priorities = new Map<SourceFile, number>();
1481
1542
  for (const file of libFiles) priorities.set(file, this.libFilePriority(file));
1482
1543
  libFiles.sort((a, b) => (priorities.get(a) ?? 0) - (priorities.get(b) ?? 0));
1483
1544
  const allFiles = [...libFiles, ...files];
1484
1545
  for (const redirect of redirectFilesByPath.values()) redirect.index += libFiles.length;
1546
+ // The files a file adds, without reading its resolutions again or looking up their paths.
1547
+ const dataOfFiles: ParseTaskData[] = [];
1548
+ for (const file of libFiles) dataOfFiles.push(libData.get(file)!);
1549
+ for (const data of fileData) dataOfFiles.push(data);
1550
+ for (let i = 0; i < dataOfFiles.length; i++) dataOfFiles[i]!.index = i;
1551
+ const addedFiles: number[][] = [];
1552
+ for (let i = 0; i < dataOfFiles.length; i++) {
1553
+ const added: number[] = [];
1554
+ for (const data of dataOfFiles[i]!.added) {
1555
+ const kept = data.keptCopy;
1556
+ const k = kept !== undefined ? kept.index : data.index;
1557
+ if (k >= 0 && k !== i) added.push(k);
1558
+ }
1559
+ addedFiles.push(added);
1560
+ }
1485
1561
 
1486
1562
  const keys = [...this.libResolutions.keys()].sort();
1487
1563
  for (const key of keys) {
@@ -1511,6 +1587,7 @@ export class FileLoader {
1511
1587
  redirectFilesByPath,
1512
1588
  this.mapper,
1513
1589
  this.tookPreparedResolutions ? preparedPackageJsonLookups() : noPackageJsonLookups,
1590
+ addedFiles,
1514
1591
  );
1515
1592
  }
1516
1593
  }
@@ -1864,8 +1864,21 @@ class EmitFilesHandler {
1864
1864
  const files = program.program.getSourceFiles();
1865
1865
  const emittedOnCheckers = this.emittedOnCheckers();
1866
1866
  const wrapped = this.wrap(writer);
1867
- const result =
1868
- emittedOnCheckers.size === 0
1867
+ const ahead = this.declarationsEmittedAhead();
1868
+ const result = program.checked.javaScriptEmittedOnThreads
1869
+ ? combineEmitResults(
1870
+ files.map((file) =>
1871
+ this.emitAfterEmitThreads(
1872
+ file,
1873
+ EmitOnly.All,
1874
+ emittedOnCheckers.get(file),
1875
+ ahead.get(file),
1876
+ writer,
1877
+ wrapped,
1878
+ ),
1879
+ ),
1880
+ )
1881
+ : emittedOnCheckers.size === 0
1869
1882
  ? program.emitter.emit(files, EmitOnly.All, wrapped)
1870
1883
  : combineEmitResults(
1871
1884
  files.map((file) => {
@@ -1901,20 +1914,76 @@ class EmitFilesHandler {
1901
1914
  /**
1902
1915
  * The emit results of the files checker threads emitted right after checking them (parallel-check.ts),
1903
1916
  * which are not emitted again here. Checker threads emit every file of a program without old build
1904
- * info, with the outputs its options select, as its pending emit asks.
1917
+ * info, with the outputs its options select, as its pending emit asks. Emit threads emit only the
1918
+ * JavaScript of the files (`javaScriptEmittedOnThreads`, `emitAfterEmitThreads`).
1905
1919
  */
1906
1920
  private emittedOnCheckers(): Map<SourceFile, EmitResult> {
1921
+ return this.byFile(this.program.checked.emittedInParallel);
1922
+ }
1923
+
1924
+ /** The declaration emit of the files the declaration thread emitted (parallel-emit.ts). */
1925
+ private declarationsEmittedAhead(): Map<SourceFile, EmitResult> {
1926
+ return this.byFile(this.program.checked.declarationsEmittedAhead);
1927
+ }
1928
+
1929
+ /** The results of `results`, by file index, by file. */
1930
+ private byFile(results: readonly (EmitResult | undefined)[] | undefined): Map<SourceFile, EmitResult> {
1907
1931
  const result = new Map<SourceFile, EmitResult>();
1908
- const emitted = this.program.checked.emittedInParallel;
1909
- if (emitted === undefined) return result;
1932
+ if (results === undefined) return result;
1910
1933
  const files = this.program.program.getSourceFiles();
1911
1934
  for (let i = 0; i < files.length; i++) {
1912
- const fileResult = emitted[i];
1935
+ const fileResult = results[i];
1913
1936
  if (fileResult !== undefined) result.set(files[i]!, fileResult);
1914
1937
  }
1915
1938
  return result;
1916
1939
  }
1917
1940
 
1941
+ /**
1942
+ * The emit of `file` with `emitOnly` where emit threads emitted the JavaScript of the program's files
1943
+ * while this thread checked it (`javaScriptEmittedOnThreads`, parallel-emit.ts): the outputs an emit thread
1944
+ * emitted of its JavaScript (`javaScript`) and the declaration thread of its declarations (`declarations`),
1945
+ * and the rest emitted here. A file no thread emitted is emitted here as without the threads.
1946
+ */
1947
+ private emitAfterEmitThreads(
1948
+ file: SourceFile,
1949
+ emitOnly: EmitOnly,
1950
+ javaScript: EmitResult | undefined,
1951
+ declarations: EmitResult | undefined,
1952
+ writer: OutputWriter,
1953
+ wrapped: OutputWriter,
1954
+ ): EmitResult {
1955
+ const program = this.program;
1956
+ if (wrapped instanceof SignatureRecordingWriter) wrapped.file = file;
1957
+ if (javaScript === undefined && declarations === undefined) return program.emitter.emit([file], emitOnly, wrapped);
1958
+ const results: EmitResult[] = [];
1959
+ if (emitOnly !== EmitOnly.Dts)
1960
+ results.push(
1961
+ javaScript !== undefined
1962
+ ? this.writeThreadOutputs(file, javaScript, writer)
1963
+ : program.emitter.emit([file], EmitOnly.Js, writer),
1964
+ );
1965
+ if (emitOnly !== EmitOnly.Js && getEmitDeclarations(program.snapshot.options))
1966
+ results.push(
1967
+ declarations !== undefined
1968
+ ? this.writeThreadOutputs(file, declarations, writer)
1969
+ : program.emitter.emit([file], EmitOnly.Dts, wrapped),
1970
+ );
1971
+ return combineEmitResults(results);
1972
+ }
1973
+
1974
+ /**
1975
+ * Writes the outputs a thread emitted for `file` with `writer`, as this thread would have written them:
1976
+ * its declaration signatures are recorded (`recordDeclarationSignatures`), and the unchanged declaration
1977
+ * files of a composite program are left unwritten.
1978
+ */
1979
+ private writeThreadOutputs(file: SourceFile, emitted: EmitResult, writer: OutputWriter): EmitResult {
1980
+ const skips = this.recordDeclarationSignatures(file, emitted.declarationSignatures);
1981
+ return writeHeldOutputs(
1982
+ emitted,
1983
+ skips === undefined ? writer : new HeldDeclarationWriter(writer, skips, this.program.host),
1984
+ );
1985
+ }
1986
+
1918
1987
  /** Records declaration signatures when declarations are emitted. */
1919
1988
  private wrap(writer: OutputWriter): OutputWriter {
1920
1989
  return getEmitDeclarations(this.program.snapshot.options) ? new SignatureRecordingWriter(this, writer) : writer;
@@ -1926,6 +1995,7 @@ class EmitFilesHandler {
1926
1995
  collectAllAffectedFiles(program);
1927
1996
  const wrapped = this.wrap(writer);
1928
1997
  const emittedOnCheckers = this.emittedOnCheckers();
1998
+ const ahead = this.declarationsEmittedAhead();
1929
1999
  for (const [path, emitKind] of snapshot.affectedFilesPendingEmit) {
1930
2000
  const affectedFile = program.program.getSourceFileByPath(path);
1931
2001
  if (affectedFile === undefined || !program.program.sourceFileMayBeEmitted(affectedFile, false)) {
@@ -1939,13 +2009,10 @@ class EmitFilesHandler {
1939
2009
  if (!this.isForDtsErrors) {
1940
2010
  const emitOnly = emitOnlyOfPendingKind(pendingKind);
1941
2011
  const emitted = emittedOnCheckers.get(affectedFile);
1942
- if (emitted !== undefined) {
1943
- const skips = this.recordDeclarationSignatures(affectedFile, emitted.declarationSignatures);
1944
- result = writeHeldOutputs(
1945
- emitted,
1946
- skips === undefined ? writer : new HeldDeclarationWriter(writer, skips, program.host),
1947
- );
1948
- } else {
2012
+ if (program.checked.javaScriptEmittedOnThreads)
2013
+ result = this.emitAfterEmitThreads(affectedFile, emitOnly, emitted, ahead.get(affectedFile), writer, wrapped);
2014
+ else if (emitted !== undefined) result = this.writeThreadOutputs(affectedFile, emitted, writer);
2015
+ else {
1949
2016
  if (wrapped instanceof SignatureRecordingWriter) wrapped.file = affectedFile;
1950
2017
  result = program.emitter.emit([affectedFile], emitOnly, wrapped);
1951
2018
  }
@@ -169,6 +169,38 @@ export class ClusteredIntLinkTable {
169
169
  }
170
170
  }
171
171
 
172
+ /** Ids in pages of `IdSetPaging.Ids` ids, one bit each; a page is created by the first id added to it. */
173
+ export class PagedIdSet {
174
+ readonly pages: (Int32Array | undefined)[] = [];
175
+
176
+ has(id: number): boolean {
177
+ const page = this.pages[id >> IdSetPaging.Bits];
178
+ return page !== undefined && (page[(id >> 5) & IdSetPaging.WordMask]! & (1 << (id & 31))) !== 0;
179
+ }
180
+
181
+ add(id: number): void {
182
+ const index = id >> IdSetPaging.Bits;
183
+ let page = this.pages[index];
184
+ if (page === undefined) {
185
+ while (this.pages.length <= index) this.pages.push(undefined);
186
+ page = new Int32Array(IdSetPaging.Words);
187
+ this.pages[index] = page;
188
+ }
189
+ page[(id >> 5) & IdSetPaging.WordMask] |= 1 << (id & 31);
190
+ }
191
+
192
+ clear(): void {
193
+ this.pages.length = 0;
194
+ }
195
+ }
196
+
197
+ /** `PagedIdSet` pages: 8192 ids in 256 words. */
198
+ const enum IdSetPaging {
199
+ Bits = 13,
200
+ Words = 1 << (Bits - 5),
201
+ WordMask = Words - 1,
202
+ }
203
+
172
204
  /** The number of set bits of a 32-bit value. */
173
205
  function bitCount(x: number): number {
174
206
  x = x - ((x >>> 1) & 0x55555555);