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.
Files changed (79) hide show
  1. package/README.md +25 -6
  2. package/cmd/ttsc/api_compile.go +101 -106
  3. package/cmd/ttsc/api_compile_branches_test.go +81 -0
  4. package/cmd/ttsc/api_transform.go +49 -54
  5. package/cmd/ttsc/api_transform_branches_test.go +41 -0
  6. package/cmd/ttsc/build.go +108 -109
  7. package/cmd/ttsc/build_branches_test.go +130 -0
  8. package/cmd/ttsc/helpers_test.go +47 -0
  9. package/cmd/ttsc/main.go +104 -103
  10. package/cmd/ttsc/main_alias_branches_test.go +29 -0
  11. package/cmd/ttscserver/main.go +104 -96
  12. package/cmd/ttscserver/run_lsp_prefers_tsgo_flag_test.go +42 -0
  13. package/cmd/ttscserver/run_lsp_reports_server_error_test.go +23 -25
  14. package/cmd/ttscserver/run_lsp_uses_tsgo_environment_test.go +44 -0
  15. package/cmd/utility-host/main.go +42 -0
  16. package/driver/lsp.go +60 -0
  17. package/driver/plugins.go +122 -0
  18. package/driver/program.go +346 -356
  19. package/driver/rewrite.go +336 -339
  20. package/{driver → internal/lspserver}/lsp_envelope.go +70 -64
  21. package/{driver → internal/lspserver}/lsp_frame.go +62 -62
  22. package/{driver → internal/lspserver}/lsp_plugin.go +58 -58
  23. package/internal/lspserver/lsp_proxy.go +391 -0
  24. package/internal/lspserver/lsp_server.go +231 -0
  25. package/lib/TtscCompiler.d.ts +3 -3
  26. package/lib/TtscCompiler.js +20 -6
  27. package/lib/TtscCompiler.js.map +1 -1
  28. package/lib/compiler/internal/resolveTsgo.d.ts +1 -0
  29. package/lib/compiler/internal/resolveTsgo.js +15 -4
  30. package/lib/compiler/internal/resolveTsgo.js.map +1 -1
  31. package/lib/compiler/internal/runBuild.js +12 -4
  32. package/lib/compiler/internal/runBuild.js.map +1 -1
  33. package/lib/compiler/internal/sharedHostHelpers.d.ts +12 -33
  34. package/lib/compiler/internal/sharedHostHelpers.js +27 -70
  35. package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
  36. package/lib/compiler/internal/transformProjectInMemory.js +12 -5
  37. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  38. package/lib/index.d.ts +0 -1
  39. package/lib/index.js +0 -1
  40. package/lib/index.js.map +1 -1
  41. package/lib/launcher/internal/runTtsc.js +9 -4
  42. package/lib/launcher/internal/runTtsc.js.map +1 -1
  43. package/lib/launcher/internal/runTtscserver.d.ts +1 -0
  44. package/lib/launcher/internal/runTtscserver.js +38 -1
  45. package/lib/launcher/internal/runTtscserver.js.map +1 -1
  46. package/lib/plugin/internal/buildSourcePlugin.d.ts +3 -0
  47. package/lib/plugin/internal/buildSourcePlugin.js +444 -41
  48. package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
  49. package/lib/plugin/internal/loadProjectPlugins.js +153 -34
  50. package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
  51. package/lib/structures/ITtscCompilerContext.d.ts +2 -1
  52. package/lib/structures/ITtscPlugin.d.ts +8 -13
  53. package/lib/structures/ITtscPluginFactoryContext.d.ts +4 -5
  54. package/lib/structures/ITtscProjectPluginConfig.d.ts +2 -2
  55. package/lib/structures/internal/ITtscLoadedNativePlugin.d.ts +4 -2
  56. package/package.json +8 -8
  57. package/shim/lsp/shim.go +11 -17
  58. package/src/TtscCompiler.ts +20 -7
  59. package/src/compiler/internal/resolveTsgo.ts +18 -5
  60. package/src/compiler/internal/runBuild.ts +17 -4
  61. package/src/compiler/internal/sharedHostHelpers.ts +34 -67
  62. package/src/compiler/internal/transformProjectInMemory.ts +18 -5
  63. package/src/index.ts +0 -1
  64. package/src/launcher/internal/runTtsc.ts +9 -4
  65. package/src/launcher/internal/runTtscserver.ts +43 -1
  66. package/src/plugin/internal/buildSourcePlugin.ts +475 -41
  67. package/src/plugin/internal/loadProjectPlugins.ts +199 -39
  68. package/src/structures/ITtscCompilerContext.ts +2 -1
  69. package/src/structures/ITtscPlugin.ts +9 -13
  70. package/src/structures/ITtscPluginFactoryContext.ts +4 -5
  71. package/src/structures/ITtscProjectPluginConfig.ts +2 -2
  72. package/src/structures/internal/ITtscLoadedNativePlugin.ts +4 -2
  73. package/utility/host.go +267 -1296
  74. package/driver/lsp_proxy.go +0 -394
  75. package/driver/lsp_server.go +0 -197
  76. package/lib/defineTtscPlugin.d.ts +0 -18
  77. package/lib/defineTtscPlugin.js +0 -7
  78. package/lib/defineTtscPlugin.js.map +0 -1
  79. 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 env vars that change the produced binary. Hashed into the plugin
20
- // build cache key so cross-compile target / build tags / cgo toggle
21
- // don't collide with the native-platform key.
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 useDirs) {
384
+ for (const dir of effectiveUseDirs) {
310
385
  useLines.push(`\t${dir.replace(/\\/g, "/")}`);
311
386
  }
312
- const replaceLines = sourceBuildWorkspaceReplacements(useDirs, goModReader);
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, "plugins");
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, "plugins");
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
- return node_path_1.default.join(root, ".ttsc", "plugins");
630
+ const root = resolveGlobalPluginCacheRoot();
631
+ maybePruneGlobalPluginCache(root);
632
+ return root;
560
633
  }
561
- function isDirectory(candidate) {
562
- try {
563
- return node_fs_1.default.statSync(candidate).isDirectory();
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
- catch {
566
- return false;
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
- // Hash the Go env vars that change `go build`'s output (cross-compile
600
- // target, build tags, cgo toggle, etc.). Without this, a user setting
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
- let statText = "";
692
- try {
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 (process.env.PATH ?? "").split(node_path_1.default.delimiter)) {
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