ttsc 0.12.4 → 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/ttsc/api_compile.go +15 -2
- package/cmd/ttsc/api_transform.go +15 -2
- package/cmd/ttsc/build.go +34 -4
- 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/driver/program.go +105 -4
- package/driver/rewrite.go +39 -4
- package/lib/compiler/internal/project/resolveProjectConfig.js +18 -0
- package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +5 -4
- package/lib/compiler/internal/runBuild.js +243 -47
- package/lib/compiler/internal/runBuild.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/prepareExecution.js +3 -0
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/runTtsc.js +71 -147
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtsx.js +104 -72
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.js +58 -23
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- 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/package.json +9 -9
- package/src/compiler/internal/project/resolveProjectConfig.ts +20 -0
- package/src/compiler/internal/runBuild.ts +288 -57
- package/src/flags/parser.ts +367 -0
- package/src/flags/schema.ts +570 -0
- package/src/launcher/internal/prepareExecution.ts +3 -0
- package/src/launcher/internal/runTtsc.ts +78 -136
- package/src/launcher/internal/runTtsx.ts +116 -74
- package/src/plugin/internal/loadProjectPlugins.ts +74 -27
- package/src/structures/ITtscPlugin.ts +26 -0
- 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/utility/flags_gen.go +30 -0
- package/utility/host.go +43 -33
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { INTERNAL_SHADOW_FLAGS, TERMINAL_FLAGS } from "../../flags/schema";
|
|
4
|
+
import {
|
|
5
|
+
hasProjectPluginEntries,
|
|
6
|
+
loadProjectPlugins,
|
|
7
|
+
} from "../../plugin/internal/loadProjectPlugins";
|
|
4
8
|
import type { ITtscCompilerDiagnostic } from "../../structures/ITtscCompilerDiagnostic";
|
|
5
9
|
import type { ITtscLoadedNativePlugin } from "../../structures/internal/ITtscLoadedNativePlugin";
|
|
6
10
|
import type { TtscBuildOptions } from "../../structures/internal/TtscBuildOptions";
|
|
7
11
|
import type { TtscBuildResult } from "../../structures/internal/TtscBuildResult";
|
|
8
12
|
import type { TtscCommonOptions } from "../../structures/internal/TtscCommonOptions";
|
|
9
|
-
import {
|
|
13
|
+
import { readProjectConfig } from "./project/readProjectConfig";
|
|
10
14
|
import { resolveBinary } from "./resolveBinary";
|
|
11
15
|
import { resolveTsgo } from "./resolveTsgo";
|
|
12
16
|
import {
|
|
@@ -16,6 +20,11 @@ import {
|
|
|
16
20
|
} from "./sharedHostHelpers";
|
|
17
21
|
import { outputText, spawnNative } from "./spawnNative";
|
|
18
22
|
|
|
23
|
+
type RunBuildOptions = TtscBuildOptions & {
|
|
24
|
+
skipDiagnosticsCheck?: boolean;
|
|
25
|
+
forceListEmittedFiles?: boolean;
|
|
26
|
+
};
|
|
27
|
+
|
|
19
28
|
/**
|
|
20
29
|
* Merge extra environment variables over `process.env`, always injecting
|
|
21
30
|
* `TTSC_NODE_BINARY` so child processes can re-invoke the same Node.js binary
|
|
@@ -61,24 +70,20 @@ function nativePluginEnv(
|
|
|
61
70
|
* Run `ttsc` against a tsconfig. Returns once the binary exits so the CLI can
|
|
62
71
|
* decide how to surface diagnostics. Does not throw on non-zero exit.
|
|
63
72
|
*/
|
|
64
|
-
export function runBuild(
|
|
65
|
-
options: TtscBuildOptions & {
|
|
66
|
-
skipDiagnosticsCheck?: boolean;
|
|
67
|
-
forceListEmittedFiles?: boolean;
|
|
68
|
-
} = {},
|
|
69
|
-
): TtscBuildResult {
|
|
73
|
+
export function runBuild(options: RunBuildOptions = {}): TtscBuildResult {
|
|
70
74
|
const execution = resolveExecutionContext(options);
|
|
75
|
+
const buildOptions = applyProjectNoEmit(options, execution);
|
|
71
76
|
if (execution.nativePlugins.length > 0) {
|
|
72
77
|
const compilers = execution.nativePlugins.filter(
|
|
73
78
|
(plugin) => plugin.stage === "transform",
|
|
74
79
|
);
|
|
75
|
-
const checked = runNativeCheckPlugins(
|
|
80
|
+
const checked = runNativeCheckPlugins(buildOptions, execution);
|
|
76
81
|
if (checked.status !== 0) {
|
|
77
82
|
return checked;
|
|
78
83
|
}
|
|
79
84
|
|
|
80
|
-
if (
|
|
81
|
-
if (
|
|
85
|
+
if (buildOptions.emit === false) {
|
|
86
|
+
if (buildOptions.format === true) {
|
|
82
87
|
// Format mode is write-only by contract: the lint sidecar
|
|
83
88
|
// already rewrote source files and reported nothing. Running
|
|
84
89
|
// tsgo --noEmit OR a transform compiler afterwards would either
|
|
@@ -97,12 +102,15 @@ export function runBuild(
|
|
|
97
102
|
assertSharedHostCompatibility(compilers, "emit");
|
|
98
103
|
return appendBuildOutput(
|
|
99
104
|
checked,
|
|
100
|
-
buildWithNativeCompilerPlugins(
|
|
105
|
+
buildWithNativeCompilerPlugins(buildOptions, execution, compilers),
|
|
101
106
|
);
|
|
102
107
|
}
|
|
108
|
+
if (checkPluginsReportTypeScriptDiagnostics(execution.nativePlugins)) {
|
|
109
|
+
return checked;
|
|
110
|
+
}
|
|
103
111
|
return appendBuildOutput(
|
|
104
112
|
checked,
|
|
105
|
-
runTsgo(execution, ["--noEmit"],
|
|
113
|
+
runTsgo(execution, ["--noEmit"], buildOptions),
|
|
106
114
|
);
|
|
107
115
|
}
|
|
108
116
|
|
|
@@ -111,26 +119,30 @@ export function runBuild(
|
|
|
111
119
|
assertSharedHostCompatibility(compilers, "emit");
|
|
112
120
|
result = appendBuildOutput(
|
|
113
121
|
checked,
|
|
114
|
-
buildWithNativeCompilerPlugins(
|
|
122
|
+
buildWithNativeCompilerPlugins(buildOptions, execution, compilers),
|
|
115
123
|
);
|
|
116
124
|
} else {
|
|
117
|
-
if (
|
|
118
|
-
|
|
125
|
+
if (
|
|
126
|
+
buildOptions.skipDiagnosticsCheck !== true &&
|
|
127
|
+
!checkPluginsReportTypeScriptDiagnostics(execution.nativePlugins) &&
|
|
128
|
+
!forwardsTerminalTsgoFlag(buildOptions)
|
|
129
|
+
) {
|
|
130
|
+
const tsgoChecked = runTsgo(execution, ["--noEmit"], buildOptions);
|
|
119
131
|
if (tsgoChecked.status !== 0) {
|
|
120
132
|
return appendBuildOutput(checked, tsgoChecked);
|
|
121
133
|
}
|
|
122
134
|
}
|
|
123
|
-
const args = createTsgoBuildArgs(execution,
|
|
124
|
-
listEmittedFiles:
|
|
135
|
+
const args = createTsgoBuildArgs(execution, buildOptions, {
|
|
136
|
+
listEmittedFiles: buildOptions.forceListEmittedFiles === true,
|
|
125
137
|
});
|
|
126
|
-
const emitted = runTsgoBuild(execution,
|
|
138
|
+
const emitted = runTsgoBuild(execution, buildOptions, args);
|
|
127
139
|
result = appendBuildOutput(checked, emitted);
|
|
128
140
|
}
|
|
129
141
|
|
|
130
142
|
return result;
|
|
131
143
|
}
|
|
132
144
|
|
|
133
|
-
if (
|
|
145
|
+
if (buildOptions.format === true) {
|
|
134
146
|
// Format mode is write-only by contract — see the matching
|
|
135
147
|
// short-circuit in the with-native-plugins branch above. When no
|
|
136
148
|
// native plugin is loaded there is nothing for `ttsc format` to
|
|
@@ -145,18 +157,100 @@ export function runBuild(
|
|
|
145
157
|
};
|
|
146
158
|
}
|
|
147
159
|
|
|
148
|
-
|
|
149
|
-
const checked = runTsgo(execution, ["--noEmit"], options);
|
|
150
|
-
if (checked.status !== 0) {
|
|
151
|
-
return checked;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const args = createTsgoBuildArgs(execution, options, {
|
|
160
|
+
const args = createTsgoBuildArgs(execution, buildOptions, {
|
|
156
161
|
listEmittedFiles:
|
|
157
|
-
|
|
162
|
+
buildOptions.emit !== false &&
|
|
163
|
+
buildOptions.forceListEmittedFiles === true,
|
|
164
|
+
noEmitOnError:
|
|
165
|
+
buildOptions.emit !== false &&
|
|
166
|
+
buildOptions.skipDiagnosticsCheck !== true &&
|
|
167
|
+
!forwardsTerminalTsgoFlag(buildOptions),
|
|
158
168
|
});
|
|
159
|
-
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
|
+
);
|
|
224
|
+
}
|
|
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
|
+
);
|
|
160
254
|
}
|
|
161
255
|
|
|
162
256
|
/**
|
|
@@ -186,7 +280,7 @@ function buildWithNativeCompilerPlugins(
|
|
|
186
280
|
function runTsgo(
|
|
187
281
|
execution: ReturnType<typeof resolveExecutionContext>,
|
|
188
282
|
extraArgs: readonly string[],
|
|
189
|
-
options:
|
|
283
|
+
options: RunBuildOptions,
|
|
190
284
|
): TtscBuildResult {
|
|
191
285
|
const res = spawnNative(
|
|
192
286
|
execution.tsgo.binary,
|
|
@@ -195,6 +289,8 @@ function runTsgo(
|
|
|
195
289
|
execution.tsconfig,
|
|
196
290
|
...extraArgs,
|
|
197
291
|
...createTsgoDiagnosticArgs(options),
|
|
292
|
+
...createTsgoThreadingArgs(options),
|
|
293
|
+
...(options.passthrough ?? []),
|
|
198
294
|
],
|
|
199
295
|
{
|
|
200
296
|
cwd: execution.projectRoot,
|
|
@@ -227,7 +323,7 @@ function runTsgo(
|
|
|
227
323
|
*/
|
|
228
324
|
function runTsgoBuild(
|
|
229
325
|
execution: ReturnType<typeof resolveExecutionContext>,
|
|
230
|
-
options:
|
|
326
|
+
options: RunBuildOptions,
|
|
231
327
|
args: readonly string[],
|
|
232
328
|
): TtscBuildResult {
|
|
233
329
|
const res = spawnNative(execution.tsgo.binary, args, {
|
|
@@ -249,7 +345,18 @@ function runTsgoBuild(
|
|
|
249
345
|
stderr: outputText(res.stderr),
|
|
250
346
|
};
|
|
251
347
|
const emittedFiles = parseEmittedFiles(result.stdout);
|
|
252
|
-
|
|
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) {
|
|
253
360
|
result.stdout = stripEmittedFileLines(result.stdout);
|
|
254
361
|
}
|
|
255
362
|
return normalizeBuildOutput(
|
|
@@ -261,12 +368,15 @@ function runTsgoBuild(
|
|
|
261
368
|
/** Build the argument list for a direct `tsgo` build invocation. */
|
|
262
369
|
function createTsgoBuildArgs(
|
|
263
370
|
execution: ReturnType<typeof resolveExecutionContext>,
|
|
264
|
-
options:
|
|
265
|
-
flags: { listEmittedFiles: boolean },
|
|
371
|
+
options: RunBuildOptions,
|
|
372
|
+
flags: { listEmittedFiles: boolean; noEmitOnError?: boolean },
|
|
266
373
|
): string[] {
|
|
267
374
|
const args = ["-p", execution.tsconfig];
|
|
268
375
|
if (options.emit === true) {
|
|
269
376
|
args.push("--noEmit", "false", "--emitDeclarationOnly", "false");
|
|
377
|
+
if (execution.rewriteRelativeImportExtensionsForEmit) {
|
|
378
|
+
args.push("--rewriteRelativeImportExtensions");
|
|
379
|
+
}
|
|
270
380
|
} else if (options.emit === false) {
|
|
271
381
|
args.push("--noEmit");
|
|
272
382
|
}
|
|
@@ -277,15 +387,49 @@ function createTsgoBuildArgs(
|
|
|
277
387
|
args.push("--listEmittedFiles");
|
|
278
388
|
}
|
|
279
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
|
+
}
|
|
280
395
|
return args;
|
|
281
396
|
}
|
|
282
397
|
|
|
283
398
|
/**
|
|
284
399
|
* Return `["--pretty", "false"]` when structured diagnostics are requested so
|
|
285
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.
|
|
286
411
|
*/
|
|
287
412
|
function createTsgoDiagnosticArgs(options: TtscCommonOptions): string[] {
|
|
288
|
-
|
|
413
|
+
if (options.structuredDiagnostics !== true) return [];
|
|
414
|
+
if (forwardsInternalShadowFlag(options, "--pretty")) return [];
|
|
415
|
+
return ["--pretty", "false"];
|
|
416
|
+
}
|
|
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;
|
|
289
433
|
}
|
|
290
434
|
|
|
291
435
|
/** Build the argument list for a native plugin `build`/`check` invocation. */
|
|
@@ -302,17 +446,21 @@ function createNativeBuildArgs(
|
|
|
302
446
|
];
|
|
303
447
|
if (options.emit === true) {
|
|
304
448
|
args.push("--emit");
|
|
305
|
-
} else if (options.emit === false) {
|
|
306
|
-
args.push("--noEmit");
|
|
307
449
|
}
|
|
308
450
|
if (options.outDir) {
|
|
309
451
|
args.push("--outDir=" + path.resolve(execution.cwd, options.outDir));
|
|
310
452
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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
|
+
}
|
|
315
462
|
}
|
|
463
|
+
args.push(...createNativeTsgoArgs(options));
|
|
316
464
|
return args;
|
|
317
465
|
}
|
|
318
466
|
|
|
@@ -320,6 +468,7 @@ function createNativeBuildArgs(
|
|
|
320
468
|
function createNativeCheckArgs(
|
|
321
469
|
execution: ReturnType<typeof resolveExecutionContext>,
|
|
322
470
|
options: TtscBuildOptions,
|
|
471
|
+
plugin: ITtscLoadedNativePlugin,
|
|
323
472
|
): string[] {
|
|
324
473
|
const args = [
|
|
325
474
|
nativeCheckSubcommand(options),
|
|
@@ -335,9 +484,85 @@ function createNativeCheckArgs(
|
|
|
335
484
|
} else if (options.quiet === true) {
|
|
336
485
|
args.push("--quiet");
|
|
337
486
|
}
|
|
487
|
+
args.push(...createNativeCheckThreadingArgs(options, plugin));
|
|
488
|
+
args.push(...createNativeTsgoArgs(options));
|
|
489
|
+
return args;
|
|
490
|
+
}
|
|
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
|
+
}
|
|
338
525
|
return args;
|
|
339
526
|
}
|
|
340
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
|
+
|
|
341
566
|
/**
|
|
342
567
|
* Decide which native plugin subcommand the lint sidecar should run for the
|
|
343
568
|
* current `runBuild` invocation. The launcher selects exactly one of `fix` /
|
|
@@ -385,7 +610,7 @@ function runNativeCheckPlugins(
|
|
|
385
610
|
)) {
|
|
386
611
|
const result = runNativePluginCommand(
|
|
387
612
|
plugin,
|
|
388
|
-
createNativeCheckArgs(execution, options),
|
|
613
|
+
createNativeCheckArgs(execution, options, plugin),
|
|
389
614
|
options,
|
|
390
615
|
execution,
|
|
391
616
|
"ttsc.check",
|
|
@@ -458,30 +683,36 @@ export function appendBuildOutput(
|
|
|
458
683
|
* here so every code path in `runBuild` shares the same resolution logic.
|
|
459
684
|
*/
|
|
460
685
|
function resolveExecutionContext(
|
|
461
|
-
options: TtscCommonOptions & { tsconfig?: string },
|
|
686
|
+
options: TtscCommonOptions & { emit?: boolean; tsconfig?: string },
|
|
462
687
|
) {
|
|
463
688
|
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
464
|
-
const
|
|
689
|
+
const project = readProjectConfig({
|
|
465
690
|
cwd,
|
|
691
|
+
projectRoot: options.projectRoot,
|
|
466
692
|
tsconfig: options.tsconfig,
|
|
467
693
|
});
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
: path.dirname(tsconfig);
|
|
694
|
+
const tsconfig = project.path;
|
|
695
|
+
const projectRoot = project.root;
|
|
471
696
|
const tsgo = resolveTsgo({ ...options, cwd: projectRoot });
|
|
472
|
-
const
|
|
473
|
-
const loaded =
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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: [] };
|
|
481
708
|
return {
|
|
482
709
|
cwd,
|
|
483
710
|
nativePlugins: loaded.nativePlugins,
|
|
711
|
+
projectNoEmit: project.compilerOptions.noEmit === true,
|
|
484
712
|
projectRoot,
|
|
713
|
+
rewriteRelativeImportExtensionsForEmit:
|
|
714
|
+
options.emit === true &&
|
|
715
|
+
project.compilerOptions.allowImportingTsExtensions === true,
|
|
485
716
|
tsgo,
|
|
486
717
|
tsconfig,
|
|
487
718
|
};
|