From a8a3f6174194a2c7cc9b3287b41b07bf86b0e19c Mon Sep 17 00:00:00 2001 From: syumai Date: Mon, 4 May 2026 17:46:25 +0900 Subject: [PATCH 1/3] scripts: add wasm_exec.js generator Adds scripts/gen-wasm-exec, a TypeScript + jscodeshift tool that fetches upstream wasm_exec.js from golang/go and tinygo-org/tinygo at the configured tags and reapplies the Workers-specific patches (context arg on Go.run, globalProxy, TinyGo Node.js-only blocks commented out). Run `make gen-wasm-exec` (with optional GO_VERSION / TINYGO_VERSION overrides) to regenerate cmd/workers-assets-gen/assets/wasm_exec_*.js. Co-Authored-By: Claude Opus 4.7 (1M context) --- Makefile | 7 + cmd/workers-assets-gen/assets/wasm_exec_go.js | 2 +- .../assets/wasm_exec_tinygo.js | 1 + scripts/gen-wasm-exec/.gitignore | 1 + scripts/gen-wasm-exec/package.json | 20 + scripts/gen-wasm-exec/pnpm-lock.yaml | 941 ++++++++++++++++++ scripts/gen-wasm-exec/src/fetch.ts | 23 + scripts/gen-wasm-exec/src/index.ts | 78 ++ scripts/gen-wasm-exec/src/post-process.ts | 73 ++ scripts/gen-wasm-exec/src/transform-go.ts | 110 ++ scripts/gen-wasm-exec/src/transform-tinygo.ts | 168 ++++ scripts/gen-wasm-exec/tsconfig.json | 14 + 12 files changed, 1437 insertions(+), 1 deletion(-) create mode 100644 scripts/gen-wasm-exec/.gitignore create mode 100644 scripts/gen-wasm-exec/package.json create mode 100644 scripts/gen-wasm-exec/pnpm-lock.yaml create mode 100644 scripts/gen-wasm-exec/src/fetch.ts create mode 100644 scripts/gen-wasm-exec/src/index.ts create mode 100644 scripts/gen-wasm-exec/src/post-process.ts create mode 100644 scripts/gen-wasm-exec/src/transform-go.ts create mode 100644 scripts/gen-wasm-exec/src/transform-tinygo.ts create mode 100644 scripts/gen-wasm-exec/tsconfig.json diff --git a/Makefile b/Makefile index 9bac7701..f610c255 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,8 @@ SHELL := /bin/bash +GO_VERSION ?= 1.26.2 +TINYGO_VERSION ?= 0.41.1 + .PHONY: test test: @PATH=$(CURDIR)/misc/wasm:$$PATH GOOS=js GOARCH=wasm go test ./... @@ -12,3 +15,7 @@ build-examples: cd $$dir && GOOS=js GOARCH=wasm go build -o ./build/app.wasm; \ cd ../../; \ done + +.PHONY: gen-wasm-exec +gen-wasm-exec: + cd scripts/gen-wasm-exec && pnpm run gen --go $(GO_VERSION) --tinygo $(TINYGO_VERSION) diff --git a/cmd/workers-assets-gen/assets/wasm_exec_go.js b/cmd/workers-assets-gen/assets/wasm_exec_go.js index 99585e00..d0ab8884 100644 --- a/cmd/workers-assets-gen/assets/wasm_exec_go.js +++ b/cmd/workers-assets-gen/assets/wasm_exec_go.js @@ -580,4 +580,4 @@ }; } } -})(); \ No newline at end of file +})(); diff --git a/cmd/workers-assets-gen/assets/wasm_exec_tinygo.js b/cmd/workers-assets-gen/assets/wasm_exec_tinygo.js index 64aa2997..cb48774b 100644 --- a/cmd/workers-assets-gen/assets/wasm_exec_tinygo.js +++ b/cmd/workers-assets-gen/assets/wasm_exec_tinygo.js @@ -550,6 +550,7 @@ }; } } + /* if ( global.require && diff --git a/scripts/gen-wasm-exec/.gitignore b/scripts/gen-wasm-exec/.gitignore new file mode 100644 index 00000000..c2658d7d --- /dev/null +++ b/scripts/gen-wasm-exec/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/scripts/gen-wasm-exec/package.json b/scripts/gen-wasm-exec/package.json new file mode 100644 index 00000000..7e606f73 --- /dev/null +++ b/scripts/gen-wasm-exec/package.json @@ -0,0 +1,20 @@ +{ + "name": "gen-wasm-exec", + "version": "0.0.0", + "private": true, + "type": "module", + "engines": { + "node": ">=24.0.0" + }, + "scripts": { + "gen": "node src/index.ts", + "typecheck": "tsc --noEmit" + }, + "packageManager": "pnpm@11.0.4", + "devDependencies": { + "@types/jscodeshift": "^17.3.0", + "@types/node": "^25.6.0", + "jscodeshift": "^17.3.0", + "typescript": "^6.0.3" + } +} diff --git a/scripts/gen-wasm-exec/pnpm-lock.yaml b/scripts/gen-wasm-exec/pnpm-lock.yaml new file mode 100644 index 00000000..892d9f1c --- /dev/null +++ b/scripts/gen-wasm-exec/pnpm-lock.yaml @@ -0,0 +1,941 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@types/jscodeshift': + specifier: ^17.3.0 + version: 17.3.0 + '@types/node': + specifier: ^25.6.0 + version: 25.6.0 + jscodeshift: + specifier: ^17.3.0 + version: 17.3.0 + typescript: + specifier: ^6.0.3 + version: 6.0.3 + +packages: + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.3': + resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.29.3': + resolution: {integrity: sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.28.5': + resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-replace-supers@7.28.6': + resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.3': + resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-syntax-flow@7.28.6': + resolution: {integrity: sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.28.6': + resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.28.6': + resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.28.6': + resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-flow-strip-types@7.27.1': + resolution: {integrity: sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.28.6': + resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6': + resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.28.6': + resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.28.6': + resolution: {integrity: sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.6': + resolution: {integrity: sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-flow@7.27.1': + resolution: {integrity: sha512-ez3a2it5Fn6P54W8QkbfIyyIbxlXvcxyWHHvno1Wg0Ej5eiJY5hBb8ExttoIOJJk7V2dZE6prP7iby5q2aQ0Lg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.28.5': + resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/register@7.29.3': + resolution: {integrity: sha512-F6C1KpIdoImKQfsD6HSxZ+mS4YY/2Q+JsqrmTC5ApVkTR2rG+nnbpjhWwzA5bDNu8mJjB3AryqDaWFLd4gCbJQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@types/jscodeshift@17.3.0': + resolution: {integrity: sha512-ogvGG8VQQqAQQ096uRh+d6tBHrYuZjsumHirKtvBa5qEyTMN3IQJ7apo+sw9lxaB/iKWIhbbLlF3zmAWk9XQIg==} + + '@types/node@25.6.0': + resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} + + ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + + baseline-browser-mapping@2.10.25: + resolution: {integrity: sha512-QO/VHsXCQdnzADMfmkeOPvHdIAkoB7i0/rGjINPJEetLx75hNttVWGQ/jycHUDP9zZ9rupbm60WRxcwViB0MiA==} + engines: {node: '>=6.0.0'} + hasBin: true + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + caniuse-lite@1.0.30001791: + resolution: {integrity: sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==} + + clone-deep@4.0.1: + resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} + engines: {node: '>=6'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + electron-to-chromium@1.5.349: + resolution: {integrity: sha512-QsWVGyRuY07Aqb234QytTfwd5d9AJlfNIQ5wIOl1L+PZDzI9d9+Fn0FRale/QYlFxt/bUnB0/nLd1jFPGxGK1A==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-cache-dir@2.1.0: + resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==} + engines: {node: '>=6'} + + find-up@3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + + flow-parser@0.312.0: + resolution: {integrity: sha512-y1iG1bU7i22Mc3sSlchhmV992o8vxzwFLy6K1ZZqbmfXf0qpljN1/AZRuhOA+8uMz8a5iQUdoUyT7pvmdN1flA==} + engines: {node: '>=0.4.0'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-object@2.0.4: + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} + + isobject@3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + jscodeshift@17.3.0: + resolution: {integrity: sha512-LjFrGOIORqXBU+jwfC9nbkjmQfFldtMIoS6d9z2LG/lkmyNXsJAySPT+2SWXJEoE68/bCWcxKpXH37npftgmow==} + engines: {node: '>=16'} + hasBin: true + peerDependencies: + '@babel/preset-env': ^7.1.6 + peerDependenciesMeta: + '@babel/preset-env': + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + locate-path@3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + make-dir@2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + node-releases@2.0.38: + resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-locate@3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-dir@3.0.0: + resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==} + engines: {node: '>=6'} + + recast@0.23.11: + resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} + engines: {node: '>= 4'} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + shallow-clone@3.0.1: + resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} + engines: {node: '>=8'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + engines: {node: '>=14.14'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.19.2: + resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + +snapshots: + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.3': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.29.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.29.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.28.5': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-syntax-flow@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-flow-strip-types@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.29.0) + + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/preset-flow@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0) + + '@babel/preset-typescript@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/register@7.29.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + clone-deep: 4.0.1 + find-cache-dir: 2.1.0 + make-dir: 2.1.0 + pirates: 4.0.7 + source-map-support: 0.5.21 + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@types/jscodeshift@17.3.0': + dependencies: + ast-types: 0.16.1 + recast: 0.23.11 + + '@types/node@25.6.0': + dependencies: + undici-types: 7.19.2 + + ast-types@0.16.1: + dependencies: + tslib: 2.8.1 + + baseline-browser-mapping@2.10.25: {} + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.25 + caniuse-lite: 1.0.30001791 + electron-to-chromium: 1.5.349 + node-releases: 2.0.38 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + buffer-from@1.1.2: {} + + caniuse-lite@1.0.30001791: {} + + clone-deep@4.0.1: + dependencies: + is-plain-object: 2.0.4 + kind-of: 6.0.3 + shallow-clone: 3.0.1 + + commondir@1.0.1: {} + + convert-source-map@2.0.0: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + electron-to-chromium@1.5.349: {} + + escalade@3.2.0: {} + + esprima@4.0.1: {} + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-cache-dir@2.1.0: + dependencies: + commondir: 1.0.1 + make-dir: 2.1.0 + pkg-dir: 3.0.0 + + find-up@3.0.0: + dependencies: + locate-path: 3.0.0 + + flow-parser@0.312.0: {} + + gensync@1.0.0-beta.2: {} + + graceful-fs@4.2.11: {} + + imurmurhash@0.1.4: {} + + is-number@7.0.0: {} + + is-plain-object@2.0.4: + dependencies: + isobject: 3.0.1 + + isobject@3.0.1: {} + + js-tokens@4.0.0: {} + + jscodeshift@17.3.0: + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.3 + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) + '@babel/preset-flow': 7.27.1(@babel/core@7.29.0) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) + '@babel/register': 7.29.3(@babel/core@7.29.0) + flow-parser: 0.312.0 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + neo-async: 2.6.2 + picocolors: 1.1.1 + recast: 0.23.11 + tmp: 0.2.5 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + + jsesc@3.1.0: {} + + json5@2.2.3: {} + + kind-of@6.0.3: {} + + locate-path@3.0.0: + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + make-dir@2.1.0: + dependencies: + pify: 4.0.1 + semver: 5.7.2 + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + ms@2.1.3: {} + + neo-async@2.6.2: {} + + node-releases@2.0.38: {} + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-locate@3.0.0: + dependencies: + p-limit: 2.3.0 + + p-try@2.2.0: {} + + path-exists@3.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + pify@4.0.1: {} + + pirates@4.0.7: {} + + pkg-dir@3.0.0: + dependencies: + find-up: 3.0.0 + + recast@0.23.11: + dependencies: + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 + + semver@5.7.2: {} + + semver@6.3.1: {} + + shallow-clone@3.0.1: + dependencies: + kind-of: 6.0.3 + + signal-exit@4.1.0: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + tiny-invariant@1.3.3: {} + + tmp@0.2.5: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + tslib@2.8.1: {} + + typescript@6.0.3: {} + + undici-types@7.19.2: {} + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + + yallist@3.1.1: {} diff --git a/scripts/gen-wasm-exec/src/fetch.ts b/scripts/gen-wasm-exec/src/fetch.ts new file mode 100644 index 00000000..5ad41197 --- /dev/null +++ b/scripts/gen-wasm-exec/src/fetch.ts @@ -0,0 +1,23 @@ +const RAW_GITHUB = "https://raw.githubusercontent.com/"; + +export function goWasmExecURL(version: string): string { + return new URL( + `golang/go/refs/tags/go${version}/lib/wasm/wasm_exec.js`, + RAW_GITHUB, + ).toString(); +} + +export function tinygoWasmExecURL(version: string): string { + return new URL( + `tinygo-org/tinygo/refs/tags/v${version}/targets/wasm_exec.js`, + RAW_GITHUB, + ).toString(); +} + +export async function fetchText(url: string): Promise { + const res = await fetch(url); + if (!res.ok) { + throw new Error(`failed to fetch ${url}: ${res.status} ${res.statusText}`); + } + return await res.text(); +} diff --git a/scripts/gen-wasm-exec/src/index.ts b/scripts/gen-wasm-exec/src/index.ts new file mode 100644 index 00000000..7743791a --- /dev/null +++ b/scripts/gen-wasm-exec/src/index.ts @@ -0,0 +1,78 @@ +import { writeFile } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { fetchText, goWasmExecURL, tinygoWasmExecURL } from "./fetch.ts"; +import { transformGo } from "./transform-go.ts"; +import { transformTinygo } from "./transform-tinygo.ts"; + +interface Args { + go?: string; + tinygo?: string; + out?: string; +} + +function parseArgs(argv: string[]): Args { + const args: Args = {}; + for (let i = 0; i < argv.length; i++) { + const a = argv[i]; + const next = argv[i + 1]; + switch (a) { + case "--go": + if (!next) throw new Error("--go requires a value"); + args.go = next; + i++; + break; + case "--tinygo": + if (!next) throw new Error("--tinygo requires a value"); + args.tinygo = next; + i++; + break; + case "--out": + if (!next) throw new Error("--out requires a value"); + args.out = next; + i++; + break; + default: + throw new Error(`unknown argument: ${a}`); + } + } + return args; +} + +async function main() { + const args = parseArgs(process.argv.slice(2)); + if (!args.go) throw new Error("--go is required (e.g. --go 1.26.2)"); + if (!args.tinygo) + throw new Error("--tinygo is required (e.g. --tinygo 0.41.1)"); + + const here = path.dirname(fileURLToPath(import.meta.url)); + // here is scripts/gen-wasm-exec/src ; default output is repo's cmd/workers-assets-gen/assets + const outDir = args.out + ? path.resolve(args.out) + : path.resolve(here, "../../../cmd/workers-assets-gen/assets"); + + const goURL = goWasmExecURL(args.go); + const tinygoURL = tinygoWasmExecURL(args.tinygo); + + console.log(`fetching ${goURL}`); + const goSrc = await fetchText(goURL); + console.log(`fetching ${tinygoURL}`); + const tinygoSrc = await fetchText(tinygoURL); + + console.log("patching Go wasm_exec.js"); + const goOut = transformGo(goSrc); + console.log("patching TinyGo wasm_exec.js"); + const tinygoOut = transformTinygo(tinygoSrc); + + const goPath = path.join(outDir, "wasm_exec_go.js"); + const tinygoPath = path.join(outDir, "wasm_exec_tinygo.js"); + await writeFile(goPath, goOut); + await writeFile(tinygoPath, tinygoOut); + console.log(`wrote ${goPath}`); + console.log(`wrote ${tinygoPath}`); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/scripts/gen-wasm-exec/src/post-process.ts b/scripts/gen-wasm-exec/src/post-process.ts new file mode 100644 index 00000000..c7459697 --- /dev/null +++ b/scripts/gen-wasm-exec/src/post-process.ts @@ -0,0 +1,73 @@ +export interface PreservedComment { + id: string; + content: string; +} + +// recast/jscodeshift drops or relocates some block comments whose contents +// happen to be valid JavaScript (e.g. tinygo's commented-out require() blocks). +// To survive the round-trip, we replace each block comment with a unique bare +// identifier so it appears in the AST as a normal ExpressionStatement, then +// swap the identifier back to the original comment after printing. +// This assumes block comments only occur at statement positions in the inputs +// we care about (Go and TinyGo wasm_exec.js both satisfy this). +export function preserveBlockComments(source: string): { + source: string; + preserved: PreservedComment[]; +} { + const preserved: PreservedComment[] = []; + let counter = 0; + const newSource = source.replace(/\/\*[\s\S]*?\*\//g, (match) => { + const id = `__GENWASM_PRESERVE_${counter++}__`; + preserved.push({ id, content: match }); + return id; + }); + return { source: newSource, preserved }; +} + +export function restoreBlockComments( + source: string, + preserved: PreservedComment[], +): string { + let result = source; + for (const p of preserved) { + // Match the identifier optionally followed by a recast-emitted `;` + const re = new RegExp(`${p.id};?`); + result = result.replace(re, p.content); + } + return result; +} + +// recast emits a mix of tabs and 4-space indents around modified nodes when +// useTabs is on but the surrounding source uses tabs. Collapse runs of 4 +// spaces that appear AFTER leading tabs into additional tabs. Leave any +// pre-tab whitespace alone so we don't mangle hand-written quirks like +// "return …" that exist in upstream sources. +export function normalizeIndent(source: string): string { + return source + .split("\n") + .map((line) => { + const m = line.match(/^(\t*)( +)/); + if (!m) return line; + const tabs = m[1]; + const spaces = m[2]; + const extraTabs = Math.floor(spaces.length / 4); + const remainingSpaces = spaces.length % 4; + return ( + tabs + + "\t".repeat(extraTabs) + + " ".repeat(remainingSpaces) + + line.slice(tabs.length + spaces.length) + ); + }) + .join("\n"); +} + +export function matchTrailingNewline( + originalSource: string, + output: string, +): string { + if (originalSource.endsWith("\n")) { + return output.endsWith("\n") ? output : `${output}\n`; + } + return output.replace(/\n+$/, ""); +} diff --git a/scripts/gen-wasm-exec/src/transform-go.ts b/scripts/gen-wasm-exec/src/transform-go.ts new file mode 100644 index 00000000..046afd76 --- /dev/null +++ b/scripts/gen-wasm-exec/src/transform-go.ts @@ -0,0 +1,110 @@ +import jscodeshift from "jscodeshift"; +import { + matchTrailingNewline, + normalizeIndent, + preserveBlockComments, + restoreBlockComments, +} from "./post-process.ts"; + +const GLOBAL_PROXY_DECL = `const globalProxy = new Proxy(globalThis, { +\tget(target, prop) { +\t\tif (prop === 'context') { +\t\t\treturn context; +\t\t} +\t\treturn Reflect.get(target, prop, target); +\t} +})`; + +export function transformGo(rawSource: string): string { + const { source, preserved } = preserveBlockComments(rawSource); + const j = jscodeshift.withParser("babel"); + const root = j(source); + + // Locate `globalThis.Go = class { ... }` + const goAssigns = root.find(j.AssignmentExpression, { + left: { + type: "MemberExpression", + object: { type: "Identifier", name: "globalThis" }, + property: { type: "Identifier", name: "Go" }, + }, + right: { type: "ClassExpression" }, + }); + if (goAssigns.size() !== 1) { + throw new Error( + `transform-go: expected 1 globalThis.Go = class assignment, got ${goAssigns.size()}`, + ); + } + const goClass = goAssigns.get(0).node.right; + + // Find the run method on the class + const runMethods = j(goClass).find(j.MethodDefinition, { + key: { name: "run" }, + }); + if (runMethods.size() !== 1) { + throw new Error( + `transform-go: expected 1 run method, got ${runMethods.size()}`, + ); + } + const runFunc = runMethods.get(0).node.value; + + // P2: add `context` parameter + if ( + runFunc.params.length !== 1 || + runFunc.params[0].type !== "Identifier" || + runFunc.params[0].name !== "instance" + ) { + throw new Error( + "transform-go: run() does not have the expected single `instance` param", + ); + } + runFunc.params.push(j.identifier("context")); + + // P3a: replace standalone `globalThis` (i.e. NOT `globalThis.foo`) inside the + // run body with `globalProxy`. Must run BEFORE inserting the proxy decl, since + // that decl itself contains a `new Proxy(globalThis, ...)` we want to keep. + const runBody = j(runFunc.body); + const standaloneGlobalThis = runBody + .find(j.Identifier, { name: "globalThis" }) + .filter((p) => { + const parent = p.parent.node; + return !( + j.MemberExpression.check(parent) && parent.object === p.node + ); + }); + if (standaloneGlobalThis.size() !== 2) { + throw new Error( + `transform-go: expected 2 standalone globalThis in run body, got ${standaloneGlobalThis.size()}`, + ); + } + standaloneGlobalThis.replaceWith(() => j.identifier("globalProxy")); + + // P3b: insert globalProxy declaration before `this._values = [...]` + const valuesStmt = runBody + .find(j.AssignmentExpression, { + left: { + type: "MemberExpression", + object: { type: "ThisExpression" }, + property: { name: "_values" }, + }, + }) + .closest(j.ExpressionStatement); + if (valuesStmt.size() !== 1) { + throw new Error( + "transform-go: could not locate this._values assignment in run body", + ); + } + const proxyDecl = j(GLOBAL_PROXY_DECL) + .find(j.VariableDeclaration) + .get(0).node; + valuesStmt.insertBefore(proxyDecl); + + let output = root.toSource({ + lineTerminator: "\n", + useTabs: true, + reuseWhitespace: true, + }); + output = restoreBlockComments(output, preserved); + output = normalizeIndent(output); + output = matchTrailingNewline(rawSource, output); + return output; +} diff --git a/scripts/gen-wasm-exec/src/transform-tinygo.ts b/scripts/gen-wasm-exec/src/transform-tinygo.ts new file mode 100644 index 00000000..4d1425ec --- /dev/null +++ b/scripts/gen-wasm-exec/src/transform-tinygo.ts @@ -0,0 +1,168 @@ +import jscodeshift from "jscodeshift"; +import { + matchTrailingNewline, + normalizeIndent, + preserveBlockComments, + restoreBlockComments, +} from "./post-process.ts"; + +// Top-level Node.js-only blocks shipped by upstream TinyGo. Each entry pins +// the exact opening substring; `closes` says how many "\n\t}" lines to skip +// past — TextEncoder is paired with TextDecoder under a single /* */, so it +// needs closes=2. +const NODEJS_BLOCKS: Array<{ opening: string; closes: number }> = [ + { + opening: '\tif (!global.require && typeof require !== "undefined") {', + closes: 1, + }, + { opening: "\tif (!global.fs && global.require) {", closes: 1 }, + { opening: "\tif (!global.crypto) {", closes: 1 }, + { opening: "\tif (!global.TextEncoder) {", closes: 2 }, + { + opening: "\tif (\n\t\tglobal.require &&\n\t\tglobal.require.main === module &&", + closes: 1, + }, +]; + +interface Range { + start: number; + end: number; +} + +function findBlockRanges(source: string): Range[] { + const ranges: Range[] = []; + for (const block of NODEJS_BLOCKS) { + const start = source.indexOf(block.opening); + if (start === -1) { + throw new Error( + `transform-tinygo: could not find block opening: ${block.opening.slice(0, 50)}…`, + ); + } + let pos = start; + for (let i = 0; i < block.closes; i++) { + const closeNL = source.indexOf("\n\t}", pos); + if (closeNL === -1) { + throw new Error( + `transform-tinygo: could not find matching close for: ${block.opening.slice(0, 50)}…`, + ); + } + pos = closeNL + 3; + } + ranges.push({ start, end: pos }); + } + return ranges; +} + +function commentOutNodejsBlocks(rawSource: string): string { + const ranges = findBlockRanges(rawSource).sort((a, b) => b.start - a.start); + let result = rawSource; + for (const m of ranges) { + const block = result.slice(m.start, m.end); + result = `${result.slice(0, m.start)}\t/*\n${block}\n\t*/${result.slice(m.end)}`; + } + return result; +} + +const GLOBAL_PROXY_DECL = `const globalProxy = new Proxy(global, { +\tget(target, prop) { +\t\tif (prop === 'context') { +\t\t\treturn context; +\t\t} +\t\treturn Reflect.get(...arguments); +\t} +})`; + +export function transformTinygo(rawSource: string): string { + const wrapped = commentOutNodejsBlocks(rawSource); + const { source, preserved } = preserveBlockComments(wrapped); + const j = jscodeshift.withParser("babel"); + const root = j(source); + + // Locate `global.Go = class { ... }` + const goAssigns = root.find(j.AssignmentExpression, { + left: { + type: "MemberExpression", + object: { type: "Identifier", name: "global" }, + property: { type: "Identifier", name: "Go" }, + }, + right: { type: "ClassExpression" }, + }); + if (goAssigns.size() !== 1) { + throw new Error( + `transform-tinygo: expected 1 global.Go = class assignment, got ${goAssigns.size()}`, + ); + } + const goClass = goAssigns.get(0).node.right; + + // Find the run method + const runMethods = j(goClass).find(j.MethodDefinition, { + key: { name: "run" }, + }); + if (runMethods.size() !== 1) { + throw new Error( + `transform-tinygo: expected 1 run method, got ${runMethods.size()}`, + ); + } + const runFunc = runMethods.get(0).node.value; + + // P1: add `context` parameter + if ( + runFunc.params.length !== 1 || + runFunc.params[0].type !== "Identifier" || + runFunc.params[0].name !== "instance" + ) { + throw new Error( + "transform-tinygo: run() does not have the expected single `instance` param", + ); + } + runFunc.params.push(j.identifier("context")); + + // P2a: replace standalone `global` inside the run body with `globalProxy`. + // Must run BEFORE inserting the proxy decl (which references `global`). + // In TinyGo's run body, the only standalone `global` is in the _values array. + const runBody = j(runFunc.body); + const standaloneGlobal = runBody + .find(j.Identifier, { name: "global" }) + .filter((p) => { + const parent = p.parent.node; + return !( + j.MemberExpression.check(parent) && parent.object === p.node + ); + }); + if (standaloneGlobal.size() !== 1) { + throw new Error( + `transform-tinygo: expected 1 standalone global in run body, got ${standaloneGlobal.size()}`, + ); + } + standaloneGlobal.replaceWith(() => j.identifier("globalProxy")); + + // P2b: insert globalProxy declaration before `this._values = [...]` + const valuesStmt = runBody + .find(j.AssignmentExpression, { + left: { + type: "MemberExpression", + object: { type: "ThisExpression" }, + property: { name: "_values" }, + }, + }) + .closest(j.ExpressionStatement); + if (valuesStmt.size() !== 1) { + throw new Error( + "transform-tinygo: could not locate this._values assignment in run body", + ); + } + const proxyDecl = j(GLOBAL_PROXY_DECL) + .find(j.VariableDeclaration) + .get(0).node; + valuesStmt.insertBefore(proxyDecl); + + let output = root.toSource({ + lineTerminator: "\n", + useTabs: true, + reuseWhitespace: true, + }); + output = restoreBlockComments(output, preserved); + output = normalizeIndent(output); + output = matchTrailingNewline(rawSource, output); + return output; +} diff --git a/scripts/gen-wasm-exec/tsconfig.json b/scripts/gen-wasm-exec/tsconfig.json new file mode 100644 index 00000000..5b74c695 --- /dev/null +++ b/scripts/gen-wasm-exec/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es2025", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "rewriteRelativeImportExtensions": true, + "erasableSyntaxOnly": true, + "verbatimModuleSyntax": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"] +} From 1d008553692d52c6251032b0b5dbe4fccd7240ac Mon Sep 17 00:00:00 2001 From: syumai Date: Mon, 4 May 2026 17:51:24 +0900 Subject: [PATCH 2/3] scripts: extract shared wasm_exec.js transform logic Move the common AST patching steps out of transform-go.ts and transform-tinygo.ts into a shared transformWasmExec() helper, parametrized by globalName, expected standalone-global count, and the globalProxy decl. TinyGo keeps its Node.js-block commenting pre-step locally and delegates the rest. Output for Go 1.26.2 / TinyGo 0.41.1 is byte-identical to before. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/gen-wasm-exec/src/transform-go.ts | 103 +-------------- scripts/gen-wasm-exec/src/transform-tinygo.ts | 104 +-------------- .../gen-wasm-exec/src/transform-wasm-exec.ts | 122 ++++++++++++++++++ 3 files changed, 134 insertions(+), 195 deletions(-) create mode 100644 scripts/gen-wasm-exec/src/transform-wasm-exec.ts diff --git a/scripts/gen-wasm-exec/src/transform-go.ts b/scripts/gen-wasm-exec/src/transform-go.ts index 046afd76..16546d86 100644 --- a/scripts/gen-wasm-exec/src/transform-go.ts +++ b/scripts/gen-wasm-exec/src/transform-go.ts @@ -1,10 +1,4 @@ -import jscodeshift from "jscodeshift"; -import { - matchTrailingNewline, - normalizeIndent, - preserveBlockComments, - restoreBlockComments, -} from "./post-process.ts"; +import { transformWasmExec } from "./transform-wasm-exec.ts"; const GLOBAL_PROXY_DECL = `const globalProxy = new Proxy(globalThis, { \tget(target, prop) { @@ -16,95 +10,10 @@ const GLOBAL_PROXY_DECL = `const globalProxy = new Proxy(globalThis, { })`; export function transformGo(rawSource: string): string { - const { source, preserved } = preserveBlockComments(rawSource); - const j = jscodeshift.withParser("babel"); - const root = j(source); - - // Locate `globalThis.Go = class { ... }` - const goAssigns = root.find(j.AssignmentExpression, { - left: { - type: "MemberExpression", - object: { type: "Identifier", name: "globalThis" }, - property: { type: "Identifier", name: "Go" }, - }, - right: { type: "ClassExpression" }, - }); - if (goAssigns.size() !== 1) { - throw new Error( - `transform-go: expected 1 globalThis.Go = class assignment, got ${goAssigns.size()}`, - ); - } - const goClass = goAssigns.get(0).node.right; - - // Find the run method on the class - const runMethods = j(goClass).find(j.MethodDefinition, { - key: { name: "run" }, - }); - if (runMethods.size() !== 1) { - throw new Error( - `transform-go: expected 1 run method, got ${runMethods.size()}`, - ); - } - const runFunc = runMethods.get(0).node.value; - - // P2: add `context` parameter - if ( - runFunc.params.length !== 1 || - runFunc.params[0].type !== "Identifier" || - runFunc.params[0].name !== "instance" - ) { - throw new Error( - "transform-go: run() does not have the expected single `instance` param", - ); - } - runFunc.params.push(j.identifier("context")); - - // P3a: replace standalone `globalThis` (i.e. NOT `globalThis.foo`) inside the - // run body with `globalProxy`. Must run BEFORE inserting the proxy decl, since - // that decl itself contains a `new Proxy(globalThis, ...)` we want to keep. - const runBody = j(runFunc.body); - const standaloneGlobalThis = runBody - .find(j.Identifier, { name: "globalThis" }) - .filter((p) => { - const parent = p.parent.node; - return !( - j.MemberExpression.check(parent) && parent.object === p.node - ); - }); - if (standaloneGlobalThis.size() !== 2) { - throw new Error( - `transform-go: expected 2 standalone globalThis in run body, got ${standaloneGlobalThis.size()}`, - ); - } - standaloneGlobalThis.replaceWith(() => j.identifier("globalProxy")); - - // P3b: insert globalProxy declaration before `this._values = [...]` - const valuesStmt = runBody - .find(j.AssignmentExpression, { - left: { - type: "MemberExpression", - object: { type: "ThisExpression" }, - property: { name: "_values" }, - }, - }) - .closest(j.ExpressionStatement); - if (valuesStmt.size() !== 1) { - throw new Error( - "transform-go: could not locate this._values assignment in run body", - ); - } - const proxyDecl = j(GLOBAL_PROXY_DECL) - .find(j.VariableDeclaration) - .get(0).node; - valuesStmt.insertBefore(proxyDecl); - - let output = root.toSource({ - lineTerminator: "\n", - useTabs: true, - reuseWhitespace: true, + return transformWasmExec(rawSource, { + flavor: "go", + globalName: "globalThis", + expectedStandaloneGlobals: 2, + globalProxyDecl: GLOBAL_PROXY_DECL, }); - output = restoreBlockComments(output, preserved); - output = normalizeIndent(output); - output = matchTrailingNewline(rawSource, output); - return output; } diff --git a/scripts/gen-wasm-exec/src/transform-tinygo.ts b/scripts/gen-wasm-exec/src/transform-tinygo.ts index 4d1425ec..05134943 100644 --- a/scripts/gen-wasm-exec/src/transform-tinygo.ts +++ b/scripts/gen-wasm-exec/src/transform-tinygo.ts @@ -1,10 +1,4 @@ -import jscodeshift from "jscodeshift"; -import { - matchTrailingNewline, - normalizeIndent, - preserveBlockComments, - restoreBlockComments, -} from "./post-process.ts"; +import { transformWasmExec } from "./transform-wasm-exec.ts"; // Top-level Node.js-only blocks shipped by upstream TinyGo. Each entry pins // the exact opening substring; `closes` says how many "\n\t}" lines to skip @@ -73,96 +67,10 @@ const GLOBAL_PROXY_DECL = `const globalProxy = new Proxy(global, { })`; export function transformTinygo(rawSource: string): string { - const wrapped = commentOutNodejsBlocks(rawSource); - const { source, preserved } = preserveBlockComments(wrapped); - const j = jscodeshift.withParser("babel"); - const root = j(source); - - // Locate `global.Go = class { ... }` - const goAssigns = root.find(j.AssignmentExpression, { - left: { - type: "MemberExpression", - object: { type: "Identifier", name: "global" }, - property: { type: "Identifier", name: "Go" }, - }, - right: { type: "ClassExpression" }, - }); - if (goAssigns.size() !== 1) { - throw new Error( - `transform-tinygo: expected 1 global.Go = class assignment, got ${goAssigns.size()}`, - ); - } - const goClass = goAssigns.get(0).node.right; - - // Find the run method - const runMethods = j(goClass).find(j.MethodDefinition, { - key: { name: "run" }, - }); - if (runMethods.size() !== 1) { - throw new Error( - `transform-tinygo: expected 1 run method, got ${runMethods.size()}`, - ); - } - const runFunc = runMethods.get(0).node.value; - - // P1: add `context` parameter - if ( - runFunc.params.length !== 1 || - runFunc.params[0].type !== "Identifier" || - runFunc.params[0].name !== "instance" - ) { - throw new Error( - "transform-tinygo: run() does not have the expected single `instance` param", - ); - } - runFunc.params.push(j.identifier("context")); - - // P2a: replace standalone `global` inside the run body with `globalProxy`. - // Must run BEFORE inserting the proxy decl (which references `global`). - // In TinyGo's run body, the only standalone `global` is in the _values array. - const runBody = j(runFunc.body); - const standaloneGlobal = runBody - .find(j.Identifier, { name: "global" }) - .filter((p) => { - const parent = p.parent.node; - return !( - j.MemberExpression.check(parent) && parent.object === p.node - ); - }); - if (standaloneGlobal.size() !== 1) { - throw new Error( - `transform-tinygo: expected 1 standalone global in run body, got ${standaloneGlobal.size()}`, - ); - } - standaloneGlobal.replaceWith(() => j.identifier("globalProxy")); - - // P2b: insert globalProxy declaration before `this._values = [...]` - const valuesStmt = runBody - .find(j.AssignmentExpression, { - left: { - type: "MemberExpression", - object: { type: "ThisExpression" }, - property: { name: "_values" }, - }, - }) - .closest(j.ExpressionStatement); - if (valuesStmt.size() !== 1) { - throw new Error( - "transform-tinygo: could not locate this._values assignment in run body", - ); - } - const proxyDecl = j(GLOBAL_PROXY_DECL) - .find(j.VariableDeclaration) - .get(0).node; - valuesStmt.insertBefore(proxyDecl); - - let output = root.toSource({ - lineTerminator: "\n", - useTabs: true, - reuseWhitespace: true, + return transformWasmExec(commentOutNodejsBlocks(rawSource), { + flavor: "tinygo", + globalName: "global", + expectedStandaloneGlobals: 1, + globalProxyDecl: GLOBAL_PROXY_DECL, }); - output = restoreBlockComments(output, preserved); - output = normalizeIndent(output); - output = matchTrailingNewline(rawSource, output); - return output; } diff --git a/scripts/gen-wasm-exec/src/transform-wasm-exec.ts b/scripts/gen-wasm-exec/src/transform-wasm-exec.ts new file mode 100644 index 00000000..a800d5ae --- /dev/null +++ b/scripts/gen-wasm-exec/src/transform-wasm-exec.ts @@ -0,0 +1,122 @@ +import jscodeshift from "jscodeshift"; +import { + matchTrailingNewline, + normalizeIndent, + preserveBlockComments, + restoreBlockComments, +} from "./post-process.ts"; + +export interface WasmExecTransformConfig { + // Used as a prefix in thrown error messages, e.g. "go" or "tinygo". + flavor: string; + // Name of the global object the wasm_exec.js source uses: "globalThis" + // for Go, "global" for TinyGo. + globalName: string; + // Number of standalone (non-MemberExpression) occurrences of `globalName` + // expected inside the run() body. Go has 2 (Object.assign target plus the + // _values array entry); TinyGo has 1 (the _values array entry only). + expectedStandaloneGlobals: number; + // Literal source of the `const globalProxy = new Proxy(...)` declaration + // to inject. The two flavors use slightly different bodies. + globalProxyDecl: string; +} + +export function transformWasmExec( + rawSource: string, + config: WasmExecTransformConfig, +): string { + const { flavor, globalName, expectedStandaloneGlobals, globalProxyDecl } = + config; + const { source, preserved } = preserveBlockComments(rawSource); + const j = jscodeshift.withParser("babel"); + const root = j(source); + + // Locate `.Go = class { ... }` + const goAssigns = root.find(j.AssignmentExpression, { + left: { + type: "MemberExpression", + object: { type: "Identifier", name: globalName }, + property: { type: "Identifier", name: "Go" }, + }, + right: { type: "ClassExpression" }, + }); + if (goAssigns.size() !== 1) { + throw new Error( + `transform-${flavor}: expected 1 ${globalName}.Go = class assignment, got ${goAssigns.size()}`, + ); + } + const goClass = goAssigns.get(0).node.right; + + // Find the run method on the class + const runMethods = j(goClass).find(j.MethodDefinition, { + key: { name: "run" }, + }); + if (runMethods.size() !== 1) { + throw new Error( + `transform-${flavor}: expected 1 run method, got ${runMethods.size()}`, + ); + } + const runFunc = runMethods.get(0).node.value; + + // Add `context` parameter to run(instance) + if ( + runFunc.params.length !== 1 || + runFunc.params[0].type !== "Identifier" || + runFunc.params[0].name !== "instance" + ) { + throw new Error( + `transform-${flavor}: run() does not have the expected single \`instance\` param`, + ); + } + runFunc.params.push(j.identifier("context")); + + // Replace standalone `` (i.e. NOT `.foo`) inside + // the run body with `globalProxy`. Must run BEFORE inserting the proxy + // decl, since that decl itself contains `new Proxy(, ...)` we + // want to keep intact. + const runBody = j(runFunc.body); + const standaloneGlobal = runBody + .find(j.Identifier, { name: globalName }) + .filter((p) => { + const parent = p.parent.node; + return !( + j.MemberExpression.check(parent) && parent.object === p.node + ); + }); + if (standaloneGlobal.size() !== expectedStandaloneGlobals) { + throw new Error( + `transform-${flavor}: expected ${expectedStandaloneGlobals} standalone ${globalName} in run body, got ${standaloneGlobal.size()}`, + ); + } + standaloneGlobal.replaceWith(() => j.identifier("globalProxy")); + + // Insert globalProxy declaration before `this._values = [...]` + const valuesStmt = runBody + .find(j.AssignmentExpression, { + left: { + type: "MemberExpression", + object: { type: "ThisExpression" }, + property: { name: "_values" }, + }, + }) + .closest(j.ExpressionStatement); + if (valuesStmt.size() !== 1) { + throw new Error( + `transform-${flavor}: could not locate this._values assignment in run body`, + ); + } + const proxyDecl = j(globalProxyDecl) + .find(j.VariableDeclaration) + .get(0).node; + valuesStmt.insertBefore(proxyDecl); + + let output = root.toSource({ + lineTerminator: "\n", + useTabs: true, + reuseWhitespace: true, + }); + output = restoreBlockComments(output, preserved); + output = normalizeIndent(output); + output = matchTrailingNewline(rawSource, output); + return output; +} From ce9de4110077eef05d2e4fcf16a945e75a40cd48 Mon Sep 17 00:00:00 2001 From: syumai Date: Mon, 4 May 2026 17:57:30 +0900 Subject: [PATCH 3/3] scripts: align TinyGo globalProxy with Go's Reflect.get form Use Reflect.get(target, prop, target) in the TinyGo globalProxy decl, matching the Go side. Now that both flavors share the same proxy body (only the global name differs), the literal moves into transform-wasm-exec behind a buildGlobalProxyDecl(globalName) helper, so the two wrappers no longer carry their own copies. Regenerated wasm_exec_tinygo.js reflects the one-line behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../assets/wasm_exec_tinygo.js | 2 +- scripts/gen-wasm-exec/src/transform-go.ts | 10 ---------- scripts/gen-wasm-exec/src/transform-tinygo.ts | 10 ---------- .../gen-wasm-exec/src/transform-wasm-exec.ts | 18 +++++++++++++----- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/cmd/workers-assets-gen/assets/wasm_exec_tinygo.js b/cmd/workers-assets-gen/assets/wasm_exec_tinygo.js index cb48774b..cc98c04f 100644 --- a/cmd/workers-assets-gen/assets/wasm_exec_tinygo.js +++ b/cmd/workers-assets-gen/assets/wasm_exec_tinygo.js @@ -488,7 +488,7 @@ if (prop === 'context') { return context; } - return Reflect.get(...arguments); + return Reflect.get(target, prop, target); } }) this._values = [ // JS values that Go currently has references to, indexed by reference id diff --git a/scripts/gen-wasm-exec/src/transform-go.ts b/scripts/gen-wasm-exec/src/transform-go.ts index 16546d86..70d9f17d 100644 --- a/scripts/gen-wasm-exec/src/transform-go.ts +++ b/scripts/gen-wasm-exec/src/transform-go.ts @@ -1,19 +1,9 @@ import { transformWasmExec } from "./transform-wasm-exec.ts"; -const GLOBAL_PROXY_DECL = `const globalProxy = new Proxy(globalThis, { -\tget(target, prop) { -\t\tif (prop === 'context') { -\t\t\treturn context; -\t\t} -\t\treturn Reflect.get(target, prop, target); -\t} -})`; - export function transformGo(rawSource: string): string { return transformWasmExec(rawSource, { flavor: "go", globalName: "globalThis", expectedStandaloneGlobals: 2, - globalProxyDecl: GLOBAL_PROXY_DECL, }); } diff --git a/scripts/gen-wasm-exec/src/transform-tinygo.ts b/scripts/gen-wasm-exec/src/transform-tinygo.ts index 05134943..46b3fa00 100644 --- a/scripts/gen-wasm-exec/src/transform-tinygo.ts +++ b/scripts/gen-wasm-exec/src/transform-tinygo.ts @@ -57,20 +57,10 @@ function commentOutNodejsBlocks(rawSource: string): string { return result; } -const GLOBAL_PROXY_DECL = `const globalProxy = new Proxy(global, { -\tget(target, prop) { -\t\tif (prop === 'context') { -\t\t\treturn context; -\t\t} -\t\treturn Reflect.get(...arguments); -\t} -})`; - export function transformTinygo(rawSource: string): string { return transformWasmExec(commentOutNodejsBlocks(rawSource), { flavor: "tinygo", globalName: "global", expectedStandaloneGlobals: 1, - globalProxyDecl: GLOBAL_PROXY_DECL, }); } diff --git a/scripts/gen-wasm-exec/src/transform-wasm-exec.ts b/scripts/gen-wasm-exec/src/transform-wasm-exec.ts index a800d5ae..e4585086 100644 --- a/scripts/gen-wasm-exec/src/transform-wasm-exec.ts +++ b/scripts/gen-wasm-exec/src/transform-wasm-exec.ts @@ -16,17 +16,25 @@ export interface WasmExecTransformConfig { // expected inside the run() body. Go has 2 (Object.assign target plus the // _values array entry); TinyGo has 1 (the _values array entry only). expectedStandaloneGlobals: number; - // Literal source of the `const globalProxy = new Proxy(...)` declaration - // to inject. The two flavors use slightly different bodies. - globalProxyDecl: string; +} + +function buildGlobalProxyDecl(globalName: string): string { + return `const globalProxy = new Proxy(${globalName}, { +\tget(target, prop) { +\t\tif (prop === 'context') { +\t\t\treturn context; +\t\t} +\t\treturn Reflect.get(target, prop, target); +\t} +})`; } export function transformWasmExec( rawSource: string, config: WasmExecTransformConfig, ): string { - const { flavor, globalName, expectedStandaloneGlobals, globalProxyDecl } = - config; + const { flavor, globalName, expectedStandaloneGlobals } = config; + const globalProxyDecl = buildGlobalProxyDecl(globalName); const { source, preserved } = preserveBlockComments(rawSource); const j = jscodeshift.withParser("babel"); const root = j(source);