Files
ko-grammar/docs/.vitepress/config.mts
T
2026-07-28 22:20:06 +07:00

163 lines
4.9 KiB
TypeScript

import fs from "node:fs";
import type { IncomingMessage, ServerResponse } from "node:http";
import path from "node:path";
import type { Connect, Plugin, ViteDevServer } from "vite";
import { defineConfig, type MarkdownOptions } from "vitepress";
const host = process.env.TAURI_DEV_HOST;
// https://vitepress.dev/reference/site-config
export default defineConfig({
title: "ko-grammar",
description: "Korean grammar edited",
lang: "ko-KR",
themeConfig: {
socialLinks: [
{ icon: "github", link: "https://git.polv.cc/polv/ko-grammar" },
],
outline: {
level: "deep",
},
},
markdown: {
config(md) {
const mdInline: MarkdownOptions["config"] = (md) => {
const className = "md";
md.core.ruler.after("inline", "inline_class_md", (state: any) => {
const tokens = state.tokens;
const tagPattern = new RegExp(
`class=(?:\"|')([^\"']*\\b${escapeReg(className)}\\b[^\"']*)(?:\"|')`,
"i",
);
for (let i = 0; i < tokens.length; i++) {
const t = tokens[i];
if (!/^(html_inline|html_block)$/.test(t.type)) continue;
const html = t.content;
// match tags with class including the target class, capture inner content (non-greedy)
const pattern = new RegExp(
`<([a-zA-Z0-9-]+)(\\b[^>]*)?>([\\s\\S]*?)<\\/\\1>`,
"gi",
);
let lastIndex = 0;
let out = "";
let m;
while ((m = pattern.exec(html)) !== null) {
const [full, tagName, attrs = "", inner] = m;
const idx = m.index;
if (idx > lastIndex) out += html.slice(lastIndex, idx);
if (tagPattern.test(attrs)) {
// render inner as inline markdown
out += `<${tagName}${attrs}>${md.renderInline(inner)}</${tagName}>`;
} else {
out += full;
}
lastIndex = idx + full.length;
}
if (lastIndex === 0) continue;
if (lastIndex < html.length) out += html.slice(lastIndex);
t.content = out;
}
});
function escapeReg(s: string) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
};
md.use(mdInline);
},
},
vite: {
plugins: [audioProxyPlugin() as any],
envDir: process.cwd(),
ssr: {
noExternal: [],
external: ["pdfjs-dist"], // don't bundle it for SSR — leave it browser-only
},
clearScreen: false,
server: {
/**
* Allow Windows Firewall via Admin PowerShell with
*
* ```
* New-NetFirewallRule -DisplayName "Vitepress Server" -Direction Inbound -Protocol TCP -LocalPort 5173 -Action Allow
* ```
*/
port: 5173,
strictPort: true,
host: host || false,
watch: {
// tell vite to ignore watching `src-tauri`
ignored: ["**/src-tauri/**"],
},
hmr: host
? {
protocol: "ws",
host,
port: 5174,
}
: undefined,
},
// Env variables starting with the item of `envPrefix` will be exposed in tauri's source code through `import.meta.env`.
envPrefix: ["VITE_", "TAURI_ENV_*"],
build: {
// Tauri uses Chromium on Windows and WebKit on macOS and Linux
target:
process.env.TAURI_ENV_PLATFORM == "windows" ? "chrome105" : "safari13",
// don't minify for debug builds
minify: !process.env.TAURI_ENV_DEBUG ? "esbuild" : false,
// produce sourcemaps for debug builds
sourcemap: !!process.env.TAURI_ENV_DEBUG,
},
},
});
function audioProxyPlugin(): Plugin {
return {
name: "audio-proxy",
configureServer(server: ViteDevServer) {
// Vite dev server middlewares are connect-style: (req, res, next)
server.middlewares.use((req, res, next) => {
const reqUrl = req.url;
if (!reqUrl || !reqUrl.startsWith("/uploads/")) return next();
if (!reqUrl.endsWith(".mp3")) return next();
proxyAudio(req, res, next);
});
},
};
}
async function proxyAudio(
req: Connect.IncomingMessage,
res: ServerResponse<IncomingMessage>,
next: Connect.NextFunction,
) {
const url = new URL(`https://www.howtostudykorean.com/wp-content${req.url}`);
const segs = req.url!.slice(1).split("/");
const filename = segs.pop()!;
const saveroot = "docs/public";
const saveto = path.join(saveroot, ...segs, filename);
if (fs.existsSync(saveto)) {
return next();
}
fs.mkdirSync(path.join(saveroot, ...segs), { recursive: true });
const r = await fetch(url, { redirect: "follow" });
if (!r.ok) {
console.error(r);
return next();
}
fs.writeFileSync(saveto, Buffer.from(await r.arrayBuffer()));
// console.log(fs.statSync(saveto));
console.log("saved to", saveto);
res.setHeader("Content-Type", "audio/mpeg");
fs.createReadStream(saveto).pipe(res);
}