#!/usr/bin/env node /* 极简静态文件服务器(官网预览用,替代沙箱内不可用的 python3 -m http.server) */ const http = require('http'); const fs = require('fs'); const path = require('path'); const ROOT = path.join(__dirname, '..', 'exp'); const PORT = Number(process.argv[2]) || 8898; const MIME = { html: 'text/html; charset=utf-8', js: 'text/javascript', mjs: 'text/javascript', css: 'text/css', json: 'application/json; charset=utf-8', png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', svg: 'image/svg+xml', webp: 'image/webp', woff: 'font/woff', woff2: 'font/woff2', ttf: 'font/ttf', pdf: 'application/pdf', docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', txt: 'text/plain; charset=utf-8', md: 'text/plain; charset=utf-8', ico: 'image/x-icon', mp4: 'video/mp4', webm: 'video/webm', }; http.createServer((req, res) => { try { let p = decodeURIComponent(new URL(req.url, 'http://x').pathname); if (p.endsWith('/')) p += 'index.html'; const file = path.normalize(path.join(ROOT, p)); if (!file.startsWith(ROOT)) { res.writeHead(403); return res.end(); } fs.readFile(file, (err, data) => { if (err) { res.writeHead(404); return res.end('not found'); } const ext = path.extname(file).slice(1).toLowerCase(); res.writeHead(200, { 'Content-Type': MIME[ext] || 'application/octet-stream' }); res.end(data); }); } catch (e) { res.writeHead(500); res.end(String(e)); } }).listen(PORT, '127.0.0.1', () => console.log(`preview: http://127.0.0.1:${PORT}/ (root: ${ROOT})`));