chore: 1.0.0 release
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { createReadStream, statSync } from "node:fs";
|
||||
import { createServer } from "node:http";
|
||||
import { dirname, extname, resolve, sep } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const defaultEditorDirectory = resolve(packageRoot, "editor");
|
||||
|
||||
const contentTypes = new Map([
|
||||
[".css", "text/css; charset=utf-8"],
|
||||
[".html", "text/html; charset=utf-8"],
|
||||
[".js", "text/javascript; charset=utf-8"],
|
||||
[".json", "application/json; charset=utf-8"],
|
||||
[".svg", "image/svg+xml"],
|
||||
]);
|
||||
|
||||
function respond(response, statusCode, message) {
|
||||
response.writeHead(statusCode, {
|
||||
"Content-Type": "text/plain; charset=utf-8",
|
||||
"Cache-Control": "no-store",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
});
|
||||
response.end(message);
|
||||
}
|
||||
|
||||
export function createEditorServer({ editorDirectory = defaultEditorDirectory } = {}) {
|
||||
const root = resolve(editorDirectory);
|
||||
const rootPrefix = `${root}${sep}`;
|
||||
|
||||
return createServer((request, response) => {
|
||||
if (request.method !== "GET" && request.method !== "HEAD") {
|
||||
respond(response, 405, "Method Not Allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
let pathname;
|
||||
try {
|
||||
pathname = decodeURIComponent(new URL(request.url ?? "/", "http://localhost").pathname);
|
||||
} catch {
|
||||
respond(response, 400, "Bad Request");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/")) pathname += "index.html";
|
||||
const filePath = resolve(root, `.${pathname}`);
|
||||
if (filePath !== root && !filePath.startsWith(rootPrefix)) {
|
||||
respond(response, 403, "Forbidden");
|
||||
return;
|
||||
}
|
||||
|
||||
let stats;
|
||||
try {
|
||||
stats = statSync(filePath);
|
||||
} catch {
|
||||
respond(response, 404, "Not Found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stats.isFile()) {
|
||||
respond(response, 404, "Not Found");
|
||||
return;
|
||||
}
|
||||
|
||||
response.writeHead(200, {
|
||||
"Content-Type": contentTypes.get(extname(filePath).toLowerCase()) ?? "application/octet-stream",
|
||||
"Content-Length": stats.size,
|
||||
"Cache-Control": "no-store",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
});
|
||||
|
||||
if (request.method === "HEAD") {
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const stream = createReadStream(filePath);
|
||||
stream.on("error", () => response.destroy());
|
||||
stream.pipe(response);
|
||||
});
|
||||
}
|
||||
|
||||
export function startEditorServer({ host = "127.0.0.1", port = 55173 } = {}) {
|
||||
const server = createEditorServer();
|
||||
|
||||
return new Promise((resolvePromise, reject) => {
|
||||
const onError = (error) => reject(error);
|
||||
server.once("error", onError);
|
||||
server.listen(port, host, () => {
|
||||
server.off("error", onError);
|
||||
const address = server.address();
|
||||
const actualPort = typeof address === "object" && address ? address.port : port;
|
||||
resolvePromise({ server, url: `http://${host}:${actualPort}/` });
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user