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.
Files changed (151) hide show
  1. package/cmd/platform/main.go +9 -0
  2. package/cmd/ttsc/api_compile.go +122 -101
  3. package/cmd/ttsc/api_compile_branches_test.go +55 -57
  4. package/cmd/ttsc/api_transform.go +67 -49
  5. package/cmd/ttsc/api_transform_branches_test.go +30 -32
  6. package/cmd/ttsc/build.go +141 -108
  7. package/cmd/ttsc/build_branches_test.go +95 -97
  8. package/cmd/ttsc/filter.go +70 -0
  9. package/cmd/ttsc/filter_strips_unknown_forwarded_flag_test.go +61 -0
  10. package/cmd/ttsc/flags_gen.go +30 -0
  11. package/cmd/ttsc/helpers_test.go +39 -28
  12. package/cmd/ttsc/main.go +111 -104
  13. package/cmd/ttsc/main_alias_branches_test.go +21 -23
  14. package/cmd/ttscserver/main.go +96 -90
  15. package/cmd/ttscserver/run_lsp_prefers_tsgo_flag_test.go +22 -22
  16. package/cmd/ttscserver/run_lsp_reports_server_error_test.go +22 -22
  17. package/cmd/ttscserver/run_lsp_uses_tsgo_environment_test.go +23 -23
  18. package/cmd/utility-host/main.go +30 -23
  19. package/driver/lsp.go +53 -7
  20. package/driver/plugins.go +88 -72
  21. package/driver/program.go +464 -340
  22. package/driver/rewrite.go +477 -380
  23. package/internal/lspserver/lsp_envelope.go +64 -64
  24. package/internal/lspserver/lsp_frame.go +60 -60
  25. package/internal/lspserver/lsp_plugin.go +57 -57
  26. package/internal/lspserver/lsp_proxy.go +265 -251
  27. package/internal/lspserver/lsp_server.go +155 -144
  28. package/lib/TtscCompiler.d.ts +7 -0
  29. package/lib/TtscCompiler.js +7 -0
  30. package/lib/TtscCompiler.js.map +1 -1
  31. package/lib/compiler/internal/buildNativeCompiler.d.ts +11 -1
  32. package/lib/compiler/internal/buildNativeCompiler.js +23 -3
  33. package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
  34. package/lib/compiler/internal/compileProjectInMemory.d.ts +9 -0
  35. package/lib/compiler/internal/compileProjectInMemory.js +36 -21
  36. package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
  37. package/lib/compiler/internal/paths.d.ts +27 -0
  38. package/lib/compiler/internal/paths.js +70 -0
  39. package/lib/compiler/internal/paths.js.map +1 -0
  40. package/lib/compiler/internal/project/readProjectConfig.d.ts +9 -1
  41. package/lib/compiler/internal/project/readProjectConfig.js +80 -1
  42. package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
  43. package/lib/compiler/internal/project/resolveProjectConfig.d.ts +13 -1
  44. package/lib/compiler/internal/project/resolveProjectConfig.js +42 -1
  45. package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
  46. package/lib/compiler/internal/resolveBinary.d.ts +9 -1
  47. package/lib/compiler/internal/resolveBinary.js +17 -1
  48. package/lib/compiler/internal/resolveBinary.js.map +1 -1
  49. package/lib/compiler/internal/resolveEmittedJavaScript.d.ts +16 -1
  50. package/lib/compiler/internal/resolveEmittedJavaScript.js +42 -10
  51. package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
  52. package/lib/compiler/internal/resolveTsgo.d.ts +21 -1
  53. package/lib/compiler/internal/resolveTsgo.js +23 -1
  54. package/lib/compiler/internal/resolveTsgo.js.map +1 -1
  55. package/lib/compiler/internal/runBuild.d.ts +24 -4
  56. package/lib/compiler/internal/runBuild.js +355 -93
  57. package/lib/compiler/internal/runBuild.js.map +1 -1
  58. package/lib/compiler/internal/runSingleFileEmit.d.ts +10 -1
  59. package/lib/compiler/internal/runSingleFileEmit.js +14 -1
  60. package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
  61. package/lib/compiler/internal/sharedHostHelpers.d.ts +5 -0
  62. package/lib/compiler/internal/sharedHostHelpers.js +6 -2
  63. package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
  64. package/lib/compiler/internal/spawnNative.d.ts +21 -0
  65. package/lib/compiler/internal/spawnNative.js +59 -0
  66. package/lib/compiler/internal/spawnNative.js.map +1 -0
  67. package/lib/compiler/internal/transformProjectInMemory.d.ts +15 -1
  68. package/lib/compiler/internal/transformProjectInMemory.js +57 -61
  69. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  70. package/lib/flags/parser.d.ts +63 -0
  71. package/lib/flags/parser.js +254 -0
  72. package/lib/flags/parser.js.map +1 -0
  73. package/lib/flags/schema.d.ts +147 -0
  74. package/lib/flags/schema.js +405 -0
  75. package/lib/flags/schema.js.map +1 -0
  76. package/lib/launcher/internal/getCompilerVersionText.js +7 -0
  77. package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
  78. package/lib/launcher/internal/prepareExecution.js +35 -0
  79. package/lib/launcher/internal/prepareExecution.js.map +1 -1
  80. package/lib/launcher/internal/resolveCacheDir.d.ts +9 -0
  81. package/lib/launcher/internal/resolveCacheDir.js +22 -0
  82. package/lib/launcher/internal/resolveCacheDir.js.map +1 -0
  83. package/lib/launcher/internal/resolveTtscserverBinary.js +2 -0
  84. package/lib/launcher/internal/resolveTtscserverBinary.js.map +1 -1
  85. package/lib/launcher/internal/runTtsc.d.ts +10 -0
  86. package/lib/launcher/internal/runTtsc.js +90 -153
  87. package/lib/launcher/internal/runTtsc.js.map +1 -1
  88. package/lib/launcher/internal/runTtscserver.js +10 -35
  89. package/lib/launcher/internal/runTtscserver.js.map +1 -1
  90. package/lib/launcher/internal/runTtsx.d.ts +8 -0
  91. package/lib/launcher/internal/runTtsx.js +137 -79
  92. package/lib/launcher/internal/runTtsx.js.map +1 -1
  93. package/lib/plugin/internal/buildSourcePlugin.d.ts +38 -0
  94. package/lib/plugin/internal/buildSourcePlugin.js +40 -17
  95. package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
  96. package/lib/plugin/internal/loadProjectPlugins.d.ts +26 -0
  97. package/lib/plugin/internal/loadProjectPlugins.js +90 -41
  98. package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
  99. package/lib/structures/ITtscCompilerTransformation.d.ts +15 -2
  100. package/lib/structures/ITtscPlugin.d.ts +24 -0
  101. package/lib/structures/ITtscPluginCapabilities.d.ts +29 -0
  102. package/lib/structures/ITtscPluginCapabilities.js +3 -0
  103. package/lib/structures/ITtscPluginCapabilities.js.map +1 -0
  104. package/lib/structures/index.d.ts +1 -0
  105. package/lib/structures/index.js +1 -0
  106. package/lib/structures/index.js.map +1 -1
  107. package/lib/structures/internal/ITtscLoadedNativePlugin.d.ts +5 -0
  108. package/lib/structures/internal/TtscCommonOptions.d.ts +19 -0
  109. package/lib/structures/internal/TtscSingleFileEmitOptions.d.ts +0 -2
  110. package/package.json +9 -9
  111. package/shim/ast/shim.go +24 -0
  112. package/shim/checker/shim.go +35 -0
  113. package/shim/core/shim.go +17 -0
  114. package/shim/diagnosticwriter/shim.go +4 -0
  115. package/shim/lsp/shim.go +3 -3
  116. package/shim/printer/shim.go +20 -0
  117. package/src/TtscCompiler.ts +7 -0
  118. package/src/compiler/internal/buildNativeCompiler.ts +23 -3
  119. package/src/compiler/internal/compileProjectInMemory.ts +34 -25
  120. package/src/compiler/internal/paths.ts +68 -0
  121. package/src/compiler/internal/project/readProjectConfig.ts +86 -1
  122. package/src/compiler/internal/project/resolveProjectConfig.ts +44 -1
  123. package/src/compiler/internal/resolveBinary.ts +17 -1
  124. package/src/compiler/internal/resolveEmittedJavaScript.ts +43 -12
  125. package/src/compiler/internal/resolveTsgo.ts +29 -1
  126. package/src/compiler/internal/runBuild.ts +398 -112
  127. package/src/compiler/internal/runSingleFileEmit.ts +14 -1
  128. package/src/compiler/internal/sharedHostHelpers.ts +7 -7
  129. package/src/compiler/internal/spawnNative.ts +64 -0
  130. package/src/compiler/internal/transformProjectInMemory.ts +47 -67
  131. package/src/flags/parser.ts +367 -0
  132. package/src/flags/schema.ts +570 -0
  133. package/src/launcher/internal/getCompilerVersionText.ts +7 -0
  134. package/src/launcher/internal/prepareExecution.ts +35 -0
  135. package/src/launcher/internal/resolveCacheDir.ts +19 -0
  136. package/src/launcher/internal/resolveTtscserverBinary.ts +2 -0
  137. package/src/launcher/internal/runTtsc.ts +97 -143
  138. package/src/launcher/internal/runTtscserver.ts +9 -1
  139. package/src/launcher/internal/runTtsx.ts +149 -82
  140. package/src/plugin/internal/buildSourcePlugin.ts +40 -14
  141. package/src/plugin/internal/loadProjectPlugins.ts +116 -45
  142. package/src/structures/ITtscCompilerTransformation.ts +15 -2
  143. package/src/structures/ITtscPlugin.ts +26 -1
  144. package/src/structures/ITtscPluginCapabilities.ts +29 -0
  145. package/src/structures/index.ts +1 -0
  146. package/src/structures/internal/ITtscLoadedNativePlugin.ts +5 -0
  147. package/src/structures/internal/TtscCommonOptions.ts +19 -0
  148. package/src/structures/internal/TtscSingleFileEmitOptions.ts +0 -2
  149. package/tools/gen_shims/main.go +43 -8
  150. package/utility/flags_gen.go +30 -0
  151. package/utility/host.go +304 -245
@@ -0,0 +1,570 @@
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
37
+ * but 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
64
+ * token is consumed.
65
+ * * `value` — `--flag value` or `--flag=value`. Required value.
66
+ * * `valueOptional` — `--flag` standalone is allowed; if followed by a
67
+ * non-flag token that token is consumed as the value.
68
+ * (Currently unused — declared for future flags like
69
+ * `--watch [path]`.)
70
+ */
71
+ export type FlagKind = "boolean" | "value" | "valueOptional";
72
+
73
+ /**
74
+ * Validation predicate for `value`-kind flags. `none` is the default
75
+ * (accept any string).
76
+ */
77
+ export type ValueValidator = "none" | "positiveInt";
78
+
79
+ /**
80
+ * One CLI flag's complete contract. Every layer's behaviour is structural —
81
+ * which subcommands accept the flag, where it is consumed, where it is
82
+ * forwarded, whether it is terminal (prints and exits) — so the next layer
83
+ * never silently drops the flag.
84
+ */
85
+ export interface FlagSpec {
86
+ /**
87
+ * Canonical flag name including leading dashes (`"--singleThreaded"`). The
88
+ * generator uses this as the map key in the Go allow-list and as the first
89
+ * column of the docs table.
90
+ */
91
+ readonly name: string;
92
+
93
+ /**
94
+ * Alternative spellings (`"-p"`, `"--project"` for `--tsconfig`). The
95
+ * parsing engine treats every alias as an equivalent of `name`.
96
+ */
97
+ readonly aliases?: readonly string[];
98
+
99
+ /** Argument shape: boolean / required value / optional value. */
100
+ readonly kind: FlagKind;
101
+
102
+ /**
103
+ * For `value` flags: optional validator. `positiveInt` mirrors the
104
+ * `--checkers minValue:1` constraint tsgo enforces.
105
+ */
106
+ readonly validator?: ValueValidator;
107
+
108
+ /** Subcommands that accept this flag. */
109
+ readonly subcommands: readonly AnySubcommand[];
110
+
111
+ /**
112
+ * Layers that read the flag into a typed option. Order matters only for
113
+ * documentation; the parsing engine merges across layers.
114
+ */
115
+ readonly consumedBy: readonly FlagLayer[];
116
+
117
+ /**
118
+ * Where the flag travels when the consuming layer does not absorb it.
119
+ * Default for any flag a layer does not consume is `"tsgo"` — i.e. the
120
+ * launcher forwards an unknown flag to tsgo via passthrough. Setting this
121
+ * to `undefined` while `consumedBy: ["launcher"]` is set means the flag is
122
+ * intentionally consumed-not-forwarded (e.g. ttsc-internal `--binary`).
123
+ */
124
+ readonly forwardTo?: FlagLayer;
125
+
126
+ /**
127
+ * Terminal flags ask the underlying tool to print something and exit
128
+ * (`--help`, `--version`, `--showConfig`, `--listFilesOnly`, `--all`,
129
+ * `--init`). ttsc must not wrap them in a pre-emit pass — that is how the
130
+ * `--showConfig prints twice` bug appeared (RC-2).
131
+ */
132
+ readonly terminal?: boolean;
133
+
134
+ /**
135
+ * `true` when ttsc may add this flag to tsgo internally and post-process
136
+ * the output. If the user also forwards the same flag, ttsc keeps the
137
+ * user-visible behaviour (no double-print, no swallowed output). The
138
+ * shadow check is structural rather than `passthrough.includes("…")`.
139
+ *
140
+ * Currently true for `--listEmittedFiles`, `--noEmit`, `--pretty`.
141
+ */
142
+ readonly internalShadow?: boolean;
143
+
144
+ /**
145
+ * Reserved for future use: native hosts that opt in via
146
+ * `capabilities.threadingArgs`. The flag travels to such hosts as a bare
147
+ * CLI argument; everything else routes through `--tsgo-args`.
148
+ */
149
+ readonly nativeCapability?: "threadingArgs";
150
+
151
+ /** Human description for `--help` and the docs table. */
152
+ readonly description: string;
153
+ }
154
+
155
+ /**
156
+ * Single source of truth for every flag the ttsc / ttsx CLI accepts. New
157
+ * flags are added here and only here; the generator rebuilds the parsers and
158
+ * the Go allow-lists from this table.
159
+ *
160
+ * Constraints enforced by the parser and the generator:
161
+ *
162
+ * 1. Every flag is uniquely identified by `name`; aliases must not collide
163
+ * with other flags' `name` or `aliases`.
164
+ * 2. A flag listed in `consumedBy: ["launcher"]` without a `forwardTo`
165
+ * consumes-not-forwards. The generator flags this in the docs and the
166
+ * Go allow-list so the boundary is explicit.
167
+ * 3. A flag with `subcommands` covering `clean` or `prepare` is parsed by
168
+ * the project-args lane; the parsing engine accepts the same flag in
169
+ * build / check / fix / format without a separate parser.
170
+ */
171
+ export const FLAG_SCHEMA: readonly FlagSpec[] = [
172
+ // -------------------------------------------------------------------------
173
+ // ttsc — terminal flags (print and exit; never wrapped in pre-emit pass)
174
+ // -------------------------------------------------------------------------
175
+ {
176
+ name: "--help",
177
+ aliases: ["-h"],
178
+ kind: "boolean",
179
+ subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"],
180
+ consumedBy: ["launcher"],
181
+ terminal: true,
182
+ description: "Show command help and exit.",
183
+ },
184
+ {
185
+ name: "--version",
186
+ aliases: ["-v"],
187
+ kind: "boolean",
188
+ subcommands: ["ttsc", "ttsx"],
189
+ consumedBy: ["launcher"],
190
+ terminal: true,
191
+ description: "Print the launcher version and exit.",
192
+ },
193
+
194
+ // -------------------------------------------------------------------------
195
+ // Project location: shared by every subcommand.
196
+ // -------------------------------------------------------------------------
197
+ {
198
+ name: "--tsconfig",
199
+ aliases: ["-p", "--project"],
200
+ kind: "value",
201
+ subcommands: [
202
+ "ttsc",
203
+ "ttsx",
204
+ "build",
205
+ "check",
206
+ "fix",
207
+ "format",
208
+ "prepare",
209
+ "clean",
210
+ ],
211
+ consumedBy: ["launcher", "host", "lint"],
212
+ description: "Resolve project settings from this tsconfig.",
213
+ },
214
+ {
215
+ name: "--cwd",
216
+ kind: "value",
217
+ subcommands: [
218
+ "ttsc",
219
+ "ttsx",
220
+ "build",
221
+ "check",
222
+ "fix",
223
+ "format",
224
+ "prepare",
225
+ "clean",
226
+ ],
227
+ consumedBy: ["launcher", "host", "lint"],
228
+ description: "Resolve project-relative paths from this directory.",
229
+ },
230
+
231
+ // -------------------------------------------------------------------------
232
+ // Emit / build mode.
233
+ // -------------------------------------------------------------------------
234
+ {
235
+ name: "--emit",
236
+ kind: "boolean",
237
+ subcommands: ["ttsc", "build", "check"],
238
+ consumedBy: ["launcher", "runBuild", "host", "lint"],
239
+ description: "Force emitted files during build.",
240
+ },
241
+ {
242
+ name: "--noEmit",
243
+ kind: "boolean",
244
+ subcommands: ["ttsc", "build", "check"],
245
+ consumedBy: ["launcher", "runBuild", "host", "lint"],
246
+ internalShadow: true,
247
+ description: "Force analysis-only build with no file writes.",
248
+ },
249
+ {
250
+ name: "--outDir",
251
+ kind: "value",
252
+ subcommands: ["ttsc", "build", "check"],
253
+ consumedBy: ["launcher", "host", "lint"],
254
+ description: "Override compilerOptions.outDir for this invocation.",
255
+ },
256
+
257
+ // -------------------------------------------------------------------------
258
+ // Watch mode (launcher only).
259
+ // -------------------------------------------------------------------------
260
+ {
261
+ name: "--watch",
262
+ aliases: ["-w"],
263
+ kind: "boolean",
264
+ subcommands: ["ttsc", "build", "check"],
265
+ consumedBy: ["launcher"],
266
+ description: "Rebuild when project files change.",
267
+ },
268
+ {
269
+ name: "--preserveWatchOutput",
270
+ kind: "boolean",
271
+ subcommands: ["ttsc", "build", "check"],
272
+ consumedBy: ["launcher"],
273
+ description: "Do not clear the screen between watch rebuilds.",
274
+ },
275
+
276
+ // -------------------------------------------------------------------------
277
+ // Output verbosity.
278
+ // -------------------------------------------------------------------------
279
+ {
280
+ name: "--quiet",
281
+ kind: "boolean",
282
+ subcommands: ["ttsc", "build", "check", "fix", "format"],
283
+ consumedBy: ["launcher", "host", "lint"],
284
+ description: "Keep build output quiet (default).",
285
+ },
286
+ {
287
+ name: "--verbose",
288
+ kind: "boolean",
289
+ subcommands: ["ttsc", "build", "check", "fix", "format"],
290
+ consumedBy: ["launcher", "host", "lint"],
291
+ description: "Print the build summary and emitted files.",
292
+ },
293
+
294
+ // -------------------------------------------------------------------------
295
+ // tsgo-binary / cache plumbing (consumed by launcher, not forwarded).
296
+ // -------------------------------------------------------------------------
297
+ {
298
+ name: "--binary",
299
+ kind: "value",
300
+ subcommands: [
301
+ "ttsc",
302
+ "ttsx",
303
+ "build",
304
+ "check",
305
+ "fix",
306
+ "format",
307
+ "prepare",
308
+ "clean",
309
+ ],
310
+ consumedBy: ["launcher"],
311
+ description: "Use an explicit tsgo binary.",
312
+ },
313
+ {
314
+ name: "--cache-dir",
315
+ kind: "value",
316
+ subcommands: [
317
+ "ttsc",
318
+ "ttsx",
319
+ "build",
320
+ "check",
321
+ "fix",
322
+ "format",
323
+ "prepare",
324
+ "clean",
325
+ ],
326
+ consumedBy: ["launcher"],
327
+ description: "Override the runner and source-plugin cache root.",
328
+ },
329
+
330
+ // -------------------------------------------------------------------------
331
+ // Threading (tsgo-native; lint host opts in via capability).
332
+ // -------------------------------------------------------------------------
333
+ {
334
+ name: "--singleThreaded",
335
+ kind: "boolean",
336
+ subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"],
337
+ consumedBy: ["launcher", "runBuild", "tsgo", "host", "lint"],
338
+ nativeCapability: "threadingArgs",
339
+ description: "Run TypeScript-Go single-threaded (one checker).",
340
+ },
341
+ {
342
+ name: "--checkers",
343
+ kind: "value",
344
+ validator: "positiveInt",
345
+ subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"],
346
+ consumedBy: ["launcher", "runBuild", "tsgo", "host", "lint"],
347
+ nativeCapability: "threadingArgs",
348
+ description: "Type-checker pool size (default: TypeScript-Go's).",
349
+ },
350
+
351
+ // -------------------------------------------------------------------------
352
+ // ttsx-specific options.
353
+ // -------------------------------------------------------------------------
354
+ {
355
+ name: "--require",
356
+ aliases: ["-r"],
357
+ kind: "value",
358
+ subcommands: ["ttsx"],
359
+ consumedBy: ["launcher"],
360
+ description: "Preload a module before the entrypoint (ttsx).",
361
+ },
362
+ {
363
+ name: "--no-plugins",
364
+ kind: "boolean",
365
+ subcommands: ["ttsx"],
366
+ consumedBy: ["launcher"],
367
+ description: "Build the project without ttsc plugins (ttsx).",
368
+ },
369
+
370
+ // -------------------------------------------------------------------------
371
+ // tsgo-internal flags ttsc adds itself; users may also forward them.
372
+ // Declaring them keeps the launcher's parser from treating them as
373
+ // unknown forwarded flags whose value token gets misclassified.
374
+ // -------------------------------------------------------------------------
375
+ {
376
+ name: "--listEmittedFiles",
377
+ kind: "boolean",
378
+ subcommands: ["ttsc", "build", "check"],
379
+ consumedBy: ["runBuild", "tsgo"],
380
+ forwardTo: "tsgo",
381
+ internalShadow: true,
382
+ description:
383
+ "Print the list of emitted files (forwarded to tsgo; ttsc keeps the lines when forwarded).",
384
+ },
385
+ {
386
+ name: "--pretty",
387
+ kind: "value",
388
+ subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"],
389
+ consumedBy: ["tsgo"],
390
+ forwardTo: "tsgo",
391
+ internalShadow: true,
392
+ description: "Toggle tsgo pretty-printed diagnostics (forwarded to tsgo).",
393
+ },
394
+ {
395
+ name: "--showConfig",
396
+ kind: "boolean",
397
+ subcommands: ["ttsc", "build", "check"],
398
+ consumedBy: ["tsgo"],
399
+ forwardTo: "tsgo",
400
+ terminal: true,
401
+ description: "Print the resolved tsconfig and exit (forwarded to tsgo).",
402
+ },
403
+ {
404
+ name: "--listFilesOnly",
405
+ kind: "boolean",
406
+ subcommands: ["ttsc", "build", "check"],
407
+ consumedBy: ["tsgo"],
408
+ forwardTo: "tsgo",
409
+ terminal: true,
410
+ description: "Print the project file list and exit (forwarded to tsgo).",
411
+ },
412
+ {
413
+ name: "--all",
414
+ kind: "boolean",
415
+ subcommands: ["ttsc", "build", "check"],
416
+ consumedBy: ["tsgo"],
417
+ forwardTo: "tsgo",
418
+ terminal: true,
419
+ description: "Print the full tsgo CLI help and exit.",
420
+ },
421
+ {
422
+ name: "--init",
423
+ kind: "boolean",
424
+ subcommands: ["ttsc", "build", "check"],
425
+ consumedBy: ["tsgo"],
426
+ forwardTo: "tsgo",
427
+ terminal: true,
428
+ description: "Write a starter tsconfig.json and exit (forwarded to tsgo).",
429
+ },
430
+
431
+ // -------------------------------------------------------------------------
432
+ // `--tsgo-args` — JSON-encoded passthrough envelope the launcher emits on
433
+ // the way down to native sidecars. The sidecars decode it back into the
434
+ // tsgo argv. Listed here so the schema describes every flag the Go layers
435
+ // accept, not just the user-facing ones.
436
+ // -------------------------------------------------------------------------
437
+ {
438
+ name: "--tsgo-args",
439
+ kind: "value",
440
+ subcommands: [
441
+ "ttsc",
442
+ "build",
443
+ "check",
444
+ "fix",
445
+ "format",
446
+ "prepare",
447
+ "clean",
448
+ ],
449
+ consumedBy: ["host", "lint"],
450
+ description:
451
+ "JSON-encoded tsgo passthrough argv (internal: emitted by runBuild).",
452
+ },
453
+ {
454
+ name: "--plugins-json",
455
+ kind: "value",
456
+ subcommands: ["build", "check", "fix", "format"],
457
+ consumedBy: ["host", "lint"],
458
+ description:
459
+ "JSON-encoded ttsc plugin manifest (internal: emitted by runBuild).",
460
+ },
461
+ {
462
+ name: "--manifest",
463
+ kind: "value",
464
+ subcommands: ["build"],
465
+ consumedBy: ["host"],
466
+ description:
467
+ "Write emitted file list as JSON to this path (host build only).",
468
+ },
469
+ {
470
+ name: "--file",
471
+ kind: "value",
472
+ subcommands: ["build", "check"],
473
+ consumedBy: ["lint"],
474
+ description:
475
+ "Absolute or cwd-relative path of the .ts file to transform (lint transform only).",
476
+ },
477
+ {
478
+ name: "--out",
479
+ kind: "value",
480
+ subcommands: ["build", "check"],
481
+ consumedBy: ["lint"],
482
+ description:
483
+ "Write transform output to PATH (lint transform only; default: stdout).",
484
+ },
485
+ ];
486
+
487
+ /**
488
+ * Lookup of `name` and every alias → its canonical FlagSpec. Built once at
489
+ * module load so the parsing engine has O(1) flag resolution.
490
+ */
491
+ export const FLAG_BY_NAME: ReadonlyMap<string, FlagSpec> = buildFlagIndex();
492
+
493
+ function buildFlagIndex(): ReadonlyMap<string, FlagSpec> {
494
+ const index = new Map<string, FlagSpec>();
495
+ for (const flag of FLAG_SCHEMA) {
496
+ register(index, flag.name, flag);
497
+ for (const alias of flag.aliases ?? []) {
498
+ register(index, alias, flag);
499
+ }
500
+ }
501
+ return index;
502
+ }
503
+
504
+ function register(
505
+ index: Map<string, FlagSpec>,
506
+ key: string,
507
+ flag: FlagSpec,
508
+ ): void {
509
+ const existing = index.get(key);
510
+ if (existing && existing !== flag) {
511
+ throw new Error(
512
+ `ttsc flag schema: duplicate token ${JSON.stringify(key)} claimed by ${existing.name} and ${flag.name}`,
513
+ );
514
+ }
515
+ index.set(key, flag);
516
+ }
517
+
518
+ /**
519
+ * Terminal flags as a derived set. Used by `runBuild.ts` to skip the
520
+ * pre-emit pass when one is present.
521
+ */
522
+ export const TERMINAL_FLAGS: ReadonlySet<string> = new Set(
523
+ FLAG_SCHEMA.flatMap((flag) =>
524
+ flag.terminal === true ? [flag.name, ...(flag.aliases ?? [])] : [],
525
+ ),
526
+ );
527
+
528
+ /**
529
+ * `internalShadow: true` flags as a derived set. Used by `runBuild.ts` to
530
+ * detect a user-forwarded copy of a flag ttsc adds internally so the
531
+ * post-processor does not eat the user's output.
532
+ */
533
+ export const INTERNAL_SHADOW_FLAGS: ReadonlySet<string> = new Set(
534
+ FLAG_SCHEMA.flatMap((flag) =>
535
+ flag.internalShadow === true ? [flag.name, ...(flag.aliases ?? [])] : [],
536
+ ),
537
+ );
538
+
539
+ /** Tokens (canonical name + aliases) accepted in `subcommand`. */
540
+ export function flagsForSubcommand(subcommand: AnySubcommand): FlagSpec[] {
541
+ return FLAG_SCHEMA.filter((flag) => flag.subcommands.includes(subcommand));
542
+ }
543
+
544
+ /**
545
+ * Allow-list map for the Go layer named `layer` (`"host"` or `"lint"`):
546
+ * flag-name (no leading dashes) → whether the flag takes a value token.
547
+ * The generator emits a literal Go map with the same shape, but this
548
+ * function is the runtime equivalent — used in tests to verify the
549
+ * generated Go matches the schema.
550
+ */
551
+ export function buildGoAllowList(layer: "host" | "lint"): Map<string, boolean> {
552
+ const out = new Map<string, boolean>();
553
+ for (const flag of FLAG_SCHEMA) {
554
+ if (!flag.consumedBy.includes(layer)) continue;
555
+ const takesValue = flag.kind === "value" || flag.kind === "valueOptional";
556
+ for (const name of [flag.name, ...(flag.aliases ?? [])]) {
557
+ const key = name.replace(/^--?/, "");
558
+ // The Go side strips the leading dashes; if two flags collide on the
559
+ // stripped key (e.g. `-p` vs `--project`), use the value-taking shape.
560
+ const existing = out.get(key);
561
+ if (existing !== undefined && existing !== takesValue) {
562
+ throw new Error(
563
+ `ttsc flag schema: conflicting Go allow-list entry for ${JSON.stringify(key)} on layer ${layer}`,
564
+ );
565
+ }
566
+ out.set(key, takesValue);
567
+ }
568
+ }
569
+ return out;
570
+ }
@@ -23,6 +23,7 @@ export function getCompilerVersionText(
23
23
  return `ttsc ${readOwnPackageVersion()} (${outputText(res.stdout).trim()})`;
24
24
  }
25
25
 
26
+ /** Coerce a spawnSync stdio field (string | Buffer | null) to a plain string. */
26
27
  function outputText(value: string | Buffer | null | undefined): string {
27
28
  if (value == null) {
28
29
  return "";
@@ -30,6 +31,12 @@ function outputText(value: string | Buffer | null | undefined): string {
30
31
  return typeof value === "string" ? value : value.toString("utf8");
31
32
  }
32
33
 
34
+ /**
35
+ * Read the `version` field from the `@ttsc/ttsc` package.json that sits three
36
+ * directories above the compiled launcher output (`lib/launcher/internal/`).
37
+ * Returns `"0.0.0"` on any I/O or parse failure so the banner is always safe to
38
+ * display.
39
+ */
33
40
  function readOwnPackageVersion(): string {
34
41
  try {
35
42
  const file = path.resolve(__dirname, "..", "..", "..", "package.json");
@@ -7,7 +7,14 @@ import { resolveEmittedJavaScript } from "../../compiler/internal/resolveEmitted
7
7
  import { runBuild } from "../../compiler/internal/runBuild";
8
8
  import type { TtscCommonOptions } from "../../structures/internal/TtscCommonOptions";
9
9
 
10
+ /** Subdirectory name that isolates concurrent ttsx processes by PID. */
10
11
  const PROCESS_CACHE_KEY = String(process.pid);
12
+ /**
13
+ * Maximum number of ancestor directories above the project root that the
14
+ * virtual filesystem overlay mirrors. Three levels covers the common monorepo
15
+ * layout (workspace-root → packages → package-root) so `node_modules` symlinks
16
+ * resolve correctly without reaching an unsafe boundary.
17
+ */
11
18
  const MAX_VIRTUAL_PARENT_DEPTH = 3;
12
19
 
13
20
  /** Build the owning project and locate the emitted JavaScript entry for `ttsx`. */
@@ -102,14 +109,17 @@ function buildProject(
102
109
  fs.mkdirSync(path.dirname(context.emitDir), { recursive: true });
103
110
  const result = runBuild({
104
111
  binary: options.binary,
112
+ checkers: options.checkers,
105
113
  cwd: context.root,
106
114
  emit: true,
107
115
  env: options.env,
108
116
  forceListEmittedFiles: true,
109
117
  cacheDir: context.pluginCacheDir,
110
118
  outDir: context.emitDir,
119
+ passthrough: options.passthrough,
111
120
  plugins: options.plugins,
112
121
  quiet: true,
122
+ singleThreaded: options.singleThreaded,
113
123
  tsconfig: context.tsconfig,
114
124
  });
115
125
  if (result.status === 0) {
@@ -162,6 +172,7 @@ function linkVirtualEntry(
162
172
  entry: fs.Dirent,
163
173
  ): void {
164
174
  if (entry.isDirectory()) {
175
+ // Use junction points on Windows; plain symlinks elsewhere.
165
176
  fs.symlinkSync(
166
177
  realEntry,
167
178
  virtualEntry,
@@ -171,15 +182,25 @@ function linkVirtualEntry(
171
182
  }
172
183
  if (entry.isFile()) {
173
184
  try {
185
+ // Hard-link first: cheap, preserves inode, no extra disk usage.
174
186
  fs.linkSync(realEntry, virtualEntry);
175
187
  } catch {
188
+ // Cross-device or unsupported filesystem: fall back to a full copy.
176
189
  fs.copyFileSync(realEntry, virtualEntry);
177
190
  }
178
191
  return;
179
192
  }
193
+ // Symlinks (and other special entries) are re-symlinked as-is.
180
194
  fs.symlinkSync(realEntry, virtualEntry);
181
195
  }
182
196
 
197
+ /**
198
+ * Walk from `projectRoot` upward (up to `MAX_VIRTUAL_PARENT_DEPTH` steps),
199
+ * stopping early at a workspace root (`pnpm-workspace.yaml` or `.git`). The
200
+ * collected directories are reversed so callers can iterate outermost-first,
201
+ * which lets inner symlinks override outer ones without conflicting mkdir
202
+ * calls.
203
+ */
183
204
  function collectLinkDirectories(projectRoot: string): string[] {
184
205
  const out: string[] = [];
185
206
  let current = projectRoot;
@@ -207,6 +228,14 @@ function isUnsafeVirtualParent(directory: string): boolean {
207
228
  return resolved === root || resolved === path.resolve(os.tmpdir());
208
229
  }
209
230
 
231
+ /**
232
+ * Map an absolute path into a stable, filesystem-safe subtree under `root`.
233
+ *
234
+ * On POSIX the root is always `/`, so every path shares the same prefix —
235
+ * represented here as `"posix"`. On Windows, drive letters and UNC roots each
236
+ * get a sanitized label (e.g. `"C_"` for `C:\`), preventing collisions between
237
+ * paths from different drives inside the same virtual root.
238
+ */
210
239
  function virtualPath(root: string, absolute: string): string {
211
240
  const parsed = path.parse(path.resolve(absolute));
212
241
  const label =
@@ -218,6 +247,12 @@ function virtualPath(root: string, absolute: string): string {
218
247
  return path.join(root, label, relative);
219
248
  }
220
249
 
250
+ /**
251
+ * Heuristic: classify emitted JS as ESM when it contains top-level `import` or
252
+ * `export` statements but none of the well-known CJS patterns. The CJS checks
253
+ * run first so that re-exported CJS bundles with both `require` calls and an
254
+ * `export` declaration are conservatively treated as CJS.
255
+ */
221
256
  function looksLikeESM(output: string): boolean {
222
257
  if (
223
258
  /\bObject\.defineProperty\(exports\b/.test(output) ||