ttsc 0.12.3 → 0.13.0
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/platform/main.go +9 -0
- package/cmd/ttsc/api_compile.go +122 -101
- package/cmd/ttsc/api_compile_branches_test.go +55 -57
- package/cmd/ttsc/api_transform.go +67 -49
- package/cmd/ttsc/api_transform_branches_test.go +30 -32
- package/cmd/ttsc/build.go +141 -108
- package/cmd/ttsc/build_branches_test.go +95 -97
- package/cmd/ttsc/filter.go +70 -0
- package/cmd/ttsc/filter_strips_unknown_forwarded_flag_test.go +61 -0
- package/cmd/ttsc/flags_gen.go +30 -0
- package/cmd/ttsc/helpers_test.go +39 -28
- package/cmd/ttsc/main.go +111 -104
- package/cmd/ttsc/main_alias_branches_test.go +21 -23
- package/cmd/ttscserver/main.go +96 -90
- package/cmd/ttscserver/run_lsp_prefers_tsgo_flag_test.go +22 -22
- package/cmd/ttscserver/run_lsp_reports_server_error_test.go +22 -22
- package/cmd/ttscserver/run_lsp_uses_tsgo_environment_test.go +23 -23
- package/cmd/utility-host/main.go +30 -23
- package/driver/lsp.go +53 -7
- package/driver/plugins.go +88 -72
- package/driver/program.go +464 -340
- package/driver/rewrite.go +477 -380
- package/internal/lspserver/lsp_envelope.go +64 -64
- package/internal/lspserver/lsp_frame.go +60 -60
- package/internal/lspserver/lsp_plugin.go +57 -57
- package/internal/lspserver/lsp_proxy.go +265 -251
- package/internal/lspserver/lsp_server.go +155 -144
- package/lib/TtscCompiler.d.ts +7 -0
- package/lib/TtscCompiler.js +7 -0
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/buildNativeCompiler.d.ts +11 -1
- package/lib/compiler/internal/buildNativeCompiler.js +23 -3
- package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
- package/lib/compiler/internal/compileProjectInMemory.d.ts +9 -0
- package/lib/compiler/internal/compileProjectInMemory.js +36 -21
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/paths.d.ts +27 -0
- package/lib/compiler/internal/paths.js +70 -0
- package/lib/compiler/internal/paths.js.map +1 -0
- package/lib/compiler/internal/project/readProjectConfig.d.ts +9 -1
- package/lib/compiler/internal/project/readProjectConfig.js +80 -1
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/project/resolveProjectConfig.d.ts +13 -1
- package/lib/compiler/internal/project/resolveProjectConfig.js +42 -1
- package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
- package/lib/compiler/internal/resolveBinary.d.ts +9 -1
- package/lib/compiler/internal/resolveBinary.js +17 -1
- package/lib/compiler/internal/resolveBinary.js.map +1 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.d.ts +16 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.js +42 -10
- package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
- package/lib/compiler/internal/resolveTsgo.d.ts +21 -1
- package/lib/compiler/internal/resolveTsgo.js +23 -1
- package/lib/compiler/internal/resolveTsgo.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +24 -4
- package/lib/compiler/internal/runBuild.js +355 -93
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/runSingleFileEmit.d.ts +10 -1
- package/lib/compiler/internal/runSingleFileEmit.js +14 -1
- package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
- package/lib/compiler/internal/sharedHostHelpers.d.ts +5 -0
- package/lib/compiler/internal/sharedHostHelpers.js +6 -2
- package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
- package/lib/compiler/internal/spawnNative.d.ts +21 -0
- package/lib/compiler/internal/spawnNative.js +59 -0
- package/lib/compiler/internal/spawnNative.js.map +1 -0
- package/lib/compiler/internal/transformProjectInMemory.d.ts +15 -1
- package/lib/compiler/internal/transformProjectInMemory.js +57 -61
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/flags/parser.d.ts +63 -0
- package/lib/flags/parser.js +254 -0
- package/lib/flags/parser.js.map +1 -0
- package/lib/flags/schema.d.ts +147 -0
- package/lib/flags/schema.js +405 -0
- package/lib/flags/schema.js.map +1 -0
- package/lib/launcher/internal/getCompilerVersionText.js +7 -0
- package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
- package/lib/launcher/internal/prepareExecution.js +35 -0
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/resolveCacheDir.d.ts +9 -0
- package/lib/launcher/internal/resolveCacheDir.js +22 -0
- package/lib/launcher/internal/resolveCacheDir.js.map +1 -0
- package/lib/launcher/internal/resolveTtscserverBinary.js +2 -0
- package/lib/launcher/internal/resolveTtscserverBinary.js.map +1 -1
- package/lib/launcher/internal/runTtsc.d.ts +10 -0
- package/lib/launcher/internal/runTtsc.js +90 -153
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.js +10 -35
- package/lib/launcher/internal/runTtscserver.js.map +1 -1
- package/lib/launcher/internal/runTtsx.d.ts +8 -0
- package/lib/launcher/internal/runTtsx.js +137 -79
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.d.ts +38 -0
- package/lib/plugin/internal/buildSourcePlugin.js +40 -17
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.d.ts +26 -0
- package/lib/plugin/internal/loadProjectPlugins.js +90 -41
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscCompilerTransformation.d.ts +15 -2
- package/lib/structures/ITtscPlugin.d.ts +24 -0
- package/lib/structures/ITtscPluginCapabilities.d.ts +29 -0
- package/lib/structures/ITtscPluginCapabilities.js +3 -0
- package/lib/structures/ITtscPluginCapabilities.js.map +1 -0
- package/lib/structures/index.d.ts +1 -0
- package/lib/structures/index.js +1 -0
- package/lib/structures/index.js.map +1 -1
- package/lib/structures/internal/ITtscLoadedNativePlugin.d.ts +5 -0
- package/lib/structures/internal/TtscCommonOptions.d.ts +19 -0
- package/lib/structures/internal/TtscSingleFileEmitOptions.d.ts +0 -2
- package/package.json +9 -9
- package/shim/ast/shim.go +24 -0
- package/shim/checker/shim.go +35 -0
- package/shim/core/shim.go +17 -0
- package/shim/diagnosticwriter/shim.go +4 -0
- package/shim/lsp/shim.go +3 -3
- package/shim/printer/shim.go +20 -0
- package/src/TtscCompiler.ts +7 -0
- package/src/compiler/internal/buildNativeCompiler.ts +23 -3
- package/src/compiler/internal/compileProjectInMemory.ts +34 -25
- package/src/compiler/internal/paths.ts +68 -0
- package/src/compiler/internal/project/readProjectConfig.ts +86 -1
- package/src/compiler/internal/project/resolveProjectConfig.ts +44 -1
- package/src/compiler/internal/resolveBinary.ts +17 -1
- package/src/compiler/internal/resolveEmittedJavaScript.ts +43 -12
- package/src/compiler/internal/resolveTsgo.ts +29 -1
- package/src/compiler/internal/runBuild.ts +398 -112
- package/src/compiler/internal/runSingleFileEmit.ts +14 -1
- package/src/compiler/internal/sharedHostHelpers.ts +7 -7
- package/src/compiler/internal/spawnNative.ts +64 -0
- package/src/compiler/internal/transformProjectInMemory.ts +47 -67
- package/src/flags/parser.ts +367 -0
- package/src/flags/schema.ts +570 -0
- package/src/launcher/internal/getCompilerVersionText.ts +7 -0
- package/src/launcher/internal/prepareExecution.ts +35 -0
- package/src/launcher/internal/resolveCacheDir.ts +19 -0
- package/src/launcher/internal/resolveTtscserverBinary.ts +2 -0
- package/src/launcher/internal/runTtsc.ts +97 -143
- package/src/launcher/internal/runTtscserver.ts +9 -1
- package/src/launcher/internal/runTtsx.ts +149 -82
- package/src/plugin/internal/buildSourcePlugin.ts +40 -14
- package/src/plugin/internal/loadProjectPlugins.ts +116 -45
- package/src/structures/ITtscCompilerTransformation.ts +15 -2
- package/src/structures/ITtscPlugin.ts +26 -1
- package/src/structures/ITtscPluginCapabilities.ts +29 -0
- package/src/structures/index.ts +1 -0
- package/src/structures/internal/ITtscLoadedNativePlugin.ts +5 -0
- package/src/structures/internal/TtscCommonOptions.ts +19 -0
- package/src/structures/internal/TtscSingleFileEmitOptions.ts +0 -2
- package/tools/gen_shims/main.go +43 -8
- package/utility/flags_gen.go +30 -0
- package/utility/host.go +304 -245
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
4
1
|
import path from "node:path";
|
|
5
2
|
|
|
6
|
-
import {
|
|
3
|
+
import { INTERNAL_SHADOW_FLAGS, TERMINAL_FLAGS } from "../../flags/schema";
|
|
4
|
+
import {
|
|
5
|
+
hasProjectPluginEntries,
|
|
6
|
+
loadProjectPlugins,
|
|
7
|
+
} from "../../plugin/internal/loadProjectPlugins";
|
|
7
8
|
import type { ITtscCompilerDiagnostic } from "../../structures/ITtscCompilerDiagnostic";
|
|
8
9
|
import type { ITtscLoadedNativePlugin } from "../../structures/internal/ITtscLoadedNativePlugin";
|
|
9
10
|
import type { TtscBuildOptions } from "../../structures/internal/TtscBuildOptions";
|
|
10
11
|
import type { TtscBuildResult } from "../../structures/internal/TtscBuildResult";
|
|
11
12
|
import type { TtscCommonOptions } from "../../structures/internal/TtscCommonOptions";
|
|
12
|
-
import {
|
|
13
|
+
import { readProjectConfig } from "./project/readProjectConfig";
|
|
13
14
|
import { resolveBinary } from "./resolveBinary";
|
|
14
15
|
import { resolveTsgo } from "./resolveTsgo";
|
|
15
16
|
import {
|
|
@@ -17,8 +18,18 @@ import {
|
|
|
17
18
|
linkedTransformPlugins,
|
|
18
19
|
selectSharedHostPlugin,
|
|
19
20
|
} from "./sharedHostHelpers";
|
|
21
|
+
import { outputText, spawnNative } from "./spawnNative";
|
|
22
|
+
|
|
23
|
+
type RunBuildOptions = TtscBuildOptions & {
|
|
24
|
+
skipDiagnosticsCheck?: boolean;
|
|
25
|
+
forceListEmittedFiles?: boolean;
|
|
26
|
+
};
|
|
20
27
|
|
|
21
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Merge extra environment variables over `process.env`, always injecting
|
|
30
|
+
* `TTSC_NODE_BINARY` so child processes can re-invoke the same Node.js binary
|
|
31
|
+
* without searching `PATH`.
|
|
32
|
+
*/
|
|
22
33
|
function mergeEnv(extra?: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|
23
34
|
const base = {
|
|
24
35
|
...process.env,
|
|
@@ -28,6 +39,12 @@ function mergeEnv(extra?: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|
|
28
39
|
return { ...base, ...extra };
|
|
29
40
|
}
|
|
30
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Build the environment for a native plugin spawn. Injects `TTSC_TSGO_BINARY`
|
|
44
|
+
* and `TTSC_TTSX_BINARY` alongside the base env from `mergeEnv`. For
|
|
45
|
+
* transform-stage plugins, also passes `TTSC_LINKED_PLUGINS_JSON` containing
|
|
46
|
+
* any linked sources so they run inside the same process as the host plugin.
|
|
47
|
+
*/
|
|
31
48
|
function nativePluginEnv(
|
|
32
49
|
extra: NodeJS.ProcessEnv | undefined,
|
|
33
50
|
execution: ReturnType<typeof resolveExecutionContext>,
|
|
@@ -49,76 +66,24 @@ function nativePluginEnv(
|
|
|
49
66
|
return env;
|
|
50
67
|
}
|
|
51
68
|
|
|
52
|
-
function spawnBinary(
|
|
53
|
-
binary: string,
|
|
54
|
-
args: readonly string[],
|
|
55
|
-
options: {
|
|
56
|
-
cwd?: string;
|
|
57
|
-
env?: NodeJS.ProcessEnv;
|
|
58
|
-
encoding?: BufferEncoding;
|
|
59
|
-
},
|
|
60
|
-
) {
|
|
61
|
-
const viaNode = /\.(?:[cm]?js|ts)$/i.test(binary);
|
|
62
|
-
if (!viaNode) {
|
|
63
|
-
ensureExecutable(binary);
|
|
64
|
-
}
|
|
65
|
-
return spawnSync(
|
|
66
|
-
viaNode ? process.execPath : binary,
|
|
67
|
-
viaNode ? [binary, ...args] : [...args],
|
|
68
|
-
{
|
|
69
|
-
cwd: options.cwd,
|
|
70
|
-
env: options.env,
|
|
71
|
-
encoding: options.encoding,
|
|
72
|
-
maxBuffer: 1024 * 1024 * 256,
|
|
73
|
-
windowsHide: true,
|
|
74
|
-
},
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function ensureExecutable(binary: string): void {
|
|
79
|
-
if (process.platform === "win32") {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
try {
|
|
83
|
-
const mode = fs.statSync(binary).mode & 0o777;
|
|
84
|
-
if ((mode & 0o111) !== 0) {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
fs.chmodSync(binary, mode | 0o755);
|
|
88
|
-
} catch {
|
|
89
|
-
/* keep the original spawn error path */
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function outputText(value: string | Buffer | null | undefined): string {
|
|
94
|
-
if (value == null) {
|
|
95
|
-
return "";
|
|
96
|
-
}
|
|
97
|
-
return typeof value === "string" ? value : value.toString("utf8");
|
|
98
|
-
}
|
|
99
|
-
|
|
100
69
|
/**
|
|
101
70
|
* Run `ttsc` against a tsconfig. Returns once the binary exits so the CLI can
|
|
102
71
|
* decide how to surface diagnostics. Does not throw on non-zero exit.
|
|
103
72
|
*/
|
|
104
|
-
export function runBuild(
|
|
105
|
-
options: TtscBuildOptions & {
|
|
106
|
-
skipDiagnosticsCheck?: boolean;
|
|
107
|
-
forceListEmittedFiles?: boolean;
|
|
108
|
-
} = {},
|
|
109
|
-
): TtscBuildResult {
|
|
73
|
+
export function runBuild(options: RunBuildOptions = {}): TtscBuildResult {
|
|
110
74
|
const execution = resolveExecutionContext(options);
|
|
75
|
+
const buildOptions = applyProjectNoEmit(options, execution);
|
|
111
76
|
if (execution.nativePlugins.length > 0) {
|
|
112
77
|
const compilers = execution.nativePlugins.filter(
|
|
113
78
|
(plugin) => plugin.stage === "transform",
|
|
114
79
|
);
|
|
115
|
-
const checked = runNativeCheckPlugins(
|
|
80
|
+
const checked = runNativeCheckPlugins(buildOptions, execution);
|
|
116
81
|
if (checked.status !== 0) {
|
|
117
82
|
return checked;
|
|
118
83
|
}
|
|
119
84
|
|
|
120
|
-
if (
|
|
121
|
-
if (
|
|
85
|
+
if (buildOptions.emit === false) {
|
|
86
|
+
if (buildOptions.format === true) {
|
|
122
87
|
// Format mode is write-only by contract: the lint sidecar
|
|
123
88
|
// already rewrote source files and reported nothing. Running
|
|
124
89
|
// tsgo --noEmit OR a transform compiler afterwards would either
|
|
@@ -137,12 +102,15 @@ export function runBuild(
|
|
|
137
102
|
assertSharedHostCompatibility(compilers, "emit");
|
|
138
103
|
return appendBuildOutput(
|
|
139
104
|
checked,
|
|
140
|
-
buildWithNativeCompilerPlugins(
|
|
105
|
+
buildWithNativeCompilerPlugins(buildOptions, execution, compilers),
|
|
141
106
|
);
|
|
142
107
|
}
|
|
108
|
+
if (checkPluginsReportTypeScriptDiagnostics(execution.nativePlugins)) {
|
|
109
|
+
return checked;
|
|
110
|
+
}
|
|
143
111
|
return appendBuildOutput(
|
|
144
112
|
checked,
|
|
145
|
-
runTsgo(execution, ["--noEmit"],
|
|
113
|
+
runTsgo(execution, ["--noEmit"], buildOptions),
|
|
146
114
|
);
|
|
147
115
|
}
|
|
148
116
|
|
|
@@ -151,26 +119,30 @@ export function runBuild(
|
|
|
151
119
|
assertSharedHostCompatibility(compilers, "emit");
|
|
152
120
|
result = appendBuildOutput(
|
|
153
121
|
checked,
|
|
154
|
-
buildWithNativeCompilerPlugins(
|
|
122
|
+
buildWithNativeCompilerPlugins(buildOptions, execution, compilers),
|
|
155
123
|
);
|
|
156
124
|
} else {
|
|
157
|
-
if (
|
|
158
|
-
|
|
125
|
+
if (
|
|
126
|
+
buildOptions.skipDiagnosticsCheck !== true &&
|
|
127
|
+
!checkPluginsReportTypeScriptDiagnostics(execution.nativePlugins) &&
|
|
128
|
+
!forwardsTerminalTsgoFlag(buildOptions)
|
|
129
|
+
) {
|
|
130
|
+
const tsgoChecked = runTsgo(execution, ["--noEmit"], buildOptions);
|
|
159
131
|
if (tsgoChecked.status !== 0) {
|
|
160
132
|
return appendBuildOutput(checked, tsgoChecked);
|
|
161
133
|
}
|
|
162
134
|
}
|
|
163
|
-
const args = createTsgoBuildArgs(execution,
|
|
164
|
-
listEmittedFiles:
|
|
135
|
+
const args = createTsgoBuildArgs(execution, buildOptions, {
|
|
136
|
+
listEmittedFiles: buildOptions.forceListEmittedFiles === true,
|
|
165
137
|
});
|
|
166
|
-
const emitted = runTsgoBuild(execution,
|
|
138
|
+
const emitted = runTsgoBuild(execution, buildOptions, args);
|
|
167
139
|
result = appendBuildOutput(checked, emitted);
|
|
168
140
|
}
|
|
169
141
|
|
|
170
142
|
return result;
|
|
171
143
|
}
|
|
172
144
|
|
|
173
|
-
if (
|
|
145
|
+
if (buildOptions.format === true) {
|
|
174
146
|
// Format mode is write-only by contract — see the matching
|
|
175
147
|
// short-circuit in the with-native-plugins branch above. When no
|
|
176
148
|
// native plugin is loaded there is nothing for `ttsc format` to
|
|
@@ -185,20 +157,107 @@ export function runBuild(
|
|
|
185
157
|
};
|
|
186
158
|
}
|
|
187
159
|
|
|
188
|
-
|
|
189
|
-
const checked = runTsgo(execution, ["--noEmit"], options);
|
|
190
|
-
if (checked.status !== 0) {
|
|
191
|
-
return checked;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const args = createTsgoBuildArgs(execution, options, {
|
|
160
|
+
const args = createTsgoBuildArgs(execution, buildOptions, {
|
|
196
161
|
listEmittedFiles:
|
|
197
|
-
|
|
162
|
+
buildOptions.emit !== false &&
|
|
163
|
+
buildOptions.forceListEmittedFiles === true,
|
|
164
|
+
noEmitOnError:
|
|
165
|
+
buildOptions.emit !== false &&
|
|
166
|
+
buildOptions.skipDiagnosticsCheck !== true &&
|
|
167
|
+
!forwardsTerminalTsgoFlag(buildOptions),
|
|
198
168
|
});
|
|
199
|
-
return runTsgoBuild(execution,
|
|
169
|
+
return runTsgoBuild(execution, buildOptions, args);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* A tsconfig-level `noEmit: true` is an analysis-only build unless the user
|
|
174
|
+
* explicitly asks `ttsc --emit` to override it. Treat it like CLI `--noEmit`
|
|
175
|
+
* before composing tsgo/native-host arguments so ttsc does not add emit-only
|
|
176
|
+
* guards around projects that cannot emit.
|
|
177
|
+
*/
|
|
178
|
+
function applyProjectNoEmit(
|
|
179
|
+
options: RunBuildOptions,
|
|
180
|
+
execution: ReturnType<typeof resolveExecutionContext>,
|
|
181
|
+
): RunBuildOptions {
|
|
182
|
+
if (options.emit !== undefined || execution.projectNoEmit !== true) {
|
|
183
|
+
return options;
|
|
184
|
+
}
|
|
185
|
+
return { ...options, emit: false };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function checkPluginsReportTypeScriptDiagnostics(
|
|
189
|
+
plugins: readonly ITtscLoadedNativePlugin[],
|
|
190
|
+
): boolean {
|
|
191
|
+
return plugins.some(
|
|
192
|
+
(plugin) =>
|
|
193
|
+
plugin.stage === "check" && plugin.reportsTypeScriptDiagnostics === true,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Tsgo CLI flags that make `tsgo` print something and exit instead of building
|
|
199
|
+
* (`--showConfig`, `--listFilesOnly`, `--help`, …). ttsc must not add
|
|
200
|
+
* build-only guard flags around these because the forwarded flag asks tsgo to
|
|
201
|
+
* print something and exit instead of compiling the project.
|
|
202
|
+
*
|
|
203
|
+
* Schema-derived: the set is computed from `FLAG_SCHEMA[*].terminal === true`,
|
|
204
|
+
* not hand-maintained next to runBuild. Adding a new terminal flag now means
|
|
205
|
+
* editing `schema.ts` and re-running `pnpm format` — every layer learns about
|
|
206
|
+
* it automatically.
|
|
207
|
+
*/
|
|
208
|
+
const TERMINAL_TSGO_FLAGS: ReadonlySet<string> = (() => {
|
|
209
|
+
// Mirror the legacy hand-list: schema's terminal flags ∪ the `-?` alias
|
|
210
|
+
// tsgo accepts (kept here because it is a tsgo synonym, not a ttsc flag).
|
|
211
|
+
const out = new Set<string>(TERMINAL_FLAGS);
|
|
212
|
+
out.add("-?");
|
|
213
|
+
return out;
|
|
214
|
+
})();
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Report whether the caller forwarded a print-and-exit tsgo flag, so ttsc can
|
|
218
|
+
* avoid adding compile-only flags to a command that is not going to compile.
|
|
219
|
+
*/
|
|
220
|
+
function forwardsTerminalTsgoFlag(options: TtscCommonOptions): boolean {
|
|
221
|
+
return (
|
|
222
|
+
options.passthrough?.some((flag) => TERMINAL_TSGO_FLAGS.has(flag)) ?? false
|
|
223
|
+
);
|
|
200
224
|
}
|
|
201
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Report whether the caller forwarded a flag ttsc adds to tsgo internally —
|
|
228
|
+
* e.g. `--listEmittedFiles` (ttsc adds it to learn emitted paths) or
|
|
229
|
+
* `--noEmit` (ttsc adds it for the pre-emit type-check). When the user
|
|
230
|
+
* also forwards the same flag, post-processing must keep the user-visible
|
|
231
|
+
* effect intact instead of stripping it as ttsc-internal noise.
|
|
232
|
+
*
|
|
233
|
+
* Schema-derived: `FLAG_SCHEMA[*].internalShadow === true`. RC-2 from the
|
|
234
|
+
* RCA (RCA section 3, `--listEmittedFiles` / `--showConfig` swallowed):
|
|
235
|
+
* the per-flag `passthrough.includes("…")` check is now one structural
|
|
236
|
+
* lookup against the schema, not one bespoke `if` per shadow flag.
|
|
237
|
+
*/
|
|
238
|
+
function forwardsInternalShadowFlag(
|
|
239
|
+
options: TtscCommonOptions,
|
|
240
|
+
flag: string,
|
|
241
|
+
): boolean {
|
|
242
|
+
if (!INTERNAL_SHADOW_FLAGS.has(flag)) return false;
|
|
243
|
+
const passthrough = options.passthrough;
|
|
244
|
+
if (passthrough === undefined) return false;
|
|
245
|
+
// Match both the bare form (`--pretty`) and the inline-value form
|
|
246
|
+
// (`--pretty=true`). The launcher's parser preserves these shapes
|
|
247
|
+
// verbatim, so a tolerant prefix compare is what we want — `passthrough`
|
|
248
|
+
// never holds a stray substring that happens to start with the flag
|
|
249
|
+
// because parser positional/passthrough sinks split on whitespace.
|
|
250
|
+
const inlinePrefix = `${flag}=`;
|
|
251
|
+
return passthrough.some(
|
|
252
|
+
(token) => token === flag || token.startsWith(inlinePrefix),
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Dispatch a build through the shared-host native plugin. The host plugin is
|
|
258
|
+
* the one that owns the process (non-linked); all other transform plugins ride
|
|
259
|
+
* inside it via the `--plugins-json` flag.
|
|
260
|
+
*/
|
|
202
261
|
function buildWithNativeCompilerPlugins(
|
|
203
262
|
options: TtscBuildOptions,
|
|
204
263
|
execution: ReturnType<typeof resolveExecutionContext>,
|
|
@@ -214,18 +273,24 @@ function buildWithNativeCompilerPlugins(
|
|
|
214
273
|
);
|
|
215
274
|
}
|
|
216
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Run `tsgo -p <tsconfig> [extraArgs]` and return the normalized result. Used
|
|
278
|
+
* for the no-emit type-check pass that precedes file emission.
|
|
279
|
+
*/
|
|
217
280
|
function runTsgo(
|
|
218
281
|
execution: ReturnType<typeof resolveExecutionContext>,
|
|
219
282
|
extraArgs: readonly string[],
|
|
220
|
-
options:
|
|
283
|
+
options: RunBuildOptions,
|
|
221
284
|
): TtscBuildResult {
|
|
222
|
-
const res =
|
|
285
|
+
const res = spawnNative(
|
|
223
286
|
execution.tsgo.binary,
|
|
224
287
|
[
|
|
225
288
|
"-p",
|
|
226
289
|
execution.tsconfig,
|
|
227
290
|
...extraArgs,
|
|
228
291
|
...createTsgoDiagnosticArgs(options),
|
|
292
|
+
...createTsgoThreadingArgs(options),
|
|
293
|
+
...(options.passthrough ?? []),
|
|
229
294
|
],
|
|
230
295
|
{
|
|
231
296
|
cwd: execution.projectRoot,
|
|
@@ -251,12 +316,17 @@ function runTsgo(
|
|
|
251
316
|
);
|
|
252
317
|
}
|
|
253
318
|
|
|
319
|
+
/**
|
|
320
|
+
* Run `tsgo` with the full emit arguments and parse `TSFILE:` lines from stdout
|
|
321
|
+
* into `emittedFiles`. The TSFILE lines are stripped before the result is
|
|
322
|
+
* returned so they do not appear in the user-facing output.
|
|
323
|
+
*/
|
|
254
324
|
function runTsgoBuild(
|
|
255
325
|
execution: ReturnType<typeof resolveExecutionContext>,
|
|
256
|
-
options:
|
|
326
|
+
options: RunBuildOptions,
|
|
257
327
|
args: readonly string[],
|
|
258
328
|
): TtscBuildResult {
|
|
259
|
-
const res =
|
|
329
|
+
const res = spawnNative(execution.tsgo.binary, args, {
|
|
260
330
|
cwd: execution.projectRoot,
|
|
261
331
|
env: mergeEnv(options.env),
|
|
262
332
|
encoding: "utf8",
|
|
@@ -275,7 +345,18 @@ function runTsgoBuild(
|
|
|
275
345
|
stderr: outputText(res.stderr),
|
|
276
346
|
};
|
|
277
347
|
const emittedFiles = parseEmittedFiles(result.stdout);
|
|
278
|
-
|
|
348
|
+
// The `TSFILE:` lines are tsgo's `--listEmittedFiles` output. ttsc adds that
|
|
349
|
+
// flag internally to learn the emitted paths and strips the lines back out
|
|
350
|
+
// as noise — but when the user themselves forwarded `--listEmittedFiles`,
|
|
351
|
+
// the listing is what they asked for, so it must survive to stdout.
|
|
352
|
+
// The lookup is schema-driven (FLAG_SCHEMA marks `--listEmittedFiles` with
|
|
353
|
+
// `internalShadow: true`); see `forwardsInternalShadowFlag` for the RC-2
|
|
354
|
+
// background.
|
|
355
|
+
const userListedEmitted = forwardsInternalShadowFlag(
|
|
356
|
+
options,
|
|
357
|
+
"--listEmittedFiles",
|
|
358
|
+
);
|
|
359
|
+
if (emittedFiles.length !== 0 && !userListedEmitted) {
|
|
279
360
|
result.stdout = stripEmittedFileLines(result.stdout);
|
|
280
361
|
}
|
|
281
362
|
return normalizeBuildOutput(
|
|
@@ -284,14 +365,18 @@ function runTsgoBuild(
|
|
|
284
365
|
);
|
|
285
366
|
}
|
|
286
367
|
|
|
368
|
+
/** Build the argument list for a direct `tsgo` build invocation. */
|
|
287
369
|
function createTsgoBuildArgs(
|
|
288
370
|
execution: ReturnType<typeof resolveExecutionContext>,
|
|
289
|
-
options:
|
|
290
|
-
flags: { listEmittedFiles: boolean },
|
|
371
|
+
options: RunBuildOptions,
|
|
372
|
+
flags: { listEmittedFiles: boolean; noEmitOnError?: boolean },
|
|
291
373
|
): string[] {
|
|
292
374
|
const args = ["-p", execution.tsconfig];
|
|
293
375
|
if (options.emit === true) {
|
|
294
376
|
args.push("--noEmit", "false", "--emitDeclarationOnly", "false");
|
|
377
|
+
if (execution.rewriteRelativeImportExtensionsForEmit) {
|
|
378
|
+
args.push("--rewriteRelativeImportExtensions");
|
|
379
|
+
}
|
|
295
380
|
} else if (options.emit === false) {
|
|
296
381
|
args.push("--noEmit");
|
|
297
382
|
}
|
|
@@ -302,13 +387,52 @@ function createTsgoBuildArgs(
|
|
|
302
387
|
args.push("--listEmittedFiles");
|
|
303
388
|
}
|
|
304
389
|
args.push(...createTsgoDiagnosticArgs(options));
|
|
390
|
+
args.push(...createTsgoThreadingArgs(options));
|
|
391
|
+
args.push(...(options.passthrough ?? []));
|
|
392
|
+
if (flags.noEmitOnError === true) {
|
|
393
|
+
args.push("--noEmitOnError");
|
|
394
|
+
}
|
|
305
395
|
return args;
|
|
306
396
|
}
|
|
307
397
|
|
|
398
|
+
/**
|
|
399
|
+
* Return `["--pretty", "false"]` when structured diagnostics are requested so
|
|
400
|
+
* that the output can be parsed line-by-line, or an empty array otherwise.
|
|
401
|
+
*
|
|
402
|
+
* When the user explicitly forwarded `--pretty` (any value), the internal
|
|
403
|
+
* `--pretty false` shadow is dropped so the user wins on the surface. ttsc's
|
|
404
|
+
* own diagnostic parser will then see pretty-formatted output and fall back
|
|
405
|
+
* to surfacing it verbatim — the RC-2 contract that `--pretty`'s
|
|
406
|
+
* `internalShadow: true` flag in `FLAG_SCHEMA` declares. Without this guard
|
|
407
|
+
* the order in `runTsgo` (internal flags first, passthrough last) would
|
|
408
|
+
* still let the user's `--pretty true` override at the tsgo level, but ttsc
|
|
409
|
+
* would have already committed to a structured-diagnostics post-process
|
|
410
|
+
* that no longer matches the actual output.
|
|
411
|
+
*/
|
|
308
412
|
function createTsgoDiagnosticArgs(options: TtscCommonOptions): string[] {
|
|
309
|
-
|
|
413
|
+
if (options.structuredDiagnostics !== true) return [];
|
|
414
|
+
if (forwardsInternalShadowFlag(options, "--pretty")) return [];
|
|
415
|
+
return ["--pretty", "false"];
|
|
310
416
|
}
|
|
311
417
|
|
|
418
|
+
/**
|
|
419
|
+
* Forward the `--singleThreaded` / `--checkers` knobs to a `tsgo` invocation.
|
|
420
|
+
* tsgo accepts both flags natively, so the no-plugin build lane only has to
|
|
421
|
+
* pass them through; the type-check and emit passes share this so the checker
|
|
422
|
+
* pool size stays consistent across both.
|
|
423
|
+
*/
|
|
424
|
+
function createTsgoThreadingArgs(options: TtscCommonOptions): string[] {
|
|
425
|
+
const args: string[] = [];
|
|
426
|
+
if (options.singleThreaded === true) {
|
|
427
|
+
args.push("--singleThreaded");
|
|
428
|
+
}
|
|
429
|
+
if (options.checkers !== undefined) {
|
|
430
|
+
args.push("--checkers", String(options.checkers));
|
|
431
|
+
}
|
|
432
|
+
return args;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/** Build the argument list for a native plugin `build`/`check` invocation. */
|
|
312
436
|
function createNativeBuildArgs(
|
|
313
437
|
execution: ReturnType<typeof resolveExecutionContext>,
|
|
314
438
|
options: TtscBuildOptions,
|
|
@@ -322,23 +446,29 @@ function createNativeBuildArgs(
|
|
|
322
446
|
];
|
|
323
447
|
if (options.emit === true) {
|
|
324
448
|
args.push("--emit");
|
|
325
|
-
} else if (options.emit === false) {
|
|
326
|
-
args.push("--noEmit");
|
|
327
449
|
}
|
|
328
450
|
if (options.outDir) {
|
|
329
451
|
args.push("--outDir=" + path.resolve(execution.cwd, options.outDir));
|
|
330
452
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
453
|
+
// Third-party transform hosts already treat the `check` subcommand as a
|
|
454
|
+
// quiet no-emit pass. Keep ttsc-owned build modifiers off that lane so older
|
|
455
|
+
// strict hosts do not reject unknown optional flags before analysis starts.
|
|
456
|
+
if (options.emit !== false) {
|
|
457
|
+
if (options.quiet === false) {
|
|
458
|
+
args.push("--verbose");
|
|
459
|
+
} else if (options.quiet === true) {
|
|
460
|
+
args.push("--quiet");
|
|
461
|
+
}
|
|
335
462
|
}
|
|
463
|
+
args.push(...createNativeTsgoArgs(options));
|
|
336
464
|
return args;
|
|
337
465
|
}
|
|
338
466
|
|
|
467
|
+
/** Build the argument list for a native plugin check/fix/format invocation. */
|
|
339
468
|
function createNativeCheckArgs(
|
|
340
469
|
execution: ReturnType<typeof resolveExecutionContext>,
|
|
341
470
|
options: TtscBuildOptions,
|
|
471
|
+
plugin: ITtscLoadedNativePlugin,
|
|
342
472
|
): string[] {
|
|
343
473
|
const args = [
|
|
344
474
|
nativeCheckSubcommand(options),
|
|
@@ -354,9 +484,85 @@ function createNativeCheckArgs(
|
|
|
354
484
|
} else if (options.quiet === true) {
|
|
355
485
|
args.push("--quiet");
|
|
356
486
|
}
|
|
487
|
+
args.push(...createNativeCheckThreadingArgs(options, plugin));
|
|
488
|
+
args.push(...createNativeTsgoArgs(options));
|
|
357
489
|
return args;
|
|
358
490
|
}
|
|
359
491
|
|
|
492
|
+
// `--singleThreaded` / `--checkers` are forwarded to native check-stage hosts
|
|
493
|
+
// only when the host is one ttsc itself owns (currently `@ttsc/lint`). #113
|
|
494
|
+
// forwarded both flags as bare CLI tokens to every native sidecar, then
|
|
495
|
+
// commit ad3443a reverted that across the board because a third-party host
|
|
496
|
+
// built before #113 has no `singleThreaded` / `checkers` flag in its
|
|
497
|
+
// `flag.FlagSet` and would exit 2 on the unknown flag — so
|
|
498
|
+
// `ttsc --singleThreaded` failed deterministically on every typia/nestia
|
|
499
|
+
// transform-plugin project.
|
|
500
|
+
//
|
|
501
|
+
// The performance ceiling that caused, though, is real: format/check passes
|
|
502
|
+
// through the lint sidecar are dominated by parallel parse + parallel rule
|
|
503
|
+
// walk, and with the threading knob silently dropped, MT and ST runs of
|
|
504
|
+
// `ttsc format` produced identical wall-clock numbers — the benchmark cell
|
|
505
|
+
// became a non-measurement. The lint sidecar is built and shipped from this
|
|
506
|
+
// repo, accepts both flags via `parseSubcommandFlags`, and threads them down
|
|
507
|
+
// to `loadProgram` (parse phase) and `engine.SetSerial` (rule walk). The host
|
|
508
|
+
// is identified by name (`@ttsc/lint`) so a third-party check-stage plugin
|
|
509
|
+
// keeps the strict-host behavior from ad3443a; only the host we control gets
|
|
510
|
+
// the bare flag. Transform-stage hosts are never reached by this path
|
|
511
|
+
// (they go through `createNativeBuildArgs`), so the typia/nestia regression
|
|
512
|
+
// remains pinned by `test_plugin_corpus_single_threaded_flag_does_not_break_a_native_plugin_build`.
|
|
513
|
+
function createNativeCheckThreadingArgs(
|
|
514
|
+
options: TtscCommonOptions,
|
|
515
|
+
plugin: ITtscLoadedNativePlugin,
|
|
516
|
+
): string[] {
|
|
517
|
+
if (!nativeHostAcceptsThreadingArgs(plugin)) return [];
|
|
518
|
+
const args: string[] = [];
|
|
519
|
+
if (options.singleThreaded === true) {
|
|
520
|
+
args.push("--singleThreaded");
|
|
521
|
+
}
|
|
522
|
+
if (options.checkers !== undefined) {
|
|
523
|
+
args.push("--checkers=" + String(options.checkers));
|
|
524
|
+
}
|
|
525
|
+
return args;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Return true when the loaded native check-stage host has declared
|
|
530
|
+
* `capabilities.threadingArgs` in its plugin descriptor.
|
|
531
|
+
*
|
|
532
|
+
* The lint sidecar (`packages/lint/src/index.ts::createTtscPlugin`) opts in
|
|
533
|
+
* because its `parseSubcommandFlags` handler accepts `--singleThreaded` and
|
|
534
|
+
* `--checkers` directly and threads them into `loadProgram` (parse phase)
|
|
535
|
+
* and `engine.SetSerial` (rule walk). Any other check-stage host that has
|
|
536
|
+
* not declared the capability is treated as a third-party binary whose flag
|
|
537
|
+
* set is unknown, matching the conservative default from commit ad3443a.
|
|
538
|
+
*
|
|
539
|
+
* The capability flag replaces the prior `plugin.name === "@ttsc/lint"`
|
|
540
|
+
* string check: routing on a descriptor field instead of the plugin name
|
|
541
|
+
* lets the next first-party check-stage plugin opt in without ttsc needing
|
|
542
|
+
* to learn its name. See `ITtscPluginCapabilities` and issue #125 for the
|
|
543
|
+
* broader CLI-parser cleanup this is the quick-win step of.
|
|
544
|
+
*/
|
|
545
|
+
function nativeHostAcceptsThreadingArgs(
|
|
546
|
+
plugin: ITtscLoadedNativePlugin,
|
|
547
|
+
): boolean {
|
|
548
|
+
return plugin.capabilities?.threadingArgs === true;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Forward the tsgo flags ttsc did not recognize to a native sidecar as one
|
|
553
|
+
* JSON-encoded `--tsgo-args` flag. The sidecar replays them through tsgo's own
|
|
554
|
+
* option parser onto `CompilerOptions`, so a flag like `ttsc --strict` reaches
|
|
555
|
+
* a plugin build the same way it reaches the plain tsgo lane. Encoded as a
|
|
556
|
+
* single token so the sidecars' unknown-flag filters keep it intact.
|
|
557
|
+
*/
|
|
558
|
+
function createNativeTsgoArgs(options: TtscCommonOptions): string[] {
|
|
559
|
+
const passthrough = options.passthrough;
|
|
560
|
+
if (passthrough === undefined || passthrough.length === 0) {
|
|
561
|
+
return [];
|
|
562
|
+
}
|
|
563
|
+
return ["--tsgo-args=" + JSON.stringify(passthrough)];
|
|
564
|
+
}
|
|
565
|
+
|
|
360
566
|
/**
|
|
361
567
|
* Decide which native plugin subcommand the lint sidecar should run for the
|
|
362
568
|
* current `runBuild` invocation. The launcher selects exactly one of `fix` /
|
|
@@ -369,6 +575,10 @@ function nativeCheckSubcommand(options: TtscBuildOptions): string {
|
|
|
369
575
|
return "check";
|
|
370
576
|
}
|
|
371
577
|
|
|
578
|
+
/**
|
|
579
|
+
* Serialize the plugin list to a compact JSON string for `--plugins-json=`.
|
|
580
|
+
* Only the fields the native binary protocol requires are included.
|
|
581
|
+
*/
|
|
372
582
|
function serializeNativePlugins(
|
|
373
583
|
plugins: readonly ITtscLoadedNativePlugin[],
|
|
374
584
|
): string {
|
|
@@ -381,6 +591,10 @@ function serializeNativePlugins(
|
|
|
381
591
|
);
|
|
382
592
|
}
|
|
383
593
|
|
|
594
|
+
/**
|
|
595
|
+
* Run every check-stage plugin in order, short-circuiting on the first non-zero
|
|
596
|
+
* exit. Aggregates diagnostics and output across all check plugins.
|
|
597
|
+
*/
|
|
384
598
|
function runNativeCheckPlugins(
|
|
385
599
|
options: TtscBuildOptions,
|
|
386
600
|
execution: ReturnType<typeof resolveExecutionContext>,
|
|
@@ -396,7 +610,7 @@ function runNativeCheckPlugins(
|
|
|
396
610
|
)) {
|
|
397
611
|
const result = runNativePluginCommand(
|
|
398
612
|
plugin,
|
|
399
|
-
createNativeCheckArgs(execution, options),
|
|
613
|
+
createNativeCheckArgs(execution, options, plugin),
|
|
400
614
|
options,
|
|
401
615
|
execution,
|
|
402
616
|
"ttsc.check",
|
|
@@ -409,6 +623,11 @@ function runNativeCheckPlugins(
|
|
|
409
623
|
return out;
|
|
410
624
|
}
|
|
411
625
|
|
|
626
|
+
/**
|
|
627
|
+
* Spawn a single native plugin binary with the given args and return the
|
|
628
|
+
* normalized build result. `label` is used in the thrown error message so the
|
|
629
|
+
* caller's context (e.g. `"ttsc.check"` or `"ttsc.build"`) is preserved.
|
|
630
|
+
*/
|
|
412
631
|
function runNativePluginCommand(
|
|
413
632
|
plugin: ITtscLoadedNativePlugin,
|
|
414
633
|
args: readonly string[],
|
|
@@ -416,7 +635,7 @@ function runNativePluginCommand(
|
|
|
416
635
|
execution: ReturnType<typeof resolveExecutionContext>,
|
|
417
636
|
label: string,
|
|
418
637
|
): TtscBuildResult {
|
|
419
|
-
const res =
|
|
638
|
+
const res = spawnNative(plugin.binary, args, {
|
|
420
639
|
cwd: execution.projectRoot,
|
|
421
640
|
env: nativePluginEnv(options.env, execution, plugin),
|
|
422
641
|
encoding: "utf8",
|
|
@@ -436,6 +655,14 @@ function runNativePluginCommand(
|
|
|
436
655
|
);
|
|
437
656
|
}
|
|
438
657
|
|
|
658
|
+
/**
|
|
659
|
+
* Merge two `TtscBuildResult` values into one.
|
|
660
|
+
*
|
|
661
|
+
* - `status`: the right status wins unless it is 0 (failure propagates).
|
|
662
|
+
* - `diagnostics`: concatenated left then right.
|
|
663
|
+
* - `emittedFiles`: right wins when present, otherwise left is kept.
|
|
664
|
+
* - `stdout`/`stderr`: concatenated left then right.
|
|
665
|
+
*/
|
|
439
666
|
export function appendBuildOutput(
|
|
440
667
|
left: TtscBuildResult,
|
|
441
668
|
right: TtscBuildResult,
|
|
@@ -450,36 +677,51 @@ export function appendBuildOutput(
|
|
|
450
677
|
});
|
|
451
678
|
}
|
|
452
679
|
|
|
680
|
+
/**
|
|
681
|
+
* Resolve all runtime context needed for a build: cwd, tsconfig path, project
|
|
682
|
+
* root, tsgo binary location, and the loaded native plugin list. Centralised
|
|
683
|
+
* here so every code path in `runBuild` shares the same resolution logic.
|
|
684
|
+
*/
|
|
453
685
|
function resolveExecutionContext(
|
|
454
|
-
options: TtscCommonOptions & { tsconfig?: string },
|
|
686
|
+
options: TtscCommonOptions & { emit?: boolean; tsconfig?: string },
|
|
455
687
|
) {
|
|
456
688
|
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
457
|
-
const
|
|
689
|
+
const project = readProjectConfig({
|
|
458
690
|
cwd,
|
|
691
|
+
projectRoot: options.projectRoot,
|
|
459
692
|
tsconfig: options.tsconfig,
|
|
460
693
|
});
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
: path.dirname(tsconfig);
|
|
694
|
+
const tsconfig = project.path;
|
|
695
|
+
const projectRoot = project.root;
|
|
464
696
|
const tsgo = resolveTsgo({ ...options, cwd: projectRoot });
|
|
465
|
-
const
|
|
466
|
-
const loaded =
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
697
|
+
const hasPlugins = hasProjectPluginEntries(project, options.plugins);
|
|
698
|
+
const loaded = hasPlugins
|
|
699
|
+
? loadProjectPlugins({
|
|
700
|
+
binary: resolveBinary(options) ?? "",
|
|
701
|
+
cacheDir: options.cacheDir ?? options.env?.TTSC_CACHE_DIR,
|
|
702
|
+
cwd,
|
|
703
|
+
entries: options.plugins,
|
|
704
|
+
projectRoot,
|
|
705
|
+
tsconfig,
|
|
706
|
+
})
|
|
707
|
+
: { nativePlugins: [] };
|
|
474
708
|
return {
|
|
475
709
|
cwd,
|
|
476
710
|
nativePlugins: loaded.nativePlugins,
|
|
711
|
+
projectNoEmit: project.compilerOptions.noEmit === true,
|
|
477
712
|
projectRoot,
|
|
713
|
+
rewriteRelativeImportExtensionsForEmit:
|
|
714
|
+
options.emit === true &&
|
|
715
|
+
project.compilerOptions.allowImportingTsExtensions === true,
|
|
478
716
|
tsgo,
|
|
479
717
|
tsconfig,
|
|
480
718
|
};
|
|
481
719
|
}
|
|
482
720
|
|
|
721
|
+
/**
|
|
722
|
+
* Extract `TSFILE: <path>` lines from tsgo stdout and return the absolute
|
|
723
|
+
* paths. tsgo emits these when `--listEmittedFiles` is passed.
|
|
724
|
+
*/
|
|
483
725
|
function parseEmittedFiles(stdout: string): string[] {
|
|
484
726
|
const out: string[] = [];
|
|
485
727
|
for (const line of stdout.split(/\r?\n/)) {
|
|
@@ -491,6 +733,10 @@ function parseEmittedFiles(stdout: string): string[] {
|
|
|
491
733
|
return out;
|
|
492
734
|
}
|
|
493
735
|
|
|
736
|
+
/**
|
|
737
|
+
* Remove `TSFILE:` lines from tsgo stdout so they do not appear in the
|
|
738
|
+
* user-facing output after they have been parsed into `emittedFiles`.
|
|
739
|
+
*/
|
|
494
740
|
function stripEmittedFileLines(stdout: string): string {
|
|
495
741
|
return stdout
|
|
496
742
|
.split(/\r?\n/)
|
|
@@ -499,10 +745,21 @@ function stripEmittedFileLines(stdout: string): string {
|
|
|
499
745
|
.replace(/\n+$/, "");
|
|
500
746
|
}
|
|
501
747
|
|
|
748
|
+
/**
|
|
749
|
+
* `TtscBuildResult` with `diagnostics` made optional. Used internally when
|
|
750
|
+
* diagnostics are parsed lazily from stdout/stderr by `normalizeBuildOutput`.
|
|
751
|
+
*/
|
|
502
752
|
type PartialBuildResult = Omit<TtscBuildResult, "diagnostics"> & {
|
|
503
753
|
diagnostics?: ITtscCompilerDiagnostic[];
|
|
504
754
|
};
|
|
505
755
|
|
|
756
|
+
/**
|
|
757
|
+
* Normalise a raw spawn result into a `TtscBuildResult`.
|
|
758
|
+
*
|
|
759
|
+
* When `diagnostics` is absent they are parsed from the text output. When the
|
|
760
|
+
* process exited non-zero but stderr is empty and stdout is non-empty, stdout
|
|
761
|
+
* is moved to stderr so the error is visible to callers who only check stderr.
|
|
762
|
+
*/
|
|
506
763
|
export function normalizeBuildOutput(
|
|
507
764
|
result: PartialBuildResult,
|
|
508
765
|
cwd?: string,
|
|
@@ -524,6 +781,11 @@ export function normalizeBuildOutput(
|
|
|
524
781
|
};
|
|
525
782
|
}
|
|
526
783
|
|
|
784
|
+
/**
|
|
785
|
+
* Parse structured diagnostics from the combined stderr+stdout text when the
|
|
786
|
+
* caller did not supply pre-parsed diagnostics. ANSI escape codes are stripped
|
|
787
|
+
* and `TSFILE:` / `Found N errors` summary lines are skipped.
|
|
788
|
+
*/
|
|
527
789
|
function parseCompilerDiagnostics(
|
|
528
790
|
result: Pick<TtscBuildResult, "stderr" | "stdout">,
|
|
529
791
|
cwd: string | undefined,
|
|
@@ -553,6 +815,16 @@ function parseCompilerDiagnostics(
|
|
|
553
815
|
return out;
|
|
554
816
|
}
|
|
555
817
|
|
|
818
|
+
/**
|
|
819
|
+
* Try to parse a single line as a TypeScript compiler diagnostic in one of
|
|
820
|
+
* three formats:
|
|
821
|
+
*
|
|
822
|
+
* - `file:line:col - category TSxxxx: message` (colon-separated, tsgo style)
|
|
823
|
+
* - `file(line,col): category TSxxxx: message` (paren style, classic tsc)
|
|
824
|
+
* - `category TSxxxx: message` (global, no file)
|
|
825
|
+
*
|
|
826
|
+
* Returns `null` when the line does not match any format.
|
|
827
|
+
*/
|
|
556
828
|
function parseDiagnosticLine(
|
|
557
829
|
line: string,
|
|
558
830
|
cwd: string | undefined,
|
|
@@ -599,6 +871,10 @@ function parseDiagnosticLine(
|
|
|
599
871
|
};
|
|
600
872
|
}
|
|
601
873
|
|
|
874
|
+
/**
|
|
875
|
+
* Return an absolute file path. When `file` is already absolute it is returned
|
|
876
|
+
* unchanged; relative paths are resolved against `cwd` when available.
|
|
877
|
+
*/
|
|
602
878
|
function normalizeDiagnosticFile(
|
|
603
879
|
file: string,
|
|
604
880
|
cwd: string | undefined,
|
|
@@ -609,6 +885,11 @@ function normalizeDiagnosticFile(
|
|
|
609
885
|
return path.resolve(cwd, file);
|
|
610
886
|
}
|
|
611
887
|
|
|
888
|
+
/**
|
|
889
|
+
* Map a raw category string (`"error"`, `"warning"`, `"suggestion"`,
|
|
890
|
+
* `"message"`) to the canonical `ITtscCompilerDiagnostic.Category`. Any
|
|
891
|
+
* unrecognised value is coerced to `"error"`.
|
|
892
|
+
*/
|
|
612
893
|
function normalizeDiagnosticCategory(
|
|
613
894
|
value: string,
|
|
614
895
|
): ITtscCompilerDiagnostic.Category {
|
|
@@ -620,10 +901,15 @@ function normalizeDiagnosticCategory(
|
|
|
620
901
|
: "error";
|
|
621
902
|
}
|
|
622
903
|
|
|
904
|
+
/**
|
|
905
|
+
* Parse a diagnostic code as a number when it is all digits (TS numeric codes
|
|
906
|
+
* like `2322`), or keep it as a string for plugin-defined alphanumeric codes.
|
|
907
|
+
*/
|
|
623
908
|
function normalizeDiagnosticCode(value: string): number | string {
|
|
624
909
|
return /^\d+$/.test(value) ? Number(value) : value;
|
|
625
910
|
}
|
|
626
911
|
|
|
912
|
+
/** Strip ANSI escape sequences from `text` for line-by-line diagnostic parsing. */
|
|
627
913
|
function stripAnsi(text: string): string {
|
|
628
914
|
return text.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
|
|
629
915
|
}
|