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
JavaScript
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
};