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,19 @@
1
+ import path from "node:path";
2
+
3
+ /**
4
+ * Resolve a user-supplied `--cache-dir` value into an absolute path.
5
+ *
6
+ * When `cacheDir` is absent or empty, returns `undefined` so callers fall
7
+ * through to the environment variable and global-cache defaults handled inside
8
+ * `buildSourcePlugin`. When `cacheDir` is already absolute it is returned
9
+ * unchanged; otherwise it is resolved relative to `cwd`.
10
+ */
11
+ export function resolveCacheDir(
12
+ cwd: string,
13
+ cacheDir?: string,
14
+ ): string | undefined {
15
+ if (!cacheDir) {
16
+ return undefined;
17
+ }
18
+ return path.isAbsolute(cacheDir) ? cacheDir : path.resolve(cwd, cacheDir);
19
+ }
@@ -47,5 +47,7 @@ function defaultLocalBinaryPath(): string | null {
47
47
 
48
48
  function packageRootDir(): string {
49
49
  const moduleDir = path.resolve(__dirname, "..", "..");
50
+ // Prefer the faster native variant; fall back to the JS implementation on
51
+ // platforms (or environments) where `realpathSync.native` is unavailable.
50
52
  return fs.realpathSync.native?.(moduleDir) ?? fs.realpathSync(moduleDir);
51
53
  }
@@ -8,10 +8,27 @@ import { resolveProjectConfig } from "../../compiler/internal/project/resolvePro
8
8
  import { resolveBinary } from "../../compiler/internal/resolveBinary";
9
9
  import { runBuild } from "../../compiler/internal/runBuild";
10
10
  import { runSingleFileEmit } from "../../compiler/internal/runSingleFileEmit";
11
+ import {
12
+ getBoolean,
13
+ getNumber,
14
+ getString,
15
+ parseFlags,
16
+ } from "../../flags/parser";
11
17
  import { defaultPluginCacheCleanTargets } from "../../plugin/internal/buildSourcePlugin";
12
18
  import type { TtscBuildOptions } from "../../structures/internal/TtscBuildOptions";
13
19
  import { getCompilerVersionText } from "./getCompilerVersionText";
14
-
20
+ import { resolveCacheDir } from "./resolveCacheDir";
21
+
22
+ /**
23
+ * CLI entry point for `ttsc`. Dispatches argv to the appropriate build lane
24
+ * (build, check, fix, format, prepare, clean, or native-delegate) and returns
25
+ * an exit code. Errors thrown by any lane are caught here and written to stderr
26
+ * so the process can exit cleanly.
27
+ *
28
+ * @param argv - Command-line arguments (defaults to `process.argv.slice(2)`).
29
+ * @returns The exit code: `0` on success, `1` on binary-not-found, `2` on user
30
+ * error or build failure.
31
+ */
15
32
  export function runTtsc(
16
33
  argv: readonly string[] = process.argv.slice(2),
17
34
  ): number {
@@ -67,6 +84,12 @@ export function runTtsc(
67
84
  }
68
85
  }
69
86
 
87
+ /**
88
+ * Return `true` when `command` looks like a build argument rather than a
89
+ * subcommand name — a flag (`-p`, `--watch`, …) or a TypeScript/config file
90
+ * path. These are forwarded to the build lane unchanged so users can write
91
+ * `ttsc -p tsconfig.json` without an explicit `build` subcommand.
92
+ */
70
93
  function isBuildAlias(command: string): boolean {
71
94
  if (command.startsWith("-")) return true;
72
95
  return [".json", ".ts", ".tsx", ".mts", ".cts"].some((ext) =>
@@ -265,141 +288,80 @@ function ensureExecutable(binary: string): void {
265
288
  }
266
289
 
267
290
  function parseProjectArgs(argv: readonly string[]) {
268
- let cacheDir: string | undefined;
269
- let cwd: string | undefined;
270
- let tsconfig: string | undefined;
271
-
272
- const rest = [...argv];
273
- while (rest.length !== 0) {
274
- const current = rest.shift()!;
275
- switch (current) {
276
- case "--cwd":
277
- cwd = takeValue(current, rest);
278
- break;
279
- case "--cache-dir":
280
- cacheDir = takeValue(current, rest);
281
- break;
282
- case "-p":
283
- case "--tsconfig":
284
- case "--project":
285
- tsconfig = takeValue(current, rest);
286
- break;
287
- default:
288
- if (current.startsWith("--cwd=")) {
289
- cwd = current.slice("--cwd=".length);
290
- } else if (current.startsWith("--cache-dir=")) {
291
- cacheDir = current.slice("--cache-dir=".length);
292
- } else if (current.startsWith("--tsconfig=")) {
293
- tsconfig = current.slice("--tsconfig=".length);
294
- } else if (current.startsWith("--project=")) {
295
- tsconfig = current.slice("--project=".length);
296
- } else {
297
- throw new Error(`ttsc: unknown option ${current}`);
298
- }
299
- break;
300
- }
301
- }
302
- return { cacheDir, cwd, tsconfig };
291
+ // `prepare` and `clean` are project-shaped commands. They share the same
292
+ // schema as the build lane; the engine forwards unknown flags as well as
293
+ // build-only flags (e.g. `--strict`) to the launcher's passthrough list
294
+ // so the legacy "unknown option" behaviour is no longer a separate trap
295
+ // (RC-3 + RC-4 prevention; see issue #125 §5 in the RCA).
296
+ const result = parseFlags({
297
+ argv,
298
+ errorPrefix: "ttsc:",
299
+ subcommand: "prepare",
300
+ });
301
+ return {
302
+ cacheDir: getString(result, "--cache-dir"),
303
+ cwd: getString(result, "--cwd"),
304
+ tsconfig: getString(result, "--tsconfig"),
305
+ };
303
306
  }
304
307
 
305
308
  function parseBuildArgs(argv: readonly string[], checkOnly: boolean) {
306
- let binary: string | undefined;
307
- let cacheDir: string | undefined;
308
- let cwd: string | undefined;
309
- let emit: boolean | undefined = checkOnly ? false : undefined;
310
- const files: string[] = [];
311
- let fix = false;
312
- let format = false;
313
- let outDir: string | undefined;
314
- let preserveWatchOutput = false;
315
- let quiet = true;
316
- let tsconfig: string | undefined;
317
- let watch = false;
318
-
319
- const rest = [...argv];
320
- while (rest.length !== 0) {
321
- const current = rest.shift()!;
322
- switch (current) {
323
- case "--emit":
324
- emit = true;
325
- break;
326
- case "--noEmit":
327
- emit = false;
328
- break;
329
- case "--quiet":
330
- quiet = true;
331
- break;
332
- case "--verbose":
333
- quiet = false;
334
- break;
335
- case "-w":
336
- case "--watch":
337
- watch = true;
338
- break;
339
- case "--preserveWatchOutput":
340
- preserveWatchOutput = true;
341
- break;
342
- case "--cwd":
343
- cwd = takeValue(current, rest);
344
- break;
345
- case "--outDir":
346
- outDir = takeValue(current, rest);
347
- break;
348
- case "-p":
349
- case "--tsconfig":
350
- case "--project":
351
- tsconfig = takeValue(current, rest);
352
- break;
353
- case "--binary":
354
- binary = takeValue(current, rest);
355
- break;
356
- case "--cache-dir":
357
- cacheDir = takeValue(current, rest);
358
- break;
359
- default:
360
- if (current.startsWith("--cwd=")) {
361
- cwd = current.slice("--cwd=".length);
362
- } else if (current.startsWith("--outDir=")) {
363
- outDir = current.slice("--outDir=".length);
364
- } else if (current === "-w") {
365
- watch = true;
366
- } else if (current.startsWith("--tsconfig=")) {
367
- tsconfig = current.slice("--tsconfig=".length);
368
- } else if (current.startsWith("--project=")) {
369
- tsconfig = current.slice("--project=".length);
370
- } else if (current.startsWith("--preserveWatchOutput=")) {
371
- preserveWatchOutput =
372
- current.slice("--preserveWatchOutput=".length) !== "false";
373
- } else if (current.startsWith("--binary=")) {
374
- binary = current.slice("--binary=".length);
375
- } else if (current.startsWith("--cache-dir=")) {
376
- cacheDir = current.slice("--cache-dir=".length);
377
- } else if (current === "--verbose") {
378
- quiet = false;
379
- } else if (current.startsWith("-")) {
380
- throw new Error(`ttsc: unknown option ${current}`);
381
- } else {
382
- files.push(current);
383
- }
384
- break;
385
- }
386
- }
309
+ const result = parseFlags({
310
+ argv,
311
+ errorPrefix: "ttsc:",
312
+ subcommand: "build",
313
+ });
314
+ // Defaults: pinned by the previous hand-parser. `quiet` defaults true,
315
+ // `--verbose` flips it to false; `emit` defaults `undefined` in build
316
+ // mode (let tsconfig decide) and `false` in check/fix/format mode.
317
+ const verbose = getBoolean(result, "--verbose");
318
+ const quietFlag = getBoolean(result, "--quiet");
319
+ const quiet = verbose === true ? false : (quietFlag ?? true);
320
+ const explicitEmit = getBoolean(result, "--emit");
321
+ const explicitNoEmit = getBoolean(result, "--noEmit");
322
+ let emit: boolean | undefined;
323
+ if (explicitEmit === true) emit = true;
324
+ else if (explicitNoEmit === true) emit = false;
325
+ if (emit === undefined && checkOnly) emit = false;
326
+
327
+ const files = result.positional.filter(looksLikeInputFile);
328
+ // Bare non-file positionals are flag values (e.g. the `es2020` in
329
+ // `--target es2020`). The engine left them in `positional` because the
330
+ // forwarded flag is unknown to the schema; route them back into
331
+ // passthrough so the user's pair reaches tsgo intact.
332
+ const trailingValues = result.positional.filter(
333
+ (token) => !looksLikeInputFile(token),
334
+ );
335
+ const passthrough = [...result.passthrough, ...trailingValues];
336
+
387
337
  return {
388
- binary,
389
- cacheDir,
390
- cwd,
338
+ binary: getString(result, "--binary"),
339
+ cacheDir: getString(result, "--cache-dir"),
340
+ checkers: getNumber(result, "--checkers"),
341
+ cwd: getString(result, "--cwd"),
391
342
  emit,
392
343
  files,
393
- fix,
394
- format,
395
- outDir,
396
- preserveWatchOutput,
344
+ fix: false,
345
+ format: false,
346
+ outDir: getString(result, "--outDir"),
347
+ passthrough,
348
+ preserveWatchOutput: getBoolean(result, "--preserveWatchOutput") === true,
397
349
  quiet,
398
- tsconfig,
399
- watch,
350
+ singleThreaded: getBoolean(result, "--singleThreaded") === true,
351
+ tsconfig: getString(result, "--tsconfig"),
352
+ watch: getBoolean(result, "--watch") === true,
400
353
  };
401
354
  }
402
355
 
356
+ /**
357
+ * Report whether a bare CLI token is a TypeScript source file ttsc should
358
+ * compile in single-file mode. Anything without a TypeScript source extension
359
+ * is treated as a forwarded flag value rather than an input file.
360
+ */
361
+ function looksLikeInputFile(token: string): boolean {
362
+ return [".ts", ".tsx", ".mts", ".cts"].some((ext) => token.endsWith(ext));
363
+ }
364
+
403
365
  function printHelp(): void {
404
366
  process.stdout.write(
405
367
  [
@@ -430,6 +392,11 @@ function printHelp(): void {
430
392
  " --verbose Print the build summary and emitted files",
431
393
  " --binary <path> Use an explicit tsgo binary",
432
394
  " --cache-dir <dir> Use this cache root for source-plugin builds",
395
+ " --singleThreaded Run TypeScript-Go single-threaded (one checker)",
396
+ " --checkers <n> Type-checker pool size (default: TypeScript-Go's)",
397
+ "",
398
+ " Any other flag is forwarded to tsgo as-is, so tsgo compiler options",
399
+ " such as --strict or --target work directly (e.g. ttsc --strict file.ts).",
433
400
  "",
434
401
  "Plugin contract:",
435
402
  " ttsc reads compilerOptions.plugins from tsconfig.json.",
@@ -464,9 +431,12 @@ function runSingleFile(options: ReturnType<typeof parseBuildArgs>): number {
464
431
  });
465
432
  const text = runSingleFileEmit({
466
433
  binary: options.binary,
434
+ checkers: options.checkers,
467
435
  cwd,
468
436
  file,
469
437
  out,
438
+ passthrough: options.passthrough,
439
+ singleThreaded: options.singleThreaded,
470
440
  tsconfig: options.tsconfig,
471
441
  });
472
442
  if (!fs.existsSync(out)) {
@@ -588,8 +558,7 @@ function runWatch(
588
558
  tsconfig: options.tsconfig,
589
559
  }),
590
560
  );
591
- const watchRoot = root;
592
- const directories = collectWatchDirectories(watchRoot);
561
+ const directories = collectWatchDirectories(root);
593
562
  const watchers = directories.map((dir) =>
594
563
  fs.watch(dir, { persistent: true }, () => trigger()),
595
564
  );
@@ -652,13 +621,6 @@ function runWatch(
652
621
  return 0;
653
622
  }
654
623
 
655
- function resolveCacheDir(cwd: string, cacheDir?: string): string | undefined {
656
- if (!cacheDir) {
657
- return undefined;
658
- }
659
- return path.isAbsolute(cacheDir) ? cacheDir : path.resolve(cwd, cacheDir);
660
- }
661
-
662
624
  function collectWatchDirectories(root: string): string[] {
663
625
  const out: string[] = [];
664
626
  const stack: string[] = [root];
@@ -682,14 +644,6 @@ function collectWatchDirectories(root: string): string[] {
682
644
  return out;
683
645
  }
684
646
 
685
- function takeValue(flag: string, rest: string[]): string {
686
- const value = rest.shift();
687
- if (!value) {
688
- throw new Error(`ttsc: ${flag} requires a value`);
689
- }
690
- return value;
691
- }
692
-
693
647
  function formatError(error: unknown): string {
694
648
  if (error instanceof Error) {
695
649
  return error.message;
@@ -1,6 +1,6 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import fs from "node:fs";
3
- import * as os from "node:os";
3
+ import os from "node:os";
4
4
 
5
5
  import { resolveTsgo } from "../../compiler/internal/resolveTsgo";
6
6
  import { resolveTtscserverBinary } from "./resolveTtscserverBinary";
@@ -66,8 +66,16 @@ export function runTtscserver(
66
66
  return result.status ?? 1;
67
67
  }
68
68
 
69
+ /**
70
+ * Build the environment for the native binary. When running in `--stdio` (LSP)
71
+ * mode the Go binary needs to know which tsgo binary to wrap; inject
72
+ * `TTSC_TSGO_BINARY` so the native host does not have to re-resolve it from
73
+ * inside a potentially different working directory. Skip injection when the
74
+ * caller already provided the variable or passed an explicit `--tsgo` option.
75
+ */
69
76
  function resolveTtscserverEnv(argv: readonly string[]): NodeJS.ProcessEnv {
70
77
  if (!argv.includes("--stdio")) {
78
+ // Non-LSP invocations (--version, --help) do not shell out to tsgo.
71
79
  return process.env;
72
80
  }
73
81
  if (process.env.TTSC_TSGO_BINARY || hasTsgoOption(argv)) {