Skip to content

Commit

Permalink
have fetch handle data uris
Browse files Browse the repository at this point in the history
  • Loading branch information
arseneyr committed Nov 6, 2020
1 parent 738acf2 commit 10b0013
Show file tree
Hide file tree
Showing 111 changed files with 2,255 additions and 595 deletions.
1,455 changes: 994 additions & 461 deletions .pnp.cjs

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 2 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ export default {
displayName: "browser",
testEnvironment: "jsdom",
globals: { ...common.globals, __maybeNode__: false },
testPathIgnorePatterns: ["/node_modules/", ".node."],
},
{
...common,
displayName: "node",
testEnvironment: "node",
globals: { ...common.globals, __maybeNode__: true },
testPathIgnorePatterns: ["/node_modules/", ".browser."],
},
],
};
10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,15 @@
"prepack": "run clean && run build:prod"
},
"devDependencies": {
"@babel/core": "^7.10.1",
"@babel/preset-env": "^7.10.1",
"@rollup/plugin-babel": "^5.0.2",
"@babel/core": "^7.12.3",
"@babel/preset-env": "^7.12.1",
"@rollup/plugin-babel": "^5.2.1",
"@rollup/plugin-json": "^4.0.3",
"@rollup/plugin-replace": "^2.3.3",
"@rollup/plugin-url": "^5.0.0",
"@types/jest": "^25.2.3",
"@types/node": "^14.14.5",
"@types/node-fetch": "^2.5.7",
"copyfiles": "^2.3.0",
"cross-env": "^7.0.2",
"jest": "^26.0.1",
Expand All @@ -61,5 +62,8 @@
"tslib": "^2.0.0",
"typescript": "^4.0.5",
"wav": "^1.0.2"
},
"resolutions": {
"node-fetch": "npm:node-fetch@next"
}
}
2 changes: 1 addition & 1 deletion rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ const outputPlugins = [
];

const plugins = (maybeNode) => [
replace({ __maybeNode__: maybeNode }),
url({ include: "**/*.wasm", limit: 1024 * 1024 * 8 }),
json(),
typescript({
transformers: ({ program }) => ({ before: [minifyPrivates(program)] }),
}),
replace({ __maybeNode__: maybeNode }),
babel({ babelHelpers: "bundled" }),
];

Expand Down
16 changes: 9 additions & 7 deletions src/__tests__/common.ts → src/__tests__/common.browser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { enableFetchMocks } from "jest-fetch-mock";
import { promises as fs } from "fs";
import { resolve } from "path";
import { createEncoder, WasmMediaEncoder } from "../encoder";
import { enableFetchMocks } from "jest-fetch-mock";

enableFetchMocks();

Expand All @@ -14,16 +14,16 @@ describe.each([
wasm = await fs.readFile(resolve(__dirname, "../wasm/build", filename));
});
beforeEach(() => {
fetchMock.mockResponse(async (req) => {
if (req.url !== "https://example.com/" + filename) {
throw new Error("Unknown request");
fetchMock?.dontMockIf(/data:application\/wasm;base64/, async (req) => {
if (req.url === "https://example.com/" + filename) {
return (await new Response(wasm)) as any;
}
return (await new Response(wasm)) as any;
if (req.url) throw new Error("Unknown request");
});
});

afterEach(() => {
fetchMock.resetMocks();
fetchMock?.resetMocks();
});

test("Unsupported mimeType", async () => {
Expand All @@ -36,6 +36,7 @@ describe.each([
await expect(
createEncoder(mimeType, `https://example.com/${filename}`)
).resolves.toBeInstanceOf(WasmMediaEncoder);
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock.mock.calls[0][0]).toMatch("example.com");
});

Expand All @@ -45,7 +46,8 @@ describe.each([
await expect(createEncoder(mimeType, dataUri)).resolves.toBeInstanceOf(
WasmMediaEncoder
);
expect(fetchMock).toHaveBeenCalledTimes(0);
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock.mock.calls[0][0]).toEqual(dataUri);
});

test("use buffer", async () => {
Expand Down
68 changes: 68 additions & 0 deletions src/__tests__/common.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { promises as fs } from "fs";
import { resolve } from "path";
import { createEncoder, WasmMediaEncoder } from "../encoder";

describe.each([
["audio/mpeg" as const, "mp3.wasm"],
["audio/ogg" as const, "ogg.wasm"],
])("MIME %s", (mimeType, filename) => {
let wasm: Buffer;
beforeAll(async () => {
wasm = await fs.readFile(resolve(__dirname, "../wasm/build", filename));
});

test("Unsupported mimeType", async () => {
await expect(
createEncoder("video/mpeg" as any, undefined as any)
).rejects.toBeInstanceOf(Error);
});

test("fetch from custom url", async () => {
await expect(
createEncoder(mimeType, `https://example.com/${filename}`)
).rejects.toBeInstanceOf(Error);
});

test("fetch from data uri", async () => {
const dataUri = "data:application/wasm;base64," + wasm.toString("base64");

await expect(createEncoder(mimeType, dataUri)).resolves.toBeInstanceOf(
WasmMediaEncoder
);
});

test("use buffer", async () => {
await expect(createEncoder(mimeType, wasm)).resolves.toBeInstanceOf(
WasmMediaEncoder
);
});

test("use precompiled module", async () => {
const mockCallback = jest.fn();
const module = await WebAssembly.compile(wasm);
await expect(
createEncoder(mimeType, await WebAssembly.compile(wasm), mockCallback)
).resolves.toBeInstanceOf(WasmMediaEncoder);
expect(mockCallback).toHaveBeenCalledWith(module);
});

test("compiled module callback", async () => {
const mockCallback = jest.fn();
await expect(
createEncoder(mimeType, wasm, mockCallback)
).resolves.toBeInstanceOf(WasmMediaEncoder);
expect(mockCallback).toHaveBeenCalledTimes(1);
expect(mockCallback.mock.calls[0][0]).toBeInstanceOf(WebAssembly.Module);
});

test("Buffer reallocation", async () => {
const encoder = await createEncoder(mimeType, wasm);
expect(encoder).toBeInstanceOf(WasmMediaEncoder);
encoder.configure({ sampleRate: 48000, channels: 1 });

// Emscripten initial memory size is 256 pages (64KiB each)
expect(encoder.encode([new Float32Array(16 * 1024 * 256)])).toBeInstanceOf(
Uint8Array
);
});
});
2 changes: 1 addition & 1 deletion src/__tests__/umd.ts → src/__tests__/umd.browser.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import WasmMediaEncoder from "../../dist/umd/WasmMediaEncoder.min";
import { enableFetchMocks } from "jest-fetch-mock";
import { promises as fs } from "fs";
import { resolve } from "path";
import pkg from "../../package.json";
import { enableFetchMocks } from "jest-fetch-mock";

enableFetchMocks();

Expand Down
61 changes: 26 additions & 35 deletions src/wasm/module.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { XOR } from "../utils";

interface IWasmEncoder {
enc_init(sample_rate: number, channel_count: number, params: number): number;
enc_encode(cfg: number, num_samples: number): number;
Expand All @@ -14,35 +16,20 @@ interface IWasmEncoder {
memory: WebAssembly.Memory;
}

function intArrayFromBase64(s: string) {
try {
//@ts-ignore
if (__maybeNode__ && Buffer) {
return Buffer.from(s, "base64");
}
var decoded = atob(s);
var bytes = new Uint8Array(decoded.length);
for (var i = 0; i < decoded.length; ++i) {
bytes[i] = decoded.charCodeAt(i);
}
return bytes;
} catch (_) {
throw new Error("Converting base64 string to bytes failed.");
}
declare global {
const __maybeNode__: boolean;
}

function parseDataUrl(url: string) {
const parts = url.split(",");
if (
parts.length !== 2 ||
/^data:(application\/octet-stream|application\/wasm);base64$/.test(
parts[0]
) === false
/^data:application\/(octet-stream|wasm);base64$/.test(parts[0]) === false
) {
return null;
throw new Error("Passed non-data URI");
}

return intArrayFromBase64(parts[1]).buffer;
return Buffer.from(parts[1], "base64");
}

export default async function (
Expand All @@ -52,26 +39,30 @@ export default async function (
wasi_snapshot_preview1: { proc_exit: () => {} },
env: { emscripten_notify_memory_growth },
};
const wasmBufferOrModule =
typeof wasm === "string"
? parseDataUrl(wasm) ??
(!WebAssembly.instantiateStreaming &&
(await (await fetch(wasm)).arrayBuffer()))
: wasm;

const output = await (wasmBufferOrModule
? WebAssembly.instantiate(wasmBufferOrModule, imports)
: WebAssembly.instantiateStreaming(fetch(wasm as string), imports));
if (typeof wasm === "string" && !WebAssembly.instantiateStreaming) {
wasm =
__maybeNode__ && typeof fetch === "undefined"
? await parseDataUrl(wasm)
: await (await fetch(wasm)).arrayBuffer();
}

const output = (await (typeof wasm === "string"
? WebAssembly.instantiateStreaming(fetch(wasm), imports)
: WebAssembly.instantiate(wasm, imports))) as XOR<
{
instance: WebAssembly.Instance;
module: WebAssembly.Module;
},
WebAssembly.Instance
>;

const { memory, _initialize, ...rest } = ((output as any).instance || output)
.exports as IWasmEncoder;
const { memory, _initialize, ...rest } = ((output.instance || output)
.exports as unknown) as IWasmEncoder;

const ret = {
...rest,
module:
wasm instanceof WebAssembly.Module
? wasm
: (output as { module?: WebAssembly.Module }).module,
module: output.module || wasm,
};

function emscripten_notify_memory_growth() {
Expand Down
Loading

0 comments on commit 10b0013

Please sign in to comment.