ttsc 0.11.0 → 0.12.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 (79) hide show
  1. package/README.md +25 -6
  2. package/cmd/ttsc/api_compile.go +101 -106
  3. package/cmd/ttsc/api_compile_branches_test.go +81 -0
  4. package/cmd/ttsc/api_transform.go +49 -54
  5. package/cmd/ttsc/api_transform_branches_test.go +41 -0
  6. package/cmd/ttsc/build.go +108 -109
  7. package/cmd/ttsc/build_branches_test.go +130 -0
  8. package/cmd/ttsc/helpers_test.go +47 -0
  9. package/cmd/ttsc/main.go +104 -103
  10. package/cmd/ttsc/main_alias_branches_test.go +29 -0
  11. package/cmd/ttscserver/main.go +104 -96
  12. package/cmd/ttscserver/run_lsp_prefers_tsgo_flag_test.go +42 -0
  13. package/cmd/ttscserver/run_lsp_reports_server_error_test.go +23 -25
  14. package/cmd/ttscserver/run_lsp_uses_tsgo_environment_test.go +44 -0
  15. package/cmd/utility-host/main.go +42 -0
  16. package/driver/lsp.go +60 -0
  17. package/driver/plugins.go +122 -0
  18. package/driver/program.go +346 -356
  19. package/driver/rewrite.go +336 -339
  20. package/{driver → internal/lspserver}/lsp_envelope.go +70 -64
  21. package/{driver → internal/lspserver}/lsp_frame.go +62 -62
  22. package/{driver → internal/lspserver}/lsp_plugin.go +58 -58
  23. package/internal/lspserver/lsp_proxy.go +391 -0
  24. package/internal/lspserver/lsp_server.go +231 -0
  25. package/lib/TtscCompiler.d.ts +3 -3
  26. package/lib/TtscCompiler.js +20 -6
  27. package/lib/TtscCompiler.js.map +1 -1
  28. package/lib/compiler/internal/resolveTsgo.d.ts +1 -0
  29. package/lib/compiler/internal/resolveTsgo.js +15 -4
  30. package/lib/compiler/internal/resolveTsgo.js.map +1 -1
  31. package/lib/compiler/internal/runBuild.js +12 -4
  32. package/lib/compiler/internal/runBuild.js.map +1 -1
  33. package/lib/compiler/internal/sharedHostHelpers.d.ts +12 -33
  34. package/lib/compiler/internal/sharedHostHelpers.js +27 -70
  35. package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
  36. package/lib/compiler/internal/transformProjectInMemory.js +12 -5
  37. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  38. package/lib/index.d.ts +0 -1
  39. package/lib/index.js +0 -1
  40. package/lib/index.js.map +1 -1
  41. package/lib/launcher/internal/runTtsc.js +9 -4
  42. package/lib/launcher/internal/runTtsc.js.map +1 -1
  43. package/lib/launcher/internal/runTtscserver.d.ts +1 -0
  44. package/lib/launcher/internal/runTtscserver.js +38 -1
  45. package/lib/launcher/internal/runTtscserver.js.map +1 -1
  46. package/lib/plugin/internal/buildSourcePlugin.d.ts +3 -0
  47. package/lib/plugin/internal/buildSourcePlugin.js +444 -41
  48. package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
  49. package/lib/plugin/internal/loadProjectPlugins.js +153 -34
  50. package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
  51. package/lib/structures/ITtscCompilerContext.d.ts +2 -1
  52. package/lib/structures/ITtscPlugin.d.ts +8 -13
  53. package/lib/structures/ITtscPluginFactoryContext.d.ts +4 -5
  54. package/lib/structures/ITtscProjectPluginConfig.d.ts +2 -2
  55. package/lib/structures/internal/ITtscLoadedNativePlugin.d.ts +4 -2
  56. package/package.json +8 -8
  57. package/shim/lsp/shim.go +11 -17
  58. package/src/TtscCompiler.ts +20 -7
  59. package/src/compiler/internal/resolveTsgo.ts +18 -5
  60. package/src/compiler/internal/runBuild.ts +17 -4
  61. package/src/compiler/internal/sharedHostHelpers.ts +34 -67
  62. package/src/compiler/internal/transformProjectInMemory.ts +18 -5
  63. package/src/index.ts +0 -1
  64. package/src/launcher/internal/runTtsc.ts +9 -4
  65. package/src/launcher/internal/runTtscserver.ts +43 -1
  66. package/src/plugin/internal/buildSourcePlugin.ts +475 -41
  67. package/src/plugin/internal/loadProjectPlugins.ts +199 -39
  68. package/src/structures/ITtscCompilerContext.ts +2 -1
  69. package/src/structures/ITtscPlugin.ts +9 -13
  70. package/src/structures/ITtscPluginFactoryContext.ts +4 -5
  71. package/src/structures/ITtscProjectPluginConfig.ts +2 -2
  72. package/src/structures/internal/ITtscLoadedNativePlugin.ts +4 -2
  73. package/utility/host.go +267 -1296
  74. package/driver/lsp_proxy.go +0 -394
  75. package/driver/lsp_server.go +0 -197
  76. package/lib/defineTtscPlugin.d.ts +0 -18
  77. package/lib/defineTtscPlugin.js +0 -7
  78. package/lib/defineTtscPlugin.js.map +0 -1
  79. package/src/defineTtscPlugin.ts +0 -26
package/shim/lsp/shim.go CHANGED
@@ -1,34 +1,28 @@
1
1
  // gen_shims:hand-maintained
2
2
  //
3
3
  // Minimal shim of tsgo's internal/lsp package. Hand-written instead of
4
- // generated so the surface stays narrow: ttscserver embeds the tsgo LSP
5
- // server unmodified and intercepts traffic at the byte level, so the only
6
- // types it touches are the opaque Server, its options struct, and the
7
- // Reader/Writer interfaces that ToReader/ToWriter return. The marker on
8
- // the first line tells gen_shims to skip this file.
4
+ // generated so the surface stays narrow for custom in-process host
5
+ // experiments. The shipped ttscserver wraps `tsgo --lsp --stdio` as an
6
+ // external process and does not import this package. The marker on the
7
+ // first line tells gen_shims to skip this file.
9
8
  package lsp
10
9
 
11
10
  import (
12
- "io"
13
- _ "unsafe"
11
+ "io"
12
+ _ "unsafe"
14
13
 
15
- innerlsp "github.com/microsoft/typescript-go/internal/lsp"
14
+ innerlsp "github.com/microsoft/typescript-go/internal/lsp"
16
15
  )
17
16
 
18
- // Server is the opaque LSP server type from tsgo. ttscserver does not poke
19
- // its internals; it only calls Run and (via wrapped Writer) observes the
20
- // traffic the server emits.
17
+ // Server is the opaque LSP server type from tsgo.
21
18
  type Server = innerlsp.Server
22
19
 
23
20
  // ServerOptions mirrors the upstream construction parameters. Fields with
24
- // internal-package types (e.g. ParseCache *project.ParseCache) are left
25
- // unset by ttscserver, which is safe because every such field is an
26
- // optional pointer the upstream constructor accepts as nil.
21
+ // internal-package types (e.g. ParseCache *project.ParseCache) can be left
22
+ // unset by callers when upstream accepts nil.
27
23
  type ServerOptions = innerlsp.ServerOptions
28
24
 
29
- // Reader / Writer carry framed lsproto.Message values. ttscserver feeds
30
- // the server through io.Pipe shims of these so it can splice its own
31
- // JSON-RPC traffic into the same stream the editor speaks.
25
+ // Reader / Writer carry framed lsproto.Message values.
32
26
  type Reader = innerlsp.Reader
33
27
  type Writer = innerlsp.Writer
34
28
 
@@ -5,6 +5,7 @@ import { compileProjectInMemory } from "./compiler/internal/compileProjectInMemo
5
5
  import { resolveProjectConfig } from "./compiler/internal/project/resolveProjectConfig";
6
6
  import { resolveBinary } from "./compiler/internal/resolveBinary";
7
7
  import { transformProjectInMemory } from "./compiler/internal/transformProjectInMemory";
8
+ import { defaultPluginCacheCleanTargets } from "./plugin/internal/buildSourcePlugin";
8
9
  import { loadProjectPlugins } from "./plugin/internal/loadProjectPlugins";
9
10
  import type { ITtscCompilerContext } from "./structures/ITtscCompilerContext";
10
11
  import type { ITtscCompilerDiagnostic } from "./structures/ITtscCompilerDiagnostic";
@@ -75,9 +76,9 @@ export class TtscCompiler {
75
76
  * Remove compiled cache artifacts for this compiler instance.
76
77
  *
77
78
  * When the constructor received `cacheDir`, this method removes exactly that
78
- * directory. Otherwise it removes the default ttsc cache locations for the
79
- * resolved project root, plus the `TTSC_CACHE_DIR/plugins` cache when that
80
- * environment variable is active.
79
+ * directory. When `TTSC_CACHE_DIR` is active, it removes that override's
80
+ * plugin cache plus legacy project-local caches. Otherwise it removes the
81
+ * default global plugin cache plus legacy project-local caches.
81
82
  *
82
83
  * The cache target comes from the constructor context. Create another
83
84
  * `TtscCompiler` instance to clean another project or another cache root.
@@ -90,13 +91,25 @@ export class TtscCompiler {
90
91
  return removeExistingDirectories([cacheDir]);
91
92
  }
92
93
 
93
- const envCacheDir =
94
- this.context.env?.TTSC_CACHE_DIR ?? process.env.TTSC_CACHE_DIR;
95
94
  const projectRoot = this.resolveCleanProjectRoot();
96
- return removeExistingDirectories([
97
- ...(envCacheDir ? [path.resolve(envCacheDir, "plugins")] : []),
95
+ const legacyTargets = [
98
96
  path.join(projectRoot, "node_modules", ".ttsc"),
99
97
  path.join(projectRoot, ".ttsc"),
98
+ ];
99
+ if (this.context.env?.TTSC_CACHE_DIR) {
100
+ return removeExistingDirectories([
101
+ path.resolve(projectRoot, this.context.env.TTSC_CACHE_DIR, "plugins"),
102
+ ...legacyTargets,
103
+ ]);
104
+ }
105
+ if (process.env.TTSC_CACHE_DIR) {
106
+ return removeExistingDirectories([
107
+ path.resolve(process.env.TTSC_CACHE_DIR, "plugins"),
108
+ ...legacyTargets,
109
+ ]);
110
+ }
111
+ return removeExistingDirectories([
112
+ ...defaultPluginCacheCleanTargets(projectRoot),
100
113
  ]);
101
114
  }
102
115
 
@@ -8,6 +8,7 @@ export function resolveTsgo(
8
8
  binary?: string;
9
9
  cwd?: string;
10
10
  env?: NodeJS.ProcessEnv;
11
+ resolveFrom?: string;
11
12
  } = {},
12
13
  ) {
13
14
  const env = opts.env ?? process.env;
@@ -28,11 +29,13 @@ export function resolveTsgo(
28
29
 
29
30
  const cwd = path.resolve(opts.cwd ?? process.cwd());
30
31
  let packageJson: string;
31
- try {
32
- packageJson = createRequire(path.join(cwd, "package.json")).resolve(
33
- "@typescript/native-preview/package.json",
34
- );
35
- } catch {
32
+ packageJson =
33
+ resolveNativePreviewPackageJson(path.join(cwd, "package.json")) ??
34
+ (opts.resolveFrom
35
+ ? resolveNativePreviewPackageJson(opts.resolveFrom)
36
+ : undefined) ??
37
+ "";
38
+ if (!packageJson) {
36
39
  throw new Error(
37
40
  [
38
41
  "ttsc: @typescript/native-preview is required.",
@@ -81,6 +84,16 @@ export function resolveTsgo(
81
84
  };
82
85
  }
83
86
 
87
+ function resolveNativePreviewPackageJson(from: string): string | undefined {
88
+ try {
89
+ return createRequire(from).resolve(
90
+ "@typescript/native-preview/package.json",
91
+ );
92
+ } catch {
93
+ return undefined;
94
+ }
95
+ }
96
+
84
97
  function readPackageJson(file: string): Record<string, unknown> {
85
98
  return JSON.parse(fs.readFileSync(file, "utf8")) as Record<string, unknown>;
86
99
  }
@@ -12,7 +12,11 @@ import type { TtscCommonOptions } from "../../structures/internal/TtscCommonOpti
12
12
  import { resolveProjectConfig } from "./project/resolveProjectConfig";
13
13
  import { resolveBinary } from "./resolveBinary";
14
14
  import { resolveTsgo } from "./resolveTsgo";
15
- import { assertSharedHostCompatibility } from "./sharedHostHelpers";
15
+ import {
16
+ assertSharedHostCompatibility,
17
+ linkedTransformPlugins,
18
+ selectSharedHostPlugin,
19
+ } from "./sharedHostHelpers";
16
20
 
17
21
  /** Merge spawn env without clobbering unrelated vars. */
18
22
  function mergeEnv(extra?: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
@@ -27,14 +31,22 @@ function mergeEnv(extra?: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
27
31
  function nativePluginEnv(
28
32
  extra: NodeJS.ProcessEnv | undefined,
29
33
  execution: ReturnType<typeof resolveExecutionContext>,
34
+ plugin?: ITtscLoadedNativePlugin,
30
35
  ): NodeJS.ProcessEnv {
31
- return mergeEnv({
36
+ const env = mergeEnv({
32
37
  TTSC_TSGO_BINARY: process.env.TTSC_TSGO_BINARY ?? execution.tsgo.binary,
33
38
  TTSC_TTSX_BINARY:
34
39
  process.env.TTSC_TTSX_BINARY ??
35
40
  path.join(__dirname, "..", "..", "launcher", "ttsx.js"),
36
41
  ...extra,
37
42
  });
43
+ if (plugin?.stage === "transform") {
44
+ const linked = linkedTransformPlugins(execution.nativePlugins);
45
+ if (linked.length !== 0) {
46
+ env.TTSC_LINKED_PLUGINS_JSON = serializeNativePlugins(linked);
47
+ }
48
+ }
49
+ return env;
38
50
  }
39
51
 
40
52
  function spawnBinary(
@@ -192,8 +204,9 @@ function buildWithNativeCompilerPlugins(
192
204
  execution: ReturnType<typeof resolveExecutionContext>,
193
205
  plugins: readonly ITtscLoadedNativePlugin[],
194
206
  ): TtscBuildResult {
207
+ const host = selectSharedHostPlugin(plugins);
195
208
  return runNativePluginCommand(
196
- plugins[0]!,
209
+ host,
197
210
  createNativeBuildArgs(execution, options, plugins),
198
211
  options,
199
212
  execution,
@@ -405,7 +418,7 @@ function runNativePluginCommand(
405
418
  ): TtscBuildResult {
406
419
  const res = spawnBinary(plugin.binary, args, {
407
420
  cwd: execution.projectRoot,
408
- env: nativePluginEnv(options.env, execution),
421
+ env: nativePluginEnv(options.env, execution, plugin),
409
422
  encoding: "utf8",
410
423
  });
411
424
  if (res.error) {
@@ -1,79 +1,19 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
-
4
1
  import type { ITtscLoadedNativePlugin } from "../../structures/internal/ITtscLoadedNativePlugin";
5
2
 
6
3
  /**
7
- * Names of the first-party utility transform plugins that are allowed to share
8
- * one compiler emit / one source-to-source pass even when their resolved
9
- * binaries differ. Each entry must be both name-pinned and manifest-pinned: the
10
- * plugin must declare itself with one of these names AND its source path must
11
- * resolve to a `package.json` whose `name` field matches.
12
- *
13
- * This whitelist exists because `@ttsc/banner`, `@ttsc/paths`, and
14
- * `@ttsc/strip` are AST-only mutators that operate on the same
15
- * `*shimast.SourceFile` graph and do not own emit. They are composed through
16
- * `packages/ttsc/utility/host.go`, so any one of their binaries can host the
17
- * shared run. Third-party plugins cannot participate in this fast path; they
18
- * must opt into composition through the explicit `ITtscPlugin.composes` field.
19
- */
20
- export const FIRST_PARTY_UTILITY_PLUGIN_NAMES: ReadonlySet<string> = new Set([
21
- "@ttsc/banner",
22
- "@ttsc/paths",
23
- "@ttsc/strip",
24
- ]);
25
-
26
- /**
27
- * Reports whether the given plugin is a first-party utility transform plugin
28
- * that may participate in the shared compiler host alongside other first-party
29
- * utility transforms. Returns `false` for `check`-stage plugins, for unknown
30
- * names, and for plugins whose `source` does not resolve to a matching
31
- * `package.json`.
4
+ * Reports whether the given transform source is linked into another compiler
5
+ * host instead of owning the process itself.
32
6
  */
33
- export function isFirstPartyUtilityTransformPlugin(
7
+ export function isLinkedTransform(
34
8
  plugin: ITtscLoadedNativePlugin,
35
9
  ): boolean {
36
- if (plugin.stage !== "transform") return false;
37
- if (!FIRST_PARTY_UTILITY_PLUGIN_NAMES.has(plugin.name)) return false;
38
- const manifest = readNearestPackageManifest(plugin.source);
39
- return manifest?.name === plugin.name;
40
- }
41
-
42
- /**
43
- * Walks upward from `source` (a directory or file path) up to four parent
44
- * levels searching for the nearest `package.json`. Returns the parsed manifest
45
- * object on success, or `undefined` when no manifest is found or parsing fails.
46
- * The depth bound mirrors the pre-existing inline implementations in
47
- * `runBuild.ts` and `transformProjectInMemory.ts`.
48
- */
49
- export function readNearestPackageManifest(
50
- source: string,
51
- ): { name?: unknown } | undefined {
52
- try {
53
- let current = fs.statSync(source).isDirectory()
54
- ? source
55
- : path.dirname(source);
56
- for (let i = 0; i < 4; i += 1) {
57
- const manifest = path.join(current, "package.json");
58
- if (fs.existsSync(manifest)) {
59
- return JSON.parse(fs.readFileSync(manifest, "utf8")) as {
60
- name?: unknown;
61
- };
62
- }
63
- const parent = path.dirname(current);
64
- if (parent === current) break;
65
- current = parent;
66
- }
67
- } catch {
68
- return undefined;
69
- }
70
- return undefined;
10
+ return plugin.stage === "transform" && plugin.kind === "linked";
71
11
  }
72
12
 
73
13
  /**
74
14
  * Verifies that all transform plugins in `plugins` either resolve to the same
75
- * native binary (the common case) or are all first-party utility plugins (the
76
- * shared compiler-host fast path).
15
+ * native binary (the common case) after linked sources are removed from the
16
+ * compiler-owner set.
77
17
  *
78
18
  * Two callers exist with subtly different error wording: the build path
79
19
  * (`runBuild.ts`) reports "multiple compiler native backends cannot share one
@@ -90,7 +30,14 @@ export function assertSharedHostCompatibility(
90
30
  if (binaries.length <= 1) {
91
31
  return;
92
32
  }
93
- if (plugins.every(isFirstPartyUtilityTransformPlugin)) {
33
+ const ownerBinaries = [
34
+ ...new Set(
35
+ plugins
36
+ .filter((plugin) => !isLinkedTransform(plugin))
37
+ .map((plugin) => plugin.binary),
38
+ ),
39
+ ];
40
+ if (ownerBinaries.length <= 1) {
94
41
  return;
95
42
  }
96
43
  const phrase =
@@ -103,3 +50,23 @@ export function assertSharedHostCompatibility(
103
50
  "; compose transform libraries through one aggregate native host",
104
51
  );
105
52
  }
53
+
54
+ /**
55
+ * Picks the native binary that must own the compiler pass. Linked transform
56
+ * sources ride inside a host that uses driver.LoadProgram, so an executable
57
+ * transform wins when one is present.
58
+ */
59
+ export function selectSharedHostPlugin(
60
+ plugins: readonly ITtscLoadedNativePlugin[],
61
+ ): ITtscLoadedNativePlugin {
62
+ return (
63
+ plugins.find((plugin) => !isLinkedTransform(plugin)) ??
64
+ plugins[0]!
65
+ );
66
+ }
67
+
68
+ export function linkedTransformPlugins(
69
+ plugins: readonly ITtscLoadedNativePlugin[],
70
+ ): ITtscLoadedNativePlugin[] {
71
+ return plugins.filter(isLinkedTransform);
72
+ }
@@ -16,7 +16,11 @@ import { readProjectConfig } from "./project/readProjectConfig";
16
16
  import { resolveBinary } from "./resolveBinary";
17
17
  import { resolveTsgo } from "./resolveTsgo";
18
18
  import { appendBuildOutput, normalizeBuildOutput } from "./runBuild";
19
- import { assertSharedHostCompatibility } from "./sharedHostHelpers";
19
+ import {
20
+ assertSharedHostCompatibility,
21
+ linkedTransformPlugins,
22
+ selectSharedHostPlugin,
23
+ } from "./sharedHostHelpers";
20
24
 
21
25
  /** Transform a project and capture TypeScript source output in memory. */
22
26
  export function transformProjectInMemory(options: ITtscCompilerContext): {
@@ -126,13 +130,13 @@ function transformProjectWithPlugins(
126
130
  }
127
131
  assertSharedHostCompatibility(transformers, "source-to-source");
128
132
 
129
- const plugin = transformers[0]!;
133
+ const plugin = selectSharedHostPlugin(transformers);
130
134
  const res = spawnNative(
131
135
  plugin.binary,
132
136
  createNativeTransformArgs(project, transformers),
133
137
  {
134
138
  cwd: project.root,
135
- env: nativePluginEnv(options, project.root),
139
+ env: nativePluginEnv(options, project.root, loaded.nativePlugins, plugin),
136
140
  },
137
141
  );
138
142
  if (res.error) {
@@ -174,7 +178,7 @@ function runNativeChecks(
174
178
  createNativeCheckArgs(project, nativePlugins),
175
179
  {
176
180
  cwd: project.root,
177
- env: nativePluginEnv(options, project.root),
181
+ env: nativePluginEnv(options, project.root, nativePlugins, plugin),
178
182
  },
179
183
  );
180
184
  if (res.error) {
@@ -239,9 +243,11 @@ function serializeNativePlugins(
239
243
  function nativePluginEnv(
240
244
  options: ITtscCompilerContext,
241
245
  projectRoot: string,
246
+ nativePlugins?: readonly ITtscLoadedNativePlugin[],
247
+ plugin?: ITtscLoadedNativePlugin,
242
248
  ): NodeJS.ProcessEnv {
243
249
  const tsgo = resolveTsgo({ ...options, cwd: projectRoot });
244
- return {
250
+ const env: NodeJS.ProcessEnv = {
245
251
  ...process.env,
246
252
  TTSC_NODE_BINARY: process.env.TTSC_NODE_BINARY ?? process.execPath,
247
253
  TTSC_TSGO_BINARY: process.env.TTSC_TSGO_BINARY ?? tsgo.binary,
@@ -250,6 +256,13 @@ function nativePluginEnv(
250
256
  path.join(__dirname, "..", "..", "launcher", "ttsx.js"),
251
257
  ...options.env,
252
258
  };
259
+ if (plugin?.stage === "transform") {
260
+ const linked = linkedTransformPlugins(nativePlugins ?? []);
261
+ if (linked.length !== 0) {
262
+ env.TTSC_LINKED_PLUGINS_JSON = serializeNativePlugins(linked);
263
+ }
264
+ }
265
+ return env;
253
266
  }
254
267
 
255
268
  function spawnNative(
package/src/index.ts CHANGED
@@ -8,5 +8,4 @@
8
8
  */
9
9
 
10
10
  export * from "./TtscCompiler";
11
- export * from "./defineTtscPlugin";
12
11
  export * from "./structures/index";
@@ -7,6 +7,7 @@ import { resolveProjectConfig } from "../../compiler/internal/project/resolvePro
7
7
  import { resolveBinary } from "../../compiler/internal/resolveBinary";
8
8
  import { runBuild } from "../../compiler/internal/runBuild";
9
9
  import { runSingleFileEmit } from "../../compiler/internal/runSingleFileEmit";
10
+ import { defaultPluginCacheCleanTargets } from "../../plugin/internal/buildSourcePlugin";
10
11
  import type { TtscBuildOptions } from "../../structures/internal/TtscBuildOptions";
11
12
  import { getCompilerVersionText } from "./getCompilerVersionText";
12
13
 
@@ -161,13 +162,17 @@ function runClean(argv: readonly string[]): number {
161
162
  const options = parseProjectArgs(argv);
162
163
  const cwd = path.resolve(options.cwd ?? process.cwd());
163
164
  const projectRoot = resolveCleanProjectRoot(cwd, options.tsconfig);
164
- const targets = [
165
+ const legacyTargets = [
165
166
  path.join(projectRoot, "node_modules", ".ttsc"),
166
167
  path.join(projectRoot, ".ttsc"),
168
+ ];
169
+ const targets = [
167
170
  ...(options.cacheDir ? [path.resolve(cwd, options.cacheDir)] : []),
168
171
  ...(process.env.TTSC_CACHE_DIR
169
- ? [path.resolve(process.env.TTSC_CACHE_DIR, "plugins")]
170
- : []),
172
+ ? [path.resolve(process.env.TTSC_CACHE_DIR, "plugins"), ...legacyTargets]
173
+ : options.cacheDir
174
+ ? legacyTargets
175
+ : defaultPluginCacheCleanTargets(projectRoot)),
171
176
  ];
172
177
  const removed: string[] = [];
173
178
  for (const target of targets) {
@@ -436,7 +441,7 @@ function printHelp(): void {
436
441
  " ttsc fix [options] Apply check-plugin lint + format edits, then run `ttsc check`.",
437
442
  " ttsc format [options] Apply check-plugin format-class edits only (write-only, no type check).",
438
443
  " ttsc prepare [options] Build configured source-plugin binaries into cache.",
439
- " ttsc clean [options] Delete local source-plugin cache directories.",
444
+ " ttsc clean [options] Delete source-plugin cache directories.",
440
445
  ].join("\n"),
441
446
  );
442
447
  process.stdout.write("\n");
@@ -2,6 +2,7 @@ import { spawnSync } from "node:child_process";
2
2
  import fs from "node:fs";
3
3
  import * as os from "node:os";
4
4
 
5
+ import { resolveTsgo } from "../../compiler/internal/resolveTsgo";
5
6
  import { resolveTtscserverBinary } from "./resolveTtscserverBinary";
6
7
 
7
8
  /**
@@ -11,6 +12,7 @@ import { resolveTtscserverBinary } from "./resolveTtscserverBinary";
11
12
  * performs only:
12
13
  *
13
14
  * - Resolve the platform binary,
15
+ * - Resolve the project TypeScript-Go binary for the native wrapper,
14
16
  * - Inject `--stdio` when the first arg is not a meta-command,
15
17
  * - Delegate to the binary with inherited stdio so OS-level signals reach the
16
18
  * child via the parent's process group.
@@ -31,9 +33,18 @@ export function runTtscserver(
31
33
  ensureExecutable(binary);
32
34
 
33
35
  const args = needsStdio(argv) ? ["--stdio", ...argv] : [...argv];
36
+ let env: NodeJS.ProcessEnv;
37
+ try {
38
+ env = resolveTtscserverEnv(args);
39
+ } catch (error) {
40
+ process.stderr.write(
41
+ `ttscserver: ${stripTtscPrefix(formatError(error))}\n`,
42
+ );
43
+ return 1;
44
+ }
34
45
  const result = spawnSync(binary, args, {
35
46
  stdio: "inherit",
36
- env: process.env,
47
+ env,
37
48
  windowsHide: true,
38
49
  });
39
50
  if (result.error) {
@@ -55,6 +66,37 @@ export function runTtscserver(
55
66
  return result.status ?? 1;
56
67
  }
57
68
 
69
+ function resolveTtscserverEnv(argv: readonly string[]): NodeJS.ProcessEnv {
70
+ if (!argv.includes("--stdio")) {
71
+ return process.env;
72
+ }
73
+ if (process.env.TTSC_TSGO_BINARY || hasTsgoOption(argv)) {
74
+ return process.env;
75
+ }
76
+ const tsgo = resolveTsgo({
77
+ cwd: process.cwd(),
78
+ resolveFrom: __filename,
79
+ });
80
+ return {
81
+ ...process.env,
82
+ TTSC_TSGO_BINARY: tsgo.binary,
83
+ };
84
+ }
85
+
86
+ function hasTsgoOption(argv: readonly string[]): boolean {
87
+ return argv.some((arg) => arg === "--tsgo" || arg.startsWith("--tsgo="));
88
+ }
89
+
90
+ function formatError(error: unknown): string {
91
+ return error instanceof Error ? error.message : String(error);
92
+ }
93
+
94
+ function stripTtscPrefix(message: string): string {
95
+ return message.startsWith("ttsc: ")
96
+ ? message.slice("ttsc: ".length)
97
+ : message;
98
+ }
99
+
58
100
  /**
59
101
  * `--stdio` is the only transport the native host accepts today. The launcher
60
102
  * injects it only when the first argv token looks like a forwarded option;