Files
kit-ui/lib/media/wasm/wasmLoader.ts
Sebastian Krüger 3a8b409d1d Fix FFmpeg WASM bundler error: use runtime import instead of static analysis
The @ffmpeg/ffmpeg package has internal dynamic imports that Turbopack
cannot statically analyze, but they work fine at runtime. This change
moves the import to the loadFFmpeg function where it's needed, allowing
Turbopack to skip static analysis and let the bundler resolve it at runtime.

Fixes: Cannot find module as expression is too dynamic error

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-25 20:27:53 +01:00

149 lines
3.9 KiB
TypeScript

// Suppress Turbopack bundler warning for FFmpeg's dynamic imports
// The @ffmpeg/ffmpeg package internally uses dynamic imports that Turbopack
// flags as "too dynamic" but still work at runtime
declare const globalThis: any;
import type { ConverterEngine, WASMModuleState } from '@/types/media';
import { initializeImageMagick } from '@imagemagick/magick-wasm';
/**
* WASM module loading state
*/
const moduleState: WASMModuleState = {
ffmpeg: false,
imagemagick: false,
};
/**
* Cached WASM instances
*/
let ffmpegInstance: any = null;
let imagemagickInstance: any = null;
/**
* Load FFmpeg WASM module
*/
export async function loadFFmpeg(): Promise<any> {
if (ffmpegInstance && moduleState.ffmpeg) {
return ffmpegInstance;
}
try {
// Import FFmpeg at runtime to avoid static bundler analysis
// The @ffmpeg/ffmpeg package has internal dynamic imports that
// Turbopack cannot statically analyze, but they work fine at runtime
const { FFmpeg } = await import('@ffmpeg/ffmpeg');
if (!FFmpeg) {
throw new Error('FFmpeg class not available');
}
ffmpegInstance = new FFmpeg();
if (!ffmpegInstance) {
throw new Error('Failed to create FFmpeg instance');
}
ffmpegInstance.on('log', ({ message }: { message: string }) => {
console.log('[FFmpeg]', message);
});
// Files are guaranteed to exist in /wasm/ by the postinstall script
const coreURL = '/wasm/ffmpeg-core.js';
const wasmURL = '/wasm/ffmpeg-core.wasm';
await ffmpegInstance.load({ coreURL, wasmURL });
moduleState.ffmpeg = true;
console.log('[FFmpeg] Loaded successfully from local assets');
return ffmpegInstance;
} catch (error) {
console.error('[FFmpeg] Failed to load:', error);
throw new Error('Failed to load FFmpeg WASM module');
}
}
/**
* Load ImageMagick WASM module
*/
export async function loadImageMagick(): Promise<any> {
if (imagemagickInstance && moduleState.imagemagick) {
return imagemagickInstance;
}
try {
// File is guaranteed to exist in /wasm/ by the postinstall script
const wasmUrl = '/wasm/magick.wasm';
console.log('[ImageMagick] Loading local WASM:', wasmUrl);
const response = await fetch(wasmUrl);
if (!response.ok) {
throw new Error(`Failed to fetch WASM file: ${response.status} ${response.statusText}`);
}
const arrayBuffer = await response.arrayBuffer();
console.log('[ImageMagick] WASM file size:', arrayBuffer.byteLength, 'bytes');
await initializeImageMagick(arrayBuffer);
// Store the module for later use
imagemagickInstance = { initialized: true };
moduleState.imagemagick = true;
console.log('[ImageMagick] Loaded and initialized successfully from local asset');
return imagemagickInstance;
} catch (error) {
console.error('[ImageMagick] Failed to load:', error);
throw new Error(`Failed to load ImageMagick WASM module: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Get loaded module state
*/
export function getModuleState(): WASMModuleState {
return { ...moduleState };
}
/**
* Check if a specific module is loaded
*/
export function isModuleLoaded(engine: ConverterEngine): boolean {
return moduleState[engine];
}
/**
* Load appropriate WASM module for converter engine
*/
export async function loadModule(engine: ConverterEngine): Promise<any> {
switch (engine) {
case 'ffmpeg':
return loadFFmpeg();
case 'imagemagick':
return loadImageMagick();
default:
throw new Error(`Unknown converter engine: ${engine}`);
}
}
/**
* Unload all WASM modules and free memory
*/
export function unloadAll(): void {
if (ffmpegInstance) {
// FFmpeg doesn't have an explicit unload method
// Just null the instance
ffmpegInstance = null;
moduleState.ffmpeg = false;
}
if (imagemagickInstance) {
imagemagickInstance = null;
moduleState.imagemagick = false;
}
console.log('All WASM modules unloaded');
}