From f7c7e547d2cbf08e6c506a69f7174da78584d75e Mon Sep 17 00:00:00 2001 From: Anthony GAEREMYNCK <1@anthony.sh> Date: Thu, 30 Jul 2026 09:00:45 +0200 Subject: [PATCH] feat: add MIME sniffing and output MIME lookup Co-Authored-By: Claude Sonnet 5 --- src/mime.js | 29 +++++++++++++++++++++++++++++ test/fixtures/sample.png | Bin 0 -> 68 bytes test/mime.test.js | 27 +++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 src/mime.js create mode 100644 test/fixtures/sample.png create mode 100644 test/mime.test.js diff --git a/src/mime.js b/src/mime.js new file mode 100644 index 0000000..f0c63e7 --- /dev/null +++ b/src/mime.js @@ -0,0 +1,29 @@ +import { fileTypeFromFile } from 'file-type'; + +const OUTPUT_MIME_TYPES = { + jpg: 'image/jpeg', + jpeg: 'image/jpeg', + png: 'image/png', + webp: 'image/webp', + gif: 'image/gif', + tiff: 'image/tiff', + avif: 'image/avif', + bmp: 'image/bmp', + pdf: 'application/pdf', + html: 'text/html', + txt: 'text/plain', + docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', +}; + +export async function detectInputMime(filePath) { + const result = await fileTypeFromFile(filePath); + return result ?? null; +} + +export function outputMimeType(targetFormat) { + const mime = OUTPUT_MIME_TYPES[targetFormat]; + if (!mime) { + throw new Error(`No known MIME type for target format "${targetFormat}"`); + } + return mime; +} diff --git a/test/fixtures/sample.png b/test/fixtures/sample.png new file mode 100644 index 0000000000000000000000000000000000000000..520b38b7bfbb918d01382f7f5abbd6714eda7eac GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1|;Q0k8}blZci7-kcv6U2@FgO46F={FMr*+ Q0TgENboFyt=akR{0DRC61ONa4 literal 0 HcmV?d00001 diff --git a/test/mime.test.js b/test/mime.test.js new file mode 100644 index 0000000..bdc5e47 --- /dev/null +++ b/test/mime.test.js @@ -0,0 +1,27 @@ +import { describe, it, expect } from 'vitest'; +import path from 'node:path'; +import { detectInputMime, outputMimeType } from '../src/mime.js'; + +describe('detectInputMime', () => { + it('detects PNG from magic bytes regardless of file extension', async () => { + const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample.png'); + + const result = await detectInputMime(fixturePath); + + expect(result).toEqual({ ext: 'png', mime: 'image/png' }); + }); +}); + +describe('outputMimeType', () => { + it('returns the MIME type for a known target format', () => { + expect(outputMimeType('pdf')).toBe('application/pdf'); + expect(outputMimeType('png')).toBe('image/png'); + expect(outputMimeType('docx')).toBe( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' + ); + }); + + it('throws for an unknown target format', () => { + expect(() => outputMimeType('made-up-format')).toThrowError(/made-up-format/); + }); +});