feat: bundle browser dependencies locally
This commit is contained in:
1 parent
0ea9e4310f
commit
5e10be463e
8 files changed
+95
-16
No files matched your search
@@ -2,25 +2,28 @@ import { readFile } from "node:fs/promises";
|
||||
import type { ServerResponse } from "node:http";
|
||||
|
||||
const CLIENT_ASSET_PREFIX = "/client/";
|
||||
const VENDOR_ASSET_PREFIX = "/vendor/";
|
||||
|
||||
export function isClientAssetPath(pathname: string): boolean {
|
||||
return pathname.startsWith(CLIENT_ASSET_PREFIX);
|
||||
return pathname.startsWith(CLIENT_ASSET_PREFIX) || pathname.startsWith(VENDOR_ASSET_PREFIX);
|
||||
}
|
||||
|
||||
export async function handleClientAssetRequest(url: URL, res: ServerResponse): Promise<void> {
|
||||
const assetName = url.pathname.slice(CLIENT_ASSET_PREFIX.length);
|
||||
const isVendorAsset = url.pathname.startsWith(VENDOR_ASSET_PREFIX);
|
||||
const assetPrefix = isVendorAsset ? VENDOR_ASSET_PREFIX : CLIENT_ASSET_PREFIX;
|
||||
const assetName = url.pathname.slice(assetPrefix.length);
|
||||
|
||||
if (!/^[a-z0-9-]+\.js$/i.test(assetName)) {
|
||||
if (!isAllowedAssetPath(assetName, isVendorAsset)) {
|
||||
res.statusCode = 404;
|
||||
res.end("Not found");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const assetUrl = new URL(`../client/${assetName}`, import.meta.url);
|
||||
const source = await readFile(assetUrl, "utf8");
|
||||
const assetUrl = new URL(`../${isVendorAsset ? "vendor" : "client"}/${assetName}`, import.meta.url);
|
||||
const source = await readFile(assetUrl);
|
||||
res.statusCode = 200;
|
||||
res.setHeader("content-type", "text/javascript; charset=utf-8");
|
||||
res.setHeader("content-type", getContentType(assetName));
|
||||
res.setHeader("cache-control", "no-cache");
|
||||
res.end(source);
|
||||
} catch {
|
||||
@@ -28,3 +31,27 @@ export async function handleClientAssetRequest(url: URL, res: ServerResponse): P
|
||||
res.end("Not found");
|
||||
}
|
||||
}
|
||||
|
||||
function isAllowedAssetPath(assetName: string, isVendorAsset: boolean): boolean {
|
||||
if (assetName.split("/").includes("..")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isVendorAsset) {
|
||||
return /^[a-z0-9/_.-]+\.(css|js|mjs|png)$/i.test(assetName);
|
||||
}
|
||||
|
||||
return /^[a-z0-9-]+\.js$/i.test(assetName);
|
||||
}
|
||||
|
||||
function getContentType(assetName: string): string {
|
||||
if (assetName.endsWith(".css")) {
|
||||
return "text/css; charset=utf-8";
|
||||
}
|
||||
|
||||
if (assetName.endsWith(".png")) {
|
||||
return "image/png";
|
||||
}
|
||||
|
||||
return "text/javascript; charset=utf-8";
|
||||
}
|
||||
@@ -7,12 +7,7 @@ export function htmlPage(): string {
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>mapix</title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
||||
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
|
||||
crossorigin=""
|
||||
/>
|
||||
<link rel="stylesheet" href="/vendor/leaflet/leaflet.css" />
|
||||
<style>
|
||||
${APP_STYLES}
|
||||
</style>
|
||||
@@ -153,7 +148,7 @@ ${APP_STYLES}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
||||
<script src="/vendor/leaflet/leaflet.js"></script>
|
||||
<script type="module" src="/client/app.js"></script>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
Reference in new issue
Block a user