Load OpenCV in the browser via a script tag: the bundler's CJS interop copies the module's then() onto the import namespace, which never settles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
38 lines
1.5 KiB
TypeScript
38 lines
1.5 KiB
TypeScript
export type CV = typeof import('@techstark/opencv-js');
|
|
|
|
// Everything returns { cv }: the cv module is a thenable, so resolving with it directly never settles.
|
|
let loading: Promise<{ cv: CV }> | null = null;
|
|
|
|
export function loadOpenCV(): Promise<{ cv: CV }> {
|
|
loading ??= (async () => {
|
|
const { cv } = typeof document === 'undefined' ? await importModule() : await loadScript();
|
|
if (!cv.Mat) {
|
|
await new Promise<void>((resolve) => {
|
|
(cv as unknown as { onRuntimeInitialized: () => void }).onRuntimeInitialized = () => resolve();
|
|
});
|
|
}
|
|
return { cv };
|
|
})();
|
|
return loading;
|
|
}
|
|
|
|
/** Node (tests). The variable specifier keeps the bundler from emitting a second OpenCV chunk. */
|
|
async function importModule(): Promise<{ cv: CV }> {
|
|
const specifier = '@techstark/opencv-js';
|
|
const mod = (await import(/* @vite-ignore */ specifier)) as { default: CV };
|
|
return { cv: mod.default };
|
|
}
|
|
|
|
// A script tag, not import(): bundler CJS interop copies `then` and the import never settles.
|
|
async function loadScript(): Promise<{ cv: CV }> {
|
|
const { default: url } = await import('@techstark/opencv-js/dist/opencv.js?url');
|
|
await new Promise<void>((resolve, reject) => {
|
|
const script = document.createElement('script');
|
|
script.src = url;
|
|
script.onload = () => resolve();
|
|
script.onerror = () => reject(new Error('Failed to load OpenCV'));
|
|
document.head.append(script);
|
|
});
|
|
return { cv: (window as unknown as { cv: CV }).cv };
|
|
}
|