Spaces:
Running
Running
Download scripts/build-katex-inline.mjs from banu4prasad/Gateprep: direct link, hf CLI and curl.
- Browser
- Download file 6.21 kB
-
https://huggingface.co/spaces/banu4prasad/Gateprep/resolve/main/scripts/build-katex-inline.mjs
- Command line
-
hf download hf://spaces/banu4prasad/Gateprep/scripts/build-katex-inline.mjs
-
curl -L -o build-katex-inline.mjs https://huggingface.co/spaces/banu4prasad/Gateprep/resolve/main/scripts/build-katex-inline.mjs
6.21 kB
| /** | |
| * scripts/build-katex-inline.mjs | |
| * | |
| * One-time generation script to produce 100% self-contained KaTeX & branded font assets | |
| * for server-side HTML result report rendering. | |
| * | |
| * It: | |
| * 1. Reads KaTeX version from frontend/package.json. | |
| * 2. Inlines all KaTeX woff2 font files into backend/app/static/katex/katex-inline.css as base64 data URIs. | |
| * 3. Strips unused relative .woff and .ttf fallback references so no broken relative paths remain. | |
| * 4. Copies katex.min.js and auto-render.min.js verbatim to backend/app/static/katex/. | |
| * 5. Inlines all branded fonts (Syne, DM Sans, JetBrains Mono) into backend/app/static/fonts/fonts-inline.css. | |
| * | |
| * Note on Version-Drift: | |
| * If the katex dependency in frontend/package.json is updated, this script must be re-run | |
| * to maintain version parity. Automated tests verify this header in backend/tests/test_result_html.py. | |
| */ | |
| import fs from 'node:fs'; | |
| import path from 'node:path'; | |
| import { fileURLToPath } from 'node:url'; | |
| const __filename = fileURLToPath(import.meta.url); | |
| const __dirname = path.dirname(__filename); | |
| const rootDir = path.resolve(__dirname, '..'); | |
| const frontendPkgPath = path.join(rootDir, 'frontend', 'package.json'); | |
| const katexDistDir = path.join(rootDir, 'frontend', 'node_modules', 'katex', 'dist'); | |
| const katexFontsDir = path.join(katexDistDir, 'fonts'); | |
| const katexCssPath = path.join(katexDistDir, 'katex.min.css'); | |
| const katexJsPath = path.join(katexDistDir, 'katex.min.js'); | |
| const katexAutoRenderJsPath = path.join(katexDistDir, 'contrib', 'auto-render.min.js'); | |
| const backendStaticKatexDir = path.join(rootDir, 'backend', 'app', 'static', 'katex'); | |
| const backendStaticFontsDir = path.join(rootDir, 'backend', 'app', 'static', 'fonts'); | |
| const fontsCssPath = path.join(rootDir, 'frontend', 'src', 'fonts.css'); | |
| const publicFontsDir = path.join(rootDir, 'frontend', 'public', 'fonts'); | |
| // 1. Verify source files exist | |
| if (!fs.existsSync(frontendPkgPath)) { | |
| console.error(`Error: frontend/package.json not found at ${frontendPkgPath}`); | |
| process.exit(1); | |
| } | |
| if (!fs.existsSync(katexCssPath)) { | |
| console.error(`Error: katex.min.css not found at ${katexCssPath}`); | |
| process.exit(1); | |
| } | |
| if (!fs.existsSync(fontsCssPath)) { | |
| console.error(`Error: fonts.css not found at ${fontsCssPath}`); | |
| process.exit(1); | |
| } | |
| const frontendPkg = JSON.parse(fs.readFileSync(frontendPkgPath, 'utf8')); | |
| const katexVersionRaw = frontendPkg.dependencies?.katex || '0.17.0'; | |
| const katexVersion = katexVersionRaw.replace(/^[\^~]/, ''); | |
| console.log(`Building airtight inline assets for KaTeX v${katexVersion}...`); | |
| // Ensure destination directories exist | |
| fs.mkdirSync(backendStaticKatexDir, { recursive: true }); | |
| fs.mkdirSync(backendStaticFontsDir, { recursive: true }); | |
| // 2. Build katex-inline.css with base64 embedded woff2 fonts and stripped dead fallbacks | |
| let katexCss = fs.readFileSync(katexCssPath, 'utf8'); | |
| // First: Strip dead relative .woff and .ttf fallback sources (e.g. ",url(fonts/...woff) format("woff")") | |
| katexCss = katexCss.replace(/,url\((['"]?)(?:fonts\/)?[^'")]+?\.(?:woff|ttf)(['"]?)\)\s*format\((['"]?)(?:woff|truetype)(['"]?)\)/g, ''); | |
| // Second: Replace remaining url(fonts/...woff2) with base64 data URIs | |
| katexCss = katexCss.replace(/url\((['"]?)(?:fonts\/)?([^'")]+?\.woff2)(['"]?)\)/g, (match, q1, fontFileName) => { | |
| const cleanName = path.basename(fontFileName); | |
| const fontFilePath = path.join(katexFontsDir, cleanName); | |
| if (fs.existsSync(fontFilePath)) { | |
| const fontBase64 = fs.readFileSync(fontFilePath).toString('base64'); | |
| return `url("data:font/woff2;base64,${fontBase64}")`; | |
| } | |
| console.warn(`Warning: KaTeX font file not found: ${fontFilePath}`); | |
| return match; | |
| }); | |
| // Verification check: ensure no relative url(...) remains in KaTeX CSS | |
| const remainingRelativeUrls = katexCss.match(/url\((?!['"]?data:)[^)]+\)/g); | |
| if (remainingRelativeUrls) { | |
| console.warn('Warning: Remaining relative URLs in KaTeX CSS:', remainingRelativeUrls); | |
| } else { | |
| console.log('β Verified: 0 relative URLs remain in katex-inline.css (100% self-contained data URIs).'); | |
| } | |
| // Add KaTeX version header for drift detection | |
| const katexInlineCssContent = `/* KaTeX v${katexVersion} (inlined fonts) */\n` + katexCss; | |
| const outputKatexCssPath = path.join(backendStaticKatexDir, 'katex-inline.css'); | |
| fs.writeFileSync(outputKatexCssPath, katexInlineCssContent, 'utf8'); | |
| console.log(`β Generated ${outputKatexCssPath} (${(katexInlineCssContent.length / 1024).toFixed(1)} KB)`); | |
| // 3. Copy katex.min.js and auto-render.min.js | |
| const outputKatexJsPath = path.join(backendStaticKatexDir, 'katex.min.js'); | |
| fs.copyFileSync(katexJsPath, outputKatexJsPath); | |
| console.log(`β Copied katex.min.js to ${outputKatexJsPath}`); | |
| const outputAutoRenderJsPath = path.join(backendStaticKatexDir, 'auto-render.min.js'); | |
| fs.copyFileSync(katexAutoRenderJsPath, outputAutoRenderJsPath); | |
| console.log(`β Copied auto-render.min.js to ${outputAutoRenderJsPath}`); | |
| // 4. Build fonts-inline.css with base64 embedded woff2 fonts for Syne & DM Sans | |
| let fontsCss = fs.readFileSync(fontsCssPath, 'utf8'); | |
| fontsCss = fontsCss.replace(/url\((['"]?)(?:\/fonts\/|fonts\/)?([^'")]+?\.woff2)(['"]?)\)/g, (match, q1, fontFileName) => { | |
| const cleanName = path.basename(fontFileName); | |
| const fontFilePath = path.join(publicFontsDir, cleanName); | |
| if (fs.existsSync(fontFilePath)) { | |
| const fontBase64 = fs.readFileSync(fontFilePath).toString('base64'); | |
| return `url("data:font/woff2;base64,${fontBase64}")`; | |
| } | |
| console.warn(`Warning: Brand font file not found: ${fontFilePath}`); | |
| return match; | |
| }); | |
| const remainingBrandRelativeUrls = fontsCss.match(/url\((?!['"]?data:)[^)]+\)/g); | |
| if (remainingBrandRelativeUrls) { | |
| console.warn('Warning: Remaining relative URLs in fonts CSS:', remainingBrandRelativeUrls); | |
| } else { | |
| console.log('β Verified: 0 relative URLs remain in fonts-inline.css (100% self-contained data URIs).'); | |
| } | |
| const outputFontsCssPath = path.join(backendStaticFontsDir, 'fonts-inline.css'); | |
| fs.writeFileSync(outputFontsCssPath, fontsCss, 'utf8'); | |
| console.log(`β Generated ${outputFontsCssPath} (${(fontsCss.length / 1024).toFixed(1)} KB)`); | |
| console.log('β All inline assets built successfully.'); | |