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
@@ -6,7 +6,7 @@ import { parentPort } from 'node:worker_threads';
6
6
  import { publish, sharesPublishedGraphs } from '@scriptc/threads';
7
7
  import { reserveNodeIds, shareNodeIds } from './ast.js';
8
8
  import type { OutputWriter } from './compile-program.js';
9
- import { useThreadTextCaches } from './diagnostics.js';
9
+ import { type DiagnosticFormatOptions, formattedReports, useThreadTextCaches } from './diagnostics.js';
10
10
  import { compareReferencesOfThread, type ReferenceJob, referenceThreads } from './incremental.js';
11
11
  import {
12
12
  CheckerResult,
@@ -16,6 +16,7 @@ import {
16
16
  parallelCheck,
17
17
  recycleFiles,
18
18
  } from './parallel-check.js';
19
+ import { emitFromQueue, parallelEmit, postedEmitResult } from './parallel-emit.js';
19
20
  import { type LoadMessage, parallelParse } from './parallel-parse.js';
20
21
  import type { ParseThread } from './parse-thread.js';
21
22
  import { useLazyJSDocTable } from './parser.js';
@@ -38,7 +39,7 @@ export interface ThreadDrivers {
38
39
  export type ThreadSpawn = (data: ThreadData) => Worker;
39
40
 
40
41
  /** The `workerData` of a thread of tsc-ts. */
41
- export type ThreadData = CheckerThreadData | ParseThreadData | ReferenceThreadData;
42
+ export type ThreadData = CheckerThreadData | EmitThreadData | ParseThreadData | ReferenceThreadData;
42
43
 
43
44
  export interface CheckerThreadData {
44
45
  readonly kind: 'check';
@@ -54,6 +55,21 @@ export interface CheckerThreadData {
54
55
  readonly emits: boolean;
55
56
  readonly emitKinds: number[] | undefined;
56
57
  readonly checking: boolean;
58
+ readonly units: number[];
59
+ /** The main thread's report format (`formattedReports`), which the thread formats its diagnostics in. */
60
+ readonly reportFormat: DiagnosticFormatOptions | undefined;
61
+ }
62
+
63
+ export interface EmitThreadData {
64
+ readonly kind: 'emit';
65
+ /** The shared, published program. */
66
+ readonly program: Program;
67
+ readonly index: number;
68
+ readonly counter: SharedArrayBuffer;
69
+ readonly files: number[];
70
+ readonly kinds: number[];
71
+ readonly nodeIds: number;
72
+ readonly symbolIds: number;
57
73
  }
58
74
 
59
75
  export interface ParseThreadData {
@@ -75,7 +91,19 @@ export interface ReferenceThreadData {
75
91
  */
76
92
  export function startThreads(spawn: ThreadSpawn, drivers: ThreadDrivers, args: readonly string[]): void {
77
93
  parallelCheck.writer = drivers.outputWriter();
78
- parallelCheck.spawn = (program, index, counter, order, nodeIds, symbolIds, reserved, emits, emitKinds, checking) =>
94
+ parallelCheck.spawn = (
95
+ program,
96
+ index,
97
+ counter,
98
+ order,
99
+ nodeIds,
100
+ symbolIds,
101
+ reserved,
102
+ emits,
103
+ emitKinds,
104
+ checking,
105
+ units,
106
+ ) =>
79
107
  spawn({
80
108
  kind: 'check',
81
109
  program,
@@ -89,8 +117,12 @@ export function startThreads(spawn: ThreadSpawn, drivers: ThreadDrivers, args: r
89
117
  emits,
90
118
  emitKinds,
91
119
  checking,
120
+ units,
121
+ reportFormat: formattedReports.format,
92
122
  });
93
123
  if (sharesPublishedGraphs) {
124
+ parallelEmit.spawn = (program, index, counter, files, kinds, nodeIds, symbolIds) =>
125
+ spawn({ kind: 'emit', program, index, counter, files, kinds, nodeIds, symbolIds });
94
126
  parallelParse.spawn = (configFileName, counter, index) =>
95
127
  spawn({ kind: 'parse', configFileName, args, counter, index });
96
128
  // The main thread compares the files a reference thread that failed did not.
@@ -109,6 +141,7 @@ export function runThread(data: unknown, drivers: ThreadDrivers): void {
109
141
  const kind = (data as { kind: string }).kind;
110
142
  if (kind === 'parse') runParseThread(data as ParseThreadData, drivers);
111
143
  else if (kind === 'references') runReferenceThread(data as ReferenceThreadData);
144
+ else if (kind === 'emit') runEmitThread(data as EmitThreadData, drivers);
112
145
  else runCheckerThread(data as CheckerThreadData, drivers);
113
146
  }
114
147
 
@@ -127,12 +160,36 @@ function runReferenceThread(data: ReferenceThreadData): void {
127
160
  mark('references compared');
128
161
  }
129
162
 
163
+ /** An emit thread (parallel-emit.ts): emits JavaScript, without a checker. */
164
+ function runEmitThread(job: EmitThreadData, drivers: ThreadDrivers): void {
165
+ setTimelineThread('e' + String(job.index));
166
+ mark('thread start');
167
+ // As on a checker thread, the shared program is never written.
168
+ reserveNodeIds(job.nodeIds);
169
+ reserveSymbolIds(job.symbolIds);
170
+ assignSymbolIdsAtCreation();
171
+ useLazyJSDocTable();
172
+ useThreadTextCaches();
173
+ const program = publish(job.program);
174
+ const result = emitFromQueue(
175
+ program,
176
+ job.index,
177
+ new Int32Array(job.counter),
178
+ job.files,
179
+ job.kinds,
180
+ drivers.outputWriter(),
181
+ );
182
+ parentPort!.postMessage(postedEmitResult(result, program));
183
+ mark('result posted');
184
+ }
185
+
130
186
  /** A checker thread (parallel-check.ts). */
131
187
  function runCheckerThread(job: CheckerThreadData, drivers: ThreadDrivers): void {
132
188
  const startedAt = Date.now();
133
189
  setTimelineThread('c' + String(job.index));
134
190
  mark('thread start');
135
191
  parallelCheck.writer = drivers.outputWriter();
192
+ formattedReports.format = job.reportFormat;
136
193
  let program: Program | undefined = undefined;
137
194
  if (job.program !== undefined) {
138
195
  // The shared program is never written: new ids continue above its own, and lazily parsed JSDoc
@@ -161,6 +218,7 @@ function runCheckerThread(job: CheckerThreadData, drivers: ThreadDrivers): void
161
218
  job.emits,
162
219
  job.emitKinds,
163
220
  job.checking,
221
+ job.units,
164
222
  );
165
223
  // Packed results are copied: nothing this thread allocated stays reachable after it ends.
166
224
  if (packsResults) parentPort!.postMessage(packResult(result, program!));
@@ -55,15 +55,20 @@ import {
55
55
  SystemFileSystem,
56
56
  } from './tsc-cli.js';
57
57
  import {
58
+ checkWhileThreadsEmit,
58
59
  emitFilesAndReportErrors,
60
+ emitKindsOf,
61
+ emitsOnThreads,
59
62
  exitStatusOf,
60
63
  rechecksOnThreads,
64
+ reportEmitAndErrors,
61
65
  Statistics,
62
66
  statisticsOf,
63
67
  SystemTimeStamps,
64
68
  } from './tsc-compile.js';
65
69
  import { sharesPublishedGraphs } from '@scriptc/threads';
66
70
  import { checkerThreads, parseThreads, shareWithCheckers, startParallelCheck } from './parallel-check.js';
71
+ import { emitThreads } from './parallel-emit.js';
67
72
  import { LoadKind, ParseSession, clearPreparedFiles, parallelParse, reportPreparedFiles } from './parallel-parse.js';
68
73
  import { assignSymbolIdsAtCreation } from './symbol.js';
69
74
 
@@ -792,10 +797,58 @@ export class Orchestrator {
792
797
  });
793
798
  return true;
794
799
  }
800
+ if (mayUseThreads && this.emitsOnThreads(checked, oldSnapshot))
801
+ return this.checkWhileThreadsEmit(task, checked, times, oldSnapshot, then);
795
802
  this.emitAndReport(task, checked, times, oldSnapshot, undefined);
796
803
  return false;
797
804
  }
798
805
 
806
+ /**
807
+ * Whether `checkWhileThreadsEmit` builds the project of `checked`: one whose emit threads apply
808
+ * (`emitsOnThreads`), that emits every file (one without old build info, or one that is not incremental)
809
+ * and that emits enough for the threads (`emitThreads`).
810
+ */
811
+ private emitsOnThreads(checked: CheckedProgram, oldSnapshot: Snapshot | undefined): boolean {
812
+ const options = checked.program.options;
813
+ return (
814
+ emitsOnThreads(options) &&
815
+ (oldSnapshot === undefined || !isIncremental(options)) &&
816
+ emitThreads(checked.program, options.singleThreaded) > 0
817
+ );
818
+ }
819
+
820
+ /**
821
+ * The rest of `checkAndEmit` with one checker for a project that `emitsOnThreads` accepts, as `tsc -p` compiles
822
+ * (`compileWithEmitThreads`, tsc-compile.ts): this thread checks the program while emit threads emit its
823
+ * JavaScript, and the declaration thread its declarations where that pays (parallel-emit.ts). Where its pending
824
+ * emit leaves too little JavaScript for the threads (`emitDeclarationOnly`), this thread checks and emits it.
825
+ * Returns whether the threads emit, and then calls `then` once the project is emitted.
826
+ */
827
+ private checkWhileThreadsEmit(
828
+ task: BuildTask,
829
+ checked: CheckedProgram,
830
+ times: CompileTimes,
831
+ oldSnapshot: Snapshot | undefined,
832
+ then: () => void,
833
+ ): boolean {
834
+ const program = checked.program;
835
+ const options = program.options;
836
+ // The program is shared before its checker does more than merge the globals.
837
+ shareWithCheckers(checked);
838
+ const incrementalProgram = this.incrementalProgramOf(task, checked, times, oldSnapshot);
839
+ const emitKinds = emitKindsOf(incrementalProgram);
840
+ const threads = emitThreads(program, options.singleThreaded, emitKinds);
841
+ if (threads === 0) {
842
+ this.emitAndReport(task, checked, times, oldSnapshot, undefined, incrementalProgram);
843
+ return false;
844
+ }
845
+ checkWhileThreadsEmit(incrementalProgram, threads, emitKinds, (diagnostics) => {
846
+ this.emitAndReport(task, checked, times, oldSnapshot, undefined, incrementalProgram, diagnostics);
847
+ then();
848
+ });
849
+ return true;
850
+ }
851
+
799
852
  /**
800
853
  * The rest of `checkAndEmit` for a project with old build info on checker threads, as `tsc -p` rebuilds
801
854
  * (`recheckInParallel`, tsc-compile.ts): the program is shared with the threads first, then the
@@ -869,22 +922,23 @@ export class Orchestrator {
869
922
  oldSnapshot: Snapshot | undefined,
870
923
  /** The wall time of checker threads that checked the program, as reported. */
871
924
  parallelCheckTime: number | undefined,
872
- /** The incremental program, where `recheckOnThreads` made it. */
925
+ /** The incremental program, where `recheckOnThreads` or `checkWhileThreadsEmit` made it. */
873
926
  made?: IncrementalProgram,
927
+ /** The program's diagnostics, where `checkWhileThreadsEmit` collected them. */
928
+ diagnostics?: Diagnostic[],
874
929
  ): void {
875
930
  const resolved = task.resolved!;
876
931
  const sys = this.sys;
877
932
  const program = checked.program;
878
933
  const incrementalProgram = made ?? this.incrementalProgramOf(task, checked, times, oldSnapshot);
879
934
 
880
- const result = emitFilesAndReportErrors(
881
- sys,
882
- incrementalProgram,
883
- new TaskDiagnosticReporter(task, this.reporter, sys, this.locale),
884
- new QuietSummary(sys, {}, this.locale),
885
- new TaskWriter(this, task),
886
- this.locale,
887
- );
935
+ const reporter = new TaskDiagnosticReporter(task, this.reporter, sys, this.locale);
936
+ const summary = new QuietSummary(sys, {}, this.locale);
937
+ const writer = new TaskWriter(this, task);
938
+ const result =
939
+ diagnostics === undefined
940
+ ? emitFilesAndReportErrors(sys, incrementalProgram, reporter, summary, writer, this.locale)
941
+ : reportEmitAndErrors(sys, incrementalProgram, diagnostics, reporter, summary, writer, this.locale);
888
942
  task.exitStatus = exitStatusOf(result);
889
943
  if (parallelCheckTime !== undefined) times.checkTime = parallelCheckTime;
890
944
  const options = program.options;
@@ -199,6 +199,11 @@ export class DiagnosticReporter {
199
199
  else this.sys.write(formatDiagnostic(diagnostic, this.format));
200
200
  }
201
201
 
202
+ /** The format `reportAll` writes diagnostics in, where it writes them plainly; undefined otherwise. */
203
+ plainFormat(): DiagnosticFormatOptions | undefined {
204
+ return this.quiet || this.pretty ? undefined : this.format;
205
+ }
206
+
202
207
  /** Writes the diagnostics at once: a write for each costs more than formatting it. */
203
208
  reportAll(diagnostics: readonly Diagnostic[]): void {
204
209
  if (this.quiet || diagnostics.length === 0) return;
@@ -11,9 +11,11 @@ import {
11
11
  getDiagnosticsOfProgram,
12
12
  OutputWriter,
13
13
  PlainProgram,
14
+ recordMetadataKinds,
14
15
  } from './compile-program.js';
15
- import type { Diagnostic, Locale } from './diagnostics.js';
16
- import { EmitResult } from './emitter.js';
16
+ import { type Diagnostic, formattedReports, type Locale } from './diagnostics.js';
17
+ import { EmitOnly, EmitResult } from './emitter.js';
18
+ import { type CompilerOptions, getEmitDeclarations } from './options.js';
17
19
  import {
18
20
  FileBuildInfoReader,
19
21
  IncrementalProgram,
@@ -21,7 +23,21 @@ import {
21
23
  type Snapshot,
22
24
  TimeStampHost,
23
25
  } from './incremental.js';
24
- import { checkerThreads, shareWithCheckers, startParallelCheck, traceSingleChecker } from './parallel-check.js';
26
+ import {
27
+ checkerThreads,
28
+ parallelCheck,
29
+ shareWithCheckers,
30
+ startParallelCheck,
31
+ traceSingleChecker,
32
+ } from './parallel-check.js';
33
+ import {
34
+ declarationThreadPays,
35
+ emitThreads,
36
+ startDeclarationErrorThread,
37
+ startDeclarationThread,
38
+ startMetadataEmit,
39
+ startParallelEmit,
40
+ } from './parallel-emit.js';
25
41
  import { sharesPublishedGraphs } from '@scriptc/threads';
26
42
  import { getNormalizedAbsolutePath } from './path.js';
27
43
  import { sortAndDeduplicateDiagnostics } from './program.js';
@@ -122,6 +138,213 @@ export function compileThreads(_sys: System, work: CompileWork): number {
122
138
  return checkerThreads(options.checkers, options.singleThreaded, work.program);
123
139
  }
124
140
 
141
+ /** The number of threads to emit JavaScript on while this thread checks (`compileWithEmitThreads`), or 0. */
142
+ export function compileEmitThreads(work: CompileWork): number {
143
+ return emitThreads(work.program, work.program.options.singleThreaded);
144
+ }
145
+
146
+ /**
147
+ * Like `compile`, with this thread checking the program while `threads` threads emit its JavaScript
148
+ * (parallel-emit.ts); calls `done` with the exit status once it is emitted and reported. Returns
149
+ * false, and `compile` runs instead, where emit threads don't apply (`emitsOnThreads`) and for an
150
+ * incremental program with build info, whose rebuild emits what changed. An incremental program
151
+ * without build info emits every file, as its pending emit holds (`emitKindsOf`).
152
+ */
153
+ export function compileWithEmitThreads(
154
+ sys: System,
155
+ work: CompileWork,
156
+ threads: number,
157
+ done: (status: ExitStatus) => void,
158
+ ): boolean {
159
+ const program = work.program;
160
+ if (!emitsOnThreads(program.options)) return false;
161
+ if (work.incremental && !checksIncrementalOnThreads(sys, program.commandLine.buildInfoFileName())) return false;
162
+ const times = new CompileTimes();
163
+ times.configTime = work.configTime;
164
+ times.parseTime = work.parseTime;
165
+ const bindStart = performance.now();
166
+ const checked = new CheckedProgram(program);
167
+ times.bindTime = performance.now() - bindStart;
168
+ // The program is shared before its checker does more than merge the globals.
169
+ shareWithCheckers(checked);
170
+ let compilerProgram: CompilerProgram;
171
+ if (work.incremental) {
172
+ const old = oldSnapshotOf(sys, work, times);
173
+ const changesStart = performance.now();
174
+ compilerProgram = new IncrementalProgram(checked, times, old, new SystemTimeStamps(sys));
175
+ times.changesComputeTime = performance.now() - changesStart;
176
+ } else compilerProgram = new PlainProgram(checked, times);
177
+ const report = (diagnostics: Diagnostic[]): void => {
178
+ const result = reportEmitAndErrors(
179
+ sys,
180
+ compilerProgram,
181
+ diagnostics,
182
+ work.reporter,
183
+ work.errorSummary,
184
+ new SystemWriter(sys),
185
+ outputLocale(program.options),
186
+ );
187
+ traceSingleChecker(checked);
188
+ done(emitStatusAndReportStatistics(sys, compilerProgram, result));
189
+ };
190
+ const emitKinds = emitKindsOf(compilerProgram);
191
+ if (emitKinds !== undefined) threads = emitThreads(program, program.options.singleThreaded, emitKinds);
192
+ if (threads > 0) checkWhileThreadsEmit(compilerProgram, threads, emitKinds, report);
193
+ else {
194
+ const diagnostics = getDiagnosticsOfProgram(compilerProgram, false);
195
+ mark('diagnostics collected');
196
+ report(diagnostics);
197
+ }
198
+ return true;
199
+ }
200
+
201
+ /**
202
+ * Whether emit threads (parallel-emit.ts) can emit a program with `options` while this thread checks it:
203
+ * not one that emits nothing or waits for its errors to emit (`noEmitOnError`), nor one that emits a
204
+ * single `outFile` from the whole program.
205
+ */
206
+ export function emitsOnThreads(options: CompilerOptions): boolean {
207
+ return (
208
+ options.noEmit !== true &&
209
+ options.noEmitOnError !== true &&
210
+ options.listFilesOnly !== true &&
211
+ (options.outFile ?? '') === ''
212
+ );
213
+ }
214
+
215
+ /**
216
+ * What emit threads emit of each file of `program`, by file index (`emitsJavaScript`, parallel-emit.ts):
217
+ * for an incremental program that keeps incremental state, the emit pending before checking
218
+ * (`IncrementalProgram.emitKindsOnCheckers`), which checking only adds to and the program's emit
219
+ * completes; `undefined` where every file is emitted in full.
220
+ */
221
+ export function emitKindsOf(program: CompilerProgram): number[] | undefined {
222
+ if (!(program instanceof IncrementalProgram) || !program.snapshot.canUseIncrementalState()) return undefined;
223
+ return program.emitKindsOnCheckers(program.program.getSourceFiles());
224
+ }
225
+
226
+ /**
227
+ * Checks `compilerProgram` on this thread while `threads` emit threads emit the JavaScript of its files that
228
+ * `emitKinds` selects, and the declaration thread their declarations where that pays; then calls `report`
229
+ * with the program's diagnostics, once the outputs of every thread arrived for the program's emit to complete
230
+ * (`PlainProgram.emit`, `IncrementalProgram.emit`). The program must be shared (`shareWithCheckers`).
231
+ */
232
+ export function checkWhileThreadsEmit(
233
+ compilerProgram: CompilerProgram,
234
+ threads: number,
235
+ emitKinds: readonly number[] | undefined,
236
+ report: (diagnostics: Diagnostic[]) => void,
237
+ ): void {
238
+ const checked = compilerProgram.checked;
239
+ const program = checked.program;
240
+ let diagnostics: Diagnostic[] | undefined = undefined;
241
+ // The first wave of emit threads, and the declaration thread or the second wave.
242
+ let emitting = 2;
243
+ let declarations: (EmitResult | undefined)[] | undefined = undefined;
244
+ const reportOnce = (): void => {
245
+ if (diagnostics === undefined || emitting > 0) return;
246
+ if (declarations !== undefined) {
247
+ // The declaration thread emitted all of each decorator metadata file: its result is the file's emit.
248
+ for (const i of metadata) {
249
+ const result = declarations[i];
250
+ if (result === undefined) continue;
251
+ checked.emittedInParallel![i] = result;
252
+ declarations[i] = new EmitResult();
253
+ }
254
+ checked.declarationsEmittedAhead = declarations;
255
+ }
256
+ report(diagnostics);
257
+ };
258
+ const emitted = (): void => {
259
+ emitting--;
260
+ reportOnce();
261
+ };
262
+ const metadata = startParallelEmit(checked, threads, emitted, emitKinds);
263
+ // With declarations, the declaration thread also emits the files with decorator metadata.
264
+ if (getEmitDeclarations(program.options) && declarationThreadPays(1))
265
+ declarations = startDeclarationThread(checked, metadata, emitted, emitKinds);
266
+ const found = getDiagnosticsOfProgram(compilerProgram, false);
267
+ mark('diagnostics collected');
268
+ // Else they are emitted with the kinds this thread's checker gives, now that it checked.
269
+ if (declarations === undefined) {
270
+ if (metadata.length > 0) {
271
+ const files = program.getSourceFiles();
272
+ const kinds = recordMetadataKinds(
273
+ checked,
274
+ metadata.map((i) => files[i]!),
275
+ );
276
+ mark('metadata kinds recorded');
277
+ startMetadataEmit(checked, threads, metadata, kinds, emitted);
278
+ } else emitting--;
279
+ }
280
+ diagnostics = found;
281
+ reportOnce();
282
+ }
283
+
284
+ /**
285
+ * Like `compile` for a `noEmit` program with declarations, which reports the errors of declaration emit:
286
+ * this thread checks the program while the declaration thread (`startDeclarationErrorThread`) finds the
287
+ * declaration errors of every file, then reports both; calls `done` with the exit status. Returns false
288
+ * where the declaration thread doesn't apply, and `compile` runs instead: an incremental program, one
289
+ * that waits for its errors (`noEmitOnError`), a program that isn't shared with threads, or one where a
290
+ * declaration thread doesn't pay (`declarationThreadPays`).
291
+ */
292
+ export function compileWithDeclarationThread(
293
+ sys: System,
294
+ work: CompileWork,
295
+ done: (status: ExitStatus) => void,
296
+ ): boolean {
297
+ const program = work.program;
298
+ const options = program.options;
299
+ if (
300
+ work.incremental ||
301
+ options.noEmit !== true ||
302
+ options.noEmitOnError === true ||
303
+ options.listFilesOnly === true ||
304
+ options.singleThreaded === true ||
305
+ !getEmitDeclarations(options) ||
306
+ parallelCheck.spawn === undefined ||
307
+ !sharesPublishedGraphs ||
308
+ !declarationThreadPays(1)
309
+ )
310
+ return false;
311
+ const times = new CompileTimes();
312
+ times.configTime = work.configTime;
313
+ times.parseTime = work.parseTime;
314
+ const bindStart = performance.now();
315
+ const checked = new CheckedProgram(program);
316
+ times.bindTime = performance.now() - bindStart;
317
+ shareWithCheckers(checked);
318
+ const compilerProgram = new PlainProgram(checked, times);
319
+ let diagnostics: Diagnostic[] | undefined = undefined;
320
+ let declaring = true;
321
+ const report = (): void => {
322
+ if (diagnostics === undefined || declaring) return;
323
+ const result = reportEmitAndErrors(
324
+ sys,
325
+ compilerProgram,
326
+ diagnostics,
327
+ work.reporter,
328
+ work.errorSummary,
329
+ new SystemWriter(sys),
330
+ outputLocale(options),
331
+ );
332
+ traceSingleChecker(checked);
333
+ done(emitStatusAndReportStatistics(sys, compilerProgram, result));
334
+ };
335
+ startDeclarationErrorThread(checked, (declarations) => {
336
+ // `PlainProgram.getDeclarationDiagnostics` reads them.
337
+ checked.declarationsInParallel = declarations;
338
+ declaring = false;
339
+ report();
340
+ });
341
+ // `PlainProgram.emit` reports the declaration errors once the thread has found them.
342
+ diagnostics = getDiagnosticsOfProgram(compilerProgram, false, true);
343
+ mark('diagnostics collected');
344
+ report();
345
+ return true;
346
+ }
347
+
125
348
  /**
126
349
  * Like `compile`, with `threads` checker threads (parallel-check.ts): calls `done` with the exit
127
350
  * status once the threads have checked the program and it is emitted and reported.
@@ -137,6 +360,8 @@ export function compileInParallel(
137
360
  times.configTime = work.configTime;
138
361
  times.parseTime = work.parseTime;
139
362
  const bindStart = performance.now();
363
+ // The checker threads format their diagnostics in the format the reporter writes them in.
364
+ formattedReports.format = work.reporter.plainFormat();
140
365
  mark(`${threads} checkers`);
141
366
  const checked = new CheckedProgram(program);
142
367
  times.bindTime = performance.now() - bindStart;
@@ -176,6 +401,43 @@ export function compileInParallel(
176
401
  };
177
402
  const options = program.options;
178
403
  if (!checking && (options.noEmit === true || options.noEmitOnError === true)) return finish(0);
404
+ if (
405
+ sharesPublishedGraphs &&
406
+ checking &&
407
+ !work.incremental &&
408
+ getEmitDeclarations(options) &&
409
+ options.noEmit !== true &&
410
+ options.noEmitOnError !== true &&
411
+ options.composite !== true &&
412
+ (options.outFile ?? '') === '' &&
413
+ declarationThreadPays(threads)
414
+ ) {
415
+ // The checker threads emit JavaScript and the declaration thread declarations (parallel-emit.ts).
416
+ const kinds: number[] = [];
417
+ for (let i = 0; i < program.getSourceFiles().length; i++) kinds.push(EmitOnly.Js);
418
+ let pending = 2;
419
+ let checkTime = 0;
420
+ let declarations: (EmitResult | undefined)[] = [];
421
+ const joined = (): void => {
422
+ if (--pending > 0) return;
423
+ checked.javaScriptEmittedOnThreads = true;
424
+ checked.declarationsEmittedAhead = declarations;
425
+ finish(checkTime);
426
+ };
427
+ declarations = startDeclarationThread(checked, [], joined);
428
+ startParallelCheck(
429
+ checked,
430
+ threads,
431
+ (time) => {
432
+ checkTime = time;
433
+ joined();
434
+ },
435
+ true,
436
+ kinds,
437
+ checking,
438
+ );
439
+ return;
440
+ }
179
441
  startParallelCheck(checked, threads, finish, true, undefined, checking);
180
442
  }
181
443
 
@@ -285,6 +547,19 @@ export function emitFilesAndReportErrors(
285
547
  ): CompileResult {
286
548
  const diagnostics = getDiagnosticsOfProgram(program, false);
287
549
  mark('diagnostics collected');
550
+ return reportEmitAndErrors(sys, program, diagnostics, reporter, errorSummary, writer, locale);
551
+ }
552
+
553
+ /** The rest of `emitFilesAndReportErrors`, with the program's `diagnostics` collected. */
554
+ export function reportEmitAndErrors(
555
+ sys: System,
556
+ program: CompilerProgram,
557
+ diagnostics: Diagnostic[],
558
+ reporter: DiagnosticReporter,
559
+ errorSummary: ErrorSummaryReporter,
560
+ writer: OutputWriter,
561
+ locale: Locale,
562
+ ): CompileResult {
288
563
  let emitResult = new EmitResult();
289
564
  emitResult.emitSkipped = true;
290
565
  if (program.options.listFilesOnly !== true) {
@@ -70,3 +70,26 @@ export function programDiagnostics(
70
70
  return [];
71
71
  }
72
72
  }
73
+
74
+ /**
75
+ * What `semantic` answers for each of `files` and what `global` answers, after checking `files` and
76
+ * no other file. Checking one file can add diagnostics located in another, so every file is checked
77
+ * before any file's diagnostics are collected. With every file of the program listed, the answers are
78
+ * those of the full check.
79
+ */
80
+ export function checkedFileDiagnostics(
81
+ checked: CheckedProgram,
82
+ files: readonly SourceFile[],
83
+ ): { files: (readonly CompilerDiagnostic[])[]; global: readonly CompilerDiagnostic[] } {
84
+ for (const file of files) checked.getSemanticDiagnostics(file);
85
+ const program = checked.program;
86
+ const global = [
87
+ ...program.getConfigFileParsingDiagnostics(),
88
+ ...program.getProgramDiagnostics(),
89
+ ...checked.getGlobalDiagnostics(),
90
+ ];
91
+ return {
92
+ files: files.map((file) => checked.getSemanticDiagnostics(file)),
93
+ global: sortAndDeduplicateDiagnostics(global).filter((d) => d.file === undefined),
94
+ };
95
+ }