ttsc 0.12.4 → 0.13.1

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.
Files changed (54) hide show
  1. package/README.md +3 -0
  2. package/cmd/ttsc/api_compile.go +15 -2
  3. package/cmd/ttsc/api_transform.go +15 -2
  4. package/cmd/ttsc/build.go +34 -4
  5. package/cmd/ttsc/filter.go +70 -0
  6. package/cmd/ttsc/filter_strips_unknown_forwarded_flag_test.go +61 -0
  7. package/cmd/ttsc/flags_gen.go +30 -0
  8. package/driver/program.go +105 -4
  9. package/driver/rewrite.go +39 -4
  10. package/lib/compiler/internal/project/resolveProjectConfig.js +18 -0
  11. package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
  12. package/lib/compiler/internal/runBuild.d.ts +5 -4
  13. package/lib/compiler/internal/runBuild.js +358 -51
  14. package/lib/compiler/internal/runBuild.js.map +1 -1
  15. package/lib/flags/parser.d.ts +63 -0
  16. package/lib/flags/parser.js +254 -0
  17. package/lib/flags/parser.js.map +1 -0
  18. package/lib/flags/schema.d.ts +146 -0
  19. package/lib/flags/schema.js +423 -0
  20. package/lib/flags/schema.js.map +1 -0
  21. package/lib/launcher/internal/prepareExecution.js +3 -0
  22. package/lib/launcher/internal/prepareExecution.js.map +1 -1
  23. package/lib/launcher/internal/runTtsc.js +71 -147
  24. package/lib/launcher/internal/runTtsc.js.map +1 -1
  25. package/lib/launcher/internal/runTtsx.js +104 -72
  26. package/lib/launcher/internal/runTtsx.js.map +1 -1
  27. package/lib/plugin/internal/loadProjectPlugins.js +58 -23
  28. package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
  29. package/lib/structures/ITtscPlugin.d.ts +24 -0
  30. package/lib/structures/ITtscPluginCapabilities.d.ts +40 -0
  31. package/lib/structures/ITtscPluginCapabilities.js +3 -0
  32. package/lib/structures/ITtscPluginCapabilities.js.map +1 -0
  33. package/lib/structures/index.d.ts +1 -0
  34. package/lib/structures/index.js +1 -0
  35. package/lib/structures/index.js.map +1 -1
  36. package/lib/structures/internal/ITtscLoadedNativePlugin.d.ts +5 -0
  37. package/lib/structures/internal/TtscCommonOptions.d.ts +19 -0
  38. package/package.json +9 -9
  39. package/shim/ast/lint.go +6 -0
  40. package/src/compiler/internal/project/resolveProjectConfig.ts +20 -0
  41. package/src/compiler/internal/runBuild.ts +463 -56
  42. package/src/flags/parser.ts +367 -0
  43. package/src/flags/schema.ts +589 -0
  44. package/src/launcher/internal/prepareExecution.ts +3 -0
  45. package/src/launcher/internal/runTtsc.ts +78 -136
  46. package/src/launcher/internal/runTtsx.ts +116 -74
  47. package/src/plugin/internal/loadProjectPlugins.ts +74 -27
  48. package/src/structures/ITtscPlugin.ts +26 -0
  49. package/src/structures/ITtscPluginCapabilities.ts +41 -0
  50. package/src/structures/index.ts +1 -0
  51. package/src/structures/internal/ITtscLoadedNativePlugin.ts +5 -0
  52. package/src/structures/internal/TtscCommonOptions.ts +19 -0
  53. package/utility/flags_gen.go +30 -0
  54. package/utility/host.go +43 -33
@@ -0,0 +1,589 @@
1
+ // SOURCE-OF-TRUTH FLAG SCHEMA for the ttsc / ttsx command-line surface.
2
+ //
3
+ // One declaration per flag, consumed by every layer that needs to know about
4
+ // the flag:
5
+ //
6
+ // * `packages/ttsc/src/flags/parser.ts` — runtime TS parsing engine used
7
+ // by `runTtsc.ts` and `runTtsx.ts`.
8
+ // * `packages/ttsc/scripts/gen-flags.cjs` — codegen that emits Go
9
+ // allow-lists and the docs table.
10
+ // * `packages/ttsc/cmd/ttsc/flags_gen.go` — generated Go allow-list shared
11
+ // by `cmd/ttsc/*.go` and
12
+ // `utility/host.go`.
13
+ // * `packages/lint/linthost/flags_gen.go` — generated Go allow-list shared
14
+ // by lint subcommand parsers.
15
+ // * `website/src/content/docs/ttsc/flags.mdx` — generated reference table.
16
+ //
17
+ // The generator runs from `pnpm format`; its committed output is the spec the
18
+ // Go side reads, just like the `gen_shims:hand-maintained` pattern from
19
+ // AGENTS.md §2.1. Editing the generated files by hand is rejected by the
20
+ // `format` check.
21
+
22
+ /**
23
+ * Subcommands the ttsc CLI dispatches to. The bare `"ttsc"` entry covers the
24
+ * default lane (no explicit subcommand, e.g. `ttsc -p tsconfig.json`).
25
+ */
26
+ export type TtscSubcommand =
27
+ | "ttsc"
28
+ | "build"
29
+ | "check"
30
+ | "fix"
31
+ | "format"
32
+ | "prepare"
33
+ | "clean";
34
+
35
+ /**
36
+ * Subcommands the ttsx CLI dispatches to. ttsx exposes a single lane today but
37
+ * the schema mirrors ttsc's shape so future subcommands plug in without a
38
+ * second engine.
39
+ */
40
+ export type TtsxSubcommand = "ttsx";
41
+
42
+ export type AnySubcommand = TtscSubcommand | TtsxSubcommand;
43
+
44
+ /**
45
+ * Layers a flag can be consumed by. The order reflects the runtime pipeline:
46
+ *
47
+ * Launcher → runBuild → tsgo / native sidecars (host, lint).
48
+ *
49
+ * A flag must declare at least one consumer. `forwardTo` declares where the
50
+ * flag travels when the consuming layer does not absorb it (e.g. ttsc-owned
51
+ * flags that the JS launcher consumes and re-emits as different tsgo flags).
52
+ */
53
+ export type FlagLayer =
54
+ | "launcher" // JS layer (`runTtsc.ts` / `runTtsx.ts`)
55
+ | "runBuild" // JS layer (`runBuild.ts`) — internally adds the flag to tsgo
56
+ | "tsgo" // tsgo binary (TypeScript-Go option parser)
57
+ | "host" // native shared host (`utility/host.go`, `cmd/ttsc/build.go`)
58
+ | "lint"; // native lint subcommand (`packages/lint/linthost/*.go`)
59
+
60
+ /**
61
+ * Argument shape of a flag.
62
+ *
63
+ * - `boolean` — `--flag` / `--flag=true` / `--flag=false`. No value token is
64
+ * consumed.
65
+ * - `value` — `--flag value` or `--flag=value`. Required value.
66
+ * - `valueOptional` — `--flag` standalone is allowed; if followed by a non-flag
67
+ * token that token is consumed as the value. (Currently unused — declared for
68
+ * future flags like `--watch [path]`.)
69
+ */
70
+ export type FlagKind = "boolean" | "value" | "valueOptional";
71
+
72
+ /**
73
+ * Validation predicate for `value`-kind flags. `none` is the default (accept
74
+ * any string).
75
+ */
76
+ export type ValueValidator = "none" | "positiveInt";
77
+
78
+ /**
79
+ * One CLI flag's complete contract. Every layer's behaviour is structural —
80
+ * which subcommands accept the flag, where it is consumed, where it is
81
+ * forwarded, whether it is terminal (prints and exits) — so the next layer
82
+ * never silently drops the flag.
83
+ */
84
+ export interface FlagSpec {
85
+ /**
86
+ * Canonical flag name including leading dashes (`"--singleThreaded"`). The
87
+ * generator uses this as the map key in the Go allow-list and as the first
88
+ * column of the docs table.
89
+ */
90
+ readonly name: string;
91
+
92
+ /**
93
+ * Alternative spellings (`"-p"`, `"--project"` for `--tsconfig`). The parsing
94
+ * engine treats every alias as an equivalent of `name`.
95
+ */
96
+ readonly aliases?: readonly string[];
97
+
98
+ /** Argument shape: boolean / required value / optional value. */
99
+ readonly kind: FlagKind;
100
+
101
+ /**
102
+ * For `value` flags: optional validator. `positiveInt` mirrors the
103
+ * `--checkers minValue:1` constraint tsgo enforces.
104
+ */
105
+ readonly validator?: ValueValidator;
106
+
107
+ /** Subcommands that accept this flag. */
108
+ readonly subcommands: readonly AnySubcommand[];
109
+
110
+ /**
111
+ * Layers that read the flag into a typed option. Order matters only for
112
+ * documentation; the parsing engine merges across layers.
113
+ */
114
+ readonly consumedBy: readonly FlagLayer[];
115
+
116
+ /**
117
+ * Where the flag travels when the consuming layer does not absorb it. Default
118
+ * for any flag a layer does not consume is `"tsgo"` — i.e. the launcher
119
+ * forwards an unknown flag to tsgo via passthrough. Setting this to
120
+ * `undefined` while `consumedBy: ["launcher"]` is set means the flag is
121
+ * intentionally consumed-not-forwarded (e.g. ttsc-internal `--binary`).
122
+ */
123
+ readonly forwardTo?: FlagLayer;
124
+
125
+ /**
126
+ * Terminal flags ask the underlying tool to print something and exit
127
+ * (`--help`, `--version`, `--showConfig`, `--listFilesOnly`, `--all`,
128
+ * `--init`). ttsc must not wrap them in a pre-emit pass — that is how the
129
+ * `--showConfig prints twice` bug appeared (RC-2).
130
+ */
131
+ readonly terminal?: boolean;
132
+
133
+ /**
134
+ * `true` when ttsc may add this flag to tsgo internally and post-process the
135
+ * output. If the user also forwards the same flag, ttsc keeps the
136
+ * user-visible behaviour (no double-print, no swallowed output). The shadow
137
+ * check is structural rather than `passthrough.includes("…")`.
138
+ *
139
+ * Currently true for `--listEmittedFiles`, `--noEmit`, `--pretty`.
140
+ */
141
+ readonly internalShadow?: boolean;
142
+
143
+ /**
144
+ * Native sidecar capability that must be declared before ttsc sends this flag
145
+ * as a bare CLI argument. Everything else routes through `--tsgo-args` or
146
+ * stays in the JS launcher.
147
+ */
148
+ readonly nativeCapability?: "diagnosticsTiming" | "threadingArgs";
149
+
150
+ /** Human description for `--help` and the docs table. */
151
+ readonly description: string;
152
+ }
153
+
154
+ /**
155
+ * Single source of truth for every flag the ttsc / ttsx CLI accepts. New flags
156
+ * are added here and only here; the generator rebuilds the parsers and the Go
157
+ * allow-lists from this table.
158
+ *
159
+ * Constraints enforced by the parser and the generator:
160
+ *
161
+ * 1. Every flag is uniquely identified by `name`; aliases must not collide with
162
+ * other flags' `name` or `aliases`.
163
+ * 2. A flag listed in `consumedBy: ["launcher"]` without a `forwardTo`
164
+ * consumes-not-forwards. The generator flags this in the docs and the Go
165
+ * allow-list so the boundary is explicit.
166
+ * 3. A flag with `subcommands` covering `clean` or `prepare` is parsed by the
167
+ * project-args lane; the parsing engine accepts the same flag in build /
168
+ * check / fix / format without a separate parser.
169
+ */
170
+ export const FLAG_SCHEMA: readonly FlagSpec[] = [
171
+ // -------------------------------------------------------------------------
172
+ // ttsc — terminal flags (print and exit; never wrapped in pre-emit pass)
173
+ // -------------------------------------------------------------------------
174
+ {
175
+ name: "--help",
176
+ aliases: ["-h"],
177
+ kind: "boolean",
178
+ subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"],
179
+ consumedBy: ["launcher"],
180
+ terminal: true,
181
+ description: "Show command help and exit.",
182
+ },
183
+ {
184
+ name: "--version",
185
+ aliases: ["-v"],
186
+ kind: "boolean",
187
+ subcommands: ["ttsc", "ttsx"],
188
+ consumedBy: ["launcher"],
189
+ terminal: true,
190
+ description: "Print the launcher version and exit.",
191
+ },
192
+
193
+ // -------------------------------------------------------------------------
194
+ // Project location: shared by every subcommand.
195
+ // -------------------------------------------------------------------------
196
+ {
197
+ name: "--tsconfig",
198
+ aliases: ["-p", "--project"],
199
+ kind: "value",
200
+ subcommands: [
201
+ "ttsc",
202
+ "ttsx",
203
+ "build",
204
+ "check",
205
+ "fix",
206
+ "format",
207
+ "prepare",
208
+ "clean",
209
+ ],
210
+ consumedBy: ["launcher", "host", "lint"],
211
+ description: "Resolve project settings from this tsconfig.",
212
+ },
213
+ {
214
+ name: "--cwd",
215
+ kind: "value",
216
+ subcommands: [
217
+ "ttsc",
218
+ "ttsx",
219
+ "build",
220
+ "check",
221
+ "fix",
222
+ "format",
223
+ "prepare",
224
+ "clean",
225
+ ],
226
+ consumedBy: ["launcher", "host", "lint"],
227
+ description: "Resolve project-relative paths from this directory.",
228
+ },
229
+
230
+ // -------------------------------------------------------------------------
231
+ // Emit / build mode.
232
+ // -------------------------------------------------------------------------
233
+ {
234
+ name: "--emit",
235
+ kind: "boolean",
236
+ subcommands: ["ttsc", "build", "check"],
237
+ consumedBy: ["launcher", "runBuild", "host", "lint"],
238
+ description: "Force emitted files during build.",
239
+ },
240
+ {
241
+ name: "--noEmit",
242
+ kind: "boolean",
243
+ subcommands: ["ttsc", "build", "check"],
244
+ consumedBy: ["launcher", "runBuild", "host", "lint"],
245
+ internalShadow: true,
246
+ description: "Force analysis-only build with no file writes.",
247
+ },
248
+ {
249
+ name: "--outDir",
250
+ kind: "value",
251
+ subcommands: ["ttsc", "build", "check"],
252
+ consumedBy: ["launcher", "host", "lint"],
253
+ description: "Override compilerOptions.outDir for this invocation.",
254
+ },
255
+
256
+ // -------------------------------------------------------------------------
257
+ // Watch mode (launcher only).
258
+ // -------------------------------------------------------------------------
259
+ {
260
+ name: "--watch",
261
+ aliases: ["-w"],
262
+ kind: "boolean",
263
+ subcommands: ["ttsc", "build", "check"],
264
+ consumedBy: ["launcher"],
265
+ description: "Rebuild when project files change.",
266
+ },
267
+ {
268
+ name: "--preserveWatchOutput",
269
+ kind: "boolean",
270
+ subcommands: ["ttsc", "build", "check"],
271
+ consumedBy: ["launcher"],
272
+ description: "Do not clear the screen between watch rebuilds.",
273
+ },
274
+
275
+ // -------------------------------------------------------------------------
276
+ // Output verbosity.
277
+ // -------------------------------------------------------------------------
278
+ {
279
+ name: "--quiet",
280
+ kind: "boolean",
281
+ subcommands: ["ttsc", "build", "check", "fix", "format"],
282
+ consumedBy: ["launcher", "host", "lint"],
283
+ description: "Keep build output quiet (default).",
284
+ },
285
+ {
286
+ name: "--verbose",
287
+ kind: "boolean",
288
+ subcommands: ["ttsc", "build", "check", "fix", "format"],
289
+ consumedBy: ["launcher", "host", "lint"],
290
+ description: "Print the build summary and emitted files.",
291
+ },
292
+
293
+ // -------------------------------------------------------------------------
294
+ // tsgo-binary / cache plumbing (consumed by launcher, not forwarded).
295
+ // -------------------------------------------------------------------------
296
+ {
297
+ name: "--binary",
298
+ kind: "value",
299
+ subcommands: [
300
+ "ttsc",
301
+ "ttsx",
302
+ "build",
303
+ "check",
304
+ "fix",
305
+ "format",
306
+ "prepare",
307
+ "clean",
308
+ ],
309
+ consumedBy: ["launcher"],
310
+ description: "Use an explicit tsgo binary.",
311
+ },
312
+ {
313
+ name: "--cache-dir",
314
+ kind: "value",
315
+ subcommands: [
316
+ "ttsc",
317
+ "ttsx",
318
+ "build",
319
+ "check",
320
+ "fix",
321
+ "format",
322
+ "prepare",
323
+ "clean",
324
+ ],
325
+ consumedBy: ["launcher"],
326
+ description: "Override the runner and source-plugin cache root.",
327
+ },
328
+
329
+ // -------------------------------------------------------------------------
330
+ // Threading (tsgo-native; lint host opts in via capability).
331
+ // -------------------------------------------------------------------------
332
+ {
333
+ name: "--singleThreaded",
334
+ kind: "boolean",
335
+ subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"],
336
+ consumedBy: ["launcher", "runBuild", "tsgo", "host", "lint"],
337
+ nativeCapability: "threadingArgs",
338
+ description: "Run TypeScript-Go single-threaded (one checker).",
339
+ },
340
+ {
341
+ name: "--checkers",
342
+ kind: "value",
343
+ validator: "positiveInt",
344
+ subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"],
345
+ consumedBy: ["launcher", "runBuild", "tsgo", "host", "lint"],
346
+ nativeCapability: "threadingArgs",
347
+ description: "Type-checker pool size (default: TypeScript-Go's).",
348
+ },
349
+
350
+ // -------------------------------------------------------------------------
351
+ // ttsx-specific options.
352
+ // -------------------------------------------------------------------------
353
+ {
354
+ name: "--require",
355
+ aliases: ["-r"],
356
+ kind: "value",
357
+ subcommands: ["ttsx"],
358
+ consumedBy: ["launcher"],
359
+ description: "Preload a module before the entrypoint (ttsx).",
360
+ },
361
+ {
362
+ name: "--no-plugins",
363
+ kind: "boolean",
364
+ subcommands: ["ttsx"],
365
+ consumedBy: ["launcher"],
366
+ description: "Build the project without ttsc plugins (ttsx).",
367
+ },
368
+
369
+ // -------------------------------------------------------------------------
370
+ // tsgo-internal flags ttsc adds itself; users may also forward them.
371
+ // Declaring them keeps the launcher's parser from treating them as
372
+ // unknown forwarded flags whose value token gets misclassified.
373
+ // -------------------------------------------------------------------------
374
+ {
375
+ name: "--listEmittedFiles",
376
+ kind: "boolean",
377
+ subcommands: ["ttsc", "build", "check"],
378
+ consumedBy: ["runBuild", "tsgo"],
379
+ forwardTo: "tsgo",
380
+ internalShadow: true,
381
+ description:
382
+ "Print the list of emitted files (forwarded to tsgo; ttsc keeps the lines when forwarded).",
383
+ },
384
+ {
385
+ name: "--pretty",
386
+ kind: "value",
387
+ subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"],
388
+ consumedBy: ["tsgo"],
389
+ forwardTo: "tsgo",
390
+ internalShadow: true,
391
+ description: "Toggle tsgo pretty-printed diagnostics (forwarded to tsgo).",
392
+ },
393
+ {
394
+ name: "--diagnostics",
395
+ kind: "boolean",
396
+ subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"],
397
+ consumedBy: ["runBuild", "tsgo", "lint"],
398
+ forwardTo: "tsgo",
399
+ nativeCapability: "diagnosticsTiming",
400
+ description:
401
+ "Print compiler performance information; plugin-backed ttsc runs add plugin wall-clock timings.",
402
+ },
403
+ {
404
+ name: "--extendedDiagnostics",
405
+ kind: "boolean",
406
+ subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"],
407
+ consumedBy: ["runBuild", "tsgo", "lint"],
408
+ forwardTo: "tsgo",
409
+ nativeCapability: "diagnosticsTiming",
410
+ description:
411
+ "Print detailed compiler performance information; plugin-backed ttsc runs add plugin wall-clock timings.",
412
+ },
413
+ {
414
+ name: "--showConfig",
415
+ kind: "boolean",
416
+ subcommands: ["ttsc", "build", "check"],
417
+ consumedBy: ["tsgo"],
418
+ forwardTo: "tsgo",
419
+ terminal: true,
420
+ description: "Print the resolved tsconfig and exit (forwarded to tsgo).",
421
+ },
422
+ {
423
+ name: "--listFilesOnly",
424
+ kind: "boolean",
425
+ subcommands: ["ttsc", "build", "check"],
426
+ consumedBy: ["tsgo"],
427
+ forwardTo: "tsgo",
428
+ terminal: true,
429
+ description: "Print the project file list and exit (forwarded to tsgo).",
430
+ },
431
+ {
432
+ name: "--all",
433
+ kind: "boolean",
434
+ subcommands: ["ttsc", "build", "check"],
435
+ consumedBy: ["tsgo"],
436
+ forwardTo: "tsgo",
437
+ terminal: true,
438
+ description: "Print the full tsgo CLI help and exit.",
439
+ },
440
+ {
441
+ name: "--init",
442
+ kind: "boolean",
443
+ subcommands: ["ttsc", "build", "check"],
444
+ consumedBy: ["tsgo"],
445
+ forwardTo: "tsgo",
446
+ terminal: true,
447
+ description: "Write a starter tsconfig.json and exit (forwarded to tsgo).",
448
+ },
449
+
450
+ // -------------------------------------------------------------------------
451
+ // `--tsgo-args` — JSON-encoded passthrough envelope the launcher emits on
452
+ // the way down to native sidecars. The sidecars decode it back into the
453
+ // tsgo argv. Listed here so the schema describes every flag the Go layers
454
+ // accept, not just the user-facing ones.
455
+ // -------------------------------------------------------------------------
456
+ {
457
+ name: "--tsgo-args",
458
+ kind: "value",
459
+ subcommands: [
460
+ "ttsc",
461
+ "build",
462
+ "check",
463
+ "fix",
464
+ "format",
465
+ "prepare",
466
+ "clean",
467
+ ],
468
+ consumedBy: ["host", "lint"],
469
+ description:
470
+ "JSON-encoded tsgo passthrough argv (internal: emitted by runBuild).",
471
+ },
472
+ {
473
+ name: "--plugins-json",
474
+ kind: "value",
475
+ subcommands: ["build", "check", "fix", "format"],
476
+ consumedBy: ["host", "lint"],
477
+ description:
478
+ "JSON-encoded ttsc plugin manifest (internal: emitted by runBuild).",
479
+ },
480
+ {
481
+ name: "--manifest",
482
+ kind: "value",
483
+ subcommands: ["build"],
484
+ consumedBy: ["host"],
485
+ description:
486
+ "Write emitted file list as JSON to this path (host build only).",
487
+ },
488
+ {
489
+ name: "--file",
490
+ kind: "value",
491
+ subcommands: ["build", "check"],
492
+ consumedBy: ["lint"],
493
+ description:
494
+ "Absolute or cwd-relative path of the .ts file to transform (lint transform only).",
495
+ },
496
+ {
497
+ name: "--out",
498
+ kind: "value",
499
+ subcommands: ["build", "check"],
500
+ consumedBy: ["lint"],
501
+ description:
502
+ "Write transform output to PATH (lint transform only; default: stdout).",
503
+ },
504
+ ];
505
+
506
+ /**
507
+ * Lookup of `name` and every alias → its canonical FlagSpec. Built once at
508
+ * module load so the parsing engine has O(1) flag resolution.
509
+ */
510
+ export const FLAG_BY_NAME: ReadonlyMap<string, FlagSpec> = buildFlagIndex();
511
+
512
+ function buildFlagIndex(): ReadonlyMap<string, FlagSpec> {
513
+ const index = new Map<string, FlagSpec>();
514
+ for (const flag of FLAG_SCHEMA) {
515
+ register(index, flag.name, flag);
516
+ for (const alias of flag.aliases ?? []) {
517
+ register(index, alias, flag);
518
+ }
519
+ }
520
+ return index;
521
+ }
522
+
523
+ function register(
524
+ index: Map<string, FlagSpec>,
525
+ key: string,
526
+ flag: FlagSpec,
527
+ ): void {
528
+ const existing = index.get(key);
529
+ if (existing && existing !== flag) {
530
+ throw new Error(
531
+ `ttsc flag schema: duplicate token ${JSON.stringify(key)} claimed by ${existing.name} and ${flag.name}`,
532
+ );
533
+ }
534
+ index.set(key, flag);
535
+ }
536
+
537
+ /**
538
+ * Terminal flags as a derived set. Used by `runBuild.ts` to skip the pre-emit
539
+ * pass when one is present.
540
+ */
541
+ export const TERMINAL_FLAGS: ReadonlySet<string> = new Set(
542
+ FLAG_SCHEMA.flatMap((flag) =>
543
+ flag.terminal === true ? [flag.name, ...(flag.aliases ?? [])] : [],
544
+ ),
545
+ );
546
+
547
+ /**
548
+ * `internalShadow: true` flags as a derived set. Used by `runBuild.ts` to
549
+ * detect a user-forwarded copy of a flag ttsc adds internally so the
550
+ * post-processor does not eat the user's output.
551
+ */
552
+ export const INTERNAL_SHADOW_FLAGS: ReadonlySet<string> = new Set(
553
+ FLAG_SCHEMA.flatMap((flag) =>
554
+ flag.internalShadow === true ? [flag.name, ...(flag.aliases ?? [])] : [],
555
+ ),
556
+ );
557
+
558
+ /** Tokens (canonical name + aliases) accepted in `subcommand`. */
559
+ export function flagsForSubcommand(subcommand: AnySubcommand): FlagSpec[] {
560
+ return FLAG_SCHEMA.filter((flag) => flag.subcommands.includes(subcommand));
561
+ }
562
+
563
+ /**
564
+ * Allow-list map for the Go layer named `layer` (`"host"` or `"lint"`):
565
+ * flag-name (no leading dashes) → whether the flag takes a value token. The
566
+ * generator emits a literal Go map with the same shape, but this function is
567
+ * the runtime equivalent — used in tests to verify the generated Go matches the
568
+ * schema.
569
+ */
570
+ export function buildGoAllowList(layer: "host" | "lint"): Map<string, boolean> {
571
+ const out = new Map<string, boolean>();
572
+ for (const flag of FLAG_SCHEMA) {
573
+ if (!flag.consumedBy.includes(layer)) continue;
574
+ const takesValue = flag.kind === "value" || flag.kind === "valueOptional";
575
+ for (const name of [flag.name, ...(flag.aliases ?? [])]) {
576
+ const key = name.replace(/^--?/, "");
577
+ // The Go side strips the leading dashes; if two flags collide on the
578
+ // stripped key (e.g. `-p` vs `--project`), use the value-taking shape.
579
+ const existing = out.get(key);
580
+ if (existing !== undefined && existing !== takesValue) {
581
+ throw new Error(
582
+ `ttsc flag schema: conflicting Go allow-list entry for ${JSON.stringify(key)} on layer ${layer}`,
583
+ );
584
+ }
585
+ out.set(key, takesValue);
586
+ }
587
+ }
588
+ return out;
589
+ }
@@ -109,14 +109,17 @@ function buildProject(
109
109
  fs.mkdirSync(path.dirname(context.emitDir), { recursive: true });
110
110
  const result = runBuild({
111
111
  binary: options.binary,
112
+ checkers: options.checkers,
112
113
  cwd: context.root,
113
114
  emit: true,
114
115
  env: options.env,
115
116
  forceListEmittedFiles: true,
116
117
  cacheDir: context.pluginCacheDir,
117
118
  outDir: context.emitDir,
119
+ passthrough: options.passthrough,
118
120
  plugins: options.plugins,
119
121
  quiet: true,
122
+ singleThreaded: options.singleThreaded,
120
123
  tsconfig: context.tsconfig,
121
124
  });
122
125
  if (result.status === 0) {