Skip to content

Commit

Permalink
Merge pull request #30 from element-hq/t3chguy/wasm-asset
Browse files Browse the repository at this point in the history
  • Loading branch information
t3chguy authored Jan 2, 2025
2 parents 5f65cbc + f54ebcc commit 161c24d
Show file tree
Hide file tree
Showing 35 changed files with 752 additions and 271 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- [Web] Add typescript checking to bindings/wysiwyg-wasm.
- [Web] Update vite and related packages.
- [Web] Simplify build scripts.
- [Web] Publish a dual CJS/ESM package with platform-specific loaders.

# [2.37.14]
- [Android] Have separate modes for parsing HTML for 'editor mode' and 'message mode' using `isEditor: Boolean` parameter.
Expand Down
11 changes: 2 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,8 @@ ios: targets-ios
web:
cd bindings/wysiwyg-wasm && \
yarn && \
yarn build && \
mkdir -p ../../platforms/web/generated && \
cp \
pkg/wysiwyg_bg.wasm \
pkg/wysiwyg_bg.wasm.d.ts \
pkg/wysiwyg.d.ts \
pkg/wysiwyg.js \
../../platforms/web/generated/
cd platforms/web && yarn install && yarn build
yarn build
cd platforms/web && yarn && yarn build

web-format:
cd platforms/web && \
Expand Down
27 changes: 18 additions & 9 deletions bindings/wysiwyg-wasm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ WASM/JavaScript bindings for wysiwyg-rust.

```sh
cd bindings/wysiwyg-wasm
npm install
npm run build
#npm run test (no tests yet)
yarn
yarn build
#yarn test (no tests yet)
```

This will generate:
Expand All @@ -26,15 +26,24 @@ pkg/matrix_sdk_wysiwyg.js
... plus other files
```

These files should be copied into a web project and imported with code like:
You can then consume these files in your project by linking the package in your package.json:

```json
{
"dependencies": {
"@matrix-org/matrix-sdk-wysiwyg-wasm": "link:../../bindings/wysiwyg-wasm"
}
}
```

And consume with code like this:

```html
<script type="module">
import init, { some_method_from_rust }
from './generated/matrix_sdk_wysiwyg.js';
import { initAsync, some_method_from_rust } from '@matrix-org/matrix-sdk-wysiwyg-wasm';
async function run() {
await init();
await initAsync();
some_method_from_rust();
}
Expand All @@ -45,8 +54,8 @@ run();
## Profiling

To generate a debugging/profiling Wasm module, use the following command
instead of `npm run build`:
instead of `yarn build`:

```sh
$ npm run dev-build
$ yarn dev-build
```
64 changes: 64 additions & 0 deletions bindings/wysiwyg-wasm/index-wasm-esm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/

// @ts-check

/**
* This is the entrypoint on non-node ESM environments which support the ES Module Integration Proposal for WebAssembly [1]
* (such as Element Web).
*
* [1]: https://github.com/webassembly/esm-integration
*/

import * as bindings from './pkg/wysiwyg_bg.js';

// We want to throw an error if the user tries to use the bindings before
// calling `initAsync`.
bindings.__wbg_set_wasm(
new Proxy(
{},
{
get() {
throw new Error(
'@element-hq/matrix-wysiwyg was used before it was initialized. Call `initAsync` first.',
);
},
},
),
);

/**
* Stores a promise of the `loadModule` call
* @type {Promise<void> | null}
*/
let modPromise = null;

/**
* Loads the WASM module asynchronously
*
* @returns {Promise<void>}
*/
async function loadModule() {
const wasm = await import('./pkg/wysiwyg_bg.wasm');
bindings.__wbg_set_wasm(wasm);
wasm.__wbindgen_start();
}

/**
* Load the WebAssembly module in the background, if it has not already been loaded.
*
* Returns a promise which will resolve once the other methods are ready.
*
* @returns {Promise<void>}
*/
export async function initAsync() {
if (!modPromise) modPromise = loadModule();
await modPromise;
}

// Re-export everything from the generated javascript wrappers
export * from './pkg/wysiwyg_bg.js';
85 changes: 85 additions & 0 deletions bindings/wysiwyg-wasm/index.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/

// @ts-check

/**
* This is the entrypoint on non-node CommonJS environments.
* `initAsync` will load the WASM module using a `fetch` call.
*/

const bindings = require("./pkg/wysiwyg_bg.cjs");

const moduleUrl = require.resolve("./pkg/wysiwyg_bg.wasm");

// We want to throw an error if the user tries to use the bindings before
// calling `initAsync`.
bindings.__wbg_set_wasm(
new Proxy(
{},
{
get() {
throw new Error(
'@element-hq/matrix-wysiwyg was used before it was initialized. Call `initAsync` first.',
);
},
},
),
);

/**
* Stores a promise of the `loadModule` call
* @type {Promise<void> | null}
*/
let modPromise = null;

/**
* Loads the WASM module asynchronously
*
* @returns {Promise<void>}
*/
async function loadModule() {
let mod;
if (typeof WebAssembly.compileStreaming === 'function') {
mod = await WebAssembly.compileStreaming(fetch(moduleUrl));
} else {
// Fallback to fetch and compile
const response = await fetch(moduleUrl);
if (!response.ok) {
throw new Error(`Failed to fetch wasm module: ${moduleUrl}`);
}
const bytes = await response.arrayBuffer();
mod = await WebAssembly.compile(bytes);
}

/** @type {{exports: typeof import("./pkg/wysiwyg_bg.wasm.d.ts")}} */
// @ts-expect-error: Typescript doesn't know what the instance exports exactly
const instance = await WebAssembly.instantiate(mod, {
'./wysiwyg_bg.js': bindings,
});

bindings.__wbg_set_wasm(instance.exports);
instance.exports.__wbindgen_start();
}

/**
* Load the WebAssembly module in the background, if it has not already been loaded.
*
* Returns a promise which will resolve once the other methods are ready.
*
* @returns {Promise<void>}
*/
async function initAsync() {
if (!modPromise) modPromise = loadModule();
await modPromise;
}

module.exports = {
// Re-export everything from the generated javascript wrappers
...bindings,
initAsync,
};
17 changes: 17 additions & 0 deletions bindings/wysiwyg-wasm/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/

export * from './pkg/wysiwyg.d';

/**
* Load the WebAssembly module in the background, if it has not already been loaded.
*
* Returns a promise which will resolve once the other methods are ready.
*
* @returns {Promise<void>}
*/
export function initAsync(): Promise<void>;
85 changes: 85 additions & 0 deletions bindings/wysiwyg-wasm/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/

// @ts-check

/**
* This is the entrypoint on non-node ESM environments.
* `initAsync` will load the WASM module using a `fetch` call.
*/

import * as bindings from './pkg/wysiwyg_bg.js';

const moduleUrl = new URL(
'./pkg/wysiwyg_bg.wasm?url',
import.meta.url,
);

// We want to throw an error if the user tries to use the bindings before
// calling `initAsync`.
bindings.__wbg_set_wasm(
new Proxy(
{},
{
get() {
throw new Error(
'@element-hq/matrix-wysiwyg was used before it was initialized. Call `initAsync` first.',
);
},
},
),
);

/**
* Stores a promise of the `loadModule` call
* @type {Promise<void> | null}
*/
let modPromise = null;

/**
* Loads the WASM module asynchronously
*
* @returns {Promise<void>}
*/
async function loadModule() {
let mod;
if (typeof WebAssembly.compileStreaming === 'function') {
mod = await WebAssembly.compileStreaming(fetch(moduleUrl));
} else {
// Fallback to fetch and compile
const response = await fetch(moduleUrl);
if (!response.ok) {
throw new Error(`Failed to fetch wasm module: ${moduleUrl}`);
}
const bytes = await response.arrayBuffer();
mod = await WebAssembly.compile(bytes);
}

/** @type {{exports: typeof import("./pkg/wysiwyg_bg.wasm.d.ts")}} */
// @ts-expect-error: Typescript doesn't know what the instance exports exactly
const instance = await WebAssembly.instantiate(mod, {
'./wysiwyg_bg.js': bindings,
});

bindings.__wbg_set_wasm(instance.exports);
instance.exports.__wbindgen_start();
}

/**
* Load the WebAssembly module in the background, if it has not already been loaded.
*
* Returns a promise which will resolve once the other methods are ready.
*
* @returns {Promise<void>}
*/
export async function initAsync() {
if (!modPromise) modPromise = loadModule();
await modPromise;
}

// Re-export everything from the generated javascript wrappers
export * from './pkg/wysiwyg_bg.js';
Loading

0 comments on commit 161c24d

Please sign in to comment.