From 2386c78c4e2b1cb59539a2cc389f733516722077 Mon Sep 17 00:00:00 2001 From: Gordon Smith Date: Tue, 17 Mar 2026 09:31:18 +0000 Subject: [PATCH] feat: switch to component model for wit-bindgen-wasm Time to eat our own dog food Signed-off-by: Gordon Smith --- .github/workflows/ci.yml | 6 + .vscodeignore | 26 +- README.md | 2 +- package-lock.json | 508 ++++++--------- package.json | 33 +- scripts/build-wasm.sh | 79 +++ scripts/setup-wasm.sh | 44 +- scripts/update-wasm-tools-version.sh | 42 ++ src/wasmUtils.ts | 188 ++---- tests/bindings-generation.test.ts | 28 +- tests/wasmUtils.test.ts | 180 ++++-- wit-bindgen-wasm/Cargo.lock | 127 +--- wit-bindgen-wasm/Cargo.toml | 21 +- wit-bindgen-wasm/README.md | 8 +- wit-bindgen-wasm/build.rs | 40 +- wit-bindgen-wasm/create_component.sh | 57 -- wit-bindgen-wasm/src/lib.rs | 896 ++++++++++++--------------- wit-bindgen-wasm/wit/wit-bindgen.wit | 75 +-- 18 files changed, 1033 insertions(+), 1327 deletions(-) create mode 100755 scripts/build-wasm.sh create mode 100755 scripts/update-wasm-tools-version.sh delete mode 100644 wit-bindgen-wasm/create_component.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 861d1e3..e957106 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,6 +15,9 @@ jobs: os: [ubuntu-latest, windows-latest, macos-latest] node-version: [22, 24] runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash permissions: contents: read issues: write @@ -41,6 +44,9 @@ jobs: - name: Install dependencies run: npm ci + - name: Setup WASM build tools + run: npm run setup-wasm + - name: Run tests run: npm test diff --git a/.vscodeignore b/.vscodeignore index 4e9a0fb..381ee63 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -1,14 +1,12 @@ -.vscode/** -.vscode-test/** -.github/ -.gitignore -node_modules/ -tests/ -commitlint.config.cjs -src/** -.yarnrc -**/tsconfig.json -**/.eslintrc.json -**/*.map -**/*.ts -wit-bindgen-wasm/** +# Exclude everything by default +** + +# Include only what the extension needs at runtime +!dist/** +!syntaxes/** +!images/wit-icon.png +!snippets.json +!language-configuration.json +!LICENSE +!README.md +!CHANGELOG.md diff --git a/README.md b/README.md index 2eea1d8..8e41b21 100644 --- a/README.md +++ b/README.md @@ -238,7 +238,7 @@ This extension is available on: This extension includes a WebAssembly component that requires the following tools for building: - **Rust** (with `cargo`): Required for building the WebAssembly module -- **wasm-pack v0.13.1**: Used specifically for the `wit-bindgen-wasm` subproject +- **wasm-tools v1.245.0**: Used for embedding WIT metadata and creating WebAssembly components You can install these dependencies by running: ```bash diff --git a/package-lock.json b/package-lock.json index abcb131..31fd0ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,8 @@ "name": "wit-idl", "version": "0.3.28", "license": "Apache-2.0 WITH LLVM-exception", - "dependencies": { - "@bytecodealliance/jco": "1.17.0", - "wit-bindgen-wasm": "file:wit-bindgen-wasm/pkg" - }, "devDependencies": { + "@bytecodealliance/jco": "1.17.0", "@eslint/css": "1.0.0", "@eslint/js": "10.0.1", "@types/node": "25.5.0", @@ -34,7 +31,7 @@ "typescript-eslint": "8.57.1", "vitest": "4.1.0", "vscode-tmgrammar-test": "0.1.3", - "wasm-pack": "0.14.0" + "wit-bindgen-wasm": "file:wit-bindgen-wasm/pkg" }, "engines": { "vscode": "^1.99.3" @@ -105,9 +102,9 @@ } }, "node_modules/@azure/core-rest-pipeline": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.2.tgz", - "integrity": "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.23.0.tgz", + "integrity": "sha512-Evs1INHo+jUjwHi1T6SG6Ua/LHOQBCLuKEEE6efIpt4ZOoNonaT1kP32GoOcdNDbfqsD2445CPri3MubBy5DEQ==", "dev": true, "license": "MIT", "dependencies": { @@ -116,7 +113,7 @@ "@azure/core-tracing": "^1.3.0", "@azure/core-util": "^1.13.0", "@azure/logger": "^1.3.0", - "@typespec/ts-http-runtime": "^0.3.0", + "@typespec/ts-http-runtime": "^0.3.4", "tslib": "^2.6.2" }, "engines": { @@ -189,22 +186,22 @@ } }, "node_modules/@azure/msal-browser": { - "version": "4.29.0", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.29.0.tgz", - "integrity": "sha512-/f3eHkSNUTl6DLQHm+bKecjBKcRQxbd/XLx8lvSYp8Nl/HRyPuIPOijt9Dt0sH50/SxOwQ62RnFCmFlGK+bR/w==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.29.1.tgz", + "integrity": "sha512-1Vrt27du1cl4QHkzLc6L4aeXqliPIDIs5l/1I4hWWMXkXccY/EznJT1+pBdoVze0azTAI8sCyq5B4cBVYG1t9w==", "dev": true, "license": "MIT", "dependencies": { - "@azure/msal-common": "15.15.0" + "@azure/msal-common": "15.16.1" }, "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-common": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.15.0.tgz", - "integrity": "sha512-/n+bN0AKlVa+AOcETkJSKj38+bvFs78BaP4rNtv3MJCmPH0YrHiskMRe74OhyZ5DZjGISlFyxqvf9/4QVEi2tw==", + "version": "15.16.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.16.1.tgz", + "integrity": "sha512-qxUG9TCl+TVSSX58onVDHDWrvT5CE0+NeeUAbkQqaESpSm79u5IePLnPWMMjCUnUR2zJd4+Bt9vioVRzLmJb2g==", "dev": true, "license": "MIT", "engines": { @@ -212,13 +209,13 @@ } }, "node_modules/@azure/msal-node": { - "version": "3.8.8", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.8.8.tgz", - "integrity": "sha512-+f1VrJH1iI517t4zgmuhqORja0bL6LDQXfBqkjuMmfTYXTQQnh1EvwwxO3UbKLT05N0obF72SRHFrC1RBDv5Gg==", + "version": "3.8.9", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.8.9.tgz", + "integrity": "sha512-jZ0pw/BbdEUWGhomCaAiVDfXRI/9K56m5hTNqB/CzcbZEYhXm5qpK1cDngN1iXfwSfmUMorOUQ2FC0dyuQ9uRg==", "dev": true, "license": "MIT", "dependencies": { - "@azure/msal-common": "15.15.0", + "@azure/msal-common": "15.16.1", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, @@ -241,6 +238,13 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -262,9 +266,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "dev": true, "license": "MIT", "dependencies": { @@ -305,6 +309,7 @@ "version": "0.19.3", "resolved": "https://registry.npmjs.org/@bytecodealliance/componentize-js/-/componentize-js-0.19.3.tgz", "integrity": "sha512-ju7Y4WeF0B9uMkSPHJgmT6ouEfSwbe9M1uR/YOnYZjBpxJjH9qzxIkJg/kf8NycVDyFJ2/lscmJ1E1uPiDQVRQ==", + "dev": true, "workspaces": [ "." ], @@ -322,6 +327,7 @@ "version": "1.17.0", "resolved": "https://registry.npmjs.org/@bytecodealliance/jco/-/jco-1.17.0.tgz", "integrity": "sha512-+8cLL6p++K+KKJiG+xqRDyKcjoWvAB1cwH+diIvuUf8O0AEN+QfzW7GdZJ0zzvfpCfGYX+uO7ylmPFvn+pWxCA==", + "dev": true, "license": "(Apache-2.0 WITH LLVM-exception)", "dependencies": { "@bytecodealliance/componentize-js": "^0.19.3", @@ -336,40 +342,18 @@ "jco": "src/jco.js" } }, - "node_modules/@bytecodealliance/jco/node_modules/commander": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", - "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/@bytecodealliance/jco/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@bytecodealliance/preview2-shim": { "version": "0.17.8", "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.17.8.tgz", "integrity": "sha512-wS5kg8u0KCML1UeHQPJ1IuOI24x/XLentCzsqPER1+gDNC5Cz2hG4G2blLOZap+3CEGhIhnJ9mmZYj6a2W0Lww==", + "dev": true, "license": "(Apache-2.0 WITH LLVM-exception)" }, "node_modules/@bytecodealliance/wizer": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer/-/wizer-10.0.0.tgz", "integrity": "sha512-ziWmovyu1jQl9TsKlfC2bwuUZwxVPFHlX4fOqTzxhgS76jITIo45nzODEwPgU+jjmOr8F3YX2V2wAChC5NKujg==", + "dev": true, "license": "Apache-2.0", "bin": { "wizer": "wizer.js" @@ -393,6 +377,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -409,6 +394,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -425,6 +411,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -441,6 +428,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -457,6 +445,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -473,6 +462,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -483,20 +473,22 @@ } }, "node_modules/@emnapi/core": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", - "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz", + "integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.1.0", + "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz", + "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -504,9 +496,10 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1013,13 +1006,13 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.2.tgz", - "integrity": "sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.3.tgz", + "integrity": "sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.1.0" + "@eslint/core": "^1.1.1" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" @@ -1178,6 +1171,7 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -1188,6 +1182,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1197,6 +1192,7 @@ "version": "0.3.11", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -1207,12 +1203,14 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1223,6 +1221,7 @@ "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1544,6 +1543,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1560,6 +1560,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1576,6 +1577,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1592,6 +1594,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1608,6 +1611,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1624,6 +1628,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1640,6 +1645,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1656,6 +1662,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1672,6 +1679,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1688,6 +1696,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1704,6 +1713,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1720,6 +1730,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1736,6 +1747,7 @@ "cpu": [ "wasm32" ], + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1752,6 +1764,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1768,6 +1781,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1791,6 +1805,7 @@ "version": "0.76.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.76.0.tgz", "integrity": "sha512-CH3THIrSViKal8yV/Wh3FK0pFhp40nzW1MUDCik9fNuid2D/7JJXKJnfFOAvMxInGXDlvmgT6ACAzrl47TqzkQ==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" @@ -2377,6 +2392,7 @@ "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -2684,9 +2700,9 @@ } }, "node_modules/@typespec/ts-http-runtime": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.3.tgz", - "integrity": "sha512-91fp6CAAJSRtH5ja95T1FHSKa8aPW9/Zw6cta81jlZTUw/+Vq8jM/AfF/14h2b71wwR84JUTW/3Y8QPhDAawFA==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.4.tgz", + "integrity": "sha512-CI0NhTrz4EBaa0U+HaaUZrJhPoso8sG7ZFya8uQoBA57fjzrjRSv87ekCjLZOFExN+gXE/z0xuN2QfH4H2HrLQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3074,10 +3090,20 @@ "concat-map": "0.0.1" } }, + "node_modules/@vscode/vsce/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@vscode/vsce/node_modules/minimatch": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.4.tgz", - "integrity": "sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -3091,6 +3117,7 @@ "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -3156,6 +3183,7 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -3248,13 +3276,6 @@ "js-tokens": "^10.0.0" } }, - "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -3298,16 +3319,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.14.8" - } - }, "node_modules/azure-devops-node-api": { "version": "12.5.0", "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz", @@ -3351,96 +3362,11 @@ "license": "MIT", "optional": true }, - "node_modules/binary-install": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/binary-install/-/binary-install-1.1.2.tgz", - "integrity": "sha512-ZS2cqFHPZOy4wLxvzqfQvDjCOifn+7uCPqNmYRIBM/03+yllON+4fNnsD0VJdW0p97y+E+dTRNPStWNqMBq+9g==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "license": "MIT", - "dependencies": { - "axios": "^0.26.1", - "rimraf": "^3.0.2", - "tar": "^6.1.11" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/binary-install/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/binary-install/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/binary-install/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/binary-install/node_modules/minimatch": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.4.tgz", - "integrity": "sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/binary-install/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/binaryen": { "version": "123.0.0", "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-123.0.0.tgz", "integrity": "sha512-/hls/a309aZCc0itqP6uhoR+5DsKSlJVfB8Opd2BY9Ndghs84IScTunlyidyF4r2Xe3lQttnfBNIDjaNpj6mTw==", + "dev": true, "license": "Apache-2.0", "bin": { "wasm-as": "bin/wasm-as", @@ -3505,9 +3431,9 @@ "license": "BSD-2-Clause" }, "node_modules/brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "license": "MIT", "dependencies": { @@ -3577,6 +3503,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, "license": "MIT" }, "node_modules/bundle-name": { @@ -3735,6 +3662,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, "license": "MIT", "dependencies": { "restore-cursor": "^5.0.0" @@ -3750,6 +3678,7 @@ "version": "2.9.2", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -3802,13 +3731,13 @@ } }, "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/concat-map": { @@ -4348,6 +4277,7 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { @@ -4612,9 +4542,9 @@ "license": "MIT" }, "node_modules/espree": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-11.1.1.tgz", - "integrity": "sha512-AVHPqQoZYc+RUM4/3Ly5udlZY/U4LS8pIG05jEjWM2lQMU/oaZ7qshzAl2YP1tfNmXfftH3ohurfwNAug+MnsQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4952,9 +4882,9 @@ "optional": true }, "node_modules/fs-extra": { - "version": "11.3.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz", - "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", "dev": true, "license": "MIT", "dependencies": { @@ -4966,32 +4896,6 @@ "node": ">=14.14" } }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -5069,6 +4973,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -5822,6 +5727,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -5999,6 +5905,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -6157,9 +6064,9 @@ } }, "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", "dev": true, "license": "MIT" }, @@ -6700,6 +6607,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, "license": "MIT", "dependencies": { "chalk": "^5.3.0", @@ -6716,6 +6624,7 @@ "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -6728,6 +6637,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -6902,6 +6812,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -6961,44 +6872,20 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", "dev": true, "license": "MIT", "bin": { - "mkdirp": "bin/cmd.js" + "mkdirp": "dist/cjs/src/bin.js" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/mkdirp-classic": { @@ -7075,9 +6962,9 @@ "license": "MIT" }, "node_modules/node-abi": { - "version": "3.87.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", - "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", + "version": "3.89.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz", + "integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==", "dev": true, "license": "MIT", "optional": true, @@ -7267,9 +7154,9 @@ } }, "node_modules/npm-run-all/node_modules/minimatch": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.4.tgz", - "integrity": "sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -7430,6 +7317,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, "license": "MIT", "dependencies": { "mimic-function": "^5.0.0" @@ -7482,6 +7370,7 @@ "version": "8.2.0", "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, "license": "MIT", "dependencies": { "chalk": "^5.3.0", @@ -7505,6 +7394,7 @@ "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -7517,12 +7407,14 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, "license": "MIT" }, "node_modules/ora/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^10.3.0", @@ -7591,6 +7483,7 @@ "version": "0.76.0", "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.76.0.tgz", "integrity": "sha512-l98B2e9evuhES7zN99rb1QGhbzx25829TJFaKi2j0ib3/K/G5z1FdGYz6HZkrU3U8jdH7v2FC8mX1j2l9JrOUg==", + "dev": true, "license": "MIT", "dependencies": { "@oxc-project/types": "^0.76.0" @@ -7813,9 +7706,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", - "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -8010,9 +7903,9 @@ } }, "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", "dev": true, "license": "MIT", "optional": true, @@ -8096,15 +7989,15 @@ } }, "node_modules/rc-config-loader": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.3.tgz", - "integrity": "sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.4.tgz", + "integrity": "sha512-3GiwEzklkbXTDp52UR5nT8iXgYAx1V9ZG/kDZT7p60u2GCv2XTwQq4NzinMoMpNtXhmt3WkhYXcj6HH8HdwCEQ==", "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.3.4", - "js-yaml": "^4.1.0", - "json5": "^2.2.2", + "debug": "^4.4.3", + "js-yaml": "^4.1.1", + "json5": "^2.2.3", "require-from-string": "^2.0.2" } }, @@ -8231,6 +8124,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, "license": "MIT", "dependencies": { "onetime": "^7.0.0", @@ -8457,9 +8351,9 @@ "license": "MIT" }, "node_modules/sax": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz", - "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -8759,6 +8653,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -8876,6 +8771,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -8895,6 +8791,7 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -8955,6 +8852,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -9105,12 +9003,13 @@ } }, "node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -9249,25 +9148,6 @@ "node": ">=8" } }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/tar-fs": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", @@ -9300,26 +9180,6 @@ "node": ">=6" } }, - "node_modules/tar/node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=8" - } - }, "node_modules/terminal-link": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-4.0.0.tgz", @@ -9338,9 +9198,10 @@ } }, "node_modules/terser": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", - "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", + "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -9359,6 +9220,7 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, "license": "MIT" }, "node_modules/text-table": { @@ -9509,7 +9371,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "devOptional": true, + "dev": true, "license": "0BSD" }, "node_modules/tunnel": { @@ -10166,9 +10028,9 @@ } }, "node_modules/vscode-tmgrammar-test/node_modules/minimatch": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.4.tgz", - "integrity": "sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -10191,20 +10053,6 @@ "node": ">=4" } }, - "node_modules/wasm-pack": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.14.0.tgz", - "integrity": "sha512-7uKj+483b6ETTnuWHK3zKNB3Ca3M159tPZ5shyXxI4j7i9Lk82rL2ck/L6E9O5VMWk9JgowdtTBOSfWmGBRFtw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT OR Apache-2.0", - "dependencies": { - "binary-install": "^1.1.2" - }, - "bin": { - "wasm-pack": "run.js" - } - }, "node_modules/whatwg-encoding": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", @@ -10469,9 +10317,7 @@ } }, "wit-bindgen-wasm/pkg": { - "name": "wit-bindgen-wasm", - "version": "0.1.0", - "license": "Apache-2.0 WITH LLVM-exception" + "dev": true } } } diff --git a/package.json b/package.json index 9874c5a..9922808 100644 --- a/package.json +++ b/package.json @@ -21,13 +21,13 @@ "type": "module", "main": "./dist/extension.js", "scripts": { - "clean": "rimraf out dist types && rimraf --glob \"*.vsix\"", + "clean": "rimraf out dist types coverage && rimraf --glob \"*.vsix\"", "clean-wasm": "rimraf wit-bindgen-wasm/pkg wit-bindgen-wasm/target", "clean-samples": "rimraf samples/target samples/tmp/*", "clean-deps": "rimraf node_modules package-lock.json", "clean-all": "npm run clean && npm run clean-wasm && npm run clean-samples", "clean-full": "npm run clean-all && npm run clean-deps", - "setup-wasm": "./scripts/setup-wasm.sh", + "setup-wasm": "bash scripts/setup-wasm.sh", "install-extension": "npm run package && code --install-extension wit-idl.vsix", "update-snapshot": "vscode-tmgrammar-snap --updateSnapshot --scope \"source.wit\" \"tests/grammar/integration/*\"", "test-grammar": "vscode-tmgrammar-test -c --grammar syntaxes/wit.tmLanguage.json \"tests/grammar/unit/*\"", @@ -39,40 +39,43 @@ "test-navigator-polyfill": "vitest run tests/navigator-polyfill.test.ts", "gen-types": "tsc --project tsconfig.json --emitDeclarationOnly", "gen-watch": "npm run gen-types -- -w", - "build-wasm-prod": "cd wit-bindgen-wasm && wasm-pack build --target web --out-dir pkg --release", - "build-wasm": "cd wit-bindgen-wasm && wasm-pack build --target web --out-dir pkg", + "build-wasm-prod": "bash scripts/build-wasm.sh --release", + "build-wasm": "bash scripts/build-wasm.sh", "build-extension-prod": "node esbuild.mts -- --production", "build-extension": "node esbuild.mts", "build-extension-watch": "node esbuild.mts --watch", - "build-samples": "cd samples && ./build.sh", + "build-samples": "cd samples && bash build.sh", "build": "run-s build-wasm build-extension", "build-with-samples": "run-s build build-samples", "vscode:prepublish": "run-s build-wasm-prod build-extension-prod", "package": "npx -y @vscode/vsce package -o wit-idl.vsix", - "fmt": "prettier . --write", - "fmt-check": "prettier --check .", + "fmt-ts": "prettier . --write", + "fmt-rust": "cargo fmt --manifest-path wit-bindgen-wasm/Cargo.toml", + "fmt": "run-s fmt-ts fmt-rust", + "fmt-check-ts": "prettier --check .", + "fmt-check-rust": "cargo fmt --manifest-path wit-bindgen-wasm/Cargo.toml -- --check", + "fmt-check": "run-s fmt-check-ts fmt-check-rust", "lint": "eslint src --ext ts ./*.m?s", "lint-fix": "eslint src --ext ts ./*.m?s --fix", "test": "run-s lint fmt-check build package gen-types test-grammar test-unit", "test-wasm": "cd wit-bindgen-wasm && cargo test", "check-wasm": "cd wit-bindgen-wasm && cargo check", - "verify-wasm": "node -e \"console.log('Verifying WASM build...'); const fs = require('fs'); const path = 'wit-bindgen-wasm/pkg/wit_bindgen_wasm_bg.wasm'; if (fs.existsSync(path)) { console.log('✅ WASM file exists:', path); const size = Math.round(fs.statSync(path).size / 1024); console.log('📏 Size:', size, 'KB'); try { const fd = fs.openSync(path, 'r'); const header = Buffer.alloc(8); fs.readSync(fd, header, 0, 8, 0); fs.closeSync(fd); const magicOk = header[0] === 0x00 && header[1] === 0x61 && header[2] === 0x73 && header[3] === 0x6D; if (!magicOk) { console.warn('⚠️ Not a WebAssembly binary (bad magic).'); process.exit(1); } const version = header.readUInt32LE(4); if (version === 1) { console.log('🧩 Type: Core WebAssembly module (vanilla wasm)'); } else if (version === 0x0A) { console.log('🧩 Type: WebAssembly component'); } else { console.log('🧩 Type: Unknown/other (version ' + version + ')'); } } catch (e) { console.error('❌ Error reading header:', e.message); process.exit(1); } } else { console.error('❌ WASM file not found:', path); process.exit(1); }\"", + "verify-wasm": "node -e \"console.log('Verifying WASM build...'); const fs = require('fs'); const path = 'wit-bindgen-wasm/pkg/wit_bindgen_wasm.core.wasm'; if (fs.existsSync(path)) { console.log('✅ WASM file exists:', path); const size = Math.round(fs.statSync(path).size / 1024); console.log('📏 Size:', size, 'KB'); try { const fd = fs.openSync(path, 'r'); const header = Buffer.alloc(8); fs.readSync(fd, header, 0, 8, 0); fs.closeSync(fd); const magicOk = header[0] === 0x00 && header[1] === 0x61 && header[2] === 0x73 && header[3] === 0x6D; if (!magicOk) { console.warn('⚠️ Not a WebAssembly binary (bad magic).'); process.exit(1); } const version = header.readUInt32LE(4); if (version === 1) { console.log('🧩 Type: Core WebAssembly module'); } else if (version === 0x0A) { console.log('🧩 Type: WebAssembly component'); } else { console.log('🧩 Type: Unknown/other (version ' + version + ')'); } console.log('✅ WASM verification passed'); } catch (e) { console.error('❌ Error reading header:', e.message); process.exit(1); } } else { console.error('❌ WASM file not found:', path); process.exit(1); }\"", "dev": "run-p gen-watch build-extension-watch", "publish": "npx -y @vscode/vsce publish", "publish-ovsx": "ovsx publish", "release": "npx release-please release-pr --repo-url bytecodealliance/vscode-wit --config-file .github/release-please-config.json --manifest-file .github/release-please-manifest.json --token", "update-npm": "npx npm-check-updates -u -t minor", "update-npm-major": "npx npm-check-updates -u", - "update-cargo": "cd wit-bindgen-wasm && cargo update && (command -v cargo-upgrade >/dev/null 2>&1 || cargo install cargo-edit) && cargo upgrade", - "update-cargo-major": "cd wit-bindgen-wasm && cargo update && (command -v cargo-upgrade >/dev/null 2>&1 || cargo install cargo-edit) && cargo upgrade --incompatible", + "update-cargo": "cd wit-bindgen-wasm && cargo update && (command -v cargo-upgrade >/dev/null 2>&1 || cargo install cargo-edit) && cargo upgrade && npm run update-wasm-tools", + "update-cargo-major": "cd wit-bindgen-wasm && cargo update && (command -v cargo-upgrade >/dev/null 2>&1 || cargo install cargo-edit) && cargo upgrade --incompatible && npm run update-wasm-tools", + "update-wasm-tools": "bash scripts/update-wasm-tools-version.sh", "update": "run-s update-npm update-cargo", "update-major": "run-s update-npm-major update-cargo-major" }, - "dependencies": { - "@bytecodealliance/jco": "1.17.0", - "wit-bindgen-wasm": "file:wit-bindgen-wasm/pkg" - }, + "dependencies": {}, "devDependencies": { + "@bytecodealliance/jco": "1.17.0", "@eslint/css": "1.0.0", "@eslint/js": "10.0.1", "@types/node": "25.5.0", @@ -94,7 +97,7 @@ "typescript-eslint": "8.57.1", "vitest": "4.1.0", "vscode-tmgrammar-test": "0.1.3", - "wasm-pack": "0.14.0" + "wit-bindgen-wasm": "file:wit-bindgen-wasm/pkg" }, "contributes": { "colors": [ diff --git a/scripts/build-wasm.sh b/scripts/build-wasm.sh new file mode 100755 index 0000000..46b75c3 --- /dev/null +++ b/scripts/build-wasm.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +# Build WebAssembly component from Rust source +# Uses: cargo (Rust), wasm-tools (component model), jco (JS transpilation) + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +WASM_DIR="$PROJECT_DIR/wit-bindgen-wasm" +PKG_DIR="$WASM_DIR/pkg" +TARGET_DIR="$WASM_DIR/target" + +# Parse arguments +RELEASE_FLAG="" +CARGO_PROFILE="debug" +if [[ "$1" == "--release" ]]; then + RELEASE_FLAG="--release" + CARGO_PROFILE="release" +fi + +echo "🔨 Building WebAssembly component..." + +# Step 1: Build the Rust code to a core WASM module +echo "📦 Step 1/4: Compiling Rust to WebAssembly..." +cd "$WASM_DIR" +cargo build --target wasm32-unknown-unknown $RELEASE_FLAG + +CORE_WASM="$TARGET_DIR/wasm32-unknown-unknown/$CARGO_PROFILE/wit_bindgen_wasm.wasm" +if [ ! -f "$CORE_WASM" ]; then + echo "❌ Core WASM not found at: $CORE_WASM" + exit 1 +fi +echo " Core module: $(du -h "$CORE_WASM" | cut -f1)" + +# Step 2: Embed WIT metadata into the core module +echo "🔗 Step 2/4: Embedding WIT component type information..." +EMBEDDED_WASM="$TARGET_DIR/wit_bindgen_wasm.embedded.wasm" +wasm-tools component embed wit/ "$CORE_WASM" --world wit-bindgen-wasm -o "$EMBEDDED_WASM" + +# Step 3: Create the WebAssembly component +echo "🧩 Step 3/4: Creating WebAssembly component..." +COMPONENT_WASM="$TARGET_DIR/wit_bindgen_wasm.component.wasm" +wasm-tools component new "$EMBEDDED_WASM" -o "$COMPONENT_WASM" +echo " Component: $(du -h "$COMPONENT_WASM" | cut -f1)" + +# Step 4: Transpile to JavaScript using jco +echo "🔄 Step 4/4: Transpiling component to JavaScript..." +mkdir -p "$PKG_DIR/interfaces" + +# Clean old generated files (preserve package.json) +find "$PKG_DIR" -name "*.js" -o -name "*.d.ts" -o -name "*.wasm" | xargs rm -f 2>/dev/null || true +rm -rf "$PKG_DIR/interfaces/" + +npx jco transpile "$COMPONENT_WASM" -o "$PKG_DIR" --name wit_bindgen_wasm + +# Ensure package.json exists for npm resolution +cat > "$PKG_DIR/package.json" << 'PKGJSON' +{ + "name": "wit-bindgen-wasm", + "type": "module", + "description": "WebAssembly component wrapper for wit-bindgen", + "version": "0.1.0", + "license": "Apache-2.0 WITH LLVM-exception", + "files": [ + "wit_bindgen_wasm.core.wasm", + "wit_bindgen_wasm.js", + "wit_bindgen_wasm.d.ts", + "interfaces/" + ], + "main": "wit_bindgen_wasm.js", + "types": "wit_bindgen_wasm.d.ts" +} +PKGJSON + +echo "" +echo "✅ WebAssembly component build complete!" +echo " Output: $PKG_DIR/" +ls -la "$PKG_DIR/" diff --git a/scripts/setup-wasm.sh b/scripts/setup-wasm.sh index 6d4cddb..68a2918 100755 --- a/scripts/setup-wasm.sh +++ b/scripts/setup-wasm.sh @@ -1,10 +1,10 @@ #!/bin/bash -# Setup script for WebAssembly build tools +# Setup script for WebAssembly Component Model build tools set -e -echo "Setting up WebAssembly build tools..." +echo "Setting up WebAssembly Component Model build tools..." # Check if Rust is installed if ! command -v cargo &> /dev/null; then @@ -16,27 +16,37 @@ fi echo "✅ Rust is installed: $(cargo --version)" -# Check if wasm-pack is installed -if ! command -v wasm-pack &> /dev/null; then - echo "📦 Installing wasm-pack v0.13.1..." - cargo install wasm-pack@0.13.1 -else - echo "✅ wasm-pack is already installed: $(wasm-pack --version)" - # Check if the version is compatible (0.13.x) - WASM_PACK_VERSION=$(wasm-pack --version | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') - if [[ ! "$WASM_PACK_VERSION" =~ ^0\.13\. ]]; then - echo "⚠️ Warning: wasm-pack version $WASM_PACK_VERSION detected. This project is tested with v0.13.1" - echo " Consider running: cargo install wasm-pack@0.13.1" +# Check if correct version of wasm-tools is installed +WASM_TOOLS_VERSION="1.245.0" +if command -v wasm-tools &> /dev/null; then + INSTALLED_VERSION=$(wasm-tools --version | awk '{print $2}') + if [[ "$INSTALLED_VERSION" == "$WASM_TOOLS_VERSION" ]]; then + echo "✅ wasm-tools is installed: $(wasm-tools --version)" + else + echo "📦 Updating wasm-tools from $INSTALLED_VERSION to $WASM_TOOLS_VERSION..." + cargo install wasm-tools@"$WASM_TOOLS_VERSION" fi +else + echo "📦 Installing wasm-tools $WASM_TOOLS_VERSION..." + cargo install wasm-tools@"$WASM_TOOLS_VERSION" fi # Add wasm32 target if not present echo "🎯 Adding wasm32-unknown-unknown target..." rustup target add wasm32-unknown-unknown -echo "✅ WebAssembly build tools setup complete!" +# Check if jco is available via npx +echo "🔍 Checking jco availability..." +if npx jco --version &> /dev/null; then + echo "✅ jco is available: $(npx jco --version)" +else + echo "⚠️ jco not found. Install it with: npm install @bytecodealliance/jco" + echo " (It should be in the project's devDependencies)" +fi + +echo "" +echo "✅ WebAssembly Component Model build tools setup complete!" echo "" echo "You can now run:" -echo " npm run build-wasm # Build WASM in release mode" -echo " npm run build-wasm-dev # Build WASM in development mode" -echo " npm run build # Build the entire extension" +echo " npm run build-wasm # Build WASM component" +echo " npm run build # Build the entire extension" diff --git a/scripts/update-wasm-tools-version.sh b/scripts/update-wasm-tools-version.sh new file mode 100755 index 0000000..c71feec --- /dev/null +++ b/scripts/update-wasm-tools-version.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# Syncs the wasm-tools CLI version with the wit-component crate version in Cargo.toml, +# installs the matching wasm-tools version, and updates setup-wasm.sh. + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +CARGO_TOML="$PROJECT_DIR/wit-bindgen-wasm/Cargo.toml" +SETUP_SCRIPT="$SCRIPT_DIR/setup-wasm.sh" + +# Extract wit-component version from Cargo.toml (e.g., "0.245") +set +e +WIT_COMPONENT_VERSION=$(grep -E 'wit-component\s*=\s*"[^"]+"' "$CARGO_TOML" | sed -E 's/.*"([^"]+)".*/\1/') +set -e + +if [ -z "$WIT_COMPONENT_VERSION" ]; then + echo "❌ Could not find wit-component version in $CARGO_TOML" + exit 1 +fi + +# Convert crate version 0.X to CLI version 1.X (e.g., 0.245 -> 1.245.0) +MINOR=$(echo "$WIT_COMPONENT_VERSION" | sed 's/^0\.//') +WASM_TOOLS_VERSION="1.${MINOR}.0" + +echo "📦 wit-component crate version: $WIT_COMPONENT_VERSION" +echo "🔧 Matching wasm-tools CLI version: $WASM_TOOLS_VERSION" + +# Install the matching wasm-tools version +echo "📥 Installing wasm-tools $WASM_TOOLS_VERSION..." +cargo install "wasm-tools@${WASM_TOOLS_VERSION}" + +# Update the pinned version in setup-wasm.sh using a portable approach +if command -v perl &> /dev/null; then + perl -i -pe "s/WASM_TOOLS_VERSION=\"[^\"]*\"/WASM_TOOLS_VERSION=\"${WASM_TOOLS_VERSION}\"/" "$SETUP_SCRIPT" +else + # Fallback: use temp file for maximum portability + sed "s/WASM_TOOLS_VERSION=\"[^\"]*\"/WASM_TOOLS_VERSION=\"${WASM_TOOLS_VERSION}\"/" "$SETUP_SCRIPT" > "$SETUP_SCRIPT.tmp" + mv "$SETUP_SCRIPT.tmp" "$SETUP_SCRIPT" +fi +echo "✅ Updated WASM_TOOLS_VERSION in setup-wasm.sh to $WASM_TOOLS_VERSION" diff --git a/src/wasmUtils.ts b/src/wasmUtils.ts index b0baf14..88ceae4 100644 --- a/src/wasmUtils.ts +++ b/src/wasmUtils.ts @@ -1,71 +1,51 @@ -import * as vscode from "vscode"; - -// Import the WASM module types -import type { WitBindgen } from "wit-bindgen-wasm"; - /** - * WASM module instance for WIT bindgen functionality + * Cached reference to the witValidator interface from the WASM component module. + * The jco-transpiled module self-initializes via top-level await, + * so the module is ready to use once the dynamic import resolves. */ -let wasmModule: typeof import("wit-bindgen-wasm") | null = null; +let witValidatorApi: typeof import("wit-bindgen-wasm").witValidator | null = null; /** - * Initialize the WASM module + * Initialize the WASM module by dynamically importing it. + * The jco-transpiled component handles WASM loading internally. * @returns Promise that resolves when the module is loaded */ export async function initializeWasm(): Promise { - if (wasmModule) { + if (witValidatorApi) { return; // Already initialized } try { - // Import the WASM module const module = await import("wit-bindgen-wasm"); - - // Initialize the WASM module with the binary - const ext = vscode.extensions.getExtension("bytecodealliance.wit-idl"); - if (!ext) { - throw new Error("Extension path not found"); - } - - // Look for the WASM file in the extension's dist directory (bundled location) - const wasmUri = vscode.Uri.joinPath(ext.extensionUri, "dist", "wit_bindgen_wasm_bg.wasm"); - - try { - // Check if the file exists; stat will throw if it doesn't - await vscode.workspace.fs.stat(wasmUri); - const wasmBuffer = await vscode.workspace.fs.readFile(wasmUri); - await module.default(wasmBuffer); - } catch { - // Fallback: try to initialize without explicit path - await module.default(); - } - - wasmModule = module; - console.log("WIT bindgen WASM module initialized successfully"); + witValidatorApi = module.witValidator; + console.log("WIT bindgen WASM component module initialized successfully"); } catch (error) { - console.error("Failed to initialize WIT bindgen WASM module:", error); + console.error("Failed to initialize WIT bindgen WASM component module:", error); throw error; } } /** - * Get the WIT bindgen version from the WASM module - * @returns Promise that resolves to the version string + * Get the witValidator API, initializing if needed. */ -export async function getWitBindgenVersionFromWasm(): Promise { - if (!wasmModule) { +async function getApi(): Promise { + if (!witValidatorApi) { await initializeWasm(); } - - if (!wasmModule) { + if (!witValidatorApi) { throw new Error("WASM module not initialized"); } + return witValidatorApi; +} +/** + * Get the WIT bindgen version from the WASM module + * @returns Promise that resolves to the version string + */ +export async function getWitBindgenVersionFromWasm(): Promise { + const api = await getApi(); try { - const instance = new wasmModule.WitBindgen(); - const version = instance.version(); - instance.free(); - return version; + return api.version(); } catch (error) { console.error("Failed to get version from WASM module:", error); throw new Error(`Failed to get version: ${error instanceof Error ? error.message : String(error)}`, { @@ -74,22 +54,6 @@ export async function getWitBindgenVersionFromWasm(): Promise { } } -/** - * Create a WIT bindgen instance for validation - * @returns Promise that resolves to a WitBindgen instance - */ -export async function createWitBindgenInstance(): Promise { - if (!wasmModule) { - await initializeWasm(); - } - - if (!wasmModule) { - throw new Error("WASM module not initialized"); - } - - return new wasmModule.WitBindgen(); -} - /** * Check if a filename has a .wit extension * @param filename - The filename to check @@ -105,50 +69,22 @@ export async function isWitFileExtensionFromWasm(filename: string): Promise { - const instance = await createWitBindgenInstance(); - try { - return instance.validateWitSyntax(content); - } finally { - instance.free(); - } + const api = await getApi(); + return api.validateWitSyntax(content); } /** * Extract WIT text from a WebAssembly component using the WASM module. - * The underlying WASM must implement `extractWitFromComponent(bytes: Uint8Array): string`. * @param bytes - The WebAssembly component bytes * @returns Promise resolving to extracted WIT text */ export async function extractWitFromComponent(bytes: Uint8Array): Promise { - if (!wasmModule) { - await initializeWasm(); - } - - if (!wasmModule) { - throw new Error("WASM module not initialized"); - } - - // Type guard for extractWitFromComponent method - function hasExtractWitFromComponent( - obj: unknown - ): obj is { extractWitFromComponent: (data: Uint8Array) => string } { - return typeof (obj as { extractWitFromComponent?: unknown }).extractWitFromComponent === "function"; - } - - // Create instance and call the optional method via a type-guarded access - const instance = new wasmModule.WitBindgen(); - try { - if (!hasExtractWitFromComponent(instance)) { - throw new Error("extractWitFromComponent not available in wit-bindgen-wasm module"); - } - const witText = instance.extractWitFromComponent(bytes); - if (!witText || typeof witText !== "string") { - throw new Error("WIT extraction returned no data"); - } - return witText; - } finally { - instance.free(); + const api = await getApi(); + const witText = api.extractWitFromComponent(bytes); + if (!witText) { + throw new Error("WIT extraction returned no data"); } + return witText; } /** @@ -156,34 +92,14 @@ export async function extractWitFromComponent(bytes: Uint8Array): Promise binary bytes map. Empty map on failure or none found. */ export async function extractCoreWasmFromComponent(bytes: Uint8Array): Promise> { - if (!wasmModule) { - await initializeWasm(); - } - if (!wasmModule) { - throw new Error("WASM module not initialized"); - } - - function hasExtractCore(obj: unknown): obj is { extractCoreWasmFromComponent: (data: Uint8Array) => string } { - return typeof (obj as { extractCoreWasmFromComponent?: unknown }).extractCoreWasmFromComponent === "function"; - } - - const instance = new wasmModule.WitBindgen(); - try { - if (!hasExtractCore(instance)) { - throw new Error("extractCoreWasmFromComponent not available in wit-bindgen-wasm module"); - } - const json = instance.extractCoreWasmFromComponent(bytes); - const textMap = JSON.parse(json || "{}") as Record; - // Decode into binary bytes (latin1-to-bytes convention from WASM side) - const result: Record = {}; - for (const [name, content] of Object.entries(textMap)) { - // Buffer.from with 'latin1' yields the original byte values 0..255 - result[name] = Buffer.from(content, "latin1"); - } - return result; - } finally { - instance.free(); - } + const api = await getApi(); + const json = api.extractCoreWasmFromComponent(bytes); + const textMap = JSON.parse(json || "{}") as Record; + const result: Record = {}; + for (const [name, content] of Object.entries(textMap)) { + result[name] = Buffer.from(content, "latin1"); + } + return result; } /** @@ -192,12 +108,8 @@ export async function extractCoreWasmFromComponent(bytes: Uint8Array): Promise { - const instance = await createWitBindgenInstance(); - try { - return instance.extractInterfaces(content); - } finally { - instance.free(); - } + const api = await getApi(); + return api.extractInterfaces(content); } /** @@ -212,13 +124,9 @@ export async function generateBindingsFromWasm( language: string, worldName?: string ): Promise> { - const instance = await createWitBindgenInstance(); - try { - const jsonResult = instance.generateBindings(content, language, worldName); - return JSON.parse(jsonResult); - } finally { - instance.free(); - } + const api = await getApi(); + const jsonResult = api.generateBindings(content, language, worldName); + return JSON.parse(jsonResult); } /** @@ -236,11 +144,7 @@ export interface WitValidationResult { * @returns Promise that resolves to detailed validation results */ export async function validateWitSyntaxDetailedFromWasm(content: string): Promise { - const instance = await createWitBindgenInstance(); - try { - const resultJson = instance.validateWitSyntaxDetailed(content); - return JSON.parse(resultJson); - } finally { - instance.free(); - } + const api = await getApi(); + const resultJson = api.validateWitSyntaxDetailed(content); + return JSON.parse(resultJson); } diff --git a/tests/bindings-generation.test.ts b/tests/bindings-generation.test.ts index 8bfeaf2..f86b369 100644 --- a/tests/bindings-generation.test.ts +++ b/tests/bindings-generation.test.ts @@ -1,12 +1,7 @@ import { describe, it, expect, beforeAll } from "vitest"; -import { readFileSync } from "fs"; -import path from "path"; -import { fileURLToPath } from "url"; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); - -// Import the WASM module directly -import init, { WitBindgen } from "../wit-bindgen-wasm/pkg/wit_bindgen_wasm.js"; +// Import the jco-transpiled WASM component module directly +import { witValidator } from "../wit-bindgen-wasm/pkg/wit_bindgen_wasm.js"; const TEST_WIT = `package example:test; @@ -16,16 +11,9 @@ world test-world { `; describe("Bindings Generation for All Languages", () => { - let witBindgen: WitBindgen; - beforeAll(async () => { - // Initialize WASM module using the built file - const wasmPath = path.join(__dirname, "../wit-bindgen-wasm/pkg/wit_bindgen_wasm_bg.wasm"); - const wasmBuffer = readFileSync(wasmPath); - await init(wasmBuffer); - - // Create WitBindgen instance - witBindgen = new WitBindgen(); + // The jco-transpiled module self-initializes via top-level await. + // By the time the import resolves, the WASM is ready. }); const supportedLanguages = [ @@ -40,7 +28,7 @@ describe("Bindings Generation for All Languages", () => { supportedLanguages.forEach(({ lang, extension, expectedContent, minLength }) => { it(`should generate actual code stubs for ${lang}`, () => { - const resultJson = witBindgen.generateBindings(TEST_WIT, lang, undefined); + const resultJson = witValidator.generateBindings(TEST_WIT, lang, undefined); const result = JSON.parse(resultJson); // Verify files were generated @@ -78,7 +66,7 @@ describe("Bindings Generation for All Languages", () => { }); it(`should not generate only README files for ${lang}`, () => { - const resultJson = witBindgen.generateBindings(TEST_WIT, lang, undefined); + const resultJson = witValidator.generateBindings(TEST_WIT, lang, undefined); const result = JSON.parse(resultJson); // Verify that not all files are README files @@ -102,7 +90,7 @@ describe("Bindings Generation for All Languages", () => { const results: Record> = {}; for (const { lang } of supportedLanguages) { - const resultJson = witBindgen.generateBindings(TEST_WIT, lang, undefined); + const resultJson = witValidator.generateBindings(TEST_WIT, lang, undefined); results[lang] = JSON.parse(resultJson); } @@ -161,7 +149,7 @@ world app { `; for (const { lang, extension } of supportedLanguages) { - const resultJson = witBindgen.generateBindings(complexWit, lang, undefined); + const resultJson = witValidator.generateBindings(complexWit, lang, undefined); const result = JSON.parse(resultJson); expect(Object.keys(result).length).toBeGreaterThan(0); diff --git a/tests/wasmUtils.test.ts b/tests/wasmUtils.test.ts index a666371..6b47a68 100644 --- a/tests/wasmUtils.test.ts +++ b/tests/wasmUtils.test.ts @@ -1,51 +1,39 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; -// Mock vscode before importing wasmUtils -vi.mock("vscode", () => ({ - extensions: { - getExtension: vi.fn(() => ({ - extensionUri: { fsPath: "/mock/extension" }, - })), - }, - workspace: { - fs: { - stat: vi.fn(), - readFile: vi.fn(), - }, - }, - Uri: { - joinPath: vi.fn((_base: unknown, ...segments: string[]) => ({ - fsPath: `/mock/extension/${segments.join("/")}`, - })), - }, -})); - -// Mock the wit-bindgen-wasm module +// Mock the wit-bindgen-wasm module with the new jco-transpiled API vi.mock("wit-bindgen-wasm", () => { - class MockWitBindgen { - version(): string { - return "0.42.0-mock"; - } - validateWitSyntax(content: string): boolean { - return content.includes("package"); - } - validateWitSyntaxDetailed(content: string): string { - if (content.includes("package")) { - return JSON.stringify({ valid: true }); - } - return JSON.stringify({ valid: false, error: "expected package", errorType: "parsing" }); - } - extractInterfaces(): string { - return "my-interface, another-interface"; - } - generateBindings(): string { - return JSON.stringify({ "lib.rs": "// generated" }); - } - free(): void {} - } return { - default: vi.fn(), - WitBindgen: MockWitBindgen, + witValidator: { + version(): string { + return "0.42.0-mock"; + }, + validateWitSyntax(content: string): boolean { + return content.includes("package"); + }, + validateWitSyntaxDetailed(content: string): string { + if (content.includes("package")) { + return JSON.stringify({ valid: true }); + } + return JSON.stringify({ valid: false, error: "expected package", errorType: "parsing" }); + }, + extractInterfaces(): string { + return "my-interface, another-interface"; + }, + generateBindings(): string { + return JSON.stringify({ "lib.rs": "// generated" }); + }, + extractWitFromComponent: vi.fn().mockReturnValue("package test:component;"), + extractCoreWasmFromComponent: vi.fn().mockReturnValue(JSON.stringify({ "module-0.wasm": "\x00asm" })), + hasWorldDefinition(content: string): boolean { + return content.includes("world"); + }, + isWitFileExtension(filename: string): boolean { + return filename.toLowerCase().endsWith(".wit"); + }, + getWitBindgenVersion(): string { + return "0.42.0-mock"; + }, + }, }; }); @@ -53,12 +41,14 @@ import { isWitFileExtensionFromWasm, validateWitSyntaxFromWasm, getWitBindgenVersionFromWasm, - createWitBindgenInstance, extractInterfacesFromWasm, generateBindingsFromWasm, validateWitSyntaxDetailedFromWasm, + extractWitFromComponent, + extractCoreWasmFromComponent, initializeWasm, } from "../src/wasmUtils.js"; +import { witValidator } from "wit-bindgen-wasm"; describe("wasmUtils", () => { beforeEach(() => { @@ -111,14 +101,6 @@ describe("wasmUtils", () => { }); }); - describe("createWitBindgenInstance", () => { - it("should return a WitBindgen instance", async () => { - const instance = await createWitBindgenInstance(); - expect(instance).toBeDefined(); - expect(typeof instance.version).toBe("function"); - }); - }); - describe("validateWitSyntaxFromWasm", () => { it("should return true for valid WIT content", async () => { const result = await validateWitSyntaxFromWasm("package foo:bar;"); @@ -165,4 +147,96 @@ describe("wasmUtils", () => { expect(result).toBeDefined(); }); }); + + describe("extractWitFromComponent", () => { + const dummyBytes = new Uint8Array([0, 97, 115, 109]); + const mockFn = witValidator.extractWitFromComponent as ReturnType; + + beforeEach(() => { + mockFn.mockReturnValue("package test:component;"); + }); + + it("should return extracted WIT text on success", async () => { + const result = await extractWitFromComponent(dummyBytes); + expect(result).toBe("package test:component;"); + expect(mockFn).toHaveBeenCalledWith(dummyBytes); + }); + + it("should throw when API returns empty string", async () => { + mockFn.mockReturnValue(""); + await expect(extractWitFromComponent(dummyBytes)).rejects.toThrow("WIT extraction returned no data"); + }); + + it("should throw when API returns undefined", async () => { + mockFn.mockReturnValue(undefined); + await expect(extractWitFromComponent(dummyBytes)).rejects.toThrow("WIT extraction returned no data"); + }); + + it("should throw when API returns null", async () => { + mockFn.mockReturnValue(null); + await expect(extractWitFromComponent(dummyBytes)).rejects.toThrow("WIT extraction returned no data"); + }); + }); + + describe("extractCoreWasmFromComponent", () => { + const dummyBytes = new Uint8Array([0, 97, 115, 109]); + const mockFn = witValidator.extractCoreWasmFromComponent as ReturnType; + + beforeEach(() => { + mockFn.mockReturnValue(JSON.stringify({ "module-0.wasm": "\x00asm" })); + }); + + it("should return decoded binary buffers keyed by filename", async () => { + const result = await extractCoreWasmFromComponent(dummyBytes); + expect(Object.keys(result)).toEqual(["module-0.wasm"]); + expect(result["module-0.wasm"]).toBeInstanceOf(Uint8Array); + }); + + it("should correctly decode latin1-encoded content", async () => { + const latin1Str = "\x00\x01\xFF\xFE"; + mockFn.mockReturnValue(JSON.stringify({ "core.wasm": latin1Str })); + const result = await extractCoreWasmFromComponent(dummyBytes); + const buf = result["core.wasm"]; + expect(buf[0]).toBe(0x00); + expect(buf[1]).toBe(0x01); + expect(buf[2]).toBe(0xff); + expect(buf[3]).toBe(0xfe); + }); + + it("should return empty object when no modules found", async () => { + mockFn.mockReturnValue(JSON.stringify({})); + const result = await extractCoreWasmFromComponent(dummyBytes); + expect(result).toEqual({}); + }); + + it("should fall back to empty object when API returns null", async () => { + mockFn.mockReturnValue(null); + const result = await extractCoreWasmFromComponent(dummyBytes); + expect(result).toEqual({}); + }); + + it("should fall back to empty object when API returns empty string", async () => { + mockFn.mockReturnValue(""); + const result = await extractCoreWasmFromComponent(dummyBytes); + expect(result).toEqual({}); + }); + + it("should handle multiple modules", async () => { + mockFn.mockReturnValue( + JSON.stringify({ + "module-0.wasm": "abc", + "module-1.wasm": "def", + }) + ); + const result = await extractCoreWasmFromComponent(dummyBytes); + expect(Object.keys(result)).toHaveLength(2); + expect(result["module-0.wasm"]).toBeInstanceOf(Uint8Array); + expect(result["module-1.wasm"]).toBeInstanceOf(Uint8Array); + }); + + it("should throw on invalid JSON", async () => { + mockFn.mockReturnValue("not json at all"); + await expect(extractCoreWasmFromComponent(dummyBytes)).rejects.toThrow(); + }); + }); }); diff --git a/wit-bindgen-wasm/Cargo.lock b/wit-bindgen-wasm/Cargo.lock index c1badeb..ceaa790 100644 --- a/wit-bindgen-wasm/Cargo.lock +++ b/wit-bindgen-wasm/Cargo.lock @@ -14,28 +14,6 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" -[[package]] -name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -89,16 +67,6 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" -[[package]] -name = "js-sys" -version = "0.3.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - [[package]] name = "leb128fmt" version = "0.1.0" @@ -117,12 +85,6 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - [[package]] name = "prettyplease" version = "0.2.37" @@ -162,12 +124,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - [[package]] name = "semver" version = "1.0.27" @@ -293,51 +249,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "wasm-bindgen" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" -dependencies = [ - "unicode-ident", -] - [[package]] name = "wasm-encoder" version = "0.245.1" @@ -373,22 +284,21 @@ dependencies = [ "serde", ] -[[package]] -name = "web-sys" -version = "0.3.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "winnow" version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +[[package]] +name = "wit-bindgen" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb00254d5051d69730ee32580b7373592f10ad786757c372f0f2c7b61f86a2c" +dependencies = [ + "wit-bindgen-rust-macro", +] + [[package]] name = "wit-bindgen-c" version = "0.54.0" @@ -498,17 +408,30 @@ dependencies = [ "wit-component", ] +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a284e17b2bc808c72ba008f6694626fa76bcac608b3d1ed0880f9add3f558f8e" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + [[package]] name = "wit-bindgen-wasm" version = "0.1.0" dependencies = [ "anyhow", - "console_error_panic_hook", "serde_json", "toml", - "wasm-bindgen", "wasmparser", - "web-sys", + "wit-bindgen", "wit-bindgen-c", "wit-bindgen-core", "wit-bindgen-cpp", diff --git a/wit-bindgen-wasm/Cargo.toml b/wit-bindgen-wasm/Cargo.toml index 2c75f56..f0240d2 100644 --- a/wit-bindgen-wasm/Cargo.toml +++ b/wit-bindgen-wasm/Cargo.toml @@ -2,19 +2,14 @@ name = "wit-bindgen-wasm" version = "0.1.0" edition = "2021" -description = "WebAssembly wrapper for wit-bindgen" +description = "WebAssembly component wrapper for wit-bindgen" license = "Apache-2.0 WITH LLVM-exception" [lib] crate-type = ["cdylib"] -[features] -default = ["console_error_panic_hook"] -console_error_panic_hook = ["dep:console_error_panic_hook"] - [dependencies] -wasm-bindgen = "0.2.114" -console_error_panic_hook = { version = "0.1.7", optional = true } +wit-bindgen = { version = "0.54", default-features = false, features = ["macros", "realloc"] } serde_json = "1.0" wasmparser = { version = "0.245", features = ["component-model"] } wit-parser = "0.245" @@ -29,20 +24,8 @@ wit-bindgen-markdown = "0.54" wit-component = "0.245" anyhow = "1.0.102" -[dependencies.web-sys] -version = "0.3.91" -features = [ - "console", -] - [build-dependencies] toml = "1.0.6" [profile.release] -# Tell `rustc` to optimize for small code size. opt-level = "s" - -# Disable Binaryen optimization step; avoids failures on Windows CI environments -# where the bundled/expected `wasm-opt` binary may be unavailable. -[package.metadata.wasm-pack.profile.release] -wasm-opt = false diff --git a/wit-bindgen-wasm/README.md b/wit-bindgen-wasm/README.md index 843a455..2e526b3 100644 --- a/wit-bindgen-wasm/README.md +++ b/wit-bindgen-wasm/README.md @@ -14,11 +14,11 @@ A simple WebAssembly wrapper for the Rust `wit-bindgen` package, designed to pro To build the WebAssembly module: ```bash -# Install wasm-pack (specific version for consistency) -cargo install wasm-pack@0.13.1 +# Install build tools (specific versions for consistency) +npm run setup-wasm -# Build the WebAssembly package -wasm-pack build --target web --out-dir pkg +# Build the WebAssembly component +npm run build-wasm ``` ## Usage diff --git a/wit-bindgen-wasm/build.rs b/wit-bindgen-wasm/build.rs index 160d54a..267bc54 100644 --- a/wit-bindgen-wasm/build.rs +++ b/wit-bindgen-wasm/build.rs @@ -5,21 +5,19 @@ use std::path::Path; fn main() { // Tell Cargo to rerun this build script if Cargo.toml changes println!("cargo:rerun-if-changed=Cargo.toml"); - + // Read and parse Cargo.toml to extract wit-bindgen-core version let cargo_toml_path = Path::new("Cargo.toml"); match fs::read_to_string(cargo_toml_path) { - Ok(cargo_toml_content) => { - match extract_wit_bindgen_core_version(&cargo_toml_content) { - Ok(version) => { - println!("cargo:rustc-env=WIT_BINDGEN_CORE_VERSION={}", version); - } - Err(e) => { - eprintln!("Warning: Failed to parse wit-bindgen-core version: {}", e); - println!("cargo:rustc-env=WIT_BINDGEN_CORE_VERSION=unknown"); - } + Ok(cargo_toml_content) => match extract_wit_bindgen_core_version(&cargo_toml_content) { + Ok(version) => { + println!("cargo:rustc-env=WIT_BINDGEN_CORE_VERSION={}", version); } - } + Err(e) => { + eprintln!("Warning: Failed to parse wit-bindgen-core version: {}", e); + println!("cargo:rustc-env=WIT_BINDGEN_CORE_VERSION=unknown"); + } + }, Err(e) => { eprintln!("Warning: Failed to read Cargo.toml: {}", e); println!("cargo:rustc-env=WIT_BINDGEN_CORE_VERSION=unknown"); @@ -28,39 +26,43 @@ fn main() { } /// Extract wit-bindgen-core version from Cargo.toml content -fn extract_wit_bindgen_core_version(cargo_toml_content: &str) -> Result> { +fn extract_wit_bindgen_core_version( + cargo_toml_content: &str, +) -> Result> { let parsed: toml::Value = cargo_toml_content.parse()?; - + // Look in dependencies section if let Some(dependencies) = parsed.get("dependencies") { if let Some(wit_bindgen_core) = dependencies.get("wit-bindgen-core") { return extract_version_from_dependency(wit_bindgen_core); } } - + // Look in dev-dependencies section if let Some(dev_dependencies) = parsed.get("dev-dependencies") { if let Some(wit_bindgen_core) = dev_dependencies.get("wit-bindgen-core") { return extract_version_from_dependency(wit_bindgen_core); } } - + // Look in build-dependencies section if let Some(build_dependencies) = parsed.get("build-dependencies") { if let Some(wit_bindgen_core) = build_dependencies.get("wit-bindgen-core") { return extract_version_from_dependency(wit_bindgen_core); } } - + Err("wit-bindgen-core dependency not found".into()) } /// Extract version string from a TOML dependency value -fn extract_version_from_dependency(dependency: &toml::Value) -> Result> { +fn extract_version_from_dependency( + dependency: &toml::Value, +) -> Result> { match dependency { // Simple version string: wit-bindgen-core = "0.42.1" toml::Value::String(version) => Ok(version.clone()), - + // Table format: wit-bindgen-core = { version = "0.42.1", features = [...] } toml::Value::Table(table) => { if let Some(toml::Value::String(version)) = table.get("version") { @@ -69,7 +71,7 @@ fn extract_version_from_dependency(dependency: &toml::Value) -> Result Err("unsupported dependency format".into()), } } diff --git a/wit-bindgen-wasm/create_component.sh b/wit-bindgen-wasm/create_component.sh deleted file mode 100644 index 4ff060d..0000000 --- a/wit-bindgen-wasm/create_component.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash - -# WebAssembly Component Creation Workflow -# This script demonstrates how to use wasm-tools to create components - -echo "🔧 WebAssembly Component Creation Workflow" -echo "===========================================" - -echo "" -echo "📁 Current files:" -ls -la *.wit *.wasm 2>/dev/null || echo "No .wit or .wasm files in current directory" - -echo "" -echo "🏗️ Building the WebAssembly module..." -cargo build --target wasm32-unknown-unknown --release - -echo "" -echo "📦 Available WASM files:" -ls -la target/wasm32-unknown-unknown/release/*.wasm - -echo "" -echo "🔍 Inspecting the WASM module with wasm-tools..." -echo "Module info:" -wasm-tools validate target/wasm32-unknown-unknown/release/wit_bindgen_wasm.wasm -echo "✅ Module is valid" - -echo "" -echo "📝 Available WIT files:" -ls -la *.wit - -echo "" -echo "🎯 Attempting to create component (this might fail for wasm-bindgen modules)..." -echo "Step 1: Embed WIT metadata" -if wasm-tools component embed wit-bindgen.wit target/wasm32-unknown-unknown/release/wit_bindgen_wasm.wasm -o wit_bindgen_embedded.wasm 2>/dev/null; then - echo "✅ WIT metadata embedded successfully" - - echo "Step 2: Create component" - if wasm-tools component new wit_bindgen_embedded.wasm -o wit_bindgen_component.wasm 2>/dev/null; then - echo "✅ Component created successfully!" - echo "Component size: $(wc -c < wit_bindgen_component.wasm) bytes" - else - echo "❌ Component creation failed (expected for wasm-bindgen modules)" - echo "This is normal - wasm-bindgen modules are designed for JavaScript, not pure WASI components" - fi -else - echo "❌ WIT embedding failed (expected for wasm-bindgen modules)" -fi - -echo "" -echo "📊 Summary:" -echo "- ✅ Rust compilation works" -echo "- ✅ WebAssembly module generation works" -echo "- ✅ JavaScript bindings work (via wasm-pack)" -echo "- ❌ Pure WebAssembly component creation requires different approach" -echo "" -echo "💡 For VS Code extension use, the wasm-pack generated bindings are what we need!" -echo " The JavaScript bindings in pkg/ directory are ready to use." diff --git a/wit-bindgen-wasm/src/lib.rs b/wit-bindgen-wasm/src/lib.rs index e46f59f..6ea3f9c 100644 --- a/wit-bindgen-wasm/src/lib.rs +++ b/wit-bindgen-wasm/src/lib.rs @@ -1,39 +1,25 @@ -use wasm_bindgen::prelude::*; use std::collections::HashMap; use std::path::Path; -use wit_parser::{Resolve, PackageId}; use anyhow::Context; -// For component decoding (no text printing here; CLI fallback will be used) -use wit_component as wcomp; use wasmparser::{Parser, Payload}; +use wit_bindgen_c as c; use wit_bindgen_core::Files; use wit_bindgen_core::WorldGenerator; -use wit_bindgen_rust as rust; -use wit_bindgen_c as c; use wit_bindgen_cpp as cpp; -use wit_bindgen_go as go; use wit_bindgen_csharp as csharp; -use wit_bindgen_moonbit as moonbit; +use wit_bindgen_go as go; use wit_bindgen_markdown as markdown; +use wit_bindgen_moonbit as moonbit; +use wit_bindgen_rust as rust; +use wit_component as wcomp; +use wit_parser::{PackageId, Resolve}; -/// Initialize the panic hook for better error messages in development -#[wasm_bindgen(start)] -pub fn main() { - #[cfg(feature = "console_error_panic_hook")] - console_error_panic_hook::set_once(); -} - -// Helper function for logging errors to console -#[cfg(target_arch = "wasm32")] -fn console_error(msg: &str) { - web_sys::console::error_1(&msg.into()); -} - -#[cfg(not(target_arch = "wasm32"))] -fn console_error(msg: &str) { - eprintln!("{}", msg); -} +// Generate component model bindings from our WIT file +wit_bindgen::generate!({ + world: "wit-bindgen-wasm", + path: "wit/wit-bindgen.wit", +}); /// Convert bytes to Latin1 string encoding for safe round-trip conversion /// Each byte (0-255) maps directly to Unicode code point (0-255) @@ -41,149 +27,73 @@ fn bytes_to_latin1_string(bytes: &[u8]) -> String { bytes.iter().map(|&b| b as char).collect() } -/// A WIT validation and processing instance -#[wasm_bindgen] -#[derive(Default)] -pub struct WitBindgen { - #[wasm_bindgen(skip)] - pub inner: String, -} - -#[wasm_bindgen] -impl WitBindgen { - /// Create a new WitBindgen instance - #[wasm_bindgen(constructor)] - pub fn new() -> WitBindgen { - WitBindgen { - inner: String::new(), - } - } - - /// Extract WIT text from a WebAssembly component (bytes) - /// Returns an empty string on error (JS side will fall back to CLI) - #[wasm_bindgen(js_name = extractWitFromComponent)] - pub fn extract_wit_from_component(&self, bytes: &[u8]) -> String { - match Self::extract_wit_from_component_impl(bytes) { - Ok(s) => s, - Err(e) => { - console_error(&format!("WIT extraction failed: {}", e)); - String::new() - } - } - } +struct WitBindgenComponent; - fn extract_wit_from_component_impl(bytes: &[u8]) -> anyhow::Result { - // Decode component/package and render WIT using wit-component printing utilities - let decoded = wcomp::decode(bytes) - .with_context(|| "failed to decode component metadata to WIT")?; - - // Prepare printer - let mut printer = wcomp::WitPrinter::new(wcomp::OutputToString::default()); - // Keep docs in output (default true); call emit_docs(true) explicitly for clarity - let _ = printer.emit_docs(true); - - // Build the list of nested packages: all dependencies of the main package - let resolve: &Resolve = decoded.resolve(); - let main_pkg: PackageId = decoded.package(); - - // Collect transitive deps of main_pkg - use std::collections::{HashSet, VecDeque}; - let mut visited: HashSet = HashSet::new(); - let mut queue: VecDeque = VecDeque::new(); - queue.push_back(main_pkg); - visited.insert(main_pkg); - while let Some(pkg) = queue.pop_front() { - for dep in resolve.package_direct_deps(pkg) { - if !visited.contains(&dep) { - visited.insert(dep); - queue.push_back(dep); - } - } - } - // Order nested by topo order, excluding main package - let nested: Vec = resolve - .topological_packages() - .into_iter() - .filter(|p| *p != main_pkg && visited.contains(p)) - .collect(); - - // Print into a string - printer - .print(resolve, main_pkg, &nested) - .with_context(|| "failed to render WIT text from decoded component")?; - let out: String = printer.output.into(); - Ok(out) - } +export!(WitBindgenComponent); - /// Extract core WebAssembly modules from a component. - /// Returns a JSON object mapping filename -> latin1 content. - /// If no core modules are found, returns an empty JSON object. - #[wasm_bindgen(js_name = extractCoreWasmFromComponent)] - pub fn extract_core_wasm_from_component(&self, bytes: &[u8]) -> String { - match Self::extract_core_wasm_impl(bytes) { - Ok(map) => serde_json::to_string(&map).unwrap_or_else(|_| "{}".to_string()), - Err(e) => { - console_error(&format!("Core wasm extraction failed: {}", e)); - "{}".to_string() - } - } - } +impl exports::wit_bindgen::wasm::wit_validator::Guest for WitBindgenComponent { + fn validate_wit_syntax(content: String) -> bool { + let trimmed = content.trim(); - fn extract_core_wasm_impl(bytes: &[u8]) -> anyhow::Result> { - let mut map = std::collections::HashMap::new(); - - // Use wasmparser's incremental Parser with component-model feature enabled. - // This will iterate payloads and yield ModuleSection for embedded core modules. - let parser = Parser::new(0); - let mut index: usize = 0; - for payload in parser.parse_all(bytes) { - let payload = payload?; - match payload { - // Embedded core module inside a component - Payload::ModuleSection { unchecked_range, .. } => { - let module_bytes = &bytes[unchecked_range.start..unchecked_range.end]; - let filename = format!("core{index}.wasm"); - map.insert(filename, bytes_to_latin1_string(module_bytes)); - index += 1; - } - // Nested component: continue; parse_all already iterates it - Payload::ComponentSection { .. } => {} - _ => {} - } + if trimmed.is_empty() { + return false; } - Ok(map) + let inline_path = Path::new("inline.wit"); + let mut resolve = Resolve::default(); + resolve.push_str(inline_path, trimmed).is_ok() } - /// Validate WIT syntax using wit-parser - #[wasm_bindgen(js_name = validateWitSyntax)] - pub fn validate_wit_syntax(&self, content: &str) -> bool { + fn validate_wit_syntax_detailed(content: String) -> String { let trimmed = content.trim(); - + if trimmed.is_empty() { - return false; + return serde_json::json!({ + "valid": false, + "error": "Empty or whitespace-only content is not valid WIT", + "errorType": "validation" + }) + .to_string(); } let inline_path = Path::new("inline.wit"); let mut resolve = Resolve::default(); match resolve.push_str(inline_path, trimmed) { - Ok(_package_id) => true, + Ok(_package_id) => serde_json::json!({ + "valid": true + }) + .to_string(), Err(e) => { - #[cfg(feature = "console_error_panic_hook")] - console_error(&format!("WIT parsing failed: {}", e)); - false + let error_message = e.to_string(); + + if error_message.contains("package not found") + || error_message.contains("interface not found") + || error_message.contains("failed to find package") + { + serde_json::json!({ + "valid": false, + "error": format!("Dependency error: {}", error_message), + "errorType": "dependency" + }) + .to_string() + } else { + serde_json::json!({ + "valid": false, + "error": error_message, + "errorType": "parsing" + }) + .to_string() + } } } } - /// Extract interface names from WIT content - #[wasm_bindgen(js_name = extractInterfaces)] - pub fn extract_interfaces(&self, content: &str) -> String { + fn extract_interfaces(content: String) -> String { let mut interfaces = Vec::new(); - + let inline_path = Path::new("inline.wit"); let mut resolve = Resolve::default(); - match resolve.push_str(inline_path, content) { + match resolve.push_str(inline_path, &content) { Ok(package_id) => { let package = &resolve.packages[package_id]; for (interface_name, _interface_id) in &package.interfaces { @@ -192,16 +102,14 @@ impl WitBindgen { } Err(_) => {} } - + interfaces.join(", ") } - /// Check if content contains world definitions - #[wasm_bindgen(js_name = hasWorldDefinition)] - pub fn has_world_definition(&self, content: &str) -> bool { + fn has_world_definition(content: String) -> bool { let inline_path = Path::new("inline.wit"); let mut resolve = Resolve::default(); - match resolve.push_str(inline_path, content) { + match resolve.push_str(inline_path, &content) { Ok(package_id) => { let package = &resolve.packages[package_id]; !package.worlds.is_empty() @@ -210,400 +118,400 @@ impl WitBindgen { } } - /// Get version information - #[wasm_bindgen] - pub fn version(&self) -> String { + fn version() -> String { env!("WIT_BINDGEN_CORE_VERSION").to_string() } - /// Generate language bindings from WIT content - /// Supports: rust, c, csharp, go, moonbit - /// Returns JSON string containing file map - #[wasm_bindgen(js_name = generateBindings)] - pub fn generate_bindings(&self, content: &str, language: &str, world_name: Option) -> String { + fn generate_bindings(content: String, language: String, world_name: Option) -> String { let files = match language.to_lowercase().as_str() { - "rust" => self.generate_rust_bindings(content, world_name), - "c" => self.generate_c_bindings(content, world_name), - "cpp" | "c++" => self.generate_cpp_bindings(content, world_name), - "csharp" | "c#" => self.generate_csharp_bindings(content, world_name), - "go" => self.generate_go_bindings(content, world_name), - "moonbit" => self.generate_moonbit_bindings(content, world_name), - "markdown" | "md" => self.generate_markdown_bindings(content, world_name), + "rust" => generate_rust_bindings(&content, world_name), + "c" => generate_c_bindings(&content, world_name), + "cpp" | "c++" => generate_cpp_bindings(&content, world_name), + "csharp" | "c#" => generate_csharp_bindings(&content, world_name), + "go" => generate_go_bindings(&content, world_name), + "moonbit" => generate_moonbit_bindings(&content, world_name), + "markdown" | "md" => generate_markdown_bindings(&content, world_name), _ => { let mut error_files = HashMap::new(); error_files.insert( - "error.txt".to_string(), - format!("// Unsupported language: {}\n// Supported languages: rust, c, cpp, csharp, moonbit, markdown\n// Note: Go bindings moved to go.bytecodealliance.org/cmd/wit-bindgen-go", language) + "error.txt".to_string(), + format!("// Unsupported language: {}\n// Supported languages: rust, c, cpp, csharp, go, moonbit, markdown\n// Note: Go bindings moved to go.bytecodealliance.org/cmd/wit-bindgen-go", language) ); error_files - }, + } }; - + serde_json::to_string(&files).unwrap_or_else(|_| "{}".to_string()) } - /// Generate C bindings using wit-bindgen-c library - fn generate_c_bindings(&self, content: &str, world_name: Option) -> HashMap { - match self.generate_c_with_wit_bindgen(content, world_name.as_deref()) { - Ok(files) => files, + fn extract_wit_from_component(bytes: Vec) -> String { + match extract_wit_from_component_impl(&bytes) { + Ok(s) => s, Err(e) => { - console_error(&format!("wit-bindgen-c failed: {}", e)); - let mut error_files = HashMap::new(); - error_files.insert( - "error.txt".to_string(), - format!("C binding generation failed: {}", e) - ); - error_files + eprintln!("WIT extraction failed: {}", e); + String::new() } } } - - /// Generate C bindings using wit-bindgen-c library - fn generate_c_with_wit_bindgen(&self, content: &str, world_name: Option<&str>) -> Result, anyhow::Error> { - let inline_path = Path::new("inline.wit"); - let mut resolve = Resolve::default(); - let package_id = resolve.push_str(inline_path, content) - .with_context(|| "Failed to parse WIT content for C binding generation")?; - - let world_id = if let Some(world_name) = world_name { - resolve.select_world(&[package_id], Some(world_name))? - } else { - resolve.select_world(&[package_id], None)? - }; - - let opts = c::Opts::default(); - let mut generator = opts.build(); - let mut files = Files::default(); - - generator.generate(&mut resolve, world_id, &mut files)?; - - let mut result = HashMap::new(); - for (filename, content) in files.iter() { - result.insert(filename.to_string(), bytes_to_latin1_string(content)); - } - - Ok(result) - } - - /// Generate C++ bindings using wit-bindgen-cpp library - fn generate_cpp_bindings(&self, content: &str, world_name: Option) -> HashMap { - match self.generate_cpp_with_wit_bindgen(content, world_name.as_deref()) { - Ok(files) => files, + + fn extract_core_wasm_from_component(bytes: Vec) -> String { + match extract_core_wasm_impl(&bytes) { + Ok(map) => serde_json::to_string(&map).unwrap_or_else(|_| "{}".to_string()), Err(e) => { - console_error(&format!("wit-bindgen-cpp failed: {}", e)); - let mut error_files = HashMap::new(); - error_files.insert( - "error.txt".to_string(), - format!("C++ binding generation failed: {}", e) - ); - error_files + eprintln!("Core wasm extraction failed: {}", e); + "{}".to_string() } } } - - /// Generate C++ bindings using wit-bindgen-cpp library - fn generate_cpp_with_wit_bindgen(&self, content: &str, world_name: Option<&str>) -> Result, anyhow::Error> { - let inline_path = Path::new("inline.wit"); - let mut resolve = Resolve::default(); - let package_id = resolve.push_str(inline_path, content) - .with_context(|| "Failed to parse WIT content for C++ binding generation")?; - - let world_id = if let Some(world_name) = world_name { - resolve.select_world(&[package_id], Some(world_name))? - } else { - resolve.select_world(&[package_id], None)? - }; - - let opts = cpp::Opts::default(); - let mut generator = opts.build(None); - let mut files = Files::default(); - - generator.generate(&mut resolve, world_id, &mut files)?; - - let mut result = HashMap::new(); - for (filename, content) in files.iter() { - result.insert(filename.to_string(), bytes_to_latin1_string(content)); - } - - Ok(result) + + fn is_wit_file_extension(filename: String) -> bool { + filename.to_lowercase().ends_with(".wit") } - - /// Generate Rust bindings using wit-bindgen-rust library - fn generate_rust_bindings(&self, content: &str, world_name: Option) -> HashMap { - match self.generate_rust_with_wit_bindgen(content, world_name.as_deref()) { - Ok(files) => files, - Err(e) => { - console_error(&format!("wit-bindgen-rust failed: {}", e)); - let mut error_files = HashMap::new(); - error_files.insert( - "error.txt".to_string(), - format!("Rust binding generation failed: {}", e) - ); - error_files - } - } + + fn get_wit_bindgen_version() -> String { + env!("WIT_BINDGEN_CORE_VERSION").to_string() } +} - /// Generate Rust bindings using wit-bindgen-rust library - fn generate_rust_with_wit_bindgen(&self, content: &str, world_name: Option<&str>) -> Result, anyhow::Error> { - let inline_path = Path::new("inline.wit"); - let mut resolve = Resolve::default(); - let package_id = resolve.push_str(inline_path, content) - .with_context(|| "Failed to parse WIT content for Rust binding generation")?; - - let world_id = if let Some(world_name) = world_name { - resolve.select_world(&[package_id], Some(world_name))? - } else { - resolve.select_world(&[package_id], None)? - }; - - let opts = rust::Opts { - generate_all: true, - ..Default::default() - }; - let mut generator = opts.build(); - let mut files = Files::default(); - - generator.generate(&mut resolve, world_id, &mut files)?; - - let mut result = HashMap::new(); - for (filename, content) in files.iter() { - result.insert(filename.to_string(), bytes_to_latin1_string(content)); +// --- Implementation functions (unchanged logic from previous version) --- + +fn extract_wit_from_component_impl(bytes: &[u8]) -> anyhow::Result { + let decoded = + wcomp::decode(bytes).with_context(|| "failed to decode component metadata to WIT")?; + + let mut printer = wcomp::WitPrinter::new(wcomp::OutputToString::default()); + let _ = printer.emit_docs(true); + + let resolve: &Resolve = decoded.resolve(); + let main_pkg: PackageId = decoded.package(); + + use std::collections::{HashSet, VecDeque}; + let mut visited: HashSet = HashSet::new(); + let mut queue: VecDeque = VecDeque::new(); + queue.push_back(main_pkg); + visited.insert(main_pkg); + while let Some(pkg) = queue.pop_front() { + for dep in resolve.package_direct_deps(pkg) { + if !visited.contains(&dep) { + visited.insert(dep); + queue.push_back(dep); + } } - - Ok(result) } + let nested: Vec = resolve + .topological_packages() + .into_iter() + .filter(|p| *p != main_pkg && visited.contains(p)) + .collect(); + + printer + .print(resolve, main_pkg, &nested) + .with_context(|| "failed to render WIT text from decoded component")?; + let out: String = printer.output.into(); + Ok(out) +} - /// Generate C# bindings using wit-bindgen-csharp library - fn generate_csharp_bindings(&self, content: &str, world_name: Option) -> HashMap { - match self.generate_csharp_with_wit_bindgen(content, world_name.as_deref()) { - Ok(files) => files, - Err(e) => { - console_error(&format!("wit-bindgen-csharp failed: {}", e)); - let mut error_files = HashMap::new(); - error_files.insert( - "error.txt".to_string(), - format!("C# binding generation failed: {}", e) - ); - error_files +fn extract_core_wasm_impl(bytes: &[u8]) -> anyhow::Result> { + let mut map = HashMap::new(); + let parser = Parser::new(0); + let mut index: usize = 0; + for payload in parser.parse_all(bytes) { + let payload = payload?; + match payload { + Payload::ModuleSection { + unchecked_range, .. + } => { + let module_bytes = &bytes[unchecked_range.start..unchecked_range.end]; + let filename = format!("core{index}.wasm"); + map.insert(filename, bytes_to_latin1_string(module_bytes)); + index += 1; } + Payload::ComponentSection { .. } => {} + _ => {} } } + Ok(map) +} - /// Generate C# bindings using wit-bindgen-csharp library - fn generate_csharp_with_wit_bindgen(&self, content: &str, world_name: Option<&str>) -> Result, anyhow::Error> { - let inline_path = Path::new("inline.wit"); - let mut resolve = Resolve::default(); - let package_id = resolve.push_str(inline_path, content) - .with_context(|| "Failed to parse WIT content for C# binding generation")?; - - let world_id = if let Some(world_name) = world_name { - resolve.select_world(&[package_id], Some(world_name))? - } else { - resolve.select_world(&[package_id], None)? - }; - - let opts = csharp::Opts::default(); - let mut generator = opts.build(); - let mut files = Files::default(); - - generator.generate(&mut resolve, world_id, &mut files)?; - - let mut result = HashMap::new(); - for (filename, content) in files.iter() { - result.insert(filename.to_string(), bytes_to_latin1_string(content)); +fn generate_rust_bindings(content: &str, world_name: Option) -> HashMap { + match generate_rust_with_wit_bindgen(content, world_name.as_deref()) { + Ok(files) => files, + Err(e) => { + let mut error_files = HashMap::new(); + error_files.insert( + "error.txt".to_string(), + format!("Rust binding generation failed: {}", e), + ); + error_files } - - Ok(result) } +} - /// Generate Go bindings using wit-bindgen-go library - fn generate_go_bindings(&self, content: &str, world_name: Option) -> HashMap { - match self.generate_go_with_wit_bindgen(content, world_name.as_deref()) { - Ok(files) => files, - Err(e) => { - console_error(&format!("wit-bindgen-go failed: {}", e)); - let mut error_files = HashMap::new(); - error_files.insert( - "error.txt".to_string(), - format!("Go binding generation failed: {}", e) - ); - error_files - } - } +fn generate_rust_with_wit_bindgen( + content: &str, + world_name: Option<&str>, +) -> Result, anyhow::Error> { + let inline_path = Path::new("inline.wit"); + let mut resolve = Resolve::default(); + let package_id = resolve + .push_str(inline_path, content) + .with_context(|| "Failed to parse WIT content for Rust binding generation")?; + + let world_id = resolve.select_world(&[package_id], world_name)?; + + let opts = rust::Opts { + generate_all: true, + ..Default::default() + }; + let mut generator = opts.build(); + let mut files = Files::default(); + generator.generate(&mut resolve, world_id, &mut files)?; + + let mut result = HashMap::new(); + for (filename, content) in files.iter() { + result.insert(filename.to_string(), bytes_to_latin1_string(content)); } + Ok(result) +} - /// Generate Go bindings using wit-bindgen-go library - fn generate_go_with_wit_bindgen(&self, content: &str, world_name: Option<&str>) -> Result, anyhow::Error> { - let inline_path = Path::new("inline.wit"); - let mut resolve = Resolve::default(); - let package_id = resolve.push_str(inline_path, content) - .with_context(|| "Failed to parse WIT content for Go binding generation")?; - - let world_id = if let Some(world_name) = world_name { - resolve.select_world(&[package_id], Some(world_name))? - } else { - resolve.select_world(&[package_id], None)? - }; - - let opts = go::Opts::default(); - let mut generator = opts.build(); - let mut files = Files::default(); - - generator.generate(&mut resolve, world_id, &mut files)?; - - let mut result = HashMap::new(); - for (filename, content) in files.iter() { - result.insert(filename.to_string(), bytes_to_latin1_string(content)); +fn generate_c_bindings(content: &str, world_name: Option) -> HashMap { + match generate_c_with_wit_bindgen(content, world_name.as_deref()) { + Ok(files) => files, + Err(e) => { + let mut error_files = HashMap::new(); + error_files.insert( + "error.txt".to_string(), + format!("C binding generation failed: {}", e), + ); + error_files } - - Ok(result) } +} - /// Generate MoonBit bindings using wit-bindgen-moonbit library - fn generate_moonbit_bindings(&self, content: &str, world_name: Option) -> HashMap { - match self.generate_moonbit_with_wit_bindgen(content, world_name.as_deref()) { - Ok(files) => files, - Err(e) => { - console_error(&format!("wit-bindgen-moonbit failed: {}", e)); - let mut error_files = HashMap::new(); - error_files.insert( - "error.txt".to_string(), - format!("MoonBit binding generation failed: {}", e) - ); - error_files - } - } +fn generate_c_with_wit_bindgen( + content: &str, + world_name: Option<&str>, +) -> Result, anyhow::Error> { + let inline_path = Path::new("inline.wit"); + let mut resolve = Resolve::default(); + let package_id = resolve + .push_str(inline_path, content) + .with_context(|| "Failed to parse WIT content for C binding generation")?; + + let world_id = resolve.select_world(&[package_id], world_name)?; + + let opts = c::Opts::default(); + let mut generator = opts.build(); + let mut files = Files::default(); + generator.generate(&mut resolve, world_id, &mut files)?; + + let mut result = HashMap::new(); + for (filename, content) in files.iter() { + result.insert(filename.to_string(), bytes_to_latin1_string(content)); } + Ok(result) +} - /// Generate Markdown documentation using wit-bindgen-markdown library - fn generate_markdown_bindings(&self, content: &str, world_name: Option) -> HashMap { - match self.generate_markdown_with_wit_bindgen(content, world_name.as_deref()) { - Ok(files) => files, - Err(e) => { - console_error(&format!("wit-bindgen-markdown failed: {}", e)); - let mut error_files = HashMap::new(); - error_files.insert( - "error.txt".to_string(), - format!("Markdown generation failed: {}", e) - ); - error_files - } +fn generate_cpp_bindings(content: &str, world_name: Option) -> HashMap { + match generate_cpp_with_wit_bindgen(content, world_name.as_deref()) { + Ok(files) => files, + Err(e) => { + let mut error_files = HashMap::new(); + error_files.insert( + "error.txt".to_string(), + format!("C++ binding generation failed: {}", e), + ); + error_files } } - - /// Internal implementation for generating Markdown documentation. - /// - /// This function performs the actual Markdown generation using the wit-bindgen-markdown library. - /// It returns a `Result` containing a map of generated files, or an error if generation fails. - fn generate_markdown_with_wit_bindgen(&self, content: &str, world_name: Option<&str>) -> Result, anyhow::Error> { - let inline_path = Path::new("inline.wit"); - let mut resolve = Resolve::default(); - let package_id = resolve.push_str(inline_path, content) - .with_context(|| "Failed to parse WIT content for Markdown generation")?; - - let world_id = if let Some(world_name) = world_name { - resolve.select_world(&[package_id], Some(world_name))? - } else { - resolve.select_world(&[package_id], None)? - }; - - let opts = markdown::Opts::default(); - let mut generator = opts.build(); - let mut files = Files::default(); - - generator.generate(&mut resolve, world_id, &mut files)?; - - let mut result = HashMap::new(); - for (filename, content) in files.iter() { - result.insert(filename.to_string(), bytes_to_latin1_string(content)); +} + +fn generate_cpp_with_wit_bindgen( + content: &str, + world_name: Option<&str>, +) -> Result, anyhow::Error> { + let inline_path = Path::new("inline.wit"); + let mut resolve = Resolve::default(); + let package_id = resolve + .push_str(inline_path, content) + .with_context(|| "Failed to parse WIT content for C++ binding generation")?; + + let world_id = resolve.select_world(&[package_id], world_name)?; + + let opts = cpp::Opts::default(); + let mut generator = opts.build(None); + let mut files = Files::default(); + generator.generate(&mut resolve, world_id, &mut files)?; + + let mut result = HashMap::new(); + for (filename, content) in files.iter() { + result.insert(filename.to_string(), bytes_to_latin1_string(content)); + } + Ok(result) +} + +fn generate_csharp_bindings(content: &str, world_name: Option) -> HashMap { + match generate_csharp_with_wit_bindgen(content, world_name.as_deref()) { + Ok(files) => files, + Err(e) => { + let mut error_files = HashMap::new(); + error_files.insert( + "error.txt".to_string(), + format!("C# binding generation failed: {}", e), + ); + error_files } - - Ok(result) } +} - /// Generate MoonBit bindings using wit-bindgen-moonbit library - fn generate_moonbit_with_wit_bindgen(&self, content: &str, world_name: Option<&str>) -> Result, anyhow::Error> { - let inline_path = Path::new("inline.wit"); - let mut resolve = Resolve::default(); - let package_id = resolve.push_str(inline_path, content) - .with_context(|| "Failed to parse WIT content for MoonBit binding generation")?; - - let world_id = if let Some(world_name) = world_name { - resolve.select_world(&[package_id], Some(world_name))? - } else { - resolve.select_world(&[package_id], None)? - }; - - let opts = moonbit::Opts::default(); - let mut generator = opts.build(); - let mut files = Files::default(); - - generator.generate(&mut resolve, world_id, &mut files)?; - - let mut result = HashMap::new(); - for (filename, content) in files.iter() { - result.insert(filename.to_string(), bytes_to_latin1_string(content)); +fn generate_csharp_with_wit_bindgen( + content: &str, + world_name: Option<&str>, +) -> Result, anyhow::Error> { + let inline_path = Path::new("inline.wit"); + let mut resolve = Resolve::default(); + let package_id = resolve + .push_str(inline_path, content) + .with_context(|| "Failed to parse WIT content for C# binding generation")?; + + let world_id = resolve.select_world(&[package_id], world_name)?; + + let opts = csharp::Opts::default(); + let mut generator = opts.build(); + let mut files = Files::default(); + generator.generate(&mut resolve, world_id, &mut files)?; + + let mut result = HashMap::new(); + for (filename, content) in files.iter() { + result.insert(filename.to_string(), bytes_to_latin1_string(content)); + } + Ok(result) +} + +fn generate_go_bindings(content: &str, world_name: Option) -> HashMap { + match generate_go_with_wit_bindgen(content, world_name.as_deref()) { + Ok(files) => files, + Err(e) => { + let mut error_files = HashMap::new(); + error_files.insert( + "error.txt".to_string(), + format!("Go binding generation failed: {}", e), + ); + error_files } - - Ok(result) } +} - /// Validate WIT syntax and return detailed error information - #[wasm_bindgen(js_name = validateWitSyntaxDetailed)] - pub fn validate_wit_syntax_detailed(&self, content: &str) -> String { - let trimmed = content.trim(); - - if trimmed.is_empty() { - return serde_json::json!({ - "valid": false, - "error": "Empty or whitespace-only content is not valid WIT", - "errorType": "validation" - }).to_string(); +fn generate_go_with_wit_bindgen( + content: &str, + world_name: Option<&str>, +) -> Result, anyhow::Error> { + let inline_path = Path::new("inline.wit"); + let mut resolve = Resolve::default(); + let package_id = resolve + .push_str(inline_path, content) + .with_context(|| "Failed to parse WIT content for Go binding generation")?; + + let world_id = resolve.select_world(&[package_id], world_name)?; + + let opts = go::Opts::default(); + let mut generator = opts.build(); + let mut files = Files::default(); + generator.generate(&mut resolve, world_id, &mut files)?; + + let mut result = HashMap::new(); + for (filename, content) in files.iter() { + result.insert(filename.to_string(), bytes_to_latin1_string(content)); + } + Ok(result) +} + +fn generate_moonbit_bindings(content: &str, world_name: Option) -> HashMap { + match generate_moonbit_with_wit_bindgen(content, world_name.as_deref()) { + Ok(files) => files, + Err(e) => { + let mut error_files = HashMap::new(); + error_files.insert( + "error.txt".to_string(), + format!("MoonBit binding generation failed: {}", e), + ); + error_files } + } +} - let inline_path = Path::new("inline.wit"); - let mut resolve = Resolve::default(); - match resolve.push_str(inline_path, trimmed) { - Ok(_package_id) => { - serde_json::json!({ - "valid": true - }).to_string() - } - Err(e) => { - let error_message = e.to_string(); - - #[cfg(feature = "console_error_panic_hook")] - console_error(&format!("WIT parsing failed: {}", error_message)); - - if error_message.contains("package not found") || - error_message.contains("interface not found") || - error_message.contains("failed to find package") { - serde_json::json!({ - "valid": false, - "error": format!("Dependency error: {}", error_message), - "errorType": "dependency" - }).to_string() - } else { - serde_json::json!({ - "valid": false, - "error": error_message, - "errorType": "parsing" - }).to_string() - } - } +fn generate_moonbit_with_wit_bindgen( + content: &str, + world_name: Option<&str>, +) -> Result, anyhow::Error> { + let inline_path = Path::new("inline.wit"); + let mut resolve = Resolve::default(); + let package_id = resolve + .push_str(inline_path, content) + .with_context(|| "Failed to parse WIT content for MoonBit binding generation")?; + + let world_id = resolve.select_world(&[package_id], world_name)?; + + let opts = moonbit::Opts::default(); + let mut generator = opts.build(); + let mut files = Files::default(); + generator.generate(&mut resolve, world_id, &mut files)?; + + let mut result = HashMap::new(); + for (filename, content) in files.iter() { + result.insert(filename.to_string(), bytes_to_latin1_string(content)); + } + Ok(result) +} + +fn generate_markdown_bindings( + content: &str, + world_name: Option, +) -> HashMap { + match generate_markdown_with_wit_bindgen(content, world_name.as_deref()) { + Ok(files) => files, + Err(e) => { + let mut error_files = HashMap::new(); + error_files.insert( + "error.txt".to_string(), + format!("Markdown generation failed: {}", e), + ); + error_files } } } +fn generate_markdown_with_wit_bindgen( + content: &str, + world_name: Option<&str>, +) -> Result, anyhow::Error> { + let inline_path = Path::new("inline.wit"); + let mut resolve = Resolve::default(); + let package_id = resolve + .push_str(inline_path, content) + .with_context(|| "Failed to parse WIT content for Markdown generation")?; + + let world_id = resolve.select_world(&[package_id], world_name)?; + + let opts = markdown::Opts::default(); + let mut generator = opts.build(); + let mut files = Files::default(); + generator.generate(&mut resolve, world_id, &mut files)?; + + let mut result = HashMap::new(); + for (filename, content) in files.iter() { + result.insert(filename.to_string(), bytes_to_latin1_string(content)); + } + Ok(result) +} + #[cfg(test)] mod tests { - use super::*; + use crate::exports::wit_bindgen::wasm::wit_validator::Guest; + use crate::WitBindgenComponent; #[test] fn test_validate_wit_syntax_with_undefined_type() { - let wit_bindgen = WitBindgen::new(); - let invalid_content = r#"package foo:foo; interface i { @@ -622,38 +530,32 @@ world foo { export foo: func() -> tuple; }"#; - let result = wit_bindgen.validate_wit_syntax(invalid_content); + let result = WitBindgenComponent::validate_wit_syntax(invalid_content.to_string()); assert!(!result, "Should detect undefined type4 as invalid"); } #[test] fn test_validate_wit_syntax_with_valid_content() { - let wit_bindgen = WitBindgen::new(); - let valid_content = r#"package foo:foo; world foo { }"#; - let result = wit_bindgen.validate_wit_syntax(valid_content); + let result = WitBindgenComponent::validate_wit_syntax(valid_content.to_string()); assert!(result, "Should validate correct WIT syntax as valid"); } #[test] fn test_validate_wit_syntax_with_empty_content() { - let wit_bindgen = WitBindgen::new(); - - let result = wit_bindgen.validate_wit_syntax(""); + let result = WitBindgenComponent::validate_wit_syntax("".to_string()); assert!(!result, "Empty content should be invalid"); - - let result = wit_bindgen.validate_wit_syntax(" \n \t "); + + let result = WitBindgenComponent::validate_wit_syntax(" \n \t ".to_string()); assert!(!result, "Whitespace-only content should be invalid"); } #[test] fn test_validate_wit_syntax_with_sized_lists() { - let wit_bindgen = WitBindgen::new(); - let valid_content = r#"package foo:foo; interface test { @@ -665,7 +567,7 @@ world test-world { export test; }"#; - let result = wit_bindgen.validate_wit_syntax(valid_content); + let result = WitBindgenComponent::validate_wit_syntax(valid_content.to_string()); assert!(result, "Should validate sized list syntax as valid"); } } diff --git a/wit-bindgen-wasm/wit/wit-bindgen.wit b/wit-bindgen-wasm/wit/wit-bindgen.wit index 8e660e5..cb28b9f 100644 --- a/wit-bindgen-wasm/wit/wit-bindgen.wit +++ b/wit-bindgen-wasm/wit/wit-bindgen.wit @@ -1,5 +1,5 @@ // WIT interface definition for wit-bindgen-wasm -// This defines the exports available from the WebAssembly module +// This defines the exports available from the WebAssembly component // // BINARY SAFETY CONTRACT: // All file content returned by this module is encoded using Latin1 (ISO-8859-1) @@ -11,43 +11,46 @@ package wit-bindgen:wasm@0.1.0; /// Core interface for WIT validation and processing interface wit-validator { - /// A WIT validation and processing instance - resource wit-bindgen { - /// Create a new wit-bindgen instance - constructor(); - - /// Validate WIT syntax in the given content - /// Returns true if the content appears to be valid WIT syntax - validate-wit-syntax: func(content: string) -> bool; - - /// Extract interface names from WIT content - /// Returns a comma-separated list of interface names - extract-interfaces: func(content: string) -> string; - - /// Check if content contains world definitions - has-world-definition: func(content: string) -> bool; - - /// Get version information - version: func() -> string; - - /// Generate language bindings from WIT content - /// Supports: rust, c, csharp, go, moonbit - /// Returns JSON string containing file map where file contents are Latin1-encoded - /// - /// IMPORTANT: All file content strings are encoded using Latin1 (ISO-8859-1) - /// to preserve binary data integrity. This ensures that binary files (like .wasm, .o) - /// and text files can be round-tripped without corruption. Consumers must decode - /// file content using Latin1 encoding when writing to disk. - generate-bindings: func(content: string, language: string, world-name: option) -> string; - - /// Validate WIT syntax and return detailed error information - /// Returns JSON string with validation results and error details - validate-wit-syntax-detailed: func(content: string) -> string; - } - + /// Validate WIT syntax in the given content + /// Returns true if the content appears to be valid WIT syntax + validate-wit-syntax: func(content: string) -> bool; + + /// Validate WIT syntax and return detailed error information + /// Returns JSON string with validation results and error details + validate-wit-syntax-detailed: func(content: string) -> string; + + /// Extract interface names from WIT content + /// Returns a comma-separated list of interface names + extract-interfaces: func(content: string) -> string; + + /// Check if content contains world definitions + has-world-definition: func(content: string) -> bool; + + /// Get version information + version: func() -> string; + + /// Generate language bindings from WIT content + /// Supports: rust, c, cpp, csharp, go, moonbit, markdown + /// Returns JSON string containing file map where file contents are Latin1-encoded + /// + /// IMPORTANT: All file content strings are encoded using Latin1 (ISO-8859-1) + /// to preserve binary data integrity. This ensures that binary files (like .wasm, .o) + /// and text files can be round-tripped without corruption. Consumers must decode + /// file content using Latin1 encoding when writing to disk. + generate-bindings: func(content: string, language: string, world-name: option) -> string; + + /// Extract WIT text from a WebAssembly component (bytes). + /// Returns the WIT text as a plain string on success, or an empty string on failure. + extract-wit-from-component: func(bytes: list) -> string; + + /// Extract core WebAssembly modules from a component. + /// Returns a JSON object mapping filename -> latin1 content. + /// If no core modules are found, returns an empty JSON object. + extract-core-wasm-from-component: func(bytes: list) -> string; + /// Check if filename has .wit extension is-wit-file-extension: func(filename: string) -> bool; - + /// Get wit-bindgen version get-wit-bindgen-version: func() -> string; }