Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 43 additions & 14 deletions src/BeaconPreloadFonts.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,47 @@ class BeaconPreloadFonts {
static FONT_FILE_REGEX = /\.(woff2?|ttf|otf|eot)(\?.*)?$/i;

/**
* Checks if a given font family is a system font.
* Checks if a font family or URL should be excluded from preloading.
*
* This method checks if the provided font family is part of the system fonts
* defined in the configuration. It returns true if the font family is a system
* font, and false otherwise.
* This method determines if the provided font family or any of its URLs
* match any exclusion patterns defined in the configuration. It checks for
* exact matches and substring matches for both the font family and URLs.
*
* @param {string} fontFamily - The font family to check.
* @returns {boolean} True if the font family is a system font, false otherwise.
* @param {string[]} urls - Array of font file URLs to check.
* @returns {boolean} True if the font should be excluded, false otherwise.
*/
isSystemFont(fontFamily) {
const systemFonts = new Set(this.config.system_fonts);
return systemFonts.has(fontFamily);
}
isExcluded(fontFamily, urls) {
const exclusions = this.config.preload_fonts_exclusions;
const exclusionsSet = new Set(exclusions);

// First check for exact match of fontFamily.
if (exclusionsSet.has(fontFamily)) {
return true;
}

// Then check if any exclusion is a substring of fontFamily.
if (exclusions.some(exclusion => fontFamily.includes(exclusion))) {
return true;
}

// Check URLs.
if (Array.isArray(urls) && urls.length > 0) {
// First check for exact matches of any URL.
if (urls.some(url => exclusionsSet.has(url))) {
return true;
}

// Then check if any exclusion is a substring of any URL.
if (urls.some(url =>
exclusions.some(exclusion => url.includes(exclusion))
)) {
return true;
}
}

return false;
}

/**
* Checks if an element is visible in the viewport.
Expand Down Expand Up @@ -188,11 +216,12 @@ class BeaconPreloadFonts {
style.content !== 'none' && style.content !== '""' :
element.textContent.trim();

if (hasContent && !this.isSystemFont(fontFamily) && stylesheetFonts[fontFamily]) {
if (!hostedFonts.has(fontFamily)) {
if (hasContent && stylesheetFonts[fontFamily]) {
let urls = stylesheetFonts[fontFamily].urls;
if (!this.isExcluded(fontFamily, urls) && !hostedFonts.has(fontFamily)) {
hostedFonts.set(fontFamily, {
elements: new Set(),
urls: stylesheetFonts[fontFamily].urls,
urls: urls,
variations: stylesheetFonts[fontFamily].variations
});
}
Expand Down Expand Up @@ -447,7 +476,7 @@ class BeaconPreloadFonts {
const style = window.getComputedStyle(element);
const fontInfo = getFontInfoForElement(style);
if (fontInfo) {
if (!matches.has(fontInfo.url)) {
if (!this.isExcluded(fontInfo.family, [fontInfo.url]) && !matches.has(fontInfo.url)) {
matches.set(fontInfo.url, {
elements: new Set(),
variations: new Set()
Expand All @@ -467,7 +496,7 @@ class BeaconPreloadFonts {
if (pseudoStyle.content !== 'none' && pseudoStyle.content !== '""') {
const fontInfo = getFontInfoForElement(pseudoStyle);
if (fontInfo) {
if (!matches.has(fontInfo.url)) {
if (!this.isExcluded(fontInfo.family, [fontInfo.url]) && !matches.has(fontInfo.url)) {
matches.set(fontInfo.url, {
elements: new Set(),
variations: new Set()
Expand Down
32 changes: 25 additions & 7 deletions test/BeaconPreloadFonts.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ describe('BeaconPreloadFonts', () => {
};
const config = {
system_fonts: ['Arial', 'Helvetica'],
font_data: {}
font_data: {},
preload_fonts_exclusions: []
};

// Initialize the class with mock config and logger
Expand Down Expand Up @@ -103,13 +104,30 @@ describe('BeaconPreloadFonts', () => {
sinon.restore(); // Restore sinon mocks
});

describe('isSystemFont', () => {
it('should return true for system fonts', () => {
assert.strictEqual(beaconPreloadFonts.isSystemFont('Arial'), true);
describe('isExcluded', () => {
it('should return true when fontFamily exactly matches exclusion', () => {
beaconPreloadFonts.config.preload_fonts_exclusions = ['Arial'];
assert.strictEqual(beaconPreloadFonts.isExcluded('Arial', []), true);
});

it('should return false for non-system fonts', () => {
assert.strictEqual(beaconPreloadFonts.isSystemFont('Times New Roman'), false);

it('should return true when fontFamily contains exclusion substring', () => {
beaconPreloadFonts.config.preload_fonts_exclusions = ['Ari'];
assert.strictEqual(beaconPreloadFonts.isExcluded('Arial', []), true);
});

it('should return true when URL exactly matches exclusion', () => {
beaconPreloadFonts.config.preload_fonts_exclusions = ['https://example.com/font.woff2'];
assert.strictEqual(beaconPreloadFonts.isExcluded('CustomFont', ['https://example.com/font.woff2']), true);
});

it('should return true when URL contains exclusion substring', () => {
beaconPreloadFonts.config.preload_fonts_exclusions = ['example.com'];
assert.strictEqual(beaconPreloadFonts.isExcluded('CustomFont', ['https://example.com/font.woff2']), true);
});

it('should return false when neither fontFamily nor URLs match exclusions', () => {
beaconPreloadFonts.config.preload_fonts_exclusions = ['Roboto', 'fonts.gstatic.com'];
assert.strictEqual(beaconPreloadFonts.isExcluded('OpenSans', ['https://example.com/font.woff2']), false);
});
});

Expand Down
Loading