From 58e94edce98f3894505b6f8eebc8964240892c9d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 8 Jan 2025 11:33:47 +0000 Subject: [PATCH 1/6] Fix splitbrain between `.js` being ESM & CJS inconsistently --- babel.config.js => babel.config.cjs | 0 index-wasm-esm.mjs => index-wasm-esm.js | 0 index.mjs => index.cjs | 26 ++++++++++----------- index.js | 26 +++++++++++---------- node.mjs => node.cjs | 31 ++++++++++++++----------- node.js | 31 +++++++++++-------------- package.json | 11 +++++---- 7 files changed, 63 insertions(+), 62 deletions(-) rename babel.config.js => babel.config.cjs (100%) rename index-wasm-esm.mjs => index-wasm-esm.js (100%) rename index.mjs => index.cjs (68%) rename node.mjs => node.cjs (81%) diff --git a/babel.config.js b/babel.config.cjs similarity index 100% rename from babel.config.js rename to babel.config.cjs diff --git a/index-wasm-esm.mjs b/index-wasm-esm.js similarity index 100% rename from index-wasm-esm.mjs rename to index-wasm-esm.js diff --git a/index.mjs b/index.cjs similarity index 68% rename from index.mjs rename to index.cjs index b792f114f..9b27f4e02 100644 --- a/index.mjs +++ b/index.cjs @@ -15,21 +15,16 @@ // @ts-check /** - * This is the entrypoint on non-node ESM environments. + * This is the entrypoint on non-node CommonJS environments. * `asyncLoad` will load the WASM module using a `fetch` call. */ -import * as bindings from "./pkg/matrix_sdk_crypto_wasm_bg.js"; +const bindings = require("./pkg/matrix_sdk_crypto_wasm_bg.cjs"); -const moduleUrl = new URL("./pkg/matrix_sdk_crypto_wasm_bg.wasm", import.meta.url); +const moduleUrl = require.resolve("./pkg/matrix_sdk_crypto_wasm_bg.wasm"); -// Although we could simply instantiate the WASM at import time with a top-level `await`, -// we avoid that, to make it easier for callers to delay loading the WASM (and instead -// wait until `initAsync` is called). (Also, Safari 14 doesn't support top-level `await`.) -// -// However, having done so, there is no way to synchronously load the WASM if the user ends -// up using the bindings before calling `initAsync` (unlike under Node.js), so we just throw -// an error. +// We want to throw an error if the user tries to use the bindings before +// calling `initAsync`. bindings.__wbg_set_wasm( new Proxy( {}, @@ -44,7 +39,7 @@ bindings.__wbg_set_wasm( ); /** - * Stores a promise of the `loadModuleAsync` call + * Stores a promise of the `loadModule` call * @type {Promise | null} */ let modPromise = null; @@ -72,10 +67,13 @@ async function loadModuleAsync() { * * @returns {Promise} */ -export async function initAsync() { +async function initAsync() { if (!modPromise) modPromise = loadModuleAsync(); await modPromise; } -// Re-export everything from the generated javascript wrappers -export * from "./pkg/matrix_sdk_crypto_wasm_bg.js"; +module.exports = { + // Re-export everything from the generated javascript wrappers + ...bindings, + initAsync, +}; diff --git a/index.js b/index.js index 9b27f4e02..b792f114f 100644 --- a/index.js +++ b/index.js @@ -15,16 +15,21 @@ // @ts-check /** - * This is the entrypoint on non-node CommonJS environments. + * This is the entrypoint on non-node ESM environments. * `asyncLoad` will load the WASM module using a `fetch` call. */ -const bindings = require("./pkg/matrix_sdk_crypto_wasm_bg.cjs"); +import * as bindings from "./pkg/matrix_sdk_crypto_wasm_bg.js"; -const moduleUrl = require.resolve("./pkg/matrix_sdk_crypto_wasm_bg.wasm"); +const moduleUrl = new URL("./pkg/matrix_sdk_crypto_wasm_bg.wasm", import.meta.url); -// We want to throw an error if the user tries to use the bindings before -// calling `initAsync`. +// Although we could simply instantiate the WASM at import time with a top-level `await`, +// we avoid that, to make it easier for callers to delay loading the WASM (and instead +// wait until `initAsync` is called). (Also, Safari 14 doesn't support top-level `await`.) +// +// However, having done so, there is no way to synchronously load the WASM if the user ends +// up using the bindings before calling `initAsync` (unlike under Node.js), so we just throw +// an error. bindings.__wbg_set_wasm( new Proxy( {}, @@ -39,7 +44,7 @@ bindings.__wbg_set_wasm( ); /** - * Stores a promise of the `loadModule` call + * Stores a promise of the `loadModuleAsync` call * @type {Promise | null} */ let modPromise = null; @@ -67,13 +72,10 @@ async function loadModuleAsync() { * * @returns {Promise} */ -async function initAsync() { +export async function initAsync() { if (!modPromise) modPromise = loadModuleAsync(); await modPromise; } -module.exports = { - // Re-export everything from the generated javascript wrappers - ...bindings, - initAsync, -}; +// Re-export everything from the generated javascript wrappers +export * from "./pkg/matrix_sdk_crypto_wasm_bg.js"; diff --git a/node.mjs b/node.cjs similarity index 81% rename from node.mjs rename to node.cjs index 65c9cca4d..190b4fd2e 100644 --- a/node.mjs +++ b/node.cjs @@ -15,16 +15,16 @@ // @ts-check /** - * This is the entrypoint on node-compatible ESM environments. + * This is the entrypoint on node-compatible CommonJS environments. * `asyncLoad` will use `fs.readFile` to load the WASM module. */ -import { fileURLToPath } from "node:url"; -import { readFileSync } from "node:fs"; -import { readFile } from "node:fs/promises"; -import * as bindings from "./pkg/matrix_sdk_crypto_wasm_bg.js"; +const { readFileSync } = require("node:fs"); +const { readFile } = require("node:fs/promises"); +const path = require("node:path"); +const bindings = require("./pkg/matrix_sdk_crypto_wasm_bg.cjs"); -const filename = fileURLToPath(new URL("./pkg/matrix_sdk_crypto_wasm_bg.wasm", import.meta.url)); +const filename = path.join(__dirname, "pkg/matrix_sdk_crypto_wasm_bg.wasm"); // In node environments, we want to automatically load the WASM module // synchronously if the consumer did not call `initAsync`. To do so, we install @@ -59,7 +59,7 @@ let initialised = false; * * It will throw if there is an attempt to load the module asynchronously running * - * @returns {typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d.ts")} + * @returns {typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d")} */ function loadModuleSync() { if (modPromise) throw new Error("The WASM module is being loaded asynchronously but hasn't finished"); @@ -73,14 +73,14 @@ function loadModuleSync() { initInstance(instance); - // @ts-expect-error: Typescript doesn't know what the module exports are + // @ts-expect-error: Typescript doesn't know what the instance exports exactly return instance.exports; } /** * Loads and instantiates the WASM module asynchronously * - * @returns {Promise} + * @returns {Promise} */ async function loadModuleAsync() { const bytes = await readFile(filename); @@ -91,7 +91,7 @@ async function loadModuleAsync() { initInstance(instance); - // @ts-expect-error: Typescript doesn't know what the module exports are + // @ts-expect-error: Typescript doesn't know what the instance exports exactly return instance.exports; } @@ -103,7 +103,7 @@ async function loadModuleAsync() { function initInstance(instance) { if (initialised) throw new Error("initInstance called twice"); bindings.__wbg_set_wasm(instance.exports); - // @ts-expect-error: Typescript doesn't know what the module exports are + // @ts-expect-error: Typescript doesn't know what the instance exports exactly instance.exports.__wbindgen_start(); initialised = true; } @@ -115,11 +115,14 @@ function initInstance(instance) { * * @returns {Promise} */ -export async function initAsync() { +async function initAsync() { if (initialised) return; if (!modPromise) modPromise = loadModuleAsync(); await modPromise; } -// Re-export everything from the generated javascript wrappers -export * from "./pkg/matrix_sdk_crypto_wasm_bg.js"; +module.exports = { + // Re-export everything from the generated javascript wrappers + ...bindings, + initAsync, +}; diff --git a/node.js b/node.js index 190b4fd2e..65c9cca4d 100644 --- a/node.js +++ b/node.js @@ -15,16 +15,16 @@ // @ts-check /** - * This is the entrypoint on node-compatible CommonJS environments. + * This is the entrypoint on node-compatible ESM environments. * `asyncLoad` will use `fs.readFile` to load the WASM module. */ -const { readFileSync } = require("node:fs"); -const { readFile } = require("node:fs/promises"); -const path = require("node:path"); -const bindings = require("./pkg/matrix_sdk_crypto_wasm_bg.cjs"); +import { fileURLToPath } from "node:url"; +import { readFileSync } from "node:fs"; +import { readFile } from "node:fs/promises"; +import * as bindings from "./pkg/matrix_sdk_crypto_wasm_bg.js"; -const filename = path.join(__dirname, "pkg/matrix_sdk_crypto_wasm_bg.wasm"); +const filename = fileURLToPath(new URL("./pkg/matrix_sdk_crypto_wasm_bg.wasm", import.meta.url)); // In node environments, we want to automatically load the WASM module // synchronously if the consumer did not call `initAsync`. To do so, we install @@ -59,7 +59,7 @@ let initialised = false; * * It will throw if there is an attempt to load the module asynchronously running * - * @returns {typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d")} + * @returns {typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d.ts")} */ function loadModuleSync() { if (modPromise) throw new Error("The WASM module is being loaded asynchronously but hasn't finished"); @@ -73,14 +73,14 @@ function loadModuleSync() { initInstance(instance); - // @ts-expect-error: Typescript doesn't know what the instance exports exactly + // @ts-expect-error: Typescript doesn't know what the module exports are return instance.exports; } /** * Loads and instantiates the WASM module asynchronously * - * @returns {Promise} + * @returns {Promise} */ async function loadModuleAsync() { const bytes = await readFile(filename); @@ -91,7 +91,7 @@ async function loadModuleAsync() { initInstance(instance); - // @ts-expect-error: Typescript doesn't know what the instance exports exactly + // @ts-expect-error: Typescript doesn't know what the module exports are return instance.exports; } @@ -103,7 +103,7 @@ async function loadModuleAsync() { function initInstance(instance) { if (initialised) throw new Error("initInstance called twice"); bindings.__wbg_set_wasm(instance.exports); - // @ts-expect-error: Typescript doesn't know what the instance exports exactly + // @ts-expect-error: Typescript doesn't know what the module exports are instance.exports.__wbindgen_start(); initialised = true; } @@ -115,14 +115,11 @@ function initInstance(instance) { * * @returns {Promise} */ -async function initAsync() { +export async function initAsync() { if (initialised) return; if (!modPromise) modPromise = loadModuleAsync(); await modPromise; } -module.exports = { - // Re-export everything from the generated javascript wrappers - ...bindings, - initAsync, -}; +// Re-export everything from the generated javascript wrappers +export * from "./pkg/matrix_sdk_crypto_wasm_bg.js"; diff --git a/package.json b/package.json index cbd7a27a0..4ce4ddaa1 100644 --- a/package.json +++ b/package.json @@ -18,21 +18,22 @@ "ruma", "nio" ], + "type": "module", "exports": { ".": { "matrix-org:wasm-esm": { "types": "./index.d.ts", - "default": "./index-wasm-esm.mjs" + "default": "./index-wasm-esm.js" }, "require": { "types": "./index.d.ts", - "node": "./node.js", - "default": "./index.js" + "node": "./node.cjs", + "default": "./index.cjs" }, "import": { "types": "./index.d.ts", - "node": "./node.mjs", - "default": "./index.mjs" + "node": "./node.js", + "default": "./index.js" } } }, From 1da99ad317e3bbd3d25c7f24ca798dc26ccd7832 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 8 Jan 2025 11:55:28 +0000 Subject: [PATCH 2/6] Iterate --- .eslintrc.cjs | 1 + index.d.ts | 2 +- node.cjs | 4 ++-- package.json | 6 +++--- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index e5bc5ed74..447bc0ef9 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,6 +1,7 @@ module.exports = { parserOptions: { ecmaVersion: 2020, + sourceType: "module", }, rules: { camelcase: ["error", { properties: "never" }], diff --git a/index.d.ts b/index.d.ts index 20c7dc8b2..4d07fd0f1 100644 --- a/index.d.ts +++ b/index.d.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -export * from "./pkg/matrix_sdk_crypto_wasm.d"; +export * from "./pkg/matrix_sdk_crypto_wasm.d.ts"; /** * Load the WebAssembly module in the background, if it has not already been loaded. diff --git a/node.cjs b/node.cjs index 190b4fd2e..2e67fb393 100644 --- a/node.cjs +++ b/node.cjs @@ -59,7 +59,7 @@ let initialised = false; * * It will throw if there is an attempt to load the module asynchronously running * - * @returns {typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d")} + * @returns {typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d", { with: { "resolution-mode": "require" } })} */ function loadModuleSync() { if (modPromise) throw new Error("The WASM module is being loaded asynchronously but hasn't finished"); @@ -80,7 +80,7 @@ function loadModuleSync() { /** * Loads and instantiates the WASM module asynchronously * - * @returns {Promise} + * @returns {Promise} */ async function loadModuleAsync() { const bytes = await readFile(filename); diff --git a/package.json b/package.json index 4ce4ddaa1..9e0a8368d 100644 --- a/package.json +++ b/package.json @@ -38,12 +38,12 @@ } }, "files": [ - "index.mjs", "index.js", - "index-wasm-esm.mjs", + "index.cjs", + "index-wasm-esm.js", "index.d.ts", - "node.mjs", "node.js", + "node.cjs", "pkg/matrix_sdk_crypto_wasm.d.ts", "pkg/matrix_sdk_crypto_wasm_bg.js", "pkg/matrix_sdk_crypto_wasm_bg.cjs", From bd3f07e304111d948b3a711fbc2f445157a08678 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 8 Jan 2025 12:07:10 +0000 Subject: [PATCH 3/6] Make wasm-pack test happy --- src/encryption.rs | 1 + src/requests.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/encryption.rs b/src/encryption.rs index 55a429708..72365f124 100644 --- a/src/encryption.rs +++ b/src/encryption.rs @@ -294,6 +294,7 @@ impl From for ShieldState { #[cfg(test)] pub(crate) mod tests { use wasm_bindgen_test::wasm_bindgen_test; + wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_node_experimental); use super::EncryptionAlgorithm; diff --git a/src/requests.rs b/src/requests.rs index ac01a6af8..55963381b 100644 --- a/src/requests.rs +++ b/src/requests.rs @@ -717,6 +717,7 @@ pub(crate) mod tests { use matrix_sdk_crypto::types::requests::KeysQueryRequest as OriginalKeysQueryRequest; use serde_json::Value; use wasm_bindgen_test::wasm_bindgen_test; + wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_node_experimental); use super::{KeysClaimRequest, KeysQueryRequest, KeysUploadRequest}; From 5d2f778ad76a075c6188917eaa9cd1f126b0bd56 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 8 Jan 2025 12:35:46 +0000 Subject: [PATCH 4/6] Use mjs/cjs explicitly --- index-wasm-esm.js => index-wasm-esm.mjs | 0 index.js => index.mjs | 0 node.js => node.mjs | 0 package.json | 12 ++++++------ 4 files changed, 6 insertions(+), 6 deletions(-) rename index-wasm-esm.js => index-wasm-esm.mjs (100%) rename index.js => index.mjs (100%) rename node.js => node.mjs (100%) diff --git a/index-wasm-esm.js b/index-wasm-esm.mjs similarity index 100% rename from index-wasm-esm.js rename to index-wasm-esm.mjs diff --git a/index.js b/index.mjs similarity index 100% rename from index.js rename to index.mjs diff --git a/node.js b/node.mjs similarity index 100% rename from node.js rename to node.mjs diff --git a/package.json b/package.json index 9e0a8368d..888b0848b 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ ".": { "matrix-org:wasm-esm": { "types": "./index.d.ts", - "default": "./index-wasm-esm.js" + "default": "./index-wasm-esm.mjs" }, "require": { "types": "./index.d.ts", @@ -32,17 +32,17 @@ }, "import": { "types": "./index.d.ts", - "node": "./node.js", - "default": "./index.js" + "node": "./node.mjs", + "default": "./index.mjs" } } }, "files": [ - "index.js", + "index.mjs", "index.cjs", - "index-wasm-esm.js", + "index-wasm-esm.mjs", "index.d.ts", - "node.js", + "node.mjs", "node.cjs", "pkg/matrix_sdk_crypto_wasm.d.ts", "pkg/matrix_sdk_crypto_wasm_bg.js", From d94500a91015ef8ae009dcb2f796b6d496205fab Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 9 Jan 2025 17:30:05 +0000 Subject: [PATCH 5/6] Add changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aab9d31e9..6c9172c06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # UNRELEASED +- The published package now uses type=module which defaults js/ts files to ESM. + **BREAKING CHANGES** - Update matrix-rusk-sdk to `0.9.0`. From b50c3547be945e47b6bc97a7c5af01819b9540bf Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 13 Jan 2025 12:31:13 +0000 Subject: [PATCH 6/6] Update CHANGELOG.md Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c9172c06..6987f6c15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # UNRELEASED -- The published package now uses type=module which defaults js/ts files to ESM. +- Fix a problem, introduced in v12.0.0, when importing the published package as an ESM module, in which some files could be incorrectly interpreted as CommonJS, leading to syntax errors. + ([#189](https://github.com/matrix-org/matrix-rust-sdk-crypto-wasm/pull/189)) **BREAKING CHANGES**