ttsc 0.4.2 → 0.4.3-dev.20260426-2
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/lib/api.d.ts +1 -1
- package/lib/api.js +263 -71
- package/lib/api.js.map +1 -1
- package/lib/cli.js +4 -4
- package/lib/cli.js.map +1 -1
- package/lib/index.d.ts +8 -7
- package/lib/index.js +8 -7
- package/lib/index.js.map +1 -1
- package/lib/runner/cli.js +1 -1
- package/lib/runner/cli.js.map +1 -1
- package/lib/runner/register.js +47 -113
- package/lib/runner/register.js.map +1 -1
- package/lib/tsgo.d.ts +24 -0
- package/lib/tsgo.js +103 -0
- package/lib/tsgo.js.map +1 -0
- package/package.json +9 -9
- package/src/api.ts +314 -74
- package/src/cli.ts +4 -4
- package/src/index.ts +8 -7
- package/src/runner/cli.ts +2 -2
- package/src/runner/register.ts +61 -131
- package/src/tsgo.ts +108 -0
- package/test/tsgo.test.ts +68 -0
package/src/api.ts
CHANGED
|
@@ -2,26 +2,26 @@
|
|
|
2
2
|
* Programmatic API for ttsc.
|
|
3
3
|
*
|
|
4
4
|
* This module is the TypeScript surface bundler adapters (unplugin, vite,
|
|
5
|
-
* webpack, rollup, esbuild, rspack, farm, next/swc, bun) consume.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* webpack, rollup, esbuild, rspack, farm, next/swc, bun) consume. Project
|
|
6
|
+
* builds delegate to the consuming project's `@typescript/native-preview`
|
|
7
|
+
* `tsgo` binary, while plugin-selected native sidecars remain opt-in.
|
|
8
8
|
*
|
|
9
9
|
* Contract:
|
|
10
10
|
*
|
|
11
11
|
* - `transform()` emits one file's rewritten JS, returning the text.
|
|
12
12
|
* - `build()` runs the whole project (tsgo + ttsc rewrite + --emit).
|
|
13
13
|
* - `check()` runs the analysis pass without emitting (CI gate use).
|
|
14
|
-
* - `version()` returns the
|
|
14
|
+
* - `version()` returns the wrapper and resolved `tsgo` version banner.
|
|
15
15
|
*
|
|
16
16
|
* All helpers accept a `binary` override so tests can point at a specific
|
|
17
|
-
* executable without touching PATH or node_modules.
|
|
17
|
+
* `tsgo` executable without touching PATH or node_modules.
|
|
18
18
|
*/
|
|
19
19
|
import { spawnSync } from "node:child_process";
|
|
20
20
|
import * as fs from "node:fs";
|
|
21
21
|
import * as os from "node:os";
|
|
22
22
|
import * as path from "node:path";
|
|
23
23
|
|
|
24
|
-
import { type ResolveOptions,
|
|
24
|
+
import { type ResolveOptions, resolveBinary } from "./platform";
|
|
25
25
|
import {
|
|
26
26
|
type TtscPlugin,
|
|
27
27
|
applyPluginTransforms,
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
resolveProjectConfig,
|
|
33
33
|
resolveProjectRoot,
|
|
34
34
|
} from "./project";
|
|
35
|
+
import { type ResolvedTsgo, resolveTsgo } from "./tsgo";
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Options shared by every API call. `binary` takes precedence over platform
|
|
@@ -39,7 +40,7 @@ import {
|
|
|
39
40
|
* current process env.
|
|
40
41
|
*/
|
|
41
42
|
export interface CommonOptions extends ResolveOptions {
|
|
42
|
-
/** Absolute path to an already-resolved
|
|
43
|
+
/** Absolute path to an already-resolved tsgo binary. Skips package resolution. */
|
|
43
44
|
binary?: string;
|
|
44
45
|
/** Working directory passed to the child process. */
|
|
45
46
|
cwd?: string;
|
|
@@ -85,30 +86,13 @@ export interface BuildOptions extends CommonOptions {
|
|
|
85
86
|
outDir?: string;
|
|
86
87
|
/** Suppress the per-call summary banner. Default: `true`. */
|
|
87
88
|
quiet?: boolean;
|
|
89
|
+
/** @internal Caller already ran diagnostics and accepts responsibility. */
|
|
90
|
+
skipDiagnosticsCheck?: boolean;
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
/** Options for `check()`. */
|
|
91
94
|
export type CheckOptions = Omit<BuildOptions, "emit">;
|
|
92
95
|
|
|
93
|
-
/**
|
|
94
|
-
* Resolve the binary or throw a user-friendly error. Extracted so every API
|
|
95
|
-
* helper shares the same failure mode.
|
|
96
|
-
*/
|
|
97
|
-
function resolveOrThrow(opts: CommonOptions): string {
|
|
98
|
-
if (
|
|
99
|
-
opts.binary &&
|
|
100
|
-
path.isAbsolute(opts.binary) &&
|
|
101
|
-
fs.existsSync(opts.binary)
|
|
102
|
-
) {
|
|
103
|
-
return opts.binary;
|
|
104
|
-
}
|
|
105
|
-
const bin = resolveBinary(opts);
|
|
106
|
-
if (!bin) {
|
|
107
|
-
throw new Error(installHint(opts));
|
|
108
|
-
}
|
|
109
|
-
return bin;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
96
|
/** Merge spawn env without clobbering unrelated vars. */
|
|
113
97
|
function mergeEnv(extra?: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|
114
98
|
if (!extra) return process.env;
|
|
@@ -184,6 +168,62 @@ export function transform(options: TransformOptions): string {
|
|
|
184
168
|
? options.file
|
|
185
169
|
: path.resolve(options.cwd ?? process.cwd(), options.file),
|
|
186
170
|
);
|
|
171
|
+
if (execution.nativeBinary) {
|
|
172
|
+
return transformWithNativeBinary(options, execution, sourceFile);
|
|
173
|
+
}
|
|
174
|
+
if (execution.nativeMode !== "none") {
|
|
175
|
+
throw new Error(
|
|
176
|
+
`ttsc.transform: native rewrite mode "${execution.nativeMode}" requires a plugin-provided version-matched binary`,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const tempOutDir = fs.mkdtempSync(path.join(os.tmpdir(), "ttsc-transform-"));
|
|
181
|
+
try {
|
|
182
|
+
const result = build({
|
|
183
|
+
...options,
|
|
184
|
+
emit: true,
|
|
185
|
+
outDir: tempOutDir,
|
|
186
|
+
plugins: false,
|
|
187
|
+
tsconfig: execution.tsconfig,
|
|
188
|
+
});
|
|
189
|
+
if (result.status !== 0) {
|
|
190
|
+
throw new Error(
|
|
191
|
+
"ttsc.transform exited " +
|
|
192
|
+
result.status +
|
|
193
|
+
"\n" +
|
|
194
|
+
(result.stderr || result.stdout),
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
const emitted = findEmittedFile(tempOutDir, execution.projectRoot, sourceFile);
|
|
198
|
+
if (!emitted) {
|
|
199
|
+
throw new Error(`ttsc.transform: no output produced for ${sourceFile}`);
|
|
200
|
+
}
|
|
201
|
+
const transformed = finalizeTransformText(
|
|
202
|
+
execution.plugins,
|
|
203
|
+
{
|
|
204
|
+
command: "transform",
|
|
205
|
+
cwd: execution.cwd,
|
|
206
|
+
projectRoot: execution.projectRoot,
|
|
207
|
+
sourceFile,
|
|
208
|
+
tsconfig: execution.tsconfig,
|
|
209
|
+
},
|
|
210
|
+
fs.readFileSync(emitted, "utf8"),
|
|
211
|
+
);
|
|
212
|
+
if (options.out) {
|
|
213
|
+
fs.mkdirSync(path.dirname(options.out), { recursive: true });
|
|
214
|
+
fs.writeFileSync(options.out, transformed, "utf8");
|
|
215
|
+
}
|
|
216
|
+
return transformed;
|
|
217
|
+
} finally {
|
|
218
|
+
fs.rmSync(tempOutDir, { recursive: true, force: true });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function transformWithNativeBinary(
|
|
223
|
+
options: TransformOptions,
|
|
224
|
+
execution: ExecutionContext,
|
|
225
|
+
sourceFile: string,
|
|
226
|
+
): string {
|
|
187
227
|
const args = [
|
|
188
228
|
"transform",
|
|
189
229
|
"--file=" + sourceFile,
|
|
@@ -191,7 +231,7 @@ export function transform(options: TransformOptions): string {
|
|
|
191
231
|
"--rewrite-mode=" + execution.nativeMode,
|
|
192
232
|
];
|
|
193
233
|
|
|
194
|
-
const res = spawnBinary(execution.
|
|
234
|
+
const res = spawnBinary(execution.nativeBinary!, args, {
|
|
195
235
|
cwd: options.cwd,
|
|
196
236
|
env: mergeEnv(options.env),
|
|
197
237
|
encoding: "utf8",
|
|
@@ -199,7 +239,7 @@ export function transform(options: TransformOptions): string {
|
|
|
199
239
|
if (res.error) {
|
|
200
240
|
throw new Error(
|
|
201
241
|
"ttsc.transform: failed to spawn " +
|
|
202
|
-
execution.
|
|
242
|
+
execution.nativeBinary +
|
|
203
243
|
": " +
|
|
204
244
|
res.error.message,
|
|
205
245
|
);
|
|
@@ -249,45 +289,80 @@ export interface BuildResult {
|
|
|
249
289
|
*/
|
|
250
290
|
export function build(options: BuildOptions = {}): BuildResult {
|
|
251
291
|
const execution = resolveExecutionContext(options);
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
292
|
+
if (execution.nativeBinary) {
|
|
293
|
+
return buildWithNativeBinary(options, execution);
|
|
294
|
+
}
|
|
295
|
+
if (execution.nativeMode !== "none") {
|
|
296
|
+
return {
|
|
297
|
+
status: 2,
|
|
298
|
+
stdout: "",
|
|
299
|
+
stderr:
|
|
300
|
+
`ttsc.build: native rewrite mode "${execution.nativeMode}" requires ` +
|
|
301
|
+
"a version-matched sidecar build; project builds use the consuming project's @typescript/native-preview by default\n",
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (options.emit !== false && options.skipDiagnosticsCheck !== true) {
|
|
306
|
+
const checked = runTsgo(execution, ["--noEmit"], options);
|
|
307
|
+
if (checked.status !== 0) {
|
|
308
|
+
return checked;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const args = createTsgoBuildArgs(execution, options, {
|
|
313
|
+
listEmittedFiles: options.emit !== false && execution.plugins.length > 0,
|
|
314
|
+
});
|
|
315
|
+
const res = spawnBinary(execution.tsgo.binary, args, {
|
|
316
|
+
cwd: execution.projectRoot,
|
|
268
317
|
env: mergeEnv(options.env),
|
|
269
318
|
encoding: "utf8",
|
|
270
319
|
});
|
|
271
320
|
if (res.error) {
|
|
272
321
|
throw new Error(
|
|
273
|
-
"ttsc.build: failed to spawn " +
|
|
322
|
+
"ttsc.build: failed to spawn " +
|
|
323
|
+
execution.tsgo.binary +
|
|
324
|
+
": " +
|
|
325
|
+
res.error.message,
|
|
274
326
|
);
|
|
275
327
|
}
|
|
276
|
-
|
|
277
|
-
try {
|
|
278
|
-
const emittedFiles = JSON.parse(
|
|
279
|
-
fs.readFileSync(manifest, "utf8"),
|
|
280
|
-
) as string[];
|
|
281
|
-
applyBuildPlugins(execution.plugins, execution, emittedFiles);
|
|
282
|
-
} finally {
|
|
283
|
-
fs.rmSync(path.dirname(manifest), { recursive: true, force: true });
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
return {
|
|
328
|
+
const result = {
|
|
287
329
|
status: res.status ?? 1,
|
|
288
330
|
stdout: outputText(res.stdout),
|
|
289
331
|
stderr: outputText(res.stderr),
|
|
290
332
|
};
|
|
333
|
+
const emittedFiles = parseEmittedFiles(result.stdout);
|
|
334
|
+
if (emittedFiles.length !== 0) {
|
|
335
|
+
result.stdout = stripEmittedFileLines(result.stdout);
|
|
336
|
+
}
|
|
337
|
+
if (result.status === 0 && options.emit !== false && execution.plugins.length > 0) {
|
|
338
|
+
applyBuildPlugins(execution.plugins, execution, emittedFiles);
|
|
339
|
+
}
|
|
340
|
+
return normalizeFailedDiagnostics(result);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function buildWithNativeBinary(
|
|
344
|
+
options: BuildOptions,
|
|
345
|
+
execution: ExecutionContext,
|
|
346
|
+
): BuildResult {
|
|
347
|
+
const args = createNativeBuildArgs(execution, options);
|
|
348
|
+
const res = spawnBinary(execution.nativeBinary!, args, {
|
|
349
|
+
cwd: execution.projectRoot,
|
|
350
|
+
env: mergeEnv(options.env),
|
|
351
|
+
encoding: "utf8",
|
|
352
|
+
});
|
|
353
|
+
if (res.error) {
|
|
354
|
+
throw new Error(
|
|
355
|
+
"ttsc.build: failed to spawn " +
|
|
356
|
+
execution.nativeBinary +
|
|
357
|
+
": " +
|
|
358
|
+
res.error.message,
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
return normalizeFailedDiagnostics({
|
|
362
|
+
status: res.status ?? 1,
|
|
363
|
+
stdout: outputText(res.stdout),
|
|
364
|
+
stderr: outputText(res.stderr),
|
|
365
|
+
});
|
|
291
366
|
}
|
|
292
367
|
|
|
293
368
|
/**
|
|
@@ -298,10 +373,82 @@ export function check(options: CheckOptions = {}): BuildResult {
|
|
|
298
373
|
return build({ ...options, emit: false });
|
|
299
374
|
}
|
|
300
375
|
|
|
376
|
+
function runTsgo(
|
|
377
|
+
execution: ExecutionContext,
|
|
378
|
+
extraArgs: readonly string[],
|
|
379
|
+
options: BuildOptions,
|
|
380
|
+
): BuildResult {
|
|
381
|
+
const res = spawnBinary(
|
|
382
|
+
execution.tsgo.binary,
|
|
383
|
+
["-p", execution.tsconfig, ...extraArgs],
|
|
384
|
+
{
|
|
385
|
+
cwd: execution.projectRoot,
|
|
386
|
+
env: mergeEnv(options.env),
|
|
387
|
+
encoding: "utf8",
|
|
388
|
+
},
|
|
389
|
+
);
|
|
390
|
+
if (res.error) {
|
|
391
|
+
throw new Error(
|
|
392
|
+
"ttsc: failed to spawn " + execution.tsgo.binary + ": " + res.error.message,
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
return normalizeFailedDiagnostics({
|
|
396
|
+
status: res.status ?? 1,
|
|
397
|
+
stdout: outputText(res.stdout),
|
|
398
|
+
stderr: outputText(res.stderr),
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function createTsgoBuildArgs(
|
|
403
|
+
execution: ExecutionContext,
|
|
404
|
+
options: BuildOptions,
|
|
405
|
+
flags: { listEmittedFiles: boolean },
|
|
406
|
+
): string[] {
|
|
407
|
+
const args = ["-p", execution.tsconfig];
|
|
408
|
+
if (options.emit === true) {
|
|
409
|
+
args.push("--noEmit", "false", "--emitDeclarationOnly", "false");
|
|
410
|
+
} else if (options.emit === false) {
|
|
411
|
+
args.push("--noEmit");
|
|
412
|
+
}
|
|
413
|
+
if (options.outDir) {
|
|
414
|
+
args.push("--outDir", path.resolve(execution.cwd, options.outDir));
|
|
415
|
+
}
|
|
416
|
+
if (flags.listEmittedFiles) {
|
|
417
|
+
args.push("--listEmittedFiles");
|
|
418
|
+
}
|
|
419
|
+
return args;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function createNativeBuildArgs(
|
|
423
|
+
execution: ExecutionContext,
|
|
424
|
+
options: BuildOptions,
|
|
425
|
+
): string[] {
|
|
426
|
+
const args = [
|
|
427
|
+
options.emit === false ? "check" : "build",
|
|
428
|
+
"--tsconfig=" + execution.tsconfig,
|
|
429
|
+
"--rewrite-mode=" + execution.nativeMode,
|
|
430
|
+
"--cwd=" + execution.projectRoot,
|
|
431
|
+
];
|
|
432
|
+
if (options.emit === true) {
|
|
433
|
+
args.push("--emit");
|
|
434
|
+
} else if (options.emit === false) {
|
|
435
|
+
args.push("--noEmit");
|
|
436
|
+
}
|
|
437
|
+
if (options.outDir) {
|
|
438
|
+
args.push("--outDir=" + path.resolve(execution.cwd, options.outDir));
|
|
439
|
+
}
|
|
440
|
+
if (options.quiet === false) {
|
|
441
|
+
args.push("--verbose");
|
|
442
|
+
} else if (options.quiet === true) {
|
|
443
|
+
args.push("--quiet");
|
|
444
|
+
}
|
|
445
|
+
return args;
|
|
446
|
+
}
|
|
447
|
+
|
|
301
448
|
/** Ask the binary for its version banner. Handy for user-agent strings. */
|
|
302
449
|
export function version(options: CommonOptions = {}): string {
|
|
303
|
-
const
|
|
304
|
-
const res = spawnBinary(
|
|
450
|
+
const tsgo = resolveTsgo(options);
|
|
451
|
+
const res = spawnBinary(tsgo.binary, ["--version"], {
|
|
305
452
|
encoding: "utf8",
|
|
306
453
|
});
|
|
307
454
|
if (res.error || res.status !== 0) {
|
|
@@ -309,7 +456,7 @@ export function version(options: CommonOptions = {}): string {
|
|
|
309
456
|
"ttsc.version: failed: " + (outputText(res.stderr) || res.error?.message),
|
|
310
457
|
);
|
|
311
458
|
}
|
|
312
|
-
return outputText(res.stdout).trim()
|
|
459
|
+
return `ttsc ${readOwnPackageVersion()} (${outputText(res.stdout).trim()})`;
|
|
313
460
|
}
|
|
314
461
|
|
|
315
462
|
/**
|
|
@@ -322,11 +469,12 @@ export function transformAsync(options: TransformOptions): Promise<string> {
|
|
|
322
469
|
}
|
|
323
470
|
|
|
324
471
|
interface ExecutionContext {
|
|
325
|
-
bin: string;
|
|
326
472
|
cwd: string;
|
|
473
|
+
nativeBinary: string | null;
|
|
327
474
|
nativeMode: string;
|
|
328
475
|
plugins: readonly TtscPlugin[];
|
|
329
476
|
projectRoot: string;
|
|
477
|
+
tsgo: ResolvedTsgo;
|
|
330
478
|
tsconfig: string;
|
|
331
479
|
}
|
|
332
480
|
|
|
@@ -334,33 +482,26 @@ function resolveExecutionContext(
|
|
|
334
482
|
options: CommonOptions & { tsconfig?: string },
|
|
335
483
|
): ExecutionContext {
|
|
336
484
|
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
337
|
-
const fallbackBinary =
|
|
338
|
-
options.binary &&
|
|
339
|
-
path.isAbsolute(options.binary) &&
|
|
340
|
-
fs.existsSync(options.binary)
|
|
341
|
-
? options.binary
|
|
342
|
-
: resolveBinary(options);
|
|
343
485
|
const tsconfig = resolveProjectConfig({
|
|
344
486
|
cwd,
|
|
345
487
|
tsconfig: options.tsconfig,
|
|
346
488
|
});
|
|
347
489
|
const projectRoot = resolveProjectRoot({ cwd, tsconfig });
|
|
490
|
+
const tsgo = resolveTsgo({ ...options, cwd: projectRoot });
|
|
491
|
+
const fallbackBinary = resolveBinary(options);
|
|
348
492
|
const loaded = loadProjectPlugins({
|
|
349
493
|
binary: fallbackBinary ?? "",
|
|
350
494
|
cwd,
|
|
351
495
|
entries: options.plugins,
|
|
352
496
|
tsconfig,
|
|
353
497
|
});
|
|
354
|
-
const bin = options.binary ?? loaded.nativeBinary ?? fallbackBinary;
|
|
355
|
-
if (!bin) {
|
|
356
|
-
throw new Error(installHint(options));
|
|
357
|
-
}
|
|
358
498
|
return {
|
|
359
|
-
bin,
|
|
360
499
|
cwd,
|
|
500
|
+
nativeBinary: loaded.nativeBinary ?? null,
|
|
361
501
|
nativeMode: options.rewriteMode ?? loaded.nativeMode,
|
|
362
502
|
plugins: loaded.plugins.filter((plugin) => plugin.transformOutput),
|
|
363
503
|
projectRoot,
|
|
504
|
+
tsgo,
|
|
364
505
|
tsconfig,
|
|
365
506
|
};
|
|
366
507
|
}
|
|
@@ -410,7 +551,106 @@ function isJavaScriptOutput(file: string): boolean {
|
|
|
410
551
|
return /\.(?:[cm]?js)$/i.test(file);
|
|
411
552
|
}
|
|
412
553
|
|
|
413
|
-
function
|
|
414
|
-
const
|
|
415
|
-
|
|
554
|
+
function parseEmittedFiles(stdout: string): string[] {
|
|
555
|
+
const out: string[] = [];
|
|
556
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
557
|
+
const match = line.match(/^TSFILE:\s*(.+)$/);
|
|
558
|
+
if (match?.[1]) {
|
|
559
|
+
out.push(path.resolve(match[1].trim()));
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return out;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function stripEmittedFileLines(stdout: string): string {
|
|
566
|
+
return stdout
|
|
567
|
+
.split(/\r?\n/)
|
|
568
|
+
.filter((line) => !/^TSFILE:\s*/.test(line))
|
|
569
|
+
.join("\n")
|
|
570
|
+
.replace(/\n+$/, "");
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function normalizeFailedDiagnostics(result: BuildResult): BuildResult {
|
|
574
|
+
if (result.status === 0 || result.stderr.trim().length !== 0) {
|
|
575
|
+
return result;
|
|
576
|
+
}
|
|
577
|
+
if (result.stdout.trim().length === 0) {
|
|
578
|
+
return result;
|
|
579
|
+
}
|
|
580
|
+
return {
|
|
581
|
+
status: result.status,
|
|
582
|
+
stdout: "",
|
|
583
|
+
stderr: result.stdout,
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function findEmittedFile(
|
|
588
|
+
outDir: string,
|
|
589
|
+
projectRoot: string,
|
|
590
|
+
sourceFile: string,
|
|
591
|
+
): string | null {
|
|
592
|
+
let best: string | null = null;
|
|
593
|
+
let bestScore = 0;
|
|
594
|
+
for (const file of listJavaScriptFiles(outDir)) {
|
|
595
|
+
const score = sharedSourceStemSegments(file, sourceFile);
|
|
596
|
+
if (score > bestScore) {
|
|
597
|
+
best = file;
|
|
598
|
+
bestScore = score;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
if (best) {
|
|
602
|
+
return best;
|
|
603
|
+
}
|
|
604
|
+
const relative = path.relative(projectRoot, sourceFile);
|
|
605
|
+
if (relative && !relative.startsWith("..") && !path.isAbsolute(relative)) {
|
|
606
|
+
const exact = path.resolve(outDir, relative).replace(/\.[cm]?tsx?$/i, ".js");
|
|
607
|
+
if (fs.existsSync(exact)) {
|
|
608
|
+
return exact;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return null;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function listJavaScriptFiles(root: string): string[] {
|
|
615
|
+
const out: string[] = [];
|
|
616
|
+
const stack = [root];
|
|
617
|
+
while (stack.length !== 0) {
|
|
618
|
+
const current = stack.pop()!;
|
|
619
|
+
if (!fs.existsSync(current)) continue;
|
|
620
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
621
|
+
const next = path.join(current, entry.name);
|
|
622
|
+
if (entry.isDirectory()) {
|
|
623
|
+
stack.push(next);
|
|
624
|
+
} else if (entry.isFile() && isJavaScriptOutput(next)) {
|
|
625
|
+
out.push(next);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return out;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function sharedSourceStemSegments(outPath: string, srcPath: string): number {
|
|
633
|
+
const trim = (value: string): string[] => {
|
|
634
|
+
const normalized = value.replace(/\\/g, "/");
|
|
635
|
+
return normalized.replace(/\.[^.]+$/, "").split("/");
|
|
636
|
+
};
|
|
637
|
+
const a = trim(outPath);
|
|
638
|
+
const b = trim(srcPath);
|
|
639
|
+
const n = Math.min(a.length, b.length);
|
|
640
|
+
let shared = 0;
|
|
641
|
+
for (let i = 1; i <= n; i += 1) {
|
|
642
|
+
if (a[a.length - i] !== b[b.length - i]) break;
|
|
643
|
+
shared += 1;
|
|
644
|
+
}
|
|
645
|
+
return shared;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function readOwnPackageVersion(): string {
|
|
649
|
+
try {
|
|
650
|
+
const file = path.resolve(__dirname, "..", "package.json");
|
|
651
|
+
const pkg = JSON.parse(fs.readFileSync(file, "utf8")) as { version?: string };
|
|
652
|
+
return pkg.version ?? "0.0.0";
|
|
653
|
+
} catch {
|
|
654
|
+
return "0.0.0";
|
|
655
|
+
}
|
|
416
656
|
}
|
package/src/cli.ts
CHANGED
|
@@ -311,11 +311,11 @@ function printHelp(): void {
|
|
|
311
311
|
" -w, --watch Rebuild when project files change",
|
|
312
312
|
" --preserveWatchOutput Do not clear the screen between watch rebuilds",
|
|
313
313
|
" --outDir <dir> Override compilerOptions.outDir for this invocation",
|
|
314
|
-
" --rewrite-mode <mode>
|
|
315
|
-
" --quiet Keep
|
|
316
|
-
" --verbose Print the
|
|
314
|
+
" --rewrite-mode <mode> Plugin-selected native rewrite backend id",
|
|
315
|
+
" --quiet Keep build output quiet (default)",
|
|
316
|
+
" --verbose Print the build summary and emitted files",
|
|
317
317
|
" --out <path> Write transform output to a file instead of stdout",
|
|
318
|
-
" --binary <path> Use an explicit
|
|
318
|
+
" --binary <path> Use an explicit tsgo binary",
|
|
319
319
|
"",
|
|
320
320
|
"Plugin contract:",
|
|
321
321
|
" ttsc reads compilerOptions.plugins from tsconfig.json.",
|
package/src/index.ts
CHANGED
|
@@ -2,18 +2,19 @@
|
|
|
2
2
|
* ttsc — public TypeScript entry.
|
|
3
3
|
*
|
|
4
4
|
* Exports:
|
|
5
|
-
* -
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* - tsgo helpers (`resolveTsgo`, …) — resolve the consuming project's
|
|
6
|
+
* `@typescript/native-preview` compiler binary.
|
|
7
|
+
* - platform helpers (`resolveBinary`, `installHint`, …) — legacy helpers
|
|
8
|
+
* for plugin-selected native sidecars.
|
|
8
9
|
* - programmatic API (`transform`, `build`, `check`, `version`) — a thin
|
|
9
|
-
* TS wrapper around the
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* a string or a result record.
|
|
10
|
+
* TS wrapper around the consumer `tsgo` binary plus JS output-plugin
|
|
11
|
+
* hooks. Adapters never have to shell out themselves; they call these
|
|
12
|
+
* helpers and get back a string or a result record.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
export * from "./platform";
|
|
16
16
|
export * from "./api";
|
|
17
17
|
export * from "./project";
|
|
18
18
|
export * from "./plugin";
|
|
19
|
+
export * from "./tsgo";
|
|
19
20
|
export * from "./runner/register";
|
package/src/runner/cli.ts
CHANGED
|
@@ -133,7 +133,7 @@ function parseCLI(argv: readonly string[]): ParsedCLI | "help" | "version" {
|
|
|
133
133
|
binary = current.slice("--binary=".length);
|
|
134
134
|
} else if (current.startsWith("-")) {
|
|
135
135
|
throw new Error(`ttsx: unknown option ${current}`);
|
|
136
|
-
|
|
136
|
+
} else {
|
|
137
137
|
entry = current;
|
|
138
138
|
}
|
|
139
139
|
break;
|
|
@@ -167,7 +167,7 @@ function printHelp(): void {
|
|
|
167
167
|
" -P, --project <file> Use an explicit tsconfig.json",
|
|
168
168
|
" --cwd <dir> Resolve entry/project relative to this directory",
|
|
169
169
|
" --cache-dir <dir> Override the compiled JS cache directory",
|
|
170
|
-
" --binary <path> Use an explicit
|
|
170
|
+
" --binary <path> Use an explicit tsgo binary",
|
|
171
171
|
" -r, --require <file> Preload a module before the entrypoint",
|
|
172
172
|
" -h, --help Show this help",
|
|
173
173
|
" -v, --version Print the runner version",
|