UNPKG

taglib-wasm

Version:

TagLib-Wasm is the universal tagging library for TypeScript/JavaScript platforms: Browsers, Node.js, Deno, Bun, Cloudflare Workers, and Electron apps

123 lines (122 loc) 4.09 kB
import { isDeno, supportsExnref } from "../detector.js"; import { ModuleLoadError } from "./types.js"; import { errorMessage } from "../../errors/classes.js"; import { fileUrlToPath } from "../../utils/path.js"; import { getNodeFsSync } from "../../utils/node-fs.js"; function isWindows() { return isDeno() ? Deno.build.os === "windows" : globalThis.process ? globalThis.process.platform === "win32" : false; } function getPreopens() { if (!isWindows()) return { "/": "/" }; const statSync = isDeno() ? Deno.statSync : getNodeFsSync()?.statSync; if (!statSync) { console.warn( "[taglib-wasm] Windows drive detection unavailable: node:fs could not be loaded. Only C:\\ is registered as a WASI preopen." ); return { "/C": "C:\\" }; } const preopens = {}; for (const letter of "CDEFGHIJKLMNOPQRSTUVWXYZAB") { const root = `${letter}:\\`; try { statSync(root); preopens[`/${letter}`] = root; } catch { } } return Object.keys(preopens).length > 0 ? preopens : { "/C": "C:\\" }; } function resolveWasmPath(relativePath) { const url = new URL(relativePath, import.meta.url); return url.protocol === "file:" ? fileUrlToPath(url) : url.href; } async function loadModule(wasmType, runtime, options) { if (wasmType === "wasi") { return await loadWasiModuleWithFallback(runtime, options); } else { return { module: await loadEmscriptenModule(options), actualWasmType: "emscripten" }; } } async function loadWasiModuleWithFallback(runtime, options) { const defaultWasmPath = resolveWasmPath("../../../taglib-wasi.wasm"); try { const { loadWasiHost } = await import("../wasi-host-loader.js"); const wasiModule = await loadWasiHost({ wasmPath: options.wasmUrl || defaultWasmPath, preopens: getPreopens() }); return { module: wasiModule, actualWasmType: "wasi" }; } catch (hostError) { if (options.forceWasmType === "wasi") { throw new ModuleLoadError( `WASI backend failed to load: ${errorMessage(hostError)}. forceWasmType is "wasi", so the Emscripten fallback was not attempted`, "wasi", hostError ); } if (runtime.environment === "node-wasi" && !supportsExnref()) { const g = globalThis; const nodeVersion = g.process?.versions?.node ?? ""; console.warn( `[taglib-wasm] WASI unavailable: Node.js ${nodeVersion} requires --experimental-wasm-exnref. Falling back to Emscripten. Run with: node --experimental-wasm-exnref your-script.js` ); } else if (options.debug) { console.warn(`[UnifiedLoader] WASI host failed:`, hostError); } } if (options.debug) { console.warn(`[UnifiedLoader] WASI loader failed, using Emscripten`); } return { module: await loadEmscriptenModule(options), actualWasmType: "emscripten" }; } async function loadEmscriptenModule(options) { try { let createModule; try { const module2 = await import("../../../taglib-wrapper.js"); createModule = module2.default; } catch { try { const module2 = await import("../../../taglib-wrapper.js"); createModule = module2.default; } catch { throw new ModuleLoadError( "Could not load Emscripten module from build or dist", "emscripten" ); } } const moduleConfig = {}; if (options.wasmBinary) { moduleConfig.wasmBinary = options.wasmBinary; } if (options.wasmUrl) { moduleConfig.locateFile = (path) => { return path.endsWith(".wasm") ? options.wasmUrl : path; }; } else if (!options.wasmBinary) { const wasmUrl = new URL("../../../taglib-web.wasm", import.meta.url ); moduleConfig.locateFile = (path) => path.endsWith(".wasm") ? wasmUrl.href : path; } const module = await createModule(moduleConfig); return module; } catch (error) { throw new ModuleLoadError( `Failed to load Emscripten module: ${errorMessage(error)}`, "emscripten", error ); } } export { getPreopens, loadModule };