Skip to content

Commit ea43189

Browse files
authored
feat: support wasi target (#5)
1 parent e2d3390 commit ea43189

File tree

8 files changed

+349
-7
lines changed

8 files changed

+349
-7
lines changed

.eslintrc.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ extends:
2323
- prettier
2424
- eslint:recommended
2525

26+
globals:
27+
globalThis: true
28+
2629
rules:
2730
# 0 = off, 1 = warn, 2 = error
2831
'space-before-function-paren': 0

.github/workflows/CI.yml

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ jobs:
7070
- host: windows-latest
7171
target: aarch64-pc-windows-msvc
7272
build: pnpm build --target aarch64-pc-windows-msvc
73+
- host: ubuntu-latest
74+
target: wasm32-wasi-preview1-threads
75+
build: pnpm build --target wasm32-wasi-preview1-threads
7376
name: stable - ${{ matrix.settings.target }} - node@20
7477
runs-on: ${{ matrix.settings.host }}
7578
steps:
@@ -119,9 +122,18 @@ jobs:
119122
shell: bash
120123
- name: Upload artifact
121124
uses: actions/upload-artifact@v4
125+
if: matrix.settings.target != 'wasm32-wasi-preview1-threads'
122126
with:
123127
name: bindings-${{ matrix.settings.target }}
124-
path: ${{ env.APP_NAME }}.*.node
128+
path: "*.node"
129+
if-no-files-found: error
130+
131+
- name: Upload artifact
132+
uses: actions/upload-artifact@v4
133+
if: matrix.settings.target == 'wasm32-wasi-preview1-threads'
134+
with:
135+
name: bindings-${{ matrix.settings.target }}
136+
path: "*.wasm"
125137
if-no-files-found: error
126138
build-freebsd:
127139
runs-on: macos-12
@@ -275,13 +287,42 @@ jobs:
275287
image: ${{ steps.docker.outputs.IMAGE }}
276288
options: -v ${{ steps.docker.outputs.PNPM_STORE_PATH }}:${{ steps.docker.outputs.PNPM_STORE_PATH }} -v ${{ github.workspace }}:${{ github.workspace }} -w ${{ github.workspace }} --platform ${{ steps.docker.outputs.PLATFORM }}
277289
run: npm run test
290+
test-wasi:
291+
name: Test WASI target
292+
needs:
293+
- build
294+
runs-on: ubuntu-latest
295+
steps:
296+
- uses: actions/checkout@v4
297+
- name: setup pnpm
298+
uses: pnpm/action-setup@v2
299+
- name: Setup node
300+
uses: actions/setup-node@v4
301+
with:
302+
node-version: 20
303+
cache: pnpm
304+
- name: Install dependencies
305+
run: pnpm install
306+
- name: Download artifacts
307+
uses: actions/download-artifact@v4
308+
with:
309+
name: bindings-wasm32-wasi-preview1-threads
310+
path: .
311+
- name: List packages
312+
run: ls -R .
313+
shell: bash
314+
- name: Test bindings
315+
run: pnpm test
316+
env:
317+
NAPI_RS_FORCE_WASI: 1
278318
publish:
279319
name: Publish
280320
runs-on: ubuntu-latest
281321
needs:
282322
- build-freebsd
283323
- test-macOS-windows-binding
284324
- test-linux-binding
325+
- test-wasi
285326
steps:
286327
- uses: actions/checkout@v4
287328
- name: setup pnpm

package-template.wasi-browser.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { instantiateNapiModuleSync as __emnapiInstantiateNapiModuleSync } from '@emnapi/core'
2+
import { getDefaultContext as __emnapiGetDefaultContext } from '@emnapi/runtime'
3+
import { WASI as __WASI } from '@tybys/wasm-util'
4+
import { Volume as __Volume, createFsFromVolume as __createFsFromVolume } from 'memfs-browser'
5+
6+
import __wasmUrl from './package-template.wasm32-wasi.wasm?url'
7+
8+
const __fs = __createFsFromVolume(
9+
__Volume.fromJSON({
10+
'/': null,
11+
}),
12+
)
13+
14+
const __wasi = new __WASI({
15+
version: 'preview1',
16+
fs: __fs,
17+
})
18+
19+
const __emnapiContext = __emnapiGetDefaultContext()
20+
21+
const __sharedMemory = new WebAssembly.Memory({
22+
initial: 1024,
23+
maximum: 10240,
24+
shared: true,
25+
})
26+
27+
const __wasmFile = await fetch(__wasmUrl).then((res) => res.arrayBuffer())
28+
29+
const {
30+
instance: __napiInstance,
31+
module: __wasiModule,
32+
napiModule: __napiModule,
33+
} = __emnapiInstantiateNapiModuleSync(__wasmFile, {
34+
context: __emnapiContext,
35+
asyncWorkPoolSize: 4,
36+
wasi: __wasi,
37+
onCreateWorker() {
38+
return new Worker(new URL('./wasi-worker-browser.mjs', import.meta.url), {
39+
type: 'module',
40+
})
41+
},
42+
overwriteImports(importObject) {
43+
importObject.env = {
44+
...importObject.env,
45+
...importObject.napi,
46+
...importObject.emnapi,
47+
memory: __sharedMemory,
48+
}
49+
return importObject
50+
},
51+
beforeInit({ instance }) {
52+
__napi_rs_initialize_modules(instance)
53+
},
54+
})
55+
56+
function __napi_rs_initialize_modules(__napiInstance) {
57+
__napiInstance.exports['__napi_register__plus_100_0']?.()
58+
}
59+
export const plus100 = __napiModule.exports.plus100

package-template.wasi.cjs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* eslint-disable */
2+
/* prettier-ignore */
3+
4+
/* auto-generated by NAPI-RS */
5+
6+
const __nodeFs= require('node:fs')
7+
const __nodePath = require('node:path')
8+
const { WASI: __nodeWASI } = require('node:wasi')
9+
const { Worker } = require('node:worker_threads')
10+
11+
const { instantiateNapiModuleSync: __emnapiInstantiateNapiModuleSync } = require('@emnapi/core')
12+
const { getDefaultContext: __emnapiGetDefaultContext } = require('@emnapi/runtime')
13+
14+
const __wasi = new __nodeWASI({
15+
version: 'preview1',
16+
env: process.env,
17+
preopens: {
18+
'/': '/'
19+
}
20+
})
21+
22+
const __emnapiContext = __emnapiGetDefaultContext()
23+
24+
const __sharedMemory = new WebAssembly.Memory({
25+
initial: 1024,
26+
maximum: 10240,
27+
shared: true,
28+
})
29+
30+
let __wasmFilePath = __nodePath.join(__dirname, 'package-template.wasm32-wasi.wasm')
31+
32+
if (!__nodeFs.existsSync(__wasmFilePath)) {
33+
try {
34+
__wasmFilePath = __nodePath.resolve('@napi-rs/package-template-pnpm-wasm32-wasi')
35+
} catch {
36+
throw new Error('Cannot find package-template.wasm32-wasi.wasm file, and @napi-rs/package-template-pnpm-wasm32-wasi package is not installed.')
37+
}
38+
}
39+
40+
const { instance: __napiInstance, module: __wasiModule, napiModule: __napiModule } = __emnapiInstantiateNapiModuleSync(__nodeFs.readFileSync(__wasmFilePath), {
41+
context: __emnapiContext,
42+
asyncWorkPoolSize: (function() {
43+
const threadsSizeFromEnv = Number(process.env.NAPI_RS_ASYNC_WORK_POOL_SIZE ?? process.env.UV_THREADPOOL_SIZE)
44+
// NaN > 0 is false
45+
if (threadsSizeFromEnv > 0) {
46+
return threadsSizeFromEnv
47+
} else {
48+
return 4
49+
}
50+
})(),
51+
wasi: __wasi,
52+
onCreateWorker() {
53+
return new Worker(__nodePath.join(__dirname, 'wasi-worker.mjs'), {
54+
env: process.env,
55+
execArgv: ['--experimental-wasi-unstable-preview1'],
56+
})
57+
},
58+
overwriteImports(importObject) {
59+
importObject.env = {
60+
...importObject.env,
61+
...importObject.napi,
62+
...importObject.emnapi,
63+
memory: __sharedMemory,
64+
}
65+
return importObject
66+
},
67+
beforeInit({ instance }) {
68+
__napi_rs_initialize_modules(instance)
69+
}
70+
})
71+
72+
function __napi_rs_initialize_modules(__napiInstance) {
73+
__napiInstance.exports['__napi_register__plus_100_0']?.()
74+
}
75+
module.exports.plus100 = __napiModule.exports.plus100

package.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
"x86_64-unknown-freebsd",
3636
"aarch64-unknown-linux-musl",
3737
"aarch64-pc-windows-msvc",
38-
"armv7-linux-androideabi"
38+
"armv7-linux-androideabi",
39+
"wasm32-wasi-preview1-threads"
3940
]
4041
},
4142
"engines": {
@@ -60,20 +61,25 @@
6061
"version": "napi version"
6162
},
6263
"devDependencies": {
63-
"@napi-rs/cli": "^3.0.0-alpha.30",
64+
"@emnapi/core": "^0.45.0",
65+
"@emnapi/runtime": "^0.45.0",
66+
"@napi-rs/cli": "^3.0.0-alpha.33",
6467
"@swc-node/register": "^1.6.8",
6568
"@swc/core": "^1.3.102",
6669
"@taplo/cli": "^0.5.2",
70+
"@tybys/wasm-util": "^0.8.1",
6771
"@typescript-eslint/eslint-plugin": "^6.18.0",
6872
"@typescript-eslint/parser": "^6.18.0",
6973
"ava": "^6.0.1",
7074
"benny": "^3.7.1",
7175
"chalk": "^5.3.0",
76+
"emnapi": "^0.45.0",
7277
"eslint": "^8.56.0",
7378
"eslint-config-prettier": "^9.1.0",
7479
"eslint-plugin-import": "^2.29.1",
7580
"husky": "^8.0.3",
7681
"lint-staged": "^15.2.0",
82+
"memfs-browser": "^3.4.13000",
7783
"npm-run-all": "^4.1.5",
7884
"prettier": "^3.1.1",
7985
"typescript": "^5.3.3"

0 commit comments

Comments
 (0)