ttsc 0.10.2 → 0.11.0-dev.20260517
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/cmd/ttsc/api_compile.go +106 -106
- package/cmd/ttsc/api_transform.go +54 -54
- package/cmd/ttsc/main.go +103 -103
- package/cmd/ttscserver/helpers_test.go +27 -0
- package/cmd/ttscserver/main.go +154 -0
- package/cmd/ttscserver/run_lsp_reports_getwd_error_test.go +34 -0
- package/cmd/ttscserver/run_lsp_reports_server_error_test.go +42 -0
- package/driver/lsp_envelope.go +136 -0
- package/driver/lsp_frame.go +121 -0
- package/driver/lsp_plugin.go +150 -0
- package/driver/lsp_proxy.go +394 -0
- package/driver/lsp_server.go +197 -0
- package/go.mod +4 -0
- package/go.sum +4 -0
- package/internal/cwd/cwd.go +8 -8
- package/lib/TtscCompiler.d.ts +15 -16
- package/lib/TtscCompiler.js +15 -16
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/compileProjectInMemory.d.ts +4 -1
- package/lib/compiler/internal/compileProjectInMemory.js +4 -1
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/runBuild.js +43 -1
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/sharedHostHelpers.d.ts +12 -12
- package/lib/compiler/internal/sharedHostHelpers.js +12 -12
- package/lib/defineTtscPlugin.d.ts +18 -0
- package/lib/defineTtscPlugin.js +7 -0
- package/lib/defineTtscPlugin.js.map +1 -0
- package/lib/index.d.ts +4 -3
- package/lib/index.js +4 -3
- package/lib/index.js.map +1 -1
- package/lib/launcher/internal/prepareExecution.js +1 -1
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/resolveTtscserverBinary.d.ts +12 -0
- package/lib/launcher/internal/resolveTtscserverBinary.js +43 -0
- package/lib/launcher/internal/resolveTtscserverBinary.js.map +1 -0
- package/lib/launcher/internal/runTtsc.js +47 -8
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.d.ts +20 -0
- package/lib/launcher/internal/runTtscserver.js +125 -0
- package/lib/launcher/internal/runTtscserver.js.map +1 -0
- package/lib/launcher/internal/runTtsx.js +1 -1
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/launcher/ttscserver.d.ts +2 -0
- package/lib/launcher/ttscserver.js +9 -0
- package/lib/launcher/ttscserver.js.map +1 -0
- package/lib/plugin/internal/buildSourcePlugin.d.ts +9 -0
- package/lib/plugin/internal/buildSourcePlugin.js +197 -2
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.js +86 -8
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscCompilerContext.d.ts +1 -1
- package/lib/structures/ITtscCompilerDiagnostic.d.ts +3 -3
- package/lib/structures/ITtscCompilerResult.d.ts +10 -22
- package/lib/structures/ITtscCompilerTransformation.d.ts +2 -2
- package/lib/structures/ITtscPlugin.d.ts +37 -4
- package/lib/structures/ITtscPlugin.js +0 -4
- package/lib/structures/ITtscPlugin.js.map +1 -1
- package/lib/structures/ITtscPluginContributor.d.ts +26 -0
- package/lib/structures/ITtscPluginContributor.js +3 -0
- package/lib/structures/ITtscPluginContributor.js.map +1 -0
- package/lib/structures/ITtscPluginFactoryContext.d.ts +5 -5
- package/lib/structures/ITtscProjectPluginConfig.d.ts +2 -2
- package/lib/structures/TtscPluginStage.d.ts +4 -3
- package/lib/structures/index.d.ts +1 -0
- package/lib/structures/index.js +1 -0
- package/lib/structures/index.js.map +1 -1
- package/lib/structures/internal/ITtscLoadedNativePlugin.d.ts +3 -0
- package/lib/structures/internal/TtscBuildOptions.d.ts +13 -0
- package/lib/structures/internal/TtscCommonOptions.d.ts +5 -2
- package/package.json +10 -9
- package/shim/lsp/extra-shim.json +4 -0
- package/shim/lsp/go.mod +16 -0
- package/shim/lsp/go.sum +26 -0
- package/shim/lsp/shim.go +42 -0
- package/shim/printer/shim.go +5 -5
- package/src/TtscCompiler.ts +15 -16
- package/src/compiler/internal/compileProjectInMemory.ts +4 -1
- package/src/compiler/internal/project/readProjectConfig.ts +0 -1
- package/src/compiler/internal/runBuild.ts +43 -1
- package/src/compiler/internal/sharedHostHelpers.ts +12 -12
- package/src/defineTtscPlugin.ts +26 -0
- package/src/index.ts +4 -3
- package/src/launcher/internal/prepareExecution.ts +1 -1
- package/src/launcher/internal/resolveTtscserverBinary.ts +51 -0
- package/src/launcher/internal/runTtsc.ts +53 -11
- package/src/launcher/internal/runTtscserver.ts +92 -0
- package/src/launcher/internal/runTtsx.ts +1 -1
- package/src/launcher/ttscserver.ts +8 -0
- package/src/plugin/internal/buildSourcePlugin.ts +231 -12
- package/src/plugin/internal/loadProjectPlugins.ts +108 -8
- package/src/structures/ITtscCompilerContext.ts +1 -1
- package/src/structures/ITtscCompilerDiagnostic.ts +3 -3
- package/src/structures/ITtscCompilerResult.ts +10 -22
- package/src/structures/ITtscCompilerTransformation.ts +2 -2
- package/src/structures/ITtscPlugin.ts +37 -11
- package/src/structures/ITtscPluginContributor.ts +27 -0
- package/src/structures/ITtscPluginFactoryContext.ts +5 -5
- package/src/structures/ITtscProjectPluginConfig.ts +2 -2
- package/src/structures/TtscPluginStage.ts +4 -3
- package/src/structures/index.ts +1 -0
- package/src/structures/internal/ITtscLoadedNativePlugin.ts +3 -0
- package/src/structures/internal/TtscBuildOptions.ts +13 -0
- package/src/structures/internal/TtscCommonOptions.ts +5 -2
|
@@ -106,6 +106,21 @@ export function runBuild(
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
if (options.emit === false) {
|
|
109
|
+
if (options.format === true) {
|
|
110
|
+
// Format mode is write-only by contract: the lint sidecar
|
|
111
|
+
// already rewrote source files and reported nothing. Running
|
|
112
|
+
// tsgo --noEmit OR a transform compiler afterwards would either
|
|
113
|
+
// surface unrelated type errors as if they were format failures
|
|
114
|
+
// (tsgo path) or apply transform-stage rewrites on top of the
|
|
115
|
+
// formatted source (transform path), both of which break the
|
|
116
|
+
// documented "ttsc format only formats" guarantee. The
|
|
117
|
+
// short-circuit fires before either branch so format mode is a
|
|
118
|
+
// single concern regardless of how many compilers the project
|
|
119
|
+
// configures. Callers that want a recheck or a transform pass
|
|
120
|
+
// after format should run `ttsc check` / `ttsc build` as a
|
|
121
|
+
// separate invocation.
|
|
122
|
+
return checked;
|
|
123
|
+
}
|
|
109
124
|
if (compilers.length !== 0) {
|
|
110
125
|
assertSharedHostCompatibility(compilers, "emit");
|
|
111
126
|
return appendBuildOutput(
|
|
@@ -143,6 +158,21 @@ export function runBuild(
|
|
|
143
158
|
return result;
|
|
144
159
|
}
|
|
145
160
|
|
|
161
|
+
if (options.format === true) {
|
|
162
|
+
// Format mode is write-only by contract — see the matching
|
|
163
|
+
// short-circuit in the with-native-plugins branch above. When no
|
|
164
|
+
// native plugin is loaded there is nothing for `ttsc format` to
|
|
165
|
+
// rewrite, so emit an empty success result rather than falling
|
|
166
|
+
// through to a tsgo pass that would surface unrelated type errors
|
|
167
|
+
// as if they were format failures.
|
|
168
|
+
return {
|
|
169
|
+
diagnostics: [],
|
|
170
|
+
status: 0,
|
|
171
|
+
stdout: "",
|
|
172
|
+
stderr: "",
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
146
176
|
if (options.emit !== false && options.skipDiagnosticsCheck !== true) {
|
|
147
177
|
const checked = runTsgo(execution, ["--noEmit"], options);
|
|
148
178
|
if (checked.status !== 0) {
|
|
@@ -298,7 +328,7 @@ function createNativeCheckArgs(
|
|
|
298
328
|
options: TtscBuildOptions,
|
|
299
329
|
): string[] {
|
|
300
330
|
const args = [
|
|
301
|
-
|
|
331
|
+
nativeCheckSubcommand(options),
|
|
302
332
|
"--tsconfig=" + execution.tsconfig,
|
|
303
333
|
"--plugins-json=" + serializeNativePlugins(execution.nativePlugins),
|
|
304
334
|
"--cwd=" + execution.projectRoot,
|
|
@@ -314,6 +344,18 @@ function createNativeCheckArgs(
|
|
|
314
344
|
return args;
|
|
315
345
|
}
|
|
316
346
|
|
|
347
|
+
/**
|
|
348
|
+
* Decide which native plugin subcommand the lint sidecar should run for the
|
|
349
|
+
* current `runBuild` invocation. The launcher selects exactly one of `fix` /
|
|
350
|
+
* `format` / `check` via subcommand dispatch, so at most one of the
|
|
351
|
+
* `options.fix` / `options.format` booleans is true.
|
|
352
|
+
*/
|
|
353
|
+
function nativeCheckSubcommand(options: TtscBuildOptions): string {
|
|
354
|
+
if (options.format === true) return "format";
|
|
355
|
+
if (options.fix === true) return "fix";
|
|
356
|
+
return "check";
|
|
357
|
+
}
|
|
358
|
+
|
|
317
359
|
function serializeNativePlugins(
|
|
318
360
|
plugins: readonly ITtscLoadedNativePlugin[],
|
|
319
361
|
): string {
|
|
@@ -10,12 +10,12 @@ import type { ITtscLoadedNativePlugin } from "../../structures/internal/ITtscLoa
|
|
|
10
10
|
* plugin must declare itself with one of these names AND its source path must
|
|
11
11
|
* resolve to a `package.json` whose `name` field matches.
|
|
12
12
|
*
|
|
13
|
-
* This whitelist exists because `@ttsc/banner`, `@ttsc/paths`, and
|
|
14
|
-
* are AST-only mutators that operate on the same
|
|
15
|
-
* and do not own emit. They are composed through
|
|
16
|
-
* so any one of their binaries can host the
|
|
17
|
-
* cannot participate in this fast path; they
|
|
18
|
-
* the explicit `ITtscPlugin.composes` field.
|
|
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
19
|
*/
|
|
20
20
|
export const FIRST_PARTY_UTILITY_PLUGIN_NAMES: ReadonlySet<string> = new Set([
|
|
21
21
|
"@ttsc/banner",
|
|
@@ -42,8 +42,8 @@ export function isFirstPartyUtilityTransformPlugin(
|
|
|
42
42
|
/**
|
|
43
43
|
* Walks upward from `source` (a directory or file path) up to four parent
|
|
44
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
|
|
46
|
-
*
|
|
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
47
|
* `runBuild.ts` and `transformProjectInMemory.ts`.
|
|
48
48
|
*/
|
|
49
49
|
export function readNearestPackageManifest(
|
|
@@ -77,10 +77,10 @@ export function readNearestPackageManifest(
|
|
|
77
77
|
*
|
|
78
78
|
* Two callers exist with subtly different error wording: the build path
|
|
79
79
|
* (`runBuild.ts`) reports "multiple compiler native backends cannot share one
|
|
80
|
-
* emit pass" while the source-to-source path
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
80
|
+
* emit pass" while the source-to-source path (`transformProjectInMemory.ts`)
|
|
81
|
+
* reports "cannot share one source-to-source pass". The `pass` argument selects
|
|
82
|
+
* the appropriate phrase so the error message remains diagnostic-grade instead
|
|
83
|
+
* of generic.
|
|
84
84
|
*/
|
|
85
85
|
export function assertSharedHostCompatibility(
|
|
86
86
|
plugins: readonly ITtscLoadedNativePlugin[],
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ITtscPlugin } from "./structures/ITtscPlugin";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Identity helper for authoring `ITtscPlugin` descriptors with full literal
|
|
5
|
+
* type preservation.
|
|
6
|
+
*
|
|
7
|
+
* Two call shapes:
|
|
8
|
+
*
|
|
9
|
+
* - Object form: returns the descriptor unchanged but pins its narrowed type so
|
|
10
|
+
* editor tooling and `satisfies` clauses can see exact field values.
|
|
11
|
+
* - Factory form: wraps a `(context) => descriptor` factory so the returned
|
|
12
|
+
* function carries the literal descriptor type through to consumers without
|
|
13
|
+
* forcing a manual `as const`.
|
|
14
|
+
*
|
|
15
|
+
* Pure pass-through at runtime — the function exists to teach TypeScript the
|
|
16
|
+
* const-narrowing the user would otherwise have to spell out.
|
|
17
|
+
*/
|
|
18
|
+
export function defineTtscPlugin<const T extends ITtscPlugin>(plugin: T): T;
|
|
19
|
+
export function defineTtscPlugin<TContext, const T extends ITtscPlugin>(
|
|
20
|
+
factory: (context: TContext) => T,
|
|
21
|
+
): (context: TContext) => T;
|
|
22
|
+
export function defineTtscPlugin(
|
|
23
|
+
value: ITtscPlugin | ((context: unknown) => ITtscPlugin),
|
|
24
|
+
): ITtscPlugin | ((context: unknown) => ITtscPlugin) {
|
|
25
|
+
return value;
|
|
26
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Ttsc — public TypeScript entry.
|
|
3
3
|
*
|
|
4
4
|
* The package root intentionally exposes only the programmatic compiler class
|
|
5
5
|
* and the plugin-author contracts. CLI launcher functions, binary resolution,
|
|
6
|
-
* project parsing helpers, and native build helpers stay internal so the
|
|
7
|
-
*
|
|
6
|
+
* project parsing helpers, and native build helpers stay internal so the public
|
|
7
|
+
* package surface remains small and stable.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
export * from "./TtscCompiler";
|
|
11
|
+
export * from "./defineTtscPlugin";
|
|
11
12
|
export * from "./structures/index";
|
|
@@ -2,9 +2,9 @@ import fs from "node:fs";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
|
+
import { readProjectConfig } from "../../compiler/internal/project/readProjectConfig";
|
|
5
6
|
import { resolveEmittedJavaScript } from "../../compiler/internal/resolveEmittedJavaScript";
|
|
6
7
|
import { runBuild } from "../../compiler/internal/runBuild";
|
|
7
|
-
import { readProjectConfig } from "../../compiler/internal/project/readProjectConfig";
|
|
8
8
|
import type { TtscCommonOptions } from "../../structures/internal/TtscCommonOptions";
|
|
9
9
|
|
|
10
10
|
const PROCESS_CACHE_KEY = String(process.pid);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the platform-specific ttscserver binary path. Looks first at the
|
|
6
|
+
* TTSCSERVER_BINARY environment override (must be absolute), then at the
|
|
7
|
+
* shipped per-platform npm package (`@ttsc/<platform>-<arch>/bin/ttscserver`),
|
|
8
|
+
* then at the local-build fallback under `packages/native/`.
|
|
9
|
+
*
|
|
10
|
+
* Mirrors `resolveBinary` for the ttsc helper so editors that install ttsc via
|
|
11
|
+
* pnpm see the LSP host alongside the existing helper.
|
|
12
|
+
*/
|
|
13
|
+
export function resolveTtscserverBinary(
|
|
14
|
+
opts: { env?: NodeJS.ProcessEnv } = {},
|
|
15
|
+
): string | null {
|
|
16
|
+
const env = opts.env ?? process.env;
|
|
17
|
+
if (env.TTSCSERVER_BINARY && path.isAbsolute(env.TTSCSERVER_BINARY)) {
|
|
18
|
+
return env.TTSCSERVER_BINARY;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
return require.resolve(
|
|
23
|
+
`@ttsc/${process.platform}-${process.arch}/bin/${
|
|
24
|
+
process.platform === "win32" ? "ttscserver.exe" : "ttscserver"
|
|
25
|
+
}`,
|
|
26
|
+
);
|
|
27
|
+
} catch {
|
|
28
|
+
/* fall through to local lookup */
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const local = defaultLocalBinaryPath();
|
|
32
|
+
if (local) return local;
|
|
33
|
+
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function defaultLocalBinaryPath(): string | null {
|
|
38
|
+
const root = packageRootDir();
|
|
39
|
+
const candidate = path.resolve(
|
|
40
|
+
root,
|
|
41
|
+
"..",
|
|
42
|
+
"native",
|
|
43
|
+
process.platform === "win32" ? "ttscserver.exe" : "ttscserver",
|
|
44
|
+
);
|
|
45
|
+
return fs.existsSync(candidate) ? candidate : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function packageRootDir(): string {
|
|
49
|
+
const moduleDir = path.resolve(__dirname, "..", "..");
|
|
50
|
+
return fs.realpathSync.native?.(moduleDir) ?? fs.realpathSync(moduleDir);
|
|
51
|
+
}
|
|
@@ -3,10 +3,10 @@ import fs from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
5
|
import { TtscCompiler } from "../../TtscCompiler";
|
|
6
|
+
import { resolveProjectConfig } from "../../compiler/internal/project/resolveProjectConfig";
|
|
6
7
|
import { resolveBinary } from "../../compiler/internal/resolveBinary";
|
|
7
8
|
import { runBuild } from "../../compiler/internal/runBuild";
|
|
8
9
|
import { runSingleFileEmit } from "../../compiler/internal/runSingleFileEmit";
|
|
9
|
-
import { resolveProjectConfig } from "../../compiler/internal/project/resolveProjectConfig";
|
|
10
10
|
import type { TtscBuildOptions } from "../../structures/internal/TtscBuildOptions";
|
|
11
11
|
import { getCompilerVersionText } from "./getCompilerVersionText";
|
|
12
12
|
|
|
@@ -15,7 +15,7 @@ export function runTtsc(
|
|
|
15
15
|
): number {
|
|
16
16
|
try {
|
|
17
17
|
if (argv.length === 0) {
|
|
18
|
-
return runCompatibleBuild([],
|
|
18
|
+
return runCompatibleBuild([], "build");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const [command, ...rest] = argv as [string, ...string[]];
|
|
@@ -31,9 +31,13 @@ export function runTtsc(
|
|
|
31
31
|
process.stdout.write(`${getCompilerVersionText()}\n`);
|
|
32
32
|
return 0;
|
|
33
33
|
case "build":
|
|
34
|
-
return runCompatibleBuild(rest,
|
|
34
|
+
return runCompatibleBuild(rest, "build");
|
|
35
35
|
case "check":
|
|
36
|
-
return runCompatibleBuild(rest,
|
|
36
|
+
return runCompatibleBuild(rest, "check");
|
|
37
|
+
case "fix":
|
|
38
|
+
return runCompatibleBuild(rest, "fix");
|
|
39
|
+
case "format":
|
|
40
|
+
return runCompatibleBuild(rest, "format");
|
|
37
41
|
case "clean":
|
|
38
42
|
return runClean(rest);
|
|
39
43
|
case "prepare":
|
|
@@ -42,10 +46,10 @@ export function runTtsc(
|
|
|
42
46
|
return delegateToNative(argv);
|
|
43
47
|
case "-p":
|
|
44
48
|
case "--project":
|
|
45
|
-
return runCompatibleBuild(argv,
|
|
49
|
+
return runCompatibleBuild(argv, "build");
|
|
46
50
|
default:
|
|
47
51
|
if (isBuildAlias(command)) {
|
|
48
|
-
return runCompatibleBuild(argv,
|
|
52
|
+
return runCompatibleBuild(argv, "build");
|
|
49
53
|
}
|
|
50
54
|
process.stderr.write(
|
|
51
55
|
`ttsc: unknown command ${JSON.stringify(command)}\n`,
|
|
@@ -68,15 +72,45 @@ function isBuildAlias(command: string): boolean {
|
|
|
68
72
|
);
|
|
69
73
|
}
|
|
70
74
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
type TtscMode = "build" | "check" | "fix" | "format";
|
|
76
|
+
|
|
77
|
+
function runCompatibleBuild(argv: readonly string[], mode: TtscMode): number {
|
|
78
|
+
const checkOnly = mode !== "build";
|
|
75
79
|
const options = normalizeBuildOptions(parseBuildArgs(argv, checkOnly));
|
|
80
|
+
if (mode === "fix") {
|
|
81
|
+
if (options.emit === true) {
|
|
82
|
+
throw new Error("ttsc: fix and --emit are mutually exclusive");
|
|
83
|
+
}
|
|
84
|
+
options.fix = true;
|
|
85
|
+
options.emit = false;
|
|
86
|
+
}
|
|
87
|
+
if (mode === "format") {
|
|
88
|
+
if (options.emit === true) {
|
|
89
|
+
throw new Error("ttsc: format and --emit are mutually exclusive");
|
|
90
|
+
}
|
|
91
|
+
options.format = true;
|
|
92
|
+
options.emit = false;
|
|
93
|
+
}
|
|
76
94
|
if (options.watch) {
|
|
95
|
+
if (mode === "fix") {
|
|
96
|
+
throw new Error(
|
|
97
|
+
"ttsc: fix does not support watch mode; use ttsc --noEmit --watch for incremental checks",
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (mode === "format") {
|
|
101
|
+
throw new Error(
|
|
102
|
+
"ttsc: format does not support watch mode; use ttsc --noEmit --watch for incremental checks",
|
|
103
|
+
);
|
|
104
|
+
}
|
|
77
105
|
return runWatch(options, checkOnly);
|
|
78
106
|
}
|
|
79
107
|
if (options.files.length !== 0) {
|
|
108
|
+
if (mode === "fix") {
|
|
109
|
+
throw new Error("ttsc: fix requires a project, not single-file mode");
|
|
110
|
+
}
|
|
111
|
+
if (mode === "format") {
|
|
112
|
+
throw new Error("ttsc: format requires a project, not single-file mode");
|
|
113
|
+
}
|
|
80
114
|
return runSingleFile(options);
|
|
81
115
|
}
|
|
82
116
|
const result = runBuild(checkOnly ? { ...options, emit: false } : options);
|
|
@@ -268,6 +302,8 @@ function parseBuildArgs(argv: readonly string[], checkOnly: boolean) {
|
|
|
268
302
|
let cwd: string | undefined;
|
|
269
303
|
let emit: boolean | undefined = checkOnly ? false : undefined;
|
|
270
304
|
const files: string[] = [];
|
|
305
|
+
let fix = false;
|
|
306
|
+
let format = false;
|
|
271
307
|
let outDir: string | undefined;
|
|
272
308
|
let preserveWatchOutput = false;
|
|
273
309
|
let quiet = true;
|
|
@@ -348,6 +384,8 @@ function parseBuildArgs(argv: readonly string[], checkOnly: boolean) {
|
|
|
348
384
|
cwd,
|
|
349
385
|
emit,
|
|
350
386
|
files,
|
|
387
|
+
fix,
|
|
388
|
+
format,
|
|
351
389
|
outDir,
|
|
352
390
|
preserveWatchOutput,
|
|
353
391
|
quiet,
|
|
@@ -366,6 +404,8 @@ function printHelp(): void {
|
|
|
366
404
|
" ttsc -p tsconfig.json",
|
|
367
405
|
" ttsc --watch",
|
|
368
406
|
" ttsc --noEmit",
|
|
407
|
+
" ttsc fix",
|
|
408
|
+
" ttsc format",
|
|
369
409
|
" ttsc prepare [options]",
|
|
370
410
|
" ttsc clean [options]",
|
|
371
411
|
" ttsc version",
|
|
@@ -390,9 +430,11 @@ function printHelp(): void {
|
|
|
390
430
|
" Plugin modules are descriptors for ordered native transformer backends.",
|
|
391
431
|
" JS transformOutput/transformSource functions are not part of the public contract.",
|
|
392
432
|
"",
|
|
393
|
-
"
|
|
433
|
+
"Subcommands:",
|
|
394
434
|
" ttsc build [options] Same project build lane as `ttsc [options]`.",
|
|
395
435
|
" ttsc check [options] Same as `ttsc --noEmit [options]`.",
|
|
436
|
+
" ttsc fix [options] Apply check-plugin lint + format edits, then run `ttsc check`.",
|
|
437
|
+
" ttsc format [options] Apply check-plugin format-class edits only (write-only, no type check).",
|
|
396
438
|
" ttsc prepare [options] Build configured source-plugin binaries into cache.",
|
|
397
439
|
" ttsc clean [options] Delete local source-plugin cache directories.",
|
|
398
440
|
].join("\n"),
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
|
|
5
|
+
import { resolveTtscserverBinary } from "./resolveTtscserverBinary";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Drive the ttscserver native binary from a node launcher. The launcher is
|
|
9
|
+
* deliberately thin: argument parsing, version banners, and help text are owned
|
|
10
|
+
* by the Go binary so future flags only need to change one layer. The JS side
|
|
11
|
+
* performs only:
|
|
12
|
+
*
|
|
13
|
+
* - Resolve the platform binary,
|
|
14
|
+
* - Inject `--stdio` when the first arg is not a meta-command,
|
|
15
|
+
* - Delegate to the binary with inherited stdio so OS-level signals reach the
|
|
16
|
+
* child via the parent's process group.
|
|
17
|
+
*/
|
|
18
|
+
export function runTtscserver(
|
|
19
|
+
argv: readonly string[] = process.argv.slice(2),
|
|
20
|
+
): number {
|
|
21
|
+
const binary = resolveTtscserverBinary();
|
|
22
|
+
if (!binary) {
|
|
23
|
+
process.stderr.write(
|
|
24
|
+
[
|
|
25
|
+
`ttscserver: platform-specific binary not found (@ttsc/${process.platform}-${process.arch}).`,
|
|
26
|
+
`Set TTSCSERVER_BINARY to an absolute path or reinstall ttsc with optional dependencies enabled.`,
|
|
27
|
+
].join("\n") + "\n",
|
|
28
|
+
);
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
ensureExecutable(binary);
|
|
32
|
+
|
|
33
|
+
const args = needsStdio(argv) ? ["--stdio", ...argv] : [...argv];
|
|
34
|
+
const result = spawnSync(binary, args, {
|
|
35
|
+
stdio: "inherit",
|
|
36
|
+
env: process.env,
|
|
37
|
+
windowsHide: true,
|
|
38
|
+
});
|
|
39
|
+
if (result.error) {
|
|
40
|
+
process.stderr.write(`ttscserver: ${result.error.message}\n`);
|
|
41
|
+
return 1;
|
|
42
|
+
}
|
|
43
|
+
if (result.signal) {
|
|
44
|
+
// POSIX convention: 128 + signum so wrappers (bash, npm-script, CI)
|
|
45
|
+
// can decode the signal that killed the child (130 = SIGINT, 143 =
|
|
46
|
+
// SIGTERM, etc.). On Windows, `spawnSync` does not surface a signal
|
|
47
|
+
// (TerminateProcess carries no signum) so this branch is POSIX-only
|
|
48
|
+
// by design; Windows-killed children take the `result.status ?? 1`
|
|
49
|
+
// path below.
|
|
50
|
+
const signum = (os.constants.signals as Record<string, number | undefined>)[
|
|
51
|
+
result.signal
|
|
52
|
+
];
|
|
53
|
+
return typeof signum === "number" ? 128 + signum : 1;
|
|
54
|
+
}
|
|
55
|
+
return result.status ?? 1;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* `--stdio` is the only transport the native host accepts today. The launcher
|
|
60
|
+
* injects it only when the first argv token looks like a forwarded option;
|
|
61
|
+
* meta-commands (`-v`, `--help`, `version`, etc.) pass through untouched so the
|
|
62
|
+
* Go binary owns the canonical banner. This mirrors
|
|
63
|
+
* `cmd/ttscserver/main.go::run`, which dispatches on `args[0]` only.
|
|
64
|
+
*/
|
|
65
|
+
export function needsStdio(argv: readonly string[]): boolean {
|
|
66
|
+
if (argv.length === 0) return false;
|
|
67
|
+
if (argv.includes("--stdio")) return false;
|
|
68
|
+
const head = argv[0];
|
|
69
|
+
if (
|
|
70
|
+
head === "-v" ||
|
|
71
|
+
head === "--version" ||
|
|
72
|
+
head === "version" ||
|
|
73
|
+
head === "-h" ||
|
|
74
|
+
head === "--help" ||
|
|
75
|
+
head === "help"
|
|
76
|
+
) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Mirror the ttsc helper-binary chmod hint so first-run from npm works. */
|
|
83
|
+
function ensureExecutable(binary: string): void {
|
|
84
|
+
if (process.platform === "win32") return;
|
|
85
|
+
try {
|
|
86
|
+
const mode = fs.statSync(binary).mode & 0o777;
|
|
87
|
+
if ((mode & 0o111) !== 0) return;
|
|
88
|
+
fs.chmodSync(binary, mode | 0o755);
|
|
89
|
+
} catch {
|
|
90
|
+
/* spawn will surface the underlying error */
|
|
91
|
+
}
|
|
92
|
+
}
|