Skip to content

Commit 33f24c7

Browse files
committed
feat: switch to component model for wit-bindgen-wasm
Time to eat our own dog food Signed-off-by: Gordon Smith <GordonJSmith@gmail.com>
1 parent c9c8d4d commit 33f24c7

File tree

16 files changed

+850
-1283
lines changed

16 files changed

+850
-1283
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ jobs:
1515
os: [ubuntu-latest, windows-latest, macos-latest]
1616
node-version: [22, 24]
1717
runs-on: ${{ matrix.os }}
18+
defaults:
19+
run:
20+
shell: bash
1821
permissions:
1922
contents: read
2023
issues: write
@@ -41,6 +44,9 @@ jobs:
4144
- name: Install dependencies
4245
run: npm ci
4346

47+
- name: Setup WASM build tools
48+
run: npm run setup-wasm
49+
4450
- name: Run tests
4551
run: npm test
4652

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ This extension is available on:
238238
This extension includes a WebAssembly component that requires the following tools for building:
239239

240240
- **Rust** (with `cargo`): Required for building the WebAssembly module
241-
- **wasm-pack v0.13.1**: Used specifically for the `wit-bindgen-wasm` subproject
241+
- **wasm-tools v1.245.0**: Used for embedding WIT metadata and creating WebAssembly components
242242

243243
You can install these dependencies by running:
244244
```bash

package-lock.json

Lines changed: 177 additions & 331 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"type": "module",
2222
"main": "./dist/extension.js",
2323
"scripts": {
24-
"clean": "rimraf out dist types && rimraf --glob \"*.vsix\"",
24+
"clean": "rimraf out dist types coverage && rimraf --glob \"*.vsix\"",
2525
"clean-wasm": "rimraf wit-bindgen-wasm/pkg wit-bindgen-wasm/target",
2626
"clean-samples": "rimraf samples/target samples/tmp/*",
2727
"clean-deps": "rimraf node_modules package-lock.json",
@@ -39,8 +39,8 @@
3939
"test-navigator-polyfill": "vitest run tests/navigator-polyfill.test.ts",
4040
"gen-types": "tsc --project tsconfig.json --emitDeclarationOnly",
4141
"gen-watch": "npm run gen-types -- -w",
42-
"build-wasm-prod": "cd wit-bindgen-wasm && wasm-pack build --target web --out-dir pkg --release",
43-
"build-wasm": "cd wit-bindgen-wasm && wasm-pack build --target web --out-dir pkg",
42+
"build-wasm-prod": "./scripts/build-wasm.sh --release",
43+
"build-wasm": "./scripts/build-wasm.sh",
4444
"build-extension-prod": "node esbuild.mts -- --production",
4545
"build-extension": "node esbuild.mts",
4646
"build-extension-watch": "node esbuild.mts --watch",
@@ -56,23 +56,22 @@
5656
"test": "run-s lint fmt-check build package gen-types test-grammar test-unit",
5757
"test-wasm": "cd wit-bindgen-wasm && cargo test",
5858
"check-wasm": "cd wit-bindgen-wasm && cargo check",
59-
"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); }\"",
59+
"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); }\"",
6060
"dev": "run-p gen-watch build-extension-watch",
6161
"publish": "npx -y @vscode/vsce publish",
6262
"publish-ovsx": "ovsx publish",
6363
"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",
6464
"update-npm": "npx npm-check-updates -u -t minor",
6565
"update-npm-major": "npx npm-check-updates -u",
66-
"update-cargo": "cd wit-bindgen-wasm && cargo update && (command -v cargo-upgrade >/dev/null 2>&1 || cargo install cargo-edit) && cargo upgrade",
67-
"update-cargo-major": "cd wit-bindgen-wasm && cargo update && (command -v cargo-upgrade >/dev/null 2>&1 || cargo install cargo-edit) && cargo upgrade --incompatible",
66+
"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",
67+
"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",
68+
"update-wasm-tools": "./scripts/update-wasm-tools-version.sh",
6869
"update": "run-s update-npm update-cargo",
6970
"update-major": "run-s update-npm-major update-cargo-major"
7071
},
71-
"dependencies": {
72-
"@bytecodealliance/jco": "1.17.0",
73-
"wit-bindgen-wasm": "file:wit-bindgen-wasm/pkg"
74-
},
72+
"dependencies": {},
7573
"devDependencies": {
74+
"@bytecodealliance/jco": "1.17.0",
7675
"@eslint/css": "1.0.0",
7776
"@eslint/js": "10.0.1",
7877
"@types/node": "25.5.0",
@@ -94,7 +93,7 @@
9493
"typescript-eslint": "8.57.1",
9594
"vitest": "4.1.0",
9695
"vscode-tmgrammar-test": "0.1.3",
97-
"wasm-pack": "0.14.0"
96+
"wit-bindgen-wasm": "file:wit-bindgen-wasm/pkg"
9897
},
9998
"contributes": {
10099
"colors": [

scripts/build-wasm.sh

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/bin/bash
2+
3+
# Build WebAssembly component from Rust source
4+
# Uses: cargo (Rust), wasm-tools (component model), jco (JS transpilation)
5+
6+
set -e
7+
8+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9+
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
10+
WASM_DIR="$PROJECT_DIR/wit-bindgen-wasm"
11+
PKG_DIR="$WASM_DIR/pkg"
12+
TARGET_DIR="$WASM_DIR/target"
13+
14+
# Parse arguments
15+
RELEASE_FLAG=""
16+
CARGO_PROFILE="debug"
17+
if [[ "$1" == "--release" ]]; then
18+
RELEASE_FLAG="--release"
19+
CARGO_PROFILE="release"
20+
fi
21+
22+
echo "🔨 Building WebAssembly component..."
23+
24+
# Step 1: Build the Rust code to a core WASM module
25+
echo "📦 Step 1/4: Compiling Rust to WebAssembly..."
26+
cd "$WASM_DIR"
27+
cargo build --target wasm32-unknown-unknown $RELEASE_FLAG
28+
29+
CORE_WASM="$TARGET_DIR/wasm32-unknown-unknown/$CARGO_PROFILE/wit_bindgen_wasm.wasm"
30+
if [ ! -f "$CORE_WASM" ]; then
31+
echo "❌ Core WASM not found at: $CORE_WASM"
32+
exit 1
33+
fi
34+
echo " Core module: $(du -h "$CORE_WASM" | cut -f1)"
35+
36+
# Step 2: Embed WIT metadata into the core module
37+
echo "🔗 Step 2/4: Embedding WIT component type information..."
38+
EMBEDDED_WASM="$TARGET_DIR/wit_bindgen_wasm.embedded.wasm"
39+
wasm-tools component embed wit/ "$CORE_WASM" --world wit-bindgen-wasm -o "$EMBEDDED_WASM"
40+
41+
# Step 3: Create the WebAssembly component
42+
echo "🧩 Step 3/4: Creating WebAssembly component..."
43+
COMPONENT_WASM="$TARGET_DIR/wit_bindgen_wasm.component.wasm"
44+
wasm-tools component new "$EMBEDDED_WASM" -o "$COMPONENT_WASM"
45+
echo " Component: $(du -h "$COMPONENT_WASM" | cut -f1)"
46+
47+
# Step 4: Transpile to JavaScript using jco
48+
echo "🔄 Step 4/4: Transpiling component to JavaScript..."
49+
mkdir -p "$PKG_DIR/interfaces"
50+
51+
# Clean old generated files (preserve package.json)
52+
find "$PKG_DIR" -name "*.js" -o -name "*.d.ts" -o -name "*.wasm" | xargs rm -f 2>/dev/null || true
53+
rm -rf "$PKG_DIR/interfaces/"
54+
55+
npx jco transpile "$COMPONENT_WASM" -o "$PKG_DIR" --name wit_bindgen_wasm
56+
57+
# Ensure package.json exists for npm resolution
58+
cat > "$PKG_DIR/package.json" << 'PKGJSON'
59+
{
60+
"name": "wit-bindgen-wasm",
61+
"type": "module",
62+
"description": "WebAssembly component wrapper for wit-bindgen",
63+
"version": "0.1.0",
64+
"license": "Apache-2.0 WITH LLVM-exception",
65+
"files": [
66+
"wit_bindgen_wasm.core.wasm",
67+
"wit_bindgen_wasm.js",
68+
"wit_bindgen_wasm.d.ts",
69+
"interfaces/"
70+
],
71+
"main": "wit_bindgen_wasm.js",
72+
"types": "wit_bindgen_wasm.d.ts"
73+
}
74+
PKGJSON
75+
76+
echo ""
77+
echo "✅ WebAssembly component build complete!"
78+
echo " Output: $PKG_DIR/"
79+
ls -la "$PKG_DIR/"

scripts/setup-wasm.sh

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#!/bin/bash
22

3-
# Setup script for WebAssembly build tools
3+
# Setup script for WebAssembly Component Model build tools
44

55
set -e
66

7-
echo "Setting up WebAssembly build tools..."
7+
echo "Setting up WebAssembly Component Model build tools..."
88

99
# Check if Rust is installed
1010
if ! command -v cargo &> /dev/null; then
@@ -16,27 +16,37 @@ fi
1616

1717
echo "✅ Rust is installed: $(cargo --version)"
1818

19-
# Check if wasm-pack is installed
20-
if ! command -v wasm-pack &> /dev/null; then
21-
echo "📦 Installing wasm-pack v0.13.1..."
22-
cargo install wasm-pack@0.13.1
23-
else
24-
echo "✅ wasm-pack is already installed: $(wasm-pack --version)"
25-
# Check if the version is compatible (0.13.x)
26-
WASM_PACK_VERSION=$(wasm-pack --version | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+')
27-
if [[ ! "$WASM_PACK_VERSION" =~ ^0\.13\. ]]; then
28-
echo "⚠️ Warning: wasm-pack version $WASM_PACK_VERSION detected. This project is tested with v0.13.1"
29-
echo " Consider running: cargo install wasm-pack@0.13.1"
19+
# Check if correct version of wasm-tools is installed
20+
WASM_TOOLS_VERSION="1.245.0"
21+
if command -v wasm-tools &> /dev/null; then
22+
INSTALLED_VERSION=$(wasm-tools --version | awk '{print $2}')
23+
if [[ "$INSTALLED_VERSION" == "$WASM_TOOLS_VERSION" ]]; then
24+
echo "✅ wasm-tools is installed: $(wasm-tools --version)"
25+
else
26+
echo "📦 Updating wasm-tools from $INSTALLED_VERSION to $WASM_TOOLS_VERSION..."
27+
cargo install wasm-tools@"$WASM_TOOLS_VERSION"
3028
fi
29+
else
30+
echo "📦 Installing wasm-tools $WASM_TOOLS_VERSION..."
31+
cargo install wasm-tools@"$WASM_TOOLS_VERSION"
3132
fi
3233

3334
# Add wasm32 target if not present
3435
echo "🎯 Adding wasm32-unknown-unknown target..."
3536
rustup target add wasm32-unknown-unknown
3637

37-
echo "✅ WebAssembly build tools setup complete!"
38+
# Check if jco is available via npx
39+
echo "🔍 Checking jco availability..."
40+
if npx jco --version &> /dev/null; then
41+
echo "✅ jco is available: $(npx jco --version)"
42+
else
43+
echo "⚠️ jco not found. Install it with: npm install @bytecodealliance/jco"
44+
echo " (It should be in the project's devDependencies)"
45+
fi
46+
47+
echo ""
48+
echo "✅ WebAssembly Component Model build tools setup complete!"
3849
echo ""
3950
echo "You can now run:"
40-
echo " npm run build-wasm # Build WASM in release mode"
41-
echo " npm run build-wasm-dev # Build WASM in development mode"
42-
echo " npm run build # Build the entire extension"
51+
echo " npm run build-wasm # Build WASM component"
52+
echo " npm run build # Build the entire extension"
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/bin/bash
2+
3+
# Syncs the wasm-tools CLI version with the wit-component crate version in Cargo.toml,
4+
# installs the matching wasm-tools version, and updates setup-wasm.sh.
5+
6+
set -e
7+
8+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9+
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
10+
CARGO_TOML="$PROJECT_DIR/wit-bindgen-wasm/Cargo.toml"
11+
SETUP_SCRIPT="$SCRIPT_DIR/setup-wasm.sh"
12+
13+
# Extract wit-component version from Cargo.toml (e.g., "0.245")
14+
WIT_COMPONENT_VERSION=$(grep -oP 'wit-component\s*=\s*"\K[^"]+' "$CARGO_TOML")
15+
16+
if [ -z "$WIT_COMPONENT_VERSION" ]; then
17+
echo "❌ Could not find wit-component version in $CARGO_TOML"
18+
exit 1
19+
fi
20+
21+
# Convert crate version 0.X to CLI version 1.X (e.g., 0.245 -> 1.245.0)
22+
MINOR=$(echo "$WIT_COMPONENT_VERSION" | sed 's/^0\.//')
23+
WASM_TOOLS_VERSION="1.${MINOR}.0"
24+
25+
echo "📦 wit-component crate version: $WIT_COMPONENT_VERSION"
26+
echo "🔧 Matching wasm-tools CLI version: $WASM_TOOLS_VERSION"
27+
28+
# Install the matching wasm-tools version
29+
echo "📥 Installing wasm-tools $WASM_TOOLS_VERSION..."
30+
cargo install "wasm-tools@${WASM_TOOLS_VERSION}"
31+
32+
# Update the pinned version in setup-wasm.sh
33+
sed -i "s/WASM_TOOLS_VERSION=\"[^\"]*\"/WASM_TOOLS_VERSION=\"${WASM_TOOLS_VERSION}\"/" "$SETUP_SCRIPT"
34+
echo "✅ Updated WASM_TOOLS_VERSION in setup-wasm.sh to $WASM_TOOLS_VERSION"

0 commit comments

Comments
 (0)