@bendyline/squisq-video-react 2.5.15 → 2.5.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -220,6 +220,27 @@ available, and otherwise skipped with `audioIncluded: false` plus an
220
220
  completes. GIF skips audio preparation entirely. `supportsWebCodecsH264(config)` probes a specific encoder
221
221
  configuration; `EncoderConfig` and `FfmpegWasmLoadConfig` are also exported.
222
222
 
223
+ ### RNNoise WASM for Vite hosts (recommended)
224
+
225
+ Media-edit denoise uses RNNoise, whose package embeds a 3.6 MB WASM as base64.
226
+ A host that bundles the media-edit worker and its main-thread fallback gets that
227
+ payload twice. Add the plugin to both the page and the worker builds and it
228
+ ships once, as an emitted `rnnoise-*.wasm` fetched on first use:
229
+
230
+ ```ts
231
+ // vite.config.ts
232
+ import { rnnoiseWasmPlugin } from '@bendyline/squisq-video-react/vite';
233
+
234
+ export default defineConfig({
235
+ plugins: [rnnoiseWasmPlugin()],
236
+ worker: { plugins: () => [rnnoiseWasmPlugin()] },
237
+ });
238
+ ```
239
+
240
+ It works for ES and IIFE (Vite's default) worker formats and follows `base`
241
+ the way Vite's own asset URLs do. The fetch must be allowed by `connect-src`.
242
+ Without the plugin, everything still works with the self-contained modules.
243
+
223
244
  ## Audio export
224
245
 
225
246
  `@bendyline/squisq-video-react/encoder` also exports a streaming audio-file
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ import {
30
30
  supportsWebCodecsAac,
31
31
  supportsWebCodecsH264
32
32
  } from "./chunk-RIXBIX3H.js";
33
+ import "./chunk-5MFQMJ5Z.js";
33
34
  import {
34
35
  createMediaEditRenderManager,
35
36
  createMediaEditRenderer,
@@ -38,7 +39,6 @@ import {
38
39
  useProcessedAudio
39
40
  } from "./chunk-TRCTDIWS.js";
40
41
  import "./chunk-TL2IMDIZ.js";
41
- import "./chunk-5MFQMJ5Z.js";
42
42
  export {
43
43
  CoverImageExportModal,
44
44
  DEFAULT_VIDEO_COVER_PRE_ROLL_SECONDS,
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Vite plugin that ships RNNoise's WASM as one file instead of twice in base64.
3
+ *
4
+ * The pinned `@shiguredo/rnnoise-wasm` embeds its 3.6 MB WASM as base64 in
5
+ * JavaScript. A host bundling the media-edit engine gets that payload twice:
6
+ * statically inside the worker, and again in the main-thread fallback's lazy
7
+ * chunk. This plugin extracts it to an emitted `rnnoise.wasm` that both load
8
+ * only when `Rnnoise.load()` runs, keeping the upstream runtime. Hosts add it
9
+ * to `plugins` and to `worker.plugins`; dev servers keep the self-contained
10
+ * module.
11
+ *
12
+ * The URL is written once the output format is known, by Vite's own rule: an
13
+ * absolute `base` names the file outright, a relative one resolves it against
14
+ * the running chunk. `import.meta.ROLLUP_FILE_URL` cannot be used: an IIFE
15
+ * worker (Vite's default) has no `import.meta`, so Vite 8 writes `{}.url` and
16
+ * the fetch throws.
17
+ */
18
+ /** The plugin-context methods this plugin calls (Rollup and Rolldown both provide them). */
19
+ interface RnnoisePluginContext {
20
+ emitFile(file: {
21
+ type: 'asset';
22
+ name: string;
23
+ source: Uint8Array;
24
+ }): string;
25
+ getFileName(referenceId: string): string;
26
+ error(message: string): never;
27
+ }
28
+ /** A Vite plugin, declared structurally so this package needs no Vite dependency. */
29
+ interface RnnoiseWasmPlugin {
30
+ name: string;
31
+ apply: 'build';
32
+ enforce: 'pre';
33
+ configResolved(config: {
34
+ base: string;
35
+ }): void;
36
+ transform(this: RnnoisePluginContext, code: string, id: string): {
37
+ code: string;
38
+ map: null;
39
+ } | null;
40
+ renderChunk(this: RnnoisePluginContext, code: string, chunk: {
41
+ fileName: string;
42
+ }, options: {
43
+ format: string;
44
+ }): {
45
+ code: string;
46
+ map: null;
47
+ } | null;
48
+ }
49
+ declare function rnnoiseWasmPlugin(): RnnoiseWasmPlugin;
50
+
51
+ export { type RnnoiseWasmPlugin, rnnoiseWasmPlugin };
@@ -0,0 +1,72 @@
1
+ // src/vite/rnnoiseWasmPlugin.ts
2
+ var MODULE_SUFFIX = "/@shiguredo/rnnoise-wasm/dist/rnnoise.js";
3
+ var WASM_MAGIC = [0, 97, 115, 109, 1, 0, 0, 0];
4
+ var URL_PLACEHOLDER = "__SQUISQ_RNNOISE_WASM_URL__";
5
+ var SCRIPT_URL = "(typeof document === 'undefined' ? self.location.href : (document.currentScript && document.currentScript.src) || document.baseURI)";
6
+ function rnnoiseWasmPlugin() {
7
+ let base;
8
+ let wasmReference;
9
+ return {
10
+ name: "squisq-rnnoise-wasm-asset",
11
+ apply: "build",
12
+ enforce: "pre",
13
+ configResolved(config) {
14
+ base = config.base;
15
+ },
16
+ transform(code, id) {
17
+ if (!id.replaceAll("\\", "/").endsWith(MODULE_SUFFIX)) return null;
18
+ const factory = /function (\w+)\(\) \{\s*return \w+\("([A-Za-z0-9+/=]{1000,})"\);\s*\}/.exec(
19
+ code
20
+ );
21
+ if (!factory) this.error("RNNoise embedded WASM factory changed; update the asset plugin.");
22
+ const [, name, base64] = factory;
23
+ const load = /static async load\(\) \{/g;
24
+ if ([...code.matchAll(load)].length !== 1) {
25
+ this.error("RNNoise load method changed; update the asset plugin.");
26
+ }
27
+ const bytes = Uint8Array.from(atob(base64), (char) => char.charCodeAt(0));
28
+ if (!WASM_MAGIC.every((byte, index) => bytes[index] === byte)) {
29
+ this.error("RNNoise embedded payload is not a WebAssembly binary.");
30
+ }
31
+ wasmReference = this.emitFile({ type: "asset", name: "rnnoise.wasm", source: bytes });
32
+ return {
33
+ // Fetch before entering Emscripten: a failed request must not leave
34
+ // its initialization promise or dependency-monitor timer running.
35
+ code: `let squisqRnnoiseBytes;
36
+ async function squisqFetchRnnoiseWasm() {
37
+ const response = await fetch(${URL_PLACEHOLDER});
38
+ if (!response.ok) throw new Error('RNNoise WASM load failed: ' + response.status);
39
+ return new Uint8Array(await response.arrayBuffer());
40
+ }
41
+ ` + code.replace(factory[0], `function ${name}() { return squisqRnnoiseBytes; }`).replace(
42
+ load,
43
+ "static async load() { squisqRnnoiseBytes ??= await squisqFetchRnnoiseWasm();"
44
+ ),
45
+ map: null
46
+ };
47
+ },
48
+ renderChunk(code, chunk, options) {
49
+ if (!wasmReference || !code.includes(URL_PLACEHOLDER)) return null;
50
+ const file = this.getFileName(wasmReference);
51
+ return {
52
+ code: code.replaceAll(URL_PLACEHOLDER, wasmUrl(file, chunk.fileName, options.format, base)),
53
+ map: null
54
+ };
55
+ }
56
+ };
57
+ }
58
+ function wasmUrl(file, chunk, format, base) {
59
+ if (base && /^(?:[a-z][a-z\d+.-]*:)?\//i.test(base)) return JSON.stringify(base + file);
60
+ const script = format === "es" ? "import.meta.url" : SCRIPT_URL;
61
+ return `new URL(${JSON.stringify(relativePath(chunk, file))}, ${script}).href`;
62
+ }
63
+ function relativePath(from, to) {
64
+ const fromDirs = from.split("/").slice(0, -1);
65
+ const toParts = to.split("/");
66
+ let shared = 0;
67
+ while (shared < fromDirs.length && fromDirs[shared] === toParts[shared]) shared++;
68
+ return [...fromDirs.slice(shared).map(() => ".."), ...toParts.slice(shared)].join("/");
69
+ }
70
+ export {
71
+ rnnoiseWasmPlugin
72
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq-video-react",
3
- "version": "2.5.15",
3
+ "version": "2.5.17",
4
4
  "description": "React components for browser-based MP4 and animated-GIF export of Squisq documents",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",
@@ -57,6 +57,10 @@
57
57
  "./media-edit": {
58
58
  "types": "./dist/media-edit/index.d.ts",
59
59
  "import": "./dist/media-edit/index.js"
60
+ },
61
+ "./vite": {
62
+ "types": "./dist/vite/index.d.ts",
63
+ "import": "./dist/vite/index.js"
60
64
  }
61
65
  },
62
66
  "files": [
@@ -77,9 +81,9 @@
77
81
  "react-dom": "^18.0.0 || ^19.0.0"
78
82
  },
79
83
  "dependencies": {
80
- "@bendyline/squisq": "2.13.5",
81
- "@bendyline/squisq-video": "2.4.11",
82
- "@bendyline/squisq-react": "2.12.14",
84
+ "@bendyline/squisq": "2.13.6",
85
+ "@bendyline/squisq-video": "2.4.12",
86
+ "@bendyline/squisq-react": "2.12.16",
83
87
  "@ffmpeg/core": "0.12.10",
84
88
  "@ffmpeg/ffmpeg": "0.12.15",
85
89
  "@ffmpeg/util": "0.12.2",