publish(task 11): image-collector (ignoriert hugo-derivate)

collectImages(postDir) scannt ordner nach png/jpg/jpeg/gif/webp/svg,
ignoriert hugo-resize-derivate (*_hu_<hex>.*), liest bytes und gibt
sortierte ImageFile[]-liste zurück. mimeFromExt() mapped extension
auf mime-type. deno.jsonc test-task um --allow-write erweitert (Deno.
makeTempDir+writeFile in tests).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jörg Lohrer 2026-04-18 05:31:32 +02:00
parent 05ba4e4ef9
commit 8eebd29266
3 changed files with 73 additions and 1 deletions

View File

@ -3,7 +3,7 @@
"publish": "deno run --env-file=../.env.local --allow-env --allow-read --allow-write=./logs --allow-net --allow-run=git src/cli.ts publish",
"check": "deno run --env-file=../.env.local --allow-env --allow-read --allow-net src/cli.ts check",
"validate-post": "deno run --allow-read src/cli.ts validate-post",
"test": "deno test --allow-env --allow-read --allow-net --allow-run",
"test": "deno test --allow-env --allow-read --allow-write --allow-net --allow-run",
"fmt": "deno fmt",
"lint": "deno lint"
},

View File

@ -0,0 +1,45 @@
import { extname, join } from '@std/path'
const IMG_EXTS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg'])
const MIME_MAP: Record<string, string> = {
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.webp': 'image/webp',
'.svg': 'image/svg+xml',
}
const HUGO_DERIVATIVE = /_hu_[0-9a-f]+\./
export function mimeFromExt(filename: string): string {
return MIME_MAP[extname(filename).toLowerCase()] ?? 'application/octet-stream'
}
export interface ImageFile {
fileName: string
absolutePath: string
data: Uint8Array
mimeType: string
}
export async function collectImages(postDir: string): Promise<ImageFile[]> {
const results: ImageFile[] = []
for await (const entry of Deno.readDir(postDir)) {
if (!entry.isFile) continue
if (HUGO_DERIVATIVE.test(entry.name)) continue
const ext = extname(entry.name).toLowerCase()
if (!IMG_EXTS.has(ext)) continue
const abs = join(postDir, entry.name)
const data = await Deno.readFile(abs)
results.push({
fileName: entry.name,
absolutePath: abs,
data,
mimeType: mimeFromExt(entry.name),
})
}
results.sort((a, b) => a.fileName.localeCompare(b.fileName))
return results
}

View File

@ -0,0 +1,27 @@
import { assertEquals } from '@std/assert'
import { collectImages, mimeFromExt } from '../src/core/image-collector.ts'
Deno.test('mimeFromExt: erkennt gängige formate', () => {
assertEquals(mimeFromExt('a.png'), 'image/png')
assertEquals(mimeFromExt('a.jpg'), 'image/jpeg')
assertEquals(mimeFromExt('a.jpeg'), 'image/jpeg')
assertEquals(mimeFromExt('a.gif'), 'image/gif')
assertEquals(mimeFromExt('a.webp'), 'image/webp')
assertEquals(mimeFromExt('a.svg'), 'image/svg+xml')
})
Deno.test('collectImages: liest alle bild-dateien im ordner, ignoriert hugo-derivate', async () => {
const tmp = await Deno.makeTempDir()
try {
await Deno.writeTextFile(`${tmp}/index.md`, '# hi')
await Deno.writeFile(`${tmp}/a.png`, new Uint8Array([1]))
await Deno.writeFile(`${tmp}/b.jpg`, new Uint8Array([2]))
await Deno.writeFile(`${tmp}/a_hu_deadbeef.png`, new Uint8Array([3]))
await Deno.writeTextFile(`${tmp}/notes.txt`, 'ignore me')
const imgs = await collectImages(tmp)
assertEquals(imgs.map((i) => i.fileName).sort(), ['a.png', 'b.jpg'])
assertEquals(imgs.find((i) => i.fileName === 'a.png')?.mimeType, 'image/png')
} finally {
await Deno.remove(tmp, { recursive: true })
}
})