feat(cli): improve the browser binding (#2056)

This commit is contained in:
LongYinan 2024-04-19 16:12:30 +08:00 committed by GitHub
parent cd3a850dcb
commit 4ccbb61179
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 65 additions and 32 deletions

View file

@ -931,6 +931,7 @@ class Builder {
wasiRegisterFunctions, wasiRegisterFunctions,
this.config.wasm?.initialMemory, this.config.wasm?.initialMemory,
this.config.wasm?.maximumMemory, this.config.wasm?.maximumMemory,
this.config.wasm?.browser?.fs,
) + ) +
idents idents
.map( .map(

View file

@ -3,25 +3,33 @@ export const createWasiBrowserBinding = (
wasiRegisterFunctions: string[], wasiRegisterFunctions: string[],
initialMemory = 4000, initialMemory = 4000,
maximumMemory = 65536, maximumMemory = 65536,
) => `import { fs = false,
instantiateNapiModuleSync as __emnapiInstantiateNapiModuleSync, ) => {
getDefaultContext as __emnapiGetDefaultContext, const fsImport = fs ? `import { memfs } from '@napi-rs/wasm-runtime/fs'` : ''
WASI as __WASI, const wasiCreation = fs
} from '@napi-rs/wasm-runtime' ? `
import { Volume as __Volume, createFsFromVolume as __createFsFromVolume } from '@napi-rs/wasm-runtime/fs' export const { fs: __fs, vol: __volume } = memfs()
import __wasmUrl from './${wasiFilename}.wasm?url'
const __fs = __createFsFromVolume(
__Volume.fromJSON({
'/': null,
}),
)
const __wasi = new __WASI({ const __wasi = new __WASI({
version: 'preview1', version: 'preview1',
fs: __fs, 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() const __emnapiContext = __emnapiGetDefaultContext()
@ -66,6 +74,7 @@ ${wasiRegisterFunctions
.join('\n')} .join('\n')}
} }
` `
}
export const createWasiBinding = ( export const createWasiBinding = (
wasmFileName: string, wasmFileName: string,
@ -88,11 +97,13 @@ const {
getDefaultContext: __emnapiGetDefaultContext, getDefaultContext: __emnapiGetDefaultContext,
} = require('@napi-rs/wasm-runtime') } = require('@napi-rs/wasm-runtime')
const __rootDir = __nodePath.parse(process.cwd()).root
const __wasi = new __nodeWASI({ const __wasi = new __nodeWASI({
version: 'preview1', version: 'preview1',
env: process.env, env: process.env,
preopens: { preopens: {
'/': '/' [__rootDir]: __rootDir,
} }
}) })

View file

@ -66,6 +66,16 @@ export interface UserNapiConfig {
* @default 65536 pages (4GiB) * @default 65536 pages (4GiB)
*/ */
maximumMemory?: number maximumMemory?: number
/**
* Browser wasm binding configuration
*/
browser: {
/**
* Whether to use fs module in browser
*/
fs?: boolean
}
} }
/** /**

View file

@ -2,11 +2,16 @@ import { describe, it, expect } from 'vitest'
// @ts-expect-error // @ts-expect-error
const { const {
// @ts-expect-error
__fs,
// @ts-expect-error
__volume,
DEFAULT_COST, DEFAULT_COST,
Bird, Bird,
GetterSetterWithClosures, GetterSetterWithClosures,
tsfnReturnPromise, tsfnReturnPromise,
tsfnReturnPromiseTimeout, tsfnReturnPromiseTimeout,
readFileAsync,
}: typeof import('../index.cjs') = await import('../example.wasi-browser') }: typeof import('../index.cjs') = await import('../example.wasi-browser')
describe('NAPI-RS wasi browser test', function () { 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 // trigger Promise.then in Rust after `Promise` is dropped
await new Promise((resolve) => setTimeout(resolve, 400)) 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')
})
}) })

View file

@ -3,22 +3,17 @@ import {
getDefaultContext as __emnapiGetDefaultContext, getDefaultContext as __emnapiGetDefaultContext,
WASI as __WASI, WASI as __WASI,
} from '@napi-rs/wasm-runtime' } from '@napi-rs/wasm-runtime'
import { import { memfs } from '@napi-rs/wasm-runtime/fs'
Volume as __Volume,
createFsFromVolume as __createFsFromVolume,
} from '@napi-rs/wasm-runtime/fs'
import __wasmUrl from './example.wasm32-wasi.wasm?url' import __wasmUrl from './example.wasm32-wasi.wasm?url'
const __fs = __createFsFromVolume( export const { fs: __fs, vol: __volume } = memfs()
__Volume.fromJSON({
'/': null,
}),
)
const __wasi = new __WASI({ const __wasi = new __WASI({
version: 'preview1', version: 'preview1',
fs: __fs, fs: __fs,
preopens: {
'/': '/',
},
}) })
const __emnapiContext = __emnapiGetDefaultContext() const __emnapiContext = __emnapiGetDefaultContext()

View file

@ -13,11 +13,13 @@ const {
getDefaultContext: __emnapiGetDefaultContext, getDefaultContext: __emnapiGetDefaultContext,
} = require('@napi-rs/wasm-runtime') } = require('@napi-rs/wasm-runtime')
const __rootDir = __nodePath.parse(process.cwd()).root
const __wasi = new __nodeWASI({ const __wasi = new __nodeWASI({
version: 'preview1', version: 'preview1',
env: process.env, env: process.env,
preopens: { preopens: {
'/': '/' [__rootDir]: __rootDir,
} }
}) })

View file

@ -13,7 +13,10 @@
"napi": { "napi": {
"binaryName": "example", "binaryName": "example",
"wasm": { "wasm": {
"initialMemory": 16384 "initialMemory": 16384,
"browser": {
"fs": true
}
}, },
"dtsHeader": "type MaybePromise<T> = T | Promise<T>", "dtsHeader": "type MaybePromise<T> = T | Promise<T>",
"dtsHeaderFile": "./dts-header.d.ts" "dtsHeaderFile": "./dts-header.d.ts"

View file

@ -3,7 +3,7 @@ import {
Kind, Kind,
asyncMultiTwo, asyncMultiTwo,
tsfnReturnPromise, tsfnReturnPromise,
} from './index.wasi-browser' } from './example.wasi-browser'
console.info(new Animal(Kind.Cat, 'Tom')) console.info(new Animal(Kind.Cat, 'Tom'))
asyncMultiTwo(200).then((res) => { asyncMultiTwo(200).then((res) => {

View file

@ -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 }