ttsc 0.7.3 → 0.8.0-dev.20260506
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 +3 -4
- package/cmd/ttsc/main.go +1 -1
- package/driver/program.go +56 -3
- package/driver/rewrite.go +22 -2
- package/lib/compiler/internal/runBuild.js +46 -49
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +43 -4
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/launcher/internal/runTtsc.js +20 -2
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.js +6 -0
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.js +12 -6
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscPlugin.d.ts +4 -3
- package/lib/structures/ITtscPlugin.js +4 -0
- package/lib/structures/ITtscPlugin.js.map +1 -1
- package/lib/structures/TtscPluginStage.d.ts +3 -11
- package/lib/structures/internal/ITtscLoadedNativePlugin.d.ts +2 -0
- package/package.json +11 -10
- package/shim/ast/lint.go +1 -0
- package/shim/ast/shim.go +5 -4
- package/shim/core/shim.go +3 -0
- package/src/compiler/internal/runBuild.ts +53 -85
- package/src/compiler/internal/transformProjectInMemory.ts +50 -6
- package/src/launcher/internal/runTtsc.ts +18 -2
- package/src/plugin/internal/buildSourcePlugin.ts +23 -0
- package/src/plugin/internal/loadProjectPlugins.ts +17 -6
- package/src/structures/ITtscPlugin.ts +12 -3
- package/src/structures/TtscPluginStage.ts +3 -11
- package/src/structures/internal/ITtscLoadedNativePlugin.ts +2 -0
- package/test/tsgo.test.ts +9 -6
- package/utility/host.go +735 -0
|
@@ -118,7 +118,7 @@ function transformProjectWithPlugins(
|
|
|
118
118
|
if (transformers.length === 0) {
|
|
119
119
|
return transformProjectWithNativeHost(options, project);
|
|
120
120
|
}
|
|
121
|
-
|
|
121
|
+
assertTransformHostCompatibility(transformers);
|
|
122
122
|
|
|
123
123
|
const plugin = transformers[0]!;
|
|
124
124
|
const res = spawnNative(
|
|
@@ -224,15 +224,59 @@ function serializeNativePlugins(
|
|
|
224
224
|
);
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
function
|
|
227
|
+
function assertTransformHostCompatibility(
|
|
228
228
|
plugins: readonly ITtscLoadedNativePlugin[],
|
|
229
229
|
): void {
|
|
230
230
|
const binaries = [...new Set(plugins.map((plugin) => plugin.binary))];
|
|
231
|
-
if (binaries.length
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
231
|
+
if (binaries.length <= 1) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (plugins.every(isFirstPartyUtilityTransformPlugin)) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
throw new Error(
|
|
238
|
+
"ttsc: multiple transform native backends cannot share one source-to-source pass; " +
|
|
239
|
+
"compose transform libraries through one aggregate native host",
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function isFirstPartyUtilityTransformPlugin(
|
|
244
|
+
plugin: ITtscLoadedNativePlugin,
|
|
245
|
+
): boolean {
|
|
246
|
+
if (plugin.stage !== "transform") return false;
|
|
247
|
+
if (!firstPartyUtilityPluginNames.has(plugin.name)) return false;
|
|
248
|
+
const manifest = readNearestPackageManifest(plugin.source);
|
|
249
|
+
return manifest?.name === plugin.name;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const firstPartyUtilityPluginNames = new Set([
|
|
253
|
+
"@ttsc/banner",
|
|
254
|
+
"@ttsc/paths",
|
|
255
|
+
"@ttsc/strip",
|
|
256
|
+
]);
|
|
257
|
+
|
|
258
|
+
function readNearestPackageManifest(
|
|
259
|
+
source: string,
|
|
260
|
+
): { name?: unknown } | undefined {
|
|
261
|
+
try {
|
|
262
|
+
let current = fs.statSync(source).isDirectory()
|
|
263
|
+
? source
|
|
264
|
+
: path.dirname(source);
|
|
265
|
+
for (let i = 0; i < 4; i += 1) {
|
|
266
|
+
const manifest = path.join(current, "package.json");
|
|
267
|
+
if (fs.existsSync(manifest)) {
|
|
268
|
+
return JSON.parse(fs.readFileSync(manifest, "utf8")) as {
|
|
269
|
+
name?: unknown;
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
const parent = path.dirname(current);
|
|
273
|
+
if (parent === current) break;
|
|
274
|
+
current = parent;
|
|
275
|
+
}
|
|
276
|
+
} catch {
|
|
277
|
+
return undefined;
|
|
235
278
|
}
|
|
279
|
+
return undefined;
|
|
236
280
|
}
|
|
237
281
|
|
|
238
282
|
function appendBuildResult(
|
|
@@ -89,6 +89,7 @@ function runPrepare(argv: readonly string[]): number {
|
|
|
89
89
|
const options = parseProjectArgs(argv);
|
|
90
90
|
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
91
91
|
const compiler = new TtscCompiler({
|
|
92
|
+
cacheDir: options.cacheDir,
|
|
92
93
|
cwd,
|
|
93
94
|
tsconfig: options.tsconfig,
|
|
94
95
|
});
|
|
@@ -118,6 +119,7 @@ function runClean(argv: readonly string[]): number {
|
|
|
118
119
|
const targets = [
|
|
119
120
|
path.join(projectRoot, "node_modules", ".ttsc"),
|
|
120
121
|
path.join(projectRoot, ".ttsc"),
|
|
122
|
+
...(options.cacheDir ? [path.resolve(cwd, options.cacheDir)] : []),
|
|
121
123
|
...(process.env.TTSC_CACHE_DIR
|
|
122
124
|
? [path.resolve(process.env.TTSC_CACHE_DIR, "plugins")]
|
|
123
125
|
: []),
|
|
@@ -204,6 +206,7 @@ function ensureExecutable(binary: string): void {
|
|
|
204
206
|
}
|
|
205
207
|
|
|
206
208
|
function parseProjectArgs(argv: readonly string[]) {
|
|
209
|
+
let cacheDir: string | undefined;
|
|
207
210
|
let cwd: string | undefined;
|
|
208
211
|
let tsconfig: string | undefined;
|
|
209
212
|
|
|
@@ -214,6 +217,9 @@ function parseProjectArgs(argv: readonly string[]) {
|
|
|
214
217
|
case "--cwd":
|
|
215
218
|
cwd = takeValue(current, rest);
|
|
216
219
|
break;
|
|
220
|
+
case "--cache-dir":
|
|
221
|
+
cacheDir = takeValue(current, rest);
|
|
222
|
+
break;
|
|
217
223
|
case "-p":
|
|
218
224
|
case "--tsconfig":
|
|
219
225
|
case "--project":
|
|
@@ -222,6 +228,8 @@ function parseProjectArgs(argv: readonly string[]) {
|
|
|
222
228
|
default:
|
|
223
229
|
if (current.startsWith("--cwd=")) {
|
|
224
230
|
cwd = current.slice("--cwd=".length);
|
|
231
|
+
} else if (current.startsWith("--cache-dir=")) {
|
|
232
|
+
cacheDir = current.slice("--cache-dir=".length);
|
|
225
233
|
} else if (current.startsWith("--tsconfig=")) {
|
|
226
234
|
tsconfig = current.slice("--tsconfig=".length);
|
|
227
235
|
} else if (current.startsWith("--project=")) {
|
|
@@ -232,11 +240,12 @@ function parseProjectArgs(argv: readonly string[]) {
|
|
|
232
240
|
break;
|
|
233
241
|
}
|
|
234
242
|
}
|
|
235
|
-
return { cwd, tsconfig };
|
|
243
|
+
return { cacheDir, cwd, tsconfig };
|
|
236
244
|
}
|
|
237
245
|
|
|
238
246
|
function parseBuildArgs(argv: readonly string[], checkOnly: boolean) {
|
|
239
247
|
let binary: string | undefined;
|
|
248
|
+
let cacheDir: string | undefined;
|
|
240
249
|
let cwd: string | undefined;
|
|
241
250
|
let emit: boolean | undefined = checkOnly ? false : undefined;
|
|
242
251
|
const files: string[] = [];
|
|
@@ -283,6 +292,9 @@ function parseBuildArgs(argv: readonly string[], checkOnly: boolean) {
|
|
|
283
292
|
case "--binary":
|
|
284
293
|
binary = takeValue(current, rest);
|
|
285
294
|
break;
|
|
295
|
+
case "--cache-dir":
|
|
296
|
+
cacheDir = takeValue(current, rest);
|
|
297
|
+
break;
|
|
286
298
|
default:
|
|
287
299
|
if (current.startsWith("--cwd=")) {
|
|
288
300
|
cwd = current.slice("--cwd=".length);
|
|
@@ -299,6 +311,8 @@ function parseBuildArgs(argv: readonly string[], checkOnly: boolean) {
|
|
|
299
311
|
current.slice("--preserveWatchOutput=".length) !== "false";
|
|
300
312
|
} else if (current.startsWith("--binary=")) {
|
|
301
313
|
binary = current.slice("--binary=".length);
|
|
314
|
+
} else if (current.startsWith("--cache-dir=")) {
|
|
315
|
+
cacheDir = current.slice("--cache-dir=".length);
|
|
302
316
|
} else if (current === "--verbose") {
|
|
303
317
|
quiet = false;
|
|
304
318
|
} else if (current.startsWith("-")) {
|
|
@@ -311,6 +325,7 @@ function parseBuildArgs(argv: readonly string[], checkOnly: boolean) {
|
|
|
311
325
|
}
|
|
312
326
|
return {
|
|
313
327
|
binary,
|
|
328
|
+
cacheDir,
|
|
314
329
|
cwd,
|
|
315
330
|
emit,
|
|
316
331
|
files,
|
|
@@ -349,11 +364,12 @@ function printHelp(): void {
|
|
|
349
364
|
" --quiet Keep build output quiet (default)",
|
|
350
365
|
" --verbose Print the build summary and emitted files",
|
|
351
366
|
" --binary <path> Use an explicit tsgo binary",
|
|
367
|
+
" --cache-dir <dir> Use this cache root for source-plugin builds",
|
|
352
368
|
"",
|
|
353
369
|
"Plugin contract:",
|
|
354
370
|
" ttsc reads compilerOptions.plugins from tsconfig.json.",
|
|
355
371
|
" Plugin modules are descriptors for ordered native transformer backends.",
|
|
356
|
-
" JS transformOutput/transformSource
|
|
372
|
+
" JS transformOutput/transformSource functions are not part of the public contract.",
|
|
357
373
|
"",
|
|
358
374
|
"Compatibility aliases:",
|
|
359
375
|
" ttsc build [options] Same project build lane as `ttsc [options]`.",
|
|
@@ -348,6 +348,20 @@ function resolveGoCompiler(): string {
|
|
|
348
348
|
/* fall through */
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
+
const platformPackage = path.resolve(
|
|
352
|
+
__dirname,
|
|
353
|
+
"..",
|
|
354
|
+
"..",
|
|
355
|
+
"..",
|
|
356
|
+
"..",
|
|
357
|
+
`ttsc-${process.platform}-${process.arch}`,
|
|
358
|
+
"bin",
|
|
359
|
+
"go",
|
|
360
|
+
"bin",
|
|
361
|
+
process.platform === "win32" ? "go.exe" : "go",
|
|
362
|
+
);
|
|
363
|
+
if (fs.existsSync(platformPackage)) return platformPackage;
|
|
364
|
+
|
|
351
365
|
const local = path.resolve(
|
|
352
366
|
__dirname,
|
|
353
367
|
"..",
|
|
@@ -361,6 +375,15 @@ function resolveGoCompiler(): string {
|
|
|
361
375
|
);
|
|
362
376
|
if (fs.existsSync(local)) return local;
|
|
363
377
|
|
|
378
|
+
const homeSdk = path.join(
|
|
379
|
+
process.env.HOME ?? "",
|
|
380
|
+
"go-sdk",
|
|
381
|
+
"go",
|
|
382
|
+
"bin",
|
|
383
|
+
process.platform === "win32" ? "go.exe" : "go",
|
|
384
|
+
);
|
|
385
|
+
if (fs.existsSync(homeSdk)) return homeSdk;
|
|
386
|
+
|
|
364
387
|
return "go";
|
|
365
388
|
}
|
|
366
389
|
|
|
@@ -58,6 +58,7 @@ export function loadProjectPlugins(options: {
|
|
|
58
58
|
const ttscVersion = readTtscVersion();
|
|
59
59
|
const tsgoVersion = readTsgoVersion(context.projectRoot);
|
|
60
60
|
plugins.forEach((plugin, index) => {
|
|
61
|
+
const stage = resolvePluginStage(plugin);
|
|
61
62
|
validatePluginSource(plugin);
|
|
62
63
|
const binary = buildSourcePlugin({
|
|
63
64
|
baseDir: context.projectRoot,
|
|
@@ -71,7 +72,8 @@ export function loadProjectPlugins(options: {
|
|
|
71
72
|
binary,
|
|
72
73
|
config: entries[index]!,
|
|
73
74
|
name: plugin.name,
|
|
74
|
-
|
|
75
|
+
source: plugin.source,
|
|
76
|
+
stage,
|
|
75
77
|
});
|
|
76
78
|
});
|
|
77
79
|
return {
|
|
@@ -106,11 +108,11 @@ function loadPluginEntry(
|
|
|
106
108
|
`ttsc: plugin "${specifier}" does not export a valid ttsc plugin`,
|
|
107
109
|
);
|
|
108
110
|
}
|
|
109
|
-
|
|
111
|
+
rejectJsTransformFunctions(specifier, plugin);
|
|
110
112
|
return plugin;
|
|
111
113
|
}
|
|
112
114
|
if (isTtscPlugin(candidate)) {
|
|
113
|
-
|
|
115
|
+
rejectJsTransformFunctions(specifier, candidate);
|
|
114
116
|
return candidate;
|
|
115
117
|
}
|
|
116
118
|
throw new Error(
|
|
@@ -126,10 +128,13 @@ function isTtscPlugin(value: unknown): value is ITtscPlugin {
|
|
|
126
128
|
);
|
|
127
129
|
}
|
|
128
130
|
|
|
129
|
-
function
|
|
131
|
+
function rejectJsTransformFunctions(
|
|
132
|
+
specifier: string,
|
|
133
|
+
candidate: object,
|
|
134
|
+
): void {
|
|
130
135
|
if ("transformSource" in candidate || "transformOutput" in candidate) {
|
|
131
136
|
throw new Error(
|
|
132
|
-
`ttsc: plugin "${specifier}" declares unsupported JS transform
|
|
137
|
+
`ttsc: plugin "${specifier}" declares unsupported JS transform functions; ` +
|
|
133
138
|
"declare a native backend instead",
|
|
134
139
|
);
|
|
135
140
|
}
|
|
@@ -140,6 +145,12 @@ function resolvePluginStage(plugin: ITtscPlugin): TtscPluginStage {
|
|
|
140
145
|
return "transform";
|
|
141
146
|
}
|
|
142
147
|
if (!isPluginStage(plugin.stage)) {
|
|
148
|
+
if (plugin.stage === "output") {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`ttsc: plugin "${plugin.name}" requested removed stage "output"; ` +
|
|
151
|
+
"upgrade the plugin to a transform-stage descriptor compatible with this ttsc version",
|
|
152
|
+
);
|
|
153
|
+
}
|
|
143
154
|
throw new Error(
|
|
144
155
|
`ttsc: plugin "${plugin.name}" requested unsupported stage ${JSON.stringify(plugin.stage)}`,
|
|
145
156
|
);
|
|
@@ -154,7 +165,7 @@ function validatePluginSource(plugin: ITtscPlugin): void {
|
|
|
154
165
|
}
|
|
155
166
|
|
|
156
167
|
function isPluginStage(value: string): value is TtscPluginStage {
|
|
157
|
-
return value === "transform" || value === "check"
|
|
168
|
+
return value === "transform" || value === "check";
|
|
158
169
|
}
|
|
159
170
|
|
|
160
171
|
function resolvePluginRequest(specifier: string, projectRoot: string): string {
|
|
@@ -48,11 +48,20 @@ export interface ITtscPlugin {
|
|
|
48
48
|
/**
|
|
49
49
|
* Pipeline stage implemented by the sidecar.
|
|
50
50
|
*
|
|
51
|
-
* Omit this field for normal compiler-transform plugins.
|
|
52
|
-
*
|
|
53
|
-
* emitted files (`"output"`).
|
|
51
|
+
* Omit this field for normal compiler-transform plugins. The only explicit
|
|
52
|
+
* non-transform stage is `"check"`.
|
|
54
53
|
*
|
|
55
54
|
* @default "transform"
|
|
56
55
|
*/
|
|
57
56
|
stage?: TtscPluginStage;
|
|
58
57
|
}
|
|
58
|
+
|
|
59
|
+
export function defineTtscPlugin<T extends ITtscPlugin>(plugin: T): T;
|
|
60
|
+
export function defineTtscPlugin<TContext, T extends ITtscPlugin>(
|
|
61
|
+
factory: (context: TContext) => T,
|
|
62
|
+
): (context: TContext) => T;
|
|
63
|
+
export function defineTtscPlugin(
|
|
64
|
+
value: ITtscPlugin | ((context: unknown) => ITtscPlugin),
|
|
65
|
+
): ITtscPlugin | ((context: unknown) => ITtscPlugin) {
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pipeline stage where a plugin's lazily built Go sidecar participates.
|
|
3
3
|
*
|
|
4
|
-
* - `"transform"`:
|
|
5
|
-
*
|
|
6
|
-
* typia-style source rewrites. During compilation, a transform sidecar
|
|
7
|
-
* produces the project's emitted output. During
|
|
8
|
-
* {@link TtscCompiler.transform}, the same sidecar may expose a
|
|
9
|
-
* source-to-source `transform` command that returns transformed TypeScript
|
|
10
|
-
* text.
|
|
4
|
+
* - `"transform"`: participates in the TypeScript-Go transform path. Transform
|
|
5
|
+
* plugins do not receive emitted JavaScript or emitted file text.
|
|
11
6
|
* - `"check"`: runs before emit and reports diagnostics only. Use this for
|
|
12
7
|
* lint or validation plugins that should fail the compile before JavaScript
|
|
13
8
|
* or declaration output is generated.
|
|
14
|
-
* - `"output"`: runs after emit for each emitted output file. Use this for
|
|
15
|
-
* post-processing plugins such as banner insertion, import-path rewriting,
|
|
16
|
-
* or output stripping that operate on `.js`, `.d.ts`, or map files.
|
|
17
9
|
*/
|
|
18
|
-
export type TtscPluginStage = "transform" | "check"
|
|
10
|
+
export type TtscPluginStage = "transform" | "check";
|
|
@@ -9,6 +9,8 @@ export interface ITtscLoadedNativePlugin {
|
|
|
9
9
|
config: ITtscProjectPluginConfig;
|
|
10
10
|
/** Stable plugin name used in diagnostics and native manifests. */
|
|
11
11
|
name: string;
|
|
12
|
+
/** Go source directory selected by the plugin descriptor. */
|
|
13
|
+
source: string;
|
|
12
14
|
/** Pipeline stage where the sidecar participates. */
|
|
13
15
|
stage: TtscPluginStage;
|
|
14
16
|
}
|
package/test/tsgo.test.ts
CHANGED
|
@@ -30,7 +30,7 @@ test("resolveTsgo resolves the consumer native-preview platform package", () =>
|
|
|
30
30
|
root,
|
|
31
31
|
"node_modules",
|
|
32
32
|
"@typescript",
|
|
33
|
-
|
|
33
|
+
`native-preview-${process.platform}-${process.arch}`,
|
|
34
34
|
);
|
|
35
35
|
fs.mkdirSync(nativeRoot, { recursive: true });
|
|
36
36
|
fs.mkdirSync(path.join(platformRoot, "lib"), { recursive: true });
|
|
@@ -46,21 +46,24 @@ test("resolveTsgo resolves the consumer native-preview platform package", () =>
|
|
|
46
46
|
fs.writeFileSync(
|
|
47
47
|
path.join(platformRoot, "package.json"),
|
|
48
48
|
JSON.stringify({
|
|
49
|
-
name:
|
|
49
|
+
name: `@typescript/native-preview-${process.platform}-${process.arch}`,
|
|
50
50
|
version: "7.0.0-dev.consumer",
|
|
51
51
|
}),
|
|
52
52
|
"utf8",
|
|
53
53
|
);
|
|
54
|
-
|
|
54
|
+
const binary = path.join(
|
|
55
|
+
platformRoot,
|
|
56
|
+
"lib",
|
|
57
|
+
process.platform === "win32" ? "tsgo.exe" : "tsgo",
|
|
58
|
+
);
|
|
59
|
+
fs.writeFileSync(binary, "", "utf8");
|
|
55
60
|
|
|
56
61
|
const resolved = resolveTsgo({
|
|
57
|
-
arch: "x64",
|
|
58
62
|
cwd: root,
|
|
59
63
|
env: {},
|
|
60
|
-
platform: "linux",
|
|
61
64
|
});
|
|
62
65
|
|
|
63
66
|
assert.equal(resolved.version, "7.0.0-dev.consumer");
|
|
64
67
|
assert.equal(resolved.gitHead, "abc123");
|
|
65
|
-
assert.equal(resolved.binary,
|
|
68
|
+
assert.equal(resolved.binary, binary);
|
|
66
69
|
});
|