ttsc 0.8.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -68
- package/cmd/ttsc/api_compile.go +109 -115
- package/cmd/ttsc/api_transform.go +54 -51
- package/cmd/ttsc/main.go +103 -121
- package/driver/program.go +30 -2
- package/driver/rewrite.go +126 -9
- package/go.mod +2 -0
- package/lib/TtscCompiler.js +28 -46
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/buildNativeCompiler.js +8 -38
- package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
- package/lib/compiler/internal/compileProjectInMemory.js +36 -63
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/project/readProjectConfig.js +108 -60
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/project/resolveProjectConfig.js +15 -45
- package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
- package/lib/compiler/internal/resolveBinary.js +10 -40
- package/lib/compiler/internal/resolveBinary.js.map +1 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.js +21 -48
- package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
- package/lib/compiler/internal/resolveTsgo.js +14 -44
- package/lib/compiler/internal/resolveTsgo.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +7 -0
- package/lib/compiler/internal/runBuild.js +47 -66
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/runSingleFileEmit.js +18 -48
- package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +41 -75
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/launcher/internal/getCompilerVersionText.js +7 -37
- package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
- package/lib/launcher/internal/prepareExecution.js +48 -69
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/runTtsc.js +53 -64
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtsx.js +328 -57
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.d.ts +8 -0
- package/lib/plugin/internal/buildSourcePlugin.js +287 -139
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.d.ts +2 -0
- package/lib/plugin/internal/loadProjectPlugins.js +197 -9
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscCompilerContext.d.ts +16 -3
- package/lib/structures/ITtscCompilerResult.d.ts +4 -0
- package/lib/structures/ITtscCompilerTransformation.d.ts +2 -0
- package/lib/structures/ITtscPluginFactoryContext.d.ts +6 -3
- package/lib/structures/ITtscProjectPluginConfig.d.ts +8 -7
- package/lib/structures/internal/ITtscParsedProjectConfig.d.ts +2 -0
- package/lib/structures/internal/ITtscProjectLocatorOptions.d.ts +2 -0
- package/lib/structures/internal/TtscCommonOptions.d.ts +2 -0
- package/package.json +9 -14
- package/shim/diagnosticwriter/lint.go +8 -0
- package/shim/printer/go.mod +5 -0
- package/shim/printer/shim.go +23 -0
- package/src/TtscCompiler.ts +20 -5
- package/src/compiler/internal/buildNativeCompiler.ts +2 -2
- package/src/compiler/internal/compileProjectInMemory.ts +21 -16
- package/src/compiler/internal/project/readProjectConfig.ts +134 -35
- package/src/compiler/internal/project/resolveProjectConfig.ts +2 -2
- package/src/compiler/internal/resolveBinary.ts +2 -2
- package/src/compiler/internal/resolveEmittedJavaScript.ts +11 -7
- package/src/compiler/internal/resolveTsgo.ts +2 -2
- package/src/compiler/internal/runBuild.ts +34 -21
- package/src/compiler/internal/runSingleFileEmit.ts +3 -3
- package/src/compiler/internal/transformProjectInMemory.ts +37 -37
- package/src/launcher/internal/getCompilerVersionText.ts +2 -2
- package/src/launcher/internal/prepareExecution.ts +14 -4
- package/src/launcher/internal/runTtsc.ts +30 -4
- package/src/launcher/internal/runTtsx.ts +377 -26
- package/src/plugin/internal/buildSourcePlugin.ts +321 -50
- package/src/plugin/internal/loadProjectPlugins.ts +266 -10
- package/src/structures/ITtscCompilerContext.ts +17 -3
- package/src/structures/ITtscCompilerResult.ts +5 -0
- package/src/structures/ITtscCompilerTransformation.ts +3 -0
- package/src/structures/ITtscPluginFactoryContext.ts +6 -3
- package/src/structures/ITtscProjectPluginConfig.ts +8 -7
- package/src/structures/internal/ITtscParsedProjectConfig.ts +2 -0
- package/src/structures/internal/ITtscProjectLocatorOptions.ts +2 -0
- package/src/structures/internal/TtscCommonOptions.ts +2 -0
- package/utility/host.go +622 -20
- package/cmd/ttsc/main_test.go +0 -288
- package/test/compiler.test.ts +0 -422
- package/test/platform.test.ts +0 -12
- package/test/project.test.ts +0 -197
- package/test/source-build.test.ts +0 -45
- package/test/tsgo.test.ts +0 -69
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
import fs from "node:fs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
|
-
import
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
6
7
|
|
|
7
8
|
const GO_MOD_SEARCH_MAX_DEPTH = 3;
|
|
9
|
+
const TTSC_GO_MODULE_PATH = "github.com/samchon/ttsc/packages/ttsc";
|
|
10
|
+
const TSGO_GO_MODULE_PATH = "github.com/microsoft/typescript-go";
|
|
8
11
|
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
".git",
|
|
12
|
-
"dist",
|
|
13
|
-
"build",
|
|
14
|
-
"vendor",
|
|
15
|
-
"lib",
|
|
16
|
-
]);
|
|
12
|
+
const PRUNE_DIRS = new Set(["node_modules", ".git", ".ttsc"]);
|
|
13
|
+
const GENERATED_WORKSPACE_FILES = new Set(["go.work", "go.work.sum"]);
|
|
17
14
|
|
|
18
15
|
/** Build one Go source plugin into a cached executable. */
|
|
19
16
|
export function buildSourcePlugin(opts: {
|
|
@@ -28,9 +25,14 @@ export function buildSourcePlugin(opts: {
|
|
|
28
25
|
tsgoVersion: string;
|
|
29
26
|
}): string {
|
|
30
27
|
const { dir, entry, source } = resolveSourceBuildTarget(opts);
|
|
28
|
+
const overlayDirs = opts.overlayDirs ?? findTtscOverlayDirs();
|
|
29
|
+
const goBinary = resolveGoCompiler();
|
|
30
|
+
ensureExecutableGoToolchain(goBinary);
|
|
31
31
|
const key = computeCacheKey({
|
|
32
32
|
dir,
|
|
33
33
|
entry,
|
|
34
|
+
goBinary,
|
|
35
|
+
overlayDirs,
|
|
34
36
|
ttscVersion: opts.ttscVersion,
|
|
35
37
|
tsgoVersion: opts.tsgoVersion,
|
|
36
38
|
});
|
|
@@ -49,27 +51,46 @@ export function buildSourcePlugin(opts: {
|
|
|
49
51
|
);
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
const scratchDir =
|
|
53
|
-
|
|
54
|
-
`scratch-${key}-${process.pid}-${Date.now()}`,
|
|
54
|
+
const scratchDir = fs.mkdtempSync(
|
|
55
|
+
path.join(os.tmpdir(), `ttsc-plugin-${key}-`),
|
|
55
56
|
);
|
|
56
57
|
try {
|
|
57
58
|
materializeScratchDir(dir, scratchDir);
|
|
58
|
-
writeGoWork(scratchDir,
|
|
59
|
+
writeGoWork(scratchDir, overlayDirs, goBinary, opts.pluginName);
|
|
59
60
|
const scratchBinaryName =
|
|
60
61
|
process.platform === "win32" ? ".ttsc-plugin.exe" : ".ttsc-plugin";
|
|
61
|
-
runGoBuild(scratchDir, entry, scratchBinaryName, opts.pluginName);
|
|
62
|
+
runGoBuild(scratchDir, entry, scratchBinaryName, opts.pluginName, goBinary);
|
|
62
63
|
const builtBinary = path.join(scratchDir, scratchBinaryName);
|
|
63
|
-
|
|
64
|
-
if (process.platform !== "win32") {
|
|
65
|
-
fs.chmodSync(binaryPath, 0o755);
|
|
66
|
-
}
|
|
64
|
+
publishBuiltBinary(builtBinary, binaryPath);
|
|
67
65
|
return binaryPath;
|
|
68
66
|
} finally {
|
|
69
67
|
fs.rmSync(scratchDir, { recursive: true, force: true });
|
|
70
68
|
}
|
|
71
69
|
}
|
|
72
70
|
|
|
71
|
+
function publishBuiltBinary(builtBinary: string, binaryPath: string): void {
|
|
72
|
+
const pending = `${binaryPath}.${process.pid}.${Date.now()}-${Math.random()
|
|
73
|
+
.toString(16)
|
|
74
|
+
.slice(2)}.tmp`;
|
|
75
|
+
fs.copyFileSync(builtBinary, pending);
|
|
76
|
+
if (process.platform !== "win32") {
|
|
77
|
+
fs.chmodSync(pending, 0o755);
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
fs.renameSync(pending, binaryPath);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
fs.rmSync(pending, { force: true });
|
|
83
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
84
|
+
if (
|
|
85
|
+
(code === "EEXIST" || code === "EPERM" || code === "EACCES") &&
|
|
86
|
+
fs.existsSync(binaryPath)
|
|
87
|
+
) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
73
94
|
function resolveSourceBuildTarget(opts: {
|
|
74
95
|
source: string;
|
|
75
96
|
pluginName: string;
|
|
@@ -136,52 +157,221 @@ function materializeScratchDir(source: string, scratch: string): void {
|
|
|
136
157
|
recursive: true,
|
|
137
158
|
filter: (src) => {
|
|
138
159
|
const base = path.basename(src);
|
|
139
|
-
if (
|
|
140
|
-
if (base
|
|
160
|
+
if (shouldPruneDirectory(base)) return false;
|
|
161
|
+
if (shouldOmitSourceFile(base)) return false;
|
|
141
162
|
return true;
|
|
142
163
|
},
|
|
143
164
|
});
|
|
144
165
|
}
|
|
145
166
|
|
|
146
|
-
function writeGoWork(
|
|
167
|
+
function writeGoWork(
|
|
168
|
+
scratchDir: string,
|
|
169
|
+
useDirs: readonly string[],
|
|
170
|
+
goBinary: string,
|
|
171
|
+
pluginName: string,
|
|
172
|
+
): void {
|
|
173
|
+
const goModReader = createGoModReader(goBinary, pluginName);
|
|
174
|
+
validateSourceReplacements(
|
|
175
|
+
scratchDir,
|
|
176
|
+
useDirs,
|
|
177
|
+
goModReader,
|
|
178
|
+
pluginName,
|
|
179
|
+
);
|
|
147
180
|
const useLines = ["\t."];
|
|
148
181
|
for (const dir of useDirs) {
|
|
149
182
|
useLines.push(`\t${dir.replace(/\\/g, "/")}`);
|
|
150
183
|
}
|
|
151
|
-
const replaceLines = sourceBuildWorkspaceReplacements(
|
|
184
|
+
const replaceLines = sourceBuildWorkspaceReplacements(
|
|
185
|
+
useDirs,
|
|
186
|
+
goModReader,
|
|
187
|
+
);
|
|
152
188
|
const replaceBlock =
|
|
153
189
|
replaceLines.length === 0 ? "" : `\n\n${replaceLines.join("\n")}\n`;
|
|
154
190
|
const goWork = `go 1.26\n\nuse (\n${useLines.join("\n")}\n)${replaceBlock}`;
|
|
155
191
|
fs.writeFileSync(path.join(scratchDir, "go.work"), goWork, "utf8");
|
|
156
192
|
}
|
|
157
193
|
|
|
194
|
+
function validateSourceReplacements(
|
|
195
|
+
scratchDir: string,
|
|
196
|
+
useDirs: readonly string[],
|
|
197
|
+
goModReader: GoModReader,
|
|
198
|
+
pluginName: string,
|
|
199
|
+
): void {
|
|
200
|
+
const sourceInfo = goModReader.read(scratchDir);
|
|
201
|
+
if (sourceInfo.modulePath === TTSC_GO_MODULE_PATH) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const sourceReplacements = sourceInfo.replacements;
|
|
205
|
+
if (sourceReplacements.length === 0) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const overlayModules = collectOverlayModulePaths(useDirs, goModReader);
|
|
209
|
+
for (const replacement of sourceReplacements) {
|
|
210
|
+
if (
|
|
211
|
+
isTtscManagedModulePath(replacement.modulePath) ||
|
|
212
|
+
overlayModules.has(replacement.modulePath)
|
|
213
|
+
) {
|
|
214
|
+
throw new Error(
|
|
215
|
+
`ttsc: plugin "${pluginName}" go.mod replaces ttsc-managed module ` +
|
|
216
|
+
`${JSON.stringify(replacement.modulePath)}. Remove this replace directive; ` +
|
|
217
|
+
`ttsc supplies its own compiler and shim modules while building source plugins.`,
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
158
223
|
function sourceBuildWorkspaceReplacements(
|
|
159
224
|
useDirs: readonly string[],
|
|
225
|
+
goModReader: GoModReader,
|
|
160
226
|
): string[] {
|
|
161
|
-
const ttscRoot = useDirs.find(
|
|
162
|
-
|
|
227
|
+
const ttscRoot = useDirs.find(
|
|
228
|
+
(dir) => goModReader.read(dir).modulePath === TTSC_GO_MODULE_PATH,
|
|
163
229
|
);
|
|
164
230
|
if (!ttscRoot) {
|
|
165
231
|
return [];
|
|
166
232
|
}
|
|
167
233
|
return [
|
|
168
|
-
`replace
|
|
234
|
+
`replace ${TTSC_GO_MODULE_PATH} v0.0.0 => ${ttscRoot.replace(/\\/g, "/")}`,
|
|
169
235
|
];
|
|
170
236
|
}
|
|
171
237
|
|
|
172
|
-
|
|
238
|
+
interface GoModReplacement {
|
|
239
|
+
readonly modulePath: string;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
interface GoModInfo {
|
|
243
|
+
readonly modulePath: string | null;
|
|
244
|
+
readonly replacements: readonly GoModReplacement[];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
interface GoModReader {
|
|
248
|
+
read(dir: string): GoModInfo;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
interface GoModJson {
|
|
252
|
+
readonly Module?: {
|
|
253
|
+
readonly Path?: string;
|
|
254
|
+
};
|
|
255
|
+
readonly Require?: readonly {
|
|
256
|
+
readonly Path?: string;
|
|
257
|
+
readonly Version?: string;
|
|
258
|
+
}[];
|
|
259
|
+
readonly Replace?: readonly {
|
|
260
|
+
readonly Old?: {
|
|
261
|
+
readonly Path?: string;
|
|
262
|
+
readonly Version?: string;
|
|
263
|
+
};
|
|
264
|
+
readonly New?: {
|
|
265
|
+
readonly Path?: string;
|
|
266
|
+
readonly Version?: string;
|
|
267
|
+
};
|
|
268
|
+
}[];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function createGoModReader(goBinary: string, pluginName: string): GoModReader {
|
|
272
|
+
const cache = new Map<string, GoModInfo>();
|
|
273
|
+
return {
|
|
274
|
+
read(dir) {
|
|
275
|
+
const resolved = path.resolve(dir);
|
|
276
|
+
const cached = cache.get(resolved);
|
|
277
|
+
if (cached !== undefined) {
|
|
278
|
+
return cached;
|
|
279
|
+
}
|
|
280
|
+
const info = readGoModInfo(resolved, goBinary, pluginName);
|
|
281
|
+
cache.set(resolved, info);
|
|
282
|
+
return info;
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function readGoModInfo(
|
|
288
|
+
dir: string,
|
|
289
|
+
goBinary: string,
|
|
290
|
+
pluginName: string,
|
|
291
|
+
): GoModInfo {
|
|
292
|
+
if (!fs.existsSync(path.join(dir, "go.mod"))) {
|
|
293
|
+
return emptyGoModInfo();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const result = spawnSync(goBinary, ["mod", "edit", "-json"], {
|
|
297
|
+
cwd: dir,
|
|
298
|
+
encoding: "utf8",
|
|
299
|
+
env: goBuildEnv(goBinary),
|
|
300
|
+
maxBuffer: 1024 * 1024 * 16,
|
|
301
|
+
windowsHide: true,
|
|
302
|
+
});
|
|
303
|
+
if (result.error) {
|
|
304
|
+
if ((result.error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
305
|
+
throw new Error(goToolchainNotFoundMessage(pluginName));
|
|
306
|
+
}
|
|
307
|
+
throw new Error(
|
|
308
|
+
`ttsc: reading go.mod for plugin "${pluginName}" failed to spawn ${goBinary}: ${result.error.message}`,
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
if (result.status !== 0) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
`ttsc: reading go.mod for plugin "${pluginName}" failed:\n${result.stderr || result.stdout}`,
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
let json: GoModJson;
|
|
173
318
|
try {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
319
|
+
json = JSON.parse(result.stdout) as GoModJson;
|
|
320
|
+
} catch (error) {
|
|
321
|
+
throw new Error(
|
|
322
|
+
`ttsc: reading go.mod for plugin "${pluginName}" returned invalid JSON: ${
|
|
323
|
+
error instanceof Error ? error.message : String(error)
|
|
324
|
+
}`,
|
|
177
325
|
);
|
|
178
|
-
} catch {
|
|
179
|
-
return false;
|
|
180
326
|
}
|
|
327
|
+
|
|
328
|
+
return {
|
|
329
|
+
modulePath: json.Module?.Path ?? null,
|
|
330
|
+
replacements: (json.Replace ?? [])
|
|
331
|
+
.map(jsonReplacementToGoModReplacement)
|
|
332
|
+
.filter((replacement) => replacement !== null),
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function emptyGoModInfo(): GoModInfo {
|
|
337
|
+
return {
|
|
338
|
+
modulePath: null,
|
|
339
|
+
replacements: [],
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function jsonReplacementToGoModReplacement(
|
|
344
|
+
replacement: NonNullable<GoModJson["Replace"]>[number],
|
|
345
|
+
): GoModReplacement | null {
|
|
346
|
+
const modulePath = replacement.Old?.Path;
|
|
347
|
+
if (modulePath === undefined) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
modulePath,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function collectOverlayModulePaths(
|
|
356
|
+
dirs: readonly string[],
|
|
357
|
+
goModReader: GoModReader,
|
|
358
|
+
): Set<string> {
|
|
359
|
+
const out = new Set<string>();
|
|
360
|
+
for (const dir of dirs) {
|
|
361
|
+
const modulePath = goModReader.read(dir).modulePath;
|
|
362
|
+
if (modulePath !== null) {
|
|
363
|
+
out.add(modulePath);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return out;
|
|
181
367
|
}
|
|
182
368
|
|
|
183
|
-
function
|
|
184
|
-
return
|
|
369
|
+
function isTtscManagedModulePath(modulePath: string): boolean {
|
|
370
|
+
return (
|
|
371
|
+
modulePath === TTSC_GO_MODULE_PATH ||
|
|
372
|
+
modulePath === TSGO_GO_MODULE_PATH ||
|
|
373
|
+
modulePath.startsWith("github.com/microsoft/typescript-go/shim/")
|
|
374
|
+
);
|
|
185
375
|
}
|
|
186
376
|
|
|
187
377
|
function runGoBuild(
|
|
@@ -189,8 +379,8 @@ function runGoBuild(
|
|
|
189
379
|
entry: string,
|
|
190
380
|
binaryName: string,
|
|
191
381
|
pluginName: string,
|
|
382
|
+
goBinary: string,
|
|
192
383
|
): void {
|
|
193
|
-
const goBinary = resolveGoCompiler();
|
|
194
384
|
ensureExecutableGoToolchain(goBinary);
|
|
195
385
|
const result = spawnSync(goBinary, ["build", "-o", binaryName, entry], {
|
|
196
386
|
cwd,
|
|
@@ -201,11 +391,7 @@ function runGoBuild(
|
|
|
201
391
|
});
|
|
202
392
|
if (result.error) {
|
|
203
393
|
if ((result.error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
204
|
-
throw new Error(
|
|
205
|
-
`ttsc: building plugin "${pluginName}" failed because the Go toolchain was not found. ` +
|
|
206
|
-
`Reinstall ttsc with optional dependencies so the bundled Go compiler is present, ` +
|
|
207
|
-
`or set TTSC_GO_BINARY to an absolute path.`,
|
|
208
|
-
);
|
|
394
|
+
throw new Error(goToolchainNotFoundMessage(pluginName));
|
|
209
395
|
}
|
|
210
396
|
throw new Error(
|
|
211
397
|
`ttsc: building plugin "${pluginName}" failed to spawn ${goBinary}: ${result.error.message}`,
|
|
@@ -218,6 +404,14 @@ function runGoBuild(
|
|
|
218
404
|
}
|
|
219
405
|
}
|
|
220
406
|
|
|
407
|
+
function goToolchainNotFoundMessage(pluginName: string): string {
|
|
408
|
+
return (
|
|
409
|
+
`ttsc: building plugin "${pluginName}" failed because the Go toolchain was not found. ` +
|
|
410
|
+
`Reinstall ttsc with optional dependencies so the bundled Go compiler is present, ` +
|
|
411
|
+
`or set TTSC_GO_BINARY to an absolute path.`
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
221
415
|
function goBuildEnv(goBinary: string): NodeJS.ProcessEnv {
|
|
222
416
|
const env = { ...process.env };
|
|
223
417
|
const goRoot = inferGoRoot(goBinary);
|
|
@@ -305,7 +499,7 @@ function walkForGoMod(dir: string, out: string[]): void {
|
|
|
305
499
|
}
|
|
306
500
|
for (const entry of entries) {
|
|
307
501
|
if (!entry.isDirectory()) continue;
|
|
308
|
-
if (
|
|
502
|
+
if (shouldPruneDirectory(entry.name)) continue;
|
|
309
503
|
walkForGoMod(path.join(dir, entry.name), out);
|
|
310
504
|
}
|
|
311
505
|
}
|
|
@@ -315,7 +509,7 @@ function resolvePluginCacheRoot(
|
|
|
315
509
|
cacheDir?: string,
|
|
316
510
|
): string {
|
|
317
511
|
if (cacheDir) {
|
|
318
|
-
return path.resolve(cacheDir, "plugins");
|
|
512
|
+
return path.resolve(projectRoot, cacheDir, "plugins");
|
|
319
513
|
}
|
|
320
514
|
if (process.env.TTSC_CACHE_DIR) {
|
|
321
515
|
return path.resolve(process.env.TTSC_CACHE_DIR, "plugins");
|
|
@@ -387,9 +581,11 @@ function resolveGoCompiler(): string {
|
|
|
387
581
|
return "go";
|
|
388
582
|
}
|
|
389
583
|
|
|
390
|
-
function computeCacheKey(inputs: {
|
|
584
|
+
export function computeCacheKey(inputs: {
|
|
391
585
|
dir: string;
|
|
392
586
|
entry: string;
|
|
587
|
+
goBinary?: string;
|
|
588
|
+
overlayDirs?: readonly string[];
|
|
393
589
|
ttscVersion: string;
|
|
394
590
|
tsgoVersion: string;
|
|
395
591
|
}): string {
|
|
@@ -398,12 +594,28 @@ function computeCacheKey(inputs: {
|
|
|
398
594
|
hash.update(`tsgo=${inputs.tsgoVersion}\n`);
|
|
399
595
|
hash.update(`platform=${process.platform}/${process.arch}\n`);
|
|
400
596
|
hash.update(`entry=${inputs.entry}\n`);
|
|
401
|
-
|
|
402
|
-
|
|
597
|
+
if (inputs.goBinary !== undefined) {
|
|
598
|
+
hash.update(`go=${resolveGoCompilerIdentity(inputs.goBinary)}\n`);
|
|
599
|
+
}
|
|
600
|
+
hashSourceDirectory(hash, "plugin", inputs.dir);
|
|
601
|
+
for (const [index, dir] of [...(inputs.overlayDirs ?? [])].sort().entries()) {
|
|
602
|
+
hashSourceDirectory(hash, `overlay:${index}`, dir);
|
|
603
|
+
}
|
|
604
|
+
return hash.digest("hex").slice(0, 32);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function hashSourceDirectory(
|
|
608
|
+
hash: crypto.Hash,
|
|
609
|
+
label: string,
|
|
610
|
+
root: string,
|
|
611
|
+
): void {
|
|
612
|
+
hash.update(`dir=${label}\n`);
|
|
613
|
+
for (const file of collectSourceFiles(root)) {
|
|
614
|
+
const rel = path.relative(root, file).replace(/\\/g, "/");
|
|
403
615
|
hash.update(`f=${rel}\n`);
|
|
404
616
|
hash.update(fs.readFileSync(file));
|
|
617
|
+
hash.update("\n");
|
|
405
618
|
}
|
|
406
|
-
return hash.digest("hex").slice(0, 32);
|
|
407
619
|
}
|
|
408
620
|
|
|
409
621
|
function collectSourceFiles(root: string): string[] {
|
|
@@ -421,19 +633,78 @@ function walk(dir: string, out: string[]): void {
|
|
|
421
633
|
return;
|
|
422
634
|
}
|
|
423
635
|
for (const entry of entries) {
|
|
424
|
-
if (SKIP_DIRS.has(entry.name)) continue;
|
|
425
636
|
const full = path.join(dir, entry.name);
|
|
426
637
|
if (entry.isDirectory()) {
|
|
638
|
+
if (shouldPruneDirectory(entry.name)) continue;
|
|
427
639
|
walk(full, out);
|
|
428
640
|
continue;
|
|
429
641
|
}
|
|
430
642
|
if (!entry.isFile()) continue;
|
|
643
|
+
if (shouldOmitSourceFile(entry.name)) continue;
|
|
431
644
|
if (!isHashableFile(entry.name)) continue;
|
|
432
645
|
out.push(full);
|
|
433
646
|
}
|
|
434
647
|
}
|
|
435
648
|
|
|
649
|
+
function shouldPruneDirectory(name: string): boolean {
|
|
650
|
+
return PRUNE_DIRS.has(name);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
function shouldOmitSourceFile(name: string): boolean {
|
|
654
|
+
return GENERATED_WORKSPACE_FILES.has(name);
|
|
655
|
+
}
|
|
656
|
+
|
|
436
657
|
function isHashableFile(name: string): boolean {
|
|
437
|
-
|
|
438
|
-
|
|
658
|
+
return !name.endsWith("~");
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function resolveGoCompilerIdentity(goBinary: string): string {
|
|
662
|
+
const resolved = resolveExecutableIdentityPath(goBinary);
|
|
663
|
+
const version = spawnSync(goBinary, ["version"], {
|
|
664
|
+
encoding: "utf8",
|
|
665
|
+
windowsHide: true,
|
|
666
|
+
});
|
|
667
|
+
const versionText =
|
|
668
|
+
version.error !== undefined
|
|
669
|
+
? version.error.message
|
|
670
|
+
: `${version.status ?? 0}:${version.stdout}${version.stderr}`;
|
|
671
|
+
let statText = "";
|
|
672
|
+
try {
|
|
673
|
+
const stat = fs.statSync(resolved);
|
|
674
|
+
statText = `${stat.size}:${stat.mtimeMs}`;
|
|
675
|
+
} catch {
|
|
676
|
+
statText = "missing";
|
|
677
|
+
}
|
|
678
|
+
return `${goBinary}:${resolved}:${statText}:${versionText}`;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function resolveExecutableIdentityPath(binary: string): string {
|
|
682
|
+
if (path.isAbsolute(binary)) {
|
|
683
|
+
return resolveRealPath(binary);
|
|
684
|
+
}
|
|
685
|
+
if (binary.includes(path.sep)) {
|
|
686
|
+
return resolveRealPath(path.resolve(binary));
|
|
687
|
+
}
|
|
688
|
+
for (const dir of (process.env.PATH ?? "").split(path.delimiter)) {
|
|
689
|
+
if (dir.length === 0) continue;
|
|
690
|
+
const candidate = path.join(dir, binary);
|
|
691
|
+
if (fs.existsSync(candidate)) {
|
|
692
|
+
return resolveRealPath(candidate);
|
|
693
|
+
}
|
|
694
|
+
if (process.platform === "win32") {
|
|
695
|
+
const executable = `${candidate}.exe`;
|
|
696
|
+
if (fs.existsSync(executable)) {
|
|
697
|
+
return resolveRealPath(executable);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return binary;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
function resolveRealPath(location: string): string {
|
|
705
|
+
try {
|
|
706
|
+
return fs.realpathSync(location);
|
|
707
|
+
} catch {
|
|
708
|
+
return location;
|
|
709
|
+
}
|
|
439
710
|
}
|