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