Skip to content
Snippets Groups Projects
Commit 1b8abeb7 authored by Erick Hitter's avatar Erick Hitter
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
.idea
**node_modules/
# eth-web-vitals
Log Google's "[Web Vitals](https://github.com/GoogleChrome/web-vitals)" to various analytics platforms.
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
{
"env": {
"browser": true,
"amd": true,
"node": true
},
"plugins": [
"babel"
],
"parser": "babel-eslint",
"extends": [ "eslint:recommended" ],
"rules": {
"indent": ["error", 4],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "single"]
}
}
# Performance Metrics JS library
Client library for measuring and reporting site performance.
## Building and running on localhost
First step to install dependencies:
```sh
npm install
```
To create a production build:
```sh
npm run build-prod
```
To create a development build:
```sh
npm run build-dev
```
!function(e){var t={};function n(i){if(t[i])return t[i].exports;var a=t[i]={i:i,l:!1,exports:{}};return e[i].call(a.exports,a,a.exports,n),a.l=!0,a.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var a in e)n.d(i,a,function(t){return e[t]}.bind(null,a));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t);var i,a,r=function(){return"".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)},o=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;return{name:e,value:t,delta:0,entries:[],id:r(),isFinal:!1}},u=function(e,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){var n=new PerformanceObserver((function(e){return e.getEntries().map(t)}));return n.observe({type:e,buffered:!0}),n}}catch(e){}},c=!1,s=!1,l=function(e){c=!e.persisted},d=function(){addEventListener("pagehide",l),addEventListener("unload",(function(){}))},f=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];s||(d(),s=!0),addEventListener("visibilitychange",(function(t){var n=t.timeStamp;"hidden"===document.visibilityState&&e({timeStamp:n,isUnloading:c})}),{capture:!0,once:t})},p=function(e,t,n,i){var a;return function(){n&&t.isFinal&&n.disconnect(),t.value>=0&&(i||t.isFinal||"hidden"===document.visibilityState)&&(t.delta=t.value-(a||0),(t.delta||t.isFinal||void 0===a)&&(e(t),a=t.value))}},v=function(){return void 0===i&&(i="hidden"===document.visibilityState?0:1/0,f((function(e){var t=e.timeStamp;return i=t}),!0)),{get timeStamp(){return i}}},m=function(){return a||(a=new Promise((function(e){return["scroll","keydown","pointerdown"].map((function(t){addEventListener(t,e,{once:!0,passive:!0,capture:!0})}))}))),a};const y=e=>{window.ethWebVitals.queue.push(e)},g=({id:e,name:t,delta:n})=>{const{eventCategory:i}=window.ethWebVitals.config,a=((e,t)=>Math.round("CLS"===e?1e4*t:t))(t,n),r=["send","event",{eventCategory:i,eventAction:t,eventValue:a,eventLabel:e,nonInteraction:!0}];"function"==typeof ga&&ga(...r),"object"==typeof _paq&&_paq.push(["trackEvent",i,t,a]),console.info(r[2])};window.addEventListener("unload",()=>{window.ethWebVitals.queue.map(g)}),function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=o("CLS",0),i=function(e){e.hadRecentInput||(n.value+=e.value,n.entries.push(e),r())},a=u("layout-shift",i),r=p(e,n,a,t);f((function(e){var t=e.isUnloading;a&&a.takeRecords().map(i),t&&(n.isFinal=!0),r()}))}(y),function(e){var t=o("FCP"),n=v(),i=u("paint",(function(e){"first-contentful-paint"===e.name&&e.startTime<n.timeStamp&&(t.value=e.startTime,t.isFinal=!0,t.entries.push(e),a())})),a=p(e,t,i)}(y),function(e){var t=o("FID"),n=v(),i=function(e){e.startTime<n.timeStamp&&(t.value=e.processingStart-e.startTime,t.entries.push(e),t.isFinal=!0,r())},a=u("first-input",i),r=p(e,t,a);f((function(){a&&(a.takeRecords().map(i),a.disconnect())}),!0),a||window.perfMetrics&&window.perfMetrics.onFirstInputDelay&&window.perfMetrics.onFirstInputDelay((function(e,i){i.timeStamp<n.timeStamp&&(t.value=e,t.isFinal=!0,t.entries=[{entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+e}],r())}))}(y),function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=o("LCP"),i=v(),a=function(e){var t=e.startTime;t<i.timeStamp?(n.value=t,n.entries.push(e)):n.isFinal=!0,c()},r=u("largest-contentful-paint",a),c=p(e,n,r,t),s=function(){n.isFinal||(r&&r.takeRecords().map(a),n.isFinal=!0,c())};m().then(s),f(s,!0)}(y),function(e){var t,n=o("TTFB");t=function(){try{var t=performance.getEntriesByType("navigation")[0]||function(){var e=performance.timing,t={entryType:"navigation",startTime:0};for(var n in e)"navigationStart"!==n&&"toJSON"!==n&&(t[n]=Math.max(e[n]-e.navigationStart,0));return t}();n.value=n.delta=t.responseStart,n.entries=[t],n.isFinal=!0,e(n)}catch(e){}},"complete"===document.readyState?setTimeout(t,0):addEventListener("pageshow",t)}(e=>{y(e);const t=e.value-e.entries[0].requestStart,n=Object.assign({},e);n.name="TTFB-RT",n.value=t,n.delta=t,y(n)})}]);
\ No newline at end of file
Source diff could not be displayed: it is too large. Options to address this: view the blob.
{
"name": "pmc-performance-metrics",
"version": "1.0.0",
"description": "Site performance monitoring",
"main": "index.js",
"keywords": [],
"author": "",
"license": "ISC",
"scripts": {
"clean": "rm build/*.js || true",
"dev": "npm run clean && npm run lint && webpack -d --mode development",
"prod": "npm run clean && npm run lint && webpack -p --mode production",
"lint": "eslint ./src/ -c ./.eslintrc",
"lint-fix": "npm run lint -- --fix"
},
"dependencies": {
"web-vitals": "^0.2.1"
},
"devDependencies": {
"@babel/cli": "^7.2.3",
"@babel/core": "^7.8.7",
"@babel/preset-env": "^7.8.7",
"@wordpress/eslint-plugin": "^4.0.0",
"babel-cli": "^6.26.0",
"babel-loader": "^8.0.6",
"eslint": "^6.8.0",
"eslint-plugin-babel": "^5.3.0",
"html-webpack-plugin": "^3.2.0",
"html-webpack-template": "^6.2.0",
"webpack": "^4.42.0",
"webpack-cli": "^3.3.11"
}
}
/* global ga,_paq */
import {getCLS, getFCP, getFID, getLCP, getTTFB} from 'web-vitals';
/**
* 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).
* @link https://github.com/GoogleChrome/web-vitals#using-analyticsjs
*
* @param {string} name Metric name.
* @param {int} delta Metric magnitude of change.
* @returns {number}
*/
const parseValue = ( name, delta ) => {
return Math.round( 'CLS' === name ? delta * 10000 : delta );
};
/**
* Queue a metric for reporting.
*
* @param {object} metric Reporting metric.
*/
const queue = ( metric ) => {
window.ethWebVitals.queue.push( metric );
};
/**
* Report a metric via a Google tool.
*
* @param {string} id Metric ID.
* @param {string} name Metric name.
* @param {int} delta Metric magnitude of change.
*/
const report = ( {id, name, delta} ) => {
const {eventCategory} = window.ethWebVitals.config;
const value = parseValue( name, delta );
const cbArgs = [
'send',
'event',
{
eventCategory,
eventAction: name,
eventValue: value,
eventLabel: id,
nonInteraction: true
}
];
if ( 'function' === typeof ga ) {
ga( ...cbArgs );
}
if ( 'object' === typeof _paq ) {
_paq.push( ['trackEvent', eventCategory, name, value] );
}
console.info( cbArgs[2] );
};
// Report queued metrics.
window.addEventListener(
'unload',
() => {
window.ethWebVitals.queue.map( report );
}
);
// Queue all the things!
getCLS( queue );
getFCP( queue );
getFID( queue );
getLCP( queue );
getTTFB( ( metric ) => {
queue( metric );
// Exclude timing attributed to `unload` event from previous page, etc.
// See https://github.com/GoogleChrome/web-vitals#getttfb.
const requestTime = metric.value - metric.entries[0].requestStart;
const modifiedMetric = Object.assign( {}, metric );
modifiedMetric.name = 'TTFB-RT';
modifiedMetric.value = requestTime;
modifiedMetric.delta = requestTime;
queue( modifiedMetric );
} );
const webpack = require('webpack');
const path = require('path');
const config = {
entry: {
'web-vitals': './src/web-vitals.js',
},
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].js'
}
};
module.exports = config;
<?php
/**
* Google's Web Vitals measurements.
*
* @link https://github.com/GoogleChrome/web-vitals
*
* @package eth-web-vitals
*/
namespace ETH\Web_Vitals;
/**
* Class Web_Vitals.
*/
class Web_Vitals {
use Singleton;
/**
* Reporting category for analytics provider.
*/
protected const EVENT_CATEGORY = 'Web Vitals';
/**
* Output priority.
*
* Load early to capture as soon as possible.
*/
protected const OUTPUT_PRIORITY = 0;
/**
* Web_Vitals constructor.
*/
protected function __construct() {
add_action( 'wp_head', [ $this, 'render' ], static::OUTPUT_PRIORITY );
}
/**
* Output the Web Vitals script.
*/
public function render(): void {
$data = [
'config' => [
'eventCategory' => static::EVENT_CATEGORY,
],
'queue' => [],
];
?>
<script>
window.ethWebVitals = window.ethWebVitals || {};
window.ethWebVitals = <?php echo wp_json_encode( $data ); ?>;
<?php echo file_get_contents( PLUGIN_PATH . 'assets/build/web-vitals.js' ); ?>
</script>
<?php
}
}
<?php
/**
* Singleton trait which implements Singleton pattern in any class in which this trait is used.
*
* Dependent items can use the `pmc_singleton_init_{$called_class}` hook to
* execute code immediately after _init() is called.
*
* Using the singleton pattern in WordPress is an easy way to protect against
* mistakes caused by creating multiple objects or multiple initialization
* of classes which need to be initialized only once.
*
* With complex plugins, there are many cases where multiple copies of
* the plugin would load, and action hooks would load (and trigger) multiple
* times.
*
* If you're planning on using a global variable, then you should implement
* this trait. Singletons are a way to safely use globals; they let you
* access and set the global from anywhere, without risk of collision.
*
* If any method in a class needs to be aware of "state", then you should
* implement this trait in that class.
*
* If any method in the class need to "talk" to another or be aware of what
* another method has done, then you should implement this trait in that class.
*
* If you specifically need multiple objects, then use a normal class.
*
* If you're unsure, ask in engineering chat.
*
* @since 2017-06-19 Amit Gupta
*/
namespace ETH\Web_Vitals;
trait Singleton {
protected static $_instance = array();
/**
* Protected class constructor to prevent direct object creation
*
* This is meant to be overridden in the classes which implement
* this trait. This is ideal for doing stuff that you only want to
* do once, such as hooking into actions and filters, etc.
*/
protected function __construct() { }
/**
* Prevent object cloning
*/
final protected function __clone() { }
/**
* This method returns new or existing Singleton instance
* of the class for which it is called. This method is set
* as final intentionally, it is not meant to be overridden.
*
* @return object|static Singleton instance of the class.
*/
final public static function get_instance() {
/**
* If this trait is implemented in a class which has multiple
* sub-classes then static::$_instance will be overwritten with the most recent
* sub-class instance. Thanks to late static binding
* we use get_called_class() to grab the called class name, and store
* a key=>value pair for each `classname => instance` in self::$_instance
* for each sub-class.
*/
$called_class = get_called_class();
if ( ! isset( static::$_instance[ $called_class ] ) ) {
static::$_instance[ $called_class ] = new $called_class();
}
return static::$_instance[ $called_class ];
}
}
<?php
/**
* Plugin Name: ETH Web Vitals
* Plugin URI: https://ethitter.com
* Description: Record performance metrics in analytics platforms.
* Version: 1.0
* License: PMC Proprietary. All rights reserved.
* Author: PMC
*/
namespace ETH\Web_Vitals;
define( 'ETH\\Web_Vitals\\PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
require_once __DIR__ . '/classes/trait-singleton.php';
require_once __DIR__ . '/classes/class-web-vitals.php';
Web_Vitals::get_instance();
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment