UNPKG

vite-plugin-font

Version:

An automatic Web Font optimization plugin that supports many platforms such as Vite, Next, Nuxt, and more.

114 lines (109 loc) 3.79 kB
'use strict'; Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const cnFontSplit = require('cn-font-split'); const path = require('path'); const fs = require('fs-extra'); const url = require('url'); const crypto = require('crypto'); const cnFontMetrics = require('cn-font-metrics'); function getFileName(id) { return path.basename(id).replace(/\./g, "_"); } function chunk(arr, size = 500) { if (arr) { let result = []; for (let i = 0; i < arr.length; i += size) { result.push(arr.slice(i, i + size)); } return result; } else { return; } } class BundlePlugin { constructor(config, key = "default") { this.config = config; this.key = key; this.subsets = undefined; } /** 获取正确的缓存文件夹的位置 */ getCachedPath(p) { return path.resolve(this.config.cacheDir, getFileName(p)); } /** 创建 CSS 封装层源代码 */ async createSourceCode(p) { const resolvedPath = this.getCachedPath(p); const reporter = cnFontSplit.decodeReporter( await fs.promises.readFile(resolvedPath + "/reporter.bin") ); const metrics = await fs.readJSON(resolvedPath + "/metrics.json"); const details = { ...reporter.toObject(), ...metrics }; const code = Object.entries(details).map(([k, v]) => { return `export const ${k} = ${JSON.stringify(v)};`; }).join("\n"); const resultCSS = decodeURI(url.pathToFileURL(resolvedPath).pathname); const key = (Math.random() * 1e5).toFixed(0); return `import '${resultCSS}/metrics.css?t=${key}'; import '${resultCSS}/result.css?t=${key}'; ` + code; } /** 检查整个系统的缓存 */ async checkCache(resolvedPath) { const isFullCached = await Promise.all([ fs.exists(resolvedPath), fs.exists(path.resolve(resolvedPath, "result.css")), fs.exists(path.resolve(resolvedPath, "metrics.css")), fs.exists(path.resolve(resolvedPath, "metrics.json")), fs.exists(path.resolve(resolvedPath, "reporter.bin")) ]); return isFullCached.every((i) => i); } /** 重新进行预构建字体 */ async prebuild(filePath, mode = "full") { const resolvedPath = this.getCachedPath(filePath); const isCached = await this.checkCache(resolvedPath); if (!isCached && this.config.server !== false) { console.log( "vite-plugin-font | font pre-building | " + resolvedPath ); const FontPath = filePath.split("?")[0]; const onlySubset = mode !== "full"; await cnFontSplit.fontSplit({ ...this.config, input: FontPath, outDir: resolvedPath, reporter: true, languageAreas: !onlySubset, autoSubset: true, fontFeature: true, reduceMins: !onlySubset, subsetRemainChars: !onlySubset, subsets: onlySubset ? chunk(this.subsets?.flat()) : undefined, silent: true }).catch((e) => { console.error(e); }); await this.createCSSFontFallback(FontPath, resolvedPath); } else { console.log("vite-plugin-font | using cache | " + resolvedPath); } } /** 写入 CSS 字体的 fallback 选项,减少布局抖动 */ async createCSSFontFallback(FontPath, resolvedPath) { const hash = crypto.createHash("md5").update(FontPath).digest("hex"); const { fontFamilyString, css } = await cnFontMetrics.createChineseCrossPlatformFallbackCss( FontPath, ` ${this.key} ${hash.slice(0, 6)}` ); await fs.promises.writeFile( path.resolve(resolvedPath, "metrics.css"), css ); await fs.promises.writeFile( path.resolve(resolvedPath, "metrics.json"), JSON.stringify({ fontFamilyFallback: fontFamilyString }) ); } } exports.BundlePlugin = BundlePlugin; exports.getFileName = getFileName;