Skip to content

Commit 241038d

Browse files
committed
Core Web Vitals
1 parent b245af1 commit 241038d

File tree

3 files changed

+121
-0
lines changed

3 files changed

+121
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Core Vitals
2+
3+
[Measure Core Vitals inside Google Sheets](https://www.labnol.org/core-web-vitals-200819)
4+
5+
You can get your own PageSpeed Insights API key [here](https://developers.google.com/speed/docs/insights/v5/get-started)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"timeZone": "Asia/Kolkata",
3+
"dependencies": {},
4+
"exceptionLogging": "STACKDRIVER",
5+
"runtimeVersion": "V8",
6+
"oauthScopes": [
7+
"https://www.googleapis.com/auth/script.external_request",
8+
"https://www.googleapis.com/auth/script.scriptapp",
9+
"https://www.googleapis.com/auth/spreadsheets.currentonly"
10+
]
11+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* Google Core Web Vitals
3+
* ========================
4+
*
5+
* Written by Amit Agarwal
6+
* Email: amit@labnol.org
7+
* Web: https://www.labnol.org
8+
* Twitter: @labnol
9+
*
10+
* Under MIT License
11+
*/
12+
13+
const getUrls = () => {
14+
const data = SpreadsheetApp.getActiveSpreadsheet()
15+
.getSheetByName('URLs')
16+
.getDataRange()
17+
.getDisplayValues()
18+
.map(([url, name]) => ({ url, name }))
19+
.filter(({ url }) => /^http/i.test(url));
20+
return data;
21+
};
22+
23+
const parseResponse = (metrics, audits, strategy) => {
24+
const getPercentile = (key) => {
25+
return metrics[key].percentile;
26+
};
27+
28+
const getValue = (key) => {
29+
return audits[key].numericValue.toFixed(1);
30+
};
31+
32+
const key = (text) => `${text} (${strategy})`;
33+
34+
return {
35+
[key('First Contentful Paint')]: getPercentile('FIRST_CONTENTFUL_PAINT_MS'),
36+
[key('First Input Delay')]: getPercentile('FIRST_INPUT_DELAY_MS'),
37+
[key('Largest Contentful Paint')]: getPercentile('LARGEST_CONTENTFUL_PAINT_MS'),
38+
[key('Cumulative Layout Shift')]: getPercentile('CUMULATIVE_LAYOUT_SHIFT_SCORE'),
39+
[key('Speed Index')]: getValue('speed-index'),
40+
[key('Time to Interactive')]: getValue('interactive'),
41+
[key('Estimated Input Latency')]: getValue('estimated-input-latency'),
42+
};
43+
};
44+
45+
const fetchPage = (url, strategy) => {
46+
try {
47+
const params = {
48+
url,
49+
strategy,
50+
category: 'performance',
51+
fields: 'loadingExperience,lighthouseResult(audits)',
52+
key: '<<YOUR API KEY HERE>>',
53+
};
54+
const qs = Object.keys(params)
55+
.map((key) => `${key}=${params[key]}`)
56+
.join('&');
57+
const apiUrl = `https://www.googleapis.com/pagespeedonline/v5/runPagespeed?${qs}`;
58+
let response = null;
59+
try {
60+
response = UrlFetchApp.fetch(apiUrl, {
61+
muteHttpExceptions: false,
62+
});
63+
} catch (f) {
64+
Utilities.sleep(5000);
65+
response = UrlFetchApp.fetch(apiUrl, {
66+
muteHttpExceptions: true,
67+
});
68+
}
69+
const { error, loadingExperience: { metrics = null } = {}, lighthouseResult: { audits = null } = {} } = JSON.parse(
70+
response.getContentText()
71+
);
72+
if (!error) return parseResponse(metrics, audits, strategy);
73+
} catch (f) {}
74+
return null;
75+
};
76+
77+
const writeDataToSheet = (name, data) => {
78+
const ss = SpreadsheetApp.getActiveSpreadsheet();
79+
const sheet = ss.getSheetByName(name) || ss.insertSheet(name, 1, { template: ss.getSheetByName('template') });
80+
const vitals = sheet
81+
.getRange(2, 1, sheet.getLastRow() - 1, 1)
82+
.getValues()
83+
.map(([e]) => {
84+
return [data[e]];
85+
});
86+
sheet.getRange(1, sheet.getLastColumn() + 1, vitals.length + 1, 1).setValues([[new Date()], ...vitals]);
87+
};
88+
89+
const measureCoreVitals = () => {
90+
const urls = getUrls();
91+
for (let u = 0; u < urls.length; u++) {
92+
const { name, url } = urls[u];
93+
const data = { ...fetchPage(url, 'desktop'), ...fetchPage(url, 'mobile') };
94+
if (data !== null) {
95+
writeDataToSheet(name, data);
96+
}
97+
}
98+
SpreadsheetApp.flush();
99+
};
100+
101+
const init = () => {
102+
ScriptApp.getProjectTriggers().forEach((trigger) => ScriptApp.deleteTrigger(trigger));
103+
ScriptApp.newTrigger('measureCoreVitals').timeBased().everyDays(1).create();
104+
SpreadsheetApp.getActiveSpreadsheet().toast('Success!');
105+
};

0 commit comments

Comments
 (0)