diff --git a/__mocks__/workerFactoryMock.js b/__mocks__/workerFactoryMock.js new file mode 100644 index 0000000000..56dc085cd3 --- /dev/null +++ b/__mocks__/workerFactoryMock.js @@ -0,0 +1,19 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +export default function workerFactory(options) { + return jest.fn; +} diff --git a/__mocks__/workerMock.js b/__mocks__/workerMock.js deleted file mode 100644 index 6ee585673e..0000000000 --- a/__mocks__/workerMock.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = jest.fn(); diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json index d8a522d417..a7f97875ec 100644 --- a/cypress/tsconfig.json +++ b/cypress/tsconfig.json @@ -7,7 +7,7 @@ "resolveJsonModule": true, "esModuleInterop": true, "moduleResolution": "node", - "module": "commonjs" + "module": "es2022" }, "include": ["**/*.ts"] } diff --git a/jest.config.ts b/jest.config.ts index 58bec7684e..4c62324bcc 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -31,9 +31,9 @@ const config: Config = { "decoderWorker\\.min\\.js": "/__mocks__/empty.js", "decoderWorker\\.min\\.wasm": "/__mocks__/empty.js", "waveWorker\\.min\\.js": "/__mocks__/empty.js", - "workers/(.+)\\.worker\\.ts": "/__mocks__/workerMock.js", + "workers/(.+)Factory": "/__mocks__/workerFactoryMock.js", "^!!raw-loader!.*": "jest-raw-loader", - "RecorderWorklet": "/__mocks__/empty.js", + "recorderWorkletFactory": "/__mocks__/empty.js", }, transformIgnorePatterns: ["/node_modules/(?!matrix-js-sdk).+$"], collectCoverageFrom: [ diff --git a/src/BlurhashEncoder.ts b/src/BlurhashEncoder.ts index 89ed2b56e5..457fe01e7d 100644 --- a/src/BlurhashEncoder.ts +++ b/src/BlurhashEncoder.ts @@ -15,8 +15,9 @@ limitations under the License. */ // @ts-ignore - `.ts` is needed here to make TS happy -import BlurhashWorker, { Request, Response } from "./workers/blurhash.worker.ts"; +import { Request, Response } from "./workers/blurhash.worker.ts"; import { WorkerManager } from "./WorkerManager"; +import blurhashWorkerFactory from "./workers/blurhashWorkerFactory"; export class BlurhashEncoder { private static internalInstance = new BlurhashEncoder(); @@ -25,7 +26,7 @@ export class BlurhashEncoder { return BlurhashEncoder.internalInstance; } - private readonly worker = new WorkerManager(BlurhashWorker); + private readonly worker = new WorkerManager(blurhashWorkerFactory()); public getBlurhash(imageData: ImageData): Promise { return this.worker.call({ imageData }).then((resp) => resp.blurhash); diff --git a/src/WorkerManager.ts b/src/WorkerManager.ts index 5dcb56c610..2ad5191b24 100644 --- a/src/WorkerManager.ts +++ b/src/WorkerManager.ts @@ -23,8 +23,8 @@ export class WorkerManager { private seq = 0; private pendingDeferredMap = new Map>(); - public constructor(WorkerConstructor: { new (): Worker }) { - this.worker = new WorkerConstructor(); + public constructor(worker: Worker) { + this.worker = worker; this.worker.onmessage = this.onMessage; } diff --git a/src/audio/Playback.ts b/src/audio/Playback.ts index 43cbf904c0..a40e0a463c 100644 --- a/src/audio/Playback.ts +++ b/src/audio/Playback.ts @@ -20,7 +20,7 @@ import { logger } from "matrix-js-sdk/src/logger"; import { defer } from "matrix-js-sdk/src/utils"; // @ts-ignore - `.ts` is needed here to make TS happy -import PlaybackWorker, { Request, Response } from "../workers/playback.worker.ts"; +import { Request, Response } from "../workers/playback.worker.ts"; import { UPDATE_EVENT } from "../stores/AsyncStore"; import { arrayFastResample } from "../utils/arrays"; import { IDestroyable } from "../utils/IDestroyable"; @@ -29,6 +29,7 @@ import { createAudioContext, decodeOgg } from "./compat"; import { clamp } from "../utils/numbers"; import { WorkerManager } from "../WorkerManager"; import { DEFAULT_WAVEFORM, PLAYBACK_WAVEFORM_SAMPLES } from "./consts"; +import playbackWorkerFactory from "../workers/playbackWorkerFactory"; export enum PlaybackState { Decoding = "decoding", @@ -63,7 +64,7 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte private waveformObservable = new SimpleObservable(); private readonly clock: PlaybackClock; private readonly fileSize: number; - private readonly worker = new WorkerManager(PlaybackWorker); + private readonly worker = new WorkerManager(playbackWorkerFactory()); /** * Creates a new playback instance from a buffer. diff --git a/src/audio/VoiceRecording.ts b/src/audio/VoiceRecording.ts index 2e1f5634fb..9f12dc33f7 100644 --- a/src/audio/VoiceRecording.ts +++ b/src/audio/VoiceRecording.ts @@ -28,7 +28,7 @@ import { UPDATE_EVENT } from "../stores/AsyncStore"; import { createAudioContext } from "./compat"; import { FixedRollingArray } from "../utils/FixedRollingArray"; import { clamp } from "../utils/numbers"; -import mxRecorderWorkletPath from "./RecorderWorklet"; +import recorderWorkletFactory from "./recorderWorkletFactory"; const CHANNELS = 1; // stereo isn't important export const SAMPLE_RATE = 48000; // 48khz is what WebRTC uses. 12khz is where we lose quality. @@ -129,7 +129,8 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { if (this.recorderContext.audioWorklet) { // Set up our worklet. We use this for timing information and waveform analysis: the // web audio API prefers this be done async to avoid holding the main thread with math. - await this.recorderContext.audioWorklet.addModule(mxRecorderWorkletPath); + await recorderWorkletFactory(this.recorderContext); + this.recorderWorklet = new AudioWorkletNode(this.recorderContext, WORKLET_NAME); this.recorderSource.connect(this.recorderWorklet); this.recorderWorklet.connect(this.recorderContext.destination); diff --git a/src/audio/recorderWorkletFactory.ts b/src/audio/recorderWorkletFactory.ts new file mode 100644 index 0000000000..0a0c15a121 --- /dev/null +++ b/src/audio/recorderWorkletFactory.ts @@ -0,0 +1,24 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import mxRecorderWorkletPath from "./RecorderWorklet"; + +export default function recorderWorkletFactory(context: AudioContext): Promise { + // In future we should be using the built-in worklet support in Webpack 5 with the syntax + // described in https://github.com/webpack/webpack.js.org/issues/6869: + // addModule(/* webpackChunkName: "recorder.worklet" */ new URL("./RecorderWorklet.ts", import.meta.url)); + return context.audioWorklet.addModule(mxRecorderWorkletPath); +} diff --git a/src/utils/createMatrixClient.ts b/src/utils/createMatrixClient.ts index 1bcfcba19e..cdc9616426 100644 --- a/src/utils/createMatrixClient.ts +++ b/src/utils/createMatrixClient.ts @@ -24,8 +24,7 @@ import { LocalStorageCryptoStore, } from "matrix-js-sdk/src/matrix"; -// @ts-ignore - `.ts` is needed here to make TS happy -import IndexedDBWorker from "../workers/indexeddb.worker.ts"; +import indexeddbWorkerFactory from "../workers/indexeddbWorkerFactory"; const localStorage = window.localStorage; @@ -55,7 +54,7 @@ export default function createMatrixClient(opts: ICreateClientOpts): MatrixClien indexedDB: indexedDB, dbName: "riot-web-sync", localStorage, - workerFactory: () => new IndexedDBWorker(), + workerFactory: indexeddbWorkerFactory, }); } else if (localStorage) { storeOpts.store = new MemoryStore({ localStorage }); diff --git a/src/workers/blurhashWorkerFactory.ts b/src/workers/blurhashWorkerFactory.ts new file mode 100644 index 0000000000..17e3af2fba --- /dev/null +++ b/src/workers/blurhashWorkerFactory.ts @@ -0,0 +1,22 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +export default function factory(options?: WorkerOptions | undefined): Worker { + return new Worker( + /* webpackChunkName: "blurhash.worker" */ new URL("./blurhash.worker.ts", import.meta.url), + options, + ); +} diff --git a/src/workers/indexeddbWorkerFactory.ts b/src/workers/indexeddbWorkerFactory.ts new file mode 100644 index 0000000000..46de5cfe3c --- /dev/null +++ b/src/workers/indexeddbWorkerFactory.ts @@ -0,0 +1,22 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +export default function factory(options?: WorkerOptions | undefined): Worker { + return new Worker( + /* webpackChunkName: "indexeddb.worker" */ new URL("./indexeddb.worker.ts", import.meta.url), + options, + ); +} diff --git a/src/workers/playbackWorkerFactory.ts b/src/workers/playbackWorkerFactory.ts new file mode 100644 index 0000000000..091b426ef9 --- /dev/null +++ b/src/workers/playbackWorkerFactory.ts @@ -0,0 +1,22 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +export default function factory(options?: WorkerOptions | undefined): Worker { + return new Worker( + /* webpackChunkName: "playback.worker" */ new URL("./playback.worker.ts", import.meta.url), + options, + ); +} diff --git a/test/WorkerManager-test.ts b/test/WorkerManager-test.ts index 39057ee04d..8d02c0619a 100644 --- a/test/WorkerManager-test.ts +++ b/test/WorkerManager-test.ts @@ -19,7 +19,7 @@ import { WorkerManager } from "../src/WorkerManager"; describe("WorkerManager", () => { it("should generate consecutive sequence numbers for each call", () => { const postMessage = jest.fn(); - const manager = new WorkerManager(jest.fn(() => ({ postMessage } as unknown as Worker))); + const manager = new WorkerManager({ postMessage } as unknown as Worker); manager.call({ data: "One" }); manager.call({ data: "Two" }); @@ -37,7 +37,7 @@ describe("WorkerManager", () => { it("should support resolving out of order", async () => { const postMessage = jest.fn(); const worker = { postMessage } as unknown as Worker; - const manager = new WorkerManager(jest.fn(() => worker)); + const manager = new WorkerManager(worker); const oneProm = manager.call({ data: "One" }); const twoProm = manager.call({ data: "Two" }); diff --git a/tsconfig.json b/tsconfig.json index e7689727ac..3d5f96e1c2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "emitDecoratorMetadata": false, "resolveJsonModule": true, "esModuleInterop": true, - "module": "commonjs", + "module": "es2022", "moduleResolution": "node", "target": "es2016", "noUnusedLocals": true,