Skip to content

Commit cddbcaa

Browse files
committed
fix(bridge): read TNB_LIB_PATH via os.Getenv first (Windows support)
On Windows, Node's process.env write goes through SetEnvironmentVariableW, which never touches the CRT environ block that libc getenv reads — so the noembed lib-path override introduced for the c-shared bridge was invisible to Go, and tsgo fell back to the executable-adjacent path (node.exe's dir) and panicked: 'lib.d.ts does not exist; this executable may be misplaced' (issue #9). getenvLive now tries os.Getenv first — Go resolves it through the Win32 environment block, the one channel that sees SetEnvironmentVariableW — and falls back to libc getenv for POSIX, where Go's runtime-init snapshot can miss a setenv issued immediately before koffi.load. Both set-before-dlopen channels are covered.
1 parent 90f30b7 commit cddbcaa

2 files changed

Lines changed: 20 additions & 8 deletions

File tree

‎patches/typescript-go/0005-noembed-tnb-lib-path.patch‎

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
diff --git a/internal/bundled/noembed.go b/internal/bundled/noembed.go
2-
index 39b204b45..c2f3a1cf8 100644
2+
index 39b204b45..866f99caa 100644
33
--- a/internal/bundled/noembed.go
44
+++ b/internal/bundled/noembed.go
55
@@ -2,11 +2,17 @@
@@ -20,15 +20,25 @@ index 39b204b45..c2f3a1cf8 100644
2020

2121
"github.com/microsoft/typescript-go/internal/tspath"
2222
"github.com/microsoft/typescript-go/internal/vfs"
23-
@@ -29,10 +35,35 @@ var executableDir = sync.OnceValue(func() string {
23+
@@ -29,10 +35,45 @@ var executableDir = sync.OnceValue(func() string {
2424
return tspath.GetDirectoryPath(exe)
2525
})
2626

27-
+// getenvLive reads the process environ via libc so values set with setenv
28-
+// immediately before dlopen (Node process.env.TNB_LIB_PATH = ...) are visible.
29-
+// Go's os.Getenv may miss a key that was absent from the snapshot taken at
30-
+// runtime init depending on how/when the key was introduced.
27+
+// getenvLive reads the live process environment for keys set immediately
28+
+// before dlopen (Node process.env.TNB_LIB_PATH = ...).
29+
+//
30+
+// Two channels are needed:
31+
+// - os.Getenv: on Windows this resolves through the Win32 environment block,
32+
+// which Node's process.env write (SetEnvironmentVariableW) updates. It is
33+
+// the ONLY channel that works there — the CRT environ that libc getenv
34+
+// reads is initialized at process start and never sees that write.
35+
+// - libc getenv: on POSIX, Go's os.Getenv reads the runtime-init snapshot and
36+
+// can miss a libc setenv issued right before koffi.load; libc getenv reads
37+
+// the live CRT environ instead.
3138
+func getenvLive(key string) string {
39+
+ if v := os.Getenv(key); v != "" {
40+
+ return v
41+
+ }
3242
+ ckey := C.CString(key)
3343
+ defer C.free(unsafe.Pointer(ckey))
3444
+ cval := C.getenv(ckey)

‎patches/typescript/overlay/src/compiler/tsgoChecker.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,8 +363,10 @@ function loadBridgeDeps(): void {
363363
: "asyncpreemptoff=1";
364364
}
365365
// noembed: point tsgo at TNB lib/ before dlopen. Go may snapshot environ at
366-
// runtime init, so noembed.go reads TNB_LIB_PATH via libc getenv (live), which
367-
// sees this setenv as long as it happens before koffi.load below.
366+
// runtime init; noembed.go's getenvLive therefore checks os.Getenv (Win32
367+
// block — the only channel that sees SetEnvironmentVariableW on Windows)
368+
// and libc getenv (live CRT environ for POSIX setenv) — both see this write
369+
// as long as it happens before koffi.load below.
368370
// External non-empty override wins — do not clobber a pre-set path.
369371
if (!process.env.TNB_LIB_PATH) {
370372
process.env.TNB_LIB_PATH = path.join(packageRoot, "lib");

0 commit comments

Comments
 (0)