@automattic/jetpack-image-guide 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/build/MeasurableImage.d.ts +10 -4
- package/build/find-image-elements.d.ts +2 -2
- package/build/index.asset.php +1 -1
- package/build/index.js +1 -1
- package/package.json +1 -1
- package/src/MeasurableImage.ts +23 -7
- package/src/find-image-elements.ts +22 -19
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.1] - 2023-07-11
|
|
9
|
+
### Fixed
|
|
10
|
+
- Image Guide: Fix for broken background-images causing the image guide not to load [#31792]
|
|
11
|
+
|
|
12
|
+
## [0.5.0] - 2023-07-07
|
|
13
|
+
### Added
|
|
14
|
+
- Boost: exclude small images from Image Size Analysis [#31504]
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Return an error when an image is missing, instead of -1 by -1 dimensions. [#31632]
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- Check response.url, not response.ok to verify a response worked [#31538]
|
|
21
|
+
|
|
8
22
|
## [0.4.0] - 2023-06-23
|
|
9
23
|
### Added
|
|
10
24
|
- Jetpack Boost: add a proxy to Image Guide so it can load remote images. [#31145]
|
|
@@ -49,4 +63,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
49
63
|
### Removed
|
|
50
64
|
- Minor package.json change - removing private entry.
|
|
51
65
|
|
|
66
|
+
[0.5.1]: https://github.com/Automattic/jetpack-image-guide/compare/v0.5.0...v0.5.1
|
|
67
|
+
[0.5.0]: https://github.com/Automattic/jetpack-image-guide/compare/v0.4.0...v0.5.0
|
|
52
68
|
[0.4.0]: https://github.com/Automattic/jetpack-image-guide/compare/v0.3.0...v0.4.0
|
|
@@ -6,7 +6,7 @@ export type Dimensions = {
|
|
|
6
6
|
export type Weight = {
|
|
7
7
|
weight: number;
|
|
8
8
|
};
|
|
9
|
-
type FetchFn = (input: URL | RequestInfo, init?: RequestInit) => Promise<Response>;
|
|
9
|
+
export type FetchFn = (input: URL | RequestInfo, init?: RequestInit) => Promise<Response>;
|
|
10
10
|
/**
|
|
11
11
|
* A class that represents a DOM Element that
|
|
12
12
|
* has an image that should be measured and
|
|
@@ -15,7 +15,7 @@ type FetchFn = (input: URL | RequestInfo, init?: RequestInit) => Promise<Respons
|
|
|
15
15
|
export declare class MeasurableImage {
|
|
16
16
|
readonly node: HTMLElement | HTMLImageElement;
|
|
17
17
|
private getURLCallback;
|
|
18
|
-
fetch:
|
|
18
|
+
fetch: FetchFn;
|
|
19
19
|
/**
|
|
20
20
|
* Constructor.
|
|
21
21
|
*
|
|
@@ -23,7 +23,7 @@ export declare class MeasurableImage {
|
|
|
23
23
|
* @param {SourceCallbackFn} getURL - A function that takes in the node and returns the URL of the image.
|
|
24
24
|
* @param {FetchFn} fetchFn - A function that fetches a URL and returns a Promise.
|
|
25
25
|
*/
|
|
26
|
-
constructor(node: HTMLElement | HTMLImageElement, getURL: SourceCallbackFn, fetchFn?: FetchFn);
|
|
26
|
+
constructor(node: HTMLElement | HTMLImageElement, getURL: SourceCallbackFn, fetchFn?: FetchFn | null);
|
|
27
27
|
getURL(): string;
|
|
28
28
|
getSizeOnPage(): {
|
|
29
29
|
width: number;
|
|
@@ -61,5 +61,11 @@ export declare class MeasurableImage {
|
|
|
61
61
|
* @param {string} url - image url
|
|
62
62
|
*/
|
|
63
63
|
private fetchFileDimensions;
|
|
64
|
+
/**
|
|
65
|
+
* Checks if the image is too small and should be ignored. Will return false on images
|
|
66
|
+
* that don't load at all - we can't establish they're tiny!
|
|
67
|
+
*
|
|
68
|
+
* @returns {boolean} - if the image is smaller than 65 pixels width and height return true
|
|
69
|
+
*/
|
|
70
|
+
isImageTiny(): Promise<boolean>;
|
|
64
71
|
}
|
|
65
|
-
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MeasurableImage } from './MeasurableImage';
|
|
1
|
+
import { FetchFn, MeasurableImage } from './MeasurableImage';
|
|
2
2
|
/**
|
|
3
3
|
* Get elements that either are image tags or have a background image.
|
|
4
4
|
*
|
|
@@ -28,4 +28,4 @@ export declare function backgroundImageSource(node: HTMLElement): string;
|
|
|
28
28
|
* @param {(input: string, init?: Array) => Promise<Response>} fetchFn - A function that fetches a URL and returns a Promise.
|
|
29
29
|
* @returns {MeasurableImage[]} - A list of MeasurableImage objects.
|
|
30
30
|
*/
|
|
31
|
-
export declare function getMeasurableImages(domNodes: Element[], fetchFn?:
|
|
31
|
+
export declare function getMeasurableImages(domNodes: Element[], fetchFn?: FetchFn | null): Promise<MeasurableImage[]>;
|
package/build/index.asset.php
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<?php return array('dependencies' => array(), 'version' => '
|
|
1
|
+
<?php return array('dependencies' => array(), 'version' => 'c807a5e4fdcff02da5bb');
|
package/build/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ImageGuideLibrary=e():t.ImageGuideLibrary=e()}(self,(()=>(()=>{"use strict";var t={38:(t,e,n)=>{n.d(e,{z:()=>i});var r=function(t,e,n,r){return new(n||(n=Promise))((function(o,i){function u(t){try{c(r.next(t))}catch(t){i(t)}}function a(t){try{c(r.throw(t))}catch(t){i(t)}}function c(t){var e;t.done?o(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(u,a)}c((r=r.apply(t,e||[])).next())}))},o=function(t,e){var n,r,o,i,u={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(a){return function(c){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;i&&(i=0,a[0]&&(u=0)),u;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return u.label++,{value:a[1],done:!1};case 5:u.label++,r=a[1],a=[0];continue;case 7:a=u.ops.pop(),u.trys.pop();continue;default:if(!(o=u.trys,(o=o.length>0&&o[o.length-1])||6!==a[0]&&2!==a[0])){u=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]<o[3])){u.label=a[1];break}if(6===a[0]&&u.label<o[1]){u.label=o[1],o=a;break}if(o&&u.label<o[2]){u.label=o[2],u.ops.push(a);break}o[2]&&u.ops.pop(),u.trys.pop();continue}a=e.call(t,u)}catch(t){a=[6,t],r=0}finally{n=o=0}if(5&a[0])throw a[1];return{value:a[0]?a[1]:void 0,done:!0}}([a,c])}}},i=function(){function t(t,e,n){void 0===n&&(n=
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ImageGuideLibrary=e():t.ImageGuideLibrary=e()}(self,(()=>(()=>{"use strict";var t={38:(t,e,n)=>{n.d(e,{z:()=>i});var r=function(t,e,n,r){return new(n||(n=Promise))((function(o,i){function u(t){try{c(r.next(t))}catch(t){i(t)}}function a(t){try{c(r.throw(t))}catch(t){i(t)}}function c(t){var e;t.done?o(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(u,a)}c((r=r.apply(t,e||[])).next())}))},o=function(t,e){var n,r,o,i,u={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(a){return function(c){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;i&&(i=0,a[0]&&(u=0)),u;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return u.label++,{value:a[1],done:!1};case 5:u.label++,r=a[1],a=[0];continue;case 7:a=u.ops.pop(),u.trys.pop();continue;default:if(!(o=u.trys,(o=o.length>0&&o[o.length-1])||6!==a[0]&&2!==a[0])){u=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]<o[3])){u.label=a[1];break}if(6===a[0]&&u.label<o[1]){u.label=o[1],o=a;break}if(o&&u.label<o[2]){u.label=o[2],u.ops.push(a);break}o[2]&&u.ops.pop(),u.trys.pop();continue}a=e.call(t,u)}catch(t){a=[6,t],r=0}finally{n=o=0}if(5&a[0])throw a[1];return{value:a[0]?a[1]:void 0,done:!0}}([a,c])}}},i=function(){function t(t,e,n){void 0===n&&(n=null),this.node=t,this.getURLCallback=e,this.fetch=null!=n?n:window.fetch.bind(window)}return t.prototype.getURL=function(){return this.getURLCallback(this.node)},t.prototype.getSizeOnPage=function(){var t=this.node.getBoundingClientRect(),e=t.width,n=t.height;return{width:Math.round(e),height:Math.round(n)}},t.prototype.getFileSize=function(t){return r(this,void 0,void 0,(function(){var e,n,r,i,u;return o(this,(function(o){switch(o.label){case 0:return[4,Promise.all([this.fetchFileWeight(t),this.fetchFileDimensions(t)])];case 1:return e=o.sent(),n=e[0],r=e[1],i=r.width,u=r.height,[2,{width:i,height:u,weight:n}]}}))}))},t.prototype.getPotentialSavings=function(t,e){var n=this.getOversizedRatio(t,e);return n<=1?null:Math.round(t.weight-t.weight/n)},t.prototype.getExpectedSize=function(t){var e=window.devicePixelRatio||1;return{width:Math.round(t.width*e),height:Math.round(t.height*e)}},t.prototype.getOversizedRatio=function(t,e){var n=this.getExpectedSize(e),r=n.width,o=n.height;return t.width*t.height/(r*o)},t.prototype.fetchFileWeight=function(t){return r(this,void 0,void 0,(function(){var e;return o(this,(function(n){switch(n.label){case 0:return[4,this.fetch(t)];case 1:return(e=n.sent()).ok?[2,parseInt(e.headers.get("content-length"),10)/1024]:(console.log("Can't get image size for ".concat(t," likely due to a CORS error.")),[2,-1])}}))}))},t.prototype.fetchFileDimensions=function(t){return r(this,void 0,void 0,(function(){var e;return o(this,(function(n){return(e=new Image).src=t,[2,new Promise((function(t,n){e.onload=function(){t({width:Math.round(e.width),height:Math.round(e.height)})},e.onerror=function(){n("Unable to load image.")}}))]}))}))},t.prototype.isImageTiny=function(){return r(this,void 0,void 0,(function(){var t,e;return o(this,(function(n){switch(n.label){case 0:return n.trys.push([0,2,,3]),t=65,[4,this.fetchFileDimensions(this.getURL())];case 1:return[2,(e=n.sent()).width<t||e.height<t];case 2:return n.sent(),[2,!1];case 3:return[2]}}))}))},t}()},705:(t,e,n)=>{n.d(e,{RK:()=>c});var r=n(38),o=function(t,e,n,r){return new(n||(n=Promise))((function(o,i){function u(t){try{c(r.next(t))}catch(t){i(t)}}function a(t){try{c(r.throw(t))}catch(t){i(t)}}function c(t){var e;t.done?o(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(u,a)}c((r=r.apply(t,e||[])).next())}))},i=function(t,e){var n,r,o,i,u={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(a){return function(c){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;i&&(i=0,a[0]&&(u=0)),u;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return u.label++,{value:a[1],done:!1};case 5:u.label++,r=a[1],a=[0];continue;case 7:a=u.ops.pop(),u.trys.pop();continue;default:if(!(o=u.trys,(o=o.length>0&&o[o.length-1])||6!==a[0]&&2!==a[0])){u=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]<o[3])){u.label=a[1];break}if(6===a[0]&&u.label<o[1]){u.label=o[1],o=a;break}if(o&&u.label<o[2]){u.label=o[2],u.ops.push(a);break}o[2]&&u.ops.pop(),u.trys.pop();continue}a=e.call(t,u)}catch(t){a=[6,t],r=0}finally{n=o=0}if(5&a[0])throw a[1];return{value:a[0]?a[1]:void 0,done:!0}}([a,c])}}};function u(t){return l(t.currentSrc)?t.currentSrc:l(t.src)?t.src:null}function a(t){var e=getComputedStyle(t).backgroundImage.match(/url\(\s*(['"])(.*?)\1\s*\)/i),n=window.location.href.split("#")[0];return e&&e[2]&&e[2]!==n&&l(e[2])?e[2]:null}function c(t,e){return void 0===e&&(e=null),o(this,void 0,void 0,(function(){var n;return i(this,(function(o){return n=function(t){return t.filter((function(t){return t instanceof HTMLImageElement||t instanceof HTMLElement&&"none"!==getComputedStyle(t).backgroundImage}))}(t),[2,n.map((function(t){return t instanceof HTMLImageElement?new r.z(t,u,e):t instanceof HTMLElement&&a(t)?new r.z(t,a):null})).filter((function(t){return null!==t}))]}))}))}function l(t){if(t.startsWith("/"))return!t.split("?")[0].endsWith(".svg");try{var e=new URL(t);return"http:"===e.protocol||"https:"===e.protocol}catch(t){return!1}}}},e={};function n(r){var o=e[r];if(void 0!==o)return o.exports;var i=e[r]={exports:{}};return t[r](i,i.exports,n),i.exports}n.d=(t,e)=>{for(var r in e)n.o(e,r)&&!n.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:e[r]})},n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),n.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var r={};return(()=>{n.r(r),n.d(r,{MeasurableImage:()=>t.z,getMeasurableImages:()=>e.RK});var t=n(38),e=n(705)})(),r})()));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/jetpack-image-guide",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Go through the dom to analyze image size on screen vs actual file size.",
|
|
5
5
|
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/image-guide/#readme",
|
|
6
6
|
"type": "module",
|
package/src/MeasurableImage.ts
CHANGED
|
@@ -2,7 +2,7 @@ export type SourceCallbackFn = ( node: HTMLElement ) => string | null;
|
|
|
2
2
|
export type Dimensions = { width: number; height: number };
|
|
3
3
|
export type Weight = { weight: number };
|
|
4
4
|
|
|
5
|
-
type FetchFn = ( input: URL | RequestInfo, init?: RequestInit ) => Promise< Response >;
|
|
5
|
+
export type FetchFn = ( input: URL | RequestInfo, init?: RequestInit ) => Promise< Response >;
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* A class that represents a DOM Element that
|
|
@@ -13,7 +13,7 @@ export class MeasurableImage {
|
|
|
13
13
|
readonly node: HTMLElement | HTMLImageElement;
|
|
14
14
|
private getURLCallback: SourceCallbackFn;
|
|
15
15
|
|
|
16
|
-
public fetch
|
|
16
|
+
public fetch: FetchFn;
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Constructor.
|
|
@@ -25,11 +25,11 @@ export class MeasurableImage {
|
|
|
25
25
|
constructor(
|
|
26
26
|
node: HTMLElement | HTMLImageElement,
|
|
27
27
|
getURL: SourceCallbackFn,
|
|
28
|
-
fetchFn: FetchFn =
|
|
28
|
+
fetchFn: FetchFn | null = null
|
|
29
29
|
) {
|
|
30
30
|
this.node = node;
|
|
31
31
|
this.getURLCallback = getURL;
|
|
32
|
-
this.fetch = fetchFn;
|
|
32
|
+
this.fetch = fetchFn ?? window.fetch.bind( window );
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
public getURL() {
|
|
@@ -94,7 +94,7 @@ export class MeasurableImage {
|
|
|
94
94
|
*/
|
|
95
95
|
private async fetchFileWeight( url: string ) {
|
|
96
96
|
const response = await this.fetch( url );
|
|
97
|
-
if ( ! response.
|
|
97
|
+
if ( ! response.ok ) {
|
|
98
98
|
// eslint-disable-next-line no-console
|
|
99
99
|
console.log( `Can't get image size for ${ url } likely due to a CORS error.` );
|
|
100
100
|
return -1;
|
|
@@ -112,13 +112,29 @@ export class MeasurableImage {
|
|
|
112
112
|
private async fetchFileDimensions( url: string ) {
|
|
113
113
|
const img = new Image();
|
|
114
114
|
img.src = url;
|
|
115
|
-
return new Promise< { width: number; height: number } >( resolve => {
|
|
115
|
+
return new Promise< { width: number; height: number } >( ( resolve, reject ) => {
|
|
116
116
|
img.onload = () => {
|
|
117
117
|
resolve( { width: Math.round( img.width ), height: Math.round( img.height ) } );
|
|
118
118
|
};
|
|
119
119
|
img.onerror = () => {
|
|
120
|
-
|
|
120
|
+
reject( 'Unable to load image.' );
|
|
121
121
|
};
|
|
122
122
|
} );
|
|
123
123
|
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Checks if the image is too small and should be ignored. Will return false on images
|
|
127
|
+
* that don't load at all - we can't establish they're tiny!
|
|
128
|
+
*
|
|
129
|
+
* @returns {boolean} - if the image is smaller than 65 pixels width and height return true
|
|
130
|
+
*/
|
|
131
|
+
public async isImageTiny(): Promise< boolean > {
|
|
132
|
+
try {
|
|
133
|
+
const minSize = 65;
|
|
134
|
+
const dimensions = await this.fetchFileDimensions( this.getURL() );
|
|
135
|
+
return dimensions.width < minSize || dimensions.height < minSize;
|
|
136
|
+
} catch ( err ) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
124
140
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MeasurableImage } from './MeasurableImage';
|
|
1
|
+
import { FetchFn, MeasurableImage } from './MeasurableImage';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Get elements that either are image tags or have a background image.
|
|
@@ -63,27 +63,30 @@ export function backgroundImageSource( node: HTMLElement ) {
|
|
|
63
63
|
* @param {(input: string, init?: Array) => Promise<Response>} fetchFn - A function that fetches a URL and returns a Promise.
|
|
64
64
|
* @returns {MeasurableImage[]} - A list of MeasurableImage objects.
|
|
65
65
|
*/
|
|
66
|
-
export function getMeasurableImages(
|
|
66
|
+
export async function getMeasurableImages(
|
|
67
|
+
domNodes: Element[],
|
|
68
|
+
fetchFn: FetchFn | null = null
|
|
69
|
+
): Promise< MeasurableImage[] > {
|
|
67
70
|
const nodes = findMeasurableElements( domNodes );
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return new MeasurableImage( node, backgroundImageSource );
|
|
71
|
+
const images = nodes.map( node => {
|
|
72
|
+
if ( node instanceof HTMLImageElement ) {
|
|
73
|
+
return new MeasurableImage( node, imageTagSource, fetchFn );
|
|
74
|
+
} else if ( node instanceof HTMLElement ) {
|
|
75
|
+
if ( ! backgroundImageSource( node ) ) {
|
|
76
|
+
/**
|
|
77
|
+
* Background elements that have no valid URL
|
|
78
|
+
* shouldn't be measured.
|
|
79
|
+
*/
|
|
80
|
+
return null;
|
|
82
81
|
}
|
|
83
82
|
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
|
|
83
|
+
return new MeasurableImage( node, backgroundImageSource );
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return null;
|
|
87
|
+
} );
|
|
88
|
+
|
|
89
|
+
return images.filter( i => i !== null );
|
|
87
90
|
}
|
|
88
91
|
|
|
89
92
|
/**
|