From 69957bab53767f6bd187448cc68171a6a02918ba Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Fri, 26 Aug 2022 18:58:50 +0100 Subject: [PATCH] Add web vitals library (#3553) * Adds web-vitals and greater analytics tracking * Add attribution * Linting fixes * More linting * Fix Navigation Type * Add JSON to prod build too * Linting fixes * Fix types * Review feedback * Updated plugin-json comment --- .eslintrc.js | 3 + package-lock.json | 24 +++++++ package.json | 2 + rollup.config.js | 5 ++ site/_data/analytics.json | 8 ++- site/_js/analytics.js | 78 +++++++++++++++++++++++ site/_js/main.js | 1 + types/modules/webdev_analytics/index.d.ts | 25 ++++++++ 8 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 site/_js/analytics.js create mode 100644 types/modules/webdev_analytics/index.d.ts diff --git a/.eslintrc.js b/.eslintrc.js index 58705c2a9a19..a825f721e732 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,6 +3,9 @@ module.exports = { parserOptions: { sourceType: 'module', }, + globals: { + ga: true, + }, env: { browser: true, node: true, diff --git a/package-lock.json b/package-lock.json index 41c3574d80f7..d9468a440584 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,6 +46,7 @@ "typedoc": "^0.22.7", "typescript": "^4.4.4", "unistore": "^3.5.2", + "web-vitals": "^3.0.0", "webdev-infra": "^1.0.32" }, "devDependencies": { @@ -56,6 +57,7 @@ "@rollup/plugin-json": "^4.1.0", "@types/cheerio": "^0.22.22", "@types/express": "^4.17.13", + "@types/google.analytics": "^0.0.40", "@types/js-yaml": "^4.0.3", "@types/node": "^14.17.32", "@types/node-fetch": "^2.5.12", @@ -2831,6 +2833,12 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.4.tgz", "integrity": "sha512-TMgXmy0v2xWyuCSCJM6NCna2snndD8yvQF67J29ipdzMcsPa9u+o0tjF5+EQNdhcuZplYuouYqpc4zcd5I6amQ==" }, + "node_modules/@types/google.analytics": { + "version": "0.0.40", + "resolved": "https://registry.npmjs.org/@types/google.analytics/-/google.analytics-0.0.40.tgz", + "integrity": "sha512-R3HpnLkqmKxhUAf8kIVvDVGJqPtaaZlW4yowNwjOZUTmYUQEgHh8Nh5wkSXKMroNAuQM8gbXJHmNbbgA8tdb7Q==", + "dev": true + }, "node_modules/@types/js-yaml": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.3.tgz", @@ -26550,6 +26558,11 @@ "defaults": "^1.0.3" } }, + "node_modules/web-vitals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.0.0.tgz", + "integrity": "sha512-3Gh6rH5aetFYqfkl9V59KCvjj9vp9U2Tkaep9MO+xpAVg+JULmQfi5zEkcPLkE6iU8pNYVwdjHvIU8RFAchYyQ==" + }, "node_modules/webdev-infra": { "version": "1.0.32", "resolved": "https://registry.npmjs.org/webdev-infra/-/webdev-infra-1.0.32.tgz", @@ -29304,6 +29317,12 @@ } } }, + "@types/google.analytics": { + "version": "0.0.40", + "resolved": "https://registry.npmjs.org/@types/google.analytics/-/google.analytics-0.0.40.tgz", + "integrity": "sha512-R3HpnLkqmKxhUAf8kIVvDVGJqPtaaZlW4yowNwjOZUTmYUQEgHh8Nh5wkSXKMroNAuQM8gbXJHmNbbgA8tdb7Q==", + "dev": true + }, "@types/js-yaml": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.3.tgz", @@ -47853,6 +47872,11 @@ "defaults": "^1.0.3" } }, + "web-vitals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.0.0.tgz", + "integrity": "sha512-3Gh6rH5aetFYqfkl9V59KCvjj9vp9U2Tkaep9MO+xpAVg+JULmQfi5zEkcPLkE6iU8pNYVwdjHvIU8RFAchYyQ==" + }, "webdev-infra": { "version": "1.0.32", "resolved": "https://registry.npmjs.org/webdev-infra/-/webdev-infra-1.0.32.tgz", diff --git a/package.json b/package.json index a7dd1c7efc12..622b0e502bfa 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "typedoc": "^0.22.7", "typescript": "^4.4.4", "unistore": "^3.5.2", + "web-vitals": "^3.0.0", "webdev-infra": "^1.0.32" }, "devDependencies": { @@ -89,6 +90,7 @@ "@rollup/plugin-json": "^4.1.0", "@types/cheerio": "^0.22.22", "@types/express": "^4.17.13", + "@types/google.analytics": "^0.0.40", "@types/js-yaml": "^4.0.3", "@types/node": "^14.17.32", "@types/node-fetch": "^2.5.12", diff --git a/rollup.config.js b/rollup.config.js index 4eaa49b3af26..7b5290a5a267 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -13,6 +13,9 @@ import {terser} from 'rollup-plugin-terser'; // A Rollup plugin for copying files. import copy from 'rollup-plugin-copy'; +// A Rollup plugin for reading JSON files. +import json from '@rollup/plugin-json'; + const devConfig = { input: [ 'site/_js/main.js', @@ -31,6 +34,7 @@ const devConfig = { nodeResolve(), commonjs(), svg(), + json(), copy({ // Legacy docs, like those at /docs/native-client/, rely on the old // prettify.js code for syntax highlighting. @@ -52,6 +56,7 @@ const productionConfig = { nodeResolve(), commonjs(), svg(), + json(), terser({ format: { // Remove all comments, including @license comments, diff --git a/site/_data/analytics.json b/site/_data/analytics.json index 57b090b43bae..f53856ac9bf8 100644 --- a/site/_data/analytics.json +++ b/site/_data/analytics.json @@ -1,7 +1,9 @@ { "id": "UA-41980257-1", "dimensions": { - "TRACKING_VERSION": "dimension1" + "TRACKING_VERSION": "dimension1", + "NAVIGATION_TYPE": "dimension2", + "WEB_VITALS_DEBUG": "dimension3" }, - "TRACKING_VERSION": "2.0" -} \ No newline at end of file + "TRACKING_VERSION": "3.0" +} diff --git a/site/_js/analytics.js b/site/_js/analytics.js new file mode 100644 index 000000000000..0e8bd3dabfcd --- /dev/null +++ b/site/_js/analytics.js @@ -0,0 +1,78 @@ +import { + onCLS, + onFCP, + onFID, + onLCP, + onTTFB, + onINP, +} from 'web-vitals/attribution'; +import {dimensions} from '../_data/analytics.json'; + +/** + * See: https://github.com/GoogleChrome/web-vitals#using-analyticsjs + * @param {Object} metric + */ +function sendToGoogleAnalytics({name, delta, id, attribution, navigationType}) { + let webVitalInfo = '(not set)'; + + switch (name) { + case 'CLS': + webVitalInfo = attribution.largestShiftTarget; + break; + case 'FID': + case 'INP': + webVitalInfo = attribution.eventTarget; + break; + case 'LCP': + webVitalInfo = attribution.element; + break; + } + + // Assumes the global `ga()` function exists, see: + // https://developers.google.com/analytics/devguides/collection/analyticsjs + ga('send', 'event', { + eventCategory: 'Web Vitals', + eventAction: name, + // Google Analytics metrics must be integers, so the value is rounded. + // For CLS the value is first multiplied by 1000 for greater precision + // (note: increase the multiplier for greater precision if needed). + eventValue: Math.round(name === 'CLS' ? delta * 1000 : delta), + // The `id` value will be unique to the current page load. When sending + // multiple values from the same page (e.g. for CLS), Google Analytics can + // compute a total by grouping on this ID (note: requires `eventLabel` to + // be a dimension in your report). + eventLabel: id, + // Use a non-interaction event to avoid affecting bounce rate. + nonInteraction: true, + + // See: https://web.dev/debug-web-vitals-in-the-field/ + [dimensions.WEB_VITALS_DEBUG]: webVitalInfo, + [dimensions.NAVIGATION_TYPE]: navigationType, + }); +} + +/** + * Add a listener to detect back/forward cache restores and track them + * as pageviews with the "bfcache" navigation type set (in case we need + * to distinguish them from regular pageviews). + * https://web.dev/bfcache/#how-bfcache-affects-analytics-and-performance-measurement + */ +window.addEventListener( + 'pageshow', + /** + * @param {PageTransitionEvent} e + */ + e => { + if (e.persisted) { + ga('set', dimensions.NAVIGATION_TYPE, 'back-forward-cache'); + ga('send', 'pageview'); + } + } +); + +onCLS(sendToGoogleAnalytics); +onFCP(sendToGoogleAnalytics); +onFID(sendToGoogleAnalytics); +onINP(sendToGoogleAnalytics); +onLCP(sendToGoogleAnalytics); +onTTFB(sendToGoogleAnalytics); diff --git a/site/_js/main.js b/site/_js/main.js index 404e25e0bcc0..05dadec04c8c 100644 --- a/site/_js/main.js +++ b/site/_js/main.js @@ -16,6 +16,7 @@ // Utilities import './store.js'; +import './analytics.js'; // Web Components // These are components that appear on _every_ page. diff --git a/types/modules/webdev_analytics/index.d.ts b/types/modules/webdev_analytics/index.d.ts new file mode 100644 index 000000000000..03de04d91051 --- /dev/null +++ b/types/modules/webdev_analytics/index.d.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +declare module 'webdev_analytics' { + export declare const id: string; + export declare const dimensions: { + TRACKING_VERSION: 'dimension1'; + NAVIGATION_TYPE: 'dimension2'; + WEB_VITALS_DEBUG: 'dimension3'; + }; + export declare const version: number; +}