Skip to content

Commit 2dd3820

Browse files
authored
Merge pull request #58 from wp-media/develop
Release v1.2.0
2 parents 3c33867 + 653c8e9 commit 2dd3820

File tree

8 files changed

+1644
-9
lines changed

8 files changed

+1644
-9
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "wp-rocket-scripts",
3-
"version": "1.0.8",
3+
"version": "1.2.0",
44
"description": "Rocket main scripts packages",
55
"type": "module",
66
"main": "./src/BeaconEntryPoint.js",

src/BeaconManager.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import BeaconLcp from "./BeaconLcp.js";
44
import BeaconLrc from "./BeaconLrc.js";
5+
import BeaconPreloadFonts from "./BeaconPreloadFonts.js";
6+
import BeaconPreconnectExternalDomain from "./BeaconPreconnectExternalDomain.js";
57
import BeaconUtils from "./Utils.js";
68
import Logger from "./Logger.js";
79

@@ -10,6 +12,8 @@ class BeaconManager {
1012
this.config = config;
1113
this.lcpBeacon = null;
1214
this.lrcBeacon = null;
15+
this.preloadFontsBeacon = null;
16+
this.preconnectExternalDomainBeacon = null;
1317
this.infiniteLoopId = null;
1418
this.errorCode = '';
1519
this.logger = new Logger(this.config.debug);
@@ -34,13 +38,19 @@ class BeaconManager {
3438

3539
const isGeneratedBefore = await this._getGeneratedBefore();
3640

37-
// OCI / LCP / ATF
41+
// OCI / LCP / ATF / PRELOAD FONTS / PRECONNECT EXTERNAL DOMAIN
3842
const shouldGenerateLcp = (
3943
this.config.status.atf && (isGeneratedBefore === false || isGeneratedBefore.lcp === false)
4044
);
4145
const shouldGeneratelrc = (
4246
this.config.status.lrc && (isGeneratedBefore === false || isGeneratedBefore.lrc === false)
4347
);
48+
const shouldGeneratePreloadFonts = (
49+
this.config.status.preload_fonts && (isGeneratedBefore === false || isGeneratedBefore.preload_fonts === false)
50+
);
51+
const shouldGeneratePreconnectExternalDomain = (
52+
this.config.status.preconnect_external_domain && (isGeneratedBefore === false || isGeneratedBefore.preconnect_external_domain === false)
53+
);
4454
if (shouldGenerateLcp) {
4555
this.lcpBeacon = new BeaconLcp(this.config, this.logger);
4656
await this.lcpBeacon.run();
@@ -55,7 +65,21 @@ class BeaconManager {
5565
this.logger.logMessage('Not running BeaconLrc because data is already available or feature is disabled');
5666
}
5767

58-
if (shouldGenerateLcp || shouldGeneratelrc) {
68+
if (shouldGeneratePreloadFonts) {
69+
this.preloadFontsBeacon = new BeaconPreloadFonts(this.config, this.logger);
70+
await this.preloadFontsBeacon.run();
71+
} else {
72+
this.logger.logMessage('Not running BeaconPreloadFonts because data is already available or feature is disabled');
73+
}
74+
75+
if (shouldGeneratePreconnectExternalDomain) {
76+
this.preconnectExternalDomainBeacon = new BeaconPreconnectExternalDomain(this.config, this.logger);
77+
await this.preconnectExternalDomainBeacon.run();
78+
} else {
79+
this.logger.logMessage('Not running BeaconPreconnectExternalDomain because data is already available or feature is disabled');
80+
}
81+
82+
if (shouldGenerateLcp || shouldGeneratelrc || shouldGeneratePreloadFonts || shouldGeneratePreconnectExternalDomain) {
5983
this._saveFinalResultIntoDB();
6084
} else {
6185
this.logger.logMessage("Not saving results into DB as no beacon features ran.");
@@ -101,7 +125,9 @@ class BeaconManager {
101125
_saveFinalResultIntoDB() {
102126
const results = {
103127
lcp: this.lcpBeacon ? this.lcpBeacon.getResults() : null,
104-
lrc: this.lrcBeacon ? this.lrcBeacon.getResults() : null
128+
lrc: this.lrcBeacon ? this.lrcBeacon.getResults() : null,
129+
preload_fonts: this.preloadFontsBeacon ? this.preloadFontsBeacon.getResults() : null,
130+
preconnect_external_domain: this.preconnectExternalDomainBeacon ? this.preconnectExternalDomainBeacon.getResults() : null
105131
};
106132

107133
const data = new FormData();
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
'use strict';
2+
3+
class BeaconPreconnectExternalDomain {
4+
constructor(config, logger) {
5+
this.logger = logger;
6+
this.result = [];
7+
8+
this.excludedPatterns = config.preconnect_external_domain_exclusions;
9+
this.eligibleElements = config.preconnect_external_domain_elements;
10+
11+
this.matchedItems = new Set();
12+
this.excludedItems = new Set();
13+
}
14+
15+
/**
16+
* Initiates the process of identifying and logging external domains that require preconnection.
17+
* This method queries the document for eligible elements, processes each element to determine
18+
* if it should be preconnected, and logs the results.
19+
*/
20+
async run() {
21+
const elements = document.querySelectorAll(
22+
`${this.eligibleElements.join(', ')}[src], ${this.eligibleElements.join(', ')}[href], ${this.eligibleElements.join(', ')}[rel], ${this.eligibleElements.join(', ')}[type]`
23+
);
24+
25+
elements.forEach(el => this.processElement(el));
26+
27+
this.logger.logMessage({matchedItems: this.getMatchedItems(), excludedItems: Array.from(this.excludedItems)});
28+
}
29+
30+
/**
31+
* Processes a single element to determine if it should be preconnected.
32+
*
33+
* This method checks if the element is excluded based on attribute or domain rules.
34+
* If not excluded, it checks if the element's URL is an external domain and adds it to the list of matched items.
35+
*
36+
* @param {Element} el - The element to process.
37+
*/
38+
processElement(el) {
39+
try {
40+
const url = new URL(el.src || el.href || '', location.href);
41+
42+
if (this.isExcluded(el)) {
43+
this.excludedItems.add(this.createExclusionObject(url, el));
44+
return;
45+
}
46+
47+
if (this.isExternalDomain(url)) {
48+
this.matchedItems.add(`${url.hostname}-${el.tagName.toLowerCase()}`);
49+
this.result = [...new Set(this.result.concat(url.origin))];
50+
}
51+
} catch (e) {
52+
this.logger.logMessage(e);
53+
}
54+
}
55+
56+
/**
57+
* Checks if an element is excluded based on exclusions patterns.
58+
*
59+
* This method iterates through the excludedPatterns array and checks if any pattern matches any of the element's attribute or values.
60+
* If a match is found, it returns true, indicating the element is excluded.
61+
*
62+
* @param {Element} el - The element to check.
63+
* @returns {boolean} True if the element is excluded by an attribute rule, false otherwise.
64+
*/
65+
isExcluded(el) {
66+
const outerHTML = el.outerHTML.substring(0, el.outerHTML.indexOf('>') + 1);
67+
return this.excludedPatterns.some(
68+
(pattern) => outerHTML.includes(pattern)
69+
);
70+
}
71+
72+
/**
73+
* Checks if a URL is excluded based on domain rules.
74+
*
75+
* This method iterates through the excludedPatterns array and checks if any pattern matches the URL's hostname.
76+
* If a match is found, it returns true, indicating the URL is excluded.
77+
*
78+
* @param {URL} url - The URL to check.
79+
* @returns {boolean} True if the URL is excluded by a domain rule, false otherwise.
80+
*/
81+
isExcludedByDomain(url) {
82+
return this.excludedPatterns.some(pattern =>
83+
pattern.type === 'domain' && url.hostname.includes(pattern.value)
84+
);
85+
}
86+
87+
/**
88+
* Checks if a URL is from an external domain.
89+
*
90+
* This method compares the hostname of the given URL with the hostname of the current location.
91+
* If they are not the same, it indicates the URL is from an external domain.
92+
*
93+
* @param {URL} url - The URL to check.
94+
* @returns {boolean} True if the URL is from an external domain, false otherwise.
95+
*/
96+
isExternalDomain(url) {
97+
return url.hostname !== location.hostname && url.hostname;
98+
}
99+
100+
/**
101+
* Creates an exclusion object based on the URL, element.
102+
*
103+
* @param {URL} url - The URL to create the exclusion object for.
104+
* @param {Element} el - The element to create the exclusion object for.
105+
* @returns {Object} An object with the URL's hostname, the element's tag name, and the reason.
106+
*/
107+
createExclusionObject(url, el) {
108+
return { domain: url.hostname, elementType: el.tagName.toLowerCase()};
109+
}
110+
111+
/**
112+
* Returns an array of matched items, each item split into its domain and element type.
113+
*
114+
* This method iterates through the matchedItems set, splits each item into its domain and element type using the last hyphen as a delimiter,
115+
* and returns an array of these split items.
116+
*
117+
* @returns {Array} An array of arrays, each containing a domain and an element type.
118+
*/
119+
getMatchedItems() {
120+
return Array.from(this.matchedItems).map(item => {
121+
const lastHyphenIndex = item.lastIndexOf('-');
122+
return [
123+
item.substring(0, lastHyphenIndex), // Domain
124+
item.substring(lastHyphenIndex + 1) // Element type
125+
];
126+
});
127+
}
128+
129+
/**
130+
* Returns the array of unique domain names that were found to be external.
131+
*
132+
* This method returns the result array, which contains a list of unique domain names that were identified as external during the analysis process.
133+
*
134+
* @returns {Array} An array of unique domain names.
135+
*/
136+
getResults() {
137+
return this.result;
138+
}
139+
}
140+
141+
export default BeaconPreconnectExternalDomain;

0 commit comments

Comments
 (0)