ttsc 0.18.2 → 0.18.4
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.
- package/cmd/ttscgraph/main.go +18 -12
- package/cmd/ttscgraph/run_help_version_and_bad_flags_exit_codes_test.go +23 -5
- package/cmd/ttscgraph/serve.go +863 -0
- package/cmd/ttscgraph/serve_protocol_rejects_invalid_request_line_test.go +43 -0
- package/cmd/ttscgraph/serve_protocol_retries_session_creation_test.go +83 -0
- package/cmd/ttscgraph/serve_protocol_reuses_session_test.go +35 -0
- package/cmd/ttscgraph/serve_session_applies_source_edit_incrementally_test.go +55 -0
- package/cmd/ttscgraph/serve_session_fails_closed_on_deleted_config_test.go +68 -0
- package/cmd/ttscgraph/serve_session_fails_closed_on_invalid_config_test.go +50 -0
- package/cmd/ttscgraph/serve_session_keeps_missing_literal_root_unchanged_test.go +46 -0
- package/cmd/ttscgraph/serve_session_rebuilds_import_graph_change_test.go +60 -0
- package/cmd/ttscgraph/serve_session_reloads_created_literal_root_test.go +44 -0
- package/cmd/ttscgraph/serve_session_reloads_extended_config_change_test.go +45 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_conditional_export_test.go +60 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_config_types_test.go +44 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_excluded_import_test.go +62 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_package_export_test.go +46 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_package_import_test.go +46 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_path_alias_test.go +47 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_reference_path_test.go +42 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_root_dirs_target_test.go +46 -0
- package/cmd/ttscgraph/serve_session_reloads_newly_resolved_type_reference_test.go +42 -0
- package/cmd/ttscgraph/serve_session_reloads_root_file_set_test.go +52 -0
- package/cmd/ttscgraph/serve_session_reloads_source_deleted_during_load_test.go +60 -0
- package/cmd/ttscgraph/serve_session_reloads_transitive_project_reference_test.go +55 -0
- package/cmd/ttscgraph/serve_session_reloads_when_program_plugin_linked_test.go +53 -0
- package/cmd/ttscgraph/serve_session_reuses_unchanged_preamble_source_test.go +45 -0
- package/cmd/ttscgraph/serve_session_reuses_unchanged_snapshot_test.go +52 -0
- package/cmd/ttscgraph/serve_session_revisits_source_edited_during_load_test.go +61 -0
- package/cmd/ttscgraph/serve_session_tolerates_os_invalid_resolution_candidate_test.go +56 -0
- package/cmd/ttscgraph/serve_session_tracks_all_module_specifier_forms_test.go +41 -0
- package/cmd/ttscgraph/serve_session_tracks_diamond_project_references_once_test.go +61 -0
- package/driver/emit_plugin.go +27 -12
- package/driver/plugins.go +21 -5
- package/driver/program.go +43 -14
- package/driver/session.go +11 -3
- package/lib/TtscCompiler.d.ts +3 -2
- package/lib/TtscCompiler.js +6 -14
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/compileProjectInMemory.js +14 -1
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +7 -0
- package/lib/compiler/internal/runBuild.js +176 -16
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/structures/ITtscCompilerResult.d.ts +8 -5
- package/package.json +8 -8
- package/shim/printer/shim.go +22 -4
- package/src/TtscCompiler.ts +4 -16
- package/src/compiler/internal/compileProjectInMemory.ts +17 -1
- package/src/compiler/internal/runBuild.ts +237 -23
- package/src/structures/ITtscCompilerResult.ts +8 -5
|
@@ -160,17 +160,31 @@ function runBuildTimed(
|
|
|
160
160
|
): TtscBuildResult {
|
|
161
161
|
const setupStartedAt = process.hrtime.bigint();
|
|
162
162
|
const execution = resolveExecutionContext(options);
|
|
163
|
-
if (
|
|
163
|
+
if (
|
|
164
|
+
execution.nativePlugins.length > 0 ||
|
|
165
|
+
execution.pluginSetupFailure !== undefined
|
|
166
|
+
) {
|
|
164
167
|
recordTiming(timing, "ttsc plugin setup time", setupStartedAt);
|
|
165
168
|
}
|
|
166
169
|
const buildOptions = applyProjectNoEmit(options, execution);
|
|
170
|
+
if (execution.pluginSetupFailure !== undefined) {
|
|
171
|
+
return appendTypeScriptDiagnosticsAfterPluginFailure(
|
|
172
|
+
execution.pluginSetupFailure,
|
|
173
|
+
buildOptions,
|
|
174
|
+
execution,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
167
177
|
if (execution.nativePlugins.length > 0) {
|
|
168
178
|
const compilers = execution.nativePlugins.filter(
|
|
169
179
|
(plugin) => plugin.stage === "transform",
|
|
170
180
|
);
|
|
171
181
|
const checked = runNativeCheckPlugins(buildOptions, execution, timing);
|
|
172
182
|
if (checked.status !== 0) {
|
|
173
|
-
return
|
|
183
|
+
return appendTypeScriptDiagnosticsAfterPluginFailure(
|
|
184
|
+
checked,
|
|
185
|
+
buildOptions,
|
|
186
|
+
execution,
|
|
187
|
+
);
|
|
174
188
|
}
|
|
175
189
|
|
|
176
190
|
if (buildOptions.emit === false) {
|
|
@@ -191,15 +205,20 @@ function runBuildTimed(
|
|
|
191
205
|
}
|
|
192
206
|
if (compilers.length !== 0) {
|
|
193
207
|
assertSharedHostCompatibility(compilers, "emit");
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
compilers,
|
|
200
|
-
timing,
|
|
201
|
-
),
|
|
208
|
+
const compiled = buildWithNativeCompilerPlugins(
|
|
209
|
+
buildOptions,
|
|
210
|
+
execution,
|
|
211
|
+
compilers,
|
|
212
|
+
timing,
|
|
202
213
|
);
|
|
214
|
+
const result = appendBuildOutput(checked, compiled);
|
|
215
|
+
return compiled.status === 0
|
|
216
|
+
? result
|
|
217
|
+
: appendTypeScriptDiagnosticsAfterPluginFailure(
|
|
218
|
+
result,
|
|
219
|
+
buildOptions,
|
|
220
|
+
execution,
|
|
221
|
+
);
|
|
203
222
|
}
|
|
204
223
|
if (checkPluginsReportTypeScriptDiagnostics(execution.nativePlugins)) {
|
|
205
224
|
return checked;
|
|
@@ -213,15 +232,20 @@ function runBuildTimed(
|
|
|
213
232
|
let result: TtscBuildResult;
|
|
214
233
|
if (compilers.length !== 0) {
|
|
215
234
|
assertSharedHostCompatibility(compilers, "emit");
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
235
|
+
const compiled = buildWithNativeCompilerPlugins(
|
|
236
|
+
buildOptions,
|
|
237
|
+
execution,
|
|
238
|
+
compilers,
|
|
239
|
+
timing,
|
|
240
|
+
);
|
|
241
|
+
result = appendBuildOutput(checked, compiled);
|
|
242
|
+
if (compiled.status !== 0) {
|
|
243
|
+
result = appendTypeScriptDiagnosticsAfterPluginFailure(
|
|
244
|
+
result,
|
|
219
245
|
buildOptions,
|
|
220
246
|
execution,
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
),
|
|
224
|
-
);
|
|
247
|
+
);
|
|
248
|
+
}
|
|
225
249
|
} else {
|
|
226
250
|
if (
|
|
227
251
|
buildOptions.skipDiagnosticsCheck !== true &&
|
|
@@ -295,6 +319,165 @@ function checkPluginsReportTypeScriptDiagnostics(
|
|
|
295
319
|
);
|
|
296
320
|
}
|
|
297
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Preserve a failed plugin's output and status while collecting TypeScript
|
|
324
|
+
* diagnostics through an independent no-emit pass.
|
|
325
|
+
*
|
|
326
|
+
* A sidecar can fail before it loads the project Program, including when a Go
|
|
327
|
+
* panic or another runtime error terminates the process. The plugin failure
|
|
328
|
+
* must still block emit, but it must not hide unrelated errors in the user's
|
|
329
|
+
* TypeScript source. The fallback runs only after a plugin failure, skips modes
|
|
330
|
+
* whose contract intentionally omits diagnostics, and avoids appending a batch
|
|
331
|
+
* the plugin already reported itself.
|
|
332
|
+
*/
|
|
333
|
+
function appendTypeScriptDiagnosticsAfterPluginFailure(
|
|
334
|
+
failure: TtscBuildResult,
|
|
335
|
+
options: RunBuildOptions,
|
|
336
|
+
execution: ReturnType<typeof resolveExecutionContext>,
|
|
337
|
+
): TtscBuildResult {
|
|
338
|
+
if (
|
|
339
|
+
options.format === true ||
|
|
340
|
+
options.skipDiagnosticsCheck === true ||
|
|
341
|
+
forwardsTerminalTsgoFlag(options)
|
|
342
|
+
) {
|
|
343
|
+
return failure;
|
|
344
|
+
}
|
|
345
|
+
const typechecked = runTsgo(
|
|
346
|
+
execution,
|
|
347
|
+
["--noEmit"],
|
|
348
|
+
createPluginFailureTypecheckOptions(options),
|
|
349
|
+
);
|
|
350
|
+
const fallback = filterReportedTypeScriptDiagnostics(
|
|
351
|
+
failure,
|
|
352
|
+
typechecked,
|
|
353
|
+
execution.projectRoot,
|
|
354
|
+
);
|
|
355
|
+
if (fallback === null) {
|
|
356
|
+
return failure;
|
|
357
|
+
}
|
|
358
|
+
// Structured consumers (the public API's `IFailure.diagnostics`) never see
|
|
359
|
+
// stdout/stderr, so a plugin failure that reported no parsable diagnostics
|
|
360
|
+
// must be seeded as one before recovered TypeScript diagnostics are appended
|
|
361
|
+
// — otherwise the recovery would replace the plugin error with unrelated
|
|
362
|
+
// type errors instead of surfacing both.
|
|
363
|
+
const seeded =
|
|
364
|
+
failure.diagnostics.length === 0
|
|
365
|
+
? { ...failure, diagnostics: [createProcessDiagnostic(failure)] }
|
|
366
|
+
: failure;
|
|
367
|
+
const status = failure.status;
|
|
368
|
+
return {
|
|
369
|
+
...appendBuildOutput(seeded, fallback),
|
|
370
|
+
status,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Make the recovery pass parseable regardless of the user's display flags. This
|
|
376
|
+
* is an internal second pass, so plain output is required to remove only
|
|
377
|
+
* diagnostics that the failed plugin already printed.
|
|
378
|
+
*/
|
|
379
|
+
function createPluginFailureTypecheckOptions(
|
|
380
|
+
options: RunBuildOptions,
|
|
381
|
+
): RunBuildOptions {
|
|
382
|
+
const passthrough: string[] = [];
|
|
383
|
+
for (let i = 0; i < (options.passthrough?.length ?? 0); i++) {
|
|
384
|
+
const token = options.passthrough![i]!;
|
|
385
|
+
if (token === "--pretty") {
|
|
386
|
+
if (isBooleanLiteral(options.passthrough![i + 1] ?? "")) i++;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (token.startsWith("--pretty=")) continue;
|
|
390
|
+
passthrough.push(token);
|
|
391
|
+
}
|
|
392
|
+
return {
|
|
393
|
+
...options,
|
|
394
|
+
passthrough,
|
|
395
|
+
structuredDiagnostics: true,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/** Return only fallback diagnostics the failed plugin did not already report. */
|
|
400
|
+
function filterReportedTypeScriptDiagnostics(
|
|
401
|
+
failure: TtscBuildResult,
|
|
402
|
+
typechecked: TtscBuildResult,
|
|
403
|
+
cwd: string,
|
|
404
|
+
): TtscBuildResult | null {
|
|
405
|
+
if (typechecked.diagnostics.length === 0) {
|
|
406
|
+
return typechecked.status === 0 ? null : typechecked;
|
|
407
|
+
}
|
|
408
|
+
const diagnostics = typechecked.diagnostics.filter(
|
|
409
|
+
(diagnostic) =>
|
|
410
|
+
!failure.diagnostics.some((existing) =>
|
|
411
|
+
compilerDiagnosticsEqual(existing, diagnostic),
|
|
412
|
+
),
|
|
413
|
+
);
|
|
414
|
+
if (diagnostics.length === 0) return null;
|
|
415
|
+
if (diagnostics.length === typechecked.diagnostics.length) return typechecked;
|
|
416
|
+
return {
|
|
417
|
+
...typechecked,
|
|
418
|
+
diagnostics,
|
|
419
|
+
stderr: filterCompilerDiagnosticText(typechecked.stderr, diagnostics, cwd),
|
|
420
|
+
stdout: filterCompilerDiagnosticText(typechecked.stdout, diagnostics, cwd),
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/** Remove diagnostic lines absent from the selected structured result. */
|
|
425
|
+
function filterCompilerDiagnosticText(
|
|
426
|
+
text: string,
|
|
427
|
+
diagnostics: readonly ITtscCompilerDiagnostic[],
|
|
428
|
+
cwd: string,
|
|
429
|
+
): string {
|
|
430
|
+
const out: string[] = [];
|
|
431
|
+
let keepContinuation = true;
|
|
432
|
+
for (const line of text.split(/\r?\n/)) {
|
|
433
|
+
const plain = stripAnsi(line);
|
|
434
|
+
const diagnostic = parseDiagnosticLine(plain, cwd);
|
|
435
|
+
if (diagnostic !== null) {
|
|
436
|
+
keepContinuation = diagnostics.some((selected) =>
|
|
437
|
+
compilerDiagnosticsEqual(selected, diagnostic),
|
|
438
|
+
);
|
|
439
|
+
if (keepContinuation) out.push(line);
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (/^Found\s+\d+\s+errors?/i.test(plain)) continue;
|
|
443
|
+
if (!keepContinuation && /^\s+/.test(line)) continue;
|
|
444
|
+
keepContinuation = true;
|
|
445
|
+
out.push(line);
|
|
446
|
+
}
|
|
447
|
+
return out.join("\n");
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** Compare normalized compiler diagnostics before appending fallback output. */
|
|
451
|
+
function compilerDiagnosticsEqual(
|
|
452
|
+
left: ITtscCompilerDiagnostic,
|
|
453
|
+
right: ITtscCompilerDiagnostic,
|
|
454
|
+
): boolean {
|
|
455
|
+
return (
|
|
456
|
+
left.category === right.category &&
|
|
457
|
+
left.code === right.code &&
|
|
458
|
+
left.file === right.file &&
|
|
459
|
+
diagnosticPositionsEqual(left, right) &&
|
|
460
|
+
diagnosticHeadline(left.messageText) ===
|
|
461
|
+
diagnosticHeadline(right.messageText)
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/** Compare offsets when available, otherwise compare rendered line/column. */
|
|
466
|
+
function diagnosticPositionsEqual(
|
|
467
|
+
left: ITtscCompilerDiagnostic,
|
|
468
|
+
right: ITtscCompilerDiagnostic,
|
|
469
|
+
): boolean {
|
|
470
|
+
if (left.start !== undefined && right.start !== undefined) {
|
|
471
|
+
return left.start === right.start;
|
|
472
|
+
}
|
|
473
|
+
return left.line === right.line && left.character === right.character;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/** Remove pretty-rendered source context from a diagnostic message. */
|
|
477
|
+
function diagnosticHeadline(message: string): string {
|
|
478
|
+
return message.split(/\r?\n/, 1)[0]!.trim();
|
|
479
|
+
}
|
|
480
|
+
|
|
298
481
|
/**
|
|
299
482
|
* Tsgo CLI flags that make `tsgo` print something and exit instead of building
|
|
300
483
|
* (`--showConfig`, `--listFilesOnly`, `--help`, …). ttsc must not add
|
|
@@ -850,6 +1033,26 @@ export function appendBuildOutput(
|
|
|
850
1033
|
});
|
|
851
1034
|
}
|
|
852
1035
|
|
|
1036
|
+
/**
|
|
1037
|
+
* Synthesize one structured diagnostic from a non-zero process result that
|
|
1038
|
+
* produced no parsable diagnostics, carrying the captured stderr/stdout as the
|
|
1039
|
+
* message. Shared by the public API result mapping and the plugin-failure
|
|
1040
|
+
* recovery pass so the failure text is never dropped from structured output.
|
|
1041
|
+
*/
|
|
1042
|
+
export function createProcessDiagnostic(
|
|
1043
|
+
result: TtscBuildResult,
|
|
1044
|
+
): ITtscCompilerDiagnostic {
|
|
1045
|
+
const messageText =
|
|
1046
|
+
(result.stderr || result.stdout).trim() ||
|
|
1047
|
+
`ttsc exited with status ${result.status}`;
|
|
1048
|
+
return {
|
|
1049
|
+
category: "error",
|
|
1050
|
+
code: "TTSC_PROCESS",
|
|
1051
|
+
file: null,
|
|
1052
|
+
messageText,
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
|
|
853
1056
|
/**
|
|
854
1057
|
* Resolve all runtime context needed for a build: cwd, tsconfig path, project
|
|
855
1058
|
* root, tsgo binary location, and the loaded native plugin list. Centralised
|
|
@@ -867,20 +1070,31 @@ function resolveExecutionContext(
|
|
|
867
1070
|
const tsconfig = project.path;
|
|
868
1071
|
const projectRoot = project.root;
|
|
869
1072
|
const tsgo = resolveTsgo({ ...options, cwd: projectRoot });
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1073
|
+
let pluginSetupFailure: TtscBuildResult | undefined;
|
|
1074
|
+
let nativePlugins: ITtscLoadedNativePlugin[] = [];
|
|
1075
|
+
try {
|
|
1076
|
+
if (hasProjectPluginEntries(project, options.plugins)) {
|
|
1077
|
+
nativePlugins = loadProjectPlugins({
|
|
873
1078
|
binary: resolveBinary(options) ?? "",
|
|
874
1079
|
cacheDir: options.cacheDir ?? options.env?.TTSC_CACHE_DIR,
|
|
875
1080
|
cwd,
|
|
876
1081
|
entries: options.plugins,
|
|
877
1082
|
projectRoot,
|
|
878
1083
|
tsconfig,
|
|
879
|
-
})
|
|
880
|
-
|
|
1084
|
+
}).nativePlugins;
|
|
1085
|
+
}
|
|
1086
|
+
} catch (error) {
|
|
1087
|
+
pluginSetupFailure = {
|
|
1088
|
+
diagnostics: [],
|
|
1089
|
+
status: 2,
|
|
1090
|
+
stdout: "",
|
|
1091
|
+
stderr: `${error instanceof Error ? error.message : String(error)}\n`,
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
881
1094
|
return {
|
|
882
1095
|
cwd,
|
|
883
|
-
nativePlugins
|
|
1096
|
+
nativePlugins,
|
|
1097
|
+
pluginSetupFailure,
|
|
884
1098
|
projectNoEmit: project.compilerOptions.noEmit === true,
|
|
885
1099
|
projectRoot,
|
|
886
1100
|
rewriteRelativeImportExtensionsForEmit:
|
|
@@ -47,12 +47,14 @@ export namespace ITtscCompilerResult {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
* Represents a ttsc compilation that completed
|
|
50
|
+
* Represents a ttsc compilation that completed with diagnostics or a
|
|
51
|
+
* recoverable plugin failure.
|
|
51
52
|
*
|
|
52
53
|
* This interface is returned when the compiler reports errors, warnings, or
|
|
53
|
-
* other diagnostics during normal compilation
|
|
54
|
-
*
|
|
55
|
-
*
|
|
54
|
+
* other diagnostics during normal compilation, or when a plugin failure can
|
|
55
|
+
* be retained as a build result while an independent TypeScript check runs.
|
|
56
|
+
* It contains both structured diagnostic information and any output that was
|
|
57
|
+
* still generated despite the failure.
|
|
56
58
|
*/
|
|
57
59
|
export interface IFailure {
|
|
58
60
|
/** Indicates that compilation completed with diagnostics. */
|
|
@@ -86,7 +88,8 @@ export namespace ITtscCompilerResult {
|
|
|
86
88
|
* pattern-matching error messages. Omitted when ttsc cannot determine the
|
|
87
89
|
* origin. Treat as `"unknown"` when missing.
|
|
88
90
|
*
|
|
89
|
-
* - `"plugin"`:
|
|
91
|
+
* - `"plugin"`: plugin preparation failed before a build result could be
|
|
92
|
+
* recovered.
|
|
90
93
|
* - `"host"`: the TypeScript-Go host could not start (missing binary, cache
|
|
91
94
|
* lock, invalid config).
|
|
92
95
|
* - `"unknown"`: any other host-level failure.
|