UNPKG

node-font2base64

Version:

Convert font to base64 url or src, and to inject src into style files

370 lines (367 loc) 11.2 kB
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw new Error('Dynamic require of "' + x + '" is not supported'); }); // src/index.ts import fs from "fs"; import path from "path"; import fileType from "file-type"; import css from "css"; // src/helpers.ts import { readFile, writeFile, stat, statSync, readdirSync, readdir } from "fs"; import { join, extname } from "path"; var isArray = Array.isArray; var isNil = (value) => value === void 0 || value === null; var castArray = (value) => { if (isNil(value)) return []; return isArray(value) ? value : [value]; }; var each = (items, fn, startOffset = 0, endOffset = 0) => { for (let x = startOffset; x < items.length - endOffset; x++) { if (fn(items[x], x, items) === false) break; } }; var eachArray = (items, fn, startOffset = 0, endOffset = 0) => { items = castArray(items); each(items, fn, startOffset, endOffset); }; var promiseMap = (items, fn) => { items = castArray(items); const promises = []; for (let x = 0; x < items.length; x++) { promises.push(fn(items[x], x, items)); } return Promise.all(promises); }; var promisify = (fn) => (...args) => new Promise((resolve, reject) => { fn(...args, (err, data) => { if (err) return reject(err); return resolve(data); }); }); var readFileAsync = promisify(readFile); var writeFileAsync = promisify(writeFile); var statAsync = promisify(stat); var readdirAsync = promisify(readdir); var readAllFilesAsync = async (fileOrPath, allowedExts = []) => { const files = []; await promiseMap(fileOrPath, async (fp) => { try { const stat2 = await statAsync(fp); if (stat2.isDirectory()) { const subs = await readdirAsync(fp); const subPaths = subs.map((s) => join(fp, s)); const subFiles = await readAllFilesAsync(subPaths, allowedExts); files.push(...subFiles); } else { if (allowedExts.includes(extname(fp))) files.push(fp); } } catch (e) { } }); return files; }; var readAllFilesSync = (fileOrPath, allowedExts = []) => { const files = []; eachArray(fileOrPath, (fp) => { try { const stat2 = statSync(fp); if (stat2.isDirectory()) { const subs = readdirSync(fp); const subPaths = subs.map((s) => join(fp, s)); const subFiles = readAllFilesSync(subPaths, allowedExts); files.push(...subFiles); } else { if (allowedExts.includes(extname(fp))) files.push(fp); } } catch (e) { } }); return files; }; // src/index.ts var { fromBuffer: fileTypeFromBuffer } = fileType; var sync; try { sync = __require("promise-synchronizer"); } catch (er) { sync = null; } var MAX_PATH_LENGTH = 200; var fontMap = { ".svg": { mediaType: "image/svg+xml", format: "svg" }, ".ttf": { mediaType: "font/truetype", format: "truetype" }, ".otf": { mediaType: "font/opentype", format: "opentype" }, ".eot": { mediaType: "application/vnd.ms-fontobject", format: "embedded-opentype" }, ".sfnt": { mediaType: "application/font-sfnt", format: "sfnt" }, ".woff2": { mediaType: "application/font-woff2", format: "woff2" }, ".woff": { mediaType: "application/font-woff", format: "woff" } }; var _fontTypes = Object.keys(fontMap); var _styleTypes = [".css", ".scss", ".less"]; var _buffToBase64 = (buff) => buff.toString("base64"); var _readBuffer = async (buff) => { const base64 = _buffToBase64(buff); const data = await fileTypeFromBuffer(buff); data.base64 = base64; return data; }; var _readBufferSync = (buff) => { const base64 = _buffToBase64(buff); let data; if (sync) { data = sync(fileTypeFromBuffer(buff)); } else { data = {}; } data.base64 = base64; return data; }; var _toDataUrl = (mediaType, base64) => `data:${mediaType};charset=utf-8;base64,${base64}`; var _toDataSrc = (dataUrl, format) => `url(${dataUrl}) format('${format}')`; var _getMeta = (fpath, ext) => { const naive = path.parse(fpath).ext; if (naive === ".svg") return fontMap[naive]; const type = ext && `.${ext}` || naive; return fontMap[type]; }; var toDataUrl = (fpath, { ext, mime, base64 }) => { const meta = _getMeta(fpath, ext); const mediaType = meta.mediaType; return _toDataUrl(mediaType, base64); }; var toDataSrc = (fpath, { ext, mime, base64 }) => { const meta = _getMeta(fpath, ext); if (!meta) return null; const mediaType = meta.mediaType; const format = meta.format; const dataUrl = _toDataUrl(mediaType, base64); return _toDataSrc(dataUrl, format); }; var _encodeToDataUrl = async (fpath) => { const buff = await readFileAsync(fpath); const data = await _readBuffer(buff); return toDataUrl(fpath, data); }; var _encodeToDataUrlSync = (fpath) => { const buff = fs.readFileSync(fpath); const data = _readBufferSync(buff); return toDataUrl(fpath, data); }; var _encodeToDataSrc = async (fpath) => { const buff = await readFileAsync(fpath); const data = await _readBuffer(buff); return toDataSrc(fpath, data); }; var _encodeToDataSrcSync = (fpath) => { const buff = fs.readFileSync(fpath); const data = _readBufferSync(buff); return toDataSrc(fpath, data); }; var encodeToDataUrl = async (fpath) => { if (isArray(fpath)) return Promise.all(fpath.map(_encodeToDataUrl)); return _encodeToDataUrl(fpath); }; var encodeToDataUrlSync = (fpath) => { if (isArray(fpath)) return fpath.map(_encodeToDataUrlSync); return _encodeToDataUrlSync(fpath); }; var encodeToDataSrc = (fpath) => { if (isArray(fpath)) return Promise.all(fpath.map(_encodeToDataSrcSync)); return _encodeToDataSrc(fpath); }; var encodeToDataSrcSync = (fpath) => { if (isArray(fpath)) return fpath.map(_encodeToDataSrcSync); return _encodeToDataSrcSync(fpath); }; var _extractSrcUrl = (value) => { const rx = /url\('?(.+?)(\??#.+?)?'?\)/g; const arr = rx.exec(value); if (!arr) return value; return arr[1]; }; var _updateCssAst = (content, validator, dataUrlMap, fullpathMatch, cssRoot) => { var _a; const keys = Object.keys(dataUrlMap); const ast = css.parse(content); let modified = false; each(((_a = ast.stylesheet) == null ? void 0 : _a.rules) || [], (rule) => { if (rule.type === "font-face") { each(rule.declarations || [], (dec) => { var _a2; if (dec.property === "src") { const urls = ((_a2 = dec.value) == null ? void 0 : _a2.split(",")) || []; const nUrls = urls.map((url) => { let nUrl = url; const urlpath = _extractSrcUrl(url); if (urlpath.length > MAX_PATH_LENGTH) return nUrl; const fullmatch = fullpathMatch && cssRoot; const tpathToCompare = fullmatch && path.resolve(cssRoot, urlpath) || path.basename(urlpath); each(keys, (key) => { const kpathToCompare = fullmatch && path.resolve(key) || path.basename(key); if (validator(tpathToCompare, kpathToCompare, urlpath, key)) { nUrl = dataUrlMap[key] || ""; modified = true; return false; } return true; }); return nUrl; }); dec.value = nUrls.join(",\n"); } }); } }); const result = { modified }; if (modified) { const newContent = css.stringify(ast); result.content = newContent; } else { result.content = content; } return result; }; var _defaultValidator = (path1, path2) => path1 === path2; var _generateDataUrlMap = async (fpath, fontTypes) => { const dataUrlMap = {}; try { await promiseMap(await readAllFilesAsync(fpath, fontTypes), async (fp) => { dataUrlMap[fp] = await _encodeToDataSrc(fp); }); } catch (e) { console.error(e); } return dataUrlMap; }; var _generateDataUrlMapSync = (fpath, fontTypes) => { const dataUrlMap = {}; eachArray(readAllFilesSync(fpath, fontTypes), (fp) => { dataUrlMap[fp] = _encodeToDataSrcSync(fp); }); return dataUrlMap; }; var injectBase64 = async (fpath, cpath, { validator = _defaultValidator, fontTypes = _fontTypes, cssTypes = _styleTypes, resave = true, fullpathMatch = false } = {}) => { const dataUrlMap = await _generateDataUrlMap(fpath, fontTypes); const results = []; try { await promiseMap(await readAllFilesAsync(cpath, cssTypes), async (cp) => { const content = await readFileAsync(cp, "utf8"); const result = _updateCssAst(content, validator, dataUrlMap, fullpathMatch, path.parse(cp).dir); if (result.modified && resave) { await writeFileAsync(cp, result.content, "utf8"); } result.filepath = cp; results.push(result); }); } catch (err) { console.error(err); } return resave ? true : results; }; var injectBase64Sync = (fpath, cpath, { validator = _defaultValidator, fontTypes = _fontTypes, cssTypes = _styleTypes, resave = true, fullpathMatch = false } = {}) => { const dataUrlMap = _generateDataUrlMapSync(fpath, fontTypes); const results = []; try { eachArray(readAllFilesSync(cpath, cssTypes), (cp) => { const content = fs.readFileSync(cp, "utf8"); const result = _updateCssAst(content, validator, dataUrlMap, fullpathMatch, path.parse(cp).dir); if (result.modified && resave) { fs.writeFileSync(cp, result.content || "", "utf8"); } result.filepath = cp; results.push(result); }); } catch (err) { console.error(err); } return resave ? true : results; }; injectBase64.fromContent = async (fpath, content, { validator = _defaultValidator, fontTypes = _fontTypes, fullpathMatch = false, root = "" } = {}) => { const dataUrlMap = await _generateDataUrlMap(fpath, fontTypes); return _updateCssAst(content, validator, dataUrlMap, fullpathMatch, root); }; injectBase64Sync.fromContent = (fpath, content, { validator = _defaultValidator, fontTypes = _fontTypes, fullpathMatch = false, root = "" } = {}) => { const dataUrlMap = _generateDataUrlMapSync(fpath, fontTypes); return _updateCssAst(content, validator, dataUrlMap, fullpathMatch, root); }; injectBase64.fromBuffer = async (fpath, buffer, options) => { const content = buffer.toString("utf8"); return injectBase64.fromContent(fpath, content, options); }; injectBase64Sync.fromBuffer = (fpath, buffer, options) => { const content = buffer.toString("utf8"); return injectBase64Sync.fromContent(fpath, content, options); }; var src_default = { encodeToDataUrl, encodeToDataSrc, encodeToDataUrlSync, encodeToDataSrcSync, injectBase64, injectBase64Sync }; export { src_default as default, encodeToDataSrc, encodeToDataSrcSync, encodeToDataUrl, encodeToDataUrlSync, injectBase64, injectBase64Sync }; /*! * node-font2base64 * Copyright(c) 2023 Junmin Ahn * MIT Licensed */ //# sourceMappingURL=index.mjs.map