/** * Копирует Plyr и шрифты из node_modules в public/ (автономный деплой без CDN). * Plyr: 3.8.4 (package.json) */ import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const root = path.resolve(__dirname, ".."); const nodeModules = path.join(root, "node_modules"); const publicDir = path.join(root, "public"); const plyrOut = path.join(publicDir, "vendor", "plyr"); const fontsOut = path.join(publicDir, "assets", "fonts"); const fontsFilesOut = path.join(fontsOut, "files"); const PLYR_VERSION = "3.8.4"; /** @type {{ id: string, sources: { pkg: string, css: string, familyReplace?: [string, string] }[] }[]} */ const FONT_BUNDLES = [ { id: "merriweather", sources: [ { pkg: "@fontsource/alice", css: "400.css" }, { pkg: "@fontsource/merriweather", css: "400.css" }, { pkg: "@fontsource/merriweather", css: "700.css" }, ], }, { id: "geologica", sources: [ { pkg: "@fontsource-variable/geologica", css: "wght.css", familyReplace: ["Geologica Variable", "Geologica"], }, { pkg: "@fontsource/pt-sans", css: "400.css" }, { pkg: "@fontsource/pt-sans", css: "700.css" }, ], }, { id: "mono", sources: [ { pkg: "@fontsource/pt-mono", css: "400.css" }, { pkg: "@fontsource/source-code-pro", css: "400.css" }, { pkg: "@fontsource/source-code-pro", css: "500.css" }, { pkg: "@fontsource/source-code-pro", css: "600.css" }, { pkg: "@fontsource/source-code-pro", css: "700.css" }, ], }, ]; function ensureDir(dir) { fs.mkdirSync(dir, { recursive: true }); } function emptyDir(dir) { if (!fs.existsSync(dir)) return; for (const name of fs.readdirSync(dir)) { fs.rmSync(path.join(dir, name), { recursive: true, force: true }); } } function copyPlyr() { const dist = path.join(nodeModules, "plyr", "dist"); for (const name of ["plyr.css", "plyr.min.mjs"]) { const src = path.join(dist, name); if (!fs.existsSync(src)) { throw new Error(`Plyr file not found: ${src}. Run npm install.`); } } emptyDir(plyrOut); ensureDir(plyrOut); fs.copyFileSync(path.join(dist, "plyr.css"), path.join(plyrOut, "plyr.css")); fs.copyFileSync(path.join(dist, "plyr.min.mjs"), path.join(plyrOut, "plyr.min.mjs")); console.log(`Plyr ${PLYR_VERSION} -> public/vendor/plyr/`); } const woff2Re = /url\(\.\/files\/([^)]+\.woff2)\)/g; function transformFontCss(raw, familyReplace) { let out = raw; if (familyReplace) { const [from, to] = familyReplace; out = out.split(`'${from}'`).join(`'${to}'`); out = out.split(`"${from}"`).join(`"${to}"`); } out = out.replace(/,\s*url\(\.\/files\/[^)]+\.woff\)/g, ""); out = out.replace(/\s+format\(['"]woff['"]\)/g, ""); return out; } function collectWoff2Files(cssText) { const names = new Set(); let m; woff2Re.lastIndex = 0; while ((m = woff2Re.exec(cssText)) !== null) { names.add(m[1]); } return names; } function buildFontBundles() { emptyDir(fontsFilesOut); ensureDir(fontsFilesOut); const copied = new Set(); for (const bundle of FONT_BUNDLES) { const blocks = []; for (const source of bundle.sources) { const pkgDir = path.join(nodeModules, source.pkg); const cssPath = path.join(pkgDir, source.css); if (!fs.existsSync(cssPath)) { throw new Error(`Font CSS not found: ${cssPath}`); } const raw = fs.readFileSync(cssPath, "utf8"); const transformed = transformFontCss(raw, source.familyReplace); blocks.push(transformed.trim()); const filesDir = path.join(pkgDir, "files"); for (const fileName of collectWoff2Files(transformed)) { const srcFile = path.join(filesDir, fileName); const dstFile = path.join(fontsFilesOut, fileName); if (!fs.existsSync(srcFile)) { throw new Error(`Font file not found: ${srcFile}`); } if (!copied.has(fileName)) { fs.copyFileSync(srcFile, dstFile); copied.add(fileName); } } } const outCss = `${blocks.join("\n\n")}\n`; const outPath = path.join(fontsOut, `${bundle.id}.css`); fs.writeFileSync(outPath, outCss, "utf8"); console.log(`${bundle.id}.css (${copied.size} woff2 total so far) -> public/assets/fonts/`); } console.log(`Font files: ${copied.size} woff2 in public/assets/fonts/files/`); } function main() { copyPlyr(); buildFontBundles(); console.log("vendor:copy done."); } main();