typenative 0.1.3 → 0.3.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/.gitattributes +1 -0
- package/.github/workflows/ci.yml +39 -2
- package/.github/workflows/npm-publish-github-packages.yml +7 -2
- package/CHANGELOG.md +293 -0
- package/CONTRIBUTION.md +8 -6
- package/README.md +15 -11
- package/bin/cli.js +7 -1
- package/bin/go/bridge.js +919 -0
- package/bin/go/convert.js +238 -0
- package/bin/go/metadata.js +211 -0
- package/bin/go/overrides.js +68 -0
- package/bin/go/stdlib.generated.js +175 -0
- package/bin/index.js +337 -239
- package/bin/main.js +9 -2
- package/bin/node/assert.js +344 -0
- package/bin/node/async_hooks.js +273 -0
- package/bin/node/buffer.js +765 -0
- package/bin/node/child_process.js +600 -0
- package/bin/node/cluster.js +1002 -0
- package/bin/node/console.js +121 -0
- package/bin/node/core.js +111 -0
- package/bin/node/crypto.js +414 -0
- package/bin/node/crypto_keys.js +2685 -0
- package/bin/node/dgram.js +388 -0
- package/bin/node/diagnostics_channel.js +534 -0
- package/bin/node/dns.js +635 -0
- package/bin/node/events.js +743 -0
- package/bin/node/fetch.js +737 -0
- package/bin/node/fs.js +998 -0
- package/bin/node/http.js +1500 -0
- package/bin/node/http2.js +688 -0
- package/bin/node/net.js +1143 -0
- package/bin/node/os.js +257 -0
- package/bin/node/path.js +758 -0
- package/bin/node/process.js +316 -0
- package/bin/node/punycode.js +195 -0
- package/bin/node/querystring.js +143 -0
- package/bin/node/readline.js +299 -0
- package/bin/node/registry.js +161 -0
- package/bin/node/sqlite.js +1142 -0
- package/bin/node/stream.js +1221 -0
- package/bin/node/stream_web.js +2259 -0
- package/bin/node/string_decoder.js +101 -0
- package/bin/node/system_extra.js +714 -0
- package/bin/node/test.js +1068 -0
- package/bin/node/timers.js +263 -0
- package/bin/node/tls.js +719 -0
- package/bin/node/url.js +861 -0
- package/bin/node/util.js +1311 -0
- package/bin/node/util_extra.js +599 -0
- package/bin/node/worker_threads.js +745 -0
- package/bin/node/zlib.js +263 -0
- package/bin/parse-native.js +10 -0
- package/bin/parse-node.js +10 -0
- package/bin/transpiler.js +2824 -588
- package/bin/typecheck-node.js +78 -0
- package/bin/version.js +2 -0
- package/conformance.md +708 -241
- package/govendor/github.com/ncruces/go-sqlite3/LICENSE +21 -0
- package/govendor/github.com/ncruces/go-sqlite3/README.md +121 -0
- package/govendor/github.com/ncruces/go-sqlite3/backup.go +132 -0
- package/govendor/github.com/ncruces/go-sqlite3/blob.go +266 -0
- package/govendor/github.com/ncruces/go-sqlite3/config.go +397 -0
- package/govendor/github.com/ncruces/go-sqlite3/conn.go +540 -0
- package/govendor/github.com/ncruces/go-sqlite3/conn_127.go +93 -0
- package/govendor/github.com/ncruces/go-sqlite3/const.go +436 -0
- package/govendor/github.com/ncruces/go-sqlite3/context.go +222 -0
- package/govendor/github.com/ncruces/go-sqlite3/error.go +182 -0
- package/govendor/github.com/ncruces/go-sqlite3/ext.go +153 -0
- package/govendor/github.com/ncruces/go-sqlite3/func.go +309 -0
- package/govendor/github.com/ncruces/go-sqlite3/go.mod +3 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/dotlk/dotlk.go +29 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/dotlk/dotlk_other.go +13 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/dotlk/dotlk_unix.go +57 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/errutil/error.go +41 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/alloc.go +128 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/const.go +122 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/error.go +77 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/mem.go +116 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/mem_other.go +34 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/mem_unix.go +87 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/mem_windows.go +149 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/mmap_unix.go +80 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/mmap_windows.go +129 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/sys_windows.go +60 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/wrap.go +82 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/wrap_sys.go +113 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/wrap_vfs.go +181 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/testenv/env.go +55 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/util/json.go +54 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/util/json_v2.go +54 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/util/math.go +29 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/util/page.go +5 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/util/parse.go +142 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/util/pointer.go +3 -0
- package/govendor/github.com/ncruces/go-sqlite3/internal/util/reflect.go +10 -0
- package/govendor/github.com/ncruces/go-sqlite3/json.go +100 -0
- package/govendor/github.com/ncruces/go-sqlite3/json_v2.go +116 -0
- package/govendor/github.com/ncruces/go-sqlite3/pointer.go +13 -0
- package/govendor/github.com/ncruces/go-sqlite3/quote.go +140 -0
- package/govendor/github.com/ncruces/go-sqlite3/stmt.go +692 -0
- package/govendor/github.com/ncruces/go-sqlite3/time.go +360 -0
- package/govendor/github.com/ncruces/go-sqlite3/txn.go +374 -0
- package/govendor/github.com/ncruces/go-sqlite3/value.go +212 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/README.md +133 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/api.go +239 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/cksm.go +143 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/const.go +256 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/file.go +260 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/filename.go +187 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/lock.go +128 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/lock_other.go +23 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/map.go +63 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/map_other.go +13 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go +110 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go +131 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/os_dotlk.go +140 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/os_f2fs_linux.go +56 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go +117 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/os_ofd.go +59 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/os_std.go +50 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/os_std_alloc.go +19 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/os_std_atomic.go +9 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/os_std_rw.go +13 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/os_std_sync.go +9 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/os_std_temp.go +16 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/os_unix.go +108 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go +180 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/registry.go +48 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/shm.go +26 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go +258 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/shm_copy.go +90 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/shm_dotlk.go +168 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/shm_memlk.go +52 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/shm_ofd.go +185 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go +21 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/shm_windows.go +124 -0
- package/govendor/github.com/ncruces/go-sqlite3/vfs/vfs.go +455 -0
- package/govendor/github.com/ncruces/go-sqlite3/vtab.go +677 -0
- package/govendor/github.com/ncruces/go-sqlite3/wrap.go +48 -0
- package/govendor/github.com/ncruces/go-sqlite3-wasm/v6/LICENSE +18 -0
- package/govendor/github.com/ncruces/go-sqlite3-wasm/v6/README.md +11 -0
- package/govendor/github.com/ncruces/go-sqlite3-wasm/v6/go.mod +3 -0
- package/govendor/github.com/ncruces/go-sqlite3-wasm/v6/libc.go +215 -0
- package/govendor/github.com/ncruces/go-sqlite3-wasm/v6/sqlite3.dat +0 -0
- package/govendor/github.com/ncruces/go-sqlite3-wasm/v6/sqlite3.go +157903 -0
- package/govendor/github.com/ncruces/go-sqlite3-wasm/v6/tools.sh +38 -0
- package/govendor/github.com/ncruces/julianday/LICENSE +21 -0
- package/govendor/github.com/ncruces/julianday/README.md +9 -0
- package/govendor/github.com/ncruces/julianday/go.mod +3 -0
- package/govendor/github.com/ncruces/julianday/julianday.go +124 -0
- package/govendor/golang.org/x/sys/LICENSE +27 -0
- package/govendor/golang.org/x/sys/PATENTS +22 -0
- package/govendor/golang.org/x/sys/go.mod +3 -0
- package/govendor/golang.org/x/sys/unix/README.md +184 -0
- package/govendor/golang.org/x/sys/unix/affinity_linux.go +189 -0
- package/govendor/golang.org/x/sys/unix/aliases.go +13 -0
- package/govendor/golang.org/x/sys/unix/asm_aix_ppc64.s +17 -0
- package/govendor/golang.org/x/sys/unix/asm_bsd_386.s +27 -0
- package/govendor/golang.org/x/sys/unix/asm_bsd_amd64.s +27 -0
- package/govendor/golang.org/x/sys/unix/asm_bsd_arm.s +27 -0
- package/govendor/golang.org/x/sys/unix/asm_bsd_arm64.s +27 -0
- package/govendor/golang.org/x/sys/unix/asm_bsd_ppc64.s +29 -0
- package/govendor/golang.org/x/sys/unix/asm_bsd_riscv64.s +27 -0
- package/govendor/golang.org/x/sys/unix/asm_linux_386.s +65 -0
- package/govendor/golang.org/x/sys/unix/asm_linux_amd64.s +57 -0
- package/govendor/golang.org/x/sys/unix/asm_linux_arm.s +56 -0
- package/govendor/golang.org/x/sys/unix/asm_linux_arm64.s +50 -0
- package/govendor/golang.org/x/sys/unix/asm_linux_loong64.s +51 -0
- package/govendor/golang.org/x/sys/unix/asm_linux_mips64x.s +54 -0
- package/govendor/golang.org/x/sys/unix/asm_linux_mipsx.s +52 -0
- package/govendor/golang.org/x/sys/unix/asm_linux_ppc64x.s +42 -0
- package/govendor/golang.org/x/sys/unix/asm_linux_riscv64.s +47 -0
- package/govendor/golang.org/x/sys/unix/asm_linux_s390x.s +54 -0
- package/govendor/golang.org/x/sys/unix/asm_openbsd_mips64.s +29 -0
- package/govendor/golang.org/x/sys/unix/asm_solaris_amd64.s +17 -0
- package/govendor/golang.org/x/sys/unix/asm_zos_s390x.s +382 -0
- package/govendor/golang.org/x/sys/unix/auxv.go +36 -0
- package/govendor/golang.org/x/sys/unix/auxv_unsupported.go +13 -0
- package/govendor/golang.org/x/sys/unix/bluetooth_linux.go +36 -0
- package/govendor/golang.org/x/sys/unix/bpxsvc_zos.go +657 -0
- package/govendor/golang.org/x/sys/unix/bpxsvc_zos.s +192 -0
- package/govendor/golang.org/x/sys/unix/cap_freebsd.go +195 -0
- package/govendor/golang.org/x/sys/unix/constants.go +13 -0
- package/govendor/golang.org/x/sys/unix/dev_aix_ppc.go +26 -0
- package/govendor/golang.org/x/sys/unix/dev_aix_ppc64.go +28 -0
- package/govendor/golang.org/x/sys/unix/dev_darwin.go +24 -0
- package/govendor/golang.org/x/sys/unix/dev_dragonfly.go +30 -0
- package/govendor/golang.org/x/sys/unix/dev_freebsd.go +30 -0
- package/govendor/golang.org/x/sys/unix/dev_linux.go +42 -0
- package/govendor/golang.org/x/sys/unix/dev_netbsd.go +29 -0
- package/govendor/golang.org/x/sys/unix/dev_openbsd.go +29 -0
- package/govendor/golang.org/x/sys/unix/dev_zos.go +28 -0
- package/govendor/golang.org/x/sys/unix/dirent.go +102 -0
- package/govendor/golang.org/x/sys/unix/endian_big.go +9 -0
- package/govendor/golang.org/x/sys/unix/endian_little.go +9 -0
- package/govendor/golang.org/x/sys/unix/env_unix.go +31 -0
- package/govendor/golang.org/x/sys/unix/fcntl.go +36 -0
- package/govendor/golang.org/x/sys/unix/fcntl_darwin.go +24 -0
- package/govendor/golang.org/x/sys/unix/fcntl_linux_32bit.go +13 -0
- package/govendor/golang.org/x/sys/unix/fdset.go +27 -0
- package/govendor/golang.org/x/sys/unix/gccgo.go +59 -0
- package/govendor/golang.org/x/sys/unix/gccgo_c.c +44 -0
- package/govendor/golang.org/x/sys/unix/gccgo_linux_amd64.go +20 -0
- package/govendor/golang.org/x/sys/unix/ifreq_linux.go +145 -0
- package/govendor/golang.org/x/sys/unix/ioctl_linux.go +341 -0
- package/govendor/golang.org/x/sys/unix/ioctl_signed.go +74 -0
- package/govendor/golang.org/x/sys/unix/ioctl_unsigned.go +74 -0
- package/govendor/golang.org/x/sys/unix/ioctl_zos.go +71 -0
- package/govendor/golang.org/x/sys/unix/mkall.sh +250 -0
- package/govendor/golang.org/x/sys/unix/mkerrors.sh +807 -0
- package/govendor/golang.org/x/sys/unix/mmap_nomremap.go +13 -0
- package/govendor/golang.org/x/sys/unix/mremap.go +57 -0
- package/govendor/golang.org/x/sys/unix/pagesize_unix.go +15 -0
- package/govendor/golang.org/x/sys/unix/pledge_openbsd.go +111 -0
- package/govendor/golang.org/x/sys/unix/ptrace_darwin.go +11 -0
- package/govendor/golang.org/x/sys/unix/ptrace_ios.go +11 -0
- package/govendor/golang.org/x/sys/unix/race.go +30 -0
- package/govendor/golang.org/x/sys/unix/race0.go +25 -0
- package/govendor/golang.org/x/sys/unix/readdirent_getdents.go +12 -0
- package/govendor/golang.org/x/sys/unix/readdirent_getdirentries.go +19 -0
- package/govendor/golang.org/x/sys/unix/readv_unix.go +103 -0
- package/govendor/golang.org/x/sys/unix/sockcmsg_dragonfly.go +16 -0
- package/govendor/golang.org/x/sys/unix/sockcmsg_linux.go +85 -0
- package/govendor/golang.org/x/sys/unix/sockcmsg_unix.go +106 -0
- package/govendor/golang.org/x/sys/unix/sockcmsg_unix_other.go +46 -0
- package/govendor/golang.org/x/sys/unix/sockcmsg_zos.go +58 -0
- package/govendor/golang.org/x/sys/unix/symaddr_zos_s390x.s +75 -0
- package/govendor/golang.org/x/sys/unix/syscall.go +86 -0
- package/govendor/golang.org/x/sys/unix/syscall_aix.go +582 -0
- package/govendor/golang.org/x/sys/unix/syscall_aix_ppc.go +52 -0
- package/govendor/golang.org/x/sys/unix/syscall_aix_ppc64.go +83 -0
- package/govendor/golang.org/x/sys/unix/syscall_bsd.go +609 -0
- package/govendor/golang.org/x/sys/unix/syscall_darwin.go +711 -0
- package/govendor/golang.org/x/sys/unix/syscall_darwin_amd64.go +50 -0
- package/govendor/golang.org/x/sys/unix/syscall_darwin_arm64.go +50 -0
- package/govendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go +26 -0
- package/govendor/golang.org/x/sys/unix/syscall_dragonfly.go +359 -0
- package/govendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go +56 -0
- package/govendor/golang.org/x/sys/unix/syscall_freebsd.go +455 -0
- package/govendor/golang.org/x/sys/unix/syscall_freebsd_386.go +64 -0
- package/govendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go +64 -0
- package/govendor/golang.org/x/sys/unix/syscall_freebsd_arm.go +60 -0
- package/govendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go +60 -0
- package/govendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go +60 -0
- package/govendor/golang.org/x/sys/unix/syscall_hurd.go +30 -0
- package/govendor/golang.org/x/sys/unix/syscall_hurd_386.go +28 -0
- package/govendor/golang.org/x/sys/unix/syscall_illumos.go +78 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux.go +2574 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_386.go +313 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_alarm.go +12 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_amd64.go +144 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_amd64_gc.go +12 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_arm.go +218 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_arm64.go +188 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_gc.go +14 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_gc_386.go +16 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_gc_arm.go +13 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_gccgo_386.go +30 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_gccgo_arm.go +20 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_loong64.go +220 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_mips64x.go +187 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_mipsx.go +173 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_ppc.go +203 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go +114 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_riscv64.go +193 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_s390x.go +295 -0
- package/govendor/golang.org/x/sys/unix/syscall_linux_sparc64.go +111 -0
- package/govendor/golang.org/x/sys/unix/syscall_netbsd.go +388 -0
- package/govendor/golang.org/x/sys/unix/syscall_netbsd_386.go +37 -0
- package/govendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go +37 -0
- package/govendor/golang.org/x/sys/unix/syscall_netbsd_arm.go +37 -0
- package/govendor/golang.org/x/sys/unix/syscall_netbsd_arm64.go +37 -0
- package/govendor/golang.org/x/sys/unix/syscall_openbsd.go +346 -0
- package/govendor/golang.org/x/sys/unix/syscall_openbsd_386.go +41 -0
- package/govendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go +41 -0
- package/govendor/golang.org/x/sys/unix/syscall_openbsd_arm.go +41 -0
- package/govendor/golang.org/x/sys/unix/syscall_openbsd_arm64.go +41 -0
- package/govendor/golang.org/x/sys/unix/syscall_openbsd_libc.go +26 -0
- package/govendor/golang.org/x/sys/unix/syscall_openbsd_mips64.go +39 -0
- package/govendor/golang.org/x/sys/unix/syscall_openbsd_ppc64.go +41 -0
- package/govendor/golang.org/x/sys/unix/syscall_openbsd_riscv64.go +41 -0
- package/govendor/golang.org/x/sys/unix/syscall_solaris.go +1183 -0
- package/govendor/golang.org/x/sys/unix/syscall_solaris_amd64.go +27 -0
- package/govendor/golang.org/x/sys/unix/syscall_unix.go +619 -0
- package/govendor/golang.org/x/sys/unix/syscall_unix_gc.go +14 -0
- package/govendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go +22 -0
- package/govendor/golang.org/x/sys/unix/syscall_zos_s390x.go +3213 -0
- package/govendor/golang.org/x/sys/unix/sysvshm_linux.go +20 -0
- package/govendor/golang.org/x/sys/unix/sysvshm_unix.go +51 -0
- package/govendor/golang.org/x/sys/unix/sysvshm_unix_other.go +13 -0
- package/govendor/golang.org/x/sys/unix/timestruct.go +76 -0
- package/govendor/golang.org/x/sys/unix/unveil_openbsd.go +51 -0
- package/govendor/golang.org/x/sys/unix/vgetrandom_linux.go +13 -0
- package/govendor/golang.org/x/sys/unix/vgetrandom_unsupported.go +11 -0
- package/govendor/golang.org/x/sys/unix/xattr_bsd.go +280 -0
- package/govendor/golang.org/x/sys/unix/zerrors_aix_ppc.go +1384 -0
- package/govendor/golang.org/x/sys/unix/zerrors_aix_ppc64.go +1385 -0
- package/govendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go +1922 -0
- package/govendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go +1922 -0
- package/govendor/golang.org/x/sys/unix/zerrors_dragonfly_amd64.go +1737 -0
- package/govendor/golang.org/x/sys/unix/zerrors_freebsd_386.go +2042 -0
- package/govendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go +2039 -0
- package/govendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go +2033 -0
- package/govendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go +2033 -0
- package/govendor/golang.org/x/sys/unix/zerrors_freebsd_riscv64.go +2147 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux.go +4209 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux_386.go +882 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +882 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux_arm.go +887 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +884 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux_loong64.go +874 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux_mips.go +888 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +888 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +888 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +888 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux_ppc.go +940 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +944 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +944 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +884 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +943 -0
- package/govendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +992 -0
- package/govendor/golang.org/x/sys/unix/zerrors_netbsd_386.go +1779 -0
- package/govendor/golang.org/x/sys/unix/zerrors_netbsd_amd64.go +1769 -0
- package/govendor/golang.org/x/sys/unix/zerrors_netbsd_arm.go +1758 -0
- package/govendor/golang.org/x/sys/unix/zerrors_netbsd_arm64.go +1769 -0
- package/govendor/golang.org/x/sys/unix/zerrors_openbsd_386.go +1905 -0
- package/govendor/golang.org/x/sys/unix/zerrors_openbsd_amd64.go +1905 -0
- package/govendor/golang.org/x/sys/unix/zerrors_openbsd_arm.go +1905 -0
- package/govendor/golang.org/x/sys/unix/zerrors_openbsd_arm64.go +1905 -0
- package/govendor/golang.org/x/sys/unix/zerrors_openbsd_mips64.go +1905 -0
- package/govendor/golang.org/x/sys/unix/zerrors_openbsd_ppc64.go +1904 -0
- package/govendor/golang.org/x/sys/unix/zerrors_openbsd_riscv64.go +1903 -0
- package/govendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go +1556 -0
- package/govendor/golang.org/x/sys/unix/zerrors_zos_s390x.go +990 -0
- package/govendor/golang.org/x/sys/unix/zptrace_armnn_linux.go +40 -0
- package/govendor/golang.org/x/sys/unix/zptrace_linux_arm64.go +17 -0
- package/govendor/golang.org/x/sys/unix/zptrace_mipsnn_linux.go +49 -0
- package/govendor/golang.org/x/sys/unix/zptrace_mipsnnle_linux.go +49 -0
- package/govendor/golang.org/x/sys/unix/zptrace_x86_linux.go +79 -0
- package/govendor/golang.org/x/sys/unix/zsymaddr_zos_s390x.s +364 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go +1461 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go +1420 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gc.go +1188 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gccgo.go +1069 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go +2728 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s +799 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go +2728 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s +799 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go +1666 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go +1886 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go +1886 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go +1886 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go +1886 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go +1886 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_illumos_amd64.go +101 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux.go +2267 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux_386.go +469 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go +636 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux_arm.go +584 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go +535 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go +469 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux_mips.go +636 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go +630 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go +619 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go +636 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go +641 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go +687 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go +687 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go +531 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go +478 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go +631 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go +1848 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go +1848 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go +1848 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go +1848 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go +2407 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s +719 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go +2407 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s +719 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go +2407 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s +719 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go +2407 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s +719 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go +2407 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s +719 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go +2407 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s +862 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go +2407 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s +719 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go +2217 -0
- package/govendor/golang.org/x/sys/unix/zsyscall_zos_s390x.go +3458 -0
- package/govendor/golang.org/x/sys/unix/zsysctl_openbsd_386.go +280 -0
- package/govendor/golang.org/x/sys/unix/zsysctl_openbsd_amd64.go +280 -0
- package/govendor/golang.org/x/sys/unix/zsysctl_openbsd_arm.go +280 -0
- package/govendor/golang.org/x/sys/unix/zsysctl_openbsd_arm64.go +280 -0
- package/govendor/golang.org/x/sys/unix/zsysctl_openbsd_mips64.go +280 -0
- package/govendor/golang.org/x/sys/unix/zsysctl_openbsd_ppc64.go +280 -0
- package/govendor/golang.org/x/sys/unix/zsysctl_openbsd_riscv64.go +281 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go +439 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_darwin_arm64.go +437 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_dragonfly_amd64.go +316 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go +393 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go +393 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go +393 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go +393 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_freebsd_riscv64.go +393 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_linux_386.go +470 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go +394 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_linux_arm.go +434 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go +337 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go +334 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_linux_mips.go +454 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go +384 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go +384 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go +454 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go +461 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go +433 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go +433 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go +338 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go +399 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go +413 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_netbsd_386.go +274 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_netbsd_amd64.go +274 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_netbsd_arm.go +274 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_netbsd_arm64.go +274 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_openbsd_386.go +219 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_openbsd_amd64.go +219 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_openbsd_arm.go +219 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_openbsd_arm64.go +218 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_openbsd_mips64.go +221 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_openbsd_ppc64.go +217 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_openbsd_riscv64.go +218 -0
- package/govendor/golang.org/x/sys/unix/zsysnum_zos_s390x.go +2852 -0
- package/govendor/golang.org/x/sys/unix/ztypes_aix_ppc.go +353 -0
- package/govendor/golang.org/x/sys/unix/ztypes_aix_ppc64.go +357 -0
- package/govendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go +878 -0
- package/govendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go +878 -0
- package/govendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go +473 -0
- package/govendor/golang.org/x/sys/unix/ztypes_freebsd_386.go +651 -0
- package/govendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go +656 -0
- package/govendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go +642 -0
- package/govendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go +636 -0
- package/govendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go +638 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux.go +6547 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux_386.go +747 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux_amd64.go +761 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux_arm.go +741 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux_arm64.go +740 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux_loong64.go +741 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux_mips.go +746 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux_mips64.go +743 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go +743 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go +746 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux_ppc.go +754 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go +749 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go +749 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go +828 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux_s390x.go +763 -0
- package/govendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go +744 -0
- package/govendor/golang.org/x/sys/unix/ztypes_netbsd_386.go +585 -0
- package/govendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go +593 -0
- package/govendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go +590 -0
- package/govendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go +593 -0
- package/govendor/golang.org/x/sys/unix/ztypes_openbsd_386.go +568 -0
- package/govendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go +568 -0
- package/govendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go +575 -0
- package/govendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go +568 -0
- package/govendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go +568 -0
- package/govendor/golang.org/x/sys/unix/ztypes_openbsd_ppc64.go +570 -0
- package/govendor/golang.org/x/sys/unix/ztypes_openbsd_riscv64.go +570 -0
- package/govendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go +516 -0
- package/govendor/golang.org/x/sys/unix/ztypes_zos_s390x.go +552 -0
- package/govendor/golang.org/x/sys/windows/aliases.go +13 -0
- package/govendor/golang.org/x/sys/windows/dll_windows.go +380 -0
- package/govendor/golang.org/x/sys/windows/env_windows.go +57 -0
- package/govendor/golang.org/x/sys/windows/eventlog.go +20 -0
- package/govendor/golang.org/x/sys/windows/exec_windows.go +248 -0
- package/govendor/golang.org/x/sys/windows/memory_windows.go +48 -0
- package/govendor/golang.org/x/sys/windows/mkerrors.bash +70 -0
- package/govendor/golang.org/x/sys/windows/mkknownfolderids.bash +27 -0
- package/govendor/golang.org/x/sys/windows/mksyscall.go +9 -0
- package/govendor/golang.org/x/sys/windows/race.go +30 -0
- package/govendor/golang.org/x/sys/windows/race0.go +25 -0
- package/govendor/golang.org/x/sys/windows/security_windows.go +1537 -0
- package/govendor/golang.org/x/sys/windows/service.go +257 -0
- package/govendor/golang.org/x/sys/windows/setupapi_windows.go +1425 -0
- package/govendor/golang.org/x/sys/windows/str.go +22 -0
- package/govendor/golang.org/x/sys/windows/syscall.go +104 -0
- package/govendor/golang.org/x/sys/windows/syscall_windows.go +1952 -0
- package/govendor/golang.org/x/sys/windows/types_windows.go +4058 -0
- package/govendor/golang.org/x/sys/windows/types_windows_386.go +35 -0
- package/govendor/golang.org/x/sys/windows/types_windows_amd64.go +34 -0
- package/govendor/golang.org/x/sys/windows/types_windows_arm.go +35 -0
- package/govendor/golang.org/x/sys/windows/types_windows_arm64.go +34 -0
- package/govendor/golang.org/x/sys/windows/zerrors_windows.go +9468 -0
- package/govendor/golang.org/x/sys/windows/zknownfolderids_windows.go +149 -0
- package/govendor/golang.org/x/sys/windows/zsyscall_windows.go +4828 -0
- package/govendor/manifest.txt +7 -0
- package/package.json +7 -1
- package/tsparser/check.go +157 -0
- package/tsparser/main.go +30 -1
- package/types/typenative-go.d.ts +15159 -5
- package/types/typenative-npm.d.ts +2640 -34
- package/types/typenative.d.ts +651 -8
package/bin/transpiler.js
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
|
+
import { createNodeRegistry } from './node/registry.js';
|
|
2
|
+
import { registerPath } from './node/path.js';
|
|
3
|
+
import { registerFs } from './node/fs.js';
|
|
4
|
+
import { registerUrl } from './node/url.js';
|
|
5
|
+
import { registerOs } from './node/os.js';
|
|
6
|
+
import { registerChildProcess } from './node/child_process.js';
|
|
7
|
+
import { registerReadline } from './node/readline.js';
|
|
8
|
+
import { registerCore, registerConstants } from './node/core.js';
|
|
9
|
+
import { registerBuffer } from './node/buffer.js';
|
|
10
|
+
import { registerProcess } from './node/process.js';
|
|
11
|
+
import { registerUtil } from './node/util.js';
|
|
12
|
+
import { registerUtilExtra } from './node/util_extra.js';
|
|
13
|
+
import { registerConsole } from './node/console.js';
|
|
14
|
+
import { registerAssert } from './node/assert.js';
|
|
15
|
+
import { registerPunycode } from './node/punycode.js';
|
|
16
|
+
import { registerQuerystring } from './node/querystring.js';
|
|
17
|
+
import { registerStringDecoder } from './node/string_decoder.js';
|
|
18
|
+
import { registerCrypto } from './node/crypto.js';
|
|
19
|
+
import { registerCryptoKeys } from './node/crypto_keys.js';
|
|
20
|
+
import { registerZlib } from './node/zlib.js';
|
|
21
|
+
import { registerTimers } from './node/timers.js';
|
|
22
|
+
import { registerEvents } from './node/events.js';
|
|
23
|
+
import { registerStream } from './node/stream.js';
|
|
24
|
+
import { registerDiagnosticsChannel } from './node/diagnostics_channel.js';
|
|
25
|
+
import { registerNet } from './node/net.js';
|
|
26
|
+
import { registerDns } from './node/dns.js';
|
|
27
|
+
import { registerHttp } from './node/http.js';
|
|
28
|
+
import { registerTls } from './node/tls.js';
|
|
29
|
+
import { registerDgram } from './node/dgram.js';
|
|
30
|
+
import { registerSqlite } from './node/sqlite.js';
|
|
31
|
+
import { registerFetch } from './node/fetch.js';
|
|
32
|
+
import { registerStreamWeb } from './node/stream_web.js';
|
|
33
|
+
import { registerAsyncHooks } from './node/async_hooks.js';
|
|
34
|
+
import { registerTest } from './node/test.js';
|
|
35
|
+
import { registerWorkerThreads } from './node/worker_threads.js';
|
|
36
|
+
import { registerCluster } from './node/cluster.js';
|
|
37
|
+
import { registerHttp2 } from './node/http2.js';
|
|
38
|
+
import { registerSystemExtra } from './node/system_extra.js';
|
|
39
|
+
import { goStdlib } from './go/stdlib.generated.js';
|
|
40
|
+
import { goPackage, goTypeInfo as goMetaTypeInfo, hasGoPackage, isGoNumeric as isGoNumericType } from './go/metadata.js';
|
|
41
|
+
import { bindConvertServices, initConvertPaths, isGoBasicKind, isGoNumericKind, toGo as toGoType, fromGo as fromGoType } from './go/convert.js';
|
|
42
|
+
import { goPathAliases } from './go/overrides.js';
|
|
43
|
+
import { bindBridgeServices, resetGoBridge, initKnownPaths, goLocalType, ensureGoTypePackages, registerGoBuiltin, registerGoImport, goQualifier, goPathOf, goMemberOf, visitGoCall, visitGoMethod, visitGoNew, inferGoType, goFieldType, isGoRefCall, goTupleTypes, goCallbackResultType, ensureGoQualifier } from './go/bridge.js';
|
|
1
44
|
// Shim over the removed `typescript` import: predicates and kind names operate
|
|
2
45
|
// on the normalized JSON AST ({kind: 'Xxx', ...} plain objects) produced by
|
|
3
46
|
// src/parse-node.ts (Node) or the tsparser Go tool (self-hosted).
|
|
@@ -242,10 +285,16 @@ const SyntaxKind = {
|
|
|
242
285
|
// Local replacement for nanoid's customAlphabet — must stay transpilable by
|
|
243
286
|
// TypeNative itself (no dependencies), only using mapped String methods.
|
|
244
287
|
const GO_SAFE_ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
288
|
+
// Ids come from a Park–Miller generator reset for every transpilation, so the
|
|
289
|
+
// same source always gives the same Go (reproducible builds; the self-hosted
|
|
290
|
+
// compiler's output can be compared with the Node CLI's). The products stay
|
|
291
|
+
// below 2^46, exact in a float64 in both runtimes.
|
|
292
|
+
let goSafeIdSeed = 1;
|
|
245
293
|
function goSafeId() {
|
|
246
294
|
let id = '';
|
|
247
295
|
for (let i = 0; i < 8; i++) {
|
|
248
|
-
|
|
296
|
+
goSafeIdSeed = (goSafeIdSeed * 16807) % 2147483647;
|
|
297
|
+
id += GO_SAFE_ALPHABET.charAt(goSafeIdSeed % GO_SAFE_ALPHABET.length);
|
|
249
298
|
}
|
|
250
299
|
return id;
|
|
251
300
|
}
|
|
@@ -260,6 +309,8 @@ const mainPackageVariables = [];
|
|
|
260
309
|
// True while emitting a separate Go file, whose top-level statements sit at package scope
|
|
261
310
|
let emittingModuleFile = false;
|
|
262
311
|
const importedPackages = new Set();
|
|
312
|
+
// Side-effect `import 'go:time/tzdata'` packages, imported as `_`
|
|
313
|
+
const goSideEffectImports = new Set();
|
|
263
314
|
let outsideNodes = [];
|
|
264
315
|
const classNames = new Set();
|
|
265
316
|
let promiseResolveName = '';
|
|
@@ -305,7 +356,7 @@ const referenceArrays = new Set();
|
|
|
305
356
|
function setVarGoType(name, goType) {
|
|
306
357
|
if (!name)
|
|
307
358
|
return;
|
|
308
|
-
if (goType && goType.startsWith('[]') && goType !== '[]interface{}') {
|
|
359
|
+
if (goType && goType.startsWith('[]') && goType !== '[]interface{}' && goType !== '[]byte') {
|
|
309
360
|
variableGoTypes.set(name, `*${goType}`);
|
|
310
361
|
referenceArrays.add(name);
|
|
311
362
|
return;
|
|
@@ -353,6 +404,13 @@ const classStaticProps = new Set();
|
|
|
353
404
|
let fileResolver = null;
|
|
354
405
|
// Directory of the file currently being processed (null = main entry file)
|
|
355
406
|
let currentFileDir = null;
|
|
407
|
+
// Name of the file currently being parsed, as diagnostics and line directives show it
|
|
408
|
+
let currentFileName = '';
|
|
409
|
+
// Names of the parsed source files, indexed by the fileIndex stamped on each
|
|
410
|
+
// SourceFile node (line markers and diagnostics refer to them)
|
|
411
|
+
const sourceFileNames = [];
|
|
412
|
+
// Errors found while transpiling (unsupported syntax, unresolved imports)
|
|
413
|
+
const diagnostics = [];
|
|
356
414
|
// Tracks already-included files by a stable key to prevent duplicates/cycles
|
|
357
415
|
const includedLocalImports = new Set();
|
|
358
416
|
// Collects Go source files generated from local TS imports (filename → content)
|
|
@@ -409,6 +467,8 @@ function parseSource(code) {
|
|
|
409
467
|
lowerWeakCollections(sourceFile);
|
|
410
468
|
lowerGlobalThis(sourceFile);
|
|
411
469
|
linkParents(sourceFile, undefined);
|
|
470
|
+
sourceFile.fileIndex = sourceFileNames.length;
|
|
471
|
+
sourceFileNames.push(currentFileName);
|
|
412
472
|
for (const stmt of sourceFile.statements ?? []) {
|
|
413
473
|
if (!stmt.name || !isIdentifier(stmt.name))
|
|
414
474
|
continue;
|
|
@@ -427,6 +487,115 @@ function parseSource(code) {
|
|
|
427
487
|
}
|
|
428
488
|
return sourceFile;
|
|
429
489
|
}
|
|
490
|
+
// Index (into sourceFileNames) of the file a node was parsed from, or -1
|
|
491
|
+
function sourceFileIndexOf(node) {
|
|
492
|
+
let current = node;
|
|
493
|
+
while (current && current.kind !== 'SourceFile')
|
|
494
|
+
current = current.parent;
|
|
495
|
+
return current && typeof current.fileIndex === 'number' ? current.fileIndex : -1;
|
|
496
|
+
}
|
|
497
|
+
// Line markers: generated statements and declarations start with
|
|
498
|
+
// /*tn@<file>:<line>:<column>*/, which applyLineDirectives turns into Go
|
|
499
|
+
// /*line file.ts:line:column*/ directives once the code is complete, so Go
|
|
500
|
+
// compile errors and stack traces point at the TypeScript source. The marker
|
|
501
|
+
// carries no file name until then: names like strings.ts would otherwise look
|
|
502
|
+
// like uses of a Go package to the import pruning.
|
|
503
|
+
// A line holding only this sentinel becomes //line <go file>:<next line>,
|
|
504
|
+
// which returns the following generated code (helpers) to its real Go position
|
|
505
|
+
const LINE_RESET = '/*tn@' + 'reset*/';
|
|
506
|
+
function lineMarker(node) {
|
|
507
|
+
if (!node?.line)
|
|
508
|
+
return '';
|
|
509
|
+
const fileIndex = sourceFileIndexOf(node);
|
|
510
|
+
if (fileIndex < 0)
|
|
511
|
+
return '';
|
|
512
|
+
return `/*tn@${fileIndex}:${node.line}:${node.column}*/`;
|
|
513
|
+
}
|
|
514
|
+
// Prefixes generated code with its node's line marker (after any leading
|
|
515
|
+
// whitespace, so the position applies to the code's first token)
|
|
516
|
+
function withLineMarker(node, code) {
|
|
517
|
+
if (!code.trim())
|
|
518
|
+
return code;
|
|
519
|
+
const marker = lineMarker(node);
|
|
520
|
+
if (!marker)
|
|
521
|
+
return code;
|
|
522
|
+
const indent = code.length - code.trimStart().length;
|
|
523
|
+
return code.slice(0, indent) + marker + code.slice(indent);
|
|
524
|
+
}
|
|
525
|
+
// Replaces line markers with Go line directives and resets with //line
|
|
526
|
+
// directives back to the generated file (goFile, as Go should report it).
|
|
527
|
+
// One statement often spans several Go lines (assert → if + panic): every
|
|
528
|
+
// unmarked line after a marker repeats its directive, so those lines report
|
|
529
|
+
// the statement's line instead of counting on from it.
|
|
530
|
+
function applyLineDirectives(code, goFile) {
|
|
531
|
+
const lines = code.split('\n');
|
|
532
|
+
let pin = '';
|
|
533
|
+
// Inside a multi-line raw string or block comment, where nothing may be inserted
|
|
534
|
+
let state = '';
|
|
535
|
+
for (let index = 0; index < lines.length; index++) {
|
|
536
|
+
const text = lines[index];
|
|
537
|
+
if (text === LINE_RESET) {
|
|
538
|
+
lines[index] = `//line ${goFile}:${index + 2}`;
|
|
539
|
+
pin = '';
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
const startState = state;
|
|
543
|
+
if (text.includes('`') || text.includes('/*') || text.includes('*/'))
|
|
544
|
+
state = goLineEndState(text, state);
|
|
545
|
+
if (text.includes('/*tn@')) {
|
|
546
|
+
// (regex literals inline: a /g regex held in a variable compiles to a
|
|
547
|
+
// replace-first in the self-hosted compiler). Later lines repeat the
|
|
548
|
+
// line's last directive; text like "/*tn@" in a string matches nothing
|
|
549
|
+
lines[index] = text.replace(/\/\*tn@(\d+):(\d+):(\d+)\*\//g, (_match, file, line, column) => {
|
|
550
|
+
pin = `/*line ${sourceFileNames[Number(file)]}:${line}:${column}*/`;
|
|
551
|
+
return pin;
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
else if (pin && startState === '' && text.trim()) {
|
|
555
|
+
const indent = text.length - text.trimStart().length;
|
|
556
|
+
lines[index] = text.slice(0, indent) + pin + text.slice(indent);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return lines.join('\n');
|
|
560
|
+
}
|
|
561
|
+
// The multi-line context after a Go line that starts in state: '' (code),
|
|
562
|
+
// '`' (inside a raw string) or '*' (inside a block comment)
|
|
563
|
+
function goLineEndState(line, state) {
|
|
564
|
+
let rest = line;
|
|
565
|
+
if (state !== '') {
|
|
566
|
+
const close = state === '`' ? '`' : '*/';
|
|
567
|
+
const end = rest.indexOf(close);
|
|
568
|
+
if (end < 0)
|
|
569
|
+
return state;
|
|
570
|
+
rest = rest.slice(end + close.length);
|
|
571
|
+
}
|
|
572
|
+
// Go literals and comments that end on this line, matched leftmost-first
|
|
573
|
+
// like Go's scanner (a quote inside a raw string is not a string): what's
|
|
574
|
+
// left after them is an unclosed raw string or comment
|
|
575
|
+
rest = rest.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`[^`]*`|\/\*.*?\*\/|\/\/.*/g, ' ');
|
|
576
|
+
const rawString = rest.indexOf('`');
|
|
577
|
+
const comment = rest.indexOf('/*');
|
|
578
|
+
if (rawString < 0 && comment < 0)
|
|
579
|
+
return '';
|
|
580
|
+
if (comment < 0 || (rawString >= 0 && rawString < comment))
|
|
581
|
+
return '`';
|
|
582
|
+
return '*';
|
|
583
|
+
}
|
|
584
|
+
// Records a transpile error at the node (or its nearest positioned ancestor)
|
|
585
|
+
function reportError(node, message) {
|
|
586
|
+
let positioned = node;
|
|
587
|
+
while (positioned && !positioned.line)
|
|
588
|
+
positioned = positioned.parent;
|
|
589
|
+
const fileIndex = sourceFileIndexOf(node);
|
|
590
|
+
const line = positioned ? positioned.line : 0;
|
|
591
|
+
const column = positioned ? positioned.column : 0;
|
|
592
|
+
diagnostics.push({
|
|
593
|
+
file: fileIndex >= 0 ? sourceFileNames[fileIndex] : currentFileName,
|
|
594
|
+
line: line,
|
|
595
|
+
column: column,
|
|
596
|
+
message: message
|
|
597
|
+
});
|
|
598
|
+
}
|
|
430
599
|
// `this: T` parameters only type `this`; they are not real parameters
|
|
431
600
|
function stripThisParameters(node) {
|
|
432
601
|
if (Array.isArray(node.parameters)) {
|
|
@@ -789,23 +958,40 @@ const usedHelpers = new Set();
|
|
|
789
958
|
// per-file import lists can exclude them (helpers are emitted once, in the
|
|
790
959
|
// main file, which carries these imports instead).
|
|
791
960
|
const helperProvidedPackages = new Set();
|
|
961
|
+
// GOOS of the current transpilation (see platformHelper)
|
|
962
|
+
let targetGoos = 'linux';
|
|
792
963
|
// Whether the file being parsed is .tsx
|
|
793
964
|
let currentFileIsTsx = false;
|
|
794
965
|
export function transpileToNative(code, options) {
|
|
795
966
|
fileResolver = options?.readFile ?? null;
|
|
796
967
|
parseFunction = options?.parse ?? defaultParseFunction;
|
|
797
968
|
currentFileIsTsx = !!options?.tsx;
|
|
969
|
+
currentFileName = options?.fileName ?? (currentFileIsTsx ? 'main.tsx' : 'main.ts');
|
|
970
|
+
const goFile = options?.goFile ?? 'code.go';
|
|
971
|
+
targetGoos = options?.goos ?? 'linux';
|
|
972
|
+
sourceFileNames.length = 0;
|
|
973
|
+
diagnostics.length = 0;
|
|
974
|
+
goSafeIdSeed = 1;
|
|
975
|
+
consoleGroupsUsed = code.includes('console.group');
|
|
798
976
|
declaredFunctions.clear();
|
|
799
977
|
declaredInterfaces.clear();
|
|
800
978
|
declaredTypeAliases.clear();
|
|
801
979
|
variableTypeNodes.clear();
|
|
980
|
+
goRefVariables.clear();
|
|
802
981
|
narrowedVariables.clear();
|
|
803
|
-
|
|
982
|
+
bindNodeRegistryServices();
|
|
983
|
+
nodeLocalNames.clear();
|
|
984
|
+
goPackageLocalNames.clear();
|
|
985
|
+
resetGoBridge();
|
|
986
|
+
initKnownPaths();
|
|
987
|
+
initConvertPaths();
|
|
988
|
+
nodeParentClassKeys.clear();
|
|
804
989
|
asyncFunctionNames.clear();
|
|
805
990
|
generatorFunctionNames.clear();
|
|
806
991
|
currentFileDir = null;
|
|
807
992
|
const sourceFile = parseSource(code);
|
|
808
993
|
importedPackages.clear();
|
|
994
|
+
goSideEffectImports.clear();
|
|
809
995
|
outsideNodes = [];
|
|
810
996
|
classNames.clear();
|
|
811
997
|
promiseResolveName = '';
|
|
@@ -828,6 +1014,7 @@ export function transpileToNative(code, options) {
|
|
|
828
1014
|
classGetterMethods.clear();
|
|
829
1015
|
classSetterMethods.clear();
|
|
830
1016
|
classIteratorMethods.clear();
|
|
1017
|
+
seedNodeClassTables();
|
|
831
1018
|
declaredClasses.clear();
|
|
832
1019
|
classParentNames.clear();
|
|
833
1020
|
referenceArrays.clear();
|
|
@@ -835,29 +1022,58 @@ export function transpileToNative(code, options) {
|
|
|
835
1022
|
helperProvidedPackages.clear();
|
|
836
1023
|
mainPackageVariables.length = 0;
|
|
837
1024
|
const transpiledCode = visit(sourceFile, { addFunctionOutside: true });
|
|
838
|
-
const transpiledCodeOutside = outsideNodes.map((n) => visit(n, { isOutside: true })).join('\n');
|
|
1025
|
+
const transpiledCodeOutside = outsideNodes.map((n) => withLineMarker(n, visit(n, { isOutside: true }))).join('\n');
|
|
839
1026
|
// Keep the process alive until detached async work and timers settle
|
|
840
1027
|
const drains = [];
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
if (usedHelpers.has('
|
|
846
|
-
drains.push('
|
|
847
|
-
const drainCode = drains.length > 0 ? `\n\t${drains.join('\n\t')}` : '';
|
|
1028
|
+
// The event loop runs until no timers, async work or open handles remain
|
|
1029
|
+
if (usedHelpers.has('eventLoop'))
|
|
1030
|
+
drains.push('TnRunLoop()');
|
|
1031
|
+
// process.exitCode
|
|
1032
|
+
if (usedHelpers.has('nodeProcess'))
|
|
1033
|
+
drains.push('TnProcessExit()');
|
|
1034
|
+
const drainCode = drains.length > 0 ? `\n${LINE_RESET}\n\t${drains.join('\n\t')}` : '';
|
|
1035
|
+
// Go bridge types are qualified by path internally (net/http.Cookie)
|
|
1036
|
+
const mainCode = localizeGoPaths(transpiledCode);
|
|
1037
|
+
const outsideCode = localizeGoPaths(transpiledCodeOutside);
|
|
1038
|
+
const packageVariables = mainPackageVariables.map((v) => localizeGoPaths(v));
|
|
1039
|
+
// Go bridge packages pulled in for a type that never got emitted
|
|
1040
|
+
// (a method's result type, a param type needing no conversion) would be
|
|
1041
|
+
// unused imports, which Go rejects.
|
|
1042
|
+
const emitted = stripGoStrings(`${mainCode}\n${outsideCode}\n${packageVariables.join('\n')}`);
|
|
1043
|
+
const helpersCode = emitGoHelpers();
|
|
1044
|
+
const mainImports = [...importedPackages].filter((pkg) => {
|
|
1045
|
+
if (!hasGoPackage(pkg))
|
|
1046
|
+
return true;
|
|
1047
|
+
const pkgName = goPathAliases[pkg] ?? pkg.split('/').pop();
|
|
1048
|
+
return referencesQualifier(emitted, pkgName) || referencesQualifier(helpersCode, pkgName);
|
|
1049
|
+
});
|
|
1050
|
+
for (const pkg of goSideEffectImports) {
|
|
1051
|
+
if (!mainImports.includes(pkg))
|
|
1052
|
+
mainImports.push(pkg);
|
|
1053
|
+
}
|
|
1054
|
+
const importLine = (pkg) => goSideEffectImports.has(pkg) && !importedPackages.has(pkg)
|
|
1055
|
+
? `import _ "${pkg}"`
|
|
1056
|
+
: goImportLine(pkg);
|
|
1057
|
+
// process.on('uncaughtException'): a throw in the top-level code is handed to it too
|
|
1058
|
+
const body = usedHelpers.has('nodeUncaught') ? `func() {\n\tdefer TnMainRecover()\n\t${mainCode.trim()}\n\t}()` : mainCode.trim();
|
|
848
1059
|
const main = `package main
|
|
849
1060
|
|
|
850
|
-
${
|
|
851
|
-
|
|
852
|
-
${mainPackageVariables.join('\n')}
|
|
1061
|
+
${mainImports.sort().map((pkg) => importLine(pkg)).join('\n')}
|
|
853
1062
|
|
|
1063
|
+
${packageVariables.join('\n')}
|
|
1064
|
+
${LINE_RESET}
|
|
854
1065
|
func main() {
|
|
855
|
-
${
|
|
1066
|
+
${body}${drainCode}
|
|
856
1067
|
}
|
|
857
1068
|
|
|
858
|
-
${
|
|
859
|
-
${
|
|
860
|
-
|
|
1069
|
+
${outsideCode.trim()}
|
|
1070
|
+
${LINE_RESET}
|
|
1071
|
+
${helpersCode}`.trimEnd();
|
|
1072
|
+
return {
|
|
1073
|
+
main: applyLineDirectives(main, goFile),
|
|
1074
|
+
files: localImportFiles,
|
|
1075
|
+
diagnostics: diagnostics.slice()
|
|
1076
|
+
};
|
|
861
1077
|
}
|
|
862
1078
|
export // Property map of an interface, including inherited properties (flattened —
|
|
863
1079
|
function collectInterfaceProperties(iface, into, seen = new Set()) {
|
|
@@ -896,7 +1112,7 @@ function visit(node, options = {}) {
|
|
|
896
1112
|
}
|
|
897
1113
|
if (isSourceFile(node)) {
|
|
898
1114
|
return (node.statements ?? [])
|
|
899
|
-
.map((n) => visit(n, { addFunctionOutside: true }))
|
|
1115
|
+
.map((n) => withLineMarker(n, visit(n, { addFunctionOutside: true })))
|
|
900
1116
|
.filter((n) => !!n)
|
|
901
1117
|
.join(options.inline ? '' : '\n\t');
|
|
902
1118
|
}
|
|
@@ -919,10 +1135,25 @@ function visit(node, options = {}) {
|
|
|
919
1135
|
}
|
|
920
1136
|
if (node.text === 'undefined')
|
|
921
1137
|
return 'nil';
|
|
1138
|
+
// resolve / reject passed as values (setTimeout(resolve, ms)): closures over the channel
|
|
1139
|
+
const isCallee = isCallExpression(node.parent) && node.parent.expression === node;
|
|
1140
|
+
if (!isCallee && promiseResolveName && node.text === promiseResolveName) {
|
|
1141
|
+
useHelper('promise');
|
|
1142
|
+
return 'TnResolver(ch)';
|
|
1143
|
+
}
|
|
1144
|
+
if (!isCallee && promiseRejectName && node.text === promiseRejectName) {
|
|
1145
|
+
useHelper('promise');
|
|
1146
|
+
return 'TnRejecter(ch)';
|
|
1147
|
+
}
|
|
922
1148
|
if (node.text === 'NaN' || node.text === 'Infinity') {
|
|
923
1149
|
importedPackages.add('math');
|
|
924
1150
|
return node.text === 'NaN' ? 'math.NaN()' : 'math.Inf(1)';
|
|
925
1151
|
}
|
|
1152
|
+
if (nodeLocalNames.size > 0 || nodeRegistry.globals[node.text]) {
|
|
1153
|
+
const nodeValue = visitNodeValue(node);
|
|
1154
|
+
if (nodeValue !== undefined)
|
|
1155
|
+
return nodeValue;
|
|
1156
|
+
}
|
|
926
1157
|
const goAlias = importAliases.get(node.text);
|
|
927
1158
|
if (goAlias)
|
|
928
1159
|
return goAlias;
|
|
@@ -1049,6 +1280,11 @@ function visit(node, options = {}) {
|
|
|
1049
1280
|
if (isStringLiteral(node.argumentExpression)) {
|
|
1050
1281
|
return `${visit(node.expression)}[${visit(node.argumentExpression)}]`;
|
|
1051
1282
|
}
|
|
1283
|
+
// Buffer / Uint8Array elements read as numbers
|
|
1284
|
+
if (targetType === '[]byte' && !isAssignmentTarget(node)) {
|
|
1285
|
+
useHelper('dynamic');
|
|
1286
|
+
return `float64(TnAt(${visit(node.expression)}, ${visit(node.argumentExpression)}))`;
|
|
1287
|
+
}
|
|
1052
1288
|
// Reading past the end of an array gives undefined in JS: the zero value here
|
|
1053
1289
|
if (targetType?.startsWith('[]') && !isAssignmentTarget(node)) {
|
|
1054
1290
|
useHelper('dynamic');
|
|
@@ -1057,9 +1293,28 @@ function visit(node, options = {}) {
|
|
|
1057
1293
|
return `${visit(node.expression)}[int(${visit(node.argumentExpression)})]`;
|
|
1058
1294
|
}
|
|
1059
1295
|
else if (isPropertyAccessExpression(node)) {
|
|
1296
|
+
if (isMockAccess(node)) {
|
|
1297
|
+
nodeRegistry.helper('nodeTest');
|
|
1298
|
+
return `TnMockOf(${visit(node.expression)})`;
|
|
1299
|
+
}
|
|
1300
|
+
if (isProcessEnv(node.expression) && !isCallee(node) && !isAssignmentTarget(node)) {
|
|
1301
|
+
useHelper('getenv');
|
|
1302
|
+
return `TnGetenv("${node.name.text}")`;
|
|
1303
|
+
}
|
|
1304
|
+
if (!isCallee(node) && !isAssignmentTarget(node)) {
|
|
1305
|
+
const nodeValue = visitNodeValue(node);
|
|
1306
|
+
if (nodeValue !== undefined)
|
|
1307
|
+
return nodeValue;
|
|
1308
|
+
}
|
|
1060
1309
|
const regexProperty = visitRegexProperty(node);
|
|
1061
1310
|
if (regexProperty)
|
|
1062
1311
|
return regexProperty;
|
|
1312
|
+
// Go package member or field on a Go value.
|
|
1313
|
+
if (!isCallee(node) || isCallee(node)) {
|
|
1314
|
+
const goProp = visitGoProperty(node);
|
|
1315
|
+
if (goProp !== undefined)
|
|
1316
|
+
return goProp;
|
|
1317
|
+
}
|
|
1063
1318
|
// import.meta.url — the file URL of the running program
|
|
1064
1319
|
if (isNewTargetNode(node.expression) && node.name.text === 'name')
|
|
1065
1320
|
return '__newTarget';
|
|
@@ -1137,20 +1392,34 @@ function visit(node, options = {}) {
|
|
|
1137
1392
|
return getAcessString(leftSide, rightSide, objectType);
|
|
1138
1393
|
}
|
|
1139
1394
|
else if (isVariableDeclaration(node)) {
|
|
1395
|
+
const nodeAlias = visitNodeAliasDeclaration(node);
|
|
1396
|
+
if (nodeAlias !== undefined)
|
|
1397
|
+
return nodeAlias;
|
|
1140
1398
|
// Object destructuring: const { x, y } = obj
|
|
1141
1399
|
if (isObjectBindingPattern(node.name) && node.initializer) {
|
|
1142
|
-
|
|
1400
|
+
let initExpr = visit(node.initializer);
|
|
1143
1401
|
const initType = inferExpressionType(node.initializer);
|
|
1144
1402
|
const parts = [];
|
|
1403
|
+
// Evaluate the initializer once (const { a, b } = new X())
|
|
1404
|
+
if ((node.name.elements ?? []).length > 1 && !isIdentifier(node.initializer)) {
|
|
1405
|
+
const source = getTempName('src');
|
|
1406
|
+
parts.push(`${source} := ${initExpr}`);
|
|
1407
|
+
initExpr = source;
|
|
1408
|
+
}
|
|
1145
1409
|
for (const el of node.name.elements ?? []) {
|
|
1146
1410
|
const localName = visit(el.name);
|
|
1147
1411
|
const rawProp = el.propertyName ?? el.name;
|
|
1148
1412
|
const propName = isIdentifier(rawProp) ? rawProp.text : visit(rawProp);
|
|
1413
|
+
const structName = initType ? initType.replace(/^\*/, '') : '';
|
|
1414
|
+
// Struct literal fields, or the properties of a class (Node classes included)
|
|
1149
1415
|
const fieldType = initType
|
|
1150
|
-
? getStructFieldGoType(
|
|
1416
|
+
? (getStructFieldGoType(structName, propName) ?? classPropertyTypes.get(structName)?.get(propName))
|
|
1151
1417
|
: undefined;
|
|
1152
1418
|
const defaultVal = el.initializer;
|
|
1153
|
-
if (defaultVal) {
|
|
1419
|
+
if (!defaultVal && classGetterMethods.has(`${structName}.${propName}`)) {
|
|
1420
|
+
parts.push(`${localName} := (${initExpr}).Get_${goFieldName(propName)}()`);
|
|
1421
|
+
}
|
|
1422
|
+
else if (defaultVal) {
|
|
1154
1423
|
// Missing property (nil) takes the default
|
|
1155
1424
|
const defType = inferExpressionType(defaultVal) ?? fieldType ?? 'interface{}';
|
|
1156
1425
|
const tmp = getTempName('destructure');
|
|
@@ -1172,8 +1441,22 @@ function visit(node, options = {}) {
|
|
|
1172
1441
|
: visit(node.initializer);
|
|
1173
1442
|
const tmpVar = getTempName('arr');
|
|
1174
1443
|
const elemType = initGoType?.startsWith('[]') ? initGoType.slice(2) : undefined;
|
|
1444
|
+
// Multi-value Go calls: each element asserts back to its own type
|
|
1445
|
+
const tupleTypes = goTupleTypes(unwrapParentheses(node.initializer));
|
|
1175
1446
|
const parts = [`${tmpVar} := ${initExpr}`];
|
|
1176
1447
|
node.name.elements.forEach((el, idx) => {
|
|
1448
|
+
const tupleType = tupleTypes?.[idx];
|
|
1449
|
+
if (tupleType && !isOmittedExpression(el) && isIdentifier(el.name) && !el.initializer) {
|
|
1450
|
+
const localName = visit(el.name);
|
|
1451
|
+
if (localName === '_' || localName.startsWith('_')) {
|
|
1452
|
+
parts.push(`_ = ${tmpVar}`);
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
ensureGoTypePackages(tupleType);
|
|
1456
|
+
parts.push(`${localName} := ${tmpVar}[${idx}].(${goLocalType(tupleType)})`);
|
|
1457
|
+
registerLocalVariable(localName, tupleType);
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1177
1460
|
if (isOmittedExpression(el))
|
|
1178
1461
|
return;
|
|
1179
1462
|
const bindEl = el;
|
|
@@ -1231,6 +1514,10 @@ function visit(node, options = {}) {
|
|
|
1231
1514
|
variableTypeNodes.set(node.name.text, node.type);
|
|
1232
1515
|
else
|
|
1233
1516
|
variableTypeNodes.delete(node.name.text);
|
|
1517
|
+
if (node.initializer && isGoRefCall(node.initializer))
|
|
1518
|
+
goRefVariables.add(node.name.text);
|
|
1519
|
+
else
|
|
1520
|
+
goRefVariables.delete(node.name.text);
|
|
1234
1521
|
if (node.type && !isInferredAny) {
|
|
1235
1522
|
variableGoTypes.set(node.name.text, getType(node.type));
|
|
1236
1523
|
}
|
|
@@ -1309,7 +1596,8 @@ function visit(node, options = {}) {
|
|
|
1309
1596
|
const packageType = type === ':' ? variableGoTypes.get(node.name.text) : type;
|
|
1310
1597
|
if (isMainTopLevel && packageType && packageType !== 'nil' && isIdentifier(node.name)) {
|
|
1311
1598
|
const name = getSafeName(node.name.text);
|
|
1312
|
-
|
|
1599
|
+
ensureGoTypePackages(packageType);
|
|
1600
|
+
mainPackageVariables.push(`var ${name} ${goLocalType(packageType)}`);
|
|
1313
1601
|
return initializer ? `${name} ${initializer}` : '';
|
|
1314
1602
|
}
|
|
1315
1603
|
const declName = isIdentifier(node.name) ? getSafeName(node.name.text) : visit(node.name);
|
|
@@ -1318,9 +1606,14 @@ function visit(node, options = {}) {
|
|
|
1318
1606
|
if (type === ':' &&
|
|
1319
1607
|
isIdentifier(node.name) &&
|
|
1320
1608
|
containsNode(node.initializer, (n) => isIdentifier(n) && n.text === node.name.text && !isFieldNameIdentifier(n))) {
|
|
1321
|
-
|
|
1609
|
+
const initType = inferExpressionType(node.initializer) ?? '';
|
|
1610
|
+
ensureGoTypePackages(initType);
|
|
1611
|
+
return `var ${declName} ${goLocalType(initType)}\n\t\t${declName} ${initializer}`;
|
|
1322
1612
|
}
|
|
1323
|
-
|
|
1613
|
+
const emitType = type === ':' ? type : goLocalType(type);
|
|
1614
|
+
if (type !== ':')
|
|
1615
|
+
ensureGoTypePackages(type);
|
|
1616
|
+
return `${type === ':' ? '' : 'var '}${declName} ${emitType}${type === ':' ? '' : ' '}${initializer}`;
|
|
1324
1617
|
}
|
|
1325
1618
|
else if (isCallExpression(node)) {
|
|
1326
1619
|
if (hasQuestionDot(node) ||
|
|
@@ -1333,6 +1626,26 @@ function visit(node, options = {}) {
|
|
|
1333
1626
|
return visitNullablePrimitiveOptionalCall(node, visit(node.expression.expression), receiverType);
|
|
1334
1627
|
}
|
|
1335
1628
|
}
|
|
1629
|
+
const nodeCall = visitNodeCall(node);
|
|
1630
|
+
if (nodeCall !== undefined)
|
|
1631
|
+
return nodeCall;
|
|
1632
|
+
const goCall = visitGoCall(node);
|
|
1633
|
+
if (goCall !== undefined)
|
|
1634
|
+
return goCall;
|
|
1635
|
+
// Method call on a Go-typed receiver (pkg.Type value).
|
|
1636
|
+
if (isPropertyAccessExpression(node.expression)) {
|
|
1637
|
+
const recvType = inferExpressionType(node.expression.expression);
|
|
1638
|
+
if (recvType && recvType.startsWith('chan ')) {
|
|
1639
|
+
const chanCall = visitGoChanCall(node, visit(node.expression.expression));
|
|
1640
|
+
if (chanCall !== undefined)
|
|
1641
|
+
return chanCall;
|
|
1642
|
+
}
|
|
1643
|
+
if (recvType && goMetaTypeInfo(recvType)) {
|
|
1644
|
+
const goMethod = visitGoMethod(node, visit(node.expression.expression), recvType);
|
|
1645
|
+
if (goMethod !== undefined)
|
|
1646
|
+
return goMethod;
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1336
1649
|
const recordCall = visitRecordFunctionCall(node);
|
|
1337
1650
|
if (recordCall)
|
|
1338
1651
|
return recordCall;
|
|
@@ -1387,7 +1700,12 @@ function visit(node, options = {}) {
|
|
|
1387
1700
|
const recv = visit(node.expression.expression);
|
|
1388
1701
|
const cbs = (node.arguments ?? []).map((a) => visit(a));
|
|
1389
1702
|
const cbExpr = cbs[0] ?? 'func(interface{}) {}';
|
|
1390
|
-
|
|
1703
|
+
useHelper('eventLoop');
|
|
1704
|
+
// A callback without parameters (then(() => …)) takes no value
|
|
1705
|
+
const takesValue = !(cb && (isArrowFunction(cb) || isFunctionExpression(cb)) && (cb.parameters ?? []).length === 0);
|
|
1706
|
+
const receive = takesValue ? `__v := <-(${recv})` : `<-(${recv})`;
|
|
1707
|
+
const invoke = takesValue ? `(${cbExpr})(__v)` : `(${cbExpr})()`;
|
|
1708
|
+
return `__tnWaitGroup.Add(1)\n\t\tgo func(__tnCtx TnAsyncContext) {\n\t\t\t${receive}\n\t\t\tTnLoopPost(func() {\n\t\t\t\tdefer __tnWaitGroup.Done()\n\t\t\t\tTnAsyncRun(__tnCtx, func() { ${invoke} })\n\t\t\t})\n\t\t}(TnAsyncSnapshot())`;
|
|
1391
1709
|
}
|
|
1392
1710
|
}
|
|
1393
1711
|
// Array.from(arrayLike, mapFn) — array-likes are { length } structs
|
|
@@ -1453,6 +1771,19 @@ function visit(node, options = {}) {
|
|
|
1453
1771
|
useHelper('dynamic');
|
|
1454
1772
|
return `float64(^TnInt32(${visit(node.operand)}))`;
|
|
1455
1773
|
}
|
|
1774
|
+
// -10n / -x on bigints (*big.Int)
|
|
1775
|
+
if (node.operator === 'MinusToken' && node.operand.kind === 'BigIntLiteral') {
|
|
1776
|
+
useHelper('bigint');
|
|
1777
|
+
return `TnBigInt("-${String(node.operand.text).replace(/n$/, '')}")`;
|
|
1778
|
+
}
|
|
1779
|
+
if (node.operator === 'MinusToken' && inferExpressionType(node.operand) === '*tnbig.Int') {
|
|
1780
|
+
return `new(tnbig.Int).Neg(${visit(node.operand)})`;
|
|
1781
|
+
}
|
|
1782
|
+
// -0: Go constant arithmetic has no negative zero
|
|
1783
|
+
if (node.operator === 'MinusToken' && isNumericLiteral(node.operand) && Number(node.operand.text) === 0) {
|
|
1784
|
+
importedPackages.add('math');
|
|
1785
|
+
return 'math.Copysign(0, -1)';
|
|
1786
|
+
}
|
|
1456
1787
|
return `${getOperatorText(node.operator)}${visit(node.operand)}`;
|
|
1457
1788
|
}
|
|
1458
1789
|
else if (isPostfixUnaryExpression(node)) {
|
|
@@ -1480,11 +1811,22 @@ function visit(node, options = {}) {
|
|
|
1480
1811
|
if (node.operatorToken.kind === 'QuestionQuestionToken') {
|
|
1481
1812
|
return visitNullishCoalescingExpression(node);
|
|
1482
1813
|
}
|
|
1814
|
+
if (node.operatorToken.kind === 'InstanceOfKeyword' &&
|
|
1815
|
+
isIdentifier(node.right) &&
|
|
1816
|
+
(node.right.text === 'Error' || BUILTIN_ERROR_NAMES.has(node.right.text)) &&
|
|
1817
|
+
!classNames.has(node.right.text)) {
|
|
1818
|
+
useHelper('error');
|
|
1819
|
+
return `TnIsErrorOf(${visit(node.left)}, ${toGoStringLiteral(node.right.text)})`;
|
|
1820
|
+
}
|
|
1483
1821
|
if (node.operatorToken.kind === 'InstanceOfKeyword' && isIdentifier(node.right)) {
|
|
1484
1822
|
// x instanceof C: the runtime type is C's pointer type
|
|
1485
1823
|
return `func() bool { _, ok := interface{}(${visit(node.left)}).(*${getSafeName(node.right.text)}); return ok }()`;
|
|
1486
1824
|
}
|
|
1487
1825
|
if (node.operatorToken.kind === 'InKeyword') {
|
|
1826
|
+
if (isProcessEnv(node.right)) {
|
|
1827
|
+
useHelper('nodeProcess');
|
|
1828
|
+
return `TnHasEnv(${visit(node.left)})`;
|
|
1829
|
+
}
|
|
1488
1830
|
if (isDynamicValue(node.right)) {
|
|
1489
1831
|
useHelper('dynamic');
|
|
1490
1832
|
return `TnHas(${visit(node.right)}, ${visit(node.left)})`;
|
|
@@ -1527,8 +1869,22 @@ function visit(node, options = {}) {
|
|
|
1527
1869
|
const bigRight = inferExpressionType(node.right);
|
|
1528
1870
|
if (bigLeft === '*tnbig.Int' || bigRight === '*tnbig.Int') {
|
|
1529
1871
|
useHelper('bigint');
|
|
1530
|
-
|
|
1531
|
-
|
|
1872
|
+
let bl = visit(node.left);
|
|
1873
|
+
let br = visit(node.right);
|
|
1874
|
+
// number | bigint values (node:sqlite's changes): === is true only for an
|
|
1875
|
+
// equal bigint; other operators convert the dynamic side
|
|
1876
|
+
const dynamicSide = bigLeft === 'interface{}' ? bl : bigRight === 'interface{}' ? br : '';
|
|
1877
|
+
if (dynamicSide) {
|
|
1878
|
+
const bigSide = dynamicSide === bl ? br : bl;
|
|
1879
|
+
if (op === '==')
|
|
1880
|
+
return `TnBigSame(${dynamicSide}, ${bigSide})`;
|
|
1881
|
+
if (op === '!=')
|
|
1882
|
+
return `!TnBigSame(${dynamicSide}, ${bigSide})`;
|
|
1883
|
+
if (dynamicSide === bl)
|
|
1884
|
+
bl = `TnToBigInt(${bl})`;
|
|
1885
|
+
else
|
|
1886
|
+
br = `TnToBigInt(${br})`;
|
|
1887
|
+
}
|
|
1532
1888
|
if (op === '==')
|
|
1533
1889
|
return `${bl}.Cmp(${br}) == 0`;
|
|
1534
1890
|
if (op === '!=')
|
|
@@ -1558,6 +1914,19 @@ function visit(node, options = {}) {
|
|
|
1558
1914
|
importedPackages.add('math');
|
|
1559
1915
|
return `math.Pow(${visit(node.left)}, ${visit(node.right)})`;
|
|
1560
1916
|
}
|
|
1917
|
+
// Named Go numerics (time.Duration): coerce the float64 side so Go's
|
|
1918
|
+
// operator type-checks (time.Second * 2 → time.Second * time.Duration(2)).
|
|
1919
|
+
if (['<', '>', '<=', '>=', '-', '*', '/', '+', '+=', '-=', '*=', '/='].includes(op)) {
|
|
1920
|
+
const numericCoercion = visitGoNumericArithmetic(node, op);
|
|
1921
|
+
if (numericCoercion)
|
|
1922
|
+
return numericCoercion;
|
|
1923
|
+
}
|
|
1924
|
+
// x.Kind() === reflect.String: the method result reads as a number
|
|
1925
|
+
if (op === '==' || op === '!=') {
|
|
1926
|
+
const numericEquality = visitGoNumericArithmetic(node, op);
|
|
1927
|
+
if (numericEquality)
|
|
1928
|
+
return numericEquality;
|
|
1929
|
+
}
|
|
1561
1930
|
if (op === '**=') {
|
|
1562
1931
|
importedPackages.add('math');
|
|
1563
1932
|
const target = visit(node.left);
|
|
@@ -1620,6 +1989,17 @@ function visit(node, options = {}) {
|
|
|
1620
1989
|
const other = isNewTargetNode(node.left) ? node.right : node.left;
|
|
1621
1990
|
return `__newTarget ${op} ${isIdentifier(other) ? toGoStringLiteral(other.text) : '""'}`;
|
|
1622
1991
|
}
|
|
1992
|
+
const nodeAssignment = visitNodeAssignment(node);
|
|
1993
|
+
if (nodeAssignment !== undefined)
|
|
1994
|
+
return nodeAssignment;
|
|
1995
|
+
const goFieldAssignment = visitGoFieldAssignment(node, op);
|
|
1996
|
+
if (goFieldAssignment !== undefined)
|
|
1997
|
+
return goFieldAssignment;
|
|
1998
|
+
// buf[i] = v on a Buffer / Uint8Array: bytes wrap modulo 256
|
|
1999
|
+
if (op === '=' && isElementAccessExpression(node.left) && inferExpressionType(node.left.expression) === '[]byte') {
|
|
2000
|
+
useHelper('buffer');
|
|
2001
|
+
return `${visit(node.left.expression)}[int(${visit(node.left.argumentExpression)})] = TnByte(${visit(node.right)})`;
|
|
2002
|
+
}
|
|
1623
2003
|
// obj[key] = v on any
|
|
1624
2004
|
if (op === '=' && isElementAccessExpression(node.left) && isDynamicValue(node.left.expression)) {
|
|
1625
2005
|
useHelper('dynamic');
|
|
@@ -1699,11 +2079,10 @@ function visit(node, options = {}) {
|
|
|
1699
2079
|
return '';
|
|
1700
2080
|
}
|
|
1701
2081
|
else if (isAwaitExpression(node)) {
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
}
|
|
1706
|
-
return `<-${visit(node.expression)}`;
|
|
2082
|
+
// Promises are channels: TnAwait keeps the event loop running while it waits
|
|
2083
|
+
// (a bare receive would block it, e.g. in async callbacks)
|
|
2084
|
+
useHelper('promise');
|
|
2085
|
+
return `TnAwait(${visit(node.expression)})`;
|
|
1707
2086
|
}
|
|
1708
2087
|
else if (isVariableDeclarationList(node)) {
|
|
1709
2088
|
return ((node.declarations ?? []).map((n) => visit(n)).join(options.inline ? ';' : ';\n\t') +
|
|
@@ -1718,7 +2097,7 @@ function visit(node, options = {}) {
|
|
|
1718
2097
|
!emittingModuleFile) {
|
|
1719
2098
|
useHelper('asyncWait');
|
|
1720
2099
|
useHelper('promise');
|
|
1721
|
-
return `__tnWaitGroup.Add(1)\n\t\
|
|
2100
|
+
return `__tnWaitGroup.Add(1)\n\t\tTnDetach(${visit(exprStmt)});\n\t`;
|
|
1722
2101
|
}
|
|
1723
2102
|
return visit(node.expression) + (options.inline ? '' : ';\n\t');
|
|
1724
2103
|
}
|
|
@@ -1748,10 +2127,17 @@ function visit(node, options = {}) {
|
|
|
1748
2127
|
iterNode.arguments.length > 0) {
|
|
1749
2128
|
iterNode = iterNode.arguments[0];
|
|
1750
2129
|
}
|
|
1751
|
-
|
|
2130
|
+
let iterExpr = isDynamicValue(iterNode)
|
|
1752
2131
|
? toGoValueOfType(iterNode, '[]interface{}')
|
|
1753
2132
|
: visit(iterNode, { inline: true });
|
|
1754
|
-
|
|
2133
|
+
let iterType = inferExpressionType(iterNode);
|
|
2134
|
+
// Node class instances iterate through their iterator method (URLSearchParams → entries())
|
|
2135
|
+
const nodeIterator = iterType ? nodeRegistry.iterators[iterType] : undefined;
|
|
2136
|
+
if (iterType && nodeIterator) {
|
|
2137
|
+
const cls = nodeRegistry.classes[nodeRegistry.classByGoType[iterType]];
|
|
2138
|
+
iterExpr = `${iterExpr}.${nodeIterator}()`;
|
|
2139
|
+
iterType = cls?.methods[nodeIterator] ?? iterType;
|
|
2140
|
+
}
|
|
1755
2141
|
if (iterType && iterType.startsWith('*TnMap[')) {
|
|
1756
2142
|
const valueType = extractMapValueType(iterType);
|
|
1757
2143
|
const isSet = valueType === 'struct{}';
|
|
@@ -1789,13 +2175,13 @@ function visit(node, options = {}) {
|
|
|
1789
2175
|
const thenTerm = node.elseStatement ? '' : ';';
|
|
1790
2176
|
const thenCode = withNarrowing(getNarrowedNames(node.expression, true), () => isBlock(node.thenStatement)
|
|
1791
2177
|
? visit(node.thenStatement, { inline: !!node.elseStatement })
|
|
1792
|
-
: `{\n${visit(node.thenStatement)}\n}${thenTerm}`);
|
|
2178
|
+
: `{\n${withLineMarker(node.thenStatement, visit(node.thenStatement))}\n}${thenTerm}`);
|
|
1793
2179
|
const condition = `if ${toGoCondition(node.expression)} ${thenCode}`;
|
|
1794
2180
|
if (node.elseStatement) {
|
|
1795
2181
|
// else-if chains stay chained; other single statements get a block
|
|
1796
2182
|
const elseCode = withNarrowing(getNarrowedNames(node.expression, false), () => isBlock(node.elseStatement) || isIfStatement(node.elseStatement)
|
|
1797
2183
|
? visit(node.elseStatement)
|
|
1798
|
-
: `{\n${visit(node.elseStatement)}\n};\n\t`);
|
|
2184
|
+
: `{\n${withLineMarker(node.elseStatement, visit(node.elseStatement))}\n};\n\t`);
|
|
1799
2185
|
return `${condition} else ${elseCode}`;
|
|
1800
2186
|
}
|
|
1801
2187
|
return condition;
|
|
@@ -1861,7 +2247,13 @@ function visit(node, options = {}) {
|
|
|
1861
2247
|
const enclosingFn = getEnclosingFunction(node);
|
|
1862
2248
|
const isAsync = enclosingFn?.modifiers?.some((m) => m.kind === 'AsyncKeyword');
|
|
1863
2249
|
const returnGoType = enclosingFn && !isAsync ? inferFunctionBodyReturnType(enclosingFn) : undefined;
|
|
1864
|
-
|
|
2250
|
+
// TS closures passed as Go callbacks return the Go result type
|
|
2251
|
+
const goCallbackResult = enclosingFn ? goCallbackResultType(enclosingFn) : undefined;
|
|
2252
|
+
const value = !node.expression
|
|
2253
|
+
? ''
|
|
2254
|
+
: goCallbackResult !== undefined
|
|
2255
|
+
? toGoType(visit(node.expression), inferExpressionType(node.expression) ?? '', goCallbackResult)
|
|
2256
|
+
: toGoValueOfType(node.expression, returnGoType);
|
|
1865
2257
|
const terminator = options.inline ? '' : ';\n\t';
|
|
1866
2258
|
if (tryReturn && enclosingFn === tryReturn.fn)
|
|
1867
2259
|
return tryAwareReturn(value) + terminator;
|
|
@@ -1918,7 +2310,11 @@ function visit(node, options = {}) {
|
|
|
1918
2310
|
if (!isBlock(node.body)) {
|
|
1919
2311
|
// void-style calls (console.*, assert) emit statements, not values
|
|
1920
2312
|
if (isVoidStatementCall(node.body) || !inferredRetType) {
|
|
1921
|
-
|
|
2313
|
+
// `(c) => (body += c)`: the parenthesized expression is a statement
|
|
2314
|
+
let statement = node.body;
|
|
2315
|
+
while (statement.kind === 'ParenthesizedExpression')
|
|
2316
|
+
statement = statement.expression;
|
|
2317
|
+
return `func(${parameterInfo.signature})${returnType} { ${visit(statement)}; }`;
|
|
1922
2318
|
}
|
|
1923
2319
|
return `func(${parameterInfo.signature})${returnType} { return ${visit(node.body)}; }`;
|
|
1924
2320
|
}
|
|
@@ -2090,7 +2486,7 @@ function visit(node, options = {}) {
|
|
|
2090
2486
|
let parentClassName = null;
|
|
2091
2487
|
for (const clause of node.heritageClauses ?? []) {
|
|
2092
2488
|
if (clause.token === 'ExtendsKeyword') {
|
|
2093
|
-
parentClassName = visit(clause.types[0].expression);
|
|
2489
|
+
parentClassName = nodeParentGoName(clause.types[0].expression) || visit(clause.types[0].expression);
|
|
2094
2490
|
}
|
|
2095
2491
|
}
|
|
2096
2492
|
if (parentClassName && parentClassName !== 'Error') {
|
|
@@ -2126,13 +2522,14 @@ function visit(node, options = {}) {
|
|
|
2126
2522
|
if ((node.heritageClauses ?? [])) {
|
|
2127
2523
|
for (const clause of (node.heritageClauses ?? [])) {
|
|
2128
2524
|
if (clause.token === 'ExtendsKeyword') {
|
|
2129
|
-
parentClass = visit(clause.types[0].expression);
|
|
2525
|
+
parentClass = nodeParentGoName(clause.types[0].expression) || visit(clause.types[0].expression);
|
|
2130
2526
|
}
|
|
2131
2527
|
}
|
|
2132
2528
|
}
|
|
2133
2529
|
const extendsError = parentClass === 'Error';
|
|
2134
2530
|
if (extendsError)
|
|
2135
2531
|
useHelper('error');
|
|
2532
|
+
applyNodeHookTypes(parentClass, node);
|
|
2136
2533
|
if (parentClass && !extendsError)
|
|
2137
2534
|
classParentNames.set(name, parentClass);
|
|
2138
2535
|
const isAbstractClass = (node.modifiers ?? []).some((m) => m.kind === 'AbstractKeyword');
|
|
@@ -2184,7 +2581,7 @@ function visit(node, options = {}) {
|
|
|
2184
2581
|
const parentInit = parentClass && !extendsError && !superCalledInCtor
|
|
2185
2582
|
? `self.${goFieldName(parentClass)} = *${parentConstructorCall(parentClass, [])}\n\t\t`
|
|
2186
2583
|
: '';
|
|
2187
|
-
const ctorBody = `{\n\t\tself := &${name}${typeParamNames}{}\n\t\t${ctorParameterInfo.prefixBlockContent}${parentInit}${fieldInitializers(node)}${parameterAssignments}${bodyStatements}return self;\n\t}`;
|
|
2584
|
+
const ctorBody = `{\n\t\tself := &${name}${typeParamNames}{}\n\t\t${nodeSelfLink(parentClass)}${ctorParameterInfo.prefixBlockContent}${parentInit}${fieldInitializers(node)}${parameterAssignments}${bodyStatements}${nodeSelfLink(parentClass, node)}return self;\n\t}`;
|
|
2188
2585
|
result += emitConstructor(name, typeParams, typeParamNames, ctorParameterInfo.signature, ctorBody);
|
|
2189
2586
|
}
|
|
2190
2587
|
else if (extendsError) {
|
|
@@ -2193,7 +2590,7 @@ function visit(node, options = {}) {
|
|
|
2193
2590
|
}
|
|
2194
2591
|
else {
|
|
2195
2592
|
const parentInit = parentClass && !extendsError ? `self.${goFieldName(parentClass)} = *${parentConstructorCall(parentClass, [])}\n\t\t` : '';
|
|
2196
|
-
const ctorBody = `{\n\t\tself := &${name}${typeParamNames}{}\n\t\t${parentInit}${fieldInitializers(node)}return self\n\t}`;
|
|
2593
|
+
const ctorBody = `{\n\t\tself := &${name}${typeParamNames}{}\n\t\t${parentInit}${fieldInitializers(node)}${nodeSelfLink(parentClass, node)}return self\n\t}`;
|
|
2197
2594
|
result += emitConstructor(name, typeParams, typeParamNames, '', ctorBody);
|
|
2198
2595
|
}
|
|
2199
2596
|
// Inherited concrete methods are copied onto the subclass: Go embedding
|
|
@@ -2252,7 +2649,7 @@ function visit(node, options = {}) {
|
|
|
2252
2649
|
}
|
|
2253
2650
|
const methodParameterInfo = getFunctionParametersInfo(member.parameters ?? []);
|
|
2254
2651
|
const returnType = member.type ? ` ${getType(member.type)}` : '';
|
|
2255
|
-
copiedMethods.push(
|
|
2652
|
+
copiedMethods.push(`${lineMarker(member)}func (self *${name}${typeParamNames}) ${methodName}(${methodParameterInfo.signature})${returnType} ${visit(member.body, { prefixBlockContent: methodParameterInfo.prefixBlockContent })}\n`);
|
|
2256
2653
|
}
|
|
2257
2654
|
ancestorIndex--;
|
|
2258
2655
|
}
|
|
@@ -2283,7 +2680,7 @@ function visit(node, options = {}) {
|
|
|
2283
2680
|
const genBody = visit(member.body, { prefixBlockContent: methodParameterInfo.prefixBlockContent });
|
|
2284
2681
|
asyncChannelVar = prevChannel;
|
|
2285
2682
|
asyncFunctionNode = prevFn;
|
|
2286
|
-
result +=
|
|
2683
|
+
result += `${lineMarker(member)}func (self *${name}${typeParamNames}) ${genName}(${methodParameterInfo.signature}) chan ${yieldType} {\n\t__ch := make(chan ${yieldType})\n\tgo func() {\n\t\tdefer close(__ch)\n\t\t${genBody.trim()}\n\t}()\n\treturn __ch\n}\n\n`;
|
|
2287
2684
|
}
|
|
2288
2685
|
continue;
|
|
2289
2686
|
}
|
|
@@ -2301,23 +2698,23 @@ function visit(node, options = {}) {
|
|
|
2301
2698
|
}
|
|
2302
2699
|
else if (isStatic) {
|
|
2303
2700
|
// Static methods become package-level functions named ClassName_methodName
|
|
2304
|
-
result +=
|
|
2701
|
+
result += `${lineMarker(member)}func ${name}_${methodName}(${methodParameterInfo.signature})${returnType} ${visit(member.body, { prefixBlockContent: methodParameterInfo.prefixBlockContent })}\n\n`;
|
|
2305
2702
|
}
|
|
2306
2703
|
else {
|
|
2307
|
-
result +=
|
|
2704
|
+
result += `${lineMarker(member)}func (self *${name}${typeParamNames}) ${methodName}(${methodParameterInfo.signature})${returnType} ${visit(member.body, { prefixBlockContent: methodParameterInfo.prefixBlockContent })}\n\n`;
|
|
2308
2705
|
}
|
|
2309
2706
|
}
|
|
2310
2707
|
else if (isGetAccessor(member)) {
|
|
2311
2708
|
// Getter: get prop() { ... } → func (self *T) Get_prop() RetType { ... }
|
|
2312
2709
|
const getterName = visit(member.name);
|
|
2313
2710
|
const returnType = member.type ? ` ${getType(member.type)}` : ' interface{}';
|
|
2314
|
-
result +=
|
|
2711
|
+
result += `${lineMarker(member)}func (self *${name}${typeParamNames}) Get_${getterName}()${returnType} ${visit(member.body)}\n\n`;
|
|
2315
2712
|
}
|
|
2316
2713
|
else if (isSetAccessor(member)) {
|
|
2317
2714
|
// Setter: set prop(val) { ... } → func (self *T) Set_prop(val ValType) { ... }
|
|
2318
2715
|
const setterName = visit(member.name);
|
|
2319
2716
|
const parameterInfo = getFunctionParametersInfo(member.parameters ?? []);
|
|
2320
|
-
result +=
|
|
2717
|
+
result += `${lineMarker(member)}func (self *${name}${typeParamNames}) Set_${setterName}(${parameterInfo.signature}) ${visit(member.body, { prefixBlockContent: parameterInfo.prefixBlockContent })}\n\n`;
|
|
2321
2718
|
}
|
|
2322
2719
|
}
|
|
2323
2720
|
result += copiedMethods.join('\n');
|
|
@@ -2342,6 +2739,16 @@ function visit(node, options = {}) {
|
|
|
2342
2739
|
return result.trim();
|
|
2343
2740
|
}
|
|
2344
2741
|
else if (isNewExpression(node)) {
|
|
2742
|
+
const nodeNew = visitNodeNew(node);
|
|
2743
|
+
if (nodeNew !== undefined)
|
|
2744
|
+
return nodeNew;
|
|
2745
|
+
const goTarget = goNewTarget(node);
|
|
2746
|
+
if (goTarget) {
|
|
2747
|
+
importedPackages.add(goTarget.path);
|
|
2748
|
+
const goNew = visitGoNew(node, goTarget.path, goTarget.typeName);
|
|
2749
|
+
if (goNew !== undefined)
|
|
2750
|
+
return goNew;
|
|
2751
|
+
}
|
|
2345
2752
|
const className = visit(node.expression);
|
|
2346
2753
|
if (className === 'Promise') {
|
|
2347
2754
|
return visitNewPromise(node);
|
|
@@ -2365,17 +2772,25 @@ function visit(node, options = {}) {
|
|
|
2365
2772
|
if (className === 'Set') {
|
|
2366
2773
|
return visitNewSet(node);
|
|
2367
2774
|
}
|
|
2775
|
+
// new TypeError(message) & co.: a TnError with that name
|
|
2776
|
+
if (BUILTIN_ERROR_NAMES.has(className) && !classNames.has(className)) {
|
|
2777
|
+
useHelper('error');
|
|
2778
|
+
const msg = (node.arguments ?? [])[0] ? visit(node.arguments[0]) : '""';
|
|
2779
|
+
return withErrorOptions(`TnNodeError(${toGoStringLiteral(className)}, "", ${msg})`, (node.arguments ?? [])[1]);
|
|
2780
|
+
}
|
|
2368
2781
|
// new Error(message) → a TnError value with a .message
|
|
2369
2782
|
if (className === 'Error') {
|
|
2370
2783
|
useHelper('error');
|
|
2371
2784
|
const msg = (node.arguments ?? [])[0] ? visit(node.arguments[0]) : '""';
|
|
2372
|
-
return `TnNewError(${msg})
|
|
2785
|
+
return withErrorOptions(`TnNewError(${msg})`, (node.arguments ?? [])[1]);
|
|
2373
2786
|
}
|
|
2374
2787
|
// new Date() / new Date(ms) → Go time
|
|
2375
2788
|
if (className === 'Date') {
|
|
2376
2789
|
importedPackages.add('time');
|
|
2377
|
-
if ((node.arguments ?? []).length === 0)
|
|
2378
|
-
|
|
2790
|
+
if ((node.arguments ?? []).length === 0) {
|
|
2791
|
+
useHelper('clock');
|
|
2792
|
+
return 'TnTimeNow()';
|
|
2793
|
+
}
|
|
2379
2794
|
return `time.UnixMilli(int64(${visit(node.arguments[0])})).UTC()`;
|
|
2380
2795
|
}
|
|
2381
2796
|
// new Array<number>(3) → make([]float64, 3)
|
|
@@ -2482,10 +2897,16 @@ function visit(node, options = {}) {
|
|
|
2482
2897
|
else if (isExportDeclaration(node) || isExportAssignment(node)) {
|
|
2483
2898
|
return '';
|
|
2484
2899
|
}
|
|
2900
|
+
else if (node.kind === 'EmptyStatement' || node.kind === 'DebuggerStatement') {
|
|
2901
|
+
// `;` and `debugger` do nothing in a native binary
|
|
2902
|
+
return '';
|
|
2903
|
+
}
|
|
2485
2904
|
const syntaxKind = node.kind;
|
|
2486
2905
|
if (!['VariableStatement', 'EndOfFileToken'].includes(syntaxKind)) {
|
|
2487
|
-
|
|
2488
|
-
|
|
2906
|
+
// The Go output would be incomplete: an error, not a guess (its children
|
|
2907
|
+
// are not visited, which would only report follow-on errors)
|
|
2908
|
+
reportError(node, `Unsupported syntax: ${syntaxKind}`);
|
|
2909
|
+
return '';
|
|
2489
2910
|
}
|
|
2490
2911
|
for (const child of childNodes(node)) {
|
|
2491
2912
|
code += visit(child);
|
|
@@ -2509,6 +2930,8 @@ function toGoValueOfType(expr, goType) {
|
|
|
2509
2930
|
}
|
|
2510
2931
|
// Array reference slots (*[]T): literals take an address, aliases pass through
|
|
2511
2932
|
if (goType && goType.startsWith('*[]')) {
|
|
2933
|
+
// Go bridge element types are qualified (*[]io/fs.DirEntry)
|
|
2934
|
+
goType = goLocalType(goType);
|
|
2512
2935
|
if (isIdentifier(expr) && referenceArrays.has(expr.text)) {
|
|
2513
2936
|
return getSafeName(expr.text);
|
|
2514
2937
|
}
|
|
@@ -2519,6 +2942,14 @@ function toGoValueOfType(expr, goType) {
|
|
|
2519
2942
|
? `&${code}`
|
|
2520
2943
|
: `func() ${goType} { __v := ${code}; return &__v }()`;
|
|
2521
2944
|
}
|
|
2945
|
+
// (x as string[]) on an any value, or (rows as Row[]) on an array of any
|
|
2946
|
+
// values (node:sqlite's all()): convert, then box
|
|
2947
|
+
const castSource = isAsExpression(expr) ? expr.expression : expr;
|
|
2948
|
+
const dynamicElements = goType !== '*[]interface{}' && inferExpressionType(castSource) === '[]interface{}';
|
|
2949
|
+
if (isDynamicValue(castSource) || dynamicElements) {
|
|
2950
|
+
useHelper('dynamic');
|
|
2951
|
+
return `func() ${goType} { __v := TnAs[${goType.slice(1)}](${visit(castSource)}); return &__v }()`;
|
|
2952
|
+
}
|
|
2522
2953
|
return `func() ${goType} { __v := ${visit(expr)}; return &__v }()`;
|
|
2523
2954
|
}
|
|
2524
2955
|
const code = visit(expr);
|
|
@@ -2592,10 +3023,13 @@ function isProcessEnv(expr) {
|
|
|
2592
3023
|
while (isParenthesizedExpression(expr) || isAsExpression(expr) || isNonNullExpression(expr)) {
|
|
2593
3024
|
expr = expr.expression;
|
|
2594
3025
|
}
|
|
2595
|
-
|
|
3026
|
+
if (isPropertyAccessExpression(expr) &&
|
|
2596
3027
|
isIdentifier(expr.expression) &&
|
|
2597
3028
|
expr.expression.text === 'process' &&
|
|
2598
|
-
expr.name.text === 'env')
|
|
3029
|
+
expr.name.text === 'env') {
|
|
3030
|
+
return true;
|
|
3031
|
+
}
|
|
3032
|
+
return resolveNodeKey(expr) === 'process.env';
|
|
2599
3033
|
}
|
|
2600
3034
|
// Whether an expression is (part of) an optional chain: a?.b, a?.b(), a?.b().c
|
|
2601
3035
|
function isOptionalChain(expr) {
|
|
@@ -2715,10 +3149,10 @@ function visitBlockStatements(statements) {
|
|
|
2715
3149
|
for (let index = 0; index < statements.length; index++) {
|
|
2716
3150
|
const statement = statements[index];
|
|
2717
3151
|
if (isUsingDeclaration(statement)) {
|
|
2718
|
-
parts.push(visitUsingDeclaration(statement, statements.slice(index + 1)));
|
|
3152
|
+
parts.push(withLineMarker(statement, visitUsingDeclaration(statement, statements.slice(index + 1))));
|
|
2719
3153
|
break;
|
|
2720
3154
|
}
|
|
2721
|
-
parts.push(visit(statement));
|
|
3155
|
+
parts.push(withLineMarker(statement, visit(statement)));
|
|
2722
3156
|
if (isIfStatement(statement) && !statement.elseStatement && alwaysExits(statement.thenStatement)) {
|
|
2723
3157
|
for (const name of getNarrowedNames(statement.expression, false)) {
|
|
2724
3158
|
if (!narrowedVariables.has(name)) {
|
|
@@ -2765,18 +3199,41 @@ function visitUsingDeclaration(statement, rest) {
|
|
|
2765
3199
|
return head + visitBlockStatements(rest);
|
|
2766
3200
|
return `func() {\n\t\t${head}${visitBlockStatements(rest)}}()\n\t`;
|
|
2767
3201
|
}
|
|
3202
|
+
// Binary arithmetic with a bridged named numeric: coerce the float64 side.
|
|
3203
|
+
function visitGoNumericArithmetic(node, op) {
|
|
3204
|
+
const leftType = inferExpressionType(node.left);
|
|
3205
|
+
const rightType = inferExpressionType(node.right);
|
|
3206
|
+
const leftNumeric = leftType && isGoNumericType(leftType) ? leftType : undefined;
|
|
3207
|
+
const rightNumeric = rightType && isGoNumericType(rightType) ? rightType : undefined;
|
|
3208
|
+
if (!leftNumeric && !rightNumeric)
|
|
3209
|
+
return undefined;
|
|
3210
|
+
if (leftNumeric && rightNumeric)
|
|
3211
|
+
return undefined;
|
|
3212
|
+
const numericType = leftNumeric ?? rightNumeric;
|
|
3213
|
+
if (!numericType)
|
|
3214
|
+
return undefined;
|
|
3215
|
+
const left = leftNumeric ? visit(node.left) : `${numericType}(${visit(node.left)})`;
|
|
3216
|
+
const right = rightNumeric ? visit(node.right) : `${numericType}(${visit(node.right)})`;
|
|
3217
|
+
if (['+=', '-=', '*=', '/='].includes(op)) {
|
|
3218
|
+
const target = visit(node.left);
|
|
3219
|
+
const baseOp = op.slice(0, -1);
|
|
3220
|
+
return `${target} = ${left} ${baseOp} ${right}`;
|
|
3221
|
+
}
|
|
3222
|
+
return `${left} ${op} ${right}`;
|
|
3223
|
+
}
|
|
2768
3224
|
function visitDynamicArithmetic(node, op) {
|
|
2769
3225
|
const leftDynamic = isDynamicValue(node.left);
|
|
2770
3226
|
const rightDynamic = isDynamicValue(node.right);
|
|
2771
3227
|
if (!leftDynamic && !rightDynamic)
|
|
2772
3228
|
return undefined;
|
|
2773
|
-
const isConcat = op === '+' && [node.left, node.right].some((side) => inferExpressionType(side) === 'string');
|
|
3229
|
+
const isConcat = (op === '+' || op === '+=') && [node.left, node.right].some((side) => inferExpressionType(side) === 'string');
|
|
2774
3230
|
const operand = (side, dynamic) => {
|
|
2775
3231
|
if (!dynamic)
|
|
2776
3232
|
return visit(side);
|
|
2777
3233
|
if (isConcat) {
|
|
2778
|
-
|
|
2779
|
-
|
|
3234
|
+
// String(x) as JS renders it (Buffers decode, numbers in JS notation)
|
|
3235
|
+
useHelper('dynamic');
|
|
3236
|
+
return `TnJSString(${visit(side)})`;
|
|
2780
3237
|
}
|
|
2781
3238
|
useHelper('dynamic');
|
|
2782
3239
|
return `TnAs[float64](${visit(side)})`;
|
|
@@ -2886,6 +3343,10 @@ function getStructFieldGoType(structType, field) {
|
|
|
2886
3343
|
function jsStringOf(code, goType) {
|
|
2887
3344
|
if (goType === 'string')
|
|
2888
3345
|
return code;
|
|
3346
|
+
if (goType === '[]byte') {
|
|
3347
|
+
useHelper('buffer');
|
|
3348
|
+
return `TnBufferToString(${code}, "")`;
|
|
3349
|
+
}
|
|
2889
3350
|
if (goType === 'float64') {
|
|
2890
3351
|
useHelper('dynamic');
|
|
2891
3352
|
return `TnNumStr(${code})`;
|
|
@@ -2894,10 +3355,27 @@ function jsStringOf(code, goType) {
|
|
|
2894
3355
|
importedPackages.add('strconv');
|
|
2895
3356
|
return `strconv.FormatBool(${code})`;
|
|
2896
3357
|
}
|
|
3358
|
+
if (goType === 'interface{}') {
|
|
3359
|
+
useHelper('dynamic');
|
|
3360
|
+
useHelper('error');
|
|
3361
|
+
return `TnJSString(${code})`;
|
|
3362
|
+
}
|
|
3363
|
+
// Node classes with a toString() (MIMEType, X509Certificate, …)
|
|
3364
|
+
const nodeKey = goType ? nodeRegistry.classByGoType[goType] : undefined;
|
|
3365
|
+
if (nodeKey && 'toString' in nodeRegistry.classes[nodeKey].methods)
|
|
3366
|
+
return `${code}.toString()`;
|
|
2897
3367
|
importedPackages.add('fmt');
|
|
2898
3368
|
return `fmt.Sprintf("%v", ${code})`;
|
|
2899
3369
|
}
|
|
2900
3370
|
// JS bitwise operators work on 32-bit integers (ToInt32 / ToUint32)
|
|
3371
|
+
// Go-typed numeric operands (typed constants like x509.KeyUsageCertSign,
|
|
3372
|
+
// int fields) read as float64 so the TnInt32 helpers accept them
|
|
3373
|
+
function asFloatOperand(node, code) {
|
|
3374
|
+
const goType = inferExpressionType(node);
|
|
3375
|
+
if (goType && goType !== 'float64' && isGoNumericKind(goType))
|
|
3376
|
+
return `float64(${code})`;
|
|
3377
|
+
return code;
|
|
3378
|
+
}
|
|
2901
3379
|
function visitBitwise(node, op) {
|
|
2902
3380
|
const kind = node.operatorToken.kind;
|
|
2903
3381
|
const base = {
|
|
@@ -2918,8 +3396,8 @@ function visitBitwise(node, op) {
|
|
|
2918
3396
|
if (!bitOp)
|
|
2919
3397
|
return undefined;
|
|
2920
3398
|
useHelper('dynamic');
|
|
2921
|
-
const left = visit(node.left);
|
|
2922
|
-
const right = visit(node.right);
|
|
3399
|
+
const left = asFloatOperand(node.left, visit(node.left));
|
|
3400
|
+
const right = asFloatOperand(node.right, visit(node.right));
|
|
2923
3401
|
let value;
|
|
2924
3402
|
if (bitOp === '>>>')
|
|
2925
3403
|
value = `float64(TnUint32(${left}) >> (TnUint32(${right}) & 31))`;
|
|
@@ -2938,6 +3416,10 @@ function visitDelete(node) {
|
|
|
2938
3416
|
const key = isPropertyAccessExpression(target)
|
|
2939
3417
|
? toGoStringLiteral(target.name.text)
|
|
2940
3418
|
: visit(target.argumentExpression);
|
|
3419
|
+
if (isProcessEnv(object)) {
|
|
3420
|
+
importedPackages.add('os');
|
|
3421
|
+
return `func() bool { os.Unsetenv(${key}); return true }()`;
|
|
3422
|
+
}
|
|
2941
3423
|
const objectType = inferExpressionType(object);
|
|
2942
3424
|
if (objectType?.startsWith('*TnMap['))
|
|
2943
3425
|
return `${visit(object)}.Delete(${key})`;
|
|
@@ -2990,7 +3472,60 @@ function isNewTargetNode(node) {
|
|
|
2990
3472
|
return node?.kind === 'MetaProperty' && node.name?.text === 'target';
|
|
2991
3473
|
}
|
|
2992
3474
|
// The parent part of a subclass constructor: new.target-aware chains pass the target on
|
|
3475
|
+
// Subclass instances of Node classes with overridable methods register
|
|
3476
|
+
// themselves on the embedded struct (`self.TnTransform.tnSelf = self`)
|
|
3477
|
+
function nodeSelfLink(parentClass, classNode) {
|
|
3478
|
+
if (!parentClass || !nodeParentClassKeys.has(parentClass))
|
|
3479
|
+
return '';
|
|
3480
|
+
const key = nodeParentClassKeys.get(parentClass) ?? '';
|
|
3481
|
+
const cls = nodeRegistry.classes[key];
|
|
3482
|
+
if (!cls || !('tnSelf' in cls.props))
|
|
3483
|
+
return '';
|
|
3484
|
+
let code = `self.${parentClass}.tnSelf = self\n\t\t`;
|
|
3485
|
+
// Overridden hook methods (_transform …) are registered on the embedded struct
|
|
3486
|
+
if (classNode && nodeRegistry.hookPrefixes[key]) {
|
|
3487
|
+
for (const member of classNode.members ?? []) {
|
|
3488
|
+
if (!isMethodDeclaration(member) || !member.name || !isIdentifier(member.name))
|
|
3489
|
+
continue;
|
|
3490
|
+
const name = member.name.text;
|
|
3491
|
+
if (name in nodeRegistry.hookParams)
|
|
3492
|
+
code += `self.${parentClass}.setHook(${toGoStringLiteral(name)}, self.${name})\n\t\t`;
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
return code;
|
|
3496
|
+
}
|
|
3497
|
+
// Hook methods of subclasses of Node classes take the hooks' parameter types
|
|
3498
|
+
function applyNodeHookTypes(parentClass, classNode) {
|
|
3499
|
+
if (!parentClass || !nodeParentClassKeys.has(parentClass))
|
|
3500
|
+
return;
|
|
3501
|
+
if (!nodeRegistry.hookPrefixes[nodeParentClassKeys.get(parentClass) ?? ''])
|
|
3502
|
+
return;
|
|
3503
|
+
for (const member of classNode.members ?? []) {
|
|
3504
|
+
if (!isMethodDeclaration(member) || !member.name || !isIdentifier(member.name))
|
|
3505
|
+
continue;
|
|
3506
|
+
const name = member.name.text;
|
|
3507
|
+
if (name in nodeRegistry.hookParams)
|
|
3508
|
+
member.tnParamTypes = nodeRegistry.hookParams[name];
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
// class X extends EventEmitter: the parent is a Node helper struct
|
|
3512
|
+
const nodeParentClassKeys = new Map();
|
|
3513
|
+
function nodeParentGoName(expr) {
|
|
3514
|
+
const key = resolveNodeKey(expr);
|
|
3515
|
+
const cls = key ? nodeRegistry.classes[key] : undefined;
|
|
3516
|
+
if (!cls || !cls.goType.startsWith('*') || !nodeRegistry.constructors[key])
|
|
3517
|
+
return '';
|
|
3518
|
+
const goName = cls.goType.slice(1);
|
|
3519
|
+
nodeParentClassKeys.set(goName, key);
|
|
3520
|
+
return goName;
|
|
3521
|
+
}
|
|
2993
3522
|
function parentConstructorCall(parentClass, args) {
|
|
3523
|
+
if (nodeParentClassKeys.has(parentClass)) {
|
|
3524
|
+
const key = nodeParentClassKeys.get(parentClass) ?? '';
|
|
3525
|
+
const call = { args, nodes: [], types: args.map(() => ''), recv: '' };
|
|
3526
|
+
const ctor = nodeRegistry.constructors[key](call);
|
|
3527
|
+
return ctor;
|
|
3528
|
+
}
|
|
2994
3529
|
if (!usesNewTarget(parentClass))
|
|
2995
3530
|
return `New${parentClass}(${args.join(', ')})`;
|
|
2996
3531
|
return `new${parentClass}As(${['__newTarget', ...args].join(', ')})`;
|
|
@@ -3139,6 +3674,9 @@ function toFieldGoType(goType) {
|
|
|
3139
3674
|
return !goType || goType === 'nil' ? 'interface{}' : goType;
|
|
3140
3675
|
}
|
|
3141
3676
|
function isDictionaryLiteral(node) {
|
|
3677
|
+
// Keys that are not Go identifiers ('Content-Type') can't be struct fields
|
|
3678
|
+
if (hasNonIdentifierKeys(node))
|
|
3679
|
+
return true;
|
|
3142
3680
|
const declaration = node.parent;
|
|
3143
3681
|
if (!isVariableDeclaration(declaration) || !isIdentifier(declaration.name))
|
|
3144
3682
|
return false;
|
|
@@ -3153,6 +3691,24 @@ function isDictionaryLiteral(node) {
|
|
|
3153
3691
|
n.expression.text === name &&
|
|
3154
3692
|
!isStringLiteral(n.argumentExpression));
|
|
3155
3693
|
}
|
|
3694
|
+
function hasNonIdentifierKeys(node) {
|
|
3695
|
+
if (!isObjectLiteralExpression(node))
|
|
3696
|
+
return false;
|
|
3697
|
+
let sawKey = false;
|
|
3698
|
+
for (const p of node.properties ?? []) {
|
|
3699
|
+
if (!isPropertyAssignment(p)) {
|
|
3700
|
+
if (p.kind === 'SpreadAssignment' || isMethodDeclaration(p) || isGetAccessor(p) || isSetAccessor(p))
|
|
3701
|
+
return false;
|
|
3702
|
+
continue;
|
|
3703
|
+
}
|
|
3704
|
+
// Go identifiers have no `$` ({ $id: 1 }, node:sqlite's named parameters)
|
|
3705
|
+
if (isStringLiteral(p.name) && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(p.name.text))
|
|
3706
|
+
sawKey = true;
|
|
3707
|
+
if (isIdentifier(p.name) && p.name.text.includes('$'))
|
|
3708
|
+
sawKey = true;
|
|
3709
|
+
}
|
|
3710
|
+
return sawKey;
|
|
3711
|
+
}
|
|
3156
3712
|
function containsNode(root, predicate) {
|
|
3157
3713
|
for (const child of childNodes(root)) {
|
|
3158
3714
|
if (predicate(child) || containsNode(child, predicate))
|
|
@@ -3253,6 +3809,9 @@ function getAsExpressionType(node) {
|
|
|
3253
3809
|
return inferExpressionType(node.expression) ?? 'interface{}';
|
|
3254
3810
|
}
|
|
3255
3811
|
const target = getType(node.type);
|
|
3812
|
+
// (x as File): dynamic values holding Node class instances assert to them
|
|
3813
|
+
if (nodeRegistry.classByGoType[target])
|
|
3814
|
+
return target;
|
|
3256
3815
|
if (isDynamicValue(node.expression) && (isStructGoType(target) || target.startsWith('*'))) {
|
|
3257
3816
|
return 'interface{}';
|
|
3258
3817
|
}
|
|
@@ -3554,6 +4113,8 @@ function getMemberTypeNode(typeNode, name) {
|
|
|
3554
4113
|
// (TypeScript's "contextual type"), derived syntactically from declarations
|
|
3555
4114
|
// Declared type nodes of variables/parameters in scope (latest declaration wins)
|
|
3556
4115
|
const variableTypeNodes = new Map();
|
|
4116
|
+
// Variables initialized by go:builtin ref<T>() (their `.value` dereferences)
|
|
4117
|
+
const goRefVariables = new Set();
|
|
3557
4118
|
// Declared type (as a type node) of an assignable expression, when known
|
|
3558
4119
|
function getExpressionTypeNode(expr) {
|
|
3559
4120
|
if (isIdentifier(expr))
|
|
@@ -3890,16 +4451,43 @@ function inferFunctionBodyReturnType(node) {
|
|
|
3890
4451
|
if (contextualFn?.type)
|
|
3891
4452
|
return getType(contextualFn.type);
|
|
3892
4453
|
if (isArrowFunction(node) && !isBlock(node.body)) {
|
|
3893
|
-
|
|
4454
|
+
const bodyType = inferExpressionType(node.body);
|
|
4455
|
+
// `() => undefined`: nil is not a Go type
|
|
4456
|
+
return bodyType === 'nil' ? 'interface{}' : bodyType;
|
|
3894
4457
|
}
|
|
3895
4458
|
if (node.body && isBlock(node.body)) {
|
|
3896
|
-
|
|
4459
|
+
let returnsNil = false;
|
|
4460
|
+
for (const stmt of node.body.statements ?? []) {
|
|
3897
4461
|
if (isReturnStatement(stmt) && stmt.expression) {
|
|
3898
4462
|
const exprType = inferExpressionType(stmt.expression);
|
|
3899
|
-
if (exprType)
|
|
4463
|
+
if (exprType === 'nil')
|
|
4464
|
+
returnsNil = true;
|
|
4465
|
+
else if (exprType)
|
|
3900
4466
|
return exprType;
|
|
3901
4467
|
}
|
|
3902
4468
|
}
|
|
4469
|
+
if (returnsNil)
|
|
4470
|
+
return 'interface{}';
|
|
4471
|
+
}
|
|
4472
|
+
return undefined;
|
|
4473
|
+
}
|
|
4474
|
+
function getEnclosingParameterType(expr) {
|
|
4475
|
+
const primitives = ['NumberKeyword', 'StringKeyword', 'BooleanKeyword', 'BigIntKeyword'];
|
|
4476
|
+
for (let node = expr.parent; node; node = node.parent) {
|
|
4477
|
+
if (!isArrowFunction(node) && !isFunctionExpression(node) && node.kind !== 'FunctionDeclaration' && !isMethodDeclaration(node)) {
|
|
4478
|
+
continue;
|
|
4479
|
+
}
|
|
4480
|
+
const param = (node.parameters ?? []).find((p) => isIdentifier(p.name) && p.name.text === expr.text);
|
|
4481
|
+
if (!param)
|
|
4482
|
+
continue;
|
|
4483
|
+
if (!param.type || param.questionToken || param.initializer)
|
|
4484
|
+
return undefined;
|
|
4485
|
+
// ...values: number[] reads as []float64 in the body
|
|
4486
|
+
if (param.dotDotDotToken) {
|
|
4487
|
+
const elem = param.type.kind === 'ArrayType' ? param.type.elementType : undefined;
|
|
4488
|
+
return elem && primitives.includes(elem.kind) ? `[]${getType(elem)}` : undefined;
|
|
4489
|
+
}
|
|
4490
|
+
return primitives.includes(param.type.kind) ? getParameterGoType(param) : undefined;
|
|
3903
4491
|
}
|
|
3904
4492
|
return undefined;
|
|
3905
4493
|
}
|
|
@@ -3916,7 +4504,15 @@ function inferArrowFunctionGoType(node) {
|
|
|
3916
4504
|
const retType = inferFunctionBodyReturnType(node);
|
|
3917
4505
|
return `func(${params})${retType ? ` ${retType}` : ''}`;
|
|
3918
4506
|
}
|
|
4507
|
+
// fn.mock on a function value: the call record of a node:test mock (functions have no fields in Go)
|
|
4508
|
+
function isMockAccess(node) {
|
|
4509
|
+
if (!isPropertyAccessExpression(node) || node.name.text !== 'mock' || !nodeRegistry.modules['test'])
|
|
4510
|
+
return false;
|
|
4511
|
+
return !!inferExpressionType(node.expression)?.startsWith('func(');
|
|
4512
|
+
}
|
|
3919
4513
|
function inferExpressionType(expr) {
|
|
4514
|
+
if (isMockAccess(expr))
|
|
4515
|
+
return '*TnMockFunctionContext';
|
|
3920
4516
|
if (isAwaitExpression(expr)) {
|
|
3921
4517
|
// await unwraps the promise channel
|
|
3922
4518
|
const inner = inferExpressionType(expr.expression);
|
|
@@ -3935,6 +4531,12 @@ function inferExpressionType(expr) {
|
|
|
3935
4531
|
return getAsExpressionType(expr);
|
|
3936
4532
|
if (isTypeAssertionExpression(expr))
|
|
3937
4533
|
return getType(expr.type);
|
|
4534
|
+
const nodeType = inferNodeExpressionType(expr);
|
|
4535
|
+
if (nodeType !== undefined)
|
|
4536
|
+
return nodeType === '' ? undefined : nodeType;
|
|
4537
|
+
const goType = inferGoExpressionType(expr);
|
|
4538
|
+
if (goType !== undefined)
|
|
4539
|
+
return goType;
|
|
3938
4540
|
if (isStringLiteral(expr) ||
|
|
3939
4541
|
isNoSubstitutionTemplateLiteral(expr) ||
|
|
3940
4542
|
isTemplateExpression(expr)) {
|
|
@@ -3954,6 +4556,14 @@ function inferExpressionType(expr) {
|
|
|
3954
4556
|
return 'TnJsxNode';
|
|
3955
4557
|
if (expr.kind === 'TaggedTemplateExpression' && isStringRawTag(expr.tag))
|
|
3956
4558
|
return 'string';
|
|
4559
|
+
// sql`…` with a Node class method as the tag (node:sqlite's tag store)
|
|
4560
|
+
if (expr.kind === 'TaggedTemplateExpression' && isPropertyAccessExpression(expr.tag)) {
|
|
4561
|
+
const recvType = inferExpressionType(expr.tag.expression);
|
|
4562
|
+
const cls = recvType ? nodeRegistry.classes[nodeRegistry.classByGoType[recvType]] : undefined;
|
|
4563
|
+
const result = cls?.methods[expr.tag.name.text];
|
|
4564
|
+
if (result)
|
|
4565
|
+
return result;
|
|
4566
|
+
}
|
|
3957
4567
|
if (isRegularExpressionLiteral(expr)) {
|
|
3958
4568
|
const lastSlash = expr.text.lastIndexOf('/');
|
|
3959
4569
|
if (needsStatefulRegex(expr.text.substring(1, lastSlash), expr.text.substring(lastSlash + 1))) {
|
|
@@ -3971,6 +4581,12 @@ function inferExpressionType(expr) {
|
|
|
3971
4581
|
if (isIdentifier(expr) && expr.text === 'undefined')
|
|
3972
4582
|
return 'nil';
|
|
3973
4583
|
if (isIdentifier(expr)) {
|
|
4584
|
+
// A primitive-typed parameter of an enclosing function: its annotation,
|
|
4585
|
+
// not a same-named variable registered earlier (inferring the return type
|
|
4586
|
+
// of an arrow before it is visited)
|
|
4587
|
+
const enclosingParamType = getEnclosingParameterType(expr);
|
|
4588
|
+
if (enclosingParamType)
|
|
4589
|
+
return enclosingParamType;
|
|
3974
4590
|
let varType = variableGoTypes.get(expr.text);
|
|
3975
4591
|
// Array references are read as the slice they point to
|
|
3976
4592
|
if (varType?.startsWith('*[]'))
|
|
@@ -3986,7 +4602,7 @@ function inferExpressionType(expr) {
|
|
|
3986
4602
|
const ctorName = expr.expression.text;
|
|
3987
4603
|
if (classNames.has(ctorName))
|
|
3988
4604
|
return `*${ctorName}`;
|
|
3989
|
-
if (ctorName === 'Error')
|
|
4605
|
+
if (ctorName === 'Error' || (BUILTIN_ERROR_NAMES.has(ctorName) && !classNames.has(ctorName)))
|
|
3990
4606
|
return '*TnError';
|
|
3991
4607
|
if (ctorName === 'Promise' && (expr.typeArguments ?? []).length > 0) {
|
|
3992
4608
|
return `chan ${getType(expr.typeArguments[0]) || 'struct{}'}`;
|
|
@@ -4076,13 +4692,10 @@ function inferExpressionType(expr) {
|
|
|
4076
4692
|
if (fn)
|
|
4077
4693
|
return `chan ${getGeneratorYieldType(fn)}`;
|
|
4078
4694
|
}
|
|
4079
|
-
const nodeResultType = callee ? nodeCallResultTypes.get(callee) : undefined;
|
|
4080
4695
|
if (callee && BUILTIN_CALL_TYPES[callee])
|
|
4081
4696
|
return BUILTIN_CALL_TYPES[callee];
|
|
4082
4697
|
if (callee?.startsWith('Math.'))
|
|
4083
4698
|
return 'float64';
|
|
4084
|
-
if (nodeResultType)
|
|
4085
|
-
return nodeResultType;
|
|
4086
4699
|
}
|
|
4087
4700
|
// Object.keys(map) / Object.values(map)
|
|
4088
4701
|
if (isCallExpression(expr) &&
|
|
@@ -4181,6 +4794,15 @@ function inferExpressionType(expr) {
|
|
|
4181
4794
|
const leftType = inferExpressionType(expr.expression);
|
|
4182
4795
|
if (expr.name.text === 'length')
|
|
4183
4796
|
return 'float64';
|
|
4797
|
+
// record.key reads the Record's value type
|
|
4798
|
+
if (leftType?.startsWith('*TnMap[') && expr.name.text !== 'size' && !isCallee(expr)) {
|
|
4799
|
+
const objectType = resolveExpressionType(expr.expression);
|
|
4800
|
+
if (objectType === 'Record' || objectType === 'Map' || objectType === undefined) {
|
|
4801
|
+
const valueType = extractMapValueType(leftType);
|
|
4802
|
+
if (valueType !== 'struct{}')
|
|
4803
|
+
return valueType;
|
|
4804
|
+
}
|
|
4805
|
+
}
|
|
4184
4806
|
const resolvedLeftType = leftType?.replace(/^\*/, '').replace(/\[.*\]$/, '');
|
|
4185
4807
|
const resolvedPropertyType = resolvedLeftType
|
|
4186
4808
|
? (classPropertyTypes.get(resolvedLeftType)?.get(expr.name.text) ??
|
|
@@ -4292,6 +4914,10 @@ function inferExpressionType(expr) {
|
|
|
4292
4914
|
return ownerType;
|
|
4293
4915
|
if (methodName === 'reduceRight' && expr.arguments?.[1])
|
|
4294
4916
|
return inferExpressionType(expr.arguments[1]);
|
|
4917
|
+
// the accumulator: the initial value's type, else the element type
|
|
4918
|
+
if (methodName === 'reduce') {
|
|
4919
|
+
return (expr.arguments?.[1] ? inferExpressionType(expr.arguments[1]) : undefined) ?? elementType;
|
|
4920
|
+
}
|
|
4295
4921
|
if (methodName === 'flat' && elementType.startsWith('[]'))
|
|
4296
4922
|
return elementType;
|
|
4297
4923
|
if (methodName === 'flat' || methodName === 'flatMap')
|
|
@@ -4562,7 +5188,7 @@ const BUILTIN_FUNCTION_TYPES = {
|
|
|
4562
5188
|
parseInt: 'float64',
|
|
4563
5189
|
Number: 'float64',
|
|
4564
5190
|
String: 'string',
|
|
4565
|
-
setTimeout: '*
|
|
5191
|
+
setTimeout: '*TnTimeout',
|
|
4566
5192
|
setInterval: '*TnInterval'
|
|
4567
5193
|
};
|
|
4568
5194
|
const STRING_METHOD_RETURN_TYPES = {
|
|
@@ -4919,12 +5545,18 @@ function getMapLookup(expr) {
|
|
|
4919
5545
|
return undefined;
|
|
4920
5546
|
}
|
|
4921
5547
|
function isNilableGoType(goType) {
|
|
4922
|
-
|
|
5548
|
+
if (goType.startsWith('*') ||
|
|
4923
5549
|
goType.startsWith('[]') ||
|
|
4924
5550
|
goType.startsWith('map[') ||
|
|
4925
5551
|
goType.startsWith('func') ||
|
|
4926
5552
|
goType.startsWith('chan ') ||
|
|
4927
|
-
goType === 'interface{}'
|
|
5553
|
+
goType === 'interface{}' ||
|
|
5554
|
+
goType === 'error') {
|
|
5555
|
+
return true;
|
|
5556
|
+
}
|
|
5557
|
+
// Go bridge named types over nil-able kinds (net.IP, io.Reader, http.Header)
|
|
5558
|
+
const kind = goMetaTypeInfo(goType)?.kind;
|
|
5559
|
+
return kind === 'interface' || kind === 'slice' || kind === 'map' || kind === 'func' || kind === 'chan';
|
|
4928
5560
|
}
|
|
4929
5561
|
function visitOptionalPropertyAccess(node) {
|
|
4930
5562
|
const baseExpr = visit(node.expression);
|
|
@@ -4944,7 +5576,10 @@ function visitOptionalPropertyAccess(node) {
|
|
|
4944
5576
|
'interface{}';
|
|
4945
5577
|
const nullableType = makeNullableType(propertyType);
|
|
4946
5578
|
const tmp = getTempName('opt');
|
|
4947
|
-
|
|
5579
|
+
// Class getters (also Node classes' accessors such as err.syscall) are calls
|
|
5580
|
+
const propertyAccess = classGetterMethods.has(`${className}.${node.name.text}`)
|
|
5581
|
+
? `${tmp}.Get_${visit(node.name)}()`
|
|
5582
|
+
: `${tmp}.${visit(node.name)}`;
|
|
4948
5583
|
if (nullableType.startsWith('*') && nullableType.slice(1) === propertyType) {
|
|
4949
5584
|
const valueTemp = getTempName('optv');
|
|
4950
5585
|
return `func() ${nullableType} { ${tmp} := ${baseExpr}; if ${tmp} == nil { var __zero ${nullableType}; return __zero }; ${valueTemp} := ${propertyAccess}; return &${valueTemp} }()`;
|
|
@@ -5051,10 +5686,18 @@ function inferArrayCallbackReturnType(callback, elementType, fallbackType) {
|
|
|
5051
5686
|
const explicitType = getType(callback.type);
|
|
5052
5687
|
return explicitType || fallbackType;
|
|
5053
5688
|
}
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
const
|
|
5689
|
+
// The element parameter has the element type while the body is inferred
|
|
5690
|
+
const param = (callback.parameters ?? [])[0];
|
|
5691
|
+
const paramName = param && isIdentifier(param.name) && !param.type ? param.name.text : '';
|
|
5692
|
+
const hadParam = paramName ? variableGoTypes.has(paramName) : false;
|
|
5693
|
+
const savedParam = paramName ? variableGoTypes.get(paramName) : undefined;
|
|
5694
|
+
if (paramName)
|
|
5695
|
+
variableGoTypes.set(paramName, elementType);
|
|
5696
|
+
const inferred = isBlock(callback.body) ? inferFunctionBodyReturnType(callback) : inferExpressionType(callback.body);
|
|
5697
|
+
if (paramName && hadParam && savedParam !== undefined)
|
|
5698
|
+
variableGoTypes.set(paramName, savedParam);
|
|
5699
|
+
else if (paramName)
|
|
5700
|
+
variableGoTypes.delete(paramName);
|
|
5058
5701
|
return inferred ?? fallbackType;
|
|
5059
5702
|
}
|
|
5060
5703
|
if (isIdentifier(callback)) {
|
|
@@ -5493,7 +6136,10 @@ function getType(typeNode, getArrayType = false) {
|
|
|
5493
6136
|
const params = (typeNode.parameters ?? [])
|
|
5494
6137
|
.map((p) => {
|
|
5495
6138
|
const paramType = p.type ? getType(p.type) : 'interface{}';
|
|
5496
|
-
|
|
6139
|
+
if (p.dotDotDotToken && paramType.startsWith('[]'))
|
|
6140
|
+
return `...${paramType.slice(2)}`;
|
|
6141
|
+
// optional parameters are nullable (as in declarations and at call sites)
|
|
6142
|
+
return p.questionToken ? makeNullableType(paramType) : paramType;
|
|
5497
6143
|
})
|
|
5498
6144
|
.join(', ');
|
|
5499
6145
|
const ret = typeNode.type ? ` ${getType(typeNode.type)}` : '';
|
|
@@ -5508,6 +6154,11 @@ function getType(typeNode, getArrayType = false) {
|
|
|
5508
6154
|
useHelper('symbol');
|
|
5509
6155
|
return '*TnSymbol';
|
|
5510
6156
|
}
|
|
6157
|
+
if (isTypeReferenceNode(typeNode)) {
|
|
6158
|
+
const nodeClassKey = nodeClassKeyOfTypeName(typeNode.typeName);
|
|
6159
|
+
if (nodeClassKey)
|
|
6160
|
+
return nodeRegistry.classes[nodeClassKey].goType;
|
|
6161
|
+
}
|
|
5511
6162
|
if (isTypeReferenceNode(typeNode) && isIdentifier(typeNode.typeName)) {
|
|
5512
6163
|
const name = typeNode.typeName.text;
|
|
5513
6164
|
if (enumNames.has(name)) {
|
|
@@ -5529,12 +6180,28 @@ function getType(typeNode, getArrayType = false) {
|
|
|
5529
6180
|
if (name === 'RegExp') {
|
|
5530
6181
|
return '*regexp.Regexp';
|
|
5531
6182
|
}
|
|
5532
|
-
|
|
6183
|
+
// go:builtin GoError values are Go errors.
|
|
6184
|
+
if (name === 'GoError') {
|
|
6185
|
+
return 'error';
|
|
6186
|
+
}
|
|
6187
|
+
if (name === 'GoChan' && typeNode.typeArguments && typeNode.typeArguments.length > 0) {
|
|
6188
|
+
const inner = getType(typeNode.typeArguments[0]);
|
|
6189
|
+
return `chan ${inner || 'interface{}'}`;
|
|
6190
|
+
}
|
|
6191
|
+
if (name === 'GoRef' && typeNode.typeArguments && typeNode.typeArguments.length > 0) {
|
|
6192
|
+
const inner = getType(typeNode.typeArguments[0]);
|
|
6193
|
+
return `*${inner || 'interface{}'}`;
|
|
6194
|
+
}
|
|
6195
|
+
if (name === 'Error' || (BUILTIN_ERROR_NAMES.has(name) && !classNames.has(name))) {
|
|
5533
6196
|
return '*TnError';
|
|
5534
6197
|
}
|
|
5535
6198
|
if (name === 'RegExpExecArray' || name === 'RegExpMatchArray') {
|
|
5536
6199
|
return '[]string';
|
|
5537
6200
|
}
|
|
6201
|
+
// Uint8Array / Buffer / ArrayBuffer annotations are Go []byte.
|
|
6202
|
+
if (name === 'Uint8Array' || name === 'Buffer' || name === 'ArrayBuffer') {
|
|
6203
|
+
return '[]byte';
|
|
6204
|
+
}
|
|
5538
6205
|
if (name === 'TemplateStringsArray' || name === 'ReadonlyArray') {
|
|
5539
6206
|
return name === 'TemplateStringsArray' ? '[]string' : getType(typeNode.typeArguments?.[0]);
|
|
5540
6207
|
}
|
|
@@ -5608,6 +6275,11 @@ function getTypeCategory(typeNode) {
|
|
|
5608
6275
|
if (nonNullTypes.length === 1)
|
|
5609
6276
|
return getTypeCategory(nonNullTypes[0]);
|
|
5610
6277
|
}
|
|
6278
|
+
if (isTypeReferenceNode(typeNode)) {
|
|
6279
|
+
const nodeClassKey = nodeClassKeyOfTypeName(typeNode.typeName);
|
|
6280
|
+
if (nodeClassKey)
|
|
6281
|
+
return nodeRegistry.classes[nodeClassKey].category;
|
|
6282
|
+
}
|
|
5611
6283
|
if (isTypeReferenceNode(typeNode) && isIdentifier(typeNode.typeName)) {
|
|
5612
6284
|
const name = typeNode.typeName.text;
|
|
5613
6285
|
if (name === 'Map')
|
|
@@ -5649,16 +6321,25 @@ function goTypeCategory(goType) {
|
|
|
5649
6321
|
return undefined;
|
|
5650
6322
|
if (goType.startsWith('*TnMap['))
|
|
5651
6323
|
return extractMapValueType(goType) === 'struct{}' ? 'Set' : 'Map';
|
|
6324
|
+
const nodeClassKey = nodeRegistry.classByGoType[goType];
|
|
6325
|
+
if (nodeClassKey)
|
|
6326
|
+
return nodeRegistry.classes[nodeClassKey].category;
|
|
5652
6327
|
if (goType.startsWith('*[]'))
|
|
5653
6328
|
goType = goType.slice(1);
|
|
5654
|
-
if (goType.startsWith('[]'))
|
|
6329
|
+
if (goType.startsWith('[]')) {
|
|
6330
|
+
if (goType === '[]byte')
|
|
6331
|
+
return 'Buffer';
|
|
5655
6332
|
return 'array';
|
|
6333
|
+
}
|
|
5656
6334
|
if (goType === 'string')
|
|
5657
6335
|
return 'string';
|
|
5658
6336
|
if (goType === '*regexp.Regexp' || goType === '*TnRegex')
|
|
5659
6337
|
return 'RegExp';
|
|
5660
6338
|
if (goType === 'time.Time')
|
|
5661
6339
|
return 'Date';
|
|
6340
|
+
// Bridged Go values: methods pass through as Go methods.
|
|
6341
|
+
if (goMetaTypeInfo(goType))
|
|
6342
|
+
return 'class';
|
|
5662
6343
|
return undefined;
|
|
5663
6344
|
}
|
|
5664
6345
|
function isNilLiteral(node) {
|
|
@@ -5677,6 +6358,10 @@ function getAcessString(leftSide, rightSide, objectType) {
|
|
|
5677
6358
|
if (rightSide === 'length' && objectType !== 'class') {
|
|
5678
6359
|
return `float64(len(${leftSide}))`;
|
|
5679
6360
|
}
|
|
6361
|
+
// Buffers / Uint8Arrays / ArrayBuffers are []byte
|
|
6362
|
+
if (rightSide === 'byteLength' && objectType === 'Buffer') {
|
|
6363
|
+
return `float64(len(${leftSide}))`;
|
|
6364
|
+
}
|
|
5680
6365
|
if (rightSide === 'size' && (objectType === 'Map' || objectType === 'Set')) {
|
|
5681
6366
|
return `float64(${leftSide}.Len())`;
|
|
5682
6367
|
}
|
|
@@ -5739,18 +6424,61 @@ const callHandlers = {
|
|
|
5739
6424
|
const message = args.length > 1 ? args[1] : '"Assertion failed"';
|
|
5740
6425
|
return `if !(${args[0]}) {\n\t\tpanic(${message})\n\t}`;
|
|
5741
6426
|
},
|
|
5742
|
-
'console.log': (_caller, args) =>
|
|
5743
|
-
|
|
5744
|
-
|
|
6427
|
+
'console.log': (_caller, args) => consoleOut('Stdout', args),
|
|
6428
|
+
'console.info': (_caller, args) => consoleOut('Stdout', args),
|
|
6429
|
+
'console.debug': (_caller, args) => consoleOut('Stdout', args),
|
|
6430
|
+
'console.trace': (_caller, args) => consoleOut('Stderr', args, 'Trace:'),
|
|
6431
|
+
'console.dir': (_caller, args) => {
|
|
6432
|
+
useHelper('nodeUtil');
|
|
6433
|
+
return consoleOut('Stdout', [`TnInspect(${args.join(', ')})`], '', true);
|
|
6434
|
+
},
|
|
6435
|
+
'console.assert': (_caller, args) => {
|
|
6436
|
+
const condition = args[0] ?? 'false';
|
|
6437
|
+
currentCallArgTypes = currentCallArgTypes.slice(1);
|
|
6438
|
+
const message = consoleOut('Stderr', args.slice(1), args.length > 1 ? 'Assertion failed:' : 'Assertion failed');
|
|
6439
|
+
return `if !(${condition}) {\n\t\t${message}\n\t}`;
|
|
6440
|
+
},
|
|
6441
|
+
'console.table': (_caller, args) => {
|
|
6442
|
+
useHelper('nodeUtil');
|
|
6443
|
+
return consoleOut('Stdout', [`TnConsoleTableString(${args.join(', ')})`], '', true);
|
|
6444
|
+
},
|
|
6445
|
+
'console.count': (_caller, args) => {
|
|
6446
|
+
useHelper('console');
|
|
6447
|
+
return `TnConsoleCount(${args.join(', ')})`;
|
|
6448
|
+
},
|
|
6449
|
+
'console.countReset': (_caller, args) => {
|
|
6450
|
+
useHelper('console');
|
|
6451
|
+
return `TnConsoleCountReset(${args.join(', ')})`;
|
|
6452
|
+
},
|
|
6453
|
+
'console.group': (_caller, args) => {
|
|
6454
|
+
useHelper('console');
|
|
6455
|
+
return `TnConsoleGroup(${consoleArgs(args).join(', ')})`;
|
|
6456
|
+
},
|
|
6457
|
+
'console.groupCollapsed': (_caller, args) => {
|
|
6458
|
+
useHelper('console');
|
|
6459
|
+
return `TnConsoleGroup(${consoleArgs(args).join(', ')})`;
|
|
6460
|
+
},
|
|
6461
|
+
'console.groupEnd': () => {
|
|
6462
|
+
useHelper('console');
|
|
6463
|
+
return 'TnConsoleGroupEnd()';
|
|
5745
6464
|
},
|
|
5746
6465
|
'console.time': (_caller, args) => {
|
|
5747
|
-
|
|
5748
|
-
return
|
|
6466
|
+
useHelper('console');
|
|
6467
|
+
return `TnConsoleTime(${args.slice(0, 1).join(', ')})`;
|
|
5749
6468
|
},
|
|
5750
6469
|
'console.timeEnd': (_caller, args) => {
|
|
5751
|
-
|
|
6470
|
+
useHelper('console');
|
|
6471
|
+
return `TnConsoleTimeEnd(${args.slice(0, 1).join(', ')})`;
|
|
6472
|
+
},
|
|
6473
|
+
'console.timeLog': (_caller, args) => {
|
|
6474
|
+
useHelper('console');
|
|
6475
|
+
const label = args[0] ?? '"default"';
|
|
6476
|
+
currentCallArgTypes = currentCallArgTypes.slice(1);
|
|
6477
|
+
const extra = args.length > 1 ? consoleArgs(args.slice(1)) : [];
|
|
5752
6478
|
importedPackages.add('fmt');
|
|
5753
|
-
|
|
6479
|
+
importedPackages.add('strings');
|
|
6480
|
+
const text = extra.length > 0 ? `strings.TrimSuffix(fmt.Sprintln(${extra.join(', ')}), "\\n")` : '""';
|
|
6481
|
+
return `TnConsoleTimeLog(${label}, ${text})`;
|
|
5754
6482
|
},
|
|
5755
6483
|
'Math.hypot': (_caller, args) => mathCall('Hypot', args),
|
|
5756
6484
|
'Math.cbrt': (_caller, args) => mathCall('Cbrt', args),
|
|
@@ -5925,18 +6653,11 @@ const callHandlers = {
|
|
|
5925
6653
|
},
|
|
5926
6654
|
'Date.now': () => {
|
|
5927
6655
|
importedPackages.add('time');
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
'console.error': (_caller, args) => {
|
|
5931
|
-
importedPackages.add('fmt');
|
|
5932
|
-
importedPackages.add('os');
|
|
5933
|
-
return `fmt.Fprintln(os.Stderr, ${args.join(', ')})`;
|
|
5934
|
-
},
|
|
5935
|
-
'console.warn': (_caller, args) => {
|
|
5936
|
-
importedPackages.add('fmt');
|
|
5937
|
-
importedPackages.add('os');
|
|
5938
|
-
return `fmt.Fprintln(os.Stderr, ${args.join(', ')})`;
|
|
6656
|
+
useHelper('clock');
|
|
6657
|
+
return 'float64(TnTimeNow().UnixMilli())';
|
|
5939
6658
|
},
|
|
6659
|
+
'console.error': (_caller, args) => consoleOut('Stderr', args),
|
|
6660
|
+
'console.warn': (_caller, args) => consoleOut('Stderr', args),
|
|
5940
6661
|
'Array.isArray': (_caller, args) => {
|
|
5941
6662
|
importedPackages.add('reflect');
|
|
5942
6663
|
return `(reflect.ValueOf(${args[0]}).Kind() == reflect.Slice)`;
|
|
@@ -5970,6 +6691,59 @@ const callHandlers = {
|
|
|
5970
6691
|
},
|
|
5971
6692
|
'Boolean': (_caller, args) => `(${truthinessCheck(args[0] ?? 'false', currentCallArgTypes[0] ?? 'interface{}')})`,
|
|
5972
6693
|
};
|
|
6694
|
+
// Built-in Error subclasses (TnErrors carrying their name)
|
|
6695
|
+
const BUILTIN_ERROR_NAMES = new Set(['TypeError', 'RangeError', 'SyntaxError', 'ReferenceError', 'EvalError', 'URIError']);
|
|
6696
|
+
// Whether the program uses console.group (then every console line is
|
|
6697
|
+
// written through the indentation-aware helper)
|
|
6698
|
+
let consoleGroupsUsed = false;
|
|
6699
|
+
// One console line to stdout / stderr, with an optional leading word
|
|
6700
|
+
function consoleOut(stream, args, prefix = '', formatted = false) {
|
|
6701
|
+
let parts = formatted ? args : consoleArgs(args);
|
|
6702
|
+
if (prefix)
|
|
6703
|
+
parts = [toGoStringLiteral(prefix), ...parts];
|
|
6704
|
+
importedPackages.add('fmt');
|
|
6705
|
+
if (consoleGroupsUsed) {
|
|
6706
|
+
useHelper('console');
|
|
6707
|
+
importedPackages.add('os');
|
|
6708
|
+
importedPackages.add('strings');
|
|
6709
|
+
return `TnConsoleWrite(os.${stream}, strings.TrimSuffix(fmt.Sprintln(${parts.join(', ')}), "\\n"))`;
|
|
6710
|
+
}
|
|
6711
|
+
if (stream === 'Stdout')
|
|
6712
|
+
return `fmt.Println(${parts.join(', ')})`;
|
|
6713
|
+
importedPackages.add('os');
|
|
6714
|
+
return `fmt.Fprintln(os.Stderr, ${parts.join(', ')})`;
|
|
6715
|
+
}
|
|
6716
|
+
// console.* arguments, printed like Node: strings as-is, numbers in JS
|
|
6717
|
+
// notation, everything else through util.inspect (and a format string first
|
|
6718
|
+
// argument through util.format)
|
|
6719
|
+
// The inferred type of call argument i ('' when unknown); a plain lookup keeps
|
|
6720
|
+
// the self-hosted build from dereferencing a missing (nil) entry
|
|
6721
|
+
function callArgType(i) {
|
|
6722
|
+
if (i >= currentCallArgTypes.length)
|
|
6723
|
+
return '';
|
|
6724
|
+
const t = currentCallArgTypes[i];
|
|
6725
|
+
return t ? t : '';
|
|
6726
|
+
}
|
|
6727
|
+
function consoleArgs(args) {
|
|
6728
|
+
const types = args.map((_arg, i) => callArgType(i));
|
|
6729
|
+
const isFormatString = args.length > 1 && types[0] === 'string' && args[0].includes('%');
|
|
6730
|
+
const needsInspect = args.some((_arg, i) => !['string', 'float64', 'bool', '[]byte'].includes(types[i]));
|
|
6731
|
+
if (isFormatString || needsInspect) {
|
|
6732
|
+
useHelper('nodeUtil');
|
|
6733
|
+
return [`TnUtilFormat(${args.join(', ')})`];
|
|
6734
|
+
}
|
|
6735
|
+
return args.map((arg, i) => {
|
|
6736
|
+
if (types[i] === 'float64') {
|
|
6737
|
+
useHelper('dynamic');
|
|
6738
|
+
return `TnNumStr(${arg})`;
|
|
6739
|
+
}
|
|
6740
|
+
if (types[i] === '[]byte') {
|
|
6741
|
+
useHelper('buffer');
|
|
6742
|
+
return `TnBufferInspect(${arg})`;
|
|
6743
|
+
}
|
|
6744
|
+
return arg;
|
|
6745
|
+
});
|
|
6746
|
+
}
|
|
5973
6747
|
const stringMethodHandlers = {
|
|
5974
6748
|
split: (obj, args) => {
|
|
5975
6749
|
if (args[0]?.startsWith('regexp.MustCompile('))
|
|
@@ -6003,7 +6777,10 @@ const stringMethodHandlers = {
|
|
|
6003
6777
|
},
|
|
6004
6778
|
indexOf: (obj, args) => {
|
|
6005
6779
|
importedPackages.add('strings');
|
|
6006
|
-
|
|
6780
|
+
if (args.length < 2)
|
|
6781
|
+
return `float64(strings.Index(${obj}, ${args[0]}))`;
|
|
6782
|
+
// indexOf(x, from): search from the clamped position, index into the whole string
|
|
6783
|
+
return `func() float64 { __s := ${obj}; __f := int(${args[1]}); if __f < 0 { __f = 0 }; if __f > len(__s) { __f = len(__s) }; __i := strings.Index(__s[__f:], ${args[0]}); if __i < 0 { return -1 }; return float64(__i + __f) }()`;
|
|
6007
6784
|
},
|
|
6008
6785
|
charCodeAt: (obj, args) => `float64(${obj}[int(${args[0]})])`,
|
|
6009
6786
|
localeCompare: (obj, args) => {
|
|
@@ -6258,6 +7035,8 @@ function toGoRegexp(arg) {
|
|
|
6258
7035
|
// A JS slice index: negative values count from the end
|
|
6259
7036
|
// A JS slice index: negative values count from the end; out-of-range values clamp
|
|
6260
7037
|
function sliceIndex(obj, index) {
|
|
7038
|
+
if (index === undefined || index === '')
|
|
7039
|
+
return '0';
|
|
6261
7040
|
if (index.startsWith('-'))
|
|
6262
7041
|
return `max(0, int(float64(len(${obj})) + ${index}))`;
|
|
6263
7042
|
return `min(int(${index}), len(${obj}))`;
|
|
@@ -6296,9 +7075,21 @@ const setMethodHandlers = {
|
|
|
6296
7075
|
isSupersetOf: (obj, args) => `${args[0]}.IsSubsetOf(${obj})`,
|
|
6297
7076
|
isDisjointFrom: (obj, args) => `(${obj}.Intersection(${args[0]}).Len() == 0)`
|
|
6298
7077
|
};
|
|
7078
|
+
// A table entry only when the table itself has it (not Object.prototype's valueOf …)
|
|
7079
|
+
function ownMethodHandler(table, key) {
|
|
7080
|
+
return Object.hasOwn(table, key) ? table[key] : undefined;
|
|
7081
|
+
}
|
|
6299
7082
|
function getDynamicCallHandler(caller, objectType) {
|
|
6300
7083
|
if (promiseResolveName && caller === promiseResolveName) {
|
|
6301
|
-
|
|
7084
|
+
// The first resolution wins; later ones are ignored (as in JS)
|
|
7085
|
+
return (_caller, args) => {
|
|
7086
|
+
// resolve() without a value: the channel type's zero value
|
|
7087
|
+
if (args.length === 0 || args[0] === 'nil') {
|
|
7088
|
+
useHelper('promise');
|
|
7089
|
+
return 'TnResolveZero(ch)';
|
|
7090
|
+
}
|
|
7091
|
+
return `select { case ch <- ${args[0]}: default: }`;
|
|
7092
|
+
};
|
|
6302
7093
|
}
|
|
6303
7094
|
if (promiseRejectName && caller === promiseRejectName) {
|
|
6304
7095
|
return (_caller, args) => {
|
|
@@ -6314,10 +7105,17 @@ function getDynamicCallHandler(caller, objectType) {
|
|
|
6314
7105
|
return (c, args) => {
|
|
6315
7106
|
const obj = c.substring(0, dotIndex);
|
|
6316
7107
|
if (args.length > 0) {
|
|
7108
|
+
// bigint.toString(radix)
|
|
7109
|
+
if (currentReceiverGoType === '*tnbig.Int')
|
|
7110
|
+
return `${obj}.Text(int(${args[0]}))`;
|
|
6317
7111
|
importedPackages.add('strconv');
|
|
6318
7112
|
return `strconv.FormatInt(int64(${obj}), int(${args[0]}))`;
|
|
6319
7113
|
}
|
|
6320
|
-
return jsStringOf(obj, objectType === 'number' || objectType === 'float64'
|
|
7114
|
+
return jsStringOf(obj, objectType === 'number' || objectType === 'float64'
|
|
7115
|
+
? 'float64'
|
|
7116
|
+
: currentReceiverGoType === 'interface{}'
|
|
7117
|
+
? 'interface{}'
|
|
7118
|
+
: undefined);
|
|
6321
7119
|
};
|
|
6322
7120
|
}
|
|
6323
7121
|
if (methodName === 'toPrecision') {
|
|
@@ -6338,29 +7136,29 @@ function getDynamicCallHandler(caller, objectType) {
|
|
|
6338
7136
|
return null;
|
|
6339
7137
|
let handler;
|
|
6340
7138
|
if (objectType === 'string') {
|
|
6341
|
-
handler = stringMethodHandlers
|
|
7139
|
+
handler = ownMethodHandler(stringMethodHandlers, methodName);
|
|
6342
7140
|
}
|
|
6343
7141
|
else if (objectType === 'array') {
|
|
6344
|
-
handler = arrayMethodHandlers
|
|
7142
|
+
handler = ownMethodHandler(arrayMethodHandlers, methodName);
|
|
6345
7143
|
}
|
|
6346
7144
|
else if (objectType === 'RegExp') {
|
|
6347
|
-
handler = regexpMethodHandlers
|
|
7145
|
+
handler = ownMethodHandler(regexpMethodHandlers, methodName);
|
|
6348
7146
|
}
|
|
6349
7147
|
else if (objectType === 'Date') {
|
|
6350
|
-
handler = dateMethodHandlers
|
|
7148
|
+
handler = ownMethodHandler(dateMethodHandlers, methodName);
|
|
6351
7149
|
}
|
|
6352
7150
|
else if (objectType === 'Map') {
|
|
6353
|
-
handler = mapMethodHandlers
|
|
7151
|
+
handler = ownMethodHandler(mapMethodHandlers, methodName);
|
|
6354
7152
|
}
|
|
6355
7153
|
else if (objectType === 'Set') {
|
|
6356
|
-
handler = setMethodHandlers
|
|
7154
|
+
handler = ownMethodHandler(setMethodHandlers, methodName);
|
|
6357
7155
|
}
|
|
6358
7156
|
else {
|
|
6359
7157
|
// Unknown type: try both maps for backward compatibility
|
|
6360
7158
|
handler =
|
|
6361
|
-
stringMethodHandlers
|
|
6362
|
-
arrayMethodHandlers
|
|
6363
|
-
regexpMethodHandlers
|
|
7159
|
+
ownMethodHandler(stringMethodHandlers, methodName) ??
|
|
7160
|
+
ownMethodHandler(arrayMethodHandlers, methodName) ??
|
|
7161
|
+
ownMethodHandler(regexpMethodHandlers, methodName);
|
|
6364
7162
|
}
|
|
6365
7163
|
if (handler) {
|
|
6366
7164
|
return (c, args) => {
|
|
@@ -6373,7 +7171,10 @@ function getDynamicCallHandler(caller, objectType) {
|
|
|
6373
7171
|
}
|
|
6374
7172
|
function getCallString(caller, args, typeArgs = '', objectType) {
|
|
6375
7173
|
// Parentheses from casts like `(mod as any).fn(...)` are stripped for lookup
|
|
6376
|
-
|
|
7174
|
+
// (own entries only: a plain-object lookup would also find Object.prototype members)
|
|
7175
|
+
const bare = caller.replace(/[()]/g, '');
|
|
7176
|
+
const handler = (Object.hasOwn(callHandlers, caller) ? callHandlers[caller] : undefined) ??
|
|
7177
|
+
(Object.hasOwn(callHandlers, bare) ? callHandlers[bare] : undefined) ??
|
|
6377
7178
|
getDynamicCallHandler(caller, objectType);
|
|
6378
7179
|
if (handler) {
|
|
6379
7180
|
return handler(caller, args, typeArgs);
|
|
@@ -6474,11 +7275,15 @@ function visitDynamicMethodCall(node) {
|
|
|
6474
7275
|
includes: 'TnIncludes',
|
|
6475
7276
|
indexOf: 'TnIndexOf',
|
|
6476
7277
|
slice: 'TnSlice',
|
|
6477
|
-
concat: 'TnConcat'
|
|
7278
|
+
concat: 'TnConcat',
|
|
7279
|
+
toString: 'TnJSString'
|
|
6478
7280
|
};
|
|
6479
7281
|
const helper = dynamicHelpers[method];
|
|
6480
|
-
|
|
7282
|
+
// (a plain-object lookup also finds Object.prototype members)
|
|
7283
|
+
if (!helper || typeof helper !== 'string')
|
|
6481
7284
|
return undefined;
|
|
7285
|
+
if (method === 'toString')
|
|
7286
|
+
useHelper('error');
|
|
6482
7287
|
useHelper('dynamic');
|
|
6483
7288
|
const args = (node.arguments ?? []).map((a) => visit(a));
|
|
6484
7289
|
return `${helper}(${[visit(node.expression.expression), ...args].join(', ')})`;
|
|
@@ -6901,6 +7706,13 @@ function getTypeArguments(typeArguments) {
|
|
|
6901
7706
|
return `[${args.join(', ')}]`;
|
|
6902
7707
|
}
|
|
6903
7708
|
function getParameterGoType(param) {
|
|
7709
|
+
// Node callbacks / hooks: `any`-typed parameters take Node's types too
|
|
7710
|
+
const nodeHookTypes = param.parent?.tnParamTypes;
|
|
7711
|
+
if (nodeHookTypes && param.type?.kind === 'AnyKeyword' && !param.dotDotDotToken) {
|
|
7712
|
+
const hookType = nodeHookTypes[(param.parent.parameters ?? []).indexOf(param)];
|
|
7713
|
+
if (hookType)
|
|
7714
|
+
return hookType;
|
|
7715
|
+
}
|
|
6904
7716
|
// Rest parameter: ...args — use variadic syntax
|
|
6905
7717
|
if (param.dotDotDotToken) {
|
|
6906
7718
|
if (param.type) {
|
|
@@ -6919,7 +7731,7 @@ function getParameterGoType(param) {
|
|
|
6919
7731
|
// Array parameters are references (mutations are visible to the caller) —
|
|
6920
7732
|
// except unbound type parameters (Go generics), template-tag arrays, and
|
|
6921
7733
|
// dynamic []interface{} (runtime helper arrays)
|
|
6922
|
-
if (explicitType.startsWith('[]') && explicitType !== '[]interface{}' && !param.dotDotDotToken) {
|
|
7734
|
+
if (explicitType.startsWith('[]') && explicitType !== '[]interface{}' && explicitType !== '[]byte' && !param.dotDotDotToken) {
|
|
6923
7735
|
const isTaggedTemplateArray = isTypeReferenceNode(param.type) &&
|
|
6924
7736
|
isIdentifier(param.type.typeName) &&
|
|
6925
7737
|
param.type.typeName.text === 'TemplateStringsArray';
|
|
@@ -6943,9 +7755,19 @@ function getParameterGoType(param) {
|
|
|
6943
7755
|
const contextualFn = param.parent ? getContextualFunctionType(param.parent) : undefined;
|
|
6944
7756
|
if (contextualFn) {
|
|
6945
7757
|
const index = (param.parent.parameters ?? []).indexOf(param);
|
|
6946
|
-
const
|
|
6947
|
-
|
|
6948
|
-
|
|
7758
|
+
const contextualParam = contextualFn.parameters?.[index];
|
|
7759
|
+
const contextualParamType = contextualParam?.type;
|
|
7760
|
+
if (contextualParamType) {
|
|
7761
|
+
const contextualGoType = getType(contextualParamType);
|
|
7762
|
+
return contextualParam.questionToken ? makeNullableType(contextualGoType) : contextualGoType;
|
|
7763
|
+
}
|
|
7764
|
+
}
|
|
7765
|
+
// Callbacks of Node APIs: parameter types from the registry
|
|
7766
|
+
const nodeParamTypes = param.parent?.tnParamTypes;
|
|
7767
|
+
if (nodeParamTypes) {
|
|
7768
|
+
const nodeParamType = nodeParamTypes[(param.parent.parameters ?? []).indexOf(param)];
|
|
7769
|
+
if (nodeParamType)
|
|
7770
|
+
return nodeParamType;
|
|
6949
7771
|
}
|
|
6950
7772
|
return 'interface{}';
|
|
6951
7773
|
}
|
|
@@ -7153,8 +7975,27 @@ function getFunctionParametersInfo(parameters) {
|
|
|
7153
7975
|
prefixBlockContent: prefixBlockContent + destructuring
|
|
7154
7976
|
};
|
|
7155
7977
|
}
|
|
7978
|
+
// Go packages the generated code may import: a user identifier with one of
|
|
7979
|
+
// these names would shadow the package, so it is renamed (unless the name is
|
|
7980
|
+
// itself a `go:` package import)
|
|
7981
|
+
const goPackageIdentifiers = new Set([
|
|
7982
|
+
'aes', 'atomic', 'base64', 'binary', 'bufio', 'bytes', 'cipher', 'crc32', 'errors', 'exec', 'filepath',
|
|
7983
|
+
'flate', 'fmt', 'fs', 'gzip', 'hash', 'hex', 'hkdf', 'hmac', 'html', 'io', 'iter', 'json', 'math', 'md5',
|
|
7984
|
+
'net', 'os', 'pbkdf2', 'rand', 'reflect', 'regexp', 'runtime', 'sha1', 'sha256', 'sha3', 'sha512', 'signal',
|
|
7985
|
+
'slices', 'sort', 'strconv', 'strings', 'subtle', 'sync', 'syscall', 'time', 'unicode', 'unsafe', 'user',
|
|
7986
|
+
'utf16', 'utf8', 'zlib', 'asn1', 'bits', 'context', 'ecdh', 'ecdsa', 'ed25519', 'elliptic', 'encoding', 'http',
|
|
7987
|
+
'log', 'mime', 'multipart', 'netip', 'pem', 'pkix', 'rsa', 'textproto', 'tls', 'x509',
|
|
7988
|
+
// Aliased imports (math/big, crypto, net/url, crypto/rand)
|
|
7989
|
+
'tnbig', 'tncrypto', 'tnurl', 'cryptorand',
|
|
7990
|
+
// Go builtins: a package-level `var copy` would shadow copy() in the helpers
|
|
7991
|
+
'append', 'cap', 'clear', 'close', 'complex', 'copy', 'imag', 'len', 'make', 'max', 'min', 'panic',
|
|
7992
|
+
'print', 'println', 'real', 'recover', 'any', 'byte', 'error', 'float64', 'int', 'int64', 'rune',
|
|
7993
|
+
'uint', 'uint8', 'uint32', 'nil', 'iota'
|
|
7994
|
+
]);
|
|
7995
|
+
// Local names of `go:` package imports (`import * as strings from 'go:strings'`)
|
|
7996
|
+
const goPackageLocalNames = new Set();
|
|
7156
7997
|
function getSafeName(name) {
|
|
7157
|
-
if (!dangerousNames.has(name)) {
|
|
7998
|
+
if (!dangerousNames.has(name) && !(goPackageIdentifiers.has(name) && !goPackageLocalNames.has(name))) {
|
|
7158
7999
|
return name;
|
|
7159
8000
|
}
|
|
7160
8001
|
if (!renamedFunctions.has(name)) {
|
|
@@ -7212,6 +8053,11 @@ function emitChannelFunction(node, isAsync) {
|
|
|
7212
8053
|
const body = visit(node.body, { prefixBlockContent: parameterInfo.prefixBlockContent });
|
|
7213
8054
|
asyncChannelVar = prevChannel;
|
|
7214
8055
|
asyncFunctionNode = prevFn;
|
|
8056
|
+
if (isAsync) {
|
|
8057
|
+
// The body runs synchronously until its first await, holding the JS baton
|
|
8058
|
+
useHelper('eventLoop');
|
|
8059
|
+
return (`func ${name}${typeParams}(${parameterInfo.signature}) chan ${chanType} {\n\t${channelVar} := make(chan ${chanType}, 1)\n\tTnStartAsync(func() {\n\t\tdefer close(${channelVar})${rejectOnPanic}\n\t\t${body.trim()}\n\t})\n\treturn ${channelVar}\n}`);
|
|
8060
|
+
}
|
|
7215
8061
|
return (`func ${name}${typeParams}(${parameterInfo.signature}) chan ${chanType} {\n\t${channelVar} := make(chan ${chanType})\n\tgo func() {\n\t\tdefer close(${channelVar})${rejectOnPanic}\n\t\t${body.trim()}\n\t}()\n\treturn ${channelVar}\n}`);
|
|
7216
8062
|
}
|
|
7217
8063
|
// The type a generator yields: from its first yield expression's operand
|
|
@@ -7231,6 +8077,7 @@ function visitPromiseReturn(node, options) {
|
|
|
7231
8077
|
if (!callback || (!isArrowFunction(callback) && !isFunctionExpression(callback))) {
|
|
7232
8078
|
return `return ${visit(node)}` + (options.inline ? '' : ';\n\t');
|
|
7233
8079
|
}
|
|
8080
|
+
useHelper('promise');
|
|
7234
8081
|
const channelType = getPromiseChannelType(node);
|
|
7235
8082
|
const resolveParam = callback.parameters[0];
|
|
7236
8083
|
const resolveName = resolveParam ? visit(resolveParam.name) : '';
|
|
@@ -7242,7 +8089,7 @@ function visitPromiseReturn(node, options) {
|
|
|
7242
8089
|
const body = isBlock(callback.body) ? visit(callback.body) : `{ ${visit(callback.body)} }`;
|
|
7243
8090
|
promiseResolveName = prevResolveName;
|
|
7244
8091
|
promiseRejectName = prevRejectName;
|
|
7245
|
-
return (`ch := make(chan ${channelType})\n\t\
|
|
8092
|
+
return (`ch := make(chan ${channelType}, 1)\n\t\tfunc() {\n\t\t\tdefer TnRejectExecutor(ch)\n\t\t\tfunc() ${body.trimEnd()}()\n\t\t}()\n\t\treturn ch` +
|
|
7246
8093
|
(options.inline ? '' : ';\n\t'));
|
|
7247
8094
|
}
|
|
7248
8095
|
function visitNewPromise(node) {
|
|
@@ -7261,7 +8108,10 @@ function visitNewPromise(node) {
|
|
|
7261
8108
|
const body = isBlock(callback.body) ? visit(callback.body) : `{ ${visit(callback.body)} }`;
|
|
7262
8109
|
promiseResolveName = prevResolveName;
|
|
7263
8110
|
promiseRejectName = prevRejectName;
|
|
7264
|
-
|
|
8111
|
+
// Buffered: resolving never blocks (a loop task may resolve a promise its own goroutine awaits)
|
|
8112
|
+
// The executor runs synchronously, as in JS (a throw rejects the promise)
|
|
8113
|
+
useHelper('promise');
|
|
8114
|
+
return `func() chan ${channelType} {\n\t\tch := make(chan ${channelType}, 1)\n\t\tfunc() {\n\t\t\tdefer TnRejectExecutor(ch)\n\t\t\tfunc() ${body.trimEnd()}()\n\t\t}()\n\t\treturn ch;\n\t}()`;
|
|
7265
8115
|
}
|
|
7266
8116
|
function extractMapValueType(mapType) {
|
|
7267
8117
|
const args = splitMapTypeArgs(mapType);
|
|
@@ -7350,11 +8200,13 @@ function normalizeCjsToEsm(code) {
|
|
|
7350
8200
|
code = code.replace(/(?:module\.exports|exports)\.(\w+)\s*=\s*function\s*\w*\s*\(/g, 'export function $1(');
|
|
7351
8201
|
return code;
|
|
7352
8202
|
}
|
|
7353
|
-
function includeLocalImport(code, dir, goFileName, tsx = false) {
|
|
8203
|
+
function includeLocalImport(code, dir, goFileName, tsx = false, fileName = '') {
|
|
7354
8204
|
const prevDir = currentFileDir;
|
|
7355
8205
|
const prevTsx = currentFileIsTsx;
|
|
8206
|
+
const prevFileName = currentFileName;
|
|
7356
8207
|
currentFileDir = dir;
|
|
7357
8208
|
currentFileIsTsx = tsx;
|
|
8209
|
+
currentFileName = fileName;
|
|
7358
8210
|
// Normalize CommonJS modules to ES module syntax before parsing
|
|
7359
8211
|
if (!code.includes('export ') && (code.includes('module.exports') || code.includes('exports.'))) {
|
|
7360
8212
|
code = normalizeCjsToEsm(code);
|
|
@@ -7367,6 +8219,7 @@ function includeLocalImport(code, dir, goFileName, tsx = false) {
|
|
|
7367
8219
|
}
|
|
7368
8220
|
currentFileDir = prevDir;
|
|
7369
8221
|
currentFileIsTsx = prevTsx;
|
|
8222
|
+
currentFileName = prevFileName;
|
|
7370
8223
|
return;
|
|
7371
8224
|
}
|
|
7372
8225
|
// Separate-file mode (local TS imports): transpile to its own Go file.
|
|
@@ -7374,23 +8227,30 @@ function includeLocalImport(code, dir, goFileName, tsx = false) {
|
|
|
7374
8227
|
// `package main`, so helpers are emitted once in the main output file.
|
|
7375
8228
|
const savedOutsideNodes = outsideNodes;
|
|
7376
8229
|
const savedPackages = [...importedPackages];
|
|
8230
|
+
// Helper packages registered by the including file so far must survive this
|
|
8231
|
+
// file's own tracking (the main file imports them for the helpers it carries)
|
|
8232
|
+
const savedHelperPackages = [...helperProvidedPackages];
|
|
7377
8233
|
outsideNodes = [];
|
|
7378
8234
|
importedPackages.clear();
|
|
7379
8235
|
helperProvidedPackages.clear();
|
|
7380
8236
|
const sf = parseSource(code);
|
|
7381
|
-
const
|
|
8237
|
+
const isDeclarationLine = (line) => /^(var|const|type|func|import)\b/.test(line.trim());
|
|
8238
|
+
const declarationLines = [];
|
|
8239
|
+
const statementLines = [];
|
|
7382
8240
|
const prevEmittingModuleFile = emittingModuleFile;
|
|
7383
8241
|
emittingModuleFile = true;
|
|
7384
8242
|
for (const stmt of sf.statements) {
|
|
7385
8243
|
const result = visit(stmt, { addFunctionOutside: true });
|
|
7386
|
-
if (result.trim())
|
|
7387
|
-
|
|
8244
|
+
if (!result.trim())
|
|
8245
|
+
continue;
|
|
8246
|
+
const line = withLineMarker(stmt, localizeGoPaths(result));
|
|
8247
|
+
if (isDeclarationLine(result))
|
|
8248
|
+
declarationLines.push(line);
|
|
8249
|
+
else
|
|
8250
|
+
statementLines.push(line);
|
|
7388
8251
|
}
|
|
7389
8252
|
emittingModuleFile = prevEmittingModuleFile;
|
|
7390
|
-
const fileOutside = outsideNodes.map((n) => visit(n, { isOutside: true })).join('\n');
|
|
7391
|
-
const isDeclarationLine = (line) => /^(var|const|type|func|import)\b/.test(line.trim());
|
|
7392
|
-
const declarationLines = inlineLines.filter(isDeclarationLine);
|
|
7393
|
-
const statementLines = inlineLines.filter((line) => !isDeclarationLine(line));
|
|
8253
|
+
const fileOutside = localizeGoPaths(outsideNodes.map((n) => withLineMarker(n, visit(n, { isOutside: true }))).join('\n'));
|
|
7394
8254
|
const fileInline = [
|
|
7395
8255
|
...declarationLines,
|
|
7396
8256
|
...(statementLines.length > 0 ? [`func init() {\n\t${statementLines.join('\n\t')}\n}`] : [])
|
|
@@ -7405,7 +8265,7 @@ function includeLocalImport(code, dir, goFileName, tsx = false) {
|
|
|
7405
8265
|
.filter((pkg) => {
|
|
7406
8266
|
if (!helperProvidedPackages.has(pkg))
|
|
7407
8267
|
return true;
|
|
7408
|
-
const pkgName =
|
|
8268
|
+
const pkgName = goPathAliases[pkg] ?? pkg.split('/').pop();
|
|
7409
8269
|
return stripGoStrings(fileCode).includes(`${pkgName}.`);
|
|
7410
8270
|
})
|
|
7411
8271
|
.sort()
|
|
@@ -7418,7 +8278,7 @@ function includeLocalImport(code, dir, goFileName, tsx = false) {
|
|
|
7418
8278
|
parts.push(fileInline.trim());
|
|
7419
8279
|
if (fileOutside.trim())
|
|
7420
8280
|
parts.push(fileOutside.trim());
|
|
7421
|
-
localImportFiles.set(goFileName, parts.join('\n\n'));
|
|
8281
|
+
localImportFiles.set(goFileName, applyLineDirectives(parts.join('\n\n'), goFileName));
|
|
7422
8282
|
// Restore main-file state; helper-provided packages carry over so the main
|
|
7423
8283
|
// file (which carries the helper sources) imports them.
|
|
7424
8284
|
outsideNodes = savedOutsideNodes;
|
|
@@ -7426,29 +8286,74 @@ function includeLocalImport(code, dir, goFileName, tsx = false) {
|
|
|
7426
8286
|
for (const p of savedPackages)
|
|
7427
8287
|
importedPackages.add(p);
|
|
7428
8288
|
currentFileIsTsx = prevTsx;
|
|
8289
|
+
for (const p of savedHelperPackages)
|
|
8290
|
+
helperProvidedPackages.add(p);
|
|
7429
8291
|
for (const p of helperProvidedPackages)
|
|
7430
8292
|
importedPackages.add(p);
|
|
7431
8293
|
currentFileDir = prevDir;
|
|
8294
|
+
currentFileName = prevFileName;
|
|
7432
8295
|
}
|
|
7433
8296
|
// Go source without its string literals (for detecting real package use)
|
|
7434
8297
|
function stripGoStrings(code) {
|
|
7435
8298
|
return code.replace(/"(?:[^"\\\n]|\\.)*"|`[^`]*`/g, '""');
|
|
7436
8299
|
}
|
|
7437
|
-
|
|
7438
|
-
|
|
8300
|
+
// go:builtin is virtual: ref<T> → new(T), chan<T> → make(chan T),
|
|
8301
|
+
// GoError → *TnError. No Go import; local names map to inline emitters.
|
|
8302
|
+
function registerGoBuiltinAliases(node) {
|
|
7439
8303
|
if (!node.importClause)
|
|
7440
8304
|
return;
|
|
7441
8305
|
const clause = node.importClause;
|
|
8306
|
+
if (clause.name)
|
|
8307
|
+
importAliases.set(clause.name.text, 'go:builtin');
|
|
8308
|
+
const bindings = clause.namedBindings;
|
|
8309
|
+
if (!bindings)
|
|
8310
|
+
return;
|
|
8311
|
+
if (isNamespaceImport(bindings)) {
|
|
8312
|
+
importAliases.set(bindings.name.text, 'go:builtin');
|
|
8313
|
+
return;
|
|
8314
|
+
}
|
|
8315
|
+
if (isNamedImports(bindings)) {
|
|
8316
|
+
for (const el of bindings.elements ?? []) {
|
|
8317
|
+
if (el.isTypeOnly)
|
|
8318
|
+
continue;
|
|
8319
|
+
const localName = el.name.text;
|
|
8320
|
+
const importedName = el.propertyName?.text ?? localName;
|
|
8321
|
+
importAliases.set(localName, `go:builtin.${importedName}`);
|
|
8322
|
+
registerGoBuiltin(localName, importedName);
|
|
8323
|
+
}
|
|
8324
|
+
}
|
|
8325
|
+
}
|
|
8326
|
+
function registerGoPackageAliases(node, goPkg) {
|
|
8327
|
+
// Real package name from metadata (math/rand/v2 → rand); fall back to the
|
|
8328
|
+
// last path segment for packages without metadata.
|
|
8329
|
+
const info = goPackage(goPkg);
|
|
8330
|
+
const pkgName = info ? info.name : goPkg.split('/').pop();
|
|
8331
|
+
// Collision alias (crypto/rand vs math/rand): the emitted qualifier.
|
|
8332
|
+
const qualifier = goPathAliases[goPkg] ?? pkgName;
|
|
8333
|
+
goPackageLocalNames.add(pkgName);
|
|
8334
|
+
goPackageLocalNames.add(qualifier);
|
|
8335
|
+
const named = [];
|
|
8336
|
+
let defaultName = null;
|
|
8337
|
+
let namespaceName = null;
|
|
8338
|
+
if (!node.importClause) {
|
|
8339
|
+
registerGoImport(goPkg, qualifier, null, null, []);
|
|
8340
|
+
return;
|
|
8341
|
+
}
|
|
8342
|
+
const clause = node.importClause;
|
|
7442
8343
|
// Default import: `import fmt from 'go:fmt'` or `import myFmt from 'go:fmt'`
|
|
7443
|
-
if (clause.name
|
|
7444
|
-
|
|
8344
|
+
if (clause.name) {
|
|
8345
|
+
defaultName = clause.name.text;
|
|
8346
|
+
if (clause.name.text !== pkgName && clause.name.text !== qualifier) {
|
|
8347
|
+
importAliases.set(clause.name.text, qualifier);
|
|
8348
|
+
}
|
|
7445
8349
|
}
|
|
7446
8350
|
if (clause.namedBindings) {
|
|
7447
8351
|
if (isNamespaceImport(clause.namedBindings)) {
|
|
7448
8352
|
// `import * as myFmt from 'go:fmt'`
|
|
7449
8353
|
const localName = clause.namedBindings.name.text;
|
|
7450
|
-
|
|
7451
|
-
|
|
8354
|
+
namespaceName = localName;
|
|
8355
|
+
if (localName !== pkgName && localName !== qualifier) {
|
|
8356
|
+
importAliases.set(localName, qualifier);
|
|
7452
8357
|
}
|
|
7453
8358
|
}
|
|
7454
8359
|
else if (isNamedImports(clause.namedBindings)) {
|
|
@@ -7458,10 +8363,12 @@ function registerGoPackageAliases(node, goPkg) {
|
|
|
7458
8363
|
continue;
|
|
7459
8364
|
const localName = el.name.text;
|
|
7460
8365
|
const importedName = el.propertyName?.text ?? localName;
|
|
7461
|
-
importAliases.set(localName, `${
|
|
8366
|
+
importAliases.set(localName, `${qualifier}.${importedName}`);
|
|
8367
|
+
named.push({ imported: importedName, local: localName });
|
|
7462
8368
|
}
|
|
7463
8369
|
}
|
|
7464
8370
|
}
|
|
8371
|
+
registerGoImport(goPkg, qualifier, defaultName, namespaceName, named);
|
|
7465
8372
|
}
|
|
7466
8373
|
function getImportLocalName(node) {
|
|
7467
8374
|
const clause = node.importClause;
|
|
@@ -7489,7 +8396,10 @@ function needPkg(pkg) {
|
|
|
7489
8396
|
const helperRequires = {
|
|
7490
8397
|
objectAssign: ['orderedMap'],
|
|
7491
8398
|
promiseAll: ['orderedMap', 'asyncWait', 'promise'],
|
|
7492
|
-
promise: ['error'],
|
|
8399
|
+
promise: ['error', 'eventLoop', 'asyncWait'],
|
|
8400
|
+
goMust: ['error'],
|
|
8401
|
+
bigint: ['error'],
|
|
8402
|
+
goBlocking: ['error', 'eventLoop'],
|
|
7493
8403
|
decorators: [],
|
|
7494
8404
|
jsx: [],
|
|
7495
8405
|
objectIntegrity: [],
|
|
@@ -7499,9 +8409,21 @@ const helperRequires = {
|
|
|
7499
8409
|
fancyRegex: ['orderedMap'],
|
|
7500
8410
|
dynamic: ['error'],
|
|
7501
8411
|
moduleUrl: ['pathToFileURL'],
|
|
7502
|
-
timerWait: [],
|
|
7503
|
-
|
|
8412
|
+
timerWait: ['eventLoop'],
|
|
8413
|
+
clock: [],
|
|
8414
|
+
asyncWait: ['eventLoop'],
|
|
8415
|
+
eventLoop: []
|
|
7504
8416
|
};
|
|
8417
|
+
// Helpers with OS-specific variants (Windows APIs that would not compile
|
|
8418
|
+
// elsewhere) are required as '<id>@os': the target OS's variant
|
|
8419
|
+
// ('<id>@windows') when there is one, else '<id>@other'
|
|
8420
|
+
function platformHelper(id) {
|
|
8421
|
+
if (!id.endsWith('@os'))
|
|
8422
|
+
return id;
|
|
8423
|
+
const base = id.slice(0, id.length - 3);
|
|
8424
|
+
const specific = `${base}@${targetGoos}`;
|
|
8425
|
+
return goHelpers[specific] !== undefined || nodeRegistry.goHelpers[specific] !== undefined ? specific : `${base}@other`;
|
|
8426
|
+
}
|
|
7505
8427
|
function useHelper(id) {
|
|
7506
8428
|
if (usedHelpers.has(id))
|
|
7507
8429
|
return;
|
|
@@ -7510,159 +8432,85 @@ function useHelper(id) {
|
|
|
7510
8432
|
if (id === 'namedGroups') {
|
|
7511
8433
|
interfacePropertyTypes.set('TnMatch', new Map([['groups', '*TnMap[string, string]']]));
|
|
7512
8434
|
}
|
|
7513
|
-
for (const dep of helperRequires[id] ?? [])
|
|
7514
|
-
useHelper(dep);
|
|
7515
|
-
for (const pkg of helperPackages[id] ?? []) {
|
|
8435
|
+
for (const dep of helperRequires[id] ?? nodeRegistry.helperRequires[id] ?? [])
|
|
8436
|
+
useHelper(platformHelper(dep));
|
|
8437
|
+
for (const pkg of helperPackages[id] ?? nodeRegistry.helperPackages[id] ?? []) {
|
|
7516
8438
|
importedPackages.add(pkg);
|
|
7517
8439
|
helperProvidedPackages.add(pkg);
|
|
7518
8440
|
}
|
|
7519
8441
|
}
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
useHelper('fileURLToPath');
|
|
7593
|
-
return `TnFileURLToPath(${args[0]})`;
|
|
7594
|
-
},
|
|
7595
|
-
pathToFileURL: (args) => {
|
|
7596
|
-
useHelper('pathToFileURL');
|
|
7597
|
-
return `TnPathToFileURL(${args[0]})`;
|
|
7598
|
-
}
|
|
7599
|
-
}
|
|
7600
|
-
},
|
|
7601
|
-
os: {
|
|
7602
|
-
functions: {
|
|
7603
|
-
platform: () => {
|
|
7604
|
-
useHelper('osPlatform');
|
|
7605
|
-
return 'TnOsPlatform()';
|
|
7606
|
-
},
|
|
7607
|
-
homedir: () => {
|
|
7608
|
-
needPkg('os');
|
|
7609
|
-
return `func() string { h, _ := os.UserHomeDir(); return h }()`;
|
|
7610
|
-
},
|
|
7611
|
-
tmpdir: () => {
|
|
7612
|
-
needPkg('os');
|
|
7613
|
-
return 'os.TempDir()';
|
|
7614
|
-
}
|
|
7615
|
-
}
|
|
7616
|
-
},
|
|
7617
|
-
child_process: {
|
|
7618
|
-
functions: {
|
|
7619
|
-
// exec(command) — run through the shell, returns { stdout, stderr, status }
|
|
7620
|
-
exec: (args) => {
|
|
7621
|
-
useHelper('exec');
|
|
7622
|
-
return `TnExecShell(${args[0]})`;
|
|
7623
|
-
},
|
|
7624
|
-
// execSync(command) — run through the shell, returns stdout (panics on failure)
|
|
7625
|
-
execSync: (args) => {
|
|
7626
|
-
useHelper('exec');
|
|
7627
|
-
useHelper('execSync');
|
|
7628
|
-
return `TnExecShellSync(${args[0]})`;
|
|
7629
|
-
},
|
|
7630
|
-
// spawnSync(file, args, options) — matches Node's sync API.
|
|
7631
|
-
// options with `inherit` → run with the parent's stdio (returns exit code);
|
|
7632
|
-
// otherwise output is captured. Fields match Node: stdout/stderr/status.
|
|
7633
|
-
spawnSync: (args) => {
|
|
7634
|
-
useHelper('exec');
|
|
7635
|
-
useHelper('runInherit');
|
|
7636
|
-
// Untyped array literals visit as `[] {…}` — give them the []string
|
|
7637
|
-
// type the variadic helper needs (`[]string{…}...` is valid Go spread)
|
|
7638
|
-
const arrArg = args[1] ?? '[]string{}';
|
|
7639
|
-
const typedArg = arrArg.startsWith('[]') && !arrArg.startsWith('[]string')
|
|
7640
|
-
? arrArg.replace(/^\[\]\S* \{/, '[]string{')
|
|
7641
|
-
: arrArg;
|
|
7642
|
-
if (args[2]?.includes('inherit')) {
|
|
7643
|
-
return `TnRunInherit(${args[0]}, ${typedArg}...)`;
|
|
7644
|
-
}
|
|
7645
|
-
if (args[2]?.includes('input')) {
|
|
7646
|
-
// {input: expr, ...} → run with expr as stdin
|
|
7647
|
-
useHelper('execInput');
|
|
7648
|
-
const m = /input:\s*([^,}]+)/.exec(args[2]);
|
|
7649
|
-
const inputExpr = m ? m[1] : '""';
|
|
7650
|
-
return `TnExecInput(${args[0]}, ${inputExpr}, ${typedArg}...)`;
|
|
7651
|
-
}
|
|
7652
|
-
return `TnExec(${args[0]}, ${typedArg}...)`;
|
|
7653
|
-
}
|
|
7654
|
-
}
|
|
7655
|
-
},
|
|
7656
|
-
readline: {
|
|
7657
|
-
functions: {
|
|
7658
|
-
// question(prompt) — print prompt and read one line from stdin (synchronous)
|
|
7659
|
-
question: (args) => {
|
|
7660
|
-
useHelper('question');
|
|
7661
|
-
return `TnQuestion(${args[0] ?? '""'})`;
|
|
7662
|
-
}
|
|
7663
|
-
}
|
|
7664
|
-
}
|
|
7665
|
-
};
|
|
8442
|
+
// Node.js standard library (src/node/*): functions, values and classes the
|
|
8443
|
+
// transpiler resolves imports and Node globals against
|
|
8444
|
+
// (services are bound in transpileToNative: a Go package-level initializer
|
|
8445
|
+
// referring to visit() would be an initialization cycle)
|
|
8446
|
+
const nodeRegistry = createNodeRegistry();
|
|
8447
|
+
function bindNodeRegistryServices() {
|
|
8448
|
+
nodeRegistry.pkg = needPkg;
|
|
8449
|
+
nodeRegistry.helper = useHelper;
|
|
8450
|
+
nodeRegistry.temp = getTempName;
|
|
8451
|
+
nodeRegistry.typeOf = (node) => inferExpressionType(node) ?? '';
|
|
8452
|
+
nodeRegistry.visit = (node) => visit(node);
|
|
8453
|
+
nodeRegistry.categoryOf = (node) => resolveExpressionType(node) ?? '';
|
|
8454
|
+
bindConvertServices({
|
|
8455
|
+
pkg: needPkg,
|
|
8456
|
+
helper: useHelper,
|
|
8457
|
+
typeOf: (node) => inferExpressionType(node) ?? '',
|
|
8458
|
+
localType: goLocalType
|
|
8459
|
+
});
|
|
8460
|
+
bindBridgeServices({
|
|
8461
|
+
pkg: needPkg,
|
|
8462
|
+
helper: useHelper,
|
|
8463
|
+
temp: getTempName,
|
|
8464
|
+
typeOf: (node) => inferExpressionType(node) ?? '',
|
|
8465
|
+
visit: (node) => visit(node),
|
|
8466
|
+
categoryOf: (node) => resolveExpressionType(node) ?? '',
|
|
8467
|
+
goTypeOf: (node) => getType(node),
|
|
8468
|
+
isIdentifier: (node) => isIdentifier(node),
|
|
8469
|
+
isPropertyAccess: (node) => isPropertyAccessExpression(node),
|
|
8470
|
+
isArrowFunction: (node) => isArrowFunction(node),
|
|
8471
|
+
isFunctionExpression: (node) => isFunctionExpression(node),
|
|
8472
|
+
isObjectLiteral: (node) => isObjectLiteralExpression(node),
|
|
8473
|
+
declareLocal: (name, goType) => registerLocalVariable(name, goType)
|
|
8474
|
+
});
|
|
8475
|
+
}
|
|
8476
|
+
registerCore(nodeRegistry);
|
|
8477
|
+
registerBuffer(nodeRegistry);
|
|
8478
|
+
registerPath(nodeRegistry);
|
|
8479
|
+
registerFs(nodeRegistry);
|
|
8480
|
+
registerUrl(nodeRegistry);
|
|
8481
|
+
registerOs(nodeRegistry);
|
|
8482
|
+
registerProcess(nodeRegistry);
|
|
8483
|
+
registerUtil(nodeRegistry);
|
|
8484
|
+
registerUtilExtra(nodeRegistry);
|
|
8485
|
+
registerConsole(nodeRegistry);
|
|
8486
|
+
registerAssert(nodeRegistry);
|
|
8487
|
+
registerPunycode(nodeRegistry);
|
|
8488
|
+
registerQuerystring(nodeRegistry);
|
|
8489
|
+
registerStringDecoder(nodeRegistry);
|
|
8490
|
+
registerCrypto(nodeRegistry);
|
|
8491
|
+
registerCryptoKeys(nodeRegistry);
|
|
8492
|
+
registerZlib(nodeRegistry);
|
|
8493
|
+
registerTimers(nodeRegistry);
|
|
8494
|
+
registerEvents(nodeRegistry);
|
|
8495
|
+
registerStream(nodeRegistry);
|
|
8496
|
+
registerDiagnosticsChannel(nodeRegistry);
|
|
8497
|
+
registerConstants(nodeRegistry);
|
|
8498
|
+
registerChildProcess(nodeRegistry);
|
|
8499
|
+
registerReadline(nodeRegistry);
|
|
8500
|
+
registerNet(nodeRegistry);
|
|
8501
|
+
registerDns(nodeRegistry);
|
|
8502
|
+
registerTls(nodeRegistry);
|
|
8503
|
+
registerHttp(nodeRegistry);
|
|
8504
|
+
registerDgram(nodeRegistry);
|
|
8505
|
+
registerSqlite(nodeRegistry);
|
|
8506
|
+
registerFetch(nodeRegistry);
|
|
8507
|
+
registerStreamWeb(nodeRegistry);
|
|
8508
|
+
registerAsyncHooks(nodeRegistry);
|
|
8509
|
+
registerTest(nodeRegistry);
|
|
8510
|
+
registerWorkerThreads(nodeRegistry);
|
|
8511
|
+
registerCluster(nodeRegistry);
|
|
8512
|
+
registerHttp2(nodeRegistry);
|
|
8513
|
+
registerSystemExtra(nodeRegistry);
|
|
7666
8514
|
// Go packages required by each helper, registered when the helper is used.
|
|
7667
8515
|
const helperPackages = {
|
|
7668
8516
|
regexReplaceFirst: ['regexp'],
|
|
@@ -7672,38 +8520,31 @@ const helperPackages = {
|
|
|
7672
8520
|
typeOf: ['reflect'],
|
|
7673
8521
|
dynamic: ['math', 'strings', 'fmt', 'sort', 'reflect', 'strconv', 'regexp', 'unsafe'],
|
|
7674
8522
|
regexReplaceFunc: ['regexp'],
|
|
7675
|
-
|
|
7676
|
-
writeFile: ['os'],
|
|
7677
|
-
appendFile: ['os'],
|
|
7678
|
-
mkdirAll: ['os'],
|
|
7679
|
-
readDir: ['os'],
|
|
7680
|
-
copyFile: ['os', 'io', 'path/filepath'],
|
|
7681
|
-
removeAll: ['os'],
|
|
7682
|
-
fileURLToPath: ['net/url', 'path/filepath', 'strings'],
|
|
7683
|
-
pathToFileURL: ['net/url', 'path/filepath'],
|
|
8523
|
+
pathToFileURL: ['net/url', 'path/filepath', 'strings'],
|
|
7684
8524
|
osPlatform: ['runtime'],
|
|
7685
8525
|
error: [],
|
|
7686
|
-
bigint: ['math/big'],
|
|
8526
|
+
bigint: ['math/big', 'math'],
|
|
7687
8527
|
symbol: [],
|
|
7688
8528
|
fancyRegex: ['regexp', 'strings', 'strconv', 'fmt'],
|
|
7689
8529
|
namedGroups: ['regexp'],
|
|
7690
8530
|
moduleUrl: ['os'],
|
|
7691
|
-
asyncWait: [
|
|
7692
|
-
timerWait: ['sync', 'time'],
|
|
8531
|
+
asyncWait: [],
|
|
8532
|
+
timerWait: ['sync', 'sync/atomic', 'time'],
|
|
8533
|
+
clock: ['time'],
|
|
8534
|
+
eventLoop: ['sync', 'sync/atomic', 'time'],
|
|
7693
8535
|
uri: ['strings', 'fmt', 'net/url'],
|
|
7694
8536
|
promiseAll: [],
|
|
7695
8537
|
promise: ['sync'],
|
|
8538
|
+
goMust: [],
|
|
8539
|
+
goNums: [],
|
|
8540
|
+
goStrings: [],
|
|
8541
|
+
goBlocking: [],
|
|
7696
8542
|
decorators: [],
|
|
7697
8543
|
jsx: ['strings', 'html', 'reflect', 'strconv', 'fmt'],
|
|
7698
8544
|
objectIntegrity: ['sync', 'reflect'],
|
|
7699
8545
|
structuredClone: ['reflect', 'unsafe'],
|
|
7700
8546
|
globalThis: [],
|
|
7701
|
-
exec: ['os/exec', 'bytes', 'runtime'],
|
|
7702
|
-
execSync: ['os/exec', 'bytes', 'runtime'],
|
|
7703
|
-
runInherit: ['os/exec', 'os'],
|
|
7704
|
-
question: ['bufio', 'fmt', 'os', 'strings'],
|
|
7705
8547
|
getenv: ['os'],
|
|
7706
|
-
execInput: ['os/exec', 'bytes', 'strings']
|
|
7707
8548
|
};
|
|
7708
8549
|
// Go helper sources emitted (once) into the main output file when used.
|
|
7709
8550
|
// Field names are intentionally lowercase: every generated file is `package main`,
|
|
@@ -7723,10 +8564,27 @@ func TnGet(obj interface{}, key string) interface{} {
|
|
|
7723
8564
|
if key == "length" {
|
|
7724
8565
|
return float64(len(o))
|
|
7725
8566
|
}
|
|
8567
|
+
case interface{ TnProp(string) (interface{}, bool) }:
|
|
8568
|
+
if v, ok := o.TnProp(key); ok || key != "length" {
|
|
8569
|
+
return v
|
|
8570
|
+
}
|
|
7726
8571
|
case *TnError:
|
|
7727
8572
|
if key == "message" {
|
|
7728
8573
|
return o.message
|
|
7729
8574
|
}
|
|
8575
|
+
if key == "code" {
|
|
8576
|
+
if o.code == "" {
|
|
8577
|
+
return o.prop("code")
|
|
8578
|
+
}
|
|
8579
|
+
return o.code
|
|
8580
|
+
}
|
|
8581
|
+
if key == "name" {
|
|
8582
|
+
return o.Get_name()
|
|
8583
|
+
}
|
|
8584
|
+
if key == "stack" {
|
|
8585
|
+
return o.Get_stack()
|
|
8586
|
+
}
|
|
8587
|
+
return o.prop(key)
|
|
7730
8588
|
}
|
|
7731
8589
|
rv := reflect.ValueOf(obj)
|
|
7732
8590
|
if rv.Kind() == reflect.Pointer && !rv.IsNil() && rv.Elem().Kind() == reflect.Struct {
|
|
@@ -7743,6 +8601,27 @@ func TnGet(obj interface{}, key string) interface{} {
|
|
|
7743
8601
|
return nil
|
|
7744
8602
|
}
|
|
7745
8603
|
|
|
8604
|
+
// new Error(message, { cause })
|
|
8605
|
+
func TnErrorWithOptions(e *TnError, options interface{}) *TnError {
|
|
8606
|
+
if options == nil {
|
|
8607
|
+
return e
|
|
8608
|
+
}
|
|
8609
|
+
if e.props == nil {
|
|
8610
|
+
e.props = map[string]interface{}{}
|
|
8611
|
+
}
|
|
8612
|
+
if m, ok := options.(map[string]interface{}); ok {
|
|
8613
|
+
if cause, present := m["cause"]; present {
|
|
8614
|
+
e.props["cause"] = cause
|
|
8615
|
+
}
|
|
8616
|
+
return e
|
|
8617
|
+
}
|
|
8618
|
+
rv := reflect.ValueOf(options)
|
|
8619
|
+
if rv.Kind() == reflect.Pointer && !rv.IsNil() && rv.Elem().Kind() == reflect.Struct && rv.Elem().FieldByName("cause").IsValid() {
|
|
8620
|
+
e.props["cause"] = TnGet(options, "cause")
|
|
8621
|
+
}
|
|
8622
|
+
return e
|
|
8623
|
+
}
|
|
8624
|
+
|
|
7746
8625
|
func TnSet(obj interface{}, key string, value interface{}) {
|
|
7747
8626
|
if o, ok := obj.(map[string]interface{}); ok {
|
|
7748
8627
|
o[key] = value
|
|
@@ -7781,6 +8660,15 @@ func TnIndex(obj interface{}, key interface{}) interface{} {
|
|
|
7781
8660
|
if i, ok := key.(float64); ok && i >= 0 && int(i) < len(o) {
|
|
7782
8661
|
return string(o[int(i)])
|
|
7783
8662
|
}
|
|
8663
|
+
case []byte:
|
|
8664
|
+
if i, ok := key.(float64); ok && i >= 0 && int(i) < len(o) {
|
|
8665
|
+
return float64(o[int(i)])
|
|
8666
|
+
}
|
|
8667
|
+
case interface{ TnProp(string) (interface{}, bool) }:
|
|
8668
|
+
if k, ok := key.(string); ok {
|
|
8669
|
+
v, _ := o.TnProp(k)
|
|
8670
|
+
return v
|
|
8671
|
+
}
|
|
7784
8672
|
}
|
|
7785
8673
|
return nil
|
|
7786
8674
|
}
|
|
@@ -7831,14 +8719,52 @@ func TnLength(v interface{}) float64 {
|
|
|
7831
8719
|
}
|
|
7832
8720
|
|
|
7833
8721
|
// A value as JS prints it in a template literal (nil pointers are "undefined")
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
8722
|
+
// String(v) of a dynamic value, as JS renders it
|
|
8723
|
+
func TnJSString(v interface{}) string {
|
|
8724
|
+
switch x := v.(type) {
|
|
8725
|
+
case nil:
|
|
7837
8726
|
return "undefined"
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
8727
|
+
case string:
|
|
8728
|
+
return x
|
|
8729
|
+
case float64:
|
|
8730
|
+
return TnNumStr(x)
|
|
8731
|
+
case bool:
|
|
8732
|
+
if x {
|
|
8733
|
+
return "true"
|
|
8734
|
+
}
|
|
8735
|
+
return "false"
|
|
8736
|
+
case []byte:
|
|
8737
|
+
return strings.ToValidUTF8(string(x), "�")
|
|
8738
|
+
case []interface{}:
|
|
8739
|
+
parts := make([]string, len(x))
|
|
8740
|
+
for i, e := range x {
|
|
8741
|
+
if e != nil {
|
|
8742
|
+
parts[i] = TnJSString(e)
|
|
8743
|
+
}
|
|
8744
|
+
}
|
|
8745
|
+
return strings.Join(parts, ",")
|
|
8746
|
+
case []string:
|
|
8747
|
+
return strings.Join(x, ",")
|
|
8748
|
+
case map[string]interface{}:
|
|
8749
|
+
return "[object Object]"
|
|
8750
|
+
case *TnError:
|
|
8751
|
+
return x.Get_name() + ": " + x.message
|
|
8752
|
+
case fmt.Stringer:
|
|
8753
|
+
return x.String()
|
|
8754
|
+
case interface{ toString() string }:
|
|
8755
|
+
return x.toString()
|
|
8756
|
+
}
|
|
8757
|
+
return fmt.Sprintf("%v", v)
|
|
8758
|
+
}
|
|
8759
|
+
|
|
8760
|
+
func TnFormat(v interface{}) string {
|
|
8761
|
+
rv := reflect.ValueOf(v)
|
|
8762
|
+
if !rv.IsValid() || (rv.Kind() == reflect.Pointer && rv.IsNil()) {
|
|
8763
|
+
return "undefined"
|
|
8764
|
+
}
|
|
8765
|
+
if rv.Kind() == reflect.Pointer {
|
|
8766
|
+
return fmt.Sprintf("%v", rv.Elem().Interface())
|
|
8767
|
+
}
|
|
7842
8768
|
return fmt.Sprintf("%v", v)
|
|
7843
8769
|
}
|
|
7844
8770
|
|
|
@@ -8006,6 +8932,9 @@ func TnHas(obj interface{}, key interface{}) bool {
|
|
|
8006
8932
|
}
|
|
8007
8933
|
|
|
8008
8934
|
func TnKeys(obj interface{}) []string {
|
|
8935
|
+
if record, ok := obj.(interface{ Keys() []string }); ok {
|
|
8936
|
+
return append([]string{}, record.Keys()...)
|
|
8937
|
+
}
|
|
8009
8938
|
o, _ := obj.(map[string]interface{})
|
|
8010
8939
|
keys := make([]string, 0, len(o))
|
|
8011
8940
|
for k := range o {
|
|
@@ -8093,6 +9022,10 @@ func TnAsPtr[T any](v interface{}) *T {
|
|
|
8093
9022
|
if t, ok := v.(T); ok {
|
|
8094
9023
|
return &t
|
|
8095
9024
|
}
|
|
9025
|
+
var zero T
|
|
9026
|
+
if converted, ok := TnConvertDynamic(v, reflect.TypeOf(&zero)); ok {
|
|
9027
|
+
return converted.Interface().(*T)
|
|
9028
|
+
}
|
|
8096
9029
|
return nil
|
|
8097
9030
|
}
|
|
8098
9031
|
|
|
@@ -8100,10 +9033,93 @@ func TnAs[T any](v interface{}) T {
|
|
|
8100
9033
|
if p, ok := v.(*T); ok && p != nil {
|
|
8101
9034
|
return *p
|
|
8102
9035
|
}
|
|
8103
|
-
t,
|
|
9036
|
+
t, ok := v.(T)
|
|
9037
|
+
if !ok && v != nil {
|
|
9038
|
+
if converted, cok := TnConvertDynamic(v, reflect.TypeOf((*T)(nil)).Elem()); cok {
|
|
9039
|
+
return converted.Interface().(T)
|
|
9040
|
+
}
|
|
9041
|
+
}
|
|
8104
9042
|
return t
|
|
8105
9043
|
}
|
|
8106
9044
|
|
|
9045
|
+
// Converts a dynamic value (decoded JSON: maps, []interface{}, float64 …) to a
|
|
9046
|
+
// Go type: structs (and pointers to them) field by field, slices element by
|
|
9047
|
+
// element, numbers between kinds
|
|
9048
|
+
func TnConvertDynamic(v interface{}, t reflect.Type) (reflect.Value, bool) {
|
|
9049
|
+
if v == nil {
|
|
9050
|
+
return reflect.Zero(t), true
|
|
9051
|
+
}
|
|
9052
|
+
rv := reflect.ValueOf(v)
|
|
9053
|
+
if rv.Type().AssignableTo(t) {
|
|
9054
|
+
return rv, true
|
|
9055
|
+
}
|
|
9056
|
+
switch t.Kind() {
|
|
9057
|
+
case reflect.Interface:
|
|
9058
|
+
if rv.Type().Implements(t) {
|
|
9059
|
+
return rv, true
|
|
9060
|
+
}
|
|
9061
|
+
case reflect.Pointer:
|
|
9062
|
+
if rv.Kind() == reflect.Pointer && rv.IsNil() {
|
|
9063
|
+
return reflect.Zero(t), true
|
|
9064
|
+
}
|
|
9065
|
+
inner, ok := TnConvertDynamic(v, t.Elem())
|
|
9066
|
+
if !ok {
|
|
9067
|
+
return reflect.Value{}, false
|
|
9068
|
+
}
|
|
9069
|
+
p := reflect.New(t.Elem())
|
|
9070
|
+
p.Elem().Set(inner)
|
|
9071
|
+
return p, true
|
|
9072
|
+
case reflect.Struct:
|
|
9073
|
+
m, ok := v.(map[string]interface{})
|
|
9074
|
+
if props, isRecord := v.(interface{ Entries() [][]interface{} }); !ok && isRecord {
|
|
9075
|
+
// A string-keyed TnMap (a Record, a node:sqlite row)
|
|
9076
|
+
m, ok = map[string]interface{}{}, true
|
|
9077
|
+
for _, e := range props.Entries() {
|
|
9078
|
+
if k, isString := e[0].(string); isString {
|
|
9079
|
+
m[k] = e[1]
|
|
9080
|
+
}
|
|
9081
|
+
}
|
|
9082
|
+
}
|
|
9083
|
+
if !ok {
|
|
9084
|
+
return reflect.Value{}, false
|
|
9085
|
+
}
|
|
9086
|
+
out := reflect.New(t).Elem()
|
|
9087
|
+
for i := 0; i < t.NumField(); i++ {
|
|
9088
|
+
field := t.Field(i)
|
|
9089
|
+
name := strings.TrimSuffix(field.Name, "_")
|
|
9090
|
+
value, present := m[name]
|
|
9091
|
+
if !present {
|
|
9092
|
+
continue
|
|
9093
|
+
}
|
|
9094
|
+
converted, ok := TnConvertDynamic(value, field.Type)
|
|
9095
|
+
if !ok {
|
|
9096
|
+
continue
|
|
9097
|
+
}
|
|
9098
|
+
f := out.Field(i)
|
|
9099
|
+
reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem().Set(converted)
|
|
9100
|
+
}
|
|
9101
|
+
return out, true
|
|
9102
|
+
case reflect.Slice:
|
|
9103
|
+
if rv.Kind() != reflect.Slice {
|
|
9104
|
+
return reflect.Value{}, false
|
|
9105
|
+
}
|
|
9106
|
+
out := reflect.MakeSlice(t, rv.Len(), rv.Len())
|
|
9107
|
+
for i := 0; i < rv.Len(); i++ {
|
|
9108
|
+
converted, ok := TnConvertDynamic(rv.Index(i).Interface(), t.Elem())
|
|
9109
|
+
if !ok {
|
|
9110
|
+
return reflect.Value{}, false
|
|
9111
|
+
}
|
|
9112
|
+
out.Index(i).Set(converted)
|
|
9113
|
+
}
|
|
9114
|
+
return out, true
|
|
9115
|
+
case reflect.Float64, reflect.Float32, reflect.Int, reflect.Int64, reflect.Int32:
|
|
9116
|
+
if rv.CanConvert(t) && rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Float64 {
|
|
9117
|
+
return rv.Convert(t), true
|
|
9118
|
+
}
|
|
9119
|
+
}
|
|
9120
|
+
return reflect.Value{}, false
|
|
9121
|
+
}
|
|
9122
|
+
|
|
8107
9123
|
func TnTruthy(v interface{}) bool {
|
|
8108
9124
|
switch t := v.(type) {
|
|
8109
9125
|
case nil:
|
|
@@ -8243,6 +9259,31 @@ func tnJSONWrite(buf *bytes.Buffer, rv reflect.Value, indent string, depth int)
|
|
|
8243
9259
|
}
|
|
8244
9260
|
buf.WriteByte('}')
|
|
8245
9261
|
case reflect.Struct:
|
|
9262
|
+
// Records (TnMap) keep their key order; reached through a pointer, the
|
|
9263
|
+
// struct value needs Addr() for pointer-receiver methods
|
|
9264
|
+
if rv.CanAddr() {
|
|
9265
|
+
if m, ok := rv.Addr().Interface().(interface{ Entries() [][]interface{} }); ok {
|
|
9266
|
+
buf.WriteByte('{')
|
|
9267
|
+
for i, e := range m.Entries() {
|
|
9268
|
+
if i > 0 {
|
|
9269
|
+
buf.WriteByte(',')
|
|
9270
|
+
}
|
|
9271
|
+
tnJSONNewline(buf, indent, depth+1)
|
|
9272
|
+
kd, _ := tnJSONMarshal(fmt.Sprint(e[0]))
|
|
9273
|
+
buf.Write(kd)
|
|
9274
|
+
buf.WriteByte(':')
|
|
9275
|
+
if indent != "" {
|
|
9276
|
+
buf.WriteByte(' ')
|
|
9277
|
+
}
|
|
9278
|
+
tnJSONWrite(buf, reflect.ValueOf(e[1]), indent, depth+1)
|
|
9279
|
+
}
|
|
9280
|
+
if len(m.Entries()) > 0 {
|
|
9281
|
+
tnJSONNewline(buf, indent, depth)
|
|
9282
|
+
}
|
|
9283
|
+
buf.WriteByte('}')
|
|
9284
|
+
return
|
|
9285
|
+
}
|
|
9286
|
+
}
|
|
8246
9287
|
if m, ok := rv.Interface().(json.Marshaler); ok {
|
|
8247
9288
|
data, err := m.MarshalJSON()
|
|
8248
9289
|
if err == nil {
|
|
@@ -8323,6 +9364,17 @@ func (m *TnMap[K, V]) Get(k K) V {
|
|
|
8323
9364
|
return m.values[k]
|
|
8324
9365
|
}
|
|
8325
9366
|
|
|
9367
|
+
// A string key's value when the map is held in a dynamic value (a Record or a
|
|
9368
|
+
// node:sqlite row read as \`row.name\`): TnGet finds it through this method
|
|
9369
|
+
func (m *TnMap[K, V]) TnProp(key string) (interface{}, bool) {
|
|
9370
|
+
k, ok := any(key).(K)
|
|
9371
|
+
if !ok || m == nil {
|
|
9372
|
+
return nil, false
|
|
9373
|
+
}
|
|
9374
|
+
v, present := m.values[k]
|
|
9375
|
+
return v, present
|
|
9376
|
+
}
|
|
9377
|
+
|
|
8326
9378
|
func (m *TnMap[K, V]) Lookup(k K) (V, bool) {
|
|
8327
9379
|
if m == nil {
|
|
8328
9380
|
var zero V
|
|
@@ -8447,6 +9499,40 @@ func (m *TnMap[K, V]) All() iter.Seq2[K, V] {
|
|
|
8447
9499
|
}
|
|
8448
9500
|
}
|
|
8449
9501
|
|
|
9502
|
+
// JSON objects decode in document order (json.Unmarshal into a ref)
|
|
9503
|
+
func (m *TnMap[K, V]) UnmarshalJSON(data []byte) error {
|
|
9504
|
+
dec := json.NewDecoder(bytes.NewReader(data))
|
|
9505
|
+
tok, err := dec.Token()
|
|
9506
|
+
if err != nil {
|
|
9507
|
+
return err
|
|
9508
|
+
}
|
|
9509
|
+
if delim, ok := tok.(json.Delim); !ok || delim != '{' {
|
|
9510
|
+
return fmt.Errorf("json: cannot unmarshal %v into an object", tok)
|
|
9511
|
+
}
|
|
9512
|
+
m.keys = nil
|
|
9513
|
+
m.values = map[K]V{}
|
|
9514
|
+
for dec.More() {
|
|
9515
|
+
tok, err := dec.Token()
|
|
9516
|
+
if err != nil {
|
|
9517
|
+
return err
|
|
9518
|
+
}
|
|
9519
|
+
name, _ := tok.(string)
|
|
9520
|
+
var key K
|
|
9521
|
+
if p, ok := any(&key).(*string); ok {
|
|
9522
|
+
*p = name
|
|
9523
|
+
} else if err := json.Unmarshal([]byte(name), &key); err != nil {
|
|
9524
|
+
return err
|
|
9525
|
+
}
|
|
9526
|
+
var value V
|
|
9527
|
+
if err := dec.Decode(&value); err != nil {
|
|
9528
|
+
return err
|
|
9529
|
+
}
|
|
9530
|
+
m.Set(key, value)
|
|
9531
|
+
}
|
|
9532
|
+
_, err = dec.Token()
|
|
9533
|
+
return err
|
|
9534
|
+
}
|
|
9535
|
+
|
|
8450
9536
|
// JSON objects keep insertion order
|
|
8451
9537
|
func (m *TnMap[K, V]) MarshalJSON() ([]byte, error) {
|
|
8452
9538
|
var buf bytes.Buffer
|
|
@@ -9424,81 +10510,17 @@ func TnGroup(groups []string, i int) string {
|
|
|
9424
10510
|
return groups[i]
|
|
9425
10511
|
}
|
|
9426
10512
|
return ""
|
|
9427
|
-
}`,
|
|
9428
|
-
readFile: `func TnReadFile(path string) string {
|
|
9429
|
-
data, err := os.ReadFile(path)
|
|
9430
|
-
if err != nil {
|
|
9431
|
-
panic(err)
|
|
9432
|
-
}
|
|
9433
|
-
return string(data)
|
|
9434
|
-
}`,
|
|
9435
|
-
writeFile: `func TnWriteFile(path string, content string) {
|
|
9436
|
-
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
|
9437
|
-
panic(err)
|
|
9438
|
-
}
|
|
9439
|
-
}`,
|
|
9440
|
-
appendFile: `func TnAppendFile(path string, content string) {
|
|
9441
|
-
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
|
9442
|
-
if err != nil {
|
|
9443
|
-
panic(err)
|
|
9444
|
-
}
|
|
9445
|
-
defer f.Close()
|
|
9446
|
-
if _, err := f.WriteString(content); err != nil {
|
|
9447
|
-
panic(err)
|
|
9448
|
-
}
|
|
9449
|
-
}`,
|
|
9450
|
-
mkdirAll: `func TnMkdirAll(path string) {
|
|
9451
|
-
if err := os.MkdirAll(path, 0o755); err != nil {
|
|
9452
|
-
panic(err)
|
|
9453
|
-
}
|
|
9454
|
-
}`,
|
|
9455
|
-
readDir: `func TnReadDir(path string) []string {
|
|
9456
|
-
entries, err := os.ReadDir(path)
|
|
9457
|
-
if err != nil {
|
|
9458
|
-
panic(err)
|
|
9459
|
-
}
|
|
9460
|
-
names := make([]string, 0, len(entries))
|
|
9461
|
-
for _, e := range entries {
|
|
9462
|
-
names = append(names, e.Name())
|
|
9463
|
-
}
|
|
9464
|
-
return names
|
|
9465
|
-
}`,
|
|
9466
|
-
copyFile: `func TnCopyFile(src string, dst string) {
|
|
9467
|
-
in, err := os.Open(src)
|
|
9468
|
-
if err != nil {
|
|
9469
|
-
panic(err)
|
|
9470
|
-
}
|
|
9471
|
-
defer in.Close()
|
|
9472
|
-
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
|
|
9473
|
-
panic(err)
|
|
9474
|
-
}
|
|
9475
|
-
out, err := os.Create(dst)
|
|
9476
|
-
if err != nil {
|
|
9477
|
-
panic(err)
|
|
9478
|
-
}
|
|
9479
|
-
defer out.Close()
|
|
9480
|
-
if _, err := io.Copy(out, in); err != nil {
|
|
9481
|
-
panic(err)
|
|
9482
|
-
}
|
|
9483
|
-
}`,
|
|
9484
|
-
removeAll: `func TnRemoveAll(path string) {
|
|
9485
|
-
if err := os.RemoveAll(path); err != nil {
|
|
9486
|
-
panic(err)
|
|
9487
|
-
}
|
|
9488
|
-
}`,
|
|
9489
|
-
fileURLToPath: `func TnFileURLToPath(u string) string {
|
|
9490
|
-
trimmed := strings.TrimPrefix(u, "file://")
|
|
9491
|
-
if unescaped, err := tnurl.PathUnescape(trimmed); err == nil {
|
|
9492
|
-
trimmed = unescaped
|
|
9493
|
-
}
|
|
9494
|
-
return filepath.FromSlash(trimmed)
|
|
9495
10513
|
}`,
|
|
9496
10514
|
pathToFileURL: `func TnPathToFileURL(path string) string {
|
|
9497
10515
|
abs, err := filepath.Abs(path)
|
|
9498
10516
|
if err != nil {
|
|
9499
10517
|
panic(err)
|
|
9500
10518
|
}
|
|
9501
|
-
|
|
10519
|
+
slashed := filepath.ToSlash(abs)
|
|
10520
|
+
if !strings.HasPrefix(slashed, "/") {
|
|
10521
|
+
slashed = "/" + slashed
|
|
10522
|
+
}
|
|
10523
|
+
return "file://" + (&tnurl.URL{Path: slashed}).EscapedPath()
|
|
9502
10524
|
}`,
|
|
9503
10525
|
moduleUrl: `// import.meta.url: the file URL of the running program
|
|
9504
10526
|
func TnModuleURL() string {
|
|
@@ -9517,22 +10539,223 @@ func TnModuleURL() string {
|
|
|
9517
10539
|
}
|
|
9518
10540
|
return runtime.GOOS
|
|
9519
10541
|
}`,
|
|
9520
|
-
error: `// TnError: JS Error values (thrown and caught) with a message
|
|
10542
|
+
error: `// TnError: JS Error values (thrown and caught) with a message; Node
|
|
10543
|
+
// errors also carry a code ('ENOENT', 'ERR_ASSERTION') and extra properties
|
|
9521
10544
|
type TnError struct {
|
|
9522
10545
|
message string
|
|
10546
|
+
code string
|
|
10547
|
+
props map[string]interface{}
|
|
10548
|
+
goErr error
|
|
9523
10549
|
}
|
|
9524
10550
|
|
|
9525
10551
|
func (e *TnError) Error() string { return e.message }
|
|
9526
10552
|
|
|
9527
10553
|
func TnNewError(message string) *TnError {
|
|
9528
10554
|
return &TnError{message: message}
|
|
10555
|
+
}
|
|
10556
|
+
|
|
10557
|
+
|
|
10558
|
+
// x instanceof Error / TypeError / …
|
|
10559
|
+
func TnIsErrorOf(v interface{}, name string) bool {
|
|
10560
|
+
if name == "Error" {
|
|
10561
|
+
_, ok := v.(error)
|
|
10562
|
+
return ok
|
|
10563
|
+
}
|
|
10564
|
+
e, ok := v.(*TnError)
|
|
10565
|
+
return ok && e.Get_name() == name
|
|
10566
|
+
}
|
|
10567
|
+
|
|
10568
|
+
// A Node error: name ('TypeError', 'AssertionError'), code and message
|
|
10569
|
+
func TnNodeError(name string, code string, message string) *TnError {
|
|
10570
|
+
return &TnError{message: message, code: code, props: map[string]interface{}{"name": name}}
|
|
10571
|
+
}
|
|
10572
|
+
|
|
10573
|
+
func (e *TnError) prop(key string) interface{} {
|
|
10574
|
+
if e.props == nil {
|
|
10575
|
+
return nil
|
|
10576
|
+
}
|
|
10577
|
+
return e.props[key]
|
|
10578
|
+
}
|
|
10579
|
+
|
|
10580
|
+
func (e *TnError) Get_name() string {
|
|
10581
|
+
if name, ok := e.prop("name").(string); ok {
|
|
10582
|
+
return name
|
|
10583
|
+
}
|
|
10584
|
+
return "Error"
|
|
10585
|
+
}
|
|
10586
|
+
|
|
10587
|
+
func (e *TnError) Get_stack() string { return e.Get_name() + ": " + e.message }
|
|
10588
|
+
|
|
10589
|
+
func (e *TnError) Get_syscall() string {
|
|
10590
|
+
s, _ := e.prop("syscall").(string)
|
|
10591
|
+
return s
|
|
10592
|
+
}
|
|
10593
|
+
|
|
10594
|
+
func (e *TnError) Get_path() string {
|
|
10595
|
+
s, _ := e.prop("path").(string)
|
|
10596
|
+
return s
|
|
10597
|
+
}
|
|
10598
|
+
|
|
10599
|
+
func (e *TnError) Get_errno() float64 {
|
|
10600
|
+
n, _ := e.prop("errno").(float64)
|
|
10601
|
+
return n
|
|
10602
|
+
}
|
|
10603
|
+
|
|
10604
|
+
func (e *TnError) Get_cause() interface{} { return e.prop("cause") }
|
|
10605
|
+
|
|
10606
|
+
func (e *TnError) Get_hostname() string {
|
|
10607
|
+
s, _ := e.prop("hostname").(string)
|
|
10608
|
+
return s
|
|
10609
|
+
}
|
|
10610
|
+
|
|
10611
|
+
func (e *TnError) Get_address() string {
|
|
10612
|
+
s, _ := e.prop("address").(string)
|
|
10613
|
+
return s
|
|
10614
|
+
}
|
|
10615
|
+
|
|
10616
|
+
func (e *TnError) Get_port() float64 {
|
|
10617
|
+
n, _ := e.prop("port").(float64)
|
|
10618
|
+
return n
|
|
10619
|
+
}
|
|
10620
|
+
|
|
10621
|
+
// AssertionError fields
|
|
10622
|
+
func (e *TnError) Get_actual() interface{} { return e.prop("actual") }
|
|
10623
|
+
|
|
10624
|
+
func (e *TnError) Get_expected() interface{} { return e.prop("expected") }
|
|
10625
|
+
|
|
10626
|
+
func (e *TnError) Get_operator() string {
|
|
10627
|
+
s, _ := e.prop("operator").(string)
|
|
10628
|
+
return s
|
|
10629
|
+
}
|
|
10630
|
+
|
|
10631
|
+
func (e *TnError) Get_generatedMessage() bool {
|
|
10632
|
+
b, _ := e.prop("generatedMessage").(bool)
|
|
10633
|
+
return b
|
|
10634
|
+
}`,
|
|
10635
|
+
asyncWait: `// Pending async work (promise callbacks, detached async calls) keeps the
|
|
10636
|
+
// event loop alive until it settles
|
|
10637
|
+
var __tnWaitGroup tnLoopGroup`,
|
|
10638
|
+
bigint: `// Base from the literal's prefix (0x / 0o / 0b), decimal otherwise
|
|
10639
|
+
func TnBigInt(s string) *tnbig.Int {
|
|
10640
|
+
v, ok := new(tnbig.Int).SetString(s, 0)
|
|
10641
|
+
if !ok {
|
|
10642
|
+
v, _ = new(tnbig.Int).SetString(s, 10)
|
|
10643
|
+
}
|
|
10644
|
+
return v
|
|
10645
|
+
}
|
|
10646
|
+
|
|
10647
|
+
// x === 1n where x is a dynamic (number | bigint) value
|
|
10648
|
+
func TnBigSame(v interface{}, b *tnbig.Int) bool {
|
|
10649
|
+
x, ok := v.(*tnbig.Int)
|
|
10650
|
+
return ok && x != nil && b != nil && x.Cmp(b) == 0
|
|
10651
|
+
}
|
|
10652
|
+
|
|
10653
|
+
// A dynamic value as a bigint operand: a bigint, or an integral number
|
|
10654
|
+
func TnToBigInt(v interface{}) *tnbig.Int {
|
|
10655
|
+
switch x := v.(type) {
|
|
10656
|
+
case *tnbig.Int:
|
|
10657
|
+
if x != nil {
|
|
10658
|
+
return x
|
|
10659
|
+
}
|
|
10660
|
+
case float64:
|
|
10661
|
+
if x == math.Trunc(x) && !math.IsInf(x, 0) {
|
|
10662
|
+
b, _ := new(tnbig.Float).SetFloat64(x).Int(nil)
|
|
10663
|
+
return b
|
|
10664
|
+
}
|
|
10665
|
+
}
|
|
10666
|
+
panic(TnNodeError("TypeError", "", "Cannot mix BigInt and other types, use explicit conversions"))
|
|
9529
10667
|
}`,
|
|
9530
|
-
|
|
9531
|
-
|
|
9532
|
-
|
|
9533
|
-
|
|
9534
|
-
|
|
10668
|
+
goMust: `// Go bridge: (T, error) results throw a *TnError carrying the Go error
|
|
10669
|
+
func tnGoError(err error) *TnError {
|
|
10670
|
+
return &TnError{message: err.Error(), goErr: err}
|
|
10671
|
+
}
|
|
10672
|
+
|
|
10673
|
+
func tnGoMust[T any](v T, err error) T {
|
|
10674
|
+
if err != nil {
|
|
10675
|
+
panic(tnGoError(err))
|
|
10676
|
+
}
|
|
9535
10677
|
return v
|
|
10678
|
+
}
|
|
10679
|
+
|
|
10680
|
+
func tnGoMust2[A any, B any](a A, b B, err error) (A, B) {
|
|
10681
|
+
if err != nil {
|
|
10682
|
+
panic(tnGoError(err))
|
|
10683
|
+
}
|
|
10684
|
+
return a, b
|
|
10685
|
+
}
|
|
10686
|
+
|
|
10687
|
+
func tnGoCheck(err error) {
|
|
10688
|
+
if err != nil {
|
|
10689
|
+
panic(tnGoError(err))
|
|
10690
|
+
}
|
|
10691
|
+
}`,
|
|
10692
|
+
goNums: `// Go bridge: numeric slice conversion ([]int ↔ []float64)
|
|
10693
|
+
type tnGoNumber interface {
|
|
10694
|
+
~int | ~int8 | ~int16 | ~int32 | ~int64 |
|
|
10695
|
+
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
|
|
10696
|
+
~float32 | ~float64
|
|
10697
|
+
}
|
|
10698
|
+
|
|
10699
|
+
func tnGoNums[T tnGoNumber](s []float64) []T {
|
|
10700
|
+
out := make([]T, len(s))
|
|
10701
|
+
for i, v := range s {
|
|
10702
|
+
out[i] = T(v)
|
|
10703
|
+
}
|
|
10704
|
+
return out
|
|
10705
|
+
}
|
|
10706
|
+
|
|
10707
|
+
func tnGoFromNums[T tnGoNumber](s []T) []float64 {
|
|
10708
|
+
out := make([]float64, len(s))
|
|
10709
|
+
for i, v := range s {
|
|
10710
|
+
out[i] = float64(v)
|
|
10711
|
+
}
|
|
10712
|
+
return out
|
|
10713
|
+
}`,
|
|
10714
|
+
goStrings: `// Go bridge: first rune of a string (unicode funcs take rune)
|
|
10715
|
+
func tnGoRune(s string) rune {
|
|
10716
|
+
for _, r := range s {
|
|
10717
|
+
return r
|
|
10718
|
+
}
|
|
10719
|
+
return 0
|
|
10720
|
+
}`,
|
|
10721
|
+
goBlocking: `// Go bridge: blocking calls run on a goroutine while the baton holder
|
|
10722
|
+
// pumps pending loop tasks, so timers and handlers keep running
|
|
10723
|
+
func tnGoBlocking(fn func() interface{}) interface{} {
|
|
10724
|
+
done := make(chan interface{}, 1)
|
|
10725
|
+
go func() {
|
|
10726
|
+
defer func() {
|
|
10727
|
+
if r := recover(); r != nil {
|
|
10728
|
+
done <- r
|
|
10729
|
+
return
|
|
10730
|
+
}
|
|
10731
|
+
}()
|
|
10732
|
+
done <- fn()
|
|
10733
|
+
}()
|
|
10734
|
+
for {
|
|
10735
|
+
select {
|
|
10736
|
+
case v := <-done:
|
|
10737
|
+
if err, ok := v.(*TnError); ok {
|
|
10738
|
+
panic(err)
|
|
10739
|
+
}
|
|
10740
|
+
return v
|
|
10741
|
+
default:
|
|
10742
|
+
}
|
|
10743
|
+
TnRunMicrotasks()
|
|
10744
|
+
if TnLoopRunPending() {
|
|
10745
|
+
continue
|
|
10746
|
+
}
|
|
10747
|
+
tnRelease()
|
|
10748
|
+
select {
|
|
10749
|
+
case v := <-done:
|
|
10750
|
+
tnAcquire()
|
|
10751
|
+
if err, ok := v.(*TnError); ok {
|
|
10752
|
+
panic(err)
|
|
10753
|
+
}
|
|
10754
|
+
return v
|
|
10755
|
+
case <-tnLoopSignal:
|
|
10756
|
+
tnAcquire()
|
|
10757
|
+
}
|
|
10758
|
+
}
|
|
9536
10759
|
}`,
|
|
9537
10760
|
symbol: `// TnSymbol: JS symbol values (unique by identity)
|
|
9538
10761
|
type TnSymbol struct {
|
|
@@ -9542,57 +10765,422 @@ type TnSymbol struct {
|
|
|
9542
10765
|
func TnNewSymbol(description string) *TnSymbol {
|
|
9543
10766
|
return &TnSymbol{description: description}
|
|
9544
10767
|
}`,
|
|
9545
|
-
timerWait: `//
|
|
9546
|
-
var __tnTimers
|
|
10768
|
+
timerWait: `// Timers: callbacks run on the event loop; pending (ref'ed) timers keep the process alive
|
|
10769
|
+
var __tnTimers tnLoopGroup
|
|
9547
10770
|
|
|
9548
|
-
|
|
10771
|
+
// A setTimeout / setImmediate handle (Node's Timeout: ref / unref / hasRef / refresh / close)
|
|
10772
|
+
type TnTimeout struct {
|
|
10773
|
+
timer *time.Timer
|
|
10774
|
+
ms float64
|
|
10775
|
+
fn func()
|
|
10776
|
+
refd bool
|
|
10777
|
+
active bool
|
|
10778
|
+
gen int
|
|
10779
|
+
}
|
|
10780
|
+
|
|
10781
|
+
// node:test mock timers: when set (and the API is mocked) it takes over a timer;
|
|
10782
|
+
// run calls the callback and reports whether the timer repeats
|
|
10783
|
+
var tnMockTimerHook func(kind string, ms float64, run func() bool) bool
|
|
10784
|
+
|
|
10785
|
+
func TnSetTimeout(ms float64, fn func()) *TnTimeout { return tnSetTimeout("setTimeout", ms, fn) }
|
|
10786
|
+
|
|
10787
|
+
func TnSetImmediate(fn func()) *TnTimeout { return tnSetTimeout("setImmediate", 0, fn) }
|
|
10788
|
+
|
|
10789
|
+
func tnSetTimeout(kind string, ms float64, fn func()) *TnTimeout {
|
|
10790
|
+
t := &TnTimeout{ms: ms, fn: TnAsyncWrap(fn), active: true}
|
|
10791
|
+
if tnMockTimerHook != nil && tnMockTimerHook(kind, ms, func() bool {
|
|
10792
|
+
if t.active {
|
|
10793
|
+
t.active = false
|
|
10794
|
+
t.fn()
|
|
10795
|
+
}
|
|
10796
|
+
return false
|
|
10797
|
+
}) {
|
|
10798
|
+
return t
|
|
10799
|
+
}
|
|
9549
10800
|
__tnTimers.Add(1)
|
|
9550
|
-
|
|
9551
|
-
|
|
9552
|
-
|
|
9553
|
-
|
|
10801
|
+
t.refd = true
|
|
10802
|
+
t.arm()
|
|
10803
|
+
return t
|
|
10804
|
+
}
|
|
10805
|
+
|
|
10806
|
+
func (t *TnTimeout) arm() {
|
|
10807
|
+
t.gen++
|
|
10808
|
+
gen := t.gen
|
|
10809
|
+
t.timer = time.AfterFunc(time.Duration(t.ms*float64(time.Millisecond)), func() {
|
|
10810
|
+
TnLoopPost(func() {
|
|
10811
|
+
if !t.active || gen != t.gen {
|
|
10812
|
+
return
|
|
10813
|
+
}
|
|
10814
|
+
t.active = false
|
|
10815
|
+
if t.refd {
|
|
10816
|
+
t.refd = false
|
|
10817
|
+
__tnTimers.Done()
|
|
10818
|
+
}
|
|
10819
|
+
t.fn()
|
|
10820
|
+
})
|
|
9554
10821
|
})
|
|
9555
10822
|
}
|
|
9556
10823
|
|
|
9557
|
-
|
|
10824
|
+
func (t *TnTimeout) ref() *TnTimeout {
|
|
10825
|
+
if !t.refd && t.active {
|
|
10826
|
+
t.refd = true
|
|
10827
|
+
__tnTimers.Add(1)
|
|
10828
|
+
}
|
|
10829
|
+
return t
|
|
10830
|
+
}
|
|
10831
|
+
|
|
10832
|
+
func (t *TnTimeout) unref() *TnTimeout {
|
|
10833
|
+
if t.refd {
|
|
10834
|
+
t.refd = false
|
|
10835
|
+
__tnTimers.Done()
|
|
10836
|
+
}
|
|
10837
|
+
return t
|
|
10838
|
+
}
|
|
10839
|
+
|
|
10840
|
+
func (t *TnTimeout) hasRef() bool { return t.refd }
|
|
10841
|
+
|
|
10842
|
+
// refresh(): restarts the countdown (a fired timeout runs again)
|
|
10843
|
+
func (t *TnTimeout) refresh() *TnTimeout {
|
|
10844
|
+
if t.timer == nil && t.active && !t.refd {
|
|
10845
|
+
// A mocked timer: node:test's clock owns it
|
|
10846
|
+
return t
|
|
10847
|
+
}
|
|
10848
|
+
if t.timer != nil {
|
|
10849
|
+
t.timer.Stop()
|
|
10850
|
+
}
|
|
10851
|
+
if !t.active {
|
|
10852
|
+
t.active = true
|
|
10853
|
+
if !t.refd {
|
|
10854
|
+
t.refd = true
|
|
10855
|
+
__tnTimers.Add(1)
|
|
10856
|
+
}
|
|
10857
|
+
}
|
|
10858
|
+
t.arm()
|
|
10859
|
+
return t
|
|
10860
|
+
}
|
|
10861
|
+
|
|
10862
|
+
func (t *TnTimeout) close() *TnTimeout {
|
|
10863
|
+
TnClearTimeout(t)
|
|
10864
|
+
return t
|
|
10865
|
+
}
|
|
10866
|
+
|
|
10867
|
+
// setInterval: fn every ms until cleared (the process lives until then, unless unref'ed)
|
|
9558
10868
|
type TnInterval struct {
|
|
9559
|
-
stop
|
|
9560
|
-
once
|
|
10869
|
+
stop chan struct{}
|
|
10870
|
+
once sync.Once
|
|
10871
|
+
stopped int32
|
|
10872
|
+
refd bool
|
|
10873
|
+
ms float64
|
|
10874
|
+
fn func()
|
|
9561
10875
|
}
|
|
9562
10876
|
|
|
9563
10877
|
func TnSetInterval(ms float64, fn func()) *TnInterval {
|
|
10878
|
+
t := &TnInterval{stop: make(chan struct{}), ms: ms, fn: TnAsyncWrap(fn)}
|
|
10879
|
+
if tnMockTimerHook != nil && tnMockTimerHook("setInterval", ms, func() bool {
|
|
10880
|
+
if atomic.LoadInt32(&t.stopped) != 0 {
|
|
10881
|
+
return false
|
|
10882
|
+
}
|
|
10883
|
+
t.fn()
|
|
10884
|
+
return atomic.LoadInt32(&t.stopped) == 0
|
|
10885
|
+
}) {
|
|
10886
|
+
return t
|
|
10887
|
+
}
|
|
9564
10888
|
__tnTimers.Add(1)
|
|
9565
|
-
t
|
|
10889
|
+
t.refd = true
|
|
10890
|
+
t.start(t.stop)
|
|
10891
|
+
return t
|
|
10892
|
+
}
|
|
10893
|
+
|
|
10894
|
+
func (t *TnInterval) start(stop chan struct{}) {
|
|
9566
10895
|
go func() {
|
|
9567
|
-
ticker := time.NewTicker(max(time.Duration(ms*float64(time.Millisecond)), time.Millisecond))
|
|
10896
|
+
ticker := time.NewTicker(max(time.Duration(t.ms*float64(time.Millisecond)), time.Millisecond))
|
|
9568
10897
|
defer ticker.Stop()
|
|
9569
10898
|
for {
|
|
9570
10899
|
select {
|
|
9571
|
-
case <-
|
|
10900
|
+
case <-stop:
|
|
9572
10901
|
return
|
|
9573
10902
|
case <-ticker.C:
|
|
9574
|
-
|
|
9575
|
-
|
|
10903
|
+
TnLoopPost(func() {
|
|
10904
|
+
if atomic.LoadInt32(&t.stopped) == 0 {
|
|
10905
|
+
t.fn()
|
|
10906
|
+
}
|
|
10907
|
+
})
|
|
9576
10908
|
}
|
|
9577
10909
|
}
|
|
9578
10910
|
}()
|
|
9579
|
-
return t
|
|
9580
10911
|
}
|
|
9581
10912
|
|
|
9582
10913
|
func TnClearInterval(t *TnInterval) {
|
|
9583
10914
|
if t != nil {
|
|
9584
10915
|
t.once.Do(func() {
|
|
10916
|
+
atomic.StoreInt32(&t.stopped, 1)
|
|
9585
10917
|
close(t.stop)
|
|
9586
|
-
|
|
10918
|
+
if t.refd {
|
|
10919
|
+
t.refd = false
|
|
10920
|
+
__tnTimers.Done()
|
|
10921
|
+
}
|
|
9587
10922
|
})
|
|
9588
10923
|
}
|
|
9589
10924
|
}
|
|
9590
10925
|
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
10926
|
+
func (t *TnInterval) ref() *TnInterval {
|
|
10927
|
+
if !t.refd && atomic.LoadInt32(&t.stopped) == 0 {
|
|
10928
|
+
t.refd = true
|
|
10929
|
+
__tnTimers.Add(1)
|
|
10930
|
+
}
|
|
10931
|
+
return t
|
|
10932
|
+
}
|
|
9594
10933
|
|
|
9595
|
-
func
|
|
10934
|
+
func (t *TnInterval) unref() *TnInterval {
|
|
10935
|
+
if t.refd {
|
|
10936
|
+
t.refd = false
|
|
10937
|
+
__tnTimers.Done()
|
|
10938
|
+
}
|
|
10939
|
+
return t
|
|
10940
|
+
}
|
|
10941
|
+
|
|
10942
|
+
func (t *TnInterval) hasRef() bool { return t.refd }
|
|
10943
|
+
|
|
10944
|
+
func (t *TnInterval) refresh() *TnInterval {
|
|
10945
|
+
if atomic.LoadInt32(&t.stopped) == 0 {
|
|
10946
|
+
close(t.stop)
|
|
10947
|
+
t.stop = make(chan struct{})
|
|
10948
|
+
t.start(t.stop)
|
|
10949
|
+
}
|
|
10950
|
+
return t
|
|
10951
|
+
}
|
|
10952
|
+
|
|
10953
|
+
func (t *TnInterval) close() *TnInterval {
|
|
10954
|
+
TnClearInterval(t)
|
|
10955
|
+
return t
|
|
10956
|
+
}
|
|
10957
|
+
|
|
10958
|
+
func TnClearTimeout(t *TnTimeout) {
|
|
10959
|
+
if t != nil && t.active {
|
|
10960
|
+
t.active = false
|
|
10961
|
+
if t.timer != nil {
|
|
10962
|
+
t.timer.Stop()
|
|
10963
|
+
}
|
|
10964
|
+
if t.refd {
|
|
10965
|
+
t.refd = false
|
|
10966
|
+
__tnTimers.Done()
|
|
10967
|
+
}
|
|
10968
|
+
}
|
|
10969
|
+
}`,
|
|
10970
|
+
clock: `// The current time; node:test mock timers can replace it (Date.now / new Date())
|
|
10971
|
+
var tnClockHook func() time.Time
|
|
10972
|
+
|
|
10973
|
+
func TnTimeNow() time.Time {
|
|
10974
|
+
if tnClockHook != nil {
|
|
10975
|
+
return tnClockHook()
|
|
10976
|
+
}
|
|
10977
|
+
return time.Now()
|
|
10978
|
+
}`,
|
|
10979
|
+
eventLoop: `// The event loop. JS runs one thing at a time: whoever holds the
|
|
10980
|
+
// baton (main, a loop task, a running async function body) runs JS code; the
|
|
10981
|
+
// others wait. An await runs pending loop tasks, then hands the baton over
|
|
10982
|
+
// while it waits. Callbacks from timers, I/O and other goroutines are posted
|
|
10983
|
+
// as tasks. Refs (pending timers, async work, open handles) keep it alive.
|
|
10984
|
+
var tnLoopMu sync.Mutex
|
|
10985
|
+
var tnLoopTasks []func()
|
|
10986
|
+
var tnLoopSignal = make(chan struct{}, 1)
|
|
10987
|
+
var tnLoopRefs int64
|
|
10988
|
+
|
|
10989
|
+
// The baton: free when it sits in the channel (main holds it at start).
|
|
10990
|
+
// A call of an async function lends it to the body until its first await.
|
|
10991
|
+
var tnBaton = make(chan struct{}, 1)
|
|
10992
|
+
var tnLendStack []chan struct{}
|
|
10993
|
+
|
|
10994
|
+
// Async context (AsyncLocalStorage): the baton holder's current stores. Callbacks
|
|
10995
|
+
// scheduled by JS code capture it (TnAsyncWrap) and run with it restored; an await
|
|
10996
|
+
// resumes with the context it started with. Other loop tasks run without stores.
|
|
10997
|
+
type TnAsyncContext map[interface{}]interface{}
|
|
10998
|
+
|
|
10999
|
+
var tnAsyncCtx TnAsyncContext
|
|
11000
|
+
|
|
11001
|
+
func TnAsyncSnapshot() TnAsyncContext { return tnAsyncCtx }
|
|
11002
|
+
|
|
11003
|
+
// Runs fn with ctx as the current context, restoring the previous one afterwards
|
|
11004
|
+
func TnAsyncRun(ctx TnAsyncContext, fn func()) {
|
|
11005
|
+
saved := tnAsyncCtx
|
|
11006
|
+
tnAsyncCtx = ctx
|
|
11007
|
+
defer func() { tnAsyncCtx = saved }()
|
|
11008
|
+
fn()
|
|
11009
|
+
}
|
|
11010
|
+
|
|
11011
|
+
// fn bound to the current context (no-op when there is none to keep)
|
|
11012
|
+
func TnAsyncWrap(fn func()) func() {
|
|
11013
|
+
ctx := tnAsyncCtx
|
|
11014
|
+
if ctx == nil {
|
|
11015
|
+
return fn
|
|
11016
|
+
}
|
|
11017
|
+
return func() { TnAsyncRun(ctx, fn) }
|
|
11018
|
+
}
|
|
11019
|
+
|
|
11020
|
+
func tnAcquire() { <-tnBaton }
|
|
11021
|
+
|
|
11022
|
+
func tnRelease() {
|
|
11023
|
+
if n := len(tnLendStack); n > 0 {
|
|
11024
|
+
lender := tnLendStack[n-1]
|
|
11025
|
+
tnLendStack = tnLendStack[:n-1]
|
|
11026
|
+
close(lender)
|
|
11027
|
+
return
|
|
11028
|
+
}
|
|
11029
|
+
tnBaton <- struct{}{}
|
|
11030
|
+
}
|
|
11031
|
+
|
|
11032
|
+
// Runs an async function body: synchronously until its first await (or the
|
|
11033
|
+
// end), then the caller continues while the body waits
|
|
11034
|
+
func TnStartAsync(body func()) {
|
|
11035
|
+
yielded := make(chan struct{})
|
|
11036
|
+
tnLendStack = append(tnLendStack, yielded)
|
|
11037
|
+
ctx := tnAsyncCtx
|
|
11038
|
+
go func() {
|
|
11039
|
+
defer tnRelease()
|
|
11040
|
+
tnAsyncCtx = ctx
|
|
11041
|
+
body()
|
|
11042
|
+
}()
|
|
11043
|
+
<-yielded
|
|
11044
|
+
tnAsyncCtx = ctx
|
|
11045
|
+
}
|
|
11046
|
+
|
|
11047
|
+
func tnLoopNotify() {
|
|
11048
|
+
select {
|
|
11049
|
+
case tnLoopSignal <- struct{}{}:
|
|
11050
|
+
default:
|
|
11051
|
+
}
|
|
11052
|
+
}
|
|
11053
|
+
|
|
11054
|
+
func TnLoopRef() { atomic.AddInt64(&tnLoopRefs, 1) }
|
|
11055
|
+
|
|
11056
|
+
func TnLoopUnref() {
|
|
11057
|
+
atomic.AddInt64(&tnLoopRefs, -1)
|
|
11058
|
+
tnLoopNotify()
|
|
11059
|
+
}
|
|
11060
|
+
|
|
11061
|
+
func TnLoopPost(fn func()) {
|
|
11062
|
+
tnLoopMu.Lock()
|
|
11063
|
+
tnLoopTasks = append(tnLoopTasks, fn)
|
|
11064
|
+
tnLoopMu.Unlock()
|
|
11065
|
+
tnLoopNotify()
|
|
11066
|
+
}
|
|
11067
|
+
|
|
11068
|
+
// Runs a callback on the loop from a goroutine that does not hold the baton,
|
|
11069
|
+
// and waits for it
|
|
11070
|
+
func TnLoopCall(fn func()) {
|
|
11071
|
+
done := make(chan struct{})
|
|
11072
|
+
TnLoopPost(func() {
|
|
11073
|
+
defer close(done)
|
|
11074
|
+
fn()
|
|
11075
|
+
})
|
|
11076
|
+
<-done
|
|
11077
|
+
}
|
|
11078
|
+
|
|
11079
|
+
// (baton holder) Runs the queued tasks, each followed by its microtasks
|
|
11080
|
+
func TnLoopRunPending() bool {
|
|
11081
|
+
ran := false
|
|
11082
|
+
for {
|
|
11083
|
+
tnLoopMu.Lock()
|
|
11084
|
+
if len(tnLoopTasks) == 0 {
|
|
11085
|
+
tnLoopMu.Unlock()
|
|
11086
|
+
return ran
|
|
11087
|
+
}
|
|
11088
|
+
fn := tnLoopTasks[0]
|
|
11089
|
+
tnLoopTasks = tnLoopTasks[1:]
|
|
11090
|
+
tnLoopMu.Unlock()
|
|
11091
|
+
ran = true
|
|
11092
|
+
tnRunTask(fn)
|
|
11093
|
+
TnRunMicrotasks()
|
|
11094
|
+
}
|
|
11095
|
+
}
|
|
11096
|
+
|
|
11097
|
+
// Set by process.on('uncaughtException'): handles a throw escaping a loop task
|
|
11098
|
+
// (true when handled); 'beforeExit' runs when the loop drains (true when it emitted)
|
|
11099
|
+
var tnUncaughtHook func(err interface{}) bool
|
|
11100
|
+
var tnBeforeExitHook func() bool
|
|
11101
|
+
|
|
11102
|
+
func tnRunTask(fn func()) {
|
|
11103
|
+
if tnUncaughtHook != nil {
|
|
11104
|
+
defer func() {
|
|
11105
|
+
if r := recover(); r != nil {
|
|
11106
|
+
if !tnUncaughtHook(r) {
|
|
11107
|
+
panic(r)
|
|
11108
|
+
}
|
|
11109
|
+
}
|
|
11110
|
+
}()
|
|
11111
|
+
}
|
|
11112
|
+
TnAsyncRun(nil, fn)
|
|
11113
|
+
}
|
|
11114
|
+
|
|
11115
|
+
func tnLoopIdle() bool {
|
|
11116
|
+
tnLoopMu.Lock()
|
|
11117
|
+
defer tnLoopMu.Unlock()
|
|
11118
|
+
return len(tnLoopTasks) == 0 && atomic.LoadInt64(&tnLoopRefs) <= 0
|
|
11119
|
+
}
|
|
11120
|
+
|
|
11121
|
+
// The end of main: run until nothing is pending
|
|
11122
|
+
func TnRunLoop() {
|
|
11123
|
+
for {
|
|
11124
|
+
TnRunMicrotasks()
|
|
11125
|
+
TnLoopRunPending()
|
|
11126
|
+
if tnLoopIdle() {
|
|
11127
|
+
if tnBeforeExitHook == nil || !tnBeforeExitHook() {
|
|
11128
|
+
return
|
|
11129
|
+
}
|
|
11130
|
+
TnRunMicrotasks()
|
|
11131
|
+
TnLoopRunPending()
|
|
11132
|
+
if tnLoopIdle() {
|
|
11133
|
+
return
|
|
11134
|
+
}
|
|
11135
|
+
continue
|
|
11136
|
+
}
|
|
11137
|
+
tnRelease()
|
|
11138
|
+
select {
|
|
11139
|
+
case <-tnLoopSignal:
|
|
11140
|
+
case <-time.After(10 * time.Millisecond):
|
|
11141
|
+
}
|
|
11142
|
+
tnAcquire()
|
|
11143
|
+
}
|
|
11144
|
+
}
|
|
11145
|
+
|
|
11146
|
+
// (baton holder) Receives from a channel, running loop tasks while waiting
|
|
11147
|
+
func TnLoopReceive[T any](ch chan T) (T, bool) {
|
|
11148
|
+
ctx := tnAsyncCtx
|
|
11149
|
+
defer func() { tnAsyncCtx = ctx }()
|
|
11150
|
+
for {
|
|
11151
|
+
select {
|
|
11152
|
+
case v, ok := <-ch:
|
|
11153
|
+
return v, ok
|
|
11154
|
+
default:
|
|
11155
|
+
}
|
|
11156
|
+
TnRunMicrotasks()
|
|
11157
|
+
if TnLoopRunPending() {
|
|
11158
|
+
continue
|
|
11159
|
+
}
|
|
11160
|
+
tnRelease()
|
|
11161
|
+
select {
|
|
11162
|
+
case v, ok := <-ch:
|
|
11163
|
+
tnAcquire()
|
|
11164
|
+
return v, ok
|
|
11165
|
+
case <-tnLoopSignal:
|
|
11166
|
+
tnAcquire()
|
|
11167
|
+
}
|
|
11168
|
+
}
|
|
11169
|
+
}
|
|
11170
|
+
|
|
11171
|
+
// A wait group that keeps the loop alive (detached async calls, .then callbacks)
|
|
11172
|
+
type tnLoopGroup struct{}
|
|
11173
|
+
|
|
11174
|
+
func (tnLoopGroup) Add(n int) { atomic.AddInt64(&tnLoopRefs, int64(n)) }
|
|
11175
|
+
func (tnLoopGroup) Done() { TnLoopUnref() }
|
|
11176
|
+
func (tnLoopGroup) Wait() { TnRunLoop() }
|
|
11177
|
+
|
|
11178
|
+
// queueMicrotask: run after the current synchronous code (end of main or of a loop task)
|
|
11179
|
+
var tnMicrotasks []func()
|
|
11180
|
+
var tnMicrotasksMu sync.Mutex
|
|
11181
|
+
|
|
11182
|
+
func TnQueueMicrotask(fn func()) {
|
|
11183
|
+
fn = TnAsyncWrap(fn)
|
|
9596
11184
|
tnMicrotasksMu.Lock()
|
|
9597
11185
|
tnMicrotasks = append(tnMicrotasks, fn)
|
|
9598
11186
|
tnMicrotasksMu.Unlock()
|
|
@@ -9608,13 +11196,7 @@ func TnRunMicrotasks() {
|
|
|
9608
11196
|
fn := tnMicrotasks[0]
|
|
9609
11197
|
tnMicrotasks = tnMicrotasks[1:]
|
|
9610
11198
|
tnMicrotasksMu.Unlock()
|
|
9611
|
-
fn
|
|
9612
|
-
}
|
|
9613
|
-
}
|
|
9614
|
-
|
|
9615
|
-
func TnClearTimeout(t *time.Timer) {
|
|
9616
|
-
if t != nil && t.Stop() {
|
|
9617
|
-
__tnTimers.Done()
|
|
11199
|
+
TnAsyncRun(nil, fn)
|
|
9618
11200
|
}
|
|
9619
11201
|
}`,
|
|
9620
11202
|
uri: `// encodeURIComponent: escapes everything except A-Z a-z 0-9 - _ . ! ~ * ' ( )
|
|
@@ -9851,11 +11433,76 @@ func TnDecorateClass(result interface{}) {
|
|
|
9851
11433
|
promise: `// Promises are channels; a rejected promise is a closed channel with a reason
|
|
9852
11434
|
var tnRejections sync.Map
|
|
9853
11435
|
|
|
11436
|
+
// A throw inside a Promise executor rejects the promise
|
|
11437
|
+
func TnRejectExecutor[T any](ch chan T) {
|
|
11438
|
+
if r := recover(); r != nil {
|
|
11439
|
+
if _, rejected := tnRejections.Load(ch); !rejected && len(ch) == 0 {
|
|
11440
|
+
TnReject(ch, r)
|
|
11441
|
+
}
|
|
11442
|
+
}
|
|
11443
|
+
}
|
|
11444
|
+
|
|
11445
|
+
func TnResolveZero[T any](ch chan T) {
|
|
11446
|
+
var zero T
|
|
11447
|
+
select {
|
|
11448
|
+
case ch <- zero:
|
|
11449
|
+
default:
|
|
11450
|
+
}
|
|
11451
|
+
}
|
|
11452
|
+
|
|
11453
|
+
// resolve / reject of a Promise executor used as values
|
|
11454
|
+
func TnResolver[T any](ch chan T) func(...interface{}) {
|
|
11455
|
+
return func(args ...interface{}) {
|
|
11456
|
+
var v T
|
|
11457
|
+
if len(args) > 0 {
|
|
11458
|
+
if x, ok := args[0].(T); ok {
|
|
11459
|
+
v = x
|
|
11460
|
+
}
|
|
11461
|
+
}
|
|
11462
|
+
select {
|
|
11463
|
+
case ch <- v:
|
|
11464
|
+
default:
|
|
11465
|
+
}
|
|
11466
|
+
}
|
|
11467
|
+
}
|
|
11468
|
+
|
|
11469
|
+
func TnRejecter[T any](ch chan T) func(...interface{}) {
|
|
11470
|
+
return func(args ...interface{}) {
|
|
11471
|
+
var reason interface{}
|
|
11472
|
+
if len(args) > 0 {
|
|
11473
|
+
reason = args[0]
|
|
11474
|
+
}
|
|
11475
|
+
if _, rejected := tnRejections.Load(ch); rejected || len(ch) > 0 {
|
|
11476
|
+
return
|
|
11477
|
+
}
|
|
11478
|
+
TnReject(ch, reason)
|
|
11479
|
+
}
|
|
11480
|
+
}
|
|
11481
|
+
|
|
9854
11482
|
func TnReject[T any](ch chan T, reason interface{}) {
|
|
9855
11483
|
tnRejections.Store(ch, reason)
|
|
9856
11484
|
close(ch)
|
|
9857
11485
|
}
|
|
9858
11486
|
|
|
11487
|
+
// A detached call (\`main();\`): the process waits for it; a rejection is fatal
|
|
11488
|
+
// unless process.on('unhandledRejection') takes it
|
|
11489
|
+
var tnUnhandledHook func(reason interface{}) bool
|
|
11490
|
+
|
|
11491
|
+
func TnDetach[T any](ch chan T) {
|
|
11492
|
+
go func() {
|
|
11493
|
+
defer __tnWaitGroup.Done()
|
|
11494
|
+
_, reason, rejected := tnSettle(ch)
|
|
11495
|
+
if rejected {
|
|
11496
|
+
TnLoopCall(func() {
|
|
11497
|
+
if tnUnhandledHook != nil && tnUnhandledHook(reason) {
|
|
11498
|
+
return
|
|
11499
|
+
}
|
|
11500
|
+
panic(reason)
|
|
11501
|
+
})
|
|
11502
|
+
}
|
|
11503
|
+
}()
|
|
11504
|
+
}
|
|
11505
|
+
|
|
9859
11506
|
// Deferred in async functions: a throw rejects the function's promise
|
|
9860
11507
|
func TnRejectOnPanic[T any](ch chan T) {
|
|
9861
11508
|
if r := recover(); r != nil {
|
|
@@ -9863,9 +11510,7 @@ func TnRejectOnPanic[T any](ch chan T) {
|
|
|
9863
11510
|
}
|
|
9864
11511
|
}
|
|
9865
11512
|
|
|
9866
|
-
|
|
9867
|
-
func tnSettle[T any](ch chan T) (T, interface{}, bool) {
|
|
9868
|
-
v, ok := <-ch
|
|
11513
|
+
func tnSettled[T any](ch chan T, v T, ok bool) (T, interface{}, bool) {
|
|
9869
11514
|
if !ok {
|
|
9870
11515
|
if reason, rejected := tnRejections.Load(ch); rejected {
|
|
9871
11516
|
return v, reason, true
|
|
@@ -9874,9 +11519,16 @@ func tnSettle[T any](ch chan T) (T, interface{}, bool) {
|
|
|
9874
11519
|
return v, nil, false
|
|
9875
11520
|
}
|
|
9876
11521
|
|
|
9877
|
-
//
|
|
11522
|
+
// Waits for a promise (helper goroutines, without the baton): its value, or its rejection reason
|
|
11523
|
+
func tnSettle[T any](ch chan T) (T, interface{}, bool) {
|
|
11524
|
+
v, ok := <-ch
|
|
11525
|
+
return tnSettled(ch, v, ok)
|
|
11526
|
+
}
|
|
11527
|
+
|
|
11528
|
+
// await (baton holder): the value, or the rejection rethrown; loop tasks run meanwhile
|
|
9878
11529
|
func TnAwait[T any](ch chan T) T {
|
|
9879
|
-
|
|
11530
|
+
received, ok := TnLoopReceive(ch)
|
|
11531
|
+
v, reason, rejected := tnSettled(ch, received, ok)
|
|
9880
11532
|
if rejected {
|
|
9881
11533
|
panic(reason)
|
|
9882
11534
|
}
|
|
@@ -9891,11 +11543,12 @@ func TnPromiseReject[T any](reason interface{}) chan T {
|
|
|
9891
11543
|
|
|
9892
11544
|
func TnPromiseCatch[T any, E any](p chan T, onRejected func(E)) chan T {
|
|
9893
11545
|
out := make(chan T, 1)
|
|
11546
|
+
ctx := TnAsyncSnapshot()
|
|
9894
11547
|
go func() {
|
|
9895
11548
|
v, reason, rejected := tnSettle(p)
|
|
9896
11549
|
if rejected {
|
|
9897
11550
|
e, _ := reason.(E)
|
|
9898
|
-
onRejected(e)
|
|
11551
|
+
TnLoopCall(func() { TnAsyncRun(ctx, func() { onRejected(e) }) })
|
|
9899
11552
|
}
|
|
9900
11553
|
out <- v
|
|
9901
11554
|
close(out)
|
|
@@ -9905,11 +11558,12 @@ func TnPromiseCatch[T any, E any](p chan T, onRejected func(E)) chan T {
|
|
|
9905
11558
|
|
|
9906
11559
|
func TnPromiseCatchValue[T any, E any](p chan T, onRejected func(E) T) chan T {
|
|
9907
11560
|
out := make(chan T, 1)
|
|
11561
|
+
ctx := TnAsyncSnapshot()
|
|
9908
11562
|
go func() {
|
|
9909
11563
|
v, reason, rejected := tnSettle(p)
|
|
9910
11564
|
if rejected {
|
|
9911
11565
|
e, _ := reason.(E)
|
|
9912
|
-
v = onRejected(e)
|
|
11566
|
+
TnLoopCall(func() { TnAsyncRun(ctx, func() { v = onRejected(e) }) })
|
|
9913
11567
|
}
|
|
9914
11568
|
out <- v
|
|
9915
11569
|
close(out)
|
|
@@ -9919,9 +11573,10 @@ func TnPromiseCatchValue[T any, E any](p chan T, onRejected func(E) T) chan T {
|
|
|
9919
11573
|
|
|
9920
11574
|
func TnPromiseFinally[T any](p chan T, onFinally func()) chan T {
|
|
9921
11575
|
out := make(chan T, 1)
|
|
11576
|
+
onFinally = TnAsyncWrap(onFinally)
|
|
9922
11577
|
go func() {
|
|
9923
11578
|
v, reason, rejected := tnSettle(p)
|
|
9924
|
-
onFinally
|
|
11579
|
+
TnLoopCall(onFinally)
|
|
9925
11580
|
if rejected {
|
|
9926
11581
|
TnReject(out, reason)
|
|
9927
11582
|
return
|
|
@@ -10022,170 +11677,585 @@ func TnPromiseAll[T any](chs []chan T) chan []T {
|
|
|
10022
11677
|
close(out)
|
|
10023
11678
|
}()
|
|
10024
11679
|
return out
|
|
10025
|
-
}`,
|
|
10026
|
-
exec: `type tnExecResult struct {
|
|
10027
|
-
stdout string
|
|
10028
|
-
stderr string
|
|
10029
|
-
status float64
|
|
10030
|
-
}
|
|
10031
|
-
|
|
10032
|
-
func TnExec(name string, args ...string) tnExecResult {
|
|
10033
|
-
cmd := exec.Command(name, args...)
|
|
10034
|
-
var stdout, stderr bytes.Buffer
|
|
10035
|
-
cmd.Stdout = &stdout
|
|
10036
|
-
cmd.Stderr = &stderr
|
|
10037
|
-
err := cmd.Run()
|
|
10038
|
-
status := 0.0
|
|
10039
|
-
if err != nil {
|
|
10040
|
-
if exitErr, ok := err.(*exec.ExitError); ok {
|
|
10041
|
-
status = float64(exitErr.ExitCode())
|
|
10042
|
-
} else {
|
|
10043
|
-
panic(err)
|
|
10044
|
-
}
|
|
10045
|
-
}
|
|
10046
|
-
return tnExecResult{stdout: stdout.String(), stderr: stderr.String(), status: status}
|
|
10047
|
-
}
|
|
10048
|
-
|
|
10049
|
-
func TnExecShell(command string) tnExecResult {
|
|
10050
|
-
if runtime.GOOS == "windows" {
|
|
10051
|
-
return TnExec("cmd", "/C", command)
|
|
10052
|
-
}
|
|
10053
|
-
return TnExec("sh", "-c", command)
|
|
10054
|
-
}`,
|
|
10055
|
-
execSync: `func TnExecShellSync(command string) string {
|
|
10056
|
-
result := TnExecShell(command)
|
|
10057
|
-
if result.status != 0 {
|
|
10058
|
-
panic(result.stderr)
|
|
10059
|
-
}
|
|
10060
|
-
return result.stdout
|
|
10061
11680
|
}`,
|
|
10062
11681
|
getenv: `func TnGetenv(name string) string {
|
|
10063
11682
|
return os.Getenv(name)
|
|
10064
11683
|
}`,
|
|
10065
|
-
execInput: `func TnExecInput(name string, input string, args ...string) tnExecResult {
|
|
10066
|
-
cmd := exec.Command(name, args...)
|
|
10067
|
-
cmd.Stdin = strings.NewReader(input)
|
|
10068
|
-
var stdout, stderr bytes.Buffer
|
|
10069
|
-
cmd.Stdout = &stdout
|
|
10070
|
-
cmd.Stderr = &stderr
|
|
10071
|
-
err := cmd.Run()
|
|
10072
|
-
status := 0.0
|
|
10073
|
-
if err != nil {
|
|
10074
|
-
if exitErr, ok := err.(*exec.ExitError); ok {
|
|
10075
|
-
status = float64(exitErr.ExitCode())
|
|
10076
|
-
} else {
|
|
10077
|
-
panic(err)
|
|
10078
|
-
}
|
|
10079
|
-
}
|
|
10080
|
-
return tnExecResult{stdout: stdout.String(), stderr: stderr.String(), status: status}
|
|
10081
|
-
}`,
|
|
10082
|
-
runInherit: `func TnRunInherit(name string, args ...string) tnExecResult {
|
|
10083
|
-
cmd := exec.Command(name, args...)
|
|
10084
|
-
cmd.Stdin = os.Stdin
|
|
10085
|
-
cmd.Stdout = os.Stdout
|
|
10086
|
-
cmd.Stderr = os.Stderr
|
|
10087
|
-
err := cmd.Run()
|
|
10088
|
-
if err != nil {
|
|
10089
|
-
if exitErr, ok := err.(*exec.ExitError); ok {
|
|
10090
|
-
return tnExecResult{status: float64(exitErr.ExitCode())}
|
|
10091
|
-
}
|
|
10092
|
-
panic(err)
|
|
10093
|
-
}
|
|
10094
|
-
return tnExecResult{}
|
|
10095
|
-
}`,
|
|
10096
|
-
question: `var tnStdinReader = bufio.NewReader(os.Stdin)
|
|
10097
|
-
|
|
10098
|
-
func TnQuestion(prompt string) string {
|
|
10099
|
-
if prompt != "" {
|
|
10100
|
-
fmt.Print(prompt)
|
|
10101
|
-
}
|
|
10102
|
-
// Reuse one buffered reader: a fresh reader per call would discard
|
|
10103
|
-
// buffered stdin and hit EOF on the second question.
|
|
10104
|
-
line, err := tnStdinReader.ReadString('\\n')
|
|
10105
|
-
if err != nil && line == "" {
|
|
10106
|
-
panic(err)
|
|
10107
|
-
}
|
|
10108
|
-
return strings.TrimRight(line, "\\r\\n")
|
|
10109
|
-
}`
|
|
10110
11684
|
};
|
|
10111
11685
|
// Returns the Go source for all helpers used in this transpilation, or '' if none.
|
|
10112
11686
|
// Emitted only into the main output file: every generated file shares `package main`,
|
|
10113
11687
|
// so helpers are visible across files and must not be duplicated.
|
|
10114
11688
|
function emitGoHelpers() {
|
|
10115
|
-
|
|
11689
|
+
const sources = [...usedHelpers]
|
|
10116
11690
|
.sort()
|
|
10117
|
-
.map((id) => goHelpers[id])
|
|
10118
|
-
.filter(Boolean)
|
|
10119
|
-
|
|
10120
|
-
}
|
|
10121
|
-
|
|
10122
|
-
|
|
10123
|
-
|
|
10124
|
-
|
|
10125
|
-
|
|
10126
|
-
|
|
10127
|
-
|
|
10128
|
-
|
|
10129
|
-
|
|
10130
|
-
|
|
10131
|
-
|
|
10132
|
-
|
|
10133
|
-
|
|
10134
|
-
|
|
10135
|
-
|
|
10136
|
-
|
|
10137
|
-
|
|
10138
|
-
|
|
10139
|
-
|
|
10140
|
-
rmSync: '',
|
|
10141
|
-
unlinkSync: ''
|
|
10142
|
-
},
|
|
10143
|
-
os: { platform: 'string', homedir: 'string', tmpdir: 'string' },
|
|
10144
|
-
url: { fileURLToPath: 'string', pathToFileURL: 'string' },
|
|
10145
|
-
child_process: { execSync: 'string' },
|
|
10146
|
-
readline: { question: 'string' }
|
|
10147
|
-
};
|
|
10148
|
-
// Result types of imported Node.js functions by their local callee text
|
|
10149
|
-
// (`join`, `path.join`)
|
|
10150
|
-
const nodeCallResultTypes = new Map();
|
|
10151
|
-
// Mapping from Node.js stdlib module names to Go setup functions.
|
|
10152
|
-
// Each entry adds the required Go imports and registers call handlers for the local identifier.
|
|
11691
|
+
.map((id) => goHelpers[id] ?? nodeRegistry.goHelpers[id])
|
|
11692
|
+
.filter(Boolean);
|
|
11693
|
+
if (usedHelpers.has('nodeUtil')) {
|
|
11694
|
+
const names = [...classNames].sort().map((name) => `${toGoStringLiteral(getSafeName(name))}: true`);
|
|
11695
|
+
sources.push(`// Classes util.inspect prints as \`Name { … }\`\nvar tnInspectClassNames = map[string]bool{${names.join(', ')}}`);
|
|
11696
|
+
}
|
|
11697
|
+
return sources.join('\n\n');
|
|
11698
|
+
}
|
|
11699
|
+
/// Local names bound by Node.js imports → registry keys ('join' → 'path.join',
|
|
11700
|
+
// 'fs' → 'fs', 'fsp' → 'fs/promises', 'EventEmitter' → 'events.EventEmitter')
|
|
11701
|
+
const nodeLocalNames = new Map();
|
|
11702
|
+
// The registry name of a Node module specifier ('node:fs', 'fs', 'node:sys' → 'util'),
|
|
11703
|
+
// or '' when it is not a (supported) builtin
|
|
11704
|
+
function nodeModuleName(spec) {
|
|
11705
|
+
const prefixed = spec.startsWith('node:');
|
|
11706
|
+
let name = prefixed ? spec.slice(5) : spec;
|
|
11707
|
+
if (nodeRegistry.aliases[name])
|
|
11708
|
+
name = nodeRegistry.aliases[name];
|
|
11709
|
+
if (nodeRegistry.modules[name])
|
|
11710
|
+
return name;
|
|
11711
|
+
return '';
|
|
11712
|
+
}
|
|
11713
|
+
// Binds the import's local names to registry keys
|
|
10153
11714
|
function setupNodeModuleImport(node, nodeModule) {
|
|
10154
|
-
const mapping = nodeModuleMappings[nodeModule];
|
|
10155
|
-
if (!mapping)
|
|
10156
|
-
return;
|
|
10157
11715
|
const clause = node.importClause;
|
|
10158
11716
|
if (!clause)
|
|
10159
11717
|
return;
|
|
10160
|
-
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
|
|
10171
|
-
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
|
|
10175
|
-
|
|
11718
|
+
const defaultKey = nodeRegistry.namespaces[`${nodeModule}.default`] || nodeModule;
|
|
11719
|
+
if (clause.name)
|
|
11720
|
+
nodeLocalNames.set(clause.name.text, defaultKey);
|
|
11721
|
+
const bindings = clause.namedBindings;
|
|
11722
|
+
if (!bindings)
|
|
11723
|
+
return;
|
|
11724
|
+
if (isNamespaceImport(bindings)) {
|
|
11725
|
+
nodeLocalNames.set(bindings.name.text, nodeModule);
|
|
11726
|
+
return;
|
|
11727
|
+
}
|
|
11728
|
+
for (const el of bindings.elements ?? []) {
|
|
11729
|
+
const importedName = el.propertyName?.text ?? el.name.text;
|
|
11730
|
+
if (importedName === 'default') {
|
|
11731
|
+
nodeLocalNames.set(el.name.text, defaultKey);
|
|
11732
|
+
continue;
|
|
11733
|
+
}
|
|
11734
|
+
const key = `${nodeModule}.${importedName}`;
|
|
11735
|
+
nodeLocalNames.set(el.name.text, nodeRegistry.namespaces[key] || key);
|
|
11736
|
+
}
|
|
11737
|
+
}
|
|
11738
|
+
// A Node global (Buffer, URL, process…) not shadowed by a user declaration
|
|
11739
|
+
function nodeGlobalKey(name) {
|
|
11740
|
+
const key = nodeRegistry.globals[name];
|
|
11741
|
+
if (!key || typeof key !== 'string')
|
|
11742
|
+
return '';
|
|
11743
|
+
if (variableGoTypes.has(name) || classNames.has(name) || declaredFunctions.has(name) || importAliases.has(name))
|
|
11744
|
+
return '';
|
|
11745
|
+
return key;
|
|
11746
|
+
}
|
|
11747
|
+
// Registry key of a Node binding expression (`join`, `fs.promises.readFile`,
|
|
11748
|
+
// `Buffer.from`, `(childProcess as any).spawnSync`), or '' when it is not one
|
|
11749
|
+
function resolveNodeKey(expr) {
|
|
11750
|
+
if (!expr)
|
|
11751
|
+
return '';
|
|
11752
|
+
if (isIdentifier(expr)) {
|
|
11753
|
+
if (nodeLocalNames.has(expr.text))
|
|
11754
|
+
return nodeLocalNames.get(expr.text) ?? '';
|
|
11755
|
+
return nodeGlobalKey(expr.text);
|
|
11756
|
+
}
|
|
11757
|
+
if (isParenthesizedExpression(expr) || isAsExpression(expr) || isNonNullExpression(expr)) {
|
|
11758
|
+
return resolveNodeKey(expr.expression);
|
|
11759
|
+
}
|
|
11760
|
+
// util.promisify(fs.readFile) → fs/promises.readFile
|
|
11761
|
+
if (isCallExpression(expr) && resolveNodeKey(expr.expression) === 'util.promisify') {
|
|
11762
|
+
return nodePromisifiedKey((expr.arguments ?? [])[0]);
|
|
11763
|
+
}
|
|
11764
|
+
if (isPropertyAccessExpression(expr) && !expr.questionDotToken) {
|
|
11765
|
+
const base = resolveNodeKey(expr.expression);
|
|
11766
|
+
if (!base)
|
|
11767
|
+
return '';
|
|
11768
|
+
const key = `${base}.${expr.name.text}`;
|
|
11769
|
+
return nodeRegistry.namespaces[key] || key;
|
|
11770
|
+
}
|
|
11771
|
+
return '';
|
|
11772
|
+
}
|
|
11773
|
+
// The promise variant of a Node function, '' when it has none
|
|
11774
|
+
function nodePromisifiedKey(fnExpr) {
|
|
11775
|
+
let key = resolveNodeKey(fnExpr);
|
|
11776
|
+
if (!key && isIdentifier(fnExpr) && (fnExpr.text === 'setTimeout' || fnExpr.text === 'setImmediate')) {
|
|
11777
|
+
key = `timers.${fnExpr.text}`;
|
|
11778
|
+
}
|
|
11779
|
+
if (!key)
|
|
11780
|
+
return '';
|
|
11781
|
+
if (nodeRegistry.functions[`${key}.__promisify__`])
|
|
11782
|
+
return `${key}.__promisify__`;
|
|
11783
|
+
const dot = key.lastIndexOf('.');
|
|
11784
|
+
const mod = key.slice(0, dot);
|
|
11785
|
+
const name = key.slice(dot + 1);
|
|
11786
|
+
const promisesKey = `${mod}/promises.${name}`;
|
|
11787
|
+
if (nodeRegistry.functions[promisesKey])
|
|
11788
|
+
return promisesKey;
|
|
11789
|
+
return '';
|
|
11790
|
+
}
|
|
11791
|
+
// The Node class of a Go type ('*TnEventEmitter', '[]byte'), '' when none
|
|
11792
|
+
function nodeClassKeyOfType(goType) {
|
|
11793
|
+
if (!goType)
|
|
11794
|
+
return '';
|
|
11795
|
+
return nodeRegistry.classByGoType[goType] ?? '';
|
|
11796
|
+
}
|
|
11797
|
+
// A user class extending a Node class (class X extends Transform): the Node
|
|
11798
|
+
// class key, for methods the user classes in the chain do not declare
|
|
11799
|
+
function nodeAncestorClassKey(goType, methodName) {
|
|
11800
|
+
if (!goType || !goType.startsWith('*'))
|
|
11801
|
+
return '';
|
|
11802
|
+
let name = goType.slice(1).replace(/\[.*\]$/, '');
|
|
11803
|
+
for (let depth = 0; depth < 20 && classParentNames.has(name); depth++) {
|
|
11804
|
+
if (declaredClassHasMethod(name, methodName))
|
|
11805
|
+
return '';
|
|
11806
|
+
const parent = classParentNames.get(name) ?? '';
|
|
11807
|
+
if (nodeParentClassKeys.has(parent))
|
|
11808
|
+
return nodeParentClassKeys.get(parent) ?? '';
|
|
11809
|
+
name = parent;
|
|
11810
|
+
}
|
|
11811
|
+
return '';
|
|
11812
|
+
}
|
|
11813
|
+
function declaredClassHasMethod(className, methodName) {
|
|
11814
|
+
const decl = declaredClasses.get(className);
|
|
11815
|
+
for (const member of decl?.members ?? []) {
|
|
11816
|
+
if (member.name && isIdentifier(member.name) && member.name.text === methodName)
|
|
11817
|
+
return true;
|
|
11818
|
+
}
|
|
11819
|
+
return false;
|
|
11820
|
+
}
|
|
11821
|
+
// The Node class key a type name refers to (imported or global), '' when none
|
|
11822
|
+
function nodeClassKeyOfName(name) {
|
|
11823
|
+
const key = nodeLocalNames.has(name) ? (nodeLocalNames.get(name) ?? '') : nodeGlobalKey(name);
|
|
11824
|
+
return key && nodeRegistry.classes[key] ? key : '';
|
|
11825
|
+
}
|
|
11826
|
+
// new Error(message, { cause }): the options set error.cause
|
|
11827
|
+
function withErrorOptions(errorExpr, options) {
|
|
11828
|
+
if (!options)
|
|
11829
|
+
return errorExpr;
|
|
11830
|
+
useHelper('dynamic');
|
|
11831
|
+
return `TnErrorWithOptions(${errorExpr}, ${visit(options)})`;
|
|
11832
|
+
}
|
|
11833
|
+
// The Node class key of a (qualified) type reference: `Stats`, `fs.Stats`, `NodeJS.ErrnoException`
|
|
11834
|
+
function nodeClassKeyOfTypeName(typeName) {
|
|
11835
|
+
if (isIdentifier(typeName))
|
|
11836
|
+
return nodeClassKeyOfName(typeName.text);
|
|
11837
|
+
if (typeName?.kind === 'QualifiedName') {
|
|
11838
|
+
const base = nodeQualifiedTypeBase(typeName.left);
|
|
11839
|
+
if (!base)
|
|
11840
|
+
return '';
|
|
11841
|
+
const key = `${base}.${typeName.right.text}`;
|
|
11842
|
+
return nodeRegistry.classes[key] ? key : '';
|
|
11843
|
+
}
|
|
11844
|
+
return '';
|
|
11845
|
+
}
|
|
11846
|
+
function nodeQualifiedTypeBase(left) {
|
|
11847
|
+
if (isIdentifier(left))
|
|
11848
|
+
return nodeLocalNames.has(left.text) ? (nodeLocalNames.get(left.text) ?? '') : nodeGlobalKey(left.text);
|
|
11849
|
+
if (left?.kind === 'QualifiedName') {
|
|
11850
|
+
const base = nodeQualifiedTypeBase(left.left);
|
|
11851
|
+
return base ? `${base}.${left.right.text}` : '';
|
|
11852
|
+
}
|
|
11853
|
+
return '';
|
|
11854
|
+
}
|
|
11855
|
+
// Option-object methods of Node constructors: their parameters get Node's types
|
|
11856
|
+
function applyNodeOptionTypes(key, options) {
|
|
11857
|
+
const typer = nodeRegistry.optionTypers[key];
|
|
11858
|
+
if (!typer || !isObjectLiteralExpression(options))
|
|
11859
|
+
return;
|
|
11860
|
+
const types = typer(options);
|
|
11861
|
+
for (const prop of options.properties ?? []) {
|
|
11862
|
+
if (!prop.name || !isIdentifier(prop.name))
|
|
11863
|
+
continue;
|
|
11864
|
+
const name = prop.name.text;
|
|
11865
|
+
if (!(name in types))
|
|
11866
|
+
continue;
|
|
11867
|
+
if (prop.kind === 'MethodDeclaration')
|
|
11868
|
+
prop.tnParamTypes = types[name];
|
|
11869
|
+
else if (isPropertyAssignment(prop) && (isArrowFunction(prop.initializer) || isFunctionExpression(prop.initializer))) {
|
|
11870
|
+
prop.initializer.tnParamTypes = types[name];
|
|
11871
|
+
}
|
|
11872
|
+
}
|
|
11873
|
+
}
|
|
11874
|
+
// Applies the registry's callback parameter types to the callback argument
|
|
11875
|
+
function applyNodeParamTypes(key, args) {
|
|
11876
|
+
const typer = nodeRegistry.paramTypers[key];
|
|
11877
|
+
if (!typer)
|
|
11878
|
+
return;
|
|
11879
|
+
for (let i = args.length - 1; i >= 0; i--) {
|
|
11880
|
+
if (isArrowFunction(args[i]) || isFunctionExpression(args[i])) {
|
|
11881
|
+
args[i].tnParamTypes = typer(args);
|
|
11882
|
+
return;
|
|
11883
|
+
}
|
|
11884
|
+
}
|
|
11885
|
+
}
|
|
11886
|
+
function makeNodeCall(node, recv) {
|
|
11887
|
+
const nodes = node.arguments ?? [];
|
|
11888
|
+
const hasSpread = nodes.some((a) => isSpreadElement(a));
|
|
11889
|
+
const args = hasSpread ? visitSpreadCallArguments(node) : nodes.map((a) => visit(a));
|
|
11890
|
+
const types = nodes.map((a) => inferExpressionType(a) ?? '');
|
|
11891
|
+
return { args, nodes, types, recv };
|
|
11892
|
+
}
|
|
11893
|
+
// Calls of Node APIs: imported functions, statics (Buffer.from) and methods of
|
|
11894
|
+
// Node class instances
|
|
11895
|
+
function visitNodeCall(node) {
|
|
11896
|
+
const callee = node.expression;
|
|
11897
|
+
const key = resolveNodeKey(callee);
|
|
11898
|
+
if (key) {
|
|
11899
|
+
const emit = nodeRegistry.functions[key];
|
|
11900
|
+
if (emit) {
|
|
11901
|
+
applyNodeParamTypes(key, node.arguments ?? []);
|
|
11902
|
+
return emit(makeNodeCall(node, ''));
|
|
11903
|
+
}
|
|
11904
|
+
// node:console re-exports the global console
|
|
11905
|
+
if (key.startsWith('console.') && callHandlers[key]) {
|
|
11906
|
+
const call = makeNodeCall(node, '');
|
|
11907
|
+
currentCallArgTypes = call.types.map((t) => t || undefined);
|
|
11908
|
+
const consoleCall = callHandlers[key](key, call.args, '');
|
|
11909
|
+
return consoleCall;
|
|
11910
|
+
}
|
|
11911
|
+
return undefined;
|
|
11912
|
+
}
|
|
11913
|
+
if (!isPropertyAccessExpression(callee) || callee.questionDotToken)
|
|
11914
|
+
return undefined;
|
|
11915
|
+
const receiverType = inferExpressionType(callee.expression);
|
|
11916
|
+
const classKey = nodeClassKeyOfType(receiverType) || nodeAncestorClassKey(receiverType, callee.name.text);
|
|
11917
|
+
if (!classKey)
|
|
11918
|
+
return undefined;
|
|
11919
|
+
const cls = nodeRegistry.classes[classKey];
|
|
11920
|
+
const methodKey = `${cls.key}#${callee.name.text}`;
|
|
11921
|
+
const emit = nodeRegistry.methods[methodKey];
|
|
11922
|
+
if (!emit && cls.category !== 'class')
|
|
11923
|
+
return undefined;
|
|
11924
|
+
applyNodeParamTypes(methodKey, node.arguments ?? []);
|
|
11925
|
+
const call = makeNodeCall(node, visit(callee.expression));
|
|
11926
|
+
if (emit)
|
|
11927
|
+
return emit(call);
|
|
11928
|
+
return `${call.recv}.${callee.name.text}(${call.args.join(', ')})`;
|
|
11929
|
+
}
|
|
11930
|
+
// Whether a registry key names a module or namespace object (path, fs/promises, process.stdout)
|
|
11931
|
+
function isNodeNamespaceKey(key) {
|
|
11932
|
+
return !!nodeRegistry.modules[key] || !!nodeRegistry.objects[key];
|
|
11933
|
+
}
|
|
11934
|
+
// `const win = path.win32` / `const { join, sep } = path` bind local names to
|
|
11935
|
+
// registry keys instead of declaring Go variables
|
|
11936
|
+
function visitNodeAliasDeclaration(node) {
|
|
11937
|
+
if (!node.initializer)
|
|
11938
|
+
return undefined;
|
|
11939
|
+
const key = resolveNodeKey(node.initializer);
|
|
11940
|
+
// const readFileAsync = promisify(fs.readFile): an alias of the promise variant
|
|
11941
|
+
if (key && isIdentifier(node.name) && isCallExpression(unwrapParentheses(node.initializer)) && nodeRegistry.functions[key]) {
|
|
11942
|
+
nodeLocalNames.set(node.name.text, key);
|
|
11943
|
+
return '';
|
|
11944
|
+
}
|
|
11945
|
+
if (!key || !isNodeNamespaceKey(key))
|
|
11946
|
+
return undefined;
|
|
11947
|
+
if (isIdentifier(node.name)) {
|
|
11948
|
+
nodeLocalNames.set(node.name.text, key);
|
|
11949
|
+
return '';
|
|
11950
|
+
}
|
|
11951
|
+
if (isObjectBindingPattern(node.name)) {
|
|
11952
|
+
for (const el of node.name.elements ?? []) {
|
|
11953
|
+
if (el.dotDotDotToken || el.initializer || !isIdentifier(el.name))
|
|
11954
|
+
return undefined;
|
|
11955
|
+
}
|
|
11956
|
+
for (const el of node.name.elements ?? []) {
|
|
11957
|
+
const member = (el.propertyName ?? el.name).text;
|
|
11958
|
+
const memberKey = `${key}.${member}`;
|
|
11959
|
+
nodeLocalNames.set(el.name.text, nodeRegistry.namespaces[memberKey] || memberKey);
|
|
11960
|
+
}
|
|
11961
|
+
return '';
|
|
11962
|
+
}
|
|
11963
|
+
return undefined;
|
|
11964
|
+
}
|
|
11965
|
+
// Reads of Node values (`path.sep`, `os.EOL`, an imported `sep`)
|
|
11966
|
+
function visitNodeValue(expr) {
|
|
11967
|
+
const key = resolveNodeKey(expr);
|
|
11968
|
+
if (!key)
|
|
11969
|
+
return undefined;
|
|
11970
|
+
if (key === 'process.env' && (isPropertyAccessExpression(expr.parent) || isElementAccessExpression(expr.parent)) && expr.parent.expression === expr) {
|
|
11971
|
+
return undefined;
|
|
11972
|
+
}
|
|
11973
|
+
const emit = nodeRegistry.values[key];
|
|
11974
|
+
if (!emit)
|
|
11975
|
+
return undefined;
|
|
11976
|
+
const value = emit(emptyNodeCall());
|
|
11977
|
+
return value;
|
|
11978
|
+
}
|
|
11979
|
+
function emptyNodeCall() {
|
|
11980
|
+
return { args: [], nodes: [], types: [], recv: '' };
|
|
11981
|
+
}
|
|
11982
|
+
// Writes of Node values (`process.exitCode = 1`): registered as 'key='
|
|
11983
|
+
function visitNodeAssignment(node) {
|
|
11984
|
+
if (node.operatorToken.kind !== 'EqualsToken')
|
|
11985
|
+
return undefined;
|
|
11986
|
+
// process.env.X = v / process.env[k] = v
|
|
11987
|
+
if ((isPropertyAccessExpression(node.left) || isElementAccessExpression(node.left)) && isProcessEnv(node.left.expression)) {
|
|
11988
|
+
importedPackages.add('os');
|
|
11989
|
+
const name = isPropertyAccessExpression(node.left) ? toGoStringLiteral(node.left.name.text) : visit(node.left.argumentExpression);
|
|
11990
|
+
return `os.Setenv(${name}, ${jsStringOf(visit(node.right), inferExpressionType(node.right))})`;
|
|
11991
|
+
}
|
|
11992
|
+
const key = resolveNodeKey(node.left);
|
|
11993
|
+
if (!key)
|
|
11994
|
+
return undefined;
|
|
11995
|
+
const emit = nodeRegistry.functions[`${key}=`];
|
|
11996
|
+
if (!emit)
|
|
11997
|
+
return undefined;
|
|
11998
|
+
const call = { args: [visit(node.right)], nodes: [node.right], types: [inferExpressionType(node.right) ?? ''], recv: '' };
|
|
11999
|
+
const assignment = emit(call);
|
|
12000
|
+
return assignment;
|
|
12001
|
+
}
|
|
12002
|
+
// Writes of Go struct fields convert to the field type: h.Size = 5 →
|
|
12003
|
+
// h.Size = int64(5); h.Size += 1 → h.Size = int64(float64(h.Size) + 1)
|
|
12004
|
+
function visitGoFieldAssignment(node, op) {
|
|
12005
|
+
if (!isPropertyAccessExpression(node.left))
|
|
12006
|
+
return undefined;
|
|
12007
|
+
const recvType = inferExpressionType(node.left.expression);
|
|
12008
|
+
if (!recvType)
|
|
12009
|
+
return undefined;
|
|
12010
|
+
const field = goFieldType(recvType, node.left.name.text);
|
|
12011
|
+
if (!field)
|
|
12012
|
+
return undefined;
|
|
12013
|
+
const target = `${visit(node.left.expression)}.${node.left.name.text}`;
|
|
12014
|
+
ensureGoTypePackages(field);
|
|
12015
|
+
if (op === '=') {
|
|
12016
|
+
return `${target} = ${toGoType(visit(node.right), inferExpressionType(node.right) ?? '', field)}`;
|
|
12017
|
+
}
|
|
12018
|
+
if (['+=', '-=', '*=', '/='].includes(op) && isGoNumericKind(field)) {
|
|
12019
|
+
const value = `${fromGoType(target, field)} ${op[0]} (${visit(node.right)})`;
|
|
12020
|
+
return `${target} = ${toGoType(value, 'float64', field)}`;
|
|
12021
|
+
}
|
|
12022
|
+
return undefined;
|
|
12023
|
+
}
|
|
12024
|
+
// new X(…) of a Node class
|
|
12025
|
+
function visitNodeNew(node) {
|
|
12026
|
+
const key = resolveNodeKey(node.expression);
|
|
12027
|
+
if (!key)
|
|
12028
|
+
return undefined;
|
|
12029
|
+
const emit = nodeRegistry.constructors[key];
|
|
12030
|
+
if (!emit)
|
|
12031
|
+
return undefined;
|
|
12032
|
+
applyNodeParamTypes(`${key}()`, node.arguments ?? []);
|
|
12033
|
+
applyNodeOptionTypes(key, (node.arguments ?? [])[0]);
|
|
12034
|
+
return emit(makeNodeCall(node, ''));
|
|
12035
|
+
}
|
|
12036
|
+
// The call a typer inspects: argument nodes and their Go types
|
|
12037
|
+
function nodeTypeCall(nodes) {
|
|
12038
|
+
return { args: [], nodes, types: nodes.map((a) => inferExpressionType(a) ?? ''), recv: '' };
|
|
12039
|
+
}
|
|
12040
|
+
// Go types of Node API expressions; undefined when the expression is not one
|
|
12041
|
+
// Property read on a Go package or Go value: consts, vars, fields.
|
|
12042
|
+
function visitGoProperty(node) {
|
|
12043
|
+
const base = node.expression;
|
|
12044
|
+
// Package member: strings.ToUpper (as a value), math.Pi, os.Stdout.
|
|
12045
|
+
const path = isIdentifier(base) ? goPathOf(base.text) : undefined;
|
|
12046
|
+
if (path) {
|
|
12047
|
+
const info = goPackage(path);
|
|
12048
|
+
if (!info)
|
|
12049
|
+
return undefined;
|
|
12050
|
+
const member = node.name.text;
|
|
12051
|
+
const qualifier = goQualifier(path);
|
|
12052
|
+
if (info.funcs[member])
|
|
12053
|
+
return `${qualifier}.${member}`;
|
|
12054
|
+
if (info.consts[member]) {
|
|
12055
|
+
const kind = info.consts[member].kind;
|
|
12056
|
+
// Untyped consts arrive as TS numbers; typed consts (time.Second)
|
|
12057
|
+
// keep their Go type so arithmetic stays well-typed.
|
|
12058
|
+
if (kind === 'typed')
|
|
12059
|
+
return `${qualifier}.${member}`;
|
|
12060
|
+
if (kind === 'string')
|
|
12061
|
+
return `${qualifier}.${member}`;
|
|
12062
|
+
if (kind === 'bool')
|
|
12063
|
+
return `${qualifier}.${member}`;
|
|
12064
|
+
return `float64(${qualifier}.${member})`;
|
|
12065
|
+
}
|
|
12066
|
+
if (info.vars[member]) {
|
|
12067
|
+
return `${qualifier}.${member}`;
|
|
12068
|
+
}
|
|
12069
|
+
if (info.types[member])
|
|
12070
|
+
return `${qualifier}.${member}`;
|
|
12071
|
+
return undefined;
|
|
12072
|
+
}
|
|
12073
|
+
// Field on a Go-typed value.
|
|
12074
|
+
if (isCallee(node) || isAssignmentTarget(node))
|
|
12075
|
+
return undefined;
|
|
12076
|
+
const recvType = inferExpressionType(base);
|
|
12077
|
+
if (!recvType)
|
|
12078
|
+
return undefined;
|
|
12079
|
+
const field = goFieldType(recvType, node.name.text);
|
|
12080
|
+
if (field) {
|
|
12081
|
+
const recv = visit(base);
|
|
12082
|
+
return fromGoType(`${recv}.${node.name.text}`, field);
|
|
12083
|
+
}
|
|
12084
|
+
// GoRef<T>.value dereferences the pointer cell (structs with a real
|
|
12085
|
+
// `value` field take the branch above). Only when the receiver is
|
|
12086
|
+
// declared as GoRef<...> or initialized by ref<T>(), or points to a
|
|
12087
|
+
// basic type (flag.String's *string has no fields to read).
|
|
12088
|
+
if (node.name.text === 'value' && recvType.startsWith('*') && isIdentifier(base)) {
|
|
12089
|
+
const base2 = recvType.slice(1);
|
|
12090
|
+
const isBasic = base2 === 'string' || base2 === 'float64' || base2 === 'bool' || isGoBasicKind(base2);
|
|
12091
|
+
if (isBasic || goRefVariables.has(base.text)) {
|
|
12092
|
+
return `(*${visit(base)})`;
|
|
12093
|
+
}
|
|
12094
|
+
const typeNode = variableTypeNodes.get(base.text);
|
|
12095
|
+
if (typeNode &&
|
|
12096
|
+
isTypeReferenceNode(typeNode) &&
|
|
12097
|
+
isIdentifier(typeNode.typeName) &&
|
|
12098
|
+
typeNode.typeName.text === 'GoRef') {
|
|
12099
|
+
return `(*${visit(base)})`;
|
|
12100
|
+
}
|
|
12101
|
+
}
|
|
12102
|
+
return undefined;
|
|
12103
|
+
}
|
|
12104
|
+
// Channel method call: ch.send(v) → ch <- v, ch.recv() → <-ch,
|
|
12105
|
+
// ch.close() → close(ch).
|
|
12106
|
+
function visitGoChanCall(node, recv) {
|
|
12107
|
+
if (!isPropertyAccessExpression(node.expression))
|
|
12108
|
+
return undefined;
|
|
12109
|
+
const method = node.expression.name.text;
|
|
12110
|
+
const args = node.arguments ?? [];
|
|
12111
|
+
if (method === 'send' && args.length === 1) {
|
|
12112
|
+
return `${recv} <- ${visit(args[0])}`;
|
|
12113
|
+
}
|
|
12114
|
+
if (method === 'recv' && args.length === 0) {
|
|
12115
|
+
return `<-${recv}`;
|
|
12116
|
+
}
|
|
12117
|
+
if (method === 'close' && args.length === 0) {
|
|
12118
|
+
return `close(${recv})`;
|
|
12119
|
+
}
|
|
12120
|
+
return undefined;
|
|
12121
|
+
}
|
|
12122
|
+
// Package + type of a `new pkg.T(...)` expression, null when none.
|
|
12123
|
+
function goNewTarget(node) {
|
|
12124
|
+
const ctor = node.expression;
|
|
12125
|
+
if (!ctor)
|
|
12126
|
+
return null;
|
|
12127
|
+
if (isIdentifier(ctor)) {
|
|
12128
|
+
const qualified = goMemberOf(ctor.text);
|
|
12129
|
+
if (!qualified)
|
|
12130
|
+
return null;
|
|
12131
|
+
const dot = qualified.indexOf('.');
|
|
12132
|
+
if (dot < 0)
|
|
12133
|
+
return null;
|
|
12134
|
+
const qualifier = qualified.slice(0, dot);
|
|
12135
|
+
const typeName = qualified.slice(dot + 1);
|
|
12136
|
+
for (const path of Object.keys(goStdlib)) {
|
|
12137
|
+
if (goQualifier(path) === qualifier && goPackage(path)?.types[typeName]) {
|
|
12138
|
+
return { path, typeName, qualifier };
|
|
10176
12139
|
}
|
|
10177
12140
|
}
|
|
12141
|
+
return null;
|
|
10178
12142
|
}
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
|
|
12143
|
+
if (isPropertyAccessExpression(ctor) && isIdentifier(ctor.expression)) {
|
|
12144
|
+
const path = goPathOf(ctor.expression.text);
|
|
12145
|
+
if (!path)
|
|
12146
|
+
return null;
|
|
12147
|
+
const typeName = ctor.name.text;
|
|
12148
|
+
if (!goPackage(path)?.types[typeName])
|
|
12149
|
+
return null;
|
|
12150
|
+
return { path, typeName, qualifier: goQualifier(path) };
|
|
12151
|
+
}
|
|
12152
|
+
return null;
|
|
12153
|
+
}
|
|
12154
|
+
function inferGoExpressionType(expr) {
|
|
12155
|
+
// ref<T>().value reads as the cell's element type.
|
|
12156
|
+
if (isPropertyAccessExpression(expr) &&
|
|
12157
|
+
expr.name.text === 'value' &&
|
|
12158
|
+
isIdentifier(expr.expression) &&
|
|
12159
|
+
goRefVariables.has(expr.expression.text)) {
|
|
12160
|
+
const refType = variableGoTypes.get(expr.expression.text);
|
|
12161
|
+
if (refType && refType.startsWith('*'))
|
|
12162
|
+
return refType.slice(1);
|
|
12163
|
+
}
|
|
12164
|
+
if (isCallExpression(expr) || isIdentifier(expr) || isPropertyAccessExpression(expr)) {
|
|
12165
|
+
return inferGoType(expr);
|
|
12166
|
+
}
|
|
12167
|
+
if (isNewExpression(expr)) {
|
|
12168
|
+
const target = goNewTarget(expr);
|
|
12169
|
+
if (target) {
|
|
12170
|
+
const info = goPackage(target.path);
|
|
12171
|
+
const type = info?.types[target.typeName];
|
|
12172
|
+
if (type) {
|
|
12173
|
+
for (const m of type.methods) {
|
|
12174
|
+
if (m.ptr)
|
|
12175
|
+
return `*${target.path}.${target.typeName}`;
|
|
12176
|
+
}
|
|
12177
|
+
return `${target.path}.${target.typeName}`;
|
|
12178
|
+
}
|
|
10185
12179
|
}
|
|
10186
|
-
|
|
10187
|
-
|
|
12180
|
+
return undefined;
|
|
12181
|
+
}
|
|
12182
|
+
return undefined;
|
|
12183
|
+
}
|
|
12184
|
+
function inferNodeExpressionType(expr) {
|
|
12185
|
+
if (isCallExpression(expr)) {
|
|
12186
|
+
const callee = expr.expression;
|
|
12187
|
+
const key = resolveNodeKey(callee);
|
|
12188
|
+
const nodes = expr.arguments ?? [];
|
|
12189
|
+
if (key) {
|
|
12190
|
+
const typer = nodeRegistry.typers[key];
|
|
12191
|
+
if (typer)
|
|
12192
|
+
return typer(nodeTypeCall(nodes));
|
|
12193
|
+
if (key in nodeRegistry.results)
|
|
12194
|
+
return nodeRegistry.results[key];
|
|
12195
|
+
return undefined;
|
|
12196
|
+
}
|
|
12197
|
+
if (isPropertyAccessExpression(callee) && !callee.questionDotToken) {
|
|
12198
|
+
const receiverType = inferExpressionType(callee.expression);
|
|
12199
|
+
const classKey = nodeClassKeyOfType(receiverType) || nodeAncestorClassKey(receiverType, callee.name.text);
|
|
12200
|
+
if (!classKey)
|
|
12201
|
+
return undefined;
|
|
12202
|
+
const methodKey = `${nodeRegistry.classes[classKey].key}#${callee.name.text}`;
|
|
12203
|
+
const typer = nodeRegistry.typers[methodKey];
|
|
12204
|
+
if (typer)
|
|
12205
|
+
return typer(nodeTypeCall(nodes));
|
|
12206
|
+
if (methodKey in nodeRegistry.results)
|
|
12207
|
+
return nodeRegistry.results[methodKey];
|
|
12208
|
+
const cls = nodeRegistry.classes[classKey];
|
|
12209
|
+
const methodName = callee.name.text;
|
|
12210
|
+
if (methodName in cls.methods)
|
|
12211
|
+
return cls.methods[methodName];
|
|
12212
|
+
}
|
|
12213
|
+
return undefined;
|
|
12214
|
+
}
|
|
12215
|
+
if (isNewExpression(expr)) {
|
|
12216
|
+
const key = resolveNodeKey(expr.expression);
|
|
12217
|
+
if (key && nodeRegistry.constructors[key]) {
|
|
12218
|
+
const typer = nodeRegistry.typers[`${key}()`];
|
|
12219
|
+
const nodes = expr.arguments ?? [];
|
|
12220
|
+
if (typer)
|
|
12221
|
+
return typer(nodeTypeCall(nodes));
|
|
12222
|
+
return nodeRegistry.classes[key]?.goType;
|
|
10188
12223
|
}
|
|
12224
|
+
return undefined;
|
|
12225
|
+
}
|
|
12226
|
+
if (isIdentifier(expr) || isPropertyAccessExpression(expr)) {
|
|
12227
|
+
const key = resolveNodeKey(expr);
|
|
12228
|
+
if (key && key in nodeRegistry.valueTypes)
|
|
12229
|
+
return nodeRegistry.valueTypes[key];
|
|
12230
|
+
return undefined;
|
|
12231
|
+
}
|
|
12232
|
+
if (isElementAccessExpression(expr) && inferExpressionType(expr.expression) === '[]byte')
|
|
12233
|
+
return 'float64';
|
|
12234
|
+
return undefined;
|
|
12235
|
+
}
|
|
12236
|
+
// Node classes are pseudo-classes: their methods, fields and accessors feed the
|
|
12237
|
+
// class tables, so dispatch and inference treat them like user classes
|
|
12238
|
+
function seedNodeClassTables() {
|
|
12239
|
+
for (const key of Object.keys(nodeRegistry.classes)) {
|
|
12240
|
+
const cls = nodeRegistry.classes[key];
|
|
12241
|
+
if (!cls.goType.startsWith('*'))
|
|
12242
|
+
continue;
|
|
12243
|
+
const goName = cls.goType.slice(1);
|
|
12244
|
+
// Classes sharing a Go type (ErrnoException / AssertionError on *TnError) merge their tables
|
|
12245
|
+
const methods = classMethodReturnTypes.get(goName) ?? new Map();
|
|
12246
|
+
for (const name of Object.keys(cls.methods))
|
|
12247
|
+
methods.set(name, cls.methods[name]);
|
|
12248
|
+
classMethodReturnTypes.set(goName, methods);
|
|
12249
|
+
const props = classPropertyTypes.get(goName) ?? new Map();
|
|
12250
|
+
for (const name of Object.keys(cls.props))
|
|
12251
|
+
props.set(name, cls.props[name]);
|
|
12252
|
+
for (const name of Object.keys(cls.getters)) {
|
|
12253
|
+
props.set(name, cls.getters[name]);
|
|
12254
|
+
classGetterMethods.add(`${goName}.${name}`);
|
|
12255
|
+
}
|
|
12256
|
+
for (const name of Object.keys(cls.setters))
|
|
12257
|
+
classSetterMethods.add(`${goName}.${name}`);
|
|
12258
|
+
classPropertyTypes.set(goName, props);
|
|
10189
12259
|
}
|
|
10190
12260
|
}
|
|
10191
12261
|
function visitImportDeclaration(node) {
|
|
@@ -10199,9 +12269,14 @@ function visitImportDeclaration(node) {
|
|
|
10199
12269
|
if (fileResolver && !includedLocalImports.has(key)) {
|
|
10200
12270
|
includedLocalImports.add(key);
|
|
10201
12271
|
const resolved = fileResolver(moduleSpec, currentFileDir);
|
|
10202
|
-
|
|
12272
|
+
// The same file reached through different specifiers (./registry.js from
|
|
12273
|
+
// a sibling, ./node/registry.js from the parent) is included once
|
|
12274
|
+
const stem = (moduleSpec.split('/').pop() ?? moduleSpec).replace(/\.(js|ts|tsx|mjs)$/, '');
|
|
12275
|
+
const fileKey = resolved ? `file::${resolved.dir}::${stem}` : '';
|
|
12276
|
+
if (resolved && !includedLocalImports.has(fileKey)) {
|
|
12277
|
+
includedLocalImports.add(fileKey);
|
|
10203
12278
|
const goFileName = specifierToGoFileName(moduleSpec);
|
|
10204
|
-
includeLocalImport(resolved.content, resolved.dir, goFileName, !!resolved.tsx);
|
|
12279
|
+
includeLocalImport(resolved.content, resolved.dir, goFileName, !!resolved.tsx, resolved.path ?? moduleSpec);
|
|
10205
12280
|
}
|
|
10206
12281
|
}
|
|
10207
12282
|
// Renamed bindings refer to the original declaration (one Go package)
|
|
@@ -10220,22 +12295,37 @@ function visitImportDeclaration(node) {
|
|
|
10220
12295
|
}
|
|
10221
12296
|
return '';
|
|
10222
12297
|
}
|
|
12298
|
+
// Node.js standard library (`node:path`, or a bare builtin name like `fs`):
|
|
12299
|
+
// local names resolve against the registry; type-only imports bind types
|
|
12300
|
+
const nodeModule = nodeModuleName(moduleSpec);
|
|
12301
|
+
if (nodeModule) {
|
|
12302
|
+
setupNodeModuleImport(node, nodeModule);
|
|
12303
|
+
return '';
|
|
12304
|
+
}
|
|
10223
12305
|
// Type-only imports contribute nothing to runtime
|
|
10224
12306
|
if (node.importClause?.isTypeOnly)
|
|
10225
12307
|
return '';
|
|
12308
|
+
if (moduleSpec.startsWith('node:')) {
|
|
12309
|
+
reportError(node, `Unsupported Node.js module: ${moduleSpec} (see conformance.md#node-builtins-node)`);
|
|
12310
|
+
return '';
|
|
12311
|
+
}
|
|
10226
12312
|
// Go standard library: `import { Println } from 'go:fmt'` → import "fmt"
|
|
10227
12313
|
if (moduleSpec.startsWith('go:')) {
|
|
10228
12314
|
const goPkg = moduleSpec.slice(3);
|
|
12315
|
+
// go:builtin is virtual (ref/chan/GoError): no Go import, just aliases.
|
|
12316
|
+
if (goPkg === 'builtin') {
|
|
12317
|
+
registerGoBuiltinAliases(node);
|
|
12318
|
+
return '';
|
|
12319
|
+
}
|
|
12320
|
+
// Side-effect import (`import 'go:time/tzdata'`): blank Go import
|
|
12321
|
+
if (!node.importClause) {
|
|
12322
|
+
goSideEffectImports.add(goPkg);
|
|
12323
|
+
return '';
|
|
12324
|
+
}
|
|
10229
12325
|
importedPackages.add(goPkg);
|
|
10230
12326
|
registerGoPackageAliases(node, goPkg);
|
|
10231
12327
|
return '';
|
|
10232
12328
|
}
|
|
10233
|
-
// Node.js standard library: `import path from 'node:path'` → mapped Go packages
|
|
10234
|
-
if (moduleSpec.startsWith('node:')) {
|
|
10235
|
-
const nodeModule = moduleSpec.slice(5);
|
|
10236
|
-
setupNodeModuleImport(node, nodeModule);
|
|
10237
|
-
return '';
|
|
10238
|
-
}
|
|
10239
12329
|
// npm package (bare specifier) → transpile to its own Go file
|
|
10240
12330
|
const npmKey = `npm::${moduleSpec}`;
|
|
10241
12331
|
if (fileResolver && !includedLocalImports.has(npmKey)) {
|
|
@@ -10243,7 +12333,7 @@ function visitImportDeclaration(node) {
|
|
|
10243
12333
|
const resolved = fileResolver(moduleSpec, currentFileDir);
|
|
10244
12334
|
if (resolved) {
|
|
10245
12335
|
const goFileName = specifierToGoFileName(moduleSpec);
|
|
10246
|
-
includeLocalImport(resolved.content, resolved.dir, goFileName);
|
|
12336
|
+
includeLocalImport(resolved.content, resolved.dir, goFileName, !!resolved.tsx, resolved.path ?? moduleSpec);
|
|
10247
12337
|
}
|
|
10248
12338
|
}
|
|
10249
12339
|
// Register default/namespace import name as a "stripped" namespace
|
|
@@ -10260,14 +12350,39 @@ function visitImportDeclaration(node) {
|
|
|
10260
12350
|
return '';
|
|
10261
12351
|
}
|
|
10262
12352
|
// Go packages imported under a fixed alias to avoid collisions with user
|
|
10263
|
-
// variables (a variable named `big` would shadow the package name)
|
|
10264
|
-
|
|
10265
|
-
|
|
10266
|
-
|
|
10267
|
-
|
|
10268
|
-
|
|
12353
|
+
// variables (a variable named `big` would shadow the package name).
|
|
12354
|
+
// Table lives in src/go/overrides.ts (shared with the bridge qualifier).
|
|
12355
|
+
// Go bridge types are fully qualified internally (net/http.Cookie,
|
|
12356
|
+
// *[]io/fs.DirEntry); emitted code spells them with the import qualifier.
|
|
12357
|
+
// Rewrites path-qualified names outside string literals and comments, and
|
|
12358
|
+
// imports their packages.
|
|
12359
|
+
function localizeGoPaths(code) {
|
|
12360
|
+
if (code.indexOf('/') < 0)
|
|
12361
|
+
return code;
|
|
12362
|
+
// String/rune literals and line comments match first and pass through
|
|
12363
|
+
return code.replace(/"(?:[^"\\\n]|\\.)*"|`[^`]*`|'(?:[^'\\\n]|\\.)*'|\/\/[^\n]*|\b([a-z][a-z0-9_]*(?:\/[a-z0-9_]+)+)\./g, (match, path) => {
|
|
12364
|
+
if (!path || !hasGoPackage(path))
|
|
12365
|
+
return match;
|
|
12366
|
+
ensureGoQualifier(path);
|
|
12367
|
+
importedPackages.add(path);
|
|
12368
|
+
return `${goQualifier(path)}.`;
|
|
12369
|
+
});
|
|
12370
|
+
}
|
|
12371
|
+
// Whether Go code uses `name.` as a package qualifier (not the tail of a
|
|
12372
|
+
// longer identifier or selector: `ratio.` does not use `io`)
|
|
12373
|
+
function referencesQualifier(code, name) {
|
|
12374
|
+
const needle = `${name}.`;
|
|
12375
|
+
let at = code.indexOf(needle);
|
|
12376
|
+
while (at >= 0) {
|
|
12377
|
+
const prev = at > 0 ? code[at - 1] : ' ';
|
|
12378
|
+
if (!/[A-Za-z0-9_.]/.test(prev))
|
|
12379
|
+
return true;
|
|
12380
|
+
at = code.indexOf(needle, at + 1);
|
|
12381
|
+
}
|
|
12382
|
+
return false;
|
|
12383
|
+
}
|
|
10269
12384
|
function goImportLine(pkg) {
|
|
10270
|
-
const alias =
|
|
12385
|
+
const alias = goPathAliases[pkg];
|
|
10271
12386
|
return alias ? `import ${alias} "${pkg}"` : `import "${pkg}"`;
|
|
10272
12387
|
}
|
|
10273
12388
|
// Records the type of a variable introduced by a loop or binding
|
|
@@ -10286,9 +12401,89 @@ function registerLocalVariable(name, goType) {
|
|
|
10286
12401
|
variableGoTypes.delete(name);
|
|
10287
12402
|
}
|
|
10288
12403
|
// for...of over arrays and strings (a string yields one-character strings)
|
|
12404
|
+
// Whether a Go type is an iter.Seq / Seq2 (range-over-func).
|
|
12405
|
+
function isGoIterSeq(goType) {
|
|
12406
|
+
const base = goType.replace(/^\*/, '');
|
|
12407
|
+
return base.startsWith('iter.Seq') || base.startsWith('import(\'go:iter\').Seq');
|
|
12408
|
+
}
|
|
12409
|
+
// Element type of an iter.Seq[V] (Seq2 yields its key type in for…of).
|
|
12410
|
+
function goIterElem(goType) {
|
|
12411
|
+
const start = goType.indexOf('[');
|
|
12412
|
+
const end = goType.lastIndexOf(']');
|
|
12413
|
+
if (start < 0 || end < 0)
|
|
12414
|
+
return 'interface{}';
|
|
12415
|
+
const inner = goType.slice(start + 1, end);
|
|
12416
|
+
let depth = 0;
|
|
12417
|
+
for (let i = 0; i < inner.length; i++) {
|
|
12418
|
+
if (inner[i] === '[' || inner[i] === '(')
|
|
12419
|
+
depth++;
|
|
12420
|
+
else if (inner[i] === ']' || inner[i] === ')')
|
|
12421
|
+
depth--;
|
|
12422
|
+
else if (inner[i] === ',' && depth === 0)
|
|
12423
|
+
return inner.slice(0, i).trim() || 'interface{}';
|
|
12424
|
+
}
|
|
12425
|
+
return inner.trim() || 'interface{}';
|
|
12426
|
+
}
|
|
12427
|
+
// Type args of an iter.Seq[V] / Seq2[K, V].
|
|
12428
|
+
function goIterElems(goType) {
|
|
12429
|
+
const start = goType.indexOf('[');
|
|
12430
|
+
const end = goType.lastIndexOf(']');
|
|
12431
|
+
if (start < 0 || end < 0)
|
|
12432
|
+
return [];
|
|
12433
|
+
const inner = goType.slice(start + 1, end);
|
|
12434
|
+
const out = [];
|
|
12435
|
+
let depth = 0;
|
|
12436
|
+
let from = 0;
|
|
12437
|
+
for (let i = 0; i < inner.length; i++) {
|
|
12438
|
+
if (inner[i] === '[' || inner[i] === '(')
|
|
12439
|
+
depth++;
|
|
12440
|
+
else if (inner[i] === ']' || inner[i] === ')')
|
|
12441
|
+
depth--;
|
|
12442
|
+
else if (inner[i] === ',' && depth === 0) {
|
|
12443
|
+
out.push(inner.slice(from, i).trim());
|
|
12444
|
+
from = i + 1;
|
|
12445
|
+
}
|
|
12446
|
+
}
|
|
12447
|
+
out.push(inner.slice(from).trim());
|
|
12448
|
+
return out;
|
|
12449
|
+
}
|
|
10289
12450
|
function visitForOfSequence(node, iterExpr, iterType) {
|
|
10290
12451
|
if (iterType === 'interface{}')
|
|
10291
12452
|
iterType = '[]interface{}';
|
|
12453
|
+
// Go iter.Seq[V] / Seq2[K, V]: range the func directly.
|
|
12454
|
+
if (iterType && isGoIterSeq(iterType)) {
|
|
12455
|
+
const declaration = isVariableDeclarationList(node.initializer)
|
|
12456
|
+
? node.initializer.declarations[0]
|
|
12457
|
+
: undefined;
|
|
12458
|
+
// Go ints arrive under a temp and re-bind as TS numbers
|
|
12459
|
+
let prefix = '';
|
|
12460
|
+
const bindIterVar = (name, goType) => {
|
|
12461
|
+
if (!name || isOmittedExpression(name) || !isIdentifier(name))
|
|
12462
|
+
return '_';
|
|
12463
|
+
const local = visit(name);
|
|
12464
|
+
if (goType !== 'float64' && isGoNumericKind(goType)) {
|
|
12465
|
+
const raw = getTempName('it');
|
|
12466
|
+
prefix += `${local} := float64(${raw}); _ = ${local};\n\t\t`;
|
|
12467
|
+
registerLocalVariable(name.text, 'float64');
|
|
12468
|
+
return raw;
|
|
12469
|
+
}
|
|
12470
|
+
registerLocalVariable(name.text, goType);
|
|
12471
|
+
return local;
|
|
12472
|
+
};
|
|
12473
|
+
// Seq2 destructures as [key, value]
|
|
12474
|
+
if (declaration && isArrayBindingPattern(declaration.name) && goIterElems(iterType).length === 2) {
|
|
12475
|
+
const elems = goIterElems(iterType);
|
|
12476
|
+
const bound = declaration.name.elements.map((el, i) => bindIterVar(isOmittedExpression(el) ? undefined : el.name, elems[i] ?? 'interface{}'));
|
|
12477
|
+
const key = bound[0] ?? '_';
|
|
12478
|
+
const value = bound[1] ?? '_';
|
|
12479
|
+
return `for ${key}, ${value} := range ${iterExpr}${visitLoopBody(node.statement, prefix)}`;
|
|
12480
|
+
}
|
|
12481
|
+
if (declaration && isIdentifier(declaration.name)) {
|
|
12482
|
+
const loopVar = bindIterVar(declaration.name, goIterElem(iterType));
|
|
12483
|
+
return `for ${loopVar} := range ${iterExpr}${visitLoopBody(node.statement, prefix)}`;
|
|
12484
|
+
}
|
|
12485
|
+
return `for _ = range ${iterExpr}${visitLoopBody(node.statement)}`;
|
|
12486
|
+
}
|
|
10292
12487
|
// An iterable class instance: range its Symbol_iterator channel
|
|
10293
12488
|
if (iterType?.startsWith('*')) {
|
|
10294
12489
|
const iterClass = iterType.slice(1).replace(/\[.*\]$/, '');
|
|
@@ -10309,10 +12504,49 @@ function visitForOfSequence(node, iterExpr, iterType) {
|
|
|
10309
12504
|
const declaration = isVariableDeclarationList(node.initializer)
|
|
10310
12505
|
? node.initializer.declarations[0]
|
|
10311
12506
|
: undefined;
|
|
12507
|
+
// for await: receive through the event loop (the values may come from loop
|
|
12508
|
+
// tasks — stream data, events — that must run while this loop waits)
|
|
12509
|
+
if (node.awaitModifier) {
|
|
12510
|
+
useHelper('eventLoop');
|
|
12511
|
+
const chanVar = getTempName('chan');
|
|
12512
|
+
const valueVar = declaration && isIdentifier(declaration.name) ? visit(declaration.name) : getTempName('item');
|
|
12513
|
+
if (declaration && isIdentifier(declaration.name))
|
|
12514
|
+
registerLocalVariable(declaration.name.text, elemType);
|
|
12515
|
+
// A rejected iterator (a stream error) throws out of the loop
|
|
12516
|
+
useHelper('promise');
|
|
12517
|
+
let awaitPrefix = `${valueVar}, __ok := TnLoopReceive(${chanVar})\n\t\tif !__ok {\n\t\t\tif __reason, __rejected := tnRejections.Load(${chanVar}); __rejected {\n\t\t\t\tpanic(__reason)\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\t_ = ${valueVar}\n\t\t`;
|
|
12518
|
+
if (declaration && isArrayBindingPattern(declaration.name)) {
|
|
12519
|
+
const memberType = elemType.startsWith('[]') ? elemType.slice(2) : undefined;
|
|
12520
|
+
useHelper('dynamic');
|
|
12521
|
+
(declaration.name.elements ?? []).forEach((el, index) => {
|
|
12522
|
+
if (isOmittedExpression(el))
|
|
12523
|
+
return;
|
|
12524
|
+
const name = visit(el.name);
|
|
12525
|
+
registerLocalVariable(name, memberType);
|
|
12526
|
+
awaitPrefix += `${name} := TnAt(${valueVar}, ${index})\n\t\t_ = ${name}\n\t\t`;
|
|
12527
|
+
});
|
|
12528
|
+
}
|
|
12529
|
+
return `${chanVar} := ${iterExpr}\n\t\tfor ${visitLoopBody(node.statement, awaitPrefix)}`;
|
|
12530
|
+
}
|
|
10312
12531
|
if (declaration && isIdentifier(declaration.name)) {
|
|
10313
12532
|
registerLocalVariable(declaration.name.text, elemType);
|
|
10314
12533
|
return `for ${visit(declaration.name)} := range ${iterExpr}${visitLoopBody(node.statement)}`;
|
|
10315
12534
|
}
|
|
12535
|
+
// for await (const [a, b] of channel) → a := item[0]; b := item[1]
|
|
12536
|
+
if (declaration && isArrayBindingPattern(declaration.name)) {
|
|
12537
|
+
const chanItem = getTempName('item');
|
|
12538
|
+
const memberType = elemType.startsWith('[]') ? elemType.slice(2) : undefined;
|
|
12539
|
+
let chanPrefix = '';
|
|
12540
|
+
(declaration.name.elements ?? []).forEach((el, index) => {
|
|
12541
|
+
if (isOmittedExpression(el))
|
|
12542
|
+
return;
|
|
12543
|
+
const name = visit(el.name);
|
|
12544
|
+
registerLocalVariable(name, memberType);
|
|
12545
|
+
chanPrefix += `${name} := TnAt(${chanItem}, ${index})\n\t\t_ = ${name}\n\t\t`;
|
|
12546
|
+
});
|
|
12547
|
+
useHelper('dynamic');
|
|
12548
|
+
return `for ${chanItem} := range ${iterExpr}${visitLoopBody(node.statement, chanPrefix)}`;
|
|
12549
|
+
}
|
|
10316
12550
|
return `for _ = range ${iterExpr}${visitLoopBody(node.statement)}`;
|
|
10317
12551
|
}
|
|
10318
12552
|
const elementType = iterType === 'string' ? 'string' : iterType?.startsWith('[]') ? iterType.slice(2) : undefined;
|
|
@@ -10371,6 +12605,8 @@ function visitReturningTryStatement(node, fn, options) {
|
|
|
10371
12605
|
if (node.catchClause) {
|
|
10372
12606
|
const varDecl = node.catchClause.variableDeclaration;
|
|
10373
12607
|
const catchVar = varDecl ? visit(varDecl.name) : '_r';
|
|
12608
|
+
if (varDecl && isIdentifier(varDecl.name))
|
|
12609
|
+
registerLocalVariable(varDecl.name.text, 'interface{}');
|
|
10374
12610
|
const catchBody = blockCode(node.catchClause.block, true);
|
|
10375
12611
|
deferreds.push(`defer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\t${catchVar} := r\n\t\t\t\t_ = ${catchVar}\n\t\t\t\t${catchBody}\t\t\t}\n\t\t\t}()`);
|
|
10376
12612
|
}
|