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.
Files changed (88) hide show
  1. package/README.md +39 -68
  2. package/cmd/ttsc/api_compile.go +109 -115
  3. package/cmd/ttsc/api_transform.go +54 -51
  4. package/cmd/ttsc/main.go +103 -121
  5. package/driver/program.go +30 -2
  6. package/driver/rewrite.go +126 -9
  7. package/go.mod +2 -0
  8. package/lib/TtscCompiler.js +28 -46
  9. package/lib/TtscCompiler.js.map +1 -1
  10. package/lib/compiler/internal/buildNativeCompiler.js +8 -38
  11. package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
  12. package/lib/compiler/internal/compileProjectInMemory.js +36 -63
  13. package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
  14. package/lib/compiler/internal/project/readProjectConfig.js +108 -60
  15. package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
  16. package/lib/compiler/internal/project/resolveProjectConfig.js +15 -45
  17. package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
  18. package/lib/compiler/internal/resolveBinary.js +10 -40
  19. package/lib/compiler/internal/resolveBinary.js.map +1 -1
  20. package/lib/compiler/internal/resolveEmittedJavaScript.js +21 -48
  21. package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
  22. package/lib/compiler/internal/resolveTsgo.js +14 -44
  23. package/lib/compiler/internal/resolveTsgo.js.map +1 -1
  24. package/lib/compiler/internal/runBuild.d.ts +7 -0
  25. package/lib/compiler/internal/runBuild.js +47 -66
  26. package/lib/compiler/internal/runBuild.js.map +1 -1
  27. package/lib/compiler/internal/runSingleFileEmit.js +18 -48
  28. package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
  29. package/lib/compiler/internal/transformProjectInMemory.js +41 -75
  30. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  31. package/lib/launcher/internal/getCompilerVersionText.js +7 -37
  32. package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
  33. package/lib/launcher/internal/prepareExecution.js +48 -69
  34. package/lib/launcher/internal/prepareExecution.js.map +1 -1
  35. package/lib/launcher/internal/runTtsc.js +53 -64
  36. package/lib/launcher/internal/runTtsc.js.map +1 -1
  37. package/lib/launcher/internal/runTtsx.js +328 -57
  38. package/lib/launcher/internal/runTtsx.js.map +1 -1
  39. package/lib/plugin/internal/buildSourcePlugin.d.ts +8 -0
  40. package/lib/plugin/internal/buildSourcePlugin.js +287 -139
  41. package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
  42. package/lib/plugin/internal/loadProjectPlugins.d.ts +2 -0
  43. package/lib/plugin/internal/loadProjectPlugins.js +197 -9
  44. package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
  45. package/lib/structures/ITtscCompilerContext.d.ts +16 -3
  46. package/lib/structures/ITtscCompilerResult.d.ts +4 -0
  47. package/lib/structures/ITtscCompilerTransformation.d.ts +2 -0
  48. package/lib/structures/ITtscPluginFactoryContext.d.ts +6 -3
  49. package/lib/structures/ITtscProjectPluginConfig.d.ts +8 -7
  50. package/lib/structures/internal/ITtscParsedProjectConfig.d.ts +2 -0
  51. package/lib/structures/internal/ITtscProjectLocatorOptions.d.ts +2 -0
  52. package/lib/structures/internal/TtscCommonOptions.d.ts +2 -0
  53. package/package.json +9 -14
  54. package/shim/diagnosticwriter/lint.go +8 -0
  55. package/shim/printer/go.mod +5 -0
  56. package/shim/printer/shim.go +23 -0
  57. package/src/TtscCompiler.ts +20 -5
  58. package/src/compiler/internal/buildNativeCompiler.ts +2 -2
  59. package/src/compiler/internal/compileProjectInMemory.ts +21 -16
  60. package/src/compiler/internal/project/readProjectConfig.ts +134 -35
  61. package/src/compiler/internal/project/resolveProjectConfig.ts +2 -2
  62. package/src/compiler/internal/resolveBinary.ts +2 -2
  63. package/src/compiler/internal/resolveEmittedJavaScript.ts +11 -7
  64. package/src/compiler/internal/resolveTsgo.ts +2 -2
  65. package/src/compiler/internal/runBuild.ts +34 -21
  66. package/src/compiler/internal/runSingleFileEmit.ts +3 -3
  67. package/src/compiler/internal/transformProjectInMemory.ts +37 -37
  68. package/src/launcher/internal/getCompilerVersionText.ts +2 -2
  69. package/src/launcher/internal/prepareExecution.ts +14 -4
  70. package/src/launcher/internal/runTtsc.ts +30 -4
  71. package/src/launcher/internal/runTtsx.ts +377 -26
  72. package/src/plugin/internal/buildSourcePlugin.ts +321 -50
  73. package/src/plugin/internal/loadProjectPlugins.ts +266 -10
  74. package/src/structures/ITtscCompilerContext.ts +17 -3
  75. package/src/structures/ITtscCompilerResult.ts +5 -0
  76. package/src/structures/ITtscCompilerTransformation.ts +3 -0
  77. package/src/structures/ITtscPluginFactoryContext.ts +6 -3
  78. package/src/structures/ITtscProjectPluginConfig.ts +8 -7
  79. package/src/structures/internal/ITtscParsedProjectConfig.ts +2 -0
  80. package/src/structures/internal/ITtscProjectLocatorOptions.ts +2 -0
  81. package/src/structures/internal/TtscCommonOptions.ts +2 -0
  82. package/utility/host.go +622 -20
  83. package/cmd/ttsc/main_test.go +0 -288
  84. package/test/compiler.test.ts +0 -422
  85. package/test/platform.test.ts +0 -12
  86. package/test/project.test.ts +0 -197
  87. package/test/source-build.test.ts +0 -45
  88. package/test/tsgo.test.ts +0 -69
@@ -1,101 +1,92 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
35
5
  Object.defineProperty(exports, "__esModule", { value: true });
36
6
  exports.buildSourcePlugin = buildSourcePlugin;
7
+ exports.computeCacheKey = computeCacheKey;
37
8
  const node_child_process_1 = require("node:child_process");
38
- const crypto = __importStar(require("node:crypto"));
39
- const fs = __importStar(require("node:fs"));
9
+ const node_crypto_1 = __importDefault(require("node:crypto"));
10
+ const node_fs_1 = __importDefault(require("node:fs"));
40
11
  const node_module_1 = require("node:module");
41
- const path = __importStar(require("node:path"));
12
+ const node_os_1 = __importDefault(require("node:os"));
13
+ const node_path_1 = __importDefault(require("node:path"));
42
14
  const GO_MOD_SEARCH_MAX_DEPTH = 3;
43
- const SKIP_DIRS = new Set([
44
- "node_modules",
45
- ".git",
46
- "dist",
47
- "build",
48
- "vendor",
49
- "lib",
50
- ]);
15
+ const TTSC_GO_MODULE_PATH = "github.com/samchon/ttsc/packages/ttsc";
16
+ const TSGO_GO_MODULE_PATH = "github.com/microsoft/typescript-go";
17
+ const PRUNE_DIRS = new Set(["node_modules", ".git", ".ttsc"]);
18
+ const GENERATED_WORKSPACE_FILES = new Set(["go.work", "go.work.sum"]);
51
19
  /** Build one Go source plugin into a cached executable. */
52
20
  function buildSourcePlugin(opts) {
53
21
  const { dir, entry, source } = resolveSourceBuildTarget(opts);
22
+ const overlayDirs = opts.overlayDirs ?? findTtscOverlayDirs();
23
+ const goBinary = resolveGoCompiler();
24
+ ensureExecutableGoToolchain(goBinary);
54
25
  const key = computeCacheKey({
55
26
  dir,
56
27
  entry,
28
+ goBinary,
29
+ overlayDirs,
57
30
  ttscVersion: opts.ttscVersion,
58
31
  tsgoVersion: opts.tsgoVersion,
59
32
  });
60
33
  const root = resolvePluginCacheRoot(opts.baseDir, opts.cacheDir);
61
- const cacheDir = path.join(root, key);
34
+ const cacheDir = node_path_1.default.join(root, key);
62
35
  const binaryName = process.platform === "win32" ? "plugin.exe" : "plugin";
63
- const binaryPath = path.join(cacheDir, binaryName);
64
- if (fs.existsSync(binaryPath)) {
36
+ const binaryPath = node_path_1.default.join(cacheDir, binaryName);
37
+ if (node_fs_1.default.existsSync(binaryPath)) {
65
38
  return binaryPath;
66
39
  }
67
- fs.mkdirSync(cacheDir, { recursive: true });
40
+ node_fs_1.default.mkdirSync(cacheDir, { recursive: true });
68
41
  const label = opts.label ?? "source plugin";
69
42
  if (opts.quiet !== true) {
70
43
  process.stderr.write(`ttsc: building ${label} "${opts.pluginName}" from ${source} (this runs once per cache key)\n`);
71
44
  }
72
- const scratchDir = path.join(root, `scratch-${key}-${process.pid}-${Date.now()}`);
45
+ const scratchDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), `ttsc-plugin-${key}-`));
73
46
  try {
74
47
  materializeScratchDir(dir, scratchDir);
75
- writeGoWork(scratchDir, opts.overlayDirs ?? findTtscOverlayDirs());
48
+ writeGoWork(scratchDir, overlayDirs, goBinary, opts.pluginName);
76
49
  const scratchBinaryName = process.platform === "win32" ? ".ttsc-plugin.exe" : ".ttsc-plugin";
77
- runGoBuild(scratchDir, entry, scratchBinaryName, opts.pluginName);
78
- const builtBinary = path.join(scratchDir, scratchBinaryName);
79
- fs.renameSync(builtBinary, binaryPath);
80
- if (process.platform !== "win32") {
81
- fs.chmodSync(binaryPath, 0o755);
82
- }
50
+ runGoBuild(scratchDir, entry, scratchBinaryName, opts.pluginName, goBinary);
51
+ const builtBinary = node_path_1.default.join(scratchDir, scratchBinaryName);
52
+ publishBuiltBinary(builtBinary, binaryPath);
83
53
  return binaryPath;
84
54
  }
85
55
  finally {
86
- fs.rmSync(scratchDir, { recursive: true, force: true });
56
+ node_fs_1.default.rmSync(scratchDir, { recursive: true, force: true });
57
+ }
58
+ }
59
+ function publishBuiltBinary(builtBinary, binaryPath) {
60
+ const pending = `${binaryPath}.${process.pid}.${Date.now()}-${Math.random()
61
+ .toString(16)
62
+ .slice(2)}.tmp`;
63
+ node_fs_1.default.copyFileSync(builtBinary, pending);
64
+ if (process.platform !== "win32") {
65
+ node_fs_1.default.chmodSync(pending, 0o755);
66
+ }
67
+ try {
68
+ node_fs_1.default.renameSync(pending, binaryPath);
69
+ }
70
+ catch (error) {
71
+ node_fs_1.default.rmSync(pending, { force: true });
72
+ const code = error.code;
73
+ if ((code === "EEXIST" || code === "EPERM" || code === "EACCES") &&
74
+ node_fs_1.default.existsSync(binaryPath)) {
75
+ return;
76
+ }
77
+ throw error;
87
78
  }
88
79
  }
89
80
  function resolveSourceBuildTarget(opts) {
90
- const source = path.isAbsolute(opts.source)
81
+ const source = node_path_1.default.isAbsolute(opts.source)
91
82
  ? opts.source
92
- : path.resolve(opts.baseDir, opts.source);
93
- if (!fs.existsSync(source)) {
83
+ : node_path_1.default.resolve(opts.baseDir, opts.source);
84
+ if (!node_fs_1.default.existsSync(source)) {
94
85
  throw new Error(`ttsc: plugin "${opts.pluginName}" source does not exist: ${source}`);
95
86
  }
96
- const stat = fs.statSync(source);
97
- const packageDir = stat.isFile() && path.basename(source) === "go.mod"
98
- ? path.dirname(source)
87
+ const stat = node_fs_1.default.statSync(source);
88
+ const packageDir = stat.isFile() && node_path_1.default.basename(source) === "go.mod"
89
+ ? node_path_1.default.dirname(source)
99
90
  : stat.isDirectory()
100
91
  ? source
101
92
  : null;
@@ -106,8 +97,8 @@ function resolveSourceBuildTarget(opts) {
106
97
  if (goMod === null) {
107
98
  throw new Error(`ttsc: plugin "${opts.pluginName}" source must be inside a Go module with go.mod within ${GO_MOD_SEARCH_MAX_DEPTH} parent directories: ${source}`);
108
99
  }
109
- const dir = path.dirname(goMod);
110
- const rel = path.relative(dir, packageDir).replace(/\\/g, "/");
100
+ const dir = node_path_1.default.dirname(goMod);
101
+ const rel = node_path_1.default.relative(dir, packageDir).replace(/\\/g, "/");
111
102
  return {
112
103
  dir,
113
104
  entry: rel === "" ? "." : `./${rel}`,
@@ -115,15 +106,15 @@ function resolveSourceBuildTarget(opts) {
115
106
  };
116
107
  }
117
108
  function findNearestGoMod(from, maxDepth) {
118
- let current = path.resolve(from);
109
+ let current = node_path_1.default.resolve(from);
119
110
  let depth = 0;
120
111
  while (true) {
121
- const candidate = path.join(current, "go.mod");
122
- if (fs.existsSync(candidate))
112
+ const candidate = node_path_1.default.join(current, "go.mod");
113
+ if (node_fs_1.default.existsSync(candidate))
123
114
  return candidate;
124
115
  if (depth >= maxDepth)
125
116
  return null;
126
- const parent = path.dirname(current);
117
+ const parent = node_path_1.default.dirname(current);
127
118
  if (parent === current)
128
119
  return null;
129
120
  current = parent;
@@ -131,52 +122,139 @@ function findNearestGoMod(from, maxDepth) {
131
122
  }
132
123
  }
133
124
  function materializeScratchDir(source, scratch) {
134
- fs.mkdirSync(scratch, { recursive: true });
135
- fs.cpSync(source, scratch, {
125
+ node_fs_1.default.mkdirSync(scratch, { recursive: true });
126
+ node_fs_1.default.cpSync(source, scratch, {
136
127
  recursive: true,
137
128
  filter: (src) => {
138
- const base = path.basename(src);
139
- if (SKIP_DIRS.has(base))
129
+ const base = node_path_1.default.basename(src);
130
+ if (shouldPruneDirectory(base))
140
131
  return false;
141
- if (base === "go.work" || base === "go.work.sum")
132
+ if (shouldOmitSourceFile(base))
142
133
  return false;
143
134
  return true;
144
135
  },
145
136
  });
146
137
  }
147
- function writeGoWork(scratchDir, useDirs) {
138
+ function writeGoWork(scratchDir, useDirs, goBinary, pluginName) {
139
+ const goModReader = createGoModReader(goBinary, pluginName);
140
+ validateSourceReplacements(scratchDir, useDirs, goModReader, pluginName);
148
141
  const useLines = ["\t."];
149
142
  for (const dir of useDirs) {
150
143
  useLines.push(`\t${dir.replace(/\\/g, "/")}`);
151
144
  }
152
- const replaceLines = sourceBuildWorkspaceReplacements(useDirs);
145
+ const replaceLines = sourceBuildWorkspaceReplacements(useDirs, goModReader);
153
146
  const replaceBlock = replaceLines.length === 0 ? "" : `\n\n${replaceLines.join("\n")}\n`;
154
147
  const goWork = `go 1.26\n\nuse (\n${useLines.join("\n")}\n)${replaceBlock}`;
155
- fs.writeFileSync(path.join(scratchDir, "go.work"), goWork, "utf8");
148
+ node_fs_1.default.writeFileSync(node_path_1.default.join(scratchDir, "go.work"), goWork, "utf8");
149
+ }
150
+ function validateSourceReplacements(scratchDir, useDirs, goModReader, pluginName) {
151
+ const sourceInfo = goModReader.read(scratchDir);
152
+ if (sourceInfo.modulePath === TTSC_GO_MODULE_PATH) {
153
+ return;
154
+ }
155
+ const sourceReplacements = sourceInfo.replacements;
156
+ if (sourceReplacements.length === 0) {
157
+ return;
158
+ }
159
+ const overlayModules = collectOverlayModulePaths(useDirs, goModReader);
160
+ for (const replacement of sourceReplacements) {
161
+ if (isTtscManagedModulePath(replacement.modulePath) ||
162
+ overlayModules.has(replacement.modulePath)) {
163
+ throw new Error(`ttsc: plugin "${pluginName}" go.mod replaces ttsc-managed module ` +
164
+ `${JSON.stringify(replacement.modulePath)}. Remove this replace directive; ` +
165
+ `ttsc supplies its own compiler and shim modules while building source plugins.`);
166
+ }
167
+ }
156
168
  }
157
- function sourceBuildWorkspaceReplacements(useDirs) {
158
- const ttscRoot = useDirs.find((dir) => hasModulePath(dir, "github.com/samchon/ttsc/packages/ttsc"));
169
+ function sourceBuildWorkspaceReplacements(useDirs, goModReader) {
170
+ const ttscRoot = useDirs.find((dir) => goModReader.read(dir).modulePath === TTSC_GO_MODULE_PATH);
159
171
  if (!ttscRoot) {
160
172
  return [];
161
173
  }
162
174
  return [
163
- `replace github.com/samchon/ttsc/packages/ttsc v0.0.0 => ${ttscRoot.replace(/\\/g, "/")}`,
175
+ `replace ${TTSC_GO_MODULE_PATH} v0.0.0 => ${ttscRoot.replace(/\\/g, "/")}`,
164
176
  ];
165
177
  }
166
- function hasModulePath(dir, modulePath) {
178
+ function createGoModReader(goBinary, pluginName) {
179
+ const cache = new Map();
180
+ return {
181
+ read(dir) {
182
+ const resolved = node_path_1.default.resolve(dir);
183
+ const cached = cache.get(resolved);
184
+ if (cached !== undefined) {
185
+ return cached;
186
+ }
187
+ const info = readGoModInfo(resolved, goBinary, pluginName);
188
+ cache.set(resolved, info);
189
+ return info;
190
+ },
191
+ };
192
+ }
193
+ function readGoModInfo(dir, goBinary, pluginName) {
194
+ if (!node_fs_1.default.existsSync(node_path_1.default.join(dir, "go.mod"))) {
195
+ return emptyGoModInfo();
196
+ }
197
+ const result = (0, node_child_process_1.spawnSync)(goBinary, ["mod", "edit", "-json"], {
198
+ cwd: dir,
199
+ encoding: "utf8",
200
+ env: goBuildEnv(goBinary),
201
+ maxBuffer: 1024 * 1024 * 16,
202
+ windowsHide: true,
203
+ });
204
+ if (result.error) {
205
+ if (result.error.code === "ENOENT") {
206
+ throw new Error(goToolchainNotFoundMessage(pluginName));
207
+ }
208
+ throw new Error(`ttsc: reading go.mod for plugin "${pluginName}" failed to spawn ${goBinary}: ${result.error.message}`);
209
+ }
210
+ if (result.status !== 0) {
211
+ throw new Error(`ttsc: reading go.mod for plugin "${pluginName}" failed:\n${result.stderr || result.stdout}`);
212
+ }
213
+ let json;
167
214
  try {
168
- const goMod = fs.readFileSync(path.join(dir, "go.mod"), "utf8");
169
- return new RegExp(`^module\\s+${escapeRegExp(modulePath)}\\s*$`, "m").test(goMod);
215
+ json = JSON.parse(result.stdout);
170
216
  }
171
- catch {
172
- return false;
217
+ catch (error) {
218
+ throw new Error(`ttsc: reading go.mod for plugin "${pluginName}" returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
173
219
  }
220
+ return {
221
+ modulePath: json.Module?.Path ?? null,
222
+ replacements: (json.Replace ?? [])
223
+ .map(jsonReplacementToGoModReplacement)
224
+ .filter((replacement) => replacement !== null),
225
+ };
174
226
  }
175
- function escapeRegExp(value) {
176
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
227
+ function emptyGoModInfo() {
228
+ return {
229
+ modulePath: null,
230
+ replacements: [],
231
+ };
177
232
  }
178
- function runGoBuild(cwd, entry, binaryName, pluginName) {
179
- const goBinary = resolveGoCompiler();
233
+ function jsonReplacementToGoModReplacement(replacement) {
234
+ const modulePath = replacement.Old?.Path;
235
+ if (modulePath === undefined) {
236
+ return null;
237
+ }
238
+ return {
239
+ modulePath,
240
+ };
241
+ }
242
+ function collectOverlayModulePaths(dirs, goModReader) {
243
+ const out = new Set();
244
+ for (const dir of dirs) {
245
+ const modulePath = goModReader.read(dir).modulePath;
246
+ if (modulePath !== null) {
247
+ out.add(modulePath);
248
+ }
249
+ }
250
+ return out;
251
+ }
252
+ function isTtscManagedModulePath(modulePath) {
253
+ return (modulePath === TTSC_GO_MODULE_PATH ||
254
+ modulePath === TSGO_GO_MODULE_PATH ||
255
+ modulePath.startsWith("github.com/microsoft/typescript-go/shim/"));
256
+ }
257
+ function runGoBuild(cwd, entry, binaryName, pluginName, goBinary) {
180
258
  ensureExecutableGoToolchain(goBinary);
181
259
  const result = (0, node_child_process_1.spawnSync)(goBinary, ["build", "-o", binaryName, entry], {
182
260
  cwd,
@@ -187,9 +265,7 @@ function runGoBuild(cwd, entry, binaryName, pluginName) {
187
265
  });
188
266
  if (result.error) {
189
267
  if (result.error.code === "ENOENT") {
190
- throw new Error(`ttsc: building plugin "${pluginName}" failed because the Go toolchain was not found. ` +
191
- `Reinstall ttsc with optional dependencies so the bundled Go compiler is present, ` +
192
- `or set TTSC_GO_BINARY to an absolute path.`);
268
+ throw new Error(goToolchainNotFoundMessage(pluginName));
193
269
  }
194
270
  throw new Error(`ttsc: building plugin "${pluginName}" failed to spawn ${goBinary}: ${result.error.message}`);
195
271
  }
@@ -197,6 +273,11 @@ function runGoBuild(cwd, entry, binaryName, pluginName) {
197
273
  throw new Error(`ttsc: building plugin "${pluginName}" via "go build" failed:\n${result.stderr || result.stdout}`);
198
274
  }
199
275
  }
276
+ function goToolchainNotFoundMessage(pluginName) {
277
+ return (`ttsc: building plugin "${pluginName}" failed because the Go toolchain was not found. ` +
278
+ `Reinstall ttsc with optional dependencies so the bundled Go compiler is present, ` +
279
+ `or set TTSC_GO_BINARY to an absolute path.`);
280
+ }
200
281
  function goBuildEnv(goBinary) {
201
282
  const env = { ...process.env };
202
283
  const goRoot = inferGoRoot(goBinary);
@@ -206,32 +287,32 @@ function goBuildEnv(goBinary) {
206
287
  return env;
207
288
  }
208
289
  function inferGoRoot(goBinary) {
209
- if (!path.isAbsolute(goBinary))
290
+ if (!node_path_1.default.isAbsolute(goBinary))
210
291
  return null;
211
- const binDir = path.dirname(goBinary);
212
- if (path.basename(binDir) !== "bin")
292
+ const binDir = node_path_1.default.dirname(goBinary);
293
+ if (node_path_1.default.basename(binDir) !== "bin")
213
294
  return null;
214
- const goRoot = path.dirname(binDir);
215
- return fs.existsSync(path.join(goRoot, "src", "runtime")) ? goRoot : null;
295
+ const goRoot = node_path_1.default.dirname(binDir);
296
+ return node_fs_1.default.existsSync(node_path_1.default.join(goRoot, "src", "runtime")) ? goRoot : null;
216
297
  }
217
298
  function ensureExecutableGoToolchain(goBinary) {
218
299
  if (process.platform === "win32")
219
300
  return;
220
- if (!path.isAbsolute(goBinary) || !fs.existsSync(goBinary))
301
+ if (!node_path_1.default.isAbsolute(goBinary) || !node_fs_1.default.existsSync(goBinary))
221
302
  return;
222
303
  try {
223
- fs.chmodSync(goBinary, 0o755);
304
+ node_fs_1.default.chmodSync(goBinary, 0o755);
224
305
  const goRoot = inferGoRoot(goBinary);
225
306
  if (!goRoot)
226
307
  return;
227
- const gofmt = path.join(path.dirname(goBinary), "gofmt");
228
- if (fs.existsSync(gofmt))
229
- fs.chmodSync(gofmt, 0o755);
230
- const toolDir = path.join(goRoot, "pkg", "tool");
231
- if (!fs.existsSync(toolDir))
308
+ const gofmt = node_path_1.default.join(node_path_1.default.dirname(goBinary), "gofmt");
309
+ if (node_fs_1.default.existsSync(gofmt))
310
+ node_fs_1.default.chmodSync(gofmt, 0o755);
311
+ const toolDir = node_path_1.default.join(goRoot, "pkg", "tool");
312
+ if (!node_fs_1.default.existsSync(toolDir))
232
313
  return;
233
314
  for (const file of walkToolFiles(toolDir)) {
234
- fs.chmodSync(file, 0o755);
315
+ node_fs_1.default.chmodSync(file, 0o755);
235
316
  }
236
317
  }
237
318
  catch {
@@ -242,13 +323,13 @@ function walkToolFiles(dir) {
242
323
  const out = [];
243
324
  let entries;
244
325
  try {
245
- entries = fs.readdirSync(dir, { withFileTypes: true });
326
+ entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
246
327
  }
247
328
  catch {
248
329
  return out;
249
330
  }
250
331
  for (const entry of entries) {
251
- const file = path.join(dir, entry.name);
332
+ const file = node_path_1.default.join(dir, entry.name);
252
333
  if (entry.isDirectory()) {
253
334
  out.push(...walkToolFiles(file));
254
335
  }
@@ -259,13 +340,13 @@ function walkToolFiles(dir) {
259
340
  return out;
260
341
  }
261
342
  function findTtscOverlayDirs() {
262
- const ttscRoot = path.resolve(__dirname, "..", "..", "..");
343
+ const ttscRoot = node_path_1.default.resolve(__dirname, "..", "..", "..");
263
344
  const dirs = [];
264
- if (fs.existsSync(path.join(ttscRoot, "go.mod"))) {
345
+ if (node_fs_1.default.existsSync(node_path_1.default.join(ttscRoot, "go.mod"))) {
265
346
  dirs.push(ttscRoot);
266
347
  }
267
- const shimRoot = path.join(ttscRoot, "shim");
268
- if (fs.existsSync(shimRoot)) {
348
+ const shimRoot = node_path_1.default.join(ttscRoot, "shim");
349
+ if (node_fs_1.default.existsSync(shimRoot)) {
269
350
  walkForGoMod(shimRoot, dirs);
270
351
  }
271
352
  dirs.sort();
@@ -274,7 +355,7 @@ function findTtscOverlayDirs() {
274
355
  function walkForGoMod(dir, out) {
275
356
  let entries;
276
357
  try {
277
- entries = fs.readdirSync(dir, { withFileTypes: true });
358
+ entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
278
359
  }
279
360
  catch {
280
361
  return;
@@ -291,28 +372,28 @@ function walkForGoMod(dir, out) {
291
372
  for (const entry of entries) {
292
373
  if (!entry.isDirectory())
293
374
  continue;
294
- if (SKIP_DIRS.has(entry.name))
375
+ if (shouldPruneDirectory(entry.name))
295
376
  continue;
296
- walkForGoMod(path.join(dir, entry.name), out);
377
+ walkForGoMod(node_path_1.default.join(dir, entry.name), out);
297
378
  }
298
379
  }
299
380
  function resolvePluginCacheRoot(projectRoot, cacheDir) {
300
381
  if (cacheDir) {
301
- return path.resolve(cacheDir, "plugins");
382
+ return node_path_1.default.resolve(projectRoot, cacheDir, "plugins");
302
383
  }
303
384
  if (process.env.TTSC_CACHE_DIR) {
304
- return path.resolve(process.env.TTSC_CACHE_DIR, "plugins");
385
+ return node_path_1.default.resolve(process.env.TTSC_CACHE_DIR, "plugins");
305
386
  }
306
- const root = path.resolve(projectRoot);
307
- const nodeModules = path.join(root, "node_modules");
387
+ const root = node_path_1.default.resolve(projectRoot);
388
+ const nodeModules = node_path_1.default.join(root, "node_modules");
308
389
  if (isDirectory(nodeModules)) {
309
- return path.join(nodeModules, ".ttsc", "plugins");
390
+ return node_path_1.default.join(nodeModules, ".ttsc", "plugins");
310
391
  }
311
- return path.join(root, ".ttsc", "plugins");
392
+ return node_path_1.default.join(root, ".ttsc", "plugins");
312
393
  }
313
394
  function isDirectory(candidate) {
314
395
  try {
315
- return fs.statSync(candidate).isDirectory();
396
+ return node_fs_1.default.statSync(candidate).isDirectory();
316
397
  }
317
398
  catch {
318
399
  return false;
@@ -328,30 +409,41 @@ function resolveGoCompiler() {
328
409
  catch {
329
410
  /* fall through */
330
411
  }
331
- const platformPackage = path.resolve(__dirname, "..", "..", "..", "..", `ttsc-${process.platform}-${process.arch}`, "bin", "go", "bin", process.platform === "win32" ? "go.exe" : "go");
332
- if (fs.existsSync(platformPackage))
412
+ const platformPackage = node_path_1.default.resolve(__dirname, "..", "..", "..", "..", `ttsc-${process.platform}-${process.arch}`, "bin", "go", "bin", process.platform === "win32" ? "go.exe" : "go");
413
+ if (node_fs_1.default.existsSync(platformPackage))
333
414
  return platformPackage;
334
- const local = path.resolve(__dirname, "..", "..", "..", "..", "native", "go", "bin", process.platform === "win32" ? "go.exe" : "go");
335
- if (fs.existsSync(local))
415
+ const local = node_path_1.default.resolve(__dirname, "..", "..", "..", "..", "native", "go", "bin", process.platform === "win32" ? "go.exe" : "go");
416
+ if (node_fs_1.default.existsSync(local))
336
417
  return local;
337
- const homeSdk = path.join(process.env.HOME ?? "", "go-sdk", "go", "bin", process.platform === "win32" ? "go.exe" : "go");
338
- if (fs.existsSync(homeSdk))
418
+ const homeSdk = node_path_1.default.join(process.env.HOME ?? "", "go-sdk", "go", "bin", process.platform === "win32" ? "go.exe" : "go");
419
+ if (node_fs_1.default.existsSync(homeSdk))
339
420
  return homeSdk;
340
421
  return "go";
341
422
  }
342
423
  function computeCacheKey(inputs) {
343
- const hash = crypto.createHash("sha256");
424
+ const hash = node_crypto_1.default.createHash("sha256");
344
425
  hash.update(`ttsc=${inputs.ttscVersion}\n`);
345
426
  hash.update(`tsgo=${inputs.tsgoVersion}\n`);
346
427
  hash.update(`platform=${process.platform}/${process.arch}\n`);
347
428
  hash.update(`entry=${inputs.entry}\n`);
348
- for (const file of collectSourceFiles(inputs.dir)) {
349
- const rel = path.relative(inputs.dir, file).replace(/\\/g, "/");
350
- hash.update(`f=${rel}\n`);
351
- hash.update(fs.readFileSync(file));
429
+ if (inputs.goBinary !== undefined) {
430
+ hash.update(`go=${resolveGoCompilerIdentity(inputs.goBinary)}\n`);
431
+ }
432
+ hashSourceDirectory(hash, "plugin", inputs.dir);
433
+ for (const [index, dir] of [...(inputs.overlayDirs ?? [])].sort().entries()) {
434
+ hashSourceDirectory(hash, `overlay:${index}`, dir);
352
435
  }
353
436
  return hash.digest("hex").slice(0, 32);
354
437
  }
438
+ function hashSourceDirectory(hash, label, root) {
439
+ hash.update(`dir=${label}\n`);
440
+ for (const file of collectSourceFiles(root)) {
441
+ const rel = node_path_1.default.relative(root, file).replace(/\\/g, "/");
442
+ hash.update(`f=${rel}\n`);
443
+ hash.update(node_fs_1.default.readFileSync(file));
444
+ hash.update("\n");
445
+ }
446
+ }
355
447
  function collectSourceFiles(root) {
356
448
  const out = [];
357
449
  walk(root, out);
@@ -361,29 +453,85 @@ function collectSourceFiles(root) {
361
453
  function walk(dir, out) {
362
454
  let entries;
363
455
  try {
364
- entries = fs.readdirSync(dir, { withFileTypes: true });
456
+ entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
365
457
  }
366
458
  catch {
367
459
  return;
368
460
  }
369
461
  for (const entry of entries) {
370
- if (SKIP_DIRS.has(entry.name))
371
- continue;
372
- const full = path.join(dir, entry.name);
462
+ const full = node_path_1.default.join(dir, entry.name);
373
463
  if (entry.isDirectory()) {
464
+ if (shouldPruneDirectory(entry.name))
465
+ continue;
374
466
  walk(full, out);
375
467
  continue;
376
468
  }
377
469
  if (!entry.isFile())
378
470
  continue;
471
+ if (shouldOmitSourceFile(entry.name))
472
+ continue;
379
473
  if (!isHashableFile(entry.name))
380
474
  continue;
381
475
  out.push(full);
382
476
  }
383
477
  }
478
+ function shouldPruneDirectory(name) {
479
+ return PRUNE_DIRS.has(name);
480
+ }
481
+ function shouldOmitSourceFile(name) {
482
+ return GENERATED_WORKSPACE_FILES.has(name);
483
+ }
384
484
  function isHashableFile(name) {
385
- if (name === "go.mod" || name === "go.sum" || name === "go.work")
386
- return true;
387
- return /\.(?:go|s|c|h|cpp|hpp)$/i.test(name);
485
+ return !name.endsWith("~");
486
+ }
487
+ function resolveGoCompilerIdentity(goBinary) {
488
+ const resolved = resolveExecutableIdentityPath(goBinary);
489
+ const version = (0, node_child_process_1.spawnSync)(goBinary, ["version"], {
490
+ encoding: "utf8",
491
+ windowsHide: true,
492
+ });
493
+ const versionText = version.error !== undefined
494
+ ? version.error.message
495
+ : `${version.status ?? 0}:${version.stdout}${version.stderr}`;
496
+ let statText = "";
497
+ try {
498
+ const stat = node_fs_1.default.statSync(resolved);
499
+ statText = `${stat.size}:${stat.mtimeMs}`;
500
+ }
501
+ catch {
502
+ statText = "missing";
503
+ }
504
+ return `${goBinary}:${resolved}:${statText}:${versionText}`;
505
+ }
506
+ function resolveExecutableIdentityPath(binary) {
507
+ if (node_path_1.default.isAbsolute(binary)) {
508
+ return resolveRealPath(binary);
509
+ }
510
+ if (binary.includes(node_path_1.default.sep)) {
511
+ return resolveRealPath(node_path_1.default.resolve(binary));
512
+ }
513
+ for (const dir of (process.env.PATH ?? "").split(node_path_1.default.delimiter)) {
514
+ if (dir.length === 0)
515
+ continue;
516
+ const candidate = node_path_1.default.join(dir, binary);
517
+ if (node_fs_1.default.existsSync(candidate)) {
518
+ return resolveRealPath(candidate);
519
+ }
520
+ if (process.platform === "win32") {
521
+ const executable = `${candidate}.exe`;
522
+ if (node_fs_1.default.existsSync(executable)) {
523
+ return resolveRealPath(executable);
524
+ }
525
+ }
526
+ }
527
+ return binary;
528
+ }
529
+ function resolveRealPath(location) {
530
+ try {
531
+ return node_fs_1.default.realpathSync(location);
532
+ }
533
+ catch {
534
+ return location;
535
+ }
388
536
  }
389
537
  //# sourceMappingURL=buildSourcePlugin.js.map