UNPKG

vite-plugin-webfont-dl

Version:

Vite plugin for downloading and injecting webfonts

16 lines (15 loc) 17.5 kB
"use strict";var K=Object.create;var w=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var X=Object.getPrototypeOf,Y=Object.prototype.hasOwnProperty;var Q=(n,e)=>{for(var t in e)w(n,t,{get:e[t],enumerable:!0})},O=(n,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of J(e))!Y.call(n,r)&&r!==t&&w(n,r,{get:()=>e[r],enumerable:!(s=G(e,r))||s.enumerable});return n};var d=(n,e,t)=>(t=n!=null?K(X(n)):{},O(e||!n||!n.__esModule?w(t,"default",{value:n,enumerable:!0}):t,n)),Z=n=>O(w({},"__esModule",{value:!0}),n);var re={};Q(re,{ViteWebfontDownload:()=>se,default:()=>se,viteWebfontDl:()=>se,viteWebfontDownload:()=>se,webfontDl:()=>se,webfontDownload:()=>se});module.exports=Z(re);var N=require("http"),T=d(require("picocolors")),V=require("axios");var ee={injectAsStyleTag:!0,minifyCss:!0,embedFonts:!1,async:!0,cache:!0,proxy:!1,assetsSubfolder:"",throwError:!1,subsetsAllowed:[]},B=(n={})=>({...ee,...n});var f=require("process"),H=d(require("picocolors")),b=class{setResolvedLogger(e){this.resolvedLogger=e}isTty(){return f.stdout.isTTY&&!f.env.CI}info(e,t=!0){var s;this.clearLine(),(s=this.resolvedLogger)==null||s.info((t?this.prefix():"")+e)}error(e,t=!0){var s;this.clearLine(),(s=this.resolvedLogger)==null||s.error((t?this.prefix():"")+e)}clearLine(){this.isTty()&&(f.stdout.clearLine(0),f.stdout.cursorTo(0))}flashLine(e,t=!0){this.isTty()?(this.clearLine(),e=(t?this.prefix():"")+e,e.length<f.stdout.columns?f.stdout.write(e):f.stdout.write(e.substring(0,f.stdout.columns-1)),this.flashTimeout&&clearTimeout(this.flashTimeout),this.flashTimeout=setTimeout(()=>{this.clearLine()},500)):this.info(e,t)}prefix(){return H.default.dim("[webfont-dl] ")}};var F=require("axios"),M=require("http"),U=require("https"),u=d(require("picocolors")),y=class{constructor(e,t){this.options=e;this.logger=t;this.userAgentWoff2="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.0.0 Safari/537.36";this.maxTries=3;this.timeout=2500;this.waitBeforeRetry=[25,2500];this.axios=new F.Axios({timeout:this.timeout,proxy:this.options.proxy,httpAgent:new M.Agent({keepAlive:!0,family:4}),httpsAgent:new U.Agent({keepAlive:!0,family:4})})}async download(e,t,s=1){try{let r=await this.toRequest(e,t);return s>1&&this.logger.info(u.default.green(`\u2713 ${e}`)+" "+u.default.dim(`(try #${s})`)),r}catch(r){if(this.logger.error(u.default.red(`\u2717 ${e}`)+" "+u.default.dim(`(try #${s})`)+": "+((0,F.isAxiosError)(r)?r.message:r)),s<this.maxTries)return await new Promise(i=>setTimeout(i,this.randomWaitInterval())),this.download(e,t,s+1);throw r}}toRequest(e,t){return this.axios.get(e,{headers:{"User-Agent":this.userAgentWoff2},responseType:t!=null?t:"arraybuffer"})}randomWaitInterval(){return Math.floor(Math.random()*(this.waitBeforeRetry[0]-this.waitBeforeRetry[1]+1)+this.waitBeforeRetry[1])}};var k=require("buffer"),v=require("flat-cache");var j="3.12.0";var C=class{constructor(e){this.enabled=!0;this.cacheID=`plugin-webfont-dl_${j}.json`;this.cacheDir="node_modules/.vite/cache";this.hits={css:0,font:0};e.cache===!1&&(this.enabled=!1),this.cache=(0,v.create)({cacheId:this.cacheID,cacheDir:this.cacheDir}),this.enabled||this.clear()}setCacheDir(e){this.cacheDir!==e&&(this.cacheDir=e,this.cache=(0,v.create)({cacheId:this.cacheID,cacheDir:this.cacheDir}))}get(e,t){if(!this.enabled)return;let s=this.cache.get(t);if(s)return e==="css"?this.hits.css++:this.hits.font++,s.type==="Buffer"?k.Buffer.from(s.data):s}save(e,t,s){this.enabled&&(this.cache.set(t,s),this.cache.save(!0))}clear(){(0,v.clearCacheById)(this.cacheID)}};var I=require("url"),x=class{constructor(e,t,s){this.logger=e;this.downloader=t;this.fileCache=s;this.fontUrlRegex=/[-a-z0-9@:%_+.~#?&/=]+\.(?:woff2?|eot|ttf|otf|svg)/gi}async loadAll(e){let t="";for(let s of e){let r=await this.load(s),i=this.normalizeUrls(r.trim(),s);t+=i+` `}return t.trim()}normalizeUrls(e,t){return e=e.replaceAll(this.fontUrlRegex,s=>s.startsWith("http://")||s.startsWith("https://")?s:s.startsWith("//")?"https:"+s:new I.URL(s,t).href),e}async load(e){this.logger.flashLine(e);let t=this.fileCache.get("css",e);if(t)return t;let s=await this.downloader.download(e,"text");return this.fileCache.save("css",e,s.data),s.data}};var W=require("crypto"),S=class{constructor(e){this.options=e;this.fontSrcRegex=/(?:https?:)?\/\/[-a-z0-9@:%_+.~#?&/=]+\.(?:woff2?|eot|ttf|otf|svg)/gi;this.googleFontsKitSrcRegex=/https:\/\/fonts\.gstatic\.com\/l\/font\?kit=[a-z0-9&=_-]+/gi;this.fontFaceWithSubsetCommentRegex=/\/\*(.*?)\*\/\s*@font-face\s*{[^}]*}/gi;this.fontFilenameRegex=/[^/]+\.(?:woff2?|eot|ttf|otf|svg)/i;this.googleFontsFileRegex=/\?kit=([a-z0-9_-]+)/i;this.webfontProviders=[/https:\/\/fonts\.googleapis\.com\//i,/https:\/\/fonts\.gstatic\.com\//i,/https:\/\/fonts\.bunny\.net\//i,/https:\/\/api\.fontshare\.com\//i]}parse(e,t,s){var p,c;let r=new Map;if(this.options.subsetsAllowed.length){let g="",m=e.matchAll(this.fontFaceWithSubsetCommentRegex);for(let a of m)this.options.subsetsAllowed.includes(a[1].trim())&&(g+=a[0].trim()+` `);e=g.trim()}let i=e.matchAll(this.fontSrcRegex),o=e.matchAll(this.googleFontsKitSrcRegex);if(i)for(let g of i){let m=g.toString(),a=m.match(this.fontFilenameRegex);if(a){let l=a[0];r.set(l,{url:m,filename:l,localPath:t+(s?s+"/":"")+l})}}if(o)for(let g of o){let m=g.toString(),a=(c=(p=m.match(this.googleFontsFileRegex))==null?void 0:p[1])==null?void 0:c.toString();if(a){a.length>50&&(a=(0,W.createHash)("sha1").update(a).digest("hex"));let l=a+".woff2";r.set(l,{url:m,filename:l,localPath:t+(s?s+"/":"")+l})}}return{cssContent:e,fonts:r}}parseBundleCss(e,t,s){let r=new Map,i=new Set([]),o=[],p=/@import\s*(?:url\()?['"]?([^\s'")]+)['"]?\)?;/g,c=/@font-face\s*{[^}]*}/g,g=[...e.matchAll(p)];return[...e.matchAll(c)].forEach(a=>{let l=a[0];this.parse(l,t,s).fonts.forEach(L=>{this.webfontProviders.some(_=>_.test(L.url))&&(r.set(L.filename,L),o.push(a[0]))})}),g.forEach(a=>{let l=a[1];this.webfontProviders.some(E=>E.test(l))&&(i.add(l),o.push(a[0]))}),{fonts:r,webfontUrlsCss:i,matchedCssParts:o}}};var q=d(require("clean-css")),z=(o=>(o.woff2="font/woff2",o.woff="font/woff",o.ttf="font/ttf",o.otf="font/otf",o.svg="image/svg+xml",o.eot="application/vnd.ms-fontobject",o))(z||{}),D=class{constructor(e){this.options=e}transform(e,t){return t.forEach(s=>{if(!this.options.embedFonts||!s.binary)e=e.replaceAll(s.url,s.localPath);else if(s.binary){let r=s.url.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),i=new RegExp(`url\\(['"]?\\b${r}\\b['"]?\\)`,"gi");e=e.replaceAll(i,`url(data:${this.getFontMime(s)};base64,${s.binary.toString("base64")})`)}}),e}formatCss(e,t){return!t&&this.options.minifyCss?this.minify(e):e.trim()}minify(e){return new q.default().minify(e).styles}getFontMime(e){let t=e.filename.replace(/^.+\.(.+)$/,"$1");return z[t]}};var R=class{constructor(e){this.options=e}injectAsStylesheet(e,t,s){return this.options.async?this.injectAsync(e,t,s):this.injectSync(e,t,s)}injectAsStyleTag(e,t){return this.options.minifyCss?e.replace(/(\n?)([ \t]*)<\/head>/,`$1$2$2<style>${t}</style>$1$2</head>`):e.replace(/([ \t]*)<\/head>/,`$1$1<style> ${t.replace(/^/gm,"$1$1$1")} $1$1</style> $1</head>`)}injectAsync(e,t,s){return e.replace(/([ \t]*)<\/head>/,`$1$1<link rel="preload" as="style" href="${t}${s}"> $1$1<link rel="stylesheet" media="print" onload="this.onload=null;this.removeAttribute('media');" href="${t}${s}"> $1</head>`)}injectSync(e,t,s){return e.replace(/([ \t]*)<\/head>/,`$1$1<link rel="preload" as="style" href="${t}${s}"> $1$1<link rel="stylesheet" href="${t}${s}"> $1</head>`)}};var A=class{constructor(e,t,s){this.logger=e;this.downloader=t;this.fileCache=s}async load(e){let t=this.fileCache.get("font",e);if(t)return t;this.logger.flashLine(e);let s=await this.downloader.download(e);return this.fileCache.save("font",e,s.data),s.data}};var $=class{constructor(){this.webfontRegexes=[/(<!--.*?)?<link[^>]+rel=['"]?stylesheet['"]?[^>]+href=['"]?(https:\/\/fonts\.googleapis\.com[^'">]+)['"]?[^>]*>/gs,/(<!--.*?)?<link[^>]+href=['"]?(https:\/\/fonts\.googleapis\.com[^'">]+)['"]?[^>]+rel=['"]?stylesheet['"]?[^>]*>/gs,/(<!--.*?)?<link[^>]+rel=['"]?stylesheet['"]?[^>]+href=['"]?(https:\/\/fonts\.bunny\.net[^'">]+)['"]?[^>]*>/gs,/(<!--.*?)?<link[^>]+href=['"]?(https:\/\/fonts\.bunny\.net[^'">]+)['"]?[^>]+rel=['"]?stylesheet['"]?[^>]*>/gs,/(<!--.*?)?<link[^>]+rel=['"]?stylesheet['"]?[^>]+href=['"]?(https:\/\/api\.fontshare\.com[^'">]+)['"]?[^>]*>/gs,/(<!--.*?)?<link[^>]+href=['"]?(https:\/\/api\.fontshare\.com[^'">]+)['"]?[^>]+rel=['"]?stylesheet['"]?[^>]*>/gs,/(<!--.*?)?<link[^>]+rel=['"]?stylesheet['"]?[^>]+href=['"]?(https:\/\/cdn\.jsdelivr\.net[^'">]+\.css)['"]?[^>]*>/gs,/(<!--.*?)?<link[^>]+href=['"]?(https:\/\/cdn\.jsdelivr\.net[^'">]+\.css)['"]?[^>]+rel=['"]?stylesheet['"]?[^>]*>/gs,/(<!--.*?)?<link[^>]+rel=['"]?stylesheet['"]?[^>]+href=['"]?(https:\/\/rsms\.me[^'">]+)['"]?[^>]*>/gs,/(<!--.*?)?<link[^>]+href=['"]?(https:\/\/rsms\.me[^'">]+)['"]?[^>]+rel=['"]?stylesheet['"]?[^>]*>/gs];this.preconnectRegexes=[/<link[^>]+rel=['"]?preconnect['"]?[^>]+href=['"]?https:\/\/fonts\.googleapis\.com['"]?[^>]*>/,/<link[^>]+href=['"]?https:\/\/fonts\.googleapis\.com['"]?[^>]+rel=['"]?preconnect['"]?[^>]*>/,/<link[^>]+rel=['"]?preconnect['"]?[^>]+href=['"]?https:\/\/fonts\.gstatic\.com['"]?[^>]*>/,/<link[^>]+href=['"]?https:\/\/fonts\.gstatic\.com['"]?[^>]+rel=['"]?preconnect['"]?[^>]*>/,/<link[^>]+rel=['"]?preconnect['"]?[^>]+href=['"]?https:\/\/fonts\.bunny\.net['"]?[^>]*>/,/<link[^>]+href=['"]?https:\/\/fonts\.bunny\.net['"]?[^>]+rel=['"]?preconnect['"]?[^>]*>/,/<link[^>]+rel=['"]?preconnect['"]?[^>]+href=['"]?https:\/\/api\.fontshare\.com['"]?[^>]*>/,/<link[^>]+href=['"]?https:\/\/api\.fontshare\.com['"]?[^>]+rel=['"]?preconnect['"]?[^>]*>/,/<link[^>]+rel=['"]?preconnect['"]?[^>]+href=['"]?https:\/\/rsms\.me['"]?[^>]*>/,/<link[^>]+href=['"]?https:\/\/rsms\.me['"]?[^>]+rel=['"]?preconnect['"]?[^>]*>/]}parse(e){let t=new Set;for(let s of this.webfontRegexes){let r=e.matchAll(s);if(r)for(let i of r)(!i[1]||i[1].includes("-->"))&&t.add(i[2])}return t}removeTags(e){return e=this.removePreconnectTags(e),e=this.removeWebfontTags(e),e}removePreconnectTags(e){for(let t of this.preconnectRegexes){let s=new RegExp("[ ]*"+t.source+`(\r |\r| )?`,"g");e=e.replace(s,"")}return e}removeWebfontTags(e){for(let t of this.webfontRegexes){let s=new RegExp("[ ]*"+t.source+`(\r |\r| )?`,"g");e=e.replace(s,"$1")}return e}};var h=d(require("picocolors")),P=class{constructor(e,t){this.emitFile=e=>e.name||"ref";this.getFileName=e=>e;this.cssFilename="webfonts.css";this.base="/";this.assetsDir="";this.cssPath=this.cssFilename;this.isDevServer=!1;this.cssContent="";this.parseFontDefinitions=()=>{let e=this.cssParser.parse(this.cssContent,this.base,this.assetsDir);this.cssContent=e.cssContent,e.fonts.forEach(t=>{this.fonts.set(t.filename,t)})};this.saveFont=(e,t)=>{if(this.options.embedFonts)e.binary=t;else{let s=this.options.assetsSubfolder.trim().replace(/^\/+/,"").replace(/\/+$/,"").trim();s.includes("../")&&(s="");let r=this.saveFile((s?`${s}/`:"")+e.filename,t);if(this.options.injectAsStyleTag)e.localPath=this.base+r;else{let i=this.assetsDir+"/";e.localPath=r,e.localPath.startsWith(i)&&(e.localPath=e.localPath.substring(i.length))}}};this.replaceFontUrls=()=>{this.cssContent=this.cssTransformer.transform(this.cssContent,this.fonts)};this.formatCss=()=>{this.cssContent=this.cssTransformer.formatCss(this.cssContent,this.isDevServer)};!Array.isArray(e)&&typeof e!="string"&&(e=[]),typeof e=="string"&&e!==""&&(e=[e]),this.webfontUrls=new Set(e||[]),this.webfontUrlsHtml=new Set([]),this.webfontUrlsCss=new Set([]),this.options=B(t),this.logger=new b,this.downloader=new y(this.options,this.logger),this.fileCache=new C(this.options),this.cssLoader=new x(this.logger,this.downloader,this.fileCache),this.cssParser=new S(this.options),this.cssTransformer=new D(this.options),this.cssInjector=new R(this.options),this.fontLoader=new A(this.logger,this.downloader,this.fileCache),this.indexHtmlProcessor=new $,this.fonts=new Map,this.fontUrlsDevMap=new Map}getOptions(){return this.options}setBase(e){this.base=e}getBase(){return this.base}setAssetsDir(e){this.assetsDir=e,this.cssPath=e+"/"+this.cssFilename}setCacheDir(e){this.fileCache.setCacheDir(e)}getCssPath(){return this.cssPath}getCssFilename(){return this.cssFilename}setMinifyCss(e){this.options.minifyCss=e}setResolvedLogger(e){this.logger.setResolvedLogger(e)}setIsDevServer(e){this.isDevServer=e}getIsDevServer(){return this.isDevServer}setEmitFileFunction(e){this.emitFile=e}setGetFilenameFunction(e){this.getFileName=e}collectWebfontsFromHtml(e){let t=this.indexHtmlProcessor.parse(e);for(let s of t)this.webfontUrlsHtml.add(s)}collectWebfontsFromBundleCss(e){this.webfontUrlsCss.clear();for(let t in e)if(/\.css$/.exec(t)){let s=e[t].source.toString(),r=this.cssParser.parseBundleCss(s,this.base,this.assetsDir);r.matchedCssParts.length&&(r.fonts.forEach(i=>{this.fonts.set(i.filename,i)}),r.webfontUrlsCss.forEach(i=>{this.webfontUrlsCss.add(i)}),r.matchedCssParts.forEach(i=>{s=s.replaceAll(i,""),this.cssContent+=i+` `}),e[t].source=s)}}clearWebfontUrlsHtml(){this.webfontUrlsHtml.clear()}async downloadWebfontCss(){this.cssContent="";let e=Date.now(),t=new Set([...this.webfontUrls,...this.webfontUrlsHtml,...this.webfontUrlsCss]);return t.size&&(this.cssContent+=await this.cssLoader.loadAll(t)),this.isDevServer||this.logger.info(h.default.green("\u2713")+" "+t.size.toString()+" webfont css downloaded. "+h.default.dim("("+h.default.bold(this.toDuration(e))+(t.size?", "+(this.options.cache!==!1?`cache hit: ${h.default.bold(this.toPercent(this.fileCache.hits.css,t.size))}`:"cache disabled"):"")+")"),!1),this.cssContent.length>0}async downloadFont(e){let t=await this.fontLoader.load(e);return this.logger.clearLine(),t}async downloadFonts(){let e=Date.now();for(let[,t]of this.fonts){let s=await this.fontLoader.load(t.url);this.saveFont(t,s)}this.logger.info(h.default.green("\u2713")+" "+this.fonts.size+" webfonts downloaded. "+h.default.dim("("+h.default.bold(this.toDuration(e))+(this.fonts.size?", "+(this.options.cache!==!1?`cache hit: ${h.default.bold(this.toPercent(this.fileCache.hits.font,this.fonts.size))}`:"cache disabled"):"")+")"),!1)}saveCss(){return this.cssPathSaved=this.saveFile(this.cssFilename,this.cssContent),this.cssPathSaved}saveFile(e,t){let s=this.emitFile({name:e,originalFileName:e,type:"asset",source:t});return this.getFileName(s)}removeTagsFromHtml(e){return this.indexHtmlProcessor.removeTags(e)}injectToHtml(e){return this.isDevServer||this.options.injectAsStyleTag===!1?this.cssInjector.injectAsStylesheet(e,this.base,this.cssPathSaved||this.cssPath):this.cssInjector.injectAsStyleTag(e,this.cssContent)}async loadDevServerFonts(){await this.downloadWebfontCss(),this.parseFontDefinitions(),this.replaceFontUrls(),this.formatCss(),this.fontUrlsDevMap.clear(),this.fonts.forEach(e=>{this.fontUrlsDevMap.set(e.localPath,e.url)})}async getDevServerMiddlewareCss(e){try{await this.loadDevServerFonts(),e.setHeader("Access-Control-Allow-Origin","*"),e.setHeader("Content-Type","text/css"),e.end(this.cssContent)}catch(t){this.logger.error(h.default.red(t.message)),e.statusCode=502,e.setHeader("X-Error",t.message.replace(/^Error: /,"")),e.end()}}async getDevServerMiddlewareGeneral(e,t,s){var i;let r=(i=e.originalUrl)==null?void 0:i.replace(/[?#].*$/,"");if(r&&this.fontUrlsDevMap.has(r)){let o=await this.downloadFont(this.fontUrlsDevMap.get(r));t.setHeader("Access-Control-Allow-Origin","*"),t.setHeader("Content-Type","font/"+((r==null?void 0:r.replace(/^.*\./,""))||"woff2")),t.end(o)}else s()}logError(e){this.logger.error(e)}toDuration(e){return(Date.now()-e).toLocaleString()+" ms"}toPercent(e,t){return(Math.round(e/t*100*100)/100).toFixed(2)+"%"}};function se(n,e){let t=new P(n,e),s=new Map;return{name:"vite-plugin-webfont-dl",enforce:"post",configResolved(r){t.setBase(r.base),t.setAssetsDir(r.build.assetsDir),t.setCacheDir(r.cacheDir),r.build.minify===!1&&!t.getOptions().minifyCss&&t.setMinifyCss(!1),t.setResolvedLogger(r.logger)},configureServer(r){t.setIsDevServer(!0),t.setAssetsDir("@webfonts"),r.middlewares.use(t.getBase()+t.getCssPath(),(i,o)=>{t.getDevServerMiddlewareCss(o)}),r.middlewares.use(t.getBase()+t.getCssFilename(),(i,o)=>{t.getDevServerMiddlewareCss(o)}),r.middlewares.use((i,o,p)=>{t.getDevServerMiddlewareGeneral(i,o,p)})},transformIndexHtml(r,i){return t.getIsDevServer()?(t.clearWebfontUrlsHtml(),t.collectWebfontsFromHtml(r),r=t.removeTagsFromHtml(r),r=t.injectToHtml(r)):s.set(i.path.replace(/^\//,""),r),r},async generateBundle(r,i){t.setEmitFileFunction(o=>this.emitFile(o)),t.setGetFilenameFunction(o=>this.getFileName(o)),t.clearWebfontUrlsHtml(),s.forEach(o=>t.collectWebfontsFromHtml(o)),t.collectWebfontsFromBundleCss(i);try{await t.downloadWebfontCss()&&(t.parseFontDefinitions(),await t.downloadFonts(),t.replaceFontUrls(),t.formatCss(),(!t.getOptions().injectAsStyleTag||!s.size)&&t.saveCss(),s.forEach((o,p)=>{let c=i[p];c!==void 0&&(c.source=t.removeTagsFromHtml(c.source),c.source=t.injectToHtml(c.source),s.set(p,c.source))}))}catch(o){if(t.logError(T.default.red(o.message)),o instanceof V.AxiosError&&o.request instanceof N.ClientRequest&&t.logError(T.default.red(`${o.request.method} ${o.request.protocol}//${o.request.host}${o.request.path}`)),t.getOptions().throwError)throw o}}}}0&&(module.exports={ViteWebfontDownload,viteWebfontDl,viteWebfontDownload,webfontDl,webfontDownload});