@claspo/document-connector 16.7.5 → 16.7.6-ctx.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{encodeGoogleFontFamily,escapeFontFamilyForCss,normalizeFontFamilyName}from"./GoogleFontsUtils";export function buildMetadataRequest(t){const e=buildFontUrlParts(t);if(!e.length)return null;const n=t.some(t=>(t.variations||[]).some(t=>"italic"===t.style));return{url:makeGoogleFontsUrl(e.map(t=>t.urlPart).join("")),cacheKeys:t.map(t=>getMetadataCacheKey(t)),families:t.map(t=>t.fontFamily),includesItalic:n}}export function buildFallbackRequest(t){const e=buildFontUrlParts(t);return e.length?{url:makeGoogleFontsUrl(e.map(t=>t.urlPart).join("")),fontFamilyLabel:e.map(t=>t.fontFamily).join(", ")}:null}export async function fetchMetadata(t){let e;try{e=await fetch(t.url)}catch(e){throw new Error(`Google Fonts metadata request failed (network error), url: ${t.url}, ${e}`)}if(!e.ok)throw new Error(`Google Fonts metadata request failed, url: ${t.url}, status: ${e.status}`);return e.text()}export function parseFontFaces(t){const e=[],n=/@font-face\s*{[^}]*}/gi;let o;for(;null!==(o=n.exec(t||""));){const t=o[0],n=getFontFaceProperty(t,"font-family"),a=getFontFaceProperty(t,"font-style")||"normal",r=parseFontFaceWeight(getFontFaceProperty(t,"font-weight"));n&&r&&e.push({family:n,style:"italic"===a?"italic":"normal",minWeight:r.minWeight,maxWeight:r.maxWeight,src:getFontFaceProperty(t,"src"),unicodeRange:getFontFaceProperty(t,"unicode-range"),css:t})}return e}export function resolveResources(t,e){const n=new Map,o=[],a=new Set,r=getWeightRangeByResourceKey(e);return t.forEach(t=>{const o=normalizeFontFamilyName(t.fontFamily);(t.variations||[]).forEach(i=>{const s=findNearestFontFaces(e,t.fontFamily,i.style,i.weight);s.length&&(a.add(o),s.forEach(e=>{const o=getFontFaceResourceKey(e);if(n.has(o))return;const a=r.get(o)||{minWeight:e.minWeight,maxWeight:e.maxWeight};n.set(o,{key:o,family:t.fontFamily,style:e.style,minWeight:a.minWeight,maxWeight:a.maxWeight,src:e.src,unicodeRange:e.unicodeRange,css:replaceFontFaceWeight(e.css,a.minWeight,a.maxWeight),descriptor:makeFontDescriptor(t.fontFamily,e.style,a.minWeight)})}))})}),o.push(...getUnresolvedReturnedFontFamilyWarnings(t,e,a)),o.push(...getItalicOnlyWarnings(t,e)),{resources:Array.from(n.values()),warnings:o}}function getWeightRangeByResourceKey(t){const e=new Map;return(t||[]).forEach(t=>{const n=getFontFaceResourceKey(t),o=e.get(n);e.set(n,{minWeight:o?Math.min(o.minWeight,t.minWeight):t.minWeight,maxWeight:o?Math.max(o.maxWeight,t.maxWeight):t.maxWeight})}),e}export function mergeResourceCss(t,e,n){return replaceFontFaceWeight(t.css,e,n)}export function buildFontUrlParts(t){const e=makeFontOptions();return(t||[]).map(t=>{const n=(t.variations||[]).some(t=>"italic"===t.style),o=(t.variations||[]).some(t=>"normal"===t.style)||!n,a=[];return n?(o&&e.forEach(t=>a.push(`0,${t}`)),e.forEach(t=>a.push(`1,${t}`)),{fontFamily:t.fontFamily,urlPart:`&family=${encodeGoogleFontFamily(t.fontFamily)}:ital,wght@${a.join(";")}`}):{fontFamily:t.fontFamily,urlPart:`&family=${encodeGoogleFontFamily(t.fontFamily)}:wght@${e.join(";")}`}}).filter(t=>t.fontFamily)}export function makeFontOptions(){const t=[];for(let e=0;e<9;e++)t.push(100*(e+1));return t}export function makeGoogleFontsUrl(t){return`https://fonts.googleapis.com/css2?display=swap${t}`}export function getMetadataCacheKey(t){const e=Array.from(new Set((t.variations||[]).map(t=>t.style))).sort();return`${normalizeFontFamilyName(t.fontFamily)}|${e.join(",")||"normal"}`}export function getFontFaceProperty(t,e){const n=new RegExp(`${e}\\s*:\\s*([^;]+);`,"i"),o=`${t||""}`.match(n);return o?o[1].trim().replace(/^['"]|['"]$/g,""):""}export function parseFontFaceWeight(t){const e=`${t||""}`.match(/\d+(\.\d+)?/g);if(!e||!e.length)return null;const n=Number(e[0]),o=Number(e[e.length-1]);return{minWeight:Math.min(n,o),maxWeight:Math.max(n,o)}}export function findNearestFontFaces(t,e,n,o){const a=normalizeFontFamilyName(e),r=t.filter(t=>normalizeFontFamilyName(t.family)===a&&t.style===n);if(!r.length)return[];const i=r.reduce((t,e)=>{if(!t)return e;const n=getFontFaceDistance(e,o),a=getFontFaceDistance(t,o);if(n<a)return e;if(n>a)return t;const r=resolveWeightForFontFace(e,o),i=resolveWeightForFontFace(t,o);return o<=500?r<i?e:t:r>i?e:t},null),s=resolveWeightForFontFace(i,o);return r.filter(t=>resolveWeightForFontFace(t,o)===s)}export function getFontFaceDistance(t,e){return e>=t.minWeight&&e<=t.maxWeight?0:Math.min(Math.abs(e-t.minWeight),Math.abs(e-t.maxWeight))}export function resolveWeightForFontFace(t,e){return e<t.minWeight?t.minWeight:e>t.maxWeight?t.maxWeight:e}export function getFontFaceResourceKey(t){return[normalizeFontFamilyName(t.family),t.style,t.src,t.unicodeRange,removeFontFaceWeight(t.css)].join("|")}export function removeFontFaceWeight(t){return`${t||""}`.replace(/\s*font-weight\s*:\s*[^;]+;/i,"")}export function replaceFontFaceWeight(t,e,n){const o=e===n?`${e}`:`${e} ${n}`;return`${t||""}`.replace(/font-weight\s*:\s*[^;]+;/i,`font-weight: ${o};`)}export function makeFontDescriptor(t,e,n){return`${e} ${n} 1em "${escapeFontFamilyForCss(t)}"`}export function getUnresolvedReturnedFontFamilyWarnings(t,e,n){const o=new Set(e.map(t=>normalizeFontFamilyName(t.family))),a=[];return t.forEach(t=>{const e=normalizeFontFamilyName(t.fontFamily);e&&o.has(e)&&!n.has(e)&&a.push(t.fontFamily)}),a.length?[{message:`Google Fonts metadata returned font faces that were not resolved: ${a.join(", ")}`}]:[]}export function getItalicOnlyWarnings(t,e){const n=[];return t.forEach(t=>{const o=normalizeFontFamilyName(t.fontFamily);if(!o)return;const a=e.filter(t=>normalizeFontFamilyName(t.family)===o);if(!a.length)return;const r=a.some(t=>"normal"===t.style),i=a.some(t=>"italic"===t.style);!r&&i&&n.push(t.fontFamily)}),n.length?[{message:`Google Fonts metadata returned only italic font faces for: ${n.join(", ")}. Normal text may fall back to a different font.`}]:[]}
1
+ import{buildGoogleFontsLogMessage,encodeGoogleFontFamily,escapeFontFamilyForCss,normalizeFontFamilyName}from"./GoogleFontsUtils";export function buildMetadataRequest(e){const t=buildFontUrlParts(e);if(!t.length)return null;const n=e.some(e=>(e.variations||[]).some(e=>"italic"===e.style));return{url:makeGoogleFontsUrl(t.map(e=>e.urlPart).join("")),cacheKeys:e.map(e=>getMetadataCacheKey(e)),families:e.map(e=>e.fontFamily),includesItalic:n}}export function buildFallbackRequest(e){const t=buildFontUrlParts(e);return t.length?{url:makeGoogleFontsUrl(t.map(e=>e.urlPart).join("")),fontFamilyLabel:t.map(e=>e.fontFamily).join(", ")}:null}export async function fetchMetadata(e){let t;try{t=await fetch(e.url)}catch(t){throw new Error(buildGoogleFontsLogMessage("metadata request failed (network error)",{url:e.url,error:String(t)}))}if(!t.ok)throw new Error(buildGoogleFontsLogMessage("metadata request failed",{url:e.url,status:t.status}));return t.text()}export function parseFontFaces(e){const t=[],n=/@font-face\s*{[^}]*}/gi;let o;for(;null!==(o=n.exec(e||""));){const e=o[0],n=getFontFaceProperty(e,"font-family"),a=getFontFaceProperty(e,"font-style")||"normal",r=parseFontFaceWeight(getFontFaceProperty(e,"font-weight"));n&&r&&t.push({family:n,style:"italic"===a?"italic":"normal",minWeight:r.minWeight,maxWeight:r.maxWeight,src:getFontFaceProperty(e,"src"),unicodeRange:getFontFaceProperty(e,"unicode-range"),css:e})}return t}export function resolveResources(e,t){const n=new Map,o=[],a=new Set,r=getWeightRangeByResourceKey(t);return e.forEach(e=>{const o=normalizeFontFamilyName(e.fontFamily);(e.variations||[]).forEach(i=>{const s=findNearestFontFaces(t,e.fontFamily,i.style,i.weight);s.length&&(a.add(o),s.forEach(t=>{const o=getFontFaceResourceKey(t);if(n.has(o))return;const a=r.get(o)||{minWeight:t.minWeight,maxWeight:t.maxWeight};n.set(o,{key:o,family:e.fontFamily,style:t.style,minWeight:a.minWeight,maxWeight:a.maxWeight,src:t.src,unicodeRange:t.unicodeRange,css:replaceFontFaceWeight(t.css,a.minWeight,a.maxWeight),descriptor:makeFontDescriptor(e.fontFamily,t.style,a.minWeight)})}))})}),o.push(...getUnresolvedReturnedFontFamilyWarnings(e,t,a)),o.push(...getItalicOnlyWarnings(e,t)),{resources:Array.from(n.values()),warnings:o}}function getWeightRangeByResourceKey(e){const t=new Map;return(e||[]).forEach(e=>{const n=getFontFaceResourceKey(e),o=t.get(n);t.set(n,{minWeight:o?Math.min(o.minWeight,e.minWeight):e.minWeight,maxWeight:o?Math.max(o.maxWeight,e.maxWeight):e.maxWeight})}),t}export function mergeResourceCss(e,t,n){return replaceFontFaceWeight(e.css,t,n)}export function buildFontUrlParts(e){const t=makeFontOptions();return(e||[]).map(e=>{const n=(e.variations||[]).some(e=>"italic"===e.style),o=(e.variations||[]).some(e=>"normal"===e.style)||!n,a=[];return n?(o&&t.forEach(e=>a.push(`0,${e}`)),t.forEach(e=>a.push(`1,${e}`)),{fontFamily:e.fontFamily,urlPart:`&family=${encodeGoogleFontFamily(e.fontFamily)}:ital,wght@${a.join(";")}`}):{fontFamily:e.fontFamily,urlPart:`&family=${encodeGoogleFontFamily(e.fontFamily)}:wght@${t.join(";")}`}}).filter(e=>e.fontFamily)}export function makeFontOptions(){const e=[];for(let t=0;t<9;t++)e.push(100*(t+1));return e}export function makeGoogleFontsUrl(e){return`https://fonts.googleapis.com/css2?display=swap${e}`}export function getMetadataCacheKey(e){const t=Array.from(new Set((e.variations||[]).map(e=>e.style))).sort();return`${normalizeFontFamilyName(e.fontFamily)}|${t.join(",")||"normal"}`}export function getFontFaceProperty(e,t){const n=new RegExp(`${t}\\s*:\\s*([^;]+);`,"i"),o=`${e||""}`.match(n);return o?o[1].trim().replace(/^['"]|['"]$/g,""):""}export function parseFontFaceWeight(e){const t=`${e||""}`.match(/\d+(\.\d+)?/g);if(!t||!t.length)return null;const n=Number(t[0]),o=Number(t[t.length-1]);return{minWeight:Math.min(n,o),maxWeight:Math.max(n,o)}}export function findNearestFontFaces(e,t,n,o){const a=normalizeFontFamilyName(t),r=e.filter(e=>normalizeFontFamilyName(e.family)===a&&e.style===n);if(!r.length)return[];const i=r.reduce((e,t)=>{if(!e)return t;const n=getFontFaceDistance(t,o),a=getFontFaceDistance(e,o);if(n<a)return t;if(n>a)return e;const r=resolveWeightForFontFace(t,o),i=resolveWeightForFontFace(e,o);return o<=500?r<i?t:e:r>i?t:e},null),s=resolveWeightForFontFace(i,o);return r.filter(e=>resolveWeightForFontFace(e,o)===s)}export function getFontFaceDistance(e,t){return t>=e.minWeight&&t<=e.maxWeight?0:Math.min(Math.abs(t-e.minWeight),Math.abs(t-e.maxWeight))}export function resolveWeightForFontFace(e,t){return t<e.minWeight?e.minWeight:t>e.maxWeight?e.maxWeight:t}export function getFontFaceResourceKey(e){return[normalizeFontFamilyName(e.family),e.style,e.src,e.unicodeRange,removeFontFaceWeight(e.css)].join("|")}export function removeFontFaceWeight(e){return`${e||""}`.replace(/\s*font-weight\s*:\s*[^;]+;/i,"")}export function replaceFontFaceWeight(e,t,n){const o=t===n?`${t}`:`${t} ${n}`;return`${e||""}`.replace(/font-weight\s*:\s*[^;]+;/i,`font-weight: ${o};`)}export function makeFontDescriptor(e,t,n){return`${t} ${n} 1em "${escapeFontFamilyForCss(e)}"`}export function getUnresolvedReturnedFontFamilyWarnings(e,t,n){const o=new Set(t.map(e=>normalizeFontFamilyName(e.family))),a=[];return e.forEach(e=>{const t=normalizeFontFamilyName(e.fontFamily);t&&o.has(t)&&!n.has(t)&&a.push(e.fontFamily)}),a.length?[{message:buildGoogleFontsLogMessage("metadata returned unresolved font faces",{fontFamilies:a.join(", ")})}]:[]}export function getItalicOnlyWarnings(e,t){const n=[];return e.forEach(e=>{const o=normalizeFontFamilyName(e.fontFamily);if(!o)return;const a=t.filter(e=>normalizeFontFamilyName(e.family)===o);if(!a.length)return;const r=a.some(e=>"normal"===e.style),i=a.some(e=>"italic"===e.style);!r&&i&&n.push(e.fontFamily)}),n.length?[{message:buildGoogleFontsLogMessage("metadata returned only italic font faces, normal text may fall back to a different font",{fontFamilies:n.join(", ")})}]:[]}
@@ -1 +1 @@
1
- import ErrorLogger from"@claspo/renderer/error/ErrorLogger";import{buildFallbackRequest,buildMetadataRequest,fetchMetadata,getMetadataCacheKey,makeFontDescriptor,mergeResourceCss,parseFontFaces,resolveResources}from"./GoogleFontsMetadataService";import{normalizeFontInput,sortFontVariations}from"./GoogleFontsUtils";class GoogleFontsRegistry{constructor(){this.statesByDocument=new WeakMap}async loadFonts(e){const t=e.document||document,s=this.normalizeFontsForLoading(e.fonts);if(!s.length)return;const r=this.getState(t),n=this.getOwner(r,e.ownerId);try{const o=await this.getFontFaces(r,s);if(!this.isCurrentOwner(r,e.ownerId,n))return;if(!o.length)throw new Error("Google Fonts metadata did not contain font-face rules");const a=resolveResources(s,o);if(!this.isCurrentOwner(r,e.ownerId,n))return;if(a.warnings.forEach(t=>this.logError(t.message,e.context)),!a.resources.length)throw new Error("Google Fonts metadata did not contain matching font faces");const i=a.resources.map(s=>this.addResource(r,n,t,e.ownerId,s));await this.waitForFonts(i.map(e=>e.resource),r,t,e.context)}catch(o){if(!this.isCurrentOwner(r,e.ownerId,n))return;const a=s.map(e=>e.fontFamily).join(", ");if(this.logError(`Failed to load optimized Google Fonts CSS, falling back to stylesheet link. Fonts: ${a}. ${o}`,e.context),await this.addFallbackLink(r,n,t,e.ownerId,s,e.context),!this.isCurrentOwner(r,e.ownerId,n))return;await this.waitForFonts(this.generateFallbackResources(s),r,t,e.context)}}releaseOwner(e){if(!e.document)return;const t=this.statesByDocument.get(e.document);if(!t)return;const s=t.ownersById.get(e.ownerId);s&&(s.resourceKeys.forEach(s=>{const r=t.resourcesByKey.get(s);r&&(r.owners.delete(e.ownerId),r.owners.size||(r.element.remove(),t.resourcesByKey.delete(s)))}),s.fallbackUrls.forEach(s=>{const r=t.fallbackLinksByUrl.get(s);r&&(r.owners.delete(e.ownerId),r.owners.size||(r.element.remove(),t.fallbackLinksByUrl.delete(s)))}),t.ownersById.delete(e.ownerId))}resetForTests(){this.statesByDocument=new WeakMap}normalizeFontsForLoading(e){const t=[];return(e||[]).forEach(e=>{const s=normalizeFontInput(e);if(!s)return;if("string"!=typeof e&&e.variations&&e.variations.length)return void t.push(s);const r=[];s.variations.forEach(e=>{r.push({style:"normal",weight:e.weight}),r.push({style:"italic",weight:e.weight})}),t.push({fontFamily:s.fontFamily,variations:sortFontVariations(r)})}),this.mergeFontRequests(t)}mergeFontRequests(e){const t=new Map;return e.forEach(e=>{const s=t.get(e.fontFamily);s?s.variations=sortFontVariations(s.variations.concat(e.variations)):t.set(e.fontFamily,{fontFamily:e.fontFamily,variations:sortFontVariations(e.variations)})}),Array.from(t.values())}getState(e){let t=this.statesByDocument.get(e);return t||(t={metadataByCacheKey:new Map,pendingMetadataByUrl:new Map,fallbackLinksByUrl:new Map,resourcesByKey:new Map,browserLoadsByResourceKey:new Map,ownersById:new Map},this.statesByDocument.set(e,t)),t}getOwner(e,t){let s=e.ownersById.get(t);return s||(s={resourceKeys:new Set,fallbackUrls:new Set},e.ownersById.set(t,s)),s}isCurrentOwner(e,t,s){return e.ownersById.get(t)===s}async getFontFaces(e,t){const s=this.getCachedFontFaces(e,t);if(!s.missingFonts.length)return s.cachedFontFaces;const r=this.getPendingFontFaces(e,s.missingFonts),n=r.pendingPromises.slice(),o=buildMetadataRequest(r.missingFonts);if(o){let t=e.pendingMetadataByUrl.get(o.url);if(!t){const s=fetchMetadata(o).then(t=>{const s=parseFontFaces(t);return this.cacheFontFaces(e,r.missingFonts,s),s}).finally(()=>e.pendingMetadataByUrl.delete(o.url));t={url:o.url,fonts:r.missingFonts,promise:s},e.pendingMetadataByUrl.set(o.url,t)}n.push(t.promise)}if(!n.length)return s.cachedFontFaces;const a=await Promise.all(n);return s.cachedFontFaces.concat(...a)}getCachedFontFaces(e,t){const s=[],r=new Set,n=[];for(const o of t){const t=getMetadataCacheKey(o).split("|")[0],a=[];Array.from(new Set((o.variations||[]).map(e=>e.style))).forEach(n=>{const i=Array.from(e.metadataByCacheKey.values()).find(e=>e.families.has(t)&&e.styles.has(n));i?i.fontFaces.filter(e=>this.isMatchingFontFace(e,t,n)).forEach(e=>{const t=this.getFontFaceCacheIdentity(e);r.has(t)||(r.add(t),s.push(e))}):a.push(...(o.variations||[]).filter(e=>e.style===n))}),a.length&&n.push({fontFamily:o.fontFamily,variations:sortFontVariations(a)})}return{cachedFontFaces:s,missingFonts:n}}getPendingFontFaces(e,t){const s=[],r=new Set,n=[];for(const o of t){const t=getMetadataCacheKey(o).split("|")[0],a=[];Array.from(new Set((o.variations||[]).map(e=>e.style))).forEach(n=>{const i=Array.from(e.pendingMetadataByUrl.values()).find(e=>this.hasFontStyleCoverage(e.fonts,t,n));i?r.has(i.url)||(r.add(i.url),s.push(i.promise)):a.push(...(o.variations||[]).filter(e=>e.style===n))}),a.length&&n.push({fontFamily:o.fontFamily,variations:sortFontVariations(a)})}return{pendingPromises:s,missingFonts:n}}cacheFontFaces(e,t,s){t.forEach(t=>{const r=getMetadataCacheKey(t).split("|")[0],n=new Set((t.variations||[]).map(e=>e.style)),o=s.filter(e=>this.isMatchingFontFace(e,r));e.metadataByCacheKey.set(`${r}|${Array.from(n).sort().join(",")||"normal"}`,{families:new Set([r]),styles:n,fontFaces:o})})}isMatchingFontFace(e,t,s){if(!e||!e.family)return!1;return!!getMetadataCacheKey({fontFamily:e.family,variations:[{style:e.style,weight:e.minWeight}]}).startsWith(t+"|")&&(!s||e.style===s)}getFontFaceCacheIdentity(e){return[e.family,e.style,e.minWeight,e.maxWeight,e.src,e.unicodeRange].join("|")}hasFontStyleCoverage(e,t,s){return(e||[]).some(e=>getMetadataCacheKey(e).split("|")[0]===t&&(e.variations||[]).some(e=>e.style===s))}addResource(e,t,s,r,n){const o=e.resourcesByKey.get(n.key);if(o){o.owners.add(r),t.resourceKeys.add(n.key);const e=Math.min(o.minWeight,n.minWeight),s=Math.max(o.maxWeight,n.maxWeight);return e===o.minWeight&&s===o.maxWeight||(o.minWeight=e,o.maxWeight=s,o.resource.minWeight=e,o.resource.maxWeight=s,o.resource.css=mergeResourceCss(o.resource,e,s),o.resource.descriptor=makeFontDescriptor(o.resource.family,o.resource.style,e),o.element.textContent=o.resource.css),o}const a=s.createElement("style");a.setAttribute("data-cl-google-fonts","true"),a.textContent=n.css,s.head&&s.head.appendChild(a);const i={resource:n,element:a,owners:new Set([r]),minWeight:n.minWeight,maxWeight:n.maxWeight};return e.resourcesByKey.set(n.key,i),t.resourceKeys.add(n.key),i}addFallbackLink(e,t,s,r,n,o){const a=buildFallbackRequest(n);if(!a||!s.head)return Promise.resolve();const i=e.fallbackLinksByUrl.get(a.url);if(i)return i.owners.add(r),t.fallbackUrls.add(a.url),i.promise;const l=s.createElement("link");l.rel="stylesheet",l.href=a.url;const c=new Promise((t,s)=>{l.onload=()=>t(),l.onerror=()=>{this.logError(`Failed to load font: ${a.fontFamilyLabel}`,o),l.remove(),e.fallbackLinksByUrl.delete(a.url),s(new Error(`Failed to load font: ${a.fontFamilyLabel}`))}}),d={url:a.url,element:l,owners:new Set([r]),promise:c.catch(()=>{})};return e.fallbackLinksByUrl.set(a.url,d),t.fallbackUrls.add(a.url),s.head.appendChild(l),d.promise}generateFallbackResources(e){return e.reduce((e,t)=>((t.variations||[]).forEach(s=>{const r=makeFontDescriptor(t.fontFamily,s.style,s.weight);e.push({key:`${t.fontFamily}|${s.style}|${s.weight}|fallback`,family:t.fontFamily,style:s.style,minWeight:s.weight,maxWeight:s.weight,src:"",unicodeRange:"",css:"",descriptor:r})}),e),[])}async waitForFonts(e,t,s,r){if(!e.length||!s.fonts)return;const n=this.prepareFontResourceLoads(e,t,s,r);if(!n.length)return;const o=this.createFontTester(n.map(e=>e.resource),s);o&&s.body&&s.body.appendChild(o);const a=this.waitForAllFontsToLoad(n);let i=null;const l=new Promise(e=>{i=setTimeout(()=>{this.logError(`Timeout while loading Google Fonts after 2000ms. Pending: ${n.map(e=>e.resource.descriptor).join(", ")}`,r),e()},2e3)});try{await Promise.race([a,l])}finally{i&&clearTimeout(i),o&&o.remove()}}prepareFontResourceLoads(e,t,s,r){return e.reduce((e,n)=>{const o=t.browserLoadsByResourceKey.get(n.key);if(o&&o.loaded)return e;if(o&&o.promise)return e;const a={loaded:!1,promise:null},i=this.loadBrowserFont(n,s).then(()=>{a.loaded=!0,a.promise=null}).catch(e=>{t.browserLoadsByResourceKey.delete(n.key),this.logError(`Failed to load: ${n.descriptor} ${e}`,r)});return a.promise=i,t.browserLoadsByResourceKey.set(n.key,a),e.push({resource:n,promise:i}),e},[])}async waitForAllFontsToLoad(e){for(let t=0;t<e.length;t+=10)await Promise.all(e.slice(t,t+10).map(e=>e.promise))}async loadBrowserFont(e,t){const s=t.fonts;if(!s)return;let r=!1;try{r=s.check(e.descriptor)}catch(e){}r||await s.load(e.descriptor)}createFontTester(e,t){if(!t.body)return null;const s=t.createElement("div");s.setAttribute("aria-hidden","true"),s.style.cssText="\n position: fixed;\n top: -9999px;\n left: -9999px;\n width: 1px;\n height: 1px;\n overflow: hidden;\n visibility: hidden;\n pointer-events: none;\n user-select: none;\n z-index: -1;\n ";const r={latin:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",cyrillic:"АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя"};return e.forEach(e=>{Object.entries(r).forEach(([r,n])=>{const o=t.createElement("span");o.textContent=n,o.style.cssText=`\n font-family: "${e.family}", sans-serif;\n font-weight: ${e.minWeight};\n font-style: ${e.style};\n font-size: 20px;\n display: inline-block;\n white-space: nowrap;\n `,o.dataset.font=`${e.family}-${e.minWeight}-${e.style}`,o.dataset.subset=r,s.appendChild(o)})}),s}logError(e,t){const s=[];t&&void 0!==t.widgetId&&null!==t.widgetId&&s.push(`widgetId: ${t.widgetId}`),t&&t.siteId&&s.push(`siteId: ${t.siteId}`),ErrorLogger.log(s.length?`${e} (${s.join(", ")})`:e)}}export default new GoogleFontsRegistry;
1
+ import ErrorLogger from"@claspo/renderer/error/ErrorLogger";import{buildFallbackRequest,buildMetadataRequest,fetchMetadata,getMetadataCacheKey,makeFontDescriptor,mergeResourceCss,parseFontFaces,resolveResources}from"./GoogleFontsMetadataService";import{buildGoogleFontsLogMessage,normalizeFontInput,parseGoogleFontsLogMessage,sortFontVariations}from"./GoogleFontsUtils";class GoogleFontsRegistry{constructor(){this.statesByDocument=new WeakMap}async loadFonts(e){const t=e.document||document,s=this.normalizeFontsForLoading(e.fonts);if(!s.length)return;const o=s.map(e=>e.fontFamily).join(", "),r=this.getState(t),n=this.getOwner(r,e.ownerId);try{const a=await this.getFontFaces(r,s);if(!this.isCurrentOwner(r,e.ownerId,n))return;if(!a.length)throw new Error(buildGoogleFontsLogMessage("metadata response contained no @font-face rules",{fontFamilies:o}));const i=resolveResources(s,a);if(!this.isCurrentOwner(r,e.ownerId,n))return;if(i.warnings.forEach(t=>this.logError(t.message,e.context)),!i.resources.length)throw new Error(buildGoogleFontsLogMessage("metadata response contained no matching font faces",{fontFamilies:o}));const l=i.resources.map(s=>this.addResource(r,n,t,e.ownerId,s));await this.waitForFonts(l.map(e=>e.resource),r,t,e.context)}catch(a){if(!this.isCurrentOwner(r,e.ownerId,n))return;const i=parseGoogleFontsLogMessage(a instanceof Error?a.message:String(a));if(this.logError(buildGoogleFontsLogMessage("optimized CSS load failed, falling back to stylesheet link",Object.assign({fontFamilies:o,cause:i.message},i.props)),e.context),await this.addFallbackLink(r,n,t,e.ownerId,s,e.context),!this.isCurrentOwner(r,e.ownerId,n))return;await this.waitForFonts(this.generateFallbackResources(s),r,t,e.context)}}releaseOwner(e){if(!e.document)return;const t=this.statesByDocument.get(e.document);if(!t)return;const s=t.ownersById.get(e.ownerId);s&&(s.resourceKeys.forEach(s=>{const o=t.resourcesByKey.get(s);o&&(o.owners.delete(e.ownerId),o.owners.size||(o.element.remove(),t.resourcesByKey.delete(s)))}),s.fallbackUrls.forEach(s=>{const o=t.fallbackLinksByUrl.get(s);o&&(o.owners.delete(e.ownerId),o.owners.size||(o.element.remove(),t.fallbackLinksByUrl.delete(s)))}),t.ownersById.delete(e.ownerId))}resetForTests(){this.statesByDocument=new WeakMap}normalizeFontsForLoading(e){const t=[];return(e||[]).forEach(e=>{const s=normalizeFontInput(e);if(!s)return;if("string"!=typeof e&&e.variations&&e.variations.length)return void t.push(s);const o=[];s.variations.forEach(e=>{o.push({style:"normal",weight:e.weight}),o.push({style:"italic",weight:e.weight})}),t.push({fontFamily:s.fontFamily,variations:sortFontVariations(o)})}),this.mergeFontRequests(t)}mergeFontRequests(e){const t=new Map;return e.forEach(e=>{const s=t.get(e.fontFamily);s?s.variations=sortFontVariations(s.variations.concat(e.variations)):t.set(e.fontFamily,{fontFamily:e.fontFamily,variations:sortFontVariations(e.variations)})}),Array.from(t.values())}getState(e){let t=this.statesByDocument.get(e);return t||(t={metadataByCacheKey:new Map,pendingMetadataByUrl:new Map,fallbackLinksByUrl:new Map,resourcesByKey:new Map,browserLoadsByResourceKey:new Map,ownersById:new Map},this.statesByDocument.set(e,t)),t}getOwner(e,t){let s=e.ownersById.get(t);return s||(s={resourceKeys:new Set,fallbackUrls:new Set},e.ownersById.set(t,s)),s}isCurrentOwner(e,t,s){return e.ownersById.get(t)===s}async getFontFaces(e,t){const s=this.getCachedFontFaces(e,t);if(!s.missingFonts.length)return s.cachedFontFaces;const o=this.getPendingFontFaces(e,s.missingFonts),r=o.pendingPromises.slice(),n=buildMetadataRequest(o.missingFonts);if(n){let t=e.pendingMetadataByUrl.get(n.url);if(!t){const s=fetchMetadata(n).then(t=>{const s=parseFontFaces(t);return this.cacheFontFaces(e,o.missingFonts,s),s}).finally(()=>e.pendingMetadataByUrl.delete(n.url));t={url:n.url,fonts:o.missingFonts,promise:s},e.pendingMetadataByUrl.set(n.url,t)}r.push(t.promise)}if(!r.length)return s.cachedFontFaces;const a=await Promise.all(r);return s.cachedFontFaces.concat(...a)}getCachedFontFaces(e,t){const s=[],o=new Set,r=[];for(const n of t){const t=getMetadataCacheKey(n).split("|")[0],a=[];Array.from(new Set((n.variations||[]).map(e=>e.style))).forEach(r=>{const i=Array.from(e.metadataByCacheKey.values()).find(e=>e.families.has(t)&&e.styles.has(r));i?i.fontFaces.filter(e=>this.isMatchingFontFace(e,t,r)).forEach(e=>{const t=this.getFontFaceCacheIdentity(e);o.has(t)||(o.add(t),s.push(e))}):a.push(...(n.variations||[]).filter(e=>e.style===r))}),a.length&&r.push({fontFamily:n.fontFamily,variations:sortFontVariations(a)})}return{cachedFontFaces:s,missingFonts:r}}getPendingFontFaces(e,t){const s=[],o=new Set,r=[];for(const n of t){const t=getMetadataCacheKey(n).split("|")[0],a=[];Array.from(new Set((n.variations||[]).map(e=>e.style))).forEach(r=>{const i=Array.from(e.pendingMetadataByUrl.values()).find(e=>this.hasFontStyleCoverage(e.fonts,t,r));i?o.has(i.url)||(o.add(i.url),s.push(i.promise)):a.push(...(n.variations||[]).filter(e=>e.style===r))}),a.length&&r.push({fontFamily:n.fontFamily,variations:sortFontVariations(a)})}return{pendingPromises:s,missingFonts:r}}cacheFontFaces(e,t,s){t.forEach(t=>{const o=getMetadataCacheKey(t).split("|")[0],r=new Set((t.variations||[]).map(e=>e.style)),n=s.filter(e=>this.isMatchingFontFace(e,o));e.metadataByCacheKey.set(`${o}|${Array.from(r).sort().join(",")||"normal"}`,{families:new Set([o]),styles:r,fontFaces:n})})}isMatchingFontFace(e,t,s){if(!e||!e.family)return!1;return!!getMetadataCacheKey({fontFamily:e.family,variations:[{style:e.style,weight:e.minWeight}]}).startsWith(t+"|")&&(!s||e.style===s)}getFontFaceCacheIdentity(e){return[e.family,e.style,e.minWeight,e.maxWeight,e.src,e.unicodeRange].join("|")}hasFontStyleCoverage(e,t,s){return(e||[]).some(e=>getMetadataCacheKey(e).split("|")[0]===t&&(e.variations||[]).some(e=>e.style===s))}addResource(e,t,s,o,r){const n=e.resourcesByKey.get(r.key);if(n){n.owners.add(o),t.resourceKeys.add(r.key);const e=Math.min(n.minWeight,r.minWeight),s=Math.max(n.maxWeight,r.maxWeight);return e===n.minWeight&&s===n.maxWeight||(n.minWeight=e,n.maxWeight=s,n.resource.minWeight=e,n.resource.maxWeight=s,n.resource.css=mergeResourceCss(n.resource,e,s),n.resource.descriptor=makeFontDescriptor(n.resource.family,n.resource.style,e),n.element.textContent=n.resource.css),n}const a=s.createElement("style");a.setAttribute("data-cl-google-fonts","true"),a.textContent=r.css,s.head&&s.head.appendChild(a);const i={resource:r,element:a,owners:new Set([o]),minWeight:r.minWeight,maxWeight:r.maxWeight};return e.resourcesByKey.set(r.key,i),t.resourceKeys.add(r.key),i}addFallbackLink(e,t,s,o,r,n){const a=buildFallbackRequest(r);if(!a||!s.head)return Promise.resolve();const i=e.fallbackLinksByUrl.get(a.url);if(i)return i.owners.add(o),t.fallbackUrls.add(a.url),i.promise;const l=s.createElement("link");l.rel="stylesheet",l.href=a.url;const c=new Promise((t,s)=>{l.onload=()=>t(),l.onerror=()=>{this.logError(buildGoogleFontsLogMessage("fallback stylesheet failed to load",{url:a.url,fontFamilies:a.fontFamilyLabel}),n),l.remove(),e.fallbackLinksByUrl.delete(a.url),s(new Error(buildGoogleFontsLogMessage("fallback stylesheet failed to load",{url:a.url})))}}),d={url:a.url,element:l,owners:new Set([o]),promise:c.catch(()=>{})};return e.fallbackLinksByUrl.set(a.url,d),t.fallbackUrls.add(a.url),s.head.appendChild(l),d.promise}generateFallbackResources(e){return e.reduce((e,t)=>((t.variations||[]).forEach(s=>{const o=makeFontDescriptor(t.fontFamily,s.style,s.weight);e.push({key:`${t.fontFamily}|${s.style}|${s.weight}|fallback`,family:t.fontFamily,style:s.style,minWeight:s.weight,maxWeight:s.weight,src:"",unicodeRange:"",css:"",descriptor:o})}),e),[])}async waitForFonts(e,t,s,o){if(!e.length||!s.fonts)return;const r=this.prepareFontResourceLoads(e,t,s,o);if(!r.length)return;const n=this.createFontTester(r.map(e=>e.resource),s);n&&s.body&&s.body.appendChild(n);const a=this.waitForAllFontsToLoad(r);let i=null;const l=new Promise(e=>{i=setTimeout(()=>{this.logError(buildGoogleFontsLogMessage("font loading timed out",{timeoutMs:2e3,pending:r.map(e=>e.resource.descriptor).join("; "),src:r.map(e=>e.resource.src).join("; ")}),o),e()},2e3)});try{await Promise.race([a,l])}finally{i&&clearTimeout(i),n&&n.remove()}}prepareFontResourceLoads(e,t,s,o){return e.reduce((e,r)=>{const n=t.browserLoadsByResourceKey.get(r.key);if(n&&n.loaded)return e;if(n&&n.promise)return e;const a={loaded:!1,promise:null},i=this.loadBrowserFont(r,s).then(()=>{a.loaded=!0,a.promise=null}).catch(e=>{t.browserLoadsByResourceKey.delete(r.key),this.logError(buildGoogleFontsLogMessage("font face failed to load",{descriptor:r.descriptor,src:r.src,error:String(e)}),o)});return a.promise=i,t.browserLoadsByResourceKey.set(r.key,a),e.push({resource:r,promise:i}),e},[])}async waitForAllFontsToLoad(e){for(let t=0;t<e.length;t+=10)await Promise.all(e.slice(t,t+10).map(e=>e.promise))}async loadBrowserFont(e,t){const s=t.fonts;if(!s)return;let o=!1;try{o=s.check(e.descriptor)}catch(e){}o||await s.load(e.descriptor)}createFontTester(e,t){if(!t.body)return null;const s=t.createElement("div");s.setAttribute("aria-hidden","true"),s.style.cssText="\n position: fixed;\n top: -9999px;\n left: -9999px;\n width: 1px;\n height: 1px;\n overflow: hidden;\n visibility: hidden;\n pointer-events: none;\n user-select: none;\n z-index: -1;\n ";const o={latin:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",cyrillic:"АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя"};return e.forEach(e=>{Object.entries(o).forEach(([o,r])=>{const n=t.createElement("span");n.textContent=r,n.style.cssText=`\n font-family: "${e.family}", sans-serif;\n font-weight: ${e.minWeight};\n font-style: ${e.style};\n font-size: 20px;\n display: inline-block;\n white-space: nowrap;\n `,n.dataset.font=`${e.family}-${e.minWeight}-${e.style}`,n.dataset.subset=o,s.appendChild(n)})}),s}logError(e,t){const{message:s,props:o}=parseGoogleFontsLogMessage(e);t&&void 0!==t.widgetId&&null!==t.widgetId&&(o.widgetId=t.widgetId),t&&t.siteId&&(o.siteId=t.siteId);const r=Object.keys(o).map(e=>`${e}: ${o[e]}`),n=r.length?` (${r.join(", ")})`:"";ErrorLogger.log(`GoogleFontsLoader error: ${s}${n}`)}}export default new GoogleFontsRegistry;
@@ -9,3 +9,9 @@ export declare function normalizeFontInputs(fonts: GoogleFontInput[]): ResolvedG
9
9
  export declare function normalizeFontStyle(style: GoogleFontStyleValue | string): GoogleFontStyleValue;
10
10
  export declare function escapeFontFamilyForCss(fontFamily: string): string;
11
11
  export declare function encodeGoogleFontFamily(fontFamily: string): string;
12
+ export type GoogleFontsLogPropsI = Record<string, string | number>;
13
+ export declare function buildGoogleFontsLogMessage(message: string, props?: GoogleFontsLogPropsI): string;
14
+ export declare function parseGoogleFontsLogMessage(message: string): {
15
+ message: string;
16
+ props: GoogleFontsLogPropsI;
17
+ };
@@ -1 +1 @@
1
- export function normalizeFontFamilyName(t){return`${t||""}`.trim().replace(/\s+/g," ").toLowerCase()}export function extractFontFamilyName(t){return stripWrappingQuotes(`${t||""}`.trim().replace(/\s+/g," "))}export function extractFirstCssFontFamily(t){const e=`${t||""}`.trim();if(!e)return"";let n="",r="";for(let t=0;t<e.length;t++){const o=e[t];if('"'!==o&&"'"!==o||"\\"===e[t-1]||(n?n===o&&(n=""):n=o),","===o&&!n)break;r+=o}return extractFontFamilyName(r)}export function normalizeFontWeight(t){if("normal"===t)return 400;if("bold"===t)return 700;const e=parseInt(`${t||""}`,10);return Number.isFinite(e)&&e>0?e:400}export function sortFontVariations(t){const e=new Map;return(t||[]).forEach(t=>{const n=normalizeFontStyle(t.style),r=normalizeFontWeight(t.weight);e.set(`${n}|${r}`,{style:n,weight:r})}),Array.from(e.values()).sort((t,e)=>t.weight!==e.weight?t.weight-e.weight:t.style===e.style?0:"normal"===t.style?-1:1)}export function normalizeFontInput(t){const e=extractFontFamilyName("string"==typeof t?t:t&&t.fontFamily);if(!e)return null;const n=[];return"string"==typeof t?n.push({style:"normal",weight:400}):((t.weights&&t.weights.length?t.weights:[]).forEach(t=>{n.push({style:"normal",weight:normalizeFontWeight(t)})}),(t.variations||[]).forEach(t=>{n.push({style:normalizeFontStyle(t.style),weight:normalizeFontWeight(t.weight)})}),n.length||n.push({style:"normal",weight:400})),{fontFamily:e,variations:sortFontVariations(n)}}export function normalizeFontInputs(t){return(t||[]).map(t=>normalizeFontInput(t)).filter(Boolean)}export function normalizeFontStyle(t){return"italic"===t?"italic":"normal"}export function escapeFontFamilyForCss(t){return`${t||""}`.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}export function encodeGoogleFontFamily(t){return encodeURIComponent(t).replace(/%20/g,"+")}function stripWrappingQuotes(t){const e=`${t||""}`.trim();return'"'===e[0]&&'"'===e[e.length-1]||"'"===e[0]&&"'"===e[e.length-1]?e.slice(1,-1).trim():e}
1
+ export function normalizeFontFamilyName(t){return`${t||""}`.trim().replace(/\s+/g," ").toLowerCase()}export function extractFontFamilyName(t){return stripWrappingQuotes(`${t||""}`.trim().replace(/\s+/g," "))}export function extractFirstCssFontFamily(t){const e=`${t||""}`.trim();if(!e)return"";let n="",o="";for(let t=0;t<e.length;t++){const r=e[t];if('"'!==r&&"'"!==r||"\\"===e[t-1]||(n?n===r&&(n=""):n=r),","===r&&!n)break;o+=r}return extractFontFamilyName(o)}export function normalizeFontWeight(t){if("normal"===t)return 400;if("bold"===t)return 700;const e=parseInt(`${t||""}`,10);return Number.isFinite(e)&&e>0?e:400}export function sortFontVariations(t){const e=new Map;return(t||[]).forEach(t=>{const n=normalizeFontStyle(t.style),o=normalizeFontWeight(t.weight);e.set(`${n}|${o}`,{style:n,weight:o})}),Array.from(e.values()).sort((t,e)=>t.weight!==e.weight?t.weight-e.weight:t.style===e.style?0:"normal"===t.style?-1:1)}export function normalizeFontInput(t){const e=extractFontFamilyName("string"==typeof t?t:t&&t.fontFamily);if(!e)return null;const n=[];return"string"==typeof t?n.push({style:"normal",weight:400}):((t.weights&&t.weights.length?t.weights:[]).forEach(t=>{n.push({style:"normal",weight:normalizeFontWeight(t)})}),(t.variations||[]).forEach(t=>{n.push({style:normalizeFontStyle(t.style),weight:normalizeFontWeight(t.weight)})}),n.length||n.push({style:"normal",weight:400})),{fontFamily:e,variations:sortFontVariations(n)}}export function normalizeFontInputs(t){return(t||[]).map(t=>normalizeFontInput(t)).filter(Boolean)}export function normalizeFontStyle(t){return"italic"===t?"italic":"normal"}export function escapeFontFamilyForCss(t){return`${t||""}`.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}export function encodeGoogleFontFamily(t){return encodeURIComponent(t).replace(/%20/g,"+")}export function buildGoogleFontsLogMessage(t,e){const n={message:t};return e&&Object.keys(e).forEach(t=>{const o=e[t];null!=o&&""!==o&&(n[t]=o)}),JSON.stringify(n)}export function parseGoogleFontsLogMessage(t){let e=null;try{e=JSON.parse(t)}catch(t){e=null}if(!e||"object"!=typeof e||"string"!=typeof e.message)return{message:t,props:{}};const n={};return Object.keys(e).forEach(t=>{"message"!==t&&void 0!==e[t]&&null!==e[t]&&(n[t]=e[t])}),{message:e.message,props:n}}function stripWrappingQuotes(t){const e=`${t||""}`.trim();return'"'===e[0]&&'"'===e[e.length-1]||"'"===e[0]&&"'"===e[e.length-1]?e.slice(1,-1).trim():e}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claspo/document-connector",
3
- "version": "16.7.5",
3
+ "version": "16.7.6-ctx.2",
4
4
  "scripts": {
5
5
  "test": "jest --no-cache --coverage",
6
6
  "build": "rm -rf out && tsc --project tsconfig.json && npm run minify",
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "@claspo/common": "7.2.0",
18
- "@claspo/renderer": "18.6.0"
18
+ "@claspo/renderer": "18.6.0-ctx.2"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@babel/core": "^7.28.5",