UNPKG

@bomb.sh/tools

Version:

The internal dev, build, and lint CLI for Bombshell projects

119 lines (117 loc) 3.89 kB
import { fileURLToPath, pathToFileURL } from "node:url"; import { mkdtemp, symlink } from "node:fs/promises"; import { tmpdir } from "node:os"; import { sep } from "node:path"; import { NodeHfs } from "@humanfs/node"; import { expect, onTestFinished } from "vitest"; //#region src/test-utils/fixture.ts const SYMLINK = Symbol("symlink"); function isSymlinkMarker(value) { return typeof value === "object" && value !== null && SYMLINK in value; } function isFileTree(value) { return typeof value === "object" && value !== null && !Buffer.isBuffer(value) && !Array.isArray(value) && !isSymlinkMarker(value); } function scopeHfs(inner, base) { const r = (p) => new URL(`./${p}`, base); const r2 = (a, b) => [r(a), r(b)]; return { text: (p) => inner.text(r(p)), json: (p) => inner.json(r(p)), bytes: (p) => inner.bytes(r(p)), write: (p, c) => inner.write(r(p), c), append: (p, c) => inner.append(r(p), c), isFile: (p) => inner.isFile(r(p)), isDirectory: (p) => inner.isDirectory(r(p)), createDirectory: (p) => inner.createDirectory(r(p)), delete: (p) => inner.delete(r(p)), deleteAll: (p) => inner.deleteAll(r(p)), list: (p) => inner.list(r(p)), size: (p) => inner.size(r(p)), lastModified: (p) => inner.lastModified(r(p)), copy: (s, d) => inner.copy(...r2(s, d)), copyAll: (s, d) => inner.copyAll(...r2(s, d)), move: (s, d) => inner.move(...r2(s, d)), moveAll: (s, d) => inner.moveAll(...r2(s, d)) }; } /** * Create a temporary fixture directory from an inline file tree. * * Returns a {@link Fixture} with all `hfs` methods scoped to the fixture root. * * @example * ```ts * const fixture = await createFixture({ * 'hello.txt': 'hello world', * 'package.json': { name: 'test', version: '1.0.0' }, * 'icon.png': Buffer.from([0x89, 0x50]), * src: { * 'index.ts': 'export default 1', * }, * 'link.txt': ({ symlink }) => symlink('./hello.txt'), * 'info.txt': ({ importMeta }) => `Root: ${importMeta.url}`, * }) * * const text = await fixture.text('hello.txt') * const json = await fixture.json('package.json') * ``` */ async function createFixture(files) { const prefix = (expect.getState().currentTestName ?? "bsh").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, ""); const path = await mkdtemp(fileURLToPath(new URL(`${prefix}-`, `file://${tmpdir()}/`))); const base = pathToFileURL(path + sep); const inner = new NodeHfs(); const scoped = scopeHfs(inner, base); const resolve = (...segments) => new URL(`./${segments.join("/")}`, base); const ctx = { importMeta: { url: base.toString(), filename: fileURLToPath(base), dirname: fileURLToPath(base), resolve: (p) => new URL(`./${p}`, base).toString() }, symlink: (target) => ({ [SYMLINK]: true, target }) }; async function writeTree(tree, dir) { for (const [name, raw] of Object.entries(tree)) { const url = new URL(name, dir); if (typeof raw !== "function" && !Buffer.isBuffer(raw) && !Array.isArray(raw) && isFileTree(raw) && !name.includes(".")) { await inner.createDirectory(url); await writeTree(raw, new URL(`${url}/`)); continue; } const parent = new URL("./", url); await inner.createDirectory(parent); const content = typeof raw === "function" ? raw(ctx) : raw; if (isSymlinkMarker(content)) { await symlink(content.target, url); continue; } if (Buffer.isBuffer(content)) { await inner.write(url, content); continue; } if (name.endsWith(".json") && typeof content !== "string") { await inner.write(url, JSON.stringify(content, null, 2)); continue; } await inner.write(url, content); } } await writeTree(files, base); const cleanup = () => inner.deleteAll(path).then(() => void 0); onTestFinished(cleanup); return { root: base, resolve, cleanup, ...scoped }; } //#endregion export { createFixture }; //# sourceMappingURL=fixture.mjs.map