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

189 lines (188 loc) 5.61 kB
import { EnvironmentError } from "../errors/classes.js"; import { detectRuntime } from "./detector.js"; let platformIO; async function readFullFile(readFile, path, headerSize, footerSize, fileSize) { const actualHeader = Math.min(headerSize, fileSize); const actualFooter = Math.min(footerSize, fileSize); const footerStart = Math.max(0, fileSize - actualFooter); if (footerStart > actualHeader) return void 0; return (await readFile(path)).slice(0, fileSize); } function createDenoIO() { const D = globalThis.Deno; return { readFile: (path) => D.readFile(path), writeFile: (path, data) => D.writeFile(path, data), stat: async (path) => { const s = await D.stat(path); return { size: s.size }; }, async *readDir(path) { for await (const entry of D.readDir(path)) { yield { name: entry.name, isDirectory: entry.isDirectory, isFile: entry.isFile }; } }, readPartial: async (path, headerSize, footerSize) => { const file = await D.open(path, { read: true }); try { const fileSize = (await file.stat()).size; const full = await readFullFile( (p) => D.readFile(p), path, headerSize, footerSize, fileSize ); if (full) return full; const actualHeader = Math.min(headerSize, fileSize); const header = new Uint8Array(actualHeader); await file.read(header); const actualFooter = Math.min(footerSize, fileSize); const footerStart = fileSize - actualFooter; await file.seek(footerStart, D.SeekMode.Start); const footer = new Uint8Array(actualFooter); await file.read(footer); const combined = new Uint8Array(actualHeader + actualFooter); combined.set(header, 0); combined.set(footer, actualHeader); return combined; } finally { file.close(); } } }; } function createNodeIO() { let fsCache; let bufCache; async function getFs() { return fsCache ?? (fsCache = await import("node:fs/promises")); } async function getBuf() { return bufCache ?? (bufCache = await import("node:buffer")); } return { readFile: async (path) => { const fs = await getFs(); return new Uint8Array(await fs.readFile(path)); }, writeFile: async (path, data) => { const fs = await getFs(); await fs.writeFile(path, data); }, stat: async (path) => { const fs = await getFs(); const s = await fs.stat(path); return { size: s.size }; }, async *readDir(path) { const fs = await getFs(); const entries = await fs.readdir(path, { withFileTypes: true }); for (const entry of entries) { yield { name: entry.name, isDirectory: entry.isDirectory(), isFile: entry.isFile() }; } }, readPartial: async (path, headerSize, footerSize) => { const fs = await getFs(); const { Buffer } = await getBuf(); const file = await fs.open(path, "r"); try { const fileSize = (await file.stat()).size; const full = await readFullFile( async (p) => new Uint8Array(await fs.readFile(p)), path, headerSize, footerSize, fileSize ); if (full) return full; const actualHeader = Math.min(headerSize, fileSize); const header = Buffer.alloc(actualHeader); await file.read(header, 0, actualHeader, 0); const actualFooter = Math.min(footerSize, fileSize); const footerStart = fileSize - actualFooter; const footer = Buffer.alloc(actualFooter); await file.read(footer, 0, actualFooter, footerStart); const combined = new Uint8Array(actualHeader + actualFooter); combined.set( new Uint8Array(header.buffer, header.byteOffset, header.byteLength), 0 ); combined.set( new Uint8Array(footer.buffer, footer.byteOffset, footer.byteLength), actualHeader ); return combined; } finally { await file.close(); } } }; } function createBunIO() { const B = globalThis.Bun; return { readFile: async (path) => { return new Uint8Array(await B.file(path).arrayBuffer()); }, writeFile: async (path, data) => { await B.write(path, data); }, stat: async (path) => { return { size: B.file(path).size }; }, async *readDir(path) { const fs = await import("node:fs/promises"); const entries = await fs.readdir(path, { withFileTypes: true }); for (const entry of entries) { yield { name: entry.name, isDirectory: entry.isDirectory(), isFile: entry.isFile() }; } } }; } function getPlatformIO() { if (platformIO) return platformIO; const runtime = detectRuntime(); switch (runtime.environment) { case "deno-wasi": platformIO = createDenoIO(); break; case "bun-wasi": platformIO = createBunIO(); break; case "node-wasi": case "node-emscripten": platformIO = createNodeIO(); break; default: throw new EnvironmentError( runtime.environment, "does not support filesystem operations", "filesystem access" ); } return platformIO; } function _setPlatformIOForTesting(io) { platformIO = io; } function _resetPlatformIO() { platformIO = void 0; } export { _resetPlatformIO, _setPlatformIOForTesting, getPlatformIO };