| // Simple server adapted from https://developer.mozilla.org/en-US/docs/Learn/Server-side/Node_server_without_framework: |
| import * as fs from "fs"; |
| import * as http from "http"; |
| import * as path from "path"; |
| const MIME_TYPES = { |
| default: "application/octet-stream", |
| html: "text/html; charset=UTF-8", |
| js: "application/javascript; charset=UTF-8", |
| mjs: "application/javascript; charset=UTF-8", |
| css: "text/css", |
| png: "image/png", |
| jpg: "image/jpg", |
| gif: "image/gif", |
| ico: "image/x-icon", |
| svg: "image/svg+xml", |
| }; |
| |
| const STATIC_PATH = path.join(process.cwd(), "./"); |
| const toBool = [() => true, () => false]; |
| |
| export default function serve(port) { |
| if (!port) |
| throw new Error("Port is required"); |
| |
| const prepareFile = async (url) => { |
| const paths = [STATIC_PATH, url]; |
| if (url.endsWith("/")) |
| paths.push("index.html"); |
| const filePath = path.join(...paths); |
| const pathTraversal = !filePath.startsWith(STATIC_PATH); |
| const exists = await fs.promises.access(filePath).then(...toBool); |
| const found = !pathTraversal && exists; |
| const streamPath = found ? filePath : `${STATIC_PATH}/index.html`; |
| const ext = path.extname(streamPath).substring(1).toLowerCase(); |
| const stream = fs.createReadStream(streamPath); |
| return { found, ext, stream }; |
| }; |
| |
| const server = http |
| .createServer(async (req, res) => { |
| const file = await prepareFile(req.url); |
| const statusCode = file.found ? 200 : 404; |
| const mimeType = MIME_TYPES[file.ext] || MIME_TYPES.default; |
| res.writeHead(statusCode, { "Content-Type": mimeType }); |
| file.stream.pipe(res); |
| }) |
| .listen(port); |
| |
| console.log(`Server running at http://127.0.0.1:${port}/`); |
| return server; |
| } |