File: /var/www/vhost/disk-apps/pwa.sports-crowd.com/node_modules/@nguniversal/common/fesm2022/tools.mjs
import Critters from 'critters';
import * as fs from 'fs';
/**
* Pattern used to extract the media query set by Critters in an `onload` handler.
*/
const MEDIA_SET_HANDLER_PATTERN = /^this\.media=['"'](.*)['"'];?$/;
/**
* Name of the attribute used to save the Critters media query so it can be re-assigned on load.
*/
const CSP_MEDIA_ATTR = 'ngCspMedia';
/**
* Script text used to change the media value of the link tags.
*/
const LINK_LOAD_SCRIPT_CONTENT = [
`(() => {`,
// Save the `children` in a variable since they're a live DOM node collection.
// We iterate over the direct descendants, instead of going through a `querySelectorAll`,
// because we know that the tags will be directly inside the `head`.
` const children = document.head.children;`,
// Declare `onLoad` outside the loop to avoid leaking memory.
// Can't be an arrow function, because we need `this` to refer to the DOM node.
` function onLoad() {this.media = this.getAttribute('${CSP_MEDIA_ATTR}');}`,
// Has to use a plain for loop, because some browsers don't support
// `forEach` on `children` which is a `HTMLCollection`.
` for (let i = 0; i < children.length; i++) {`,
` const child = children[i];`,
` child.hasAttribute('${CSP_MEDIA_ATTR}') && child.addEventListener('load', onLoad);`,
` }`,
`})();`,
].join('\n');
class CrittersExtended extends Critters {
constructor(optionsExtended, resourceCache) {
super({
logger: {
warn: (s) => this.warnings.push(s),
error: (s) => this.errors.push(s),
info: () => { },
},
logLevel: 'warn',
path: optionsExtended.outputPath,
publicPath: optionsExtended.deployUrl,
compress: !!optionsExtended.minify,
pruneSource: false,
reduceInlineStyles: false,
mergeStylesheets: false,
// Note: if `preload` changes to anything other than `media`, the logic in
// `embedLinkedStylesheetOverride` will have to be updated.
preload: 'media',
noscriptFallback: true,
inlineFonts: true,
});
this.optionsExtended = optionsExtended;
this.resourceCache = resourceCache;
this.warnings = [];
this.errors = [];
this.addedCspScriptsDocuments = new WeakSet();
this.documentNonces = new WeakMap();
/**
* Override of the Critters `embedLinkedStylesheet` method
* that makes it work with Angular's CSP APIs.
*/
this.embedLinkedStylesheetOverride = async (link, document) => {
const returnValue = await this.initialEmbedLinkedStylesheet(link, document);
const cspNonce = this.findCspNonce(document);
if (cspNonce) {
const crittersMedia = link.getAttribute('onload')?.match(MEDIA_SET_HANDLER_PATTERN);
if (crittersMedia) {
// If there's a Critters-generated `onload` handler and the file has an Angular CSP nonce,
// we have to remove the handler, because it's incompatible with CSP. We save the value
// in a different attribute and we generate a script tag with the nonce that uses
// `addEventListener` to apply the media query instead.
link.removeAttribute('onload');
link.setAttribute(CSP_MEDIA_ATTR, crittersMedia[1]);
this.conditionallyInsertCspLoadingScript(document, cspNonce);
}
link.prev?.setAttribute('nonce', cspNonce);
}
return returnValue;
};
// We can't use inheritance to override `embedLinkedStylesheet`, because it's not declared in
// the `Critters` .d.ts which means that we can't call the `super` implementation. TS doesn't
// allow for `super` to be cast to a different type.
this.initialEmbedLinkedStylesheet = this.embedLinkedStylesheet;
this.embedLinkedStylesheet = this.embedLinkedStylesheetOverride;
}
async readFile(path) {
let resourceContent = this.resourceCache.get(path);
if (resourceContent === undefined) {
resourceContent = await fs.promises.readFile(path, 'utf-8');
this.resourceCache.set(path, resourceContent);
}
return resourceContent;
}
/**
* Finds the CSP nonce for a specific document.
*/
findCspNonce(document) {
if (this.documentNonces.has(document)) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.documentNonces.get(document);
}
// HTML attribute are case-insensitive, but the parser used by Critters is case-sensitive.
const nonceElement = document.querySelector('[ngCspNonce], [ngcspnonce]');
const cspNonce = nonceElement?.getAttribute('ngCspNonce') || nonceElement?.getAttribute('ngcspnonce') || null;
this.documentNonces.set(document, cspNonce);
return cspNonce;
}
/**
* Inserts the `script` tag that swaps the critical CSS at runtime,
* if one hasn't been inserted into the document already.
*/
conditionallyInsertCspLoadingScript(document, nonce) {
if (this.addedCspScriptsDocuments.has(document)) {
return;
}
if (document.head.textContent.includes(LINK_LOAD_SCRIPT_CONTENT)) {
// Script was already added during the build.
this.addedCspScriptsDocuments.add(document);
return;
}
const script = document.createElement('script');
script.setAttribute('nonce', nonce);
script.textContent = LINK_LOAD_SCRIPT_CONTENT;
// Append the script to the head since it needs to
// run as early as possible, after the `link` tags.
document.head.appendChild(script);
this.addedCspScriptsDocuments.add(document);
}
}
class InlineCriticalCssProcessor {
constructor(options) {
this.options = options;
this.resourceCache = new Map();
}
async process(html, options) {
const critters = new CrittersExtended({ ...this.options, ...options }, this.resourceCache);
const content = await critters.process(html);
return {
content,
errors: critters.errors.length ? critters.errors : undefined,
warnings: critters.warnings.length ? critters.warnings : undefined,
};
}
}
/**
* Generated bundle index. Do not edit.
*/
export { InlineCriticalCssProcessor as ɵInlineCriticalCssProcessor };
//# sourceMappingURL=tools.mjs.map