@elementor/editor-audits 4.4.0-1107 → 4.4.0-1109

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementor/editor-audits",
3
- "version": "4.4.0-1107",
3
+ "version": "4.4.0-1109",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -39,21 +39,21 @@
39
39
  "dev": "tsup --config=../../tsup.dev.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@elementor/editor": "4.4.0-1107",
43
- "@elementor/editor-mcp": "4.4.0-1107",
44
- "@elementor/editor-app-bar": "4.4.0-1107",
45
- "@elementor/editor-elements": "4.4.0-1107",
46
- "@elementor/editor-floating-panels": "4.4.0-1107",
47
- "@elementor/editor-notifications": "4.4.0-1107",
48
- "@elementor/editor-props": "4.4.0-1107",
49
- "@elementor/editor-styles": "4.4.0-1107",
50
- "@elementor/editor-v1-adapters": "4.4.0-1107",
51
- "@elementor/events": "4.4.0-1107",
52
- "@elementor/http-client": "4.4.0-1107",
42
+ "@elementor/editor": "4.4.0-1109",
43
+ "@elementor/editor-mcp": "4.4.0-1109",
44
+ "@elementor/editor-app-bar": "4.4.0-1109",
45
+ "@elementor/editor-elements": "4.4.0-1109",
46
+ "@elementor/editor-floating-panels": "4.4.0-1109",
47
+ "@elementor/editor-notifications": "4.4.0-1109",
48
+ "@elementor/editor-props": "4.4.0-1109",
49
+ "@elementor/editor-styles": "4.4.0-1109",
50
+ "@elementor/editor-v1-adapters": "4.4.0-1109",
51
+ "@elementor/events": "4.4.0-1109",
52
+ "@elementor/http-client": "4.4.0-1109",
53
53
  "@elementor/icons": "~1.77.0",
54
- "@elementor/locations": "4.4.0-1107",
55
- "@elementor/session": "4.4.0-1107",
56
- "@elementor/store": "4.4.0-1107",
54
+ "@elementor/locations": "4.4.0-1109",
55
+ "@elementor/session": "4.4.0-1109",
56
+ "@elementor/store": "4.4.0-1109",
57
57
  "@elementor/ui": "1.37.5"
58
58
  },
59
59
  "peerDependencies": {
@@ -0,0 +1,67 @@
1
+ import { __ } from '@wordpress/i18n';
2
+
3
+ import { type Audit, type AuditViolation, type ElementSnapshotNode } from '../types';
4
+ import { walkAtomicBackgroundImageSources } from '../utils/atomic-background-image-sources';
5
+ import { walkAtomicImageSources } from '../utils/atomic-image-sources';
6
+ import { walkBackgroundImageSources } from '../utils/background-image-sources';
7
+ import { type ImageLikeMedia, walkImageLikeSources } from '../utils/image-like-sources';
8
+ import { buildImageSizeKey } from '../utils/image-size-key';
9
+
10
+ const LEGACY_RASTER_MIME_TYPES = new Set( [ 'image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/tiff' ] );
11
+
12
+ export const audit: Audit = {
13
+ id: 'audits/images-inefficient-format',
14
+ title: __( 'Inefficient image format', 'elementor' ),
15
+ description: __( "Image isn't served in a modern, efficient format like WebP or AVIF.", 'elementor' ),
16
+ fixHint: __( 'Convert the image to a modern format like WebP or AVIF, or enable image optimization.', 'elementor' ),
17
+ categories: [ 'performance' ],
18
+ severity: 'info',
19
+ weight: 1,
20
+ evaluate: ( ctx ) => {
21
+ const offendingElementIds = new Set< string >();
22
+ let hasAnyImage = false;
23
+ let inefficientImageFormatCount = 0;
24
+
25
+ const evaluateSource = ( node: ElementSnapshotNode, media: ImageLikeMedia ) => {
26
+ if ( media.id || media.url ) {
27
+ hasAnyImage = true;
28
+ }
29
+
30
+ const id = media.id;
31
+
32
+ if ( ! id ) {
33
+ return;
34
+ }
35
+
36
+ const mime = ctx.pageContext.image_sizes[ buildImageSizeKey( { id, size: media.size } ) ]?.mime;
37
+
38
+ if ( ! mime || ! LEGACY_RASTER_MIME_TYPES.has( mime ) ) {
39
+ return;
40
+ }
41
+
42
+ inefficientImageFormatCount++;
43
+ offendingElementIds.add( node.id );
44
+ };
45
+
46
+ walkImageLikeSources( ctx.elements.tree, ( { node, media } ) => evaluateSource( node, media ) );
47
+ walkBackgroundImageSources( ctx.elements.tree, ( { node, media } ) => evaluateSource( node, media ) );
48
+ walkAtomicImageSources( ctx.elements.tree, ( { node, media } ) => evaluateSource( node, media ) );
49
+ walkAtomicBackgroundImageSources( ctx.elements.tree, ( { node, media } ) => evaluateSource( node, media ) );
50
+
51
+ if ( ! hasAnyImage ) {
52
+ return { status: 'skipped', reason: __( 'No images', 'elementor' ) };
53
+ }
54
+
55
+ const violations: AuditViolation[] = Array.from( offendingElementIds ).map( ( elementId ) => ( {
56
+ auditId: audit.id,
57
+ elementId,
58
+ targetHint: 'element-settings' as const,
59
+ label: __( "Image isn't served in a modern format like WebP or AVIF.", 'elementor' ),
60
+ externalUrl: ctx.pageContext.image_optimization_plugin_url,
61
+ } ) );
62
+
63
+ return violations.length === 0
64
+ ? { status: 'pass' }
65
+ : { status: 'fail', violations, metadata: { inefficientImageFormatCount } };
66
+ },
67
+ };
@@ -9,6 +9,7 @@ import * as googleConsentMode from './audits/google-consent-mode';
9
9
  import * as headingStructure from './audits/heading-structure';
10
10
  import * as hiddenElements from './audits/hidden-elements';
11
11
  import * as imagesAltText from './audits/images-alt-text';
12
+ import * as imagesInefficientFormat from './audits/images-inefficient-format';
12
13
  import * as imagesTooLarge from './audits/images-too-large';
13
14
  import * as nestedBoxedContainers from './audits/nested-boxed-containers';
14
15
  import * as pageExcerpt from './audits/page-excerpt';
@@ -45,6 +46,7 @@ const AUDITS: Audit[] = [
45
46
  headingStructure.audit,
46
47
  imagesAltText.audit,
47
48
  imagesTooLarge.audit,
49
+ imagesInefficientFormat.audit,
48
50
 
49
51
  robotsNoindex.audit,
50
52
 
@@ -80,4 +80,27 @@ export const PROMOTIONS: PromotionConfig[] = [
80
80
  },
81
81
  getCtaUrl: firstFailExternalUrl,
82
82
  },
83
+ {
84
+ auditId: 'audits/images-inefficient-format',
85
+ icon: ShieldHalfFilledIcon,
86
+ ctaLabel: __( 'Optimize all', 'elementor' ),
87
+ formatSubtitle: ( run ) => {
88
+ if ( run.result.status !== 'fail' ) {
89
+ return null;
90
+ }
91
+
92
+ const count = run.result.metadata?.inefficientImageFormatCount ?? 0;
93
+
94
+ if ( count === 0 ) {
95
+ return null;
96
+ }
97
+
98
+ return sprintf(
99
+ /* translators: %d: number of images served in an inefficient format. */
100
+ _n( '%d image', '%d images', count, 'elementor' ),
101
+ count
102
+ );
103
+ },
104
+ getCtaUrl: firstFailExternalUrl,
105
+ },
83
106
  ];
package/src/types.ts CHANGED
@@ -23,6 +23,7 @@ export type AuditRun = {
23
23
  export type AuditFailMetadata = {
24
24
  missingAltImageCount?: number;
25
25
  oversizedImageCount?: number;
26
+ inefficientImageFormatCount?: number;
26
27
  };
27
28
 
28
29
  export type AuditResult =