From a0d5e7c40cd117facb16e516ad5b1a50ec46bbe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Fri, 29 May 2026 12:27:51 +0800 Subject: [PATCH] feat(server): add content-type map for preview-fs static route Implement a pure function that maps file extensions to their correct MIME types, supporting common web assets (HTML, CSS, JS, JSON, images, fonts) with proper charset declarations and fallback to application/octet-stream. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/server/api/__tests__/previewFs.test.ts | 16 +++++++++++++ src/server/api/previewFs.ts | 26 ++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/server/api/__tests__/previewFs.test.ts create mode 100644 src/server/api/previewFs.ts diff --git a/src/server/api/__tests__/previewFs.test.ts b/src/server/api/__tests__/previewFs.test.ts new file mode 100644 index 00000000..38b2df37 --- /dev/null +++ b/src/server/api/__tests__/previewFs.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'bun:test' +import { contentTypeForPath } from '../previewFs' + +describe('contentTypeForPath', () => { + it('maps common web asset extensions', () => { + expect(contentTypeForPath('/x/index.html')).toBe('text/html; charset=utf-8') + expect(contentTypeForPath('/x/app.css')).toBe('text/css; charset=utf-8') + expect(contentTypeForPath('/x/app.js')).toBe('text/javascript; charset=utf-8') + expect(contentTypeForPath('/x/data.json')).toBe('application/json; charset=utf-8') + expect(contentTypeForPath('/x/logo.svg')).toBe('image/svg+xml') + expect(contentTypeForPath('/x/p.png')).toBe('image/png') + }) + it('falls back to octet-stream for unknown', () => { + expect(contentTypeForPath('/x/file.bin')).toBe('application/octet-stream') + }) +}) diff --git a/src/server/api/previewFs.ts b/src/server/api/previewFs.ts new file mode 100644 index 00000000..61b3e676 --- /dev/null +++ b/src/server/api/previewFs.ts @@ -0,0 +1,26 @@ +import * as path from 'node:path' + +const CONTENT_TYPES: Record = { + html: 'text/html; charset=utf-8', + htm: 'text/html; charset=utf-8', + css: 'text/css; charset=utf-8', + js: 'text/javascript; charset=utf-8', + mjs: 'text/javascript; charset=utf-8', + json: 'application/json; charset=utf-8', + svg: 'image/svg+xml', + png: 'image/png', + jpg: 'image/jpeg', + jpeg: 'image/jpeg', + gif: 'image/gif', + webp: 'image/webp', + ico: 'image/x-icon', + woff: 'font/woff', + woff2: 'font/woff2', + txt: 'text/plain; charset=utf-8', + md: 'text/plain; charset=utf-8', +} + +export function contentTypeForPath(filePath: string): string { + const ext = path.extname(filePath).slice(1).toLowerCase() + return CONTENT_TYPES[ext] ?? 'application/octet-stream' +}