From 4ccbb6117943d5aa06f985eced1555ecf4c6fb05 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Fri, 19 Apr 2024 16:12:30 +0800 Subject: [PATCH] feat(cli): improve the browser binding (#2056) --- cli/src/api/build.ts | 1 + cli/src/api/templates/load-wasi-template.ts | 43 +++++++++++++-------- cli/src/utils/config.ts | 10 +++++ examples/napi/browser/values.spec.ts | 11 ++++++ examples/napi/example.wasi-browser.js | 15 +++---- examples/napi/example.wasi.cjs | 4 +- examples/napi/package.json | 5 ++- examples/napi/vite-entry.js | 2 +- wasm-runtime/fs.js | 6 +-- 9 files changed, 65 insertions(+), 32 deletions(-) diff --git a/cli/src/api/build.ts b/cli/src/api/build.ts index 4cd35757..dbc832f9 100644 --- a/cli/src/api/build.ts +++ b/cli/src/api/build.ts @@ -931,6 +931,7 @@ class Builder { wasiRegisterFunctions, this.config.wasm?.initialMemory, this.config.wasm?.maximumMemory, + this.config.wasm?.browser?.fs, ) + idents .map( diff --git a/cli/src/api/templates/load-wasi-template.ts b/cli/src/api/templates/load-wasi-template.ts index 3ce98a70..3418ab3d 100644 --- a/cli/src/api/templates/load-wasi-template.ts +++ b/cli/src/api/templates/load-wasi-template.ts @@ -3,25 +3,33 @@ export const createWasiBrowserBinding = ( wasiRegisterFunctions: string[], initialMemory = 4000, maximumMemory = 65536, -) => `import { - instantiateNapiModuleSync as __emnapiInstantiateNapiModuleSync, - getDefaultContext as __emnapiGetDefaultContext, - WASI as __WASI, -} from '@napi-rs/wasm-runtime' -import { Volume as __Volume, createFsFromVolume as __createFsFromVolume } from '@napi-rs/wasm-runtime/fs' - -import __wasmUrl from './${wasiFilename}.wasm?url' - -const __fs = __createFsFromVolume( - __Volume.fromJSON({ - '/': null, - }), -) + fs = false, +) => { + const fsImport = fs ? `import { memfs } from '@napi-rs/wasm-runtime/fs'` : '' + const wasiCreation = fs + ? ` +export const { fs: __fs, vol: __volume } = memfs() const __wasi = new __WASI({ version: 'preview1', fs: __fs, -}) + preopens: { + '/': '/', + } +})` + : ` +const __wasi = new __WASI({ + version: 'preview1', +})` + + return `import { + instantiateNapiModuleSync as __emnapiInstantiateNapiModuleSync, + getDefaultContext as __emnapiGetDefaultContext, + WASI as __WASI, +} from '@napi-rs/wasm-runtime' +${fsImport} +import __wasmUrl from './${wasiFilename}.wasm?url' +${wasiCreation} const __emnapiContext = __emnapiGetDefaultContext() @@ -66,6 +74,7 @@ ${wasiRegisterFunctions .join('\n')} } ` +} export const createWasiBinding = ( wasmFileName: string, @@ -88,11 +97,13 @@ const { getDefaultContext: __emnapiGetDefaultContext, } = require('@napi-rs/wasm-runtime') +const __rootDir = __nodePath.parse(process.cwd()).root + const __wasi = new __nodeWASI({ version: 'preview1', env: process.env, preopens: { - '/': '/' + [__rootDir]: __rootDir, } }) diff --git a/cli/src/utils/config.ts b/cli/src/utils/config.ts index 70b1ba36..cc6a47af 100644 --- a/cli/src/utils/config.ts +++ b/cli/src/utils/config.ts @@ -66,6 +66,16 @@ export interface UserNapiConfig { * @default 65536 pages (4GiB) */ maximumMemory?: number + + /** + * Browser wasm binding configuration + */ + browser: { + /** + * Whether to use fs module in browser + */ + fs?: boolean + } } /** diff --git a/examples/napi/browser/values.spec.ts b/examples/napi/browser/values.spec.ts index 69f0fc4d..af585686 100644 --- a/examples/napi/browser/values.spec.ts +++ b/examples/napi/browser/values.spec.ts @@ -2,11 +2,16 @@ import { describe, it, expect } from 'vitest' // @ts-expect-error const { + // @ts-expect-error + __fs, + // @ts-expect-error + __volume, DEFAULT_COST, Bird, GetterSetterWithClosures, tsfnReturnPromise, tsfnReturnPromiseTimeout, + readFileAsync, }: typeof import('../index.cjs') = await import('../example.wasi-browser') describe('NAPI-RS wasi browser test', function () { @@ -53,4 +58,10 @@ describe('NAPI-RS wasi browser test', function () { // trigger Promise.then in Rust after `Promise` is dropped await new Promise((resolve) => setTimeout(resolve, 400)) }) + + it.skip('readFileAsync', async () => { + __volume.writeFileSync('/test.txt', 'hello world') + const value = await readFileAsync('/test.txt') + expect(value).toBe('hello world') + }) }) diff --git a/examples/napi/example.wasi-browser.js b/examples/napi/example.wasi-browser.js index 896db5fc..09be7efc 100644 --- a/examples/napi/example.wasi-browser.js +++ b/examples/napi/example.wasi-browser.js @@ -3,22 +3,17 @@ import { getDefaultContext as __emnapiGetDefaultContext, WASI as __WASI, } from '@napi-rs/wasm-runtime' -import { - Volume as __Volume, - createFsFromVolume as __createFsFromVolume, -} from '@napi-rs/wasm-runtime/fs' - +import { memfs } from '@napi-rs/wasm-runtime/fs' import __wasmUrl from './example.wasm32-wasi.wasm?url' -const __fs = __createFsFromVolume( - __Volume.fromJSON({ - '/': null, - }), -) +export const { fs: __fs, vol: __volume } = memfs() const __wasi = new __WASI({ version: 'preview1', fs: __fs, + preopens: { + '/': '/', + }, }) const __emnapiContext = __emnapiGetDefaultContext() diff --git a/examples/napi/example.wasi.cjs b/examples/napi/example.wasi.cjs index a9e658e9..246780d9 100644 --- a/examples/napi/example.wasi.cjs +++ b/examples/napi/example.wasi.cjs @@ -13,11 +13,13 @@ const { getDefaultContext: __emnapiGetDefaultContext, } = require('@napi-rs/wasm-runtime') +const __rootDir = __nodePath.parse(process.cwd()).root + const __wasi = new __nodeWASI({ version: 'preview1', env: process.env, preopens: { - '/': '/' + [__rootDir]: __rootDir, } }) diff --git a/examples/napi/package.json b/examples/napi/package.json index d2de0a75..48282d78 100644 --- a/examples/napi/package.json +++ b/examples/napi/package.json @@ -13,7 +13,10 @@ "napi": { "binaryName": "example", "wasm": { - "initialMemory": 16384 + "initialMemory": 16384, + "browser": { + "fs": true + } }, "dtsHeader": "type MaybePromise = T | Promise", "dtsHeaderFile": "./dts-header.d.ts" diff --git a/examples/napi/vite-entry.js b/examples/napi/vite-entry.js index 9ffb495b..a825a7d4 100644 --- a/examples/napi/vite-entry.js +++ b/examples/napi/vite-entry.js @@ -3,7 +3,7 @@ import { Kind, asyncMultiTwo, tsfnReturnPromise, -} from './index.wasi-browser' +} from './example.wasi-browser' console.info(new Animal(Kind.Cat, 'Tom')) asyncMultiTwo(200).then((res) => { diff --git a/wasm-runtime/fs.js b/wasm-runtime/fs.js index 15ca0b66..d75b0b43 100644 --- a/wasm-runtime/fs.js +++ b/wasm-runtime/fs.js @@ -1,5 +1,5 @@ -import * as memfs from 'memfs' +import * as memfsExported from 'memfs' -const { createFsFromVolume, Volume, fs } = memfs +const { createFsFromVolume, Volume, fs, memfs } = memfsExported -export { createFsFromVolume, Volume, fs } +export { createFsFromVolume, Volume, fs, memfs }