ttsc 0.11.0 → 0.12.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 +25 -6
- package/cmd/ttsc/api_compile.go +101 -106
- package/cmd/ttsc/api_compile_branches_test.go +81 -0
- package/cmd/ttsc/api_transform.go +49 -54
- package/cmd/ttsc/api_transform_branches_test.go +41 -0
- package/cmd/ttsc/build.go +108 -109
- package/cmd/ttsc/build_branches_test.go +130 -0
- package/cmd/ttsc/helpers_test.go +47 -0
- package/cmd/ttsc/main.go +104 -103
- package/cmd/ttsc/main_alias_branches_test.go +29 -0
- package/cmd/ttscserver/main.go +104 -96
- package/cmd/ttscserver/run_lsp_prefers_tsgo_flag_test.go +42 -0
- package/cmd/ttscserver/run_lsp_reports_server_error_test.go +23 -25
- package/cmd/ttscserver/run_lsp_uses_tsgo_environment_test.go +44 -0
- package/cmd/utility-host/main.go +42 -0
- package/driver/lsp.go +60 -0
- package/driver/plugins.go +122 -0
- package/driver/program.go +346 -356
- package/driver/rewrite.go +336 -339
- package/{driver → internal/lspserver}/lsp_envelope.go +70 -64
- package/{driver → internal/lspserver}/lsp_frame.go +62 -62
- package/{driver → internal/lspserver}/lsp_plugin.go +58 -58
- package/internal/lspserver/lsp_proxy.go +391 -0
- package/internal/lspserver/lsp_server.go +231 -0
- package/lib/TtscCompiler.d.ts +3 -3
- package/lib/TtscCompiler.js +20 -6
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/resolveTsgo.d.ts +1 -0
- package/lib/compiler/internal/resolveTsgo.js +15 -4
- package/lib/compiler/internal/resolveTsgo.js.map +1 -1
- package/lib/compiler/internal/runBuild.js +12 -4
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/sharedHostHelpers.d.ts +12 -33
- package/lib/compiler/internal/sharedHostHelpers.js +27 -70
- package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +12 -5
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1
- package/lib/index.js.map +1 -1
- package/lib/launcher/internal/runTtsc.js +9 -4
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.d.ts +1 -0
- package/lib/launcher/internal/runTtscserver.js +38 -1
- package/lib/launcher/internal/runTtscserver.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.d.ts +3 -0
- package/lib/plugin/internal/buildSourcePlugin.js +444 -41
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.js +153 -34
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscCompilerContext.d.ts +2 -1
- package/lib/structures/ITtscPlugin.d.ts +8 -13
- package/lib/structures/ITtscPluginFactoryContext.d.ts +4 -5
- package/lib/structures/ITtscProjectPluginConfig.d.ts +2 -2
- package/lib/structures/internal/ITtscLoadedNativePlugin.d.ts +4 -2
- package/package.json +8 -8
- package/shim/lsp/shim.go +11 -17
- package/src/TtscCompiler.ts +20 -7
- package/src/compiler/internal/resolveTsgo.ts +18 -5
- package/src/compiler/internal/runBuild.ts +17 -4
- package/src/compiler/internal/sharedHostHelpers.ts +34 -67
- package/src/compiler/internal/transformProjectInMemory.ts +18 -5
- package/src/index.ts +0 -1
- package/src/launcher/internal/runTtsc.ts +9 -4
- package/src/launcher/internal/runTtscserver.ts +43 -1
- package/src/plugin/internal/buildSourcePlugin.ts +475 -41
- package/src/plugin/internal/loadProjectPlugins.ts +199 -39
- package/src/structures/ITtscCompilerContext.ts +2 -1
- package/src/structures/ITtscPlugin.ts +9 -13
- package/src/structures/ITtscPluginFactoryContext.ts +4 -5
- package/src/structures/ITtscProjectPluginConfig.ts +2 -2
- package/src/structures/internal/ITtscLoadedNativePlugin.ts +4 -2
- package/utility/host.go +267 -1296
- package/driver/lsp_proxy.go +0 -394
- package/driver/lsp_server.go +0 -197
- package/lib/defineTtscPlugin.d.ts +0 -18
- package/lib/defineTtscPlugin.js +0 -7
- package/lib/defineTtscPlugin.js.map +0 -1
- package/src/defineTtscPlugin.ts +0 -26
|
@@ -4,6 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.buildSourcePlugin = buildSourcePlugin;
|
|
7
|
+
exports.resolvePluginCacheRoot = resolvePluginCacheRoot;
|
|
8
|
+
exports.resolveGlobalPluginCacheRoot = resolveGlobalPluginCacheRoot;
|
|
9
|
+
exports.defaultPluginCacheCleanTargets = defaultPluginCacheCleanTargets;
|
|
7
10
|
exports.computeCacheKey = computeCacheKey;
|
|
8
11
|
const node_child_process_1 = require("node:child_process");
|
|
9
12
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
@@ -16,14 +19,15 @@ const TTSC_GO_MODULE_PATH = "github.com/samchon/ttsc/packages/ttsc";
|
|
|
16
19
|
const TSGO_GO_MODULE_PATH = "github.com/microsoft/typescript-go";
|
|
17
20
|
const PRUNE_DIRS = new Set(["node_modules", ".git", ".ttsc"]);
|
|
18
21
|
const GENERATED_WORKSPACE_FILES = new Set(["go.work", "go.work.sum"]);
|
|
19
|
-
// Go
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
+
// Go build environment values that can change the produced binary or decide
|
|
23
|
+
// whether `go build` succeeds. Hashed into the plugin cache key so target,
|
|
24
|
+
// build-tag, cgo, FIPS, and external-link variants never collide.
|
|
22
25
|
const GO_BUILD_ENV_KEYS = [
|
|
23
26
|
"GOOS",
|
|
24
27
|
"GOARCH",
|
|
25
28
|
"GOAMD64",
|
|
26
29
|
"GOARM",
|
|
30
|
+
"GOARM64",
|
|
27
31
|
"GO386",
|
|
28
32
|
"GOMIPS",
|
|
29
33
|
"GOMIPS64",
|
|
@@ -32,15 +36,77 @@ const GO_BUILD_ENV_KEYS = [
|
|
|
32
36
|
"GOWASM",
|
|
33
37
|
"GOFLAGS",
|
|
34
38
|
"GOEXPERIMENT",
|
|
39
|
+
"GOFIPS140",
|
|
40
|
+
"GO_EXTLINK_ENABLED",
|
|
41
|
+
"GCCGO",
|
|
42
|
+
"GCCGOTOOLDIR",
|
|
35
43
|
"CGO_ENABLED",
|
|
44
|
+
"AR",
|
|
45
|
+
"CC",
|
|
46
|
+
"CXX",
|
|
47
|
+
"FC",
|
|
48
|
+
"PKG_CONFIG",
|
|
49
|
+
"CGO_CFLAGS",
|
|
50
|
+
"CGO_CFLAGS_ALLOW",
|
|
51
|
+
"CGO_CFLAGS_DISALLOW",
|
|
52
|
+
"CGO_CPPFLAGS",
|
|
53
|
+
"CGO_CPPFLAGS_ALLOW",
|
|
54
|
+
"CGO_CPPFLAGS_DISALLOW",
|
|
55
|
+
"CGO_CXXFLAGS",
|
|
56
|
+
"CGO_CXXFLAGS_ALLOW",
|
|
57
|
+
"CGO_CXXFLAGS_DISALLOW",
|
|
58
|
+
"CGO_FFLAGS",
|
|
59
|
+
"CGO_FFLAGS_ALLOW",
|
|
60
|
+
"CGO_FFLAGS_DISALLOW",
|
|
61
|
+
"CGO_LDFLAGS",
|
|
62
|
+
"CGO_LDFLAGS_ALLOW",
|
|
63
|
+
"CGO_LDFLAGS_DISALLOW",
|
|
36
64
|
"GOTOOLCHAIN",
|
|
65
|
+
"GOROOT",
|
|
66
|
+
];
|
|
67
|
+
const GO_BUILD_COMMAND_ENV_KEYS = new Set([
|
|
68
|
+
"AR",
|
|
69
|
+
"CC",
|
|
70
|
+
"CXX",
|
|
71
|
+
"FC",
|
|
72
|
+
"GCCGO",
|
|
73
|
+
"PKG_CONFIG",
|
|
74
|
+
]);
|
|
75
|
+
const EXTERNAL_GO_BUILD_ENV_KEYS = [
|
|
76
|
+
"CPATH",
|
|
77
|
+
"C_INCLUDE_PATH",
|
|
78
|
+
"CPLUS_INCLUDE_PATH",
|
|
79
|
+
"DYLD_LIBRARY_PATH",
|
|
80
|
+
"INCLUDE",
|
|
81
|
+
"LD_LIBRARY_PATH",
|
|
82
|
+
"LIB",
|
|
83
|
+
"LIBRARY_PATH",
|
|
84
|
+
"LIBPATH",
|
|
85
|
+
"MACOSX_DEPLOYMENT_TARGET",
|
|
86
|
+
"OBJC_INCLUDE_PATH",
|
|
87
|
+
"PKG_CONFIG_ALLOW_SYSTEM_CFLAGS",
|
|
88
|
+
"PKG_CONFIG_ALLOW_SYSTEM_LIBS",
|
|
89
|
+
"PKG_CONFIG_LIBDIR",
|
|
90
|
+
"PKG_CONFIG_PATH",
|
|
91
|
+
"PKG_CONFIG_SYSROOT_DIR",
|
|
92
|
+
"PKG_CONFIG_TOP_BUILD_DIR",
|
|
93
|
+
"SDKROOT",
|
|
37
94
|
];
|
|
38
95
|
const CONTRIBUTIONS_FILE_NAME = "ttsc_contributions.go";
|
|
39
96
|
const CONTRIB_DIRNAME = "contrib";
|
|
97
|
+
const GLOBAL_CACHE_DIRNAME = "ttsc";
|
|
98
|
+
const PLUGIN_CACHE_DIRNAME = "plugins";
|
|
99
|
+
const CACHE_LAST_USED_FILE = ".last-used";
|
|
100
|
+
const CACHE_GC_MARKER_FILE = ".gc-last-run";
|
|
101
|
+
const GLOBAL_CACHE_GC_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
102
|
+
const GLOBAL_CACHE_ENTRY_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;
|
|
103
|
+
const GLOBAL_CACHE_MAX_BYTES = 2 * 1024 * 1024 * 1024;
|
|
104
|
+
const GLOBAL_CACHE_TARGET_BYTES = Math.floor(GLOBAL_CACHE_MAX_BYTES * 0.8);
|
|
105
|
+
const GLOBAL_CACHE_PROTECTED_AGE_MS = 60 * 60 * 1000;
|
|
40
106
|
/** Build one Go source plugin into a cached executable. */
|
|
41
107
|
function buildSourcePlugin(opts) {
|
|
42
108
|
const { dir, entry, source } = resolveSourceBuildTarget(opts);
|
|
43
|
-
const overlayDirs = opts.overlayDirs ?? findTtscOverlayDirs();
|
|
109
|
+
const overlayDirs = [...(opts.overlayDirs ?? findTtscOverlayDirs())].sort();
|
|
44
110
|
const contributors = opts.contributors ?? [];
|
|
45
111
|
const goBinary = resolveGoCompiler();
|
|
46
112
|
ensureExecutableGoToolchain(goBinary);
|
|
@@ -58,6 +124,7 @@ function buildSourcePlugin(opts) {
|
|
|
58
124
|
const binaryName = process.platform === "win32" ? "plugin.exe" : "plugin";
|
|
59
125
|
const binaryPath = node_path_1.default.join(cacheDir, binaryName);
|
|
60
126
|
if (node_fs_1.default.existsSync(binaryPath)) {
|
|
127
|
+
touchCacheEntry(cacheDir);
|
|
61
128
|
return binaryPath;
|
|
62
129
|
}
|
|
63
130
|
node_fs_1.default.mkdirSync(cacheDir, { recursive: true });
|
|
@@ -88,6 +155,7 @@ function buildSourcePlugin(opts) {
|
|
|
88
155
|
runGoBuild(scratchDir, entry, scratchBinaryName, opts.pluginName, goBinary);
|
|
89
156
|
const builtBinary = node_path_1.default.join(scratchDir, scratchBinaryName);
|
|
90
157
|
publishBuiltBinary(builtBinary, binaryPath);
|
|
158
|
+
touchCacheEntry(cacheDir);
|
|
91
159
|
return binaryPath;
|
|
92
160
|
}
|
|
93
161
|
finally {
|
|
@@ -305,11 +373,18 @@ function materializeScratchDir(source, scratch) {
|
|
|
305
373
|
function writeGoWork(scratchDir, useDirs, goBinary, pluginName) {
|
|
306
374
|
const goModReader = createGoModReader(goBinary, pluginName);
|
|
307
375
|
validateSourceReplacements(scratchDir, useDirs, goModReader, pluginName);
|
|
376
|
+
const sourceInfo = goModReader.read(scratchDir);
|
|
377
|
+
const effectiveUseDirs = sourceInfo.modulePath === TTSC_GO_MODULE_PATH
|
|
378
|
+
? useDirs.filter((dir) => {
|
|
379
|
+
const modulePath = goModReader.read(dir).modulePath;
|
|
380
|
+
return modulePath !== null && !isTtscManagedModulePath(modulePath);
|
|
381
|
+
})
|
|
382
|
+
: useDirs;
|
|
308
383
|
const useLines = ["\t."];
|
|
309
|
-
for (const dir of
|
|
384
|
+
for (const dir of effectiveUseDirs) {
|
|
310
385
|
useLines.push(`\t${dir.replace(/\\/g, "/")}`);
|
|
311
386
|
}
|
|
312
|
-
const replaceLines = sourceBuildWorkspaceReplacements(
|
|
387
|
+
const replaceLines = sourceBuildWorkspaceReplacements(effectiveUseDirs, goModReader);
|
|
313
388
|
const replaceBlock = replaceLines.length === 0 ? "" : `\n\n${replaceLines.join("\n")}\n`;
|
|
314
389
|
const goWork = `go 1.26\n\nuse (\n${useLines.join("\n")}\n)${replaceBlock}`;
|
|
315
390
|
node_fs_1.default.writeFileSync(node_path_1.default.join(scratchDir, "go.work"), goWork, "utf8");
|
|
@@ -447,6 +522,7 @@ function goToolchainNotFoundMessage(pluginName) {
|
|
|
447
522
|
}
|
|
448
523
|
function goBuildEnv(goBinary) {
|
|
449
524
|
const env = { ...process.env };
|
|
525
|
+
env.GOWORK = "auto";
|
|
450
526
|
const goRoot = inferGoRoot(goBinary);
|
|
451
527
|
if (goRoot && !env.GOROOT) {
|
|
452
528
|
env.GOROOT = goRoot;
|
|
@@ -546,25 +622,51 @@ function walkForGoMod(dir, out) {
|
|
|
546
622
|
}
|
|
547
623
|
function resolvePluginCacheRoot(projectRoot, cacheDir) {
|
|
548
624
|
if (cacheDir) {
|
|
549
|
-
return node_path_1.default.resolve(projectRoot, cacheDir,
|
|
625
|
+
return node_path_1.default.resolve(projectRoot, cacheDir, PLUGIN_CACHE_DIRNAME);
|
|
550
626
|
}
|
|
551
627
|
if (process.env.TTSC_CACHE_DIR) {
|
|
552
|
-
return node_path_1.default.resolve(process.env.TTSC_CACHE_DIR,
|
|
553
|
-
}
|
|
554
|
-
const root = node_path_1.default.resolve(projectRoot);
|
|
555
|
-
const nodeModules = node_path_1.default.join(root, "node_modules");
|
|
556
|
-
if (isDirectory(nodeModules)) {
|
|
557
|
-
return node_path_1.default.join(nodeModules, ".ttsc", "plugins");
|
|
628
|
+
return node_path_1.default.resolve(process.env.TTSC_CACHE_DIR, PLUGIN_CACHE_DIRNAME);
|
|
558
629
|
}
|
|
559
|
-
|
|
630
|
+
const root = resolveGlobalPluginCacheRoot();
|
|
631
|
+
maybePruneGlobalPluginCache(root);
|
|
632
|
+
return root;
|
|
560
633
|
}
|
|
561
|
-
function
|
|
562
|
-
|
|
563
|
-
|
|
634
|
+
function resolveGlobalPluginCacheRoot() {
|
|
635
|
+
return node_path_1.default.join(resolveUserCacheRoot(), GLOBAL_CACHE_DIRNAME, PLUGIN_CACHE_DIRNAME);
|
|
636
|
+
}
|
|
637
|
+
function defaultPluginCacheCleanTargets(projectRoot) {
|
|
638
|
+
return [
|
|
639
|
+
resolveGlobalPluginCacheRoot(),
|
|
640
|
+
node_path_1.default.join(projectRoot, "node_modules", ".ttsc"),
|
|
641
|
+
node_path_1.default.join(projectRoot, ".ttsc"),
|
|
642
|
+
];
|
|
643
|
+
}
|
|
644
|
+
function resolveUserCacheRoot() {
|
|
645
|
+
const xdg = process.env.XDG_CACHE_HOME;
|
|
646
|
+
if (xdg && node_path_1.default.isAbsolute(xdg)) {
|
|
647
|
+
return xdg;
|
|
648
|
+
}
|
|
649
|
+
if (process.platform === "win32") {
|
|
650
|
+
const local = process.env.LOCALAPPDATA;
|
|
651
|
+
if (local && node_path_1.default.isAbsolute(local)) {
|
|
652
|
+
return local;
|
|
653
|
+
}
|
|
654
|
+
const home = node_os_1.default.homedir();
|
|
655
|
+
if (home) {
|
|
656
|
+
return node_path_1.default.join(home, "AppData", "Local");
|
|
657
|
+
}
|
|
564
658
|
}
|
|
565
|
-
|
|
566
|
-
|
|
659
|
+
if (process.platform === "darwin") {
|
|
660
|
+
const home = node_os_1.default.homedir();
|
|
661
|
+
if (home) {
|
|
662
|
+
return node_path_1.default.join(home, "Library", "Caches");
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
const home = node_os_1.default.homedir();
|
|
666
|
+
if (home) {
|
|
667
|
+
return node_path_1.default.join(home, ".cache");
|
|
567
668
|
}
|
|
669
|
+
return node_path_1.default.join(node_os_1.default.tmpdir(), "ttsc-cache");
|
|
568
670
|
}
|
|
569
671
|
function resolveGoCompiler() {
|
|
570
672
|
const explicit = process.env.TTSC_GO_BINARY;
|
|
@@ -596,17 +698,8 @@ function computeCacheKey(inputs) {
|
|
|
596
698
|
if (inputs.goBinary !== undefined) {
|
|
597
699
|
hash.update(`go=${resolveGoCompilerIdentity(inputs.goBinary)}\n`);
|
|
598
700
|
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
// `GOOS=linux GOARCH=arm64` on a darwin host would write a Linux-arm64
|
|
602
|
-
// binary into the same cache slot as the native build, and the next
|
|
603
|
-
// native invocation would spawn an unrunnable artifact.
|
|
604
|
-
for (const key of GO_BUILD_ENV_KEYS) {
|
|
605
|
-
const value = process.env[key];
|
|
606
|
-
if (value !== undefined && value !== "") {
|
|
607
|
-
hash.update(`${key}=${value}\n`);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
701
|
+
hashGoBuildEnvironment(hash, inputs.goBinary, inputs.dir);
|
|
702
|
+
hashExternalGoBuildEnvironment(hash);
|
|
610
703
|
hashSourceDirectory(hash, "plugin", inputs.dir);
|
|
611
704
|
for (const [index, dir] of [...(inputs.overlayDirs ?? [])].sort().entries()) {
|
|
612
705
|
hashSourceDirectory(hash, `overlay:${index}`, dir);
|
|
@@ -681,22 +774,18 @@ function isHashableFile(name) {
|
|
|
681
774
|
}
|
|
682
775
|
function resolveGoCompilerIdentity(goBinary) {
|
|
683
776
|
const resolved = resolveExecutableIdentityPath(goBinary);
|
|
777
|
+
if (!node_fs_1.default.existsSync(resolved)) {
|
|
778
|
+
return "missing";
|
|
779
|
+
}
|
|
684
780
|
const version = (0, node_child_process_1.spawnSync)(goBinary, ["version"], {
|
|
685
781
|
encoding: "utf8",
|
|
686
782
|
windowsHide: true,
|
|
687
783
|
});
|
|
688
784
|
const versionText = version.error !== undefined
|
|
689
|
-
? version.error.message
|
|
785
|
+
? (version.error.code ?? version.error.message)
|
|
690
786
|
: `${version.status ?? 0}:${version.stdout}${version.stderr}`;
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
const stat = node_fs_1.default.statSync(resolved);
|
|
694
|
-
statText = `${stat.size}:${stat.mtimeMs}`;
|
|
695
|
-
}
|
|
696
|
-
catch {
|
|
697
|
-
statText = "missing";
|
|
698
|
-
}
|
|
699
|
-
return `${goBinary}:${resolved}:${statText}:${versionText}`;
|
|
787
|
+
const binaryHash = hashFile(resolved);
|
|
788
|
+
return `sha256:${binaryHash}:${versionText}`;
|
|
700
789
|
}
|
|
701
790
|
function resolveExecutableIdentityPath(binary) {
|
|
702
791
|
if (node_path_1.default.isAbsolute(binary)) {
|
|
@@ -705,7 +794,7 @@ function resolveExecutableIdentityPath(binary) {
|
|
|
705
794
|
if (binary.includes(node_path_1.default.sep)) {
|
|
706
795
|
return resolveRealPath(node_path_1.default.resolve(binary));
|
|
707
796
|
}
|
|
708
|
-
for (const dir of (
|
|
797
|
+
for (const dir of readPathEnvironment().split(node_path_1.default.delimiter)) {
|
|
709
798
|
if (dir.length === 0)
|
|
710
799
|
continue;
|
|
711
800
|
const candidate = node_path_1.default.join(dir, binary);
|
|
@@ -721,6 +810,9 @@ function resolveExecutableIdentityPath(binary) {
|
|
|
721
810
|
}
|
|
722
811
|
return binary;
|
|
723
812
|
}
|
|
813
|
+
function readPathEnvironment() {
|
|
814
|
+
return process.env.PATH ?? process.env.Path ?? "";
|
|
815
|
+
}
|
|
724
816
|
function resolveRealPath(location) {
|
|
725
817
|
try {
|
|
726
818
|
return node_fs_1.default.realpathSync(location);
|
|
@@ -729,4 +821,315 @@ function resolveRealPath(location) {
|
|
|
729
821
|
return location;
|
|
730
822
|
}
|
|
731
823
|
}
|
|
824
|
+
function hashFile(file) {
|
|
825
|
+
const hash = node_crypto_1.default.createHash("sha256");
|
|
826
|
+
hash.update(node_fs_1.default.readFileSync(file));
|
|
827
|
+
return hash.digest("hex");
|
|
828
|
+
}
|
|
829
|
+
function hashGoBuildEnvironment(hash, goBinary, cwd) {
|
|
830
|
+
const values = resolveGoBuildEnvironment(goBinary, cwd);
|
|
831
|
+
for (const key of GO_BUILD_ENV_KEYS) {
|
|
832
|
+
const value = values.get(key);
|
|
833
|
+
if (value !== undefined && value !== "") {
|
|
834
|
+
hash.update(`${key}=${value}\n`);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
function resolveGoBuildEnvironment(goBinary, cwd) {
|
|
839
|
+
const values = new Map();
|
|
840
|
+
if (goBinary !== undefined) {
|
|
841
|
+
const result = (0, node_child_process_1.spawnSync)(goBinary, ["env", "-json", ...GO_BUILD_ENV_KEYS], {
|
|
842
|
+
cwd,
|
|
843
|
+
encoding: "utf8",
|
|
844
|
+
env: goBuildEnv(goBinary),
|
|
845
|
+
maxBuffer: 1024 * 1024,
|
|
846
|
+
windowsHide: true,
|
|
847
|
+
});
|
|
848
|
+
if (result.error === undefined && result.status === 0) {
|
|
849
|
+
try {
|
|
850
|
+
const parsed = JSON.parse(result.stdout);
|
|
851
|
+
for (const key of GO_BUILD_ENV_KEYS) {
|
|
852
|
+
const raw = parsed[key];
|
|
853
|
+
if (typeof raw === "string" && raw !== "") {
|
|
854
|
+
values.set(key, normalizeGoBuildEnvValue(key, raw));
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
catch {
|
|
859
|
+
// Fall back to process.env below; a cache key is still better than
|
|
860
|
+
// failing before `go build` can produce the actionable error.
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
for (const key of GO_BUILD_ENV_KEYS) {
|
|
865
|
+
if (values.has(key))
|
|
866
|
+
continue;
|
|
867
|
+
const value = process.env[key];
|
|
868
|
+
if (value !== undefined && value !== "") {
|
|
869
|
+
values.set(key, normalizeGoBuildEnvValue(key, value));
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
return values;
|
|
873
|
+
}
|
|
874
|
+
function normalizeGoBuildEnvValue(key, value) {
|
|
875
|
+
if (key === "GOROOT") {
|
|
876
|
+
return resolveGoRootCacheIdentity(value);
|
|
877
|
+
}
|
|
878
|
+
if (GO_BUILD_COMMAND_ENV_KEYS.has(key)) {
|
|
879
|
+
return `${value}\0${resolveCommandCacheIdentity(value)}`;
|
|
880
|
+
}
|
|
881
|
+
return value;
|
|
882
|
+
}
|
|
883
|
+
function resolveCommandCacheIdentity(command) {
|
|
884
|
+
const executable = firstCommandToken(command);
|
|
885
|
+
if (executable === null) {
|
|
886
|
+
return "command:empty";
|
|
887
|
+
}
|
|
888
|
+
const resolved = resolveExecutableIdentityPath(executable);
|
|
889
|
+
if (!node_fs_1.default.existsSync(resolved)) {
|
|
890
|
+
return `command:missing:${executable}`;
|
|
891
|
+
}
|
|
892
|
+
try {
|
|
893
|
+
return `command:sha256:${hashFile(resolved)}`;
|
|
894
|
+
}
|
|
895
|
+
catch {
|
|
896
|
+
return `command:unreadable:${resolved}`;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
function firstCommandToken(command) {
|
|
900
|
+
const trimmed = command.trim();
|
|
901
|
+
if (trimmed === "") {
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
const quote = trimmed[0];
|
|
905
|
+
if (quote === "'" || quote === '"') {
|
|
906
|
+
const end = trimmed.indexOf(quote, 1);
|
|
907
|
+
return end === -1 ? trimmed.slice(1) : trimmed.slice(1, end);
|
|
908
|
+
}
|
|
909
|
+
return trimmed.split(/\s+/)[0] ?? null;
|
|
910
|
+
}
|
|
911
|
+
function hashExternalGoBuildEnvironment(hash) {
|
|
912
|
+
for (const key of EXTERNAL_GO_BUILD_ENV_KEYS) {
|
|
913
|
+
const value = process.env[key];
|
|
914
|
+
if (value !== undefined && value !== "") {
|
|
915
|
+
hash.update(`${key}=${value}\n`);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
function resolveGoRootCacheIdentity(goRoot) {
|
|
920
|
+
const resolved = resolveRealPath(goRoot);
|
|
921
|
+
if (!node_fs_1.default.existsSync(resolved)) {
|
|
922
|
+
return `missing:${goRoot}`;
|
|
923
|
+
}
|
|
924
|
+
const hash = node_crypto_1.default.createHash("sha256");
|
|
925
|
+
for (const file of collectGoRootIdentityFiles(resolved)) {
|
|
926
|
+
const relative = node_path_1.default.relative(resolved, file).replace(/\\/g, "/");
|
|
927
|
+
hash.update(`f=${relative}\n`);
|
|
928
|
+
hash.update(node_fs_1.default.readFileSync(file));
|
|
929
|
+
hash.update("\n");
|
|
930
|
+
}
|
|
931
|
+
return `sha256:${hash.digest("hex")}`;
|
|
932
|
+
}
|
|
933
|
+
function collectGoRootIdentityFiles(root) {
|
|
934
|
+
const out = [];
|
|
935
|
+
walkGoRootIdentity(root, root, out);
|
|
936
|
+
out.sort();
|
|
937
|
+
return out;
|
|
938
|
+
}
|
|
939
|
+
function walkGoRootIdentity(root, dir, out) {
|
|
940
|
+
let entries;
|
|
941
|
+
try {
|
|
942
|
+
entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
943
|
+
}
|
|
944
|
+
catch {
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
for (const entry of entries) {
|
|
948
|
+
const file = node_path_1.default.join(dir, entry.name);
|
|
949
|
+
const rel = node_path_1.default.relative(root, file).replace(/\\/g, "/");
|
|
950
|
+
if (entry.isDirectory()) {
|
|
951
|
+
if (shouldHashGoRootPath(rel, true)) {
|
|
952
|
+
walkGoRootIdentity(root, file, out);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
else if (entry.isFile() && shouldHashGoRootPath(rel, false)) {
|
|
956
|
+
out.push(file);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
function shouldHashGoRootPath(rel, isDir) {
|
|
961
|
+
if (rel === "")
|
|
962
|
+
return true;
|
|
963
|
+
const parts = rel.split("/");
|
|
964
|
+
if (parts.includes(".git") || parts.includes("testdata"))
|
|
965
|
+
return false;
|
|
966
|
+
if (!isDir && rel.endsWith("_test.go"))
|
|
967
|
+
return false;
|
|
968
|
+
const first = parts[0];
|
|
969
|
+
if (parts.length === 1) {
|
|
970
|
+
if (isDir)
|
|
971
|
+
return ["bin", "pkg", "src", "lib"].includes(first);
|
|
972
|
+
return ["VERSION", "go.env"].includes(first);
|
|
973
|
+
}
|
|
974
|
+
if (first === "bin") {
|
|
975
|
+
if (isDir)
|
|
976
|
+
return true;
|
|
977
|
+
const base = node_path_1.default.basename(rel);
|
|
978
|
+
return (base === "go" ||
|
|
979
|
+
base === "go.exe" ||
|
|
980
|
+
base === "gofmt" ||
|
|
981
|
+
base === "gofmt.exe");
|
|
982
|
+
}
|
|
983
|
+
if (first === "pkg") {
|
|
984
|
+
const second = parts[1];
|
|
985
|
+
return second === "tool" || second === "include";
|
|
986
|
+
}
|
|
987
|
+
if (first === "src") {
|
|
988
|
+
if (!isDir && parts.length === 2) {
|
|
989
|
+
return ["go.mod", "go.sum"].includes(parts[1]);
|
|
990
|
+
}
|
|
991
|
+
return parts[1] !== "cmd";
|
|
992
|
+
}
|
|
993
|
+
if (first === "lib") {
|
|
994
|
+
return parts[1] === "time";
|
|
995
|
+
}
|
|
996
|
+
return false;
|
|
997
|
+
}
|
|
998
|
+
function touchCacheEntry(cacheDir) {
|
|
999
|
+
try {
|
|
1000
|
+
node_fs_1.default.mkdirSync(cacheDir, { recursive: true });
|
|
1001
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(cacheDir, CACHE_LAST_USED_FILE), `${Date.now()}\n`);
|
|
1002
|
+
}
|
|
1003
|
+
catch {
|
|
1004
|
+
// Cache hits must not fail because metadata touch failed.
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
function maybePruneGlobalPluginCache(root) {
|
|
1008
|
+
try {
|
|
1009
|
+
node_fs_1.default.mkdirSync(root, { recursive: true });
|
|
1010
|
+
const marker = node_path_1.default.join(root, CACHE_GC_MARKER_FILE);
|
|
1011
|
+
const now = Date.now();
|
|
1012
|
+
const lastRun = readTimestamp(marker);
|
|
1013
|
+
if (lastRun !== null && now - lastRun < GLOBAL_CACHE_GC_INTERVAL_MS) {
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
node_fs_1.default.writeFileSync(marker, `${now}\n`);
|
|
1017
|
+
pruneGlobalPluginCache(root, now);
|
|
1018
|
+
}
|
|
1019
|
+
catch {
|
|
1020
|
+
// Global cache GC is opportunistic; builds still proceed when it fails.
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
function pruneGlobalPluginCache(root, now) {
|
|
1024
|
+
const entries = collectGlobalCacheEntries(root, now);
|
|
1025
|
+
for (const entry of entries) {
|
|
1026
|
+
if (now - entry.lastUsedAt <= GLOBAL_CACHE_ENTRY_MAX_AGE_MS) {
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
removeCacheEntry(entry);
|
|
1030
|
+
}
|
|
1031
|
+
const remaining = collectGlobalCacheEntries(root, now);
|
|
1032
|
+
let total = remaining.reduce((sum, entry) => sum + entry.size, 0);
|
|
1033
|
+
if (total <= GLOBAL_CACHE_MAX_BYTES) {
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
for (const entry of remaining.sort((a, b) => a.lastUsedAt - b.lastUsedAt)) {
|
|
1037
|
+
if (total <= GLOBAL_CACHE_TARGET_BYTES) {
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
if (now - entry.lastUsedAt <= GLOBAL_CACHE_PROTECTED_AGE_MS) {
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
removeCacheEntry(entry);
|
|
1044
|
+
total -= entry.size;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
function collectGlobalCacheEntries(root, now) {
|
|
1048
|
+
const entries = [];
|
|
1049
|
+
let dirents;
|
|
1050
|
+
try {
|
|
1051
|
+
dirents = node_fs_1.default.readdirSync(root, { withFileTypes: true });
|
|
1052
|
+
}
|
|
1053
|
+
catch {
|
|
1054
|
+
return entries;
|
|
1055
|
+
}
|
|
1056
|
+
for (const dirent of dirents) {
|
|
1057
|
+
if (!dirent.isDirectory()) {
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
const dir = node_path_1.default.join(root, dirent.name);
|
|
1061
|
+
const lastUsedAt = readCacheEntryLastUsedAt(dir, now);
|
|
1062
|
+
entries.push({
|
|
1063
|
+
dir,
|
|
1064
|
+
lastUsedAt,
|
|
1065
|
+
size: directorySize(dir),
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
return entries;
|
|
1069
|
+
}
|
|
1070
|
+
function readCacheEntryLastUsedAt(dir, now) {
|
|
1071
|
+
const touched = readTimestamp(node_path_1.default.join(dir, CACHE_LAST_USED_FILE));
|
|
1072
|
+
if (touched !== null) {
|
|
1073
|
+
return touched;
|
|
1074
|
+
}
|
|
1075
|
+
for (const name of ["plugin", "plugin.exe"]) {
|
|
1076
|
+
try {
|
|
1077
|
+
return node_fs_1.default.statSync(node_path_1.default.join(dir, name)).mtimeMs;
|
|
1078
|
+
}
|
|
1079
|
+
catch { }
|
|
1080
|
+
}
|
|
1081
|
+
try {
|
|
1082
|
+
return node_fs_1.default.statSync(dir).mtimeMs;
|
|
1083
|
+
}
|
|
1084
|
+
catch {
|
|
1085
|
+
return now;
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
function readTimestamp(file) {
|
|
1089
|
+
try {
|
|
1090
|
+
const text = node_fs_1.default.readFileSync(file, "utf8").trim();
|
|
1091
|
+
const value = Number(text);
|
|
1092
|
+
if (Number.isFinite(value)) {
|
|
1093
|
+
return value;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
catch { }
|
|
1097
|
+
try {
|
|
1098
|
+
return node_fs_1.default.statSync(file).mtimeMs;
|
|
1099
|
+
}
|
|
1100
|
+
catch {
|
|
1101
|
+
return null;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
function directorySize(dir) {
|
|
1105
|
+
let total = 0;
|
|
1106
|
+
let entries;
|
|
1107
|
+
try {
|
|
1108
|
+
entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
1109
|
+
}
|
|
1110
|
+
catch {
|
|
1111
|
+
return total;
|
|
1112
|
+
}
|
|
1113
|
+
for (const entry of entries) {
|
|
1114
|
+
const file = node_path_1.default.join(dir, entry.name);
|
|
1115
|
+
try {
|
|
1116
|
+
if (entry.isDirectory()) {
|
|
1117
|
+
total += directorySize(file);
|
|
1118
|
+
}
|
|
1119
|
+
else if (entry.isFile()) {
|
|
1120
|
+
total += node_fs_1.default.statSync(file).size;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
catch { }
|
|
1124
|
+
}
|
|
1125
|
+
return total;
|
|
1126
|
+
}
|
|
1127
|
+
function removeCacheEntry(entry) {
|
|
1128
|
+
try {
|
|
1129
|
+
node_fs_1.default.rmSync(entry.dir, { recursive: true, force: true });
|
|
1130
|
+
}
|
|
1131
|
+
catch {
|
|
1132
|
+
// Windows may reject removal while a plugin binary is still running.
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
732
1135
|
//# sourceMappingURL=buildSourcePlugin.js.map
|