ttsc 0.3.1 → 0.4.0-dev.20260425
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/LICENSE +1 -1
- package/README.md +93 -362
- package/cmd/ttsc/build.go +128 -0
- package/cmd/ttsc/main.go +222 -0
- package/cmd/ttsc/main_test.go +288 -0
- package/cmd/ttsc/transform.go +166 -0
- package/driver/host.go +31 -0
- package/driver/program.go +298 -0
- package/driver/rewrite.go +315 -0
- package/go.mod +48 -0
- package/go.sum +22 -0
- package/lib/api.d.ts +91 -0
- package/lib/api.js +325 -0
- package/lib/api.js.map +1 -0
- package/lib/cli.d.ts +1 -0
- package/lib/cli.js +481 -0
- package/lib/cli.js.map +1 -0
- package/lib/index.d.ts +18 -0
- package/lib/index.js +35 -0
- package/lib/index.js.map +1 -0
- package/lib/native.d.ts +15 -0
- package/lib/native.js +34 -0
- package/lib/native.js.map +1 -0
- package/lib/platform.d.ts +66 -0
- package/lib/platform.js +170 -0
- package/lib/platform.js.map +1 -0
- package/lib/plugin.d.ts +47 -0
- package/lib/plugin.js +150 -0
- package/lib/plugin.js.map +1 -0
- package/lib/project.d.ts +32 -0
- package/lib/project.js +303 -0
- package/lib/project.js.map +1 -0
- package/lib/runner/cli.d.ts +1 -0
- package/lib/runner/cli.js +284 -0
- package/lib/runner/cli.js.map +1 -0
- package/lib/runner/register.d.ts +13 -0
- package/lib/runner/register.js +424 -0
- package/lib/runner/register.js.map +1 -0
- package/package.json +72 -63
- package/shim/ast/extra-shim.json +4 -0
- package/shim/ast/go.mod +5 -0
- package/shim/ast/shim.go +117 -0
- package/shim/bundled/extra-shim.json +1 -0
- package/shim/bundled/go.mod +5 -0
- package/shim/bundled/shim.go +19 -0
- package/shim/checker/extra-shim.json +31 -0
- package/shim/checker/go.mod +5 -0
- package/shim/checker/shim.go +146 -0
- package/shim/compiler/extra-shim.json +4 -0
- package/shim/compiler/go.mod +5 -0
- package/shim/compiler/shim.go +50 -0
- package/shim/core/extra-shim.json +4 -0
- package/shim/core/go.mod +5 -0
- package/shim/core/shim.go +11 -0
- package/shim/diagnosticwriter/go.mod +5 -0
- package/shim/diagnosticwriter/shim.go +29 -0
- package/shim/parser/extra-shim.json +4 -0
- package/shim/parser/go.mod +5 -0
- package/shim/parser/shim.go +55 -0
- package/shim/scanner/extra-shim.json +4 -0
- package/shim/scanner/go.mod +5 -0
- package/shim/scanner/shim.go +96 -0
- package/shim/tsoptions/extra-shim.json +4 -0
- package/shim/tsoptions/go.mod +5 -0
- package/shim/tsoptions/shim.go +123 -0
- package/shim/tspath/extra-shim.json +4 -0
- package/shim/tspath/go.mod +5 -0
- package/shim/tspath/shim.go +165 -0
- package/shim/vfs/cachedvfs/extra-shim.json +4 -0
- package/shim/vfs/cachedvfs/go.mod +5 -0
- package/shim/vfs/cachedvfs/shim.go +12 -0
- package/shim/vfs/extra-shim.json +4 -0
- package/shim/vfs/go.mod +5 -0
- package/shim/vfs/osvfs/extra-shim.json +4 -0
- package/shim/vfs/osvfs/go.mod +5 -0
- package/shim/vfs/osvfs/shim.go +13 -0
- package/shim/vfs/shim.go +19 -0
- package/src/api.ts +416 -0
- package/src/cli.ts +492 -0
- package/src/index.ts +19 -0
- package/src/launcher/ttsc-dev.js +42 -0
- package/src/launcher/ttsc.js +19 -0
- package/src/launcher/ttsx.js +19 -0
- package/src/native.ts +60 -0
- package/src/platform.ts +163 -0
- package/src/plugin.ts +241 -0
- package/src/project.ts +330 -0
- package/src/runner/cli.ts +286 -0
- package/src/runner/register.ts +487 -0
- package/test/native.test.ts +60 -0
- package/test/package.test.ts +131 -0
- package/test/platform.test.ts +58 -0
- package/test/project.test.ts +195 -0
- package/tools/gen_shims/go.mod +14 -0
- package/tools/gen_shims/go.sum +12 -0
- package/tools/gen_shims/main.go +446 -0
- package/CHANGELOG.md +0 -2
- package/bin/tsc +0 -2
- package/bin/tsserver +0 -2
- package/lib/PluginCreator.d.ts +0 -67
- package/lib/PluginCreator.js +0 -190
- package/lib/cancelationToken.d.ts +0 -0
- package/lib/cancelationToken.js +0 -2
- package/lib/loadTypescript.d.ts +0 -7
- package/lib/loadTypescript.js +0 -56
- package/lib/patchCreateProgram.d.ts +0 -21
- package/lib/patchCreateProgram.js +0 -190
- package/lib/tsc.d.ts +0 -1
- package/lib/tsc.js +0 -24
- package/lib/tsserver.d.ts +0 -0
- package/lib/tsserver.js +0 -2
- package/lib/tsserverlibrary.d.ts +0 -3
- package/lib/tsserverlibrary.js +0 -4
- package/lib/typescript.d.ts +0 -3
- package/lib/typescript.js +0 -4
- package/lib/typescriptServices.d.ts +0 -3
- package/lib/typescriptServices.js +0 -4
- package/lib/typingsInstaller.d.ts +0 -0
- package/lib/typingsInstaller.js +0 -2
- package/lib/watchGuard.d.ts +0 -0
- package/lib/watchGuard.js +0 -2
package/src/cli.ts
ADDED
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
type BuildOptions,
|
|
7
|
+
type TransformOptions,
|
|
8
|
+
build,
|
|
9
|
+
check,
|
|
10
|
+
transform,
|
|
11
|
+
version,
|
|
12
|
+
} from "./api";
|
|
13
|
+
import { installHint, resolveBinary } from "./platform";
|
|
14
|
+
import { resolveProjectConfig, resolveProjectRoot } from "./project";
|
|
15
|
+
|
|
16
|
+
export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
|
17
|
+
try {
|
|
18
|
+
if (argv.length === 0) {
|
|
19
|
+
return runCompatibleBuild([], false);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const [command, ...rest] = argv as [string, ...string[]];
|
|
23
|
+
switch (command) {
|
|
24
|
+
case "-h":
|
|
25
|
+
case "--help":
|
|
26
|
+
case "help":
|
|
27
|
+
printHelp();
|
|
28
|
+
return 0;
|
|
29
|
+
case "-v":
|
|
30
|
+
case "--version":
|
|
31
|
+
case "version":
|
|
32
|
+
process.stdout.write(`${version()}\n`);
|
|
33
|
+
return 0;
|
|
34
|
+
case "build":
|
|
35
|
+
return runCompatibleBuild(rest, false);
|
|
36
|
+
case "check":
|
|
37
|
+
return runCompatibleBuild(rest, true);
|
|
38
|
+
case "transform":
|
|
39
|
+
return runTransform(rest);
|
|
40
|
+
case "demo":
|
|
41
|
+
return delegateToNative(argv);
|
|
42
|
+
case "-p":
|
|
43
|
+
case "--project":
|
|
44
|
+
return runCompatibleBuild(argv, false);
|
|
45
|
+
default:
|
|
46
|
+
if (isBuildAlias(command)) {
|
|
47
|
+
return runCompatibleBuild(argv, false);
|
|
48
|
+
}
|
|
49
|
+
process.stderr.write(
|
|
50
|
+
`ttsc: unknown command ${JSON.stringify(command)}\n`,
|
|
51
|
+
);
|
|
52
|
+
process.stderr.write(
|
|
53
|
+
`ttsc: run "ttsc --help" to see supported commands\n`,
|
|
54
|
+
);
|
|
55
|
+
return 2;
|
|
56
|
+
}
|
|
57
|
+
} catch (error) {
|
|
58
|
+
process.stderr.write(`${formatError(error)}\n`);
|
|
59
|
+
return 2;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isBuildAlias(command: string): boolean {
|
|
64
|
+
if (command.startsWith("-")) return true;
|
|
65
|
+
return [".json", ".ts", ".tsx", ".mts", ".cts"].some((ext) =>
|
|
66
|
+
command.endsWith(ext),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface BuildInvocation extends BuildOptions {
|
|
71
|
+
files: string[];
|
|
72
|
+
preserveWatchOutput: boolean;
|
|
73
|
+
watch: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function runCompatibleBuild(
|
|
77
|
+
argv: readonly string[],
|
|
78
|
+
checkOnly: boolean,
|
|
79
|
+
): number {
|
|
80
|
+
const options = parseBuildArgs(argv, checkOnly);
|
|
81
|
+
if (options.watch) {
|
|
82
|
+
return runWatch(options, checkOnly);
|
|
83
|
+
}
|
|
84
|
+
if (options.files.length !== 0) {
|
|
85
|
+
return runSingleFile(options);
|
|
86
|
+
}
|
|
87
|
+
const result = checkOnly ? check(options) : build(options);
|
|
88
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
89
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
90
|
+
return result.status;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function runTransform(argv: readonly string[]): number {
|
|
94
|
+
const options = parseTransformArgs(argv);
|
|
95
|
+
const text = transform(options);
|
|
96
|
+
if (!options.out) {
|
|
97
|
+
process.stdout.write(text);
|
|
98
|
+
}
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function delegateToNative(argv: readonly string[]): number {
|
|
103
|
+
const bin = resolveBinary();
|
|
104
|
+
if (!bin) {
|
|
105
|
+
process.stderr.write(`${installHint()}\n`);
|
|
106
|
+
return 1;
|
|
107
|
+
}
|
|
108
|
+
const viaNode = /\.(?:[cm]?js|ts)$/i.test(bin);
|
|
109
|
+
const result = spawnSync(
|
|
110
|
+
viaNode ? process.execPath : bin,
|
|
111
|
+
viaNode ? [bin, ...argv] : [...argv],
|
|
112
|
+
{
|
|
113
|
+
stdio: "inherit",
|
|
114
|
+
env: process.env,
|
|
115
|
+
windowsHide: true,
|
|
116
|
+
},
|
|
117
|
+
);
|
|
118
|
+
if (result.error) {
|
|
119
|
+
process.stderr.write(`${result.error.message}\n`);
|
|
120
|
+
return 1;
|
|
121
|
+
}
|
|
122
|
+
return result.status ?? 1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function parseBuildArgs(
|
|
126
|
+
argv: readonly string[],
|
|
127
|
+
checkOnly: boolean,
|
|
128
|
+
): BuildInvocation {
|
|
129
|
+
let binary: string | undefined;
|
|
130
|
+
let cwd: string | undefined;
|
|
131
|
+
let emit: boolean | undefined = checkOnly ? false : undefined;
|
|
132
|
+
const files: string[] = [];
|
|
133
|
+
let outDir: string | undefined;
|
|
134
|
+
let preserveWatchOutput = false;
|
|
135
|
+
let quiet = true;
|
|
136
|
+
let rewriteMode: string | undefined;
|
|
137
|
+
let tsconfig: string | undefined;
|
|
138
|
+
let watch = false;
|
|
139
|
+
|
|
140
|
+
const rest = [...argv];
|
|
141
|
+
while (rest.length !== 0) {
|
|
142
|
+
const current = rest.shift()!;
|
|
143
|
+
switch (current) {
|
|
144
|
+
case "--emit":
|
|
145
|
+
emit = true;
|
|
146
|
+
break;
|
|
147
|
+
case "--noEmit":
|
|
148
|
+
emit = false;
|
|
149
|
+
break;
|
|
150
|
+
case "--quiet":
|
|
151
|
+
quiet = true;
|
|
152
|
+
break;
|
|
153
|
+
case "--verbose":
|
|
154
|
+
quiet = false;
|
|
155
|
+
break;
|
|
156
|
+
case "-w":
|
|
157
|
+
case "--watch":
|
|
158
|
+
watch = true;
|
|
159
|
+
break;
|
|
160
|
+
case "--preserveWatchOutput":
|
|
161
|
+
preserveWatchOutput = true;
|
|
162
|
+
break;
|
|
163
|
+
case "--rewrite-mode":
|
|
164
|
+
rewriteMode = takeRewriteMode(takeValue(current, rest));
|
|
165
|
+
break;
|
|
166
|
+
case "--cwd":
|
|
167
|
+
cwd = takeValue(current, rest);
|
|
168
|
+
break;
|
|
169
|
+
case "--outDir":
|
|
170
|
+
outDir = takeValue(current, rest);
|
|
171
|
+
break;
|
|
172
|
+
case "-p":
|
|
173
|
+
case "--tsconfig":
|
|
174
|
+
case "--project":
|
|
175
|
+
tsconfig = takeValue(current, rest);
|
|
176
|
+
break;
|
|
177
|
+
case "--binary":
|
|
178
|
+
binary = takeValue(current, rest);
|
|
179
|
+
break;
|
|
180
|
+
default:
|
|
181
|
+
if (current.startsWith("--cwd=")) {
|
|
182
|
+
cwd = current.slice("--cwd=".length);
|
|
183
|
+
} else if (current.startsWith("--outDir=")) {
|
|
184
|
+
outDir = current.slice("--outDir=".length);
|
|
185
|
+
} else if (current === "-w") {
|
|
186
|
+
watch = true;
|
|
187
|
+
} else if (current.startsWith("--rewrite-mode=")) {
|
|
188
|
+
rewriteMode = takeRewriteMode(
|
|
189
|
+
current.slice("--rewrite-mode=".length),
|
|
190
|
+
);
|
|
191
|
+
} else if (current.startsWith("--tsconfig=")) {
|
|
192
|
+
tsconfig = current.slice("--tsconfig=".length);
|
|
193
|
+
} else if (current.startsWith("--project=")) {
|
|
194
|
+
tsconfig = current.slice("--project=".length);
|
|
195
|
+
} else if (current.startsWith("--preserveWatchOutput=")) {
|
|
196
|
+
preserveWatchOutput =
|
|
197
|
+
current.slice("--preserveWatchOutput=".length) !== "false";
|
|
198
|
+
} else if (current.startsWith("--binary=")) {
|
|
199
|
+
binary = current.slice("--binary=".length);
|
|
200
|
+
} else if (current === "--verbose") {
|
|
201
|
+
quiet = false;
|
|
202
|
+
} else if (current.startsWith("-")) {
|
|
203
|
+
throw new Error(`ttsc: unknown option ${current}`);
|
|
204
|
+
} else {
|
|
205
|
+
files.push(current);
|
|
206
|
+
}
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
binary,
|
|
212
|
+
cwd,
|
|
213
|
+
emit,
|
|
214
|
+
files,
|
|
215
|
+
outDir,
|
|
216
|
+
preserveWatchOutput,
|
|
217
|
+
quiet,
|
|
218
|
+
rewriteMode,
|
|
219
|
+
tsconfig,
|
|
220
|
+
watch,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function parseTransformArgs(argv: readonly string[]): TransformOptions {
|
|
225
|
+
let binary: string | undefined;
|
|
226
|
+
let cwd: string | undefined;
|
|
227
|
+
let file: string | undefined;
|
|
228
|
+
let out: string | undefined;
|
|
229
|
+
let rewriteMode: string | undefined;
|
|
230
|
+
let tsconfig: string | undefined;
|
|
231
|
+
|
|
232
|
+
const rest = [...argv];
|
|
233
|
+
while (rest.length !== 0) {
|
|
234
|
+
const current = rest.shift()!;
|
|
235
|
+
switch (current) {
|
|
236
|
+
case "--binary":
|
|
237
|
+
binary = takeValue(current, rest);
|
|
238
|
+
break;
|
|
239
|
+
case "--cwd":
|
|
240
|
+
cwd = takeValue(current, rest);
|
|
241
|
+
break;
|
|
242
|
+
case "--file":
|
|
243
|
+
file = takeValue(current, rest);
|
|
244
|
+
break;
|
|
245
|
+
case "--out":
|
|
246
|
+
out = takeValue(current, rest);
|
|
247
|
+
break;
|
|
248
|
+
case "--rewrite-mode":
|
|
249
|
+
rewriteMode = takeRewriteMode(takeValue(current, rest));
|
|
250
|
+
break;
|
|
251
|
+
case "--tsconfig":
|
|
252
|
+
case "--project":
|
|
253
|
+
tsconfig = takeValue(current, rest);
|
|
254
|
+
break;
|
|
255
|
+
default:
|
|
256
|
+
if (current.startsWith("--binary=")) {
|
|
257
|
+
binary = current.slice("--binary=".length);
|
|
258
|
+
} else if (current.startsWith("--cwd=")) {
|
|
259
|
+
cwd = current.slice("--cwd=".length);
|
|
260
|
+
} else if (current.startsWith("--file=")) {
|
|
261
|
+
file = current.slice("--file=".length);
|
|
262
|
+
} else if (current.startsWith("--out=")) {
|
|
263
|
+
out = current.slice("--out=".length);
|
|
264
|
+
} else if (current.startsWith("--rewrite-mode=")) {
|
|
265
|
+
rewriteMode = takeRewriteMode(
|
|
266
|
+
current.slice("--rewrite-mode=".length),
|
|
267
|
+
);
|
|
268
|
+
} else if (current.startsWith("--tsconfig=")) {
|
|
269
|
+
tsconfig = current.slice("--tsconfig=".length);
|
|
270
|
+
} else if (current.startsWith("--project=")) {
|
|
271
|
+
tsconfig = current.slice("--project=".length);
|
|
272
|
+
} else {
|
|
273
|
+
throw new Error(`ttsc: unknown option ${current}`);
|
|
274
|
+
}
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (!file) {
|
|
279
|
+
throw new Error("ttsc: transform requires --file");
|
|
280
|
+
}
|
|
281
|
+
return {
|
|
282
|
+
binary,
|
|
283
|
+
cwd,
|
|
284
|
+
file,
|
|
285
|
+
out,
|
|
286
|
+
rewriteMode,
|
|
287
|
+
tsconfig,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function printHelp(): void {
|
|
292
|
+
process.stdout.write(
|
|
293
|
+
[
|
|
294
|
+
"ttsc — standalone compiler adapter and plugin host for tsgo.",
|
|
295
|
+
"",
|
|
296
|
+
"Usage:",
|
|
297
|
+
" ttsc",
|
|
298
|
+
" ttsc -p tsconfig.json",
|
|
299
|
+
" ttsc --watch",
|
|
300
|
+
" ttsc --noEmit",
|
|
301
|
+
" ttsc transform --file <path> [options]",
|
|
302
|
+
" ttsc version",
|
|
303
|
+
" ttsc --help",
|
|
304
|
+
"",
|
|
305
|
+
"Options:",
|
|
306
|
+
" -p, --project <file> Resolve project settings from this tsconfig",
|
|
307
|
+
" --tsconfig <file> Resolve project settings from this tsconfig",
|
|
308
|
+
" --cwd <dir> Resolve project-relative paths from this directory",
|
|
309
|
+
" --emit Force emitted files during build",
|
|
310
|
+
" --noEmit Force analysis-only build with no file writes",
|
|
311
|
+
" -w, --watch Rebuild when project files change",
|
|
312
|
+
" --preserveWatchOutput Do not clear the screen between watch rebuilds",
|
|
313
|
+
" --outDir <dir> Override compilerOptions.outDir for this invocation",
|
|
314
|
+
" --rewrite-mode <mode> Native rewrite backend id",
|
|
315
|
+
" --quiet Keep native build output quiet (default)",
|
|
316
|
+
" --verbose Print the native build summary and emitted files",
|
|
317
|
+
" --out <path> Write transform output to a file instead of stdout",
|
|
318
|
+
" --binary <path> Use an explicit native backend binary",
|
|
319
|
+
"",
|
|
320
|
+
"Plugin contract:",
|
|
321
|
+
" ttsc reads compilerOptions.plugins from tsconfig.json.",
|
|
322
|
+
" TS plugins may export `definePlugin(...)` from ttsc.",
|
|
323
|
+
" Native consumer plugins may also provide their own binary path.",
|
|
324
|
+
"",
|
|
325
|
+
"Compatibility aliases:",
|
|
326
|
+
" ttsc build [options] Same project build lane as `ttsc [options]`.",
|
|
327
|
+
" ttsc check [options] Same as `ttsc --noEmit [options]`.",
|
|
328
|
+
].join("\n"),
|
|
329
|
+
);
|
|
330
|
+
process.stdout.write("\n");
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function runSingleFile(options: BuildInvocation): number {
|
|
334
|
+
if (options.files.length !== 1) {
|
|
335
|
+
throw new Error(
|
|
336
|
+
"ttsc: single-file mode currently accepts exactly one input file",
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
340
|
+
const file = path.resolve(cwd, options.files[0]!);
|
|
341
|
+
const out = resolveSingleFileOut(file, cwd, options.outDir);
|
|
342
|
+
const text = transform({
|
|
343
|
+
binary: options.binary,
|
|
344
|
+
cwd,
|
|
345
|
+
file,
|
|
346
|
+
out,
|
|
347
|
+
rewriteMode: options.rewriteMode,
|
|
348
|
+
tsconfig: options.tsconfig,
|
|
349
|
+
});
|
|
350
|
+
if (!fs.existsSync(out)) {
|
|
351
|
+
fs.mkdirSync(path.dirname(out), { recursive: true });
|
|
352
|
+
fs.writeFileSync(out, text, "utf8");
|
|
353
|
+
}
|
|
354
|
+
process.stdout.write(`${path.relative(cwd, out) || path.basename(out)}\n`);
|
|
355
|
+
return 0;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function resolveSingleFileOut(
|
|
359
|
+
file: string,
|
|
360
|
+
cwd: string,
|
|
361
|
+
outDir?: string,
|
|
362
|
+
): string {
|
|
363
|
+
const relative = path.relative(cwd, file);
|
|
364
|
+
const jsRelative = relative.replace(/\.[cm]?tsx?$/i, ".js");
|
|
365
|
+
if (outDir) {
|
|
366
|
+
return path.resolve(cwd, outDir, jsRelative);
|
|
367
|
+
}
|
|
368
|
+
return file.replace(/\.[cm]?tsx?$/i, ".js");
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function runWatch(options: BuildInvocation, checkOnly: boolean): number {
|
|
372
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
373
|
+
const invocation: BuildInvocation = {
|
|
374
|
+
...options,
|
|
375
|
+
cwd,
|
|
376
|
+
quiet: true,
|
|
377
|
+
};
|
|
378
|
+
const root = path.dirname(
|
|
379
|
+
resolveProjectConfig({
|
|
380
|
+
cwd,
|
|
381
|
+
tsconfig: options.tsconfig,
|
|
382
|
+
}),
|
|
383
|
+
);
|
|
384
|
+
const watchRoot = resolveProjectRoot({
|
|
385
|
+
cwd,
|
|
386
|
+
tsconfig: options.tsconfig,
|
|
387
|
+
});
|
|
388
|
+
const directories = collectWatchDirectories(watchRoot);
|
|
389
|
+
const watchers = directories.map((dir) =>
|
|
390
|
+
fs.watch(dir, { persistent: true }, () => trigger()),
|
|
391
|
+
);
|
|
392
|
+
let running = false;
|
|
393
|
+
let rerun = false;
|
|
394
|
+
let timer: NodeJS.Timeout | null = null;
|
|
395
|
+
|
|
396
|
+
const runOnce = () => {
|
|
397
|
+
running = true;
|
|
398
|
+
if (!options.preserveWatchOutput) {
|
|
399
|
+
process.stdout.write("\x1bc");
|
|
400
|
+
}
|
|
401
|
+
process.stdout.write(
|
|
402
|
+
`[ttsc] rebuilding at ${new Date().toLocaleTimeString()}\n`,
|
|
403
|
+
);
|
|
404
|
+
const status =
|
|
405
|
+
invocation.files.length !== 0
|
|
406
|
+
? runSingleFile(invocation)
|
|
407
|
+
: (() => {
|
|
408
|
+
const result = checkOnly ? check(invocation) : build(invocation);
|
|
409
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
410
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
411
|
+
return result.status;
|
|
412
|
+
})();
|
|
413
|
+
process.stdout.write(
|
|
414
|
+
`[ttsc] ${status === 0 ? "watch build complete" : "watch build failed"}\n`,
|
|
415
|
+
);
|
|
416
|
+
running = false;
|
|
417
|
+
if (rerun) {
|
|
418
|
+
rerun = false;
|
|
419
|
+
trigger();
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
const trigger = () => {
|
|
423
|
+
if (running) {
|
|
424
|
+
rerun = true;
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (timer) clearTimeout(timer);
|
|
428
|
+
timer = setTimeout(runOnce, 60);
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
const close = () => {
|
|
432
|
+
if (timer) clearTimeout(timer);
|
|
433
|
+
for (const watcher of watchers) watcher.close();
|
|
434
|
+
};
|
|
435
|
+
process.on("SIGINT", () => {
|
|
436
|
+
close();
|
|
437
|
+
process.exit(0);
|
|
438
|
+
});
|
|
439
|
+
process.on("SIGTERM", () => {
|
|
440
|
+
close();
|
|
441
|
+
process.exit(0);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
process.stdout.write(`[ttsc] watching ${path.relative(cwd, root) || "."}\n`);
|
|
445
|
+
runOnce();
|
|
446
|
+
return 0;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function collectWatchDirectories(root: string): string[] {
|
|
450
|
+
const out: string[] = [];
|
|
451
|
+
const stack: string[] = [root];
|
|
452
|
+
while (stack.length !== 0) {
|
|
453
|
+
const current = stack.pop()!;
|
|
454
|
+
out.push(current);
|
|
455
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
456
|
+
if (
|
|
457
|
+
entry.name === "node_modules" ||
|
|
458
|
+
entry.name === ".git" ||
|
|
459
|
+
entry.name === "lib" ||
|
|
460
|
+
entry.name === "dist"
|
|
461
|
+
) {
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
if (entry.isDirectory()) {
|
|
465
|
+
stack.push(path.join(current, entry.name));
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return out;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function takeValue(flag: string, rest: string[]): string {
|
|
473
|
+
const value = rest.shift();
|
|
474
|
+
if (!value) {
|
|
475
|
+
throw new Error(`ttsc: ${flag} requires a value`);
|
|
476
|
+
}
|
|
477
|
+
return value;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function takeRewriteMode(value: string): string {
|
|
481
|
+
if (value.length === 0) {
|
|
482
|
+
throw new Error("ttsc: rewrite mode must not be empty");
|
|
483
|
+
}
|
|
484
|
+
return value;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function formatError(error: unknown): string {
|
|
488
|
+
if (error instanceof Error) {
|
|
489
|
+
return error.message;
|
|
490
|
+
}
|
|
491
|
+
return String(error);
|
|
492
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ttsc — public TypeScript entry.
|
|
3
|
+
*
|
|
4
|
+
* Exports:
|
|
5
|
+
* - platform helpers (`resolveBinary`, `installHint`, …) — used by the
|
|
6
|
+
* public launcher and any external tool that wants to know where
|
|
7
|
+
* the native binary lives without spawning it.
|
|
8
|
+
* - programmatic API (`transform`, `build`, `check`, `version`) — a thin
|
|
9
|
+
* TS wrapper around the Go binary that bundler adapters (unplugin, vite,
|
|
10
|
+
* webpack, rollup, esbuild, rspack, farm) delegate to. Adapters never
|
|
11
|
+
* have to shell out themselves; they call these helpers and get back
|
|
12
|
+
* a string or a result record.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export * from "./platform";
|
|
16
|
+
export * from "./api";
|
|
17
|
+
export * from "./project";
|
|
18
|
+
export * from "./plugin";
|
|
19
|
+
export * from "./runner/register";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
const { spawnSync } = require("node:child_process");
|
|
7
|
+
|
|
8
|
+
const root = fs.realpathSync.native?.(__dirname) ?? fs.realpathSync(__dirname);
|
|
9
|
+
const packageRoot = path.resolve(root, "..", "..");
|
|
10
|
+
const command = process.platform === "win32" ? "go.exe" : "go";
|
|
11
|
+
const invocationCwd = process.cwd();
|
|
12
|
+
const argv = [...process.argv.slice(2)];
|
|
13
|
+
|
|
14
|
+
if (needsInvocationCwd(argv) && !argv.some((value) => value === "--cwd" || value.startsWith("--cwd="))) {
|
|
15
|
+
argv.push(`--cwd=${invocationCwd}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const result = spawnSync(command, ["run", "./cmd/ttsc", ...argv], {
|
|
19
|
+
cwd: packageRoot,
|
|
20
|
+
env: process.env,
|
|
21
|
+
stdio: "inherit",
|
|
22
|
+
windowsHide: true,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (result.error) {
|
|
26
|
+
process.stderr.write(`ttsc: failed to launch source checkout via ${command}: ${result.error.message}\n`);
|
|
27
|
+
process.exitCode = 1;
|
|
28
|
+
} else {
|
|
29
|
+
process.exitCode = result.status ?? 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function needsInvocationCwd(args) {
|
|
33
|
+
if (args.length === 0) return true;
|
|
34
|
+
const commandName = args[0];
|
|
35
|
+
return (
|
|
36
|
+
commandName === "build" ||
|
|
37
|
+
commandName === "check" ||
|
|
38
|
+
commandName === "transform" ||
|
|
39
|
+
commandName === "-p" ||
|
|
40
|
+
commandName === "--project"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
|
|
7
|
+
const builtCli = path.resolve(__dirname, "..", "..", "lib", "cli.js");
|
|
8
|
+
if (fs.existsSync(builtCli)) {
|
|
9
|
+
const { main } = require(builtCli);
|
|
10
|
+
const code = main(process.argv.slice(2));
|
|
11
|
+
if (typeof code === "number") {
|
|
12
|
+
process.exitCode = code;
|
|
13
|
+
}
|
|
14
|
+
} else {
|
|
15
|
+
process.stderr.write(
|
|
16
|
+
"ttsc: lib/cli.js is missing. Build ttsc first with `pnpm run build`.\n",
|
|
17
|
+
);
|
|
18
|
+
process.exitCode = 1;
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
|
|
7
|
+
const builtCli = path.resolve(__dirname, "..", "..", "lib", "runner", "cli.js");
|
|
8
|
+
if (fs.existsSync(builtCli)) {
|
|
9
|
+
const { main } = require(builtCli);
|
|
10
|
+
const code = main(process.argv.slice(2));
|
|
11
|
+
if (typeof code === "number") {
|
|
12
|
+
process.exitCode = code;
|
|
13
|
+
}
|
|
14
|
+
} else {
|
|
15
|
+
process.stderr.write(
|
|
16
|
+
"ttsx: lib/runner/cli.js is missing. Build ttsc first with `pnpm run build`.\n",
|
|
17
|
+
);
|
|
18
|
+
process.exitCode = 1;
|
|
19
|
+
}
|
package/src/native.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export type NativeRewriteMode = string;
|
|
2
|
+
export type NativePluginContractVersion = 1;
|
|
3
|
+
|
|
4
|
+
export interface TtscNativeBackend {
|
|
5
|
+
mode: NativeRewriteMode;
|
|
6
|
+
binary?: string;
|
|
7
|
+
contractVersion?: NativePluginContractVersion;
|
|
8
|
+
capabilities?: readonly string[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface TtscNativePluginShape {
|
|
12
|
+
name: string;
|
|
13
|
+
native?: TtscNativeBackend;
|
|
14
|
+
nativeMode?: NativeRewriteMode;
|
|
15
|
+
nativeBinary?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function resolveNativeBackend(
|
|
19
|
+
plugin: TtscNativePluginShape,
|
|
20
|
+
): TtscNativeBackend | null {
|
|
21
|
+
if (plugin.native && (plugin.nativeMode || plugin.nativeBinary)) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`ttsc: plugin "${plugin.name}" must use either native or nativeMode/nativeBinary, not both`,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
if (!plugin.native) {
|
|
27
|
+
if (
|
|
28
|
+
plugin.nativeBinary &&
|
|
29
|
+
(!plugin.nativeMode || plugin.nativeMode === "none")
|
|
30
|
+
) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`ttsc: plugin "${plugin.name}" declared nativeBinary without a non-empty nativeMode`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if (!plugin.nativeMode || plugin.nativeMode === "none") {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
binary: plugin.nativeBinary,
|
|
40
|
+
contractVersion: 1,
|
|
41
|
+
mode: plugin.nativeMode,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const backend = plugin.native;
|
|
46
|
+
if (!backend.mode || backend.mode === "none") {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`ttsc: plugin "${plugin.name}" declared native without a non-empty mode`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
if (backend.contractVersion !== undefined && backend.contractVersion !== 1) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`ttsc: plugin "${plugin.name}" requested unsupported native contract version ${backend.contractVersion}`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
...backend,
|
|
58
|
+
contractVersion: backend.contractVersion ?? 1,
|
|
59
|
+
};
|
|
60
|
+
}
|