@rushstack/heft-typescript-plugin 0.1.0-rc.4 → 0.2.0-rc.6

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.
@@ -28,25 +28,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
28
28
  exports.TypeScriptBuilder = void 0;
29
29
  const crypto = __importStar(require("crypto"));
30
30
  const path = __importStar(require("path"));
31
+ const worker_threads_1 = require("worker_threads");
31
32
  const semver = __importStar(require("semver"));
32
33
  const node_core_library_1 = require("@rushstack/node-core-library");
33
- const EmitFilesPatch_1 = require("./EmitFilesPatch");
34
- const TypeScriptCachedFileSystem_1 = require("./fileSystem/TypeScriptCachedFileSystem");
35
- const EMPTY_JSON = {};
34
+ const configureProgramForMultiEmit_1 = require("./configureProgramForMultiEmit");
36
35
  const OLDEST_SUPPORTED_TS_MAJOR_VERSION = 2;
37
36
  const OLDEST_SUPPORTED_TS_MINOR_VERSION = 9;
38
- const NEWEST_SUPPORTED_TS_MAJOR_VERSION = 4;
39
- const NEWEST_SUPPORTED_TS_MINOR_VERSION = 8;
37
+ const NEWEST_SUPPORTED_TS_MAJOR_VERSION = 5;
38
+ const NEWEST_SUPPORTED_TS_MINOR_VERSION = 0;
40
39
  class TypeScriptBuilder {
41
- constructor(configuration) {
42
- this.__tsCacheFilePath = undefined;
43
- this._tsReadJsonCache = new Map();
44
- this._cachedFileSystem = new TypeScriptCachedFileSystem_1.TypeScriptCachedFileSystem();
45
- this._tool = undefined;
46
- this._configuration = configuration;
47
- this._typescriptLogger = configuration.scopedLogger;
48
- this._typescriptTerminal = configuration.scopedLogger.terminal;
49
- }
50
40
  get _tsCacheFilePath() {
51
41
  if (!this.__tsCacheFilePath) {
52
42
  // TypeScript internally handles if the tsconfig options have changed from when the tsbuildinfo file was created.
@@ -66,7 +56,16 @@ class TypeScriptBuilder {
66
56
  }
67
57
  return this.__tsCacheFilePath;
68
58
  }
59
+ constructor(configuration) {
60
+ this._suppressedDiagnosticCodes = new Set();
61
+ this._tool = undefined;
62
+ this._nextRequestId = 0;
63
+ this._configuration = configuration;
64
+ this._typescriptLogger = configuration.scopedLogger;
65
+ this._typescriptTerminal = configuration.scopedLogger.terminal;
66
+ }
69
67
  async invokeAsync(onChangeDetected) {
68
+ var _a, _b;
70
69
  if (!this._tool) {
71
70
  // Determine the compiler version
72
71
  const compilerPackageJsonFilename = path.join(this._configuration.typeScriptToolPath, 'package.json');
@@ -111,6 +110,18 @@ class TypeScriptBuilder {
111
110
  `(${NEWEST_SUPPORTED_TS_MAJOR_VERSION}.${NEWEST_SUPPORTED_TS_MINOR_VERSION}); it may not work correctly.`);
112
111
  }
113
112
  const ts = require(this._configuration.typeScriptToolPath);
113
+ ts.performance.enable();
114
+ const suppressedCodes = [
115
+ (_a = ts.Diagnostics.Property_0_has_no_initializer_and_is_not_definitely_assigned_in_the_constructor) === null || _a === void 0 ? void 0 : _a.code,
116
+ // This diagnostic code is not present in old versions of TypeScript
117
+ (_b = ts.Diagnostics
118
+ .Element_implicitly_has_an_any_type_because_expression_of_type_0_can_t_be_used_to_index_type_1) === null || _b === void 0 ? void 0 : _b.code
119
+ ];
120
+ for (const code of suppressedCodes) {
121
+ if (code !== undefined) {
122
+ this._suppressedDiagnosticCodes.add(code);
123
+ }
124
+ }
114
125
  const measureTsPerformance = (measurementName, fn) => {
115
126
  const beforeName = `before${measurementName}`;
116
127
  ts.performance.mark(beforeName);
@@ -120,23 +131,12 @@ class TypeScriptBuilder {
120
131
  ts.performance.measure(measurementName, beforeName, afterName);
121
132
  return Object.assign(Object.assign({}, result), { duration: ts.performance.getDuration(measurementName), count: ts.performance.getCount(beforeName) });
122
133
  };
123
- const measureTsPerformanceAsync = async (measurementName, fn) => {
124
- const beforeName = `before${measurementName}`;
125
- ts.performance.mark(beforeName);
126
- const resultPromise = fn();
127
- const result = await resultPromise;
128
- const afterName = `after${measurementName}`;
129
- ts.performance.mark(afterName);
130
- ts.performance.measure(measurementName, beforeName, afterName);
131
- return Object.assign(Object.assign({}, result), { duration: ts.performance.getDuration(measurementName) });
132
- };
133
134
  this._typescriptTerminal.writeLine(`Using TypeScript version ${ts.version}`);
134
135
  const rawDiagnostics = [];
135
136
  const pendingOperations = new Set();
136
137
  this._tool = {
137
138
  ts,
138
139
  measureSync: measureTsPerformance,
139
- measureAsync: measureTsPerformanceAsync,
140
140
  sourceFileCache: new Map(),
141
141
  watchProgram: undefined,
142
142
  solutionBuilder: undefined,
@@ -158,7 +158,10 @@ class TypeScriptBuilder {
158
158
  onChangeDetected();
159
159
  }
160
160
  return timeout;
161
- }
161
+ },
162
+ worker: undefined,
163
+ pendingTranspilePromises: new Map(),
164
+ pendingTranspileSignals: new Map()
162
165
  };
163
166
  }
164
167
  const { performance } = this._tool.ts;
@@ -166,17 +169,17 @@ class TypeScriptBuilder {
166
169
  performance.disable();
167
170
  performance.enable();
168
171
  if (onChangeDetected !== undefined) {
169
- this._runWatch(this._tool);
172
+ await this._runWatchAsync(this._tool);
170
173
  }
171
174
  else if (this._useSolutionBuilder) {
172
- this._runSolutionBuild(this._tool);
175
+ await this._runSolutionBuildAsync(this._tool);
173
176
  }
174
177
  else {
175
178
  await this._runBuildAsync(this._tool);
176
179
  }
177
180
  }
178
- _runWatch(tool) {
179
- const { ts, measureSync: measureTsPerformance, pendingOperations, rawDiagnostics } = tool;
181
+ async _runWatchAsync(tool) {
182
+ const { ts, measureSync: measureTsPerformance, pendingOperations, rawDiagnostics, pendingTranspilePromises } = tool;
180
183
  if (!tool.solutionBuilder && !tool.watchProgram) {
181
184
  //#region CONFIGURE
182
185
  const { duration: configureDurationMs, tsconfig } = measureTsPerformance('Configure', () => {
@@ -206,15 +209,23 @@ class TypeScriptBuilder {
206
209
  pendingOperations.delete(operation);
207
210
  operation();
208
211
  }
212
+ if (pendingTranspilePromises.size) {
213
+ const emitResults = await Promise.all(pendingTranspilePromises.values());
214
+ for (const { diagnostics } of emitResults) {
215
+ for (const diagnostic of diagnostics) {
216
+ rawDiagnostics.push(diagnostic);
217
+ }
218
+ }
219
+ }
220
+ // eslint-disable-next-line require-atomic-updates
209
221
  tool.executing = false;
210
222
  }
211
223
  this._logDiagnostics(ts, rawDiagnostics);
212
224
  }
213
225
  async _runBuildAsync(tool) {
214
- const { ts, measureSync: measureTsPerformance, measureAsync: measureTsPerformanceAsync } = tool;
226
+ const { ts, measureSync: measureTsPerformance, pendingTranspilePromises } = tool;
215
227
  //#region CONFIGURE
216
228
  const { duration: configureDurationMs, tsconfig, compilerHost } = measureTsPerformance('Configure', () => {
217
- this._overrideTypeScriptReadJson(ts);
218
229
  const _tsconfig = this._loadTsconfig(ts);
219
230
  this._validateTsconfig(ts, _tsconfig);
220
231
  const _compilerHost = this._buildIncrementalCompilerHost(tool, _tsconfig);
@@ -228,13 +239,20 @@ class TypeScriptBuilder {
228
239
  //#region PROGRAM
229
240
  // There will be only one program here; emit will get a bit abused if we produce multiple outputs
230
241
  let builderProgram = undefined;
231
- let tsProgram;
242
+ let innerProgram;
243
+ const isolatedModules = !!this._configuration.useTranspilerWorker && !!tsconfig.options.isolatedModules;
244
+ const mode = isolatedModules ? 'declaration' : 'both';
245
+ let filesToTranspile;
232
246
  if (tsconfig.options.incremental) {
233
- builderProgram = ts.createEmitAndSemanticDiagnosticsBuilderProgram(tsconfig.fileNames, tsconfig.options, compilerHost, ts.readBuilderProgram(tsconfig.options, compilerHost), ts.getConfigFileParsingDiagnostics(tsconfig), tsconfig.projectReferences);
234
- tsProgram = builderProgram.getProgram();
247
+ // Use ts.createEmitAndSemanticDiagnositcsBuilderProgram directly because the customizations performed by
248
+ // _getCreateBuilderProgram duplicate those performed in this function for non-incremental build.
249
+ const oldProgram = ts.readBuilderProgram(tsconfig.options, compilerHost);
250
+ builderProgram = ts.createEmitAndSemanticDiagnosticsBuilderProgram(tsconfig.fileNames, tsconfig.options, compilerHost, oldProgram, ts.getConfigFileParsingDiagnostics(tsconfig), tsconfig.projectReferences);
251
+ filesToTranspile = getFilesToTranspileFromBuilderProgram(builderProgram);
252
+ innerProgram = builderProgram.getProgram();
235
253
  }
236
254
  else {
237
- tsProgram = ts.createProgram({
255
+ innerProgram = ts.createProgram({
238
256
  rootNames: tsconfig.fileNames,
239
257
  options: tsconfig.options,
240
258
  projectReferences: tsconfig.projectReferences,
@@ -242,11 +260,16 @@ class TypeScriptBuilder {
242
260
  oldProgram: undefined,
243
261
  configFileParsingDiagnostics: ts.getConfigFileParsingDiagnostics(tsconfig)
244
262
  });
263
+ filesToTranspile = getFilesToTranspileFromProgram(innerProgram);
245
264
  }
246
265
  // Prefer the builder program, since it is what gives us incremental builds
247
- const genericProgram = builderProgram || tsProgram;
266
+ const genericProgram = builderProgram || innerProgram;
248
267
  this._logReadPerformance(ts);
249
268
  //#endregion
269
+ if (isolatedModules) {
270
+ // Kick the transpilation worker.
271
+ this._queueTranspileInWorker(tool, genericProgram.getCompilerOptions(), filesToTranspile);
272
+ }
250
273
  //#region ANALYSIS
251
274
  const { duration: diagnosticsDurationMs, diagnostics: preDiagnostics } = measureTsPerformance('Analyze', () => {
252
275
  const rawDiagnostics = [
@@ -261,29 +284,34 @@ class TypeScriptBuilder {
261
284
  this._typescriptTerminal.writeVerboseLine(`Analyze: ${diagnosticsDurationMs}ms`);
262
285
  //#endregion
263
286
  //#region EMIT
264
- const emitResult = this._emit(ts, genericProgram);
287
+ const { changedFiles } = (0, configureProgramForMultiEmit_1.configureProgramForMultiEmit)(innerProgram, ts, this._moduleKindsToEmit, mode);
288
+ const emitResult = genericProgram.emit(undefined,
289
+ // The writeFile callback must be provided for the multi-emit redirector
290
+ ts.sys.writeFile, undefined, undefined, undefined);
291
+ this._cleanupWorker();
265
292
  //#endregion
266
293
  this._logEmitPerformance(ts);
267
294
  //#region FINAL_ANALYSIS
268
295
  // Need to ensure that we include emit diagnostics, since they might not be part of the other sets
269
296
  const rawDiagnostics = [...preDiagnostics, ...emitResult.diagnostics];
270
297
  //#endregion
271
- //#region WRITE
272
- // Using async file system I/O for theoretically better peak performance
273
- // Also allows to run concurrently with linting
274
- const writePromise = measureTsPerformanceAsync('Write', () => node_core_library_1.Async.forEachAsync(emitResult.filesToWrite, async ({ filePath, data }) => this._cachedFileSystem.writeFile(filePath, data, { ensureFolderExists: true }), { concurrency: this._configuration.maxWriteParallelism }));
275
- //#endregion
276
- const { duration: writeDuration } = await writePromise;
277
- this._typescriptTerminal.writeVerboseLine(`I/O Write: ${writeDuration}ms (${emitResult.filesToWrite.length} files)`);
298
+ this._configuration.emitChangedFilesCallback(innerProgram, changedFiles);
299
+ if (pendingTranspilePromises.size) {
300
+ const emitResults = await Promise.all(pendingTranspilePromises.values());
301
+ for (const { diagnostics } of emitResults) {
302
+ for (const diagnostic of diagnostics) {
303
+ rawDiagnostics.push(diagnostic);
304
+ }
305
+ }
306
+ }
278
307
  this._logDiagnostics(ts, rawDiagnostics);
279
308
  // Reset performance counters in case any are used in the callback
280
309
  ts.performance.disable();
281
310
  ts.performance.enable();
282
- this._configuration.emitChangedFilesCallback(tsProgram, emitResult.changedSourceFiles);
283
311
  }
284
- _runSolutionBuild(tool) {
312
+ async _runSolutionBuildAsync(tool) {
285
313
  this._typescriptTerminal.writeVerboseLine(`Using solution mode`);
286
- const { ts, measureSync, rawDiagnostics } = tool;
314
+ const { ts, measureSync, rawDiagnostics, pendingTranspilePromises } = tool;
287
315
  rawDiagnostics.length = 0;
288
316
  if (!tool.solutionBuilder) {
289
317
  //#region CONFIGURE
@@ -308,7 +336,16 @@ class TypeScriptBuilder {
308
336
  //#region EMIT
309
337
  // Ignoring the exit status because we only care about presence of diagnostics
310
338
  tool.solutionBuilder.build();
339
+ this._cleanupWorker();
311
340
  //#endregion
341
+ if (pendingTranspilePromises.size) {
342
+ const emitResults = await Promise.all(pendingTranspilePromises.values());
343
+ for (const { diagnostics } of emitResults) {
344
+ for (const diagnostic of diagnostics) {
345
+ rawDiagnostics.push(diagnostic);
346
+ }
347
+ }
348
+ }
312
349
  this._logDiagnostics(ts, rawDiagnostics);
313
350
  }
314
351
  _logDiagnostics(ts, rawDiagnostics) {
@@ -329,6 +366,7 @@ class TypeScriptBuilder {
329
366
  this._typescriptTerminal.writeVerboseLine(`Print: ${ts.performance.getDuration('printTime')}ms ` +
330
367
  `(${ts.performance.getCount('beforePrint')} files) (Includes Transform)`);
331
368
  this._typescriptTerminal.writeVerboseLine(`Emit: ${ts.performance.getDuration('Emit')}ms (Includes Print)`);
369
+ this._typescriptTerminal.writeVerboseLine(`I/O Write: ${ts.performance.getDuration('I/O Write')}ms (${ts.performance.getCount('beforeIOWrite')} files)`);
332
370
  }
333
371
  _logReadPerformance(ts) {
334
372
  this._typescriptTerminal.writeVerboseLine(`I/O Read: ${ts.performance.getDuration('I/O Read')}ms (${ts.performance.getCount('beforeIORead')} files)`);
@@ -382,31 +420,11 @@ class TypeScriptBuilder {
382
420
  return ts.DiagnosticCategory.Warning;
383
421
  }
384
422
  // These pedantic checks also should not be treated as hard errors
385
- switch (diagnostic.code) {
386
- case ts.Diagnostics.Property_0_has_no_initializer_and_is_not_definitely_assigned_in_the_constructor
387
- .code:
388
- case ts.Diagnostics
389
- .Element_implicitly_has_an_any_type_because_expression_of_type_0_can_t_be_used_to_index_type_1.code:
390
- return ts.DiagnosticCategory.Warning;
423
+ if (this._suppressedDiagnosticCodes.has(diagnostic.code)) {
424
+ return ts.DiagnosticCategory.Warning;
391
425
  }
392
426
  return diagnostic.category;
393
427
  }
394
- _emit(ts, genericProgram) {
395
- const filesToWrite = [];
396
- const changedFiles = new Set();
397
- EmitFilesPatch_1.EmitFilesPatch.install(ts, genericProgram.getCompilerOptions(), this._moduleKindsToEmit, changedFiles);
398
- const writeFileCallback = (filePath, data) => {
399
- filesToWrite.push({ filePath, data });
400
- };
401
- try {
402
- const result = genericProgram.emit(undefined, // Target source file
403
- writeFileCallback);
404
- return Object.assign(Object.assign({}, result), { changedSourceFiles: changedFiles, filesToWrite });
405
- }
406
- finally {
407
- EmitFilesPatch_1.EmitFilesPatch.uninstall(ts);
408
- }
409
- }
410
428
  _validateTsconfig(ts, tsconfig) {
411
429
  if ((tsconfig.options.module && !tsconfig.options.outDir) ||
412
430
  (!tsconfig.options.module && tsconfig.options.outDir)) {
@@ -537,13 +555,12 @@ class TypeScriptBuilder {
537
555
  return `${outFolderName}:${jsExtensionOverride || '.js'}`;
538
556
  }
539
557
  _loadTsconfig(ts) {
540
- const parsedConfigFile = ts.readConfigFile(this._configuration.tsconfigPath, this._cachedFileSystem.readFile);
558
+ const parsedConfigFile = ts.readConfigFile(this._configuration.tsconfigPath, ts.sys.readFile);
541
559
  const currentFolder = path.dirname(this._configuration.tsconfigPath);
542
560
  const tsconfig = ts.parseJsonConfigFileContent(parsedConfigFile.config, {
543
- fileExists: this._cachedFileSystem.exists,
544
- readFile: this._cachedFileSystem.readFile,
545
- readDirectory: (folderPath, extensions, excludes, includes, depth) => ts.matchFiles(folderPath, extensions, excludes, includes,
546
- /* useCaseSensitiveFileNames */ true, currentFolder, depth, this._cachedFileSystem.readFolderFilesAndDirectories.bind(this._cachedFileSystem), this._cachedFileSystem.getRealPath.bind(this._cachedFileSystem), this._cachedFileSystem.directoryExists.bind(this._cachedFileSystem)),
561
+ fileExists: ts.sys.fileExists,
562
+ readFile: ts.sys.readFile,
563
+ readDirectory: ts.sys.readDirectory,
547
564
  useCaseSensitiveFileNames: true
548
565
  }, currentFolder,
549
566
  /*existingOptions:*/ undefined, this._configuration.tsconfigPath);
@@ -552,41 +569,41 @@ class TypeScriptBuilder {
552
569
  }
553
570
  return tsconfig;
554
571
  }
555
- _getCreateBuilderProgram(tool) {
556
- const { _moduleKindsToEmit: moduleKindsToEmit, _configuration: { emitChangedFilesCallback } } = this;
557
- const { ts } = tool;
558
- const createMultiEmitProgram = (fileNames, compilerOptions, host, oldProgram, configFileParsingDiagnostics, projectReferences) => {
572
+ _getCreateBuilderProgram(ts) {
573
+ const { _configuration: { emitChangedFilesCallback } } = this;
574
+ const createMultiEmitBuilderProgram = (fileNames, compilerOptions, host, oldProgram, configFileParsingDiagnostics, projectReferences) => {
559
575
  // Reset performance counters
560
576
  ts.performance.disable();
561
577
  ts.performance.enable();
562
578
  this._typescriptTerminal.writeVerboseLine(`Reading program "${compilerOptions.configFilePath}"`);
563
579
  const newProgram = ts.createEmitAndSemanticDiagnosticsBuilderProgram(fileNames, compilerOptions, host, oldProgram, configFileParsingDiagnostics, projectReferences);
564
580
  this._logReadPerformance(ts);
565
- const { emit: originalEmit } = newProgram;
566
- if (!compilerOptions) {
567
- throw new Error(`Expected compilerOptions!`);
581
+ const isolatedModules = !!this._configuration.useTranspilerWorker && !!compilerOptions.isolatedModules;
582
+ const mode = isolatedModules ? 'declaration' : 'both';
583
+ if (isolatedModules) {
584
+ // Kick the transpilation worker.
585
+ const filesToTranspile = getFilesToTranspileFromBuilderProgram(newProgram);
586
+ this._queueTranspileInWorker(this._tool, compilerOptions, filesToTranspile);
568
587
  }
569
- const emit = (targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers) => {
570
- const changedFiles = new Set();
571
- try {
572
- EmitFilesPatch_1.EmitFilesPatch.install(ts, compilerOptions, moduleKindsToEmit, changedFiles);
573
- const result = originalEmit.call(newProgram, targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
574
- this._typescriptTerminal.writeVerboseLine(`Emitting program "${compilerOptions.configFilePath}"`);
575
- this._logEmitPerformance(ts);
576
- // Reset performance counters
577
- ts.performance.disable();
578
- ts.performance.enable();
579
- emitChangedFilesCallback(newProgram.getProgram(), changedFiles);
580
- return result;
581
- }
582
- finally {
583
- EmitFilesPatch_1.EmitFilesPatch.uninstall(ts);
584
- }
588
+ const { emit: originalEmit } = newProgram;
589
+ const emit = (outerTargetSourceFile, outerWriteFile, outerCancellationToken, outerEmitOnlyDtsFiles, outerCustomTransformers) => {
590
+ const innerProgram = newProgram.getProgram();
591
+ const innerCompilerOptions = innerProgram.getCompilerOptions();
592
+ const { changedFiles } = (0, configureProgramForMultiEmit_1.configureProgramForMultiEmit)(innerProgram, ts, this._moduleKindsToEmit, mode);
593
+ const result = originalEmit.call(newProgram, outerTargetSourceFile, outerWriteFile, outerCancellationToken, outerEmitOnlyDtsFiles, outerCustomTransformers);
594
+ result.changedSourceFiles = changedFiles;
595
+ this._typescriptTerminal.writeVerboseLine(`Emitting program "${innerCompilerOptions.configFilePath}"`);
596
+ this._logEmitPerformance(ts);
597
+ // Reset performance counters
598
+ ts.performance.disable();
599
+ ts.performance.enable();
600
+ emitChangedFilesCallback(innerProgram, changedFiles);
601
+ return result;
585
602
  };
586
603
  newProgram.emit = emit;
587
604
  return newProgram;
588
605
  };
589
- return createMultiEmitProgram;
606
+ return createMultiEmitBuilderProgram;
590
607
  }
591
608
  _buildSolutionBuilderHost(tool) {
592
609
  const reportSolutionBuilderStatus = tool.reportDiagnostic;
@@ -594,7 +611,7 @@ class TypeScriptBuilder {
594
611
  // Do nothing
595
612
  };
596
613
  const { ts } = tool;
597
- const solutionBuilderHost = ts.createSolutionBuilderHost(ts.sys, this._getCreateBuilderProgram(tool), tool.reportDiagnostic, reportSolutionBuilderStatus, reportEmitErrorSummary);
614
+ const solutionBuilderHost = ts.createSolutionBuilderHost(ts.sys, this._getCreateBuilderProgram(ts), tool.reportDiagnostic, reportSolutionBuilderStatus, reportEmitErrorSummary);
598
615
  solutionBuilderHost.afterProgramEmitAndDiagnostics = (program) => {
599
616
  // Use the native metric since we aren't overwriting the writer
600
617
  this._typescriptTerminal.writeVerboseLine(`I/O Write: ${ts.performance.getDuration('I/O Write')}ms (${ts.performance.getCount('beforeIOWrite')} files)`);
@@ -618,7 +635,7 @@ class TypeScriptBuilder {
618
635
  const reportWatchStatus = (diagnostic) => {
619
636
  this._printDiagnosticMessage(ts, diagnostic);
620
637
  };
621
- const compilerHost = ts.createWatchCompilerHost(tsconfig.fileNames, tsconfig.options, ts.sys, this._getCreateBuilderProgram(tool), tool.reportDiagnostic, reportWatchStatus, tsconfig.projectReferences);
638
+ const compilerHost = ts.createWatchCompilerHost(tsconfig.fileNames, tsconfig.options, ts.sys, this._getCreateBuilderProgram(ts), tool.reportDiagnostic, reportWatchStatus, tsconfig.projectReferences, tsconfig.watchOptions);
622
639
  compilerHost.clearTimeout = tool.clearTimeout;
623
640
  compilerHost.setTimeout = tool.setTimeout;
624
641
  return compilerHost;
@@ -651,42 +668,11 @@ class TypeScriptBuilder {
651
668
  }
652
669
  _buildWatchSolutionBuilderHost(tool) {
653
670
  const { reportDiagnostic, ts } = tool;
654
- const host = ts.createSolutionBuilderWithWatchHost(ts.sys, this._getCreateBuilderProgram(tool), reportDiagnostic, reportDiagnostic, reportDiagnostic);
671
+ const host = ts.createSolutionBuilderWithWatchHost(ts.sys, this._getCreateBuilderProgram(ts), reportDiagnostic, reportDiagnostic, reportDiagnostic);
655
672
  host.clearTimeout = tool.clearTimeout;
656
673
  host.setTimeout = tool.setTimeout;
657
674
  return host;
658
675
  }
659
- _overrideTypeScriptReadJson(ts) {
660
- const cachedReadJson = (filePath) => {
661
- let jsonData = this._tsReadJsonCache.get(filePath);
662
- if (jsonData) {
663
- return jsonData;
664
- }
665
- else {
666
- try {
667
- const fileContents = this._cachedFileSystem.readFile(filePath);
668
- if (!fileContents) {
669
- jsonData = EMPTY_JSON;
670
- }
671
- else {
672
- const parsedFile = ts.parseConfigFileTextToJson(filePath, fileContents);
673
- if (parsedFile.error) {
674
- jsonData = EMPTY_JSON;
675
- }
676
- else {
677
- jsonData = parsedFile.config;
678
- }
679
- }
680
- }
681
- catch (error) {
682
- jsonData = EMPTY_JSON;
683
- }
684
- this._tsReadJsonCache.set(filePath, jsonData);
685
- return jsonData;
686
- }
687
- };
688
- ts.readJson = cachedReadJson;
689
- }
690
676
  _parseModuleKind(ts, moduleKindName) {
691
677
  switch (moduleKindName.toLowerCase()) {
692
678
  case 'commonjs':
@@ -705,6 +691,100 @@ class TypeScriptBuilder {
705
691
  throw new Error(`"${moduleKindName}" is not a valid module kind name.`);
706
692
  }
707
693
  }
694
+ _queueTranspileInWorker(tool, compilerOptions, filesToTranspile) {
695
+ const { pendingTranspilePromises, pendingTranspileSignals } = tool;
696
+ let maybeWorker = tool.worker;
697
+ if (!maybeWorker) {
698
+ const workerData = {
699
+ typeScriptToolPath: this._configuration.typeScriptToolPath
700
+ };
701
+ tool.worker = maybeWorker = new worker_threads_1.Worker(require.resolve('./TranspilerWorker.js'), {
702
+ workerData: workerData
703
+ });
704
+ maybeWorker.on('message', (response) => {
705
+ const { requestId: resolvingRequestId, type, result } = response;
706
+ const signal = pendingTranspileSignals.get(resolvingRequestId);
707
+ if (type === 'error') {
708
+ const error = Object.assign(new Error(result.message), result);
709
+ if (signal) {
710
+ signal.reject(error);
711
+ }
712
+ else {
713
+ this._typescriptTerminal.writeErrorLine(`Unexpected worker rejection for request with id ${resolvingRequestId}: ${error}`);
714
+ }
715
+ }
716
+ else if (signal) {
717
+ signal.resolve(result);
718
+ }
719
+ else {
720
+ this._typescriptTerminal.writeErrorLine(`Unexpected worker resolution for request with id ${resolvingRequestId}`);
721
+ }
722
+ pendingTranspileSignals.delete(resolvingRequestId);
723
+ pendingTranspilePromises.delete(resolvingRequestId);
724
+ });
725
+ maybeWorker.once('exit', (exitCode) => {
726
+ if (pendingTranspileSignals.size) {
727
+ const error = new Error(`Worker exited unexpectedly with code ${exitCode}.`);
728
+ for (const { reject: rejectTranspile } of pendingTranspileSignals.values()) {
729
+ rejectTranspile(error);
730
+ }
731
+ pendingTranspileSignals.clear();
732
+ }
733
+ });
734
+ maybeWorker.once('error', (err) => {
735
+ for (const { reject: rejectTranspile } of pendingTranspileSignals.values()) {
736
+ rejectTranspile(err);
737
+ }
738
+ pendingTranspileSignals.clear();
739
+ });
740
+ }
741
+ // make linter happy
742
+ const worker = maybeWorker;
743
+ const requestId = ++this._nextRequestId;
744
+ const transpilePromise = new Promise((resolve, reject) => {
745
+ pendingTranspileSignals.set(requestId, { resolve, reject });
746
+ this._typescriptTerminal.writeLine(`Asynchronously transpiling ${compilerOptions.configFilePath}`);
747
+ const request = {
748
+ compilerOptions,
749
+ filesToTranspile,
750
+ moduleKindsToEmit: this._moduleKindsToEmit,
751
+ requestId
752
+ };
753
+ worker.postMessage(request);
754
+ });
755
+ pendingTranspilePromises.set(requestId, transpilePromise);
756
+ }
757
+ _cleanupWorker() {
758
+ const tool = this._tool;
759
+ if (!tool) {
760
+ return;
761
+ }
762
+ const { worker } = tool;
763
+ if (worker) {
764
+ worker.postMessage(false);
765
+ tool.worker = undefined;
766
+ }
767
+ }
708
768
  }
709
769
  exports.TypeScriptBuilder = TypeScriptBuilder;
770
+ function getFilesToTranspileFromBuilderProgram(builderProgram) {
771
+ const changedFilesSet = builderProgram.getState().changedFilesSet;
772
+ const filesToTranspile = new Map();
773
+ for (const fileName of changedFilesSet) {
774
+ const sourceFile = builderProgram.getSourceFile(fileName);
775
+ if (sourceFile && !sourceFile.isDeclarationFile) {
776
+ filesToTranspile.set(sourceFile.fileName, sourceFile.text);
777
+ }
778
+ }
779
+ return filesToTranspile;
780
+ }
781
+ function getFilesToTranspileFromProgram(program) {
782
+ const filesToTranspile = new Map();
783
+ for (const sourceFile of program.getSourceFiles()) {
784
+ if (!sourceFile.isDeclarationFile) {
785
+ filesToTranspile.set(sourceFile.fileName, sourceFile.text);
786
+ }
787
+ }
788
+ return filesToTranspile;
789
+ }
710
790
  //# sourceMappingURL=TypeScriptBuilder.js.map