@elementor/editor-audits 4.3.3 → 4.3.4

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/dist/index.js CHANGED
@@ -52,18 +52,18 @@ var import_editor_floating_panels2 = require("@elementor/editor-floating-panels"
52
52
 
53
53
  // src/components/audit-panel.tsx
54
54
  var React25 = __toESM(require("react"));
55
- var import_editor_elements5 = require("@elementor/editor-elements");
55
+ var import_editor_elements6 = require("@elementor/editor-elements");
56
56
  var import_editor_floating_panels = require("@elementor/editor-floating-panels");
57
57
  var import_ui24 = require("@elementor/ui");
58
58
  var import_i18n23 = require("@wordpress/i18n");
59
59
 
60
60
  // src/hooks/use-audit-report.ts
61
61
  var import_react = require("react");
62
- var import_editor_elements2 = require("@elementor/editor-elements");
62
+ var import_editor_elements3 = require("@elementor/editor-elements");
63
63
  var import_store2 = require("@elementor/store");
64
64
 
65
65
  // src/runner.ts
66
- var import_editor_elements = require("@elementor/editor-elements");
66
+ var import_editor_elements2 = require("@elementor/editor-elements");
67
67
 
68
68
  // src/api/page-context-client.ts
69
69
  var import_http_client = require("@elementor/http-client");
@@ -115,17 +115,17 @@ async function requestFreshNonce() {
115
115
  }
116
116
 
117
117
  // src/api/page-context-client.ts
118
- async function fetchPageContext(documentId, attachmentIds) {
119
- return requestPageContext(documentId, attachmentIds, true);
118
+ async function fetchPageContext(documentId, imageSizeRequests) {
119
+ return requestPageContext(documentId, imageSizeRequests, true);
120
120
  }
121
- async function requestPageContext(documentId, attachmentIds, allowNonceRetry) {
121
+ async function requestPageContext(documentId, imageSizeRequests, allowNonceRetry) {
122
122
  const { restNamespace, nonce } = getWindowConfig();
123
123
  const url = `${restNamespace}/audits/page-context`;
124
124
  try {
125
125
  const response = await (0, import_http_client.httpService)().get(url, {
126
126
  params: {
127
127
  document_id: documentId,
128
- attachment_ids: attachmentIds
128
+ image_size_requests: imageSizeRequests
129
129
  },
130
130
  headers: { "X-WP-Nonce": nonce }
131
131
  });
@@ -135,14 +135,14 @@ async function requestPageContext(documentId, attachmentIds, allowNonceRetry) {
135
135
  throw error;
136
136
  }
137
137
  await refreshAuditsNonce();
138
- return requestPageContext(documentId, attachmentIds, false);
138
+ return requestPageContext(documentId, imageSizeRequests, false);
139
139
  }
140
140
  }
141
141
 
142
142
  // src/registry.ts
143
143
  var registry = /* @__PURE__ */ new Map();
144
- function registerAudit(audit23) {
145
- registry.set(audit23.id, audit23);
144
+ function registerAudit(audit26) {
145
+ registry.set(audit26.id, audit26);
146
146
  }
147
147
  function getRegisteredAudits() {
148
148
  return Array.from(registry.values());
@@ -179,8 +179,8 @@ var CATEGORY_LABELS = {
179
179
  var import_i18n2 = require("@wordpress/i18n");
180
180
 
181
181
  // src/utils/is-scored-audit.ts
182
- function isScoredAudit(audit23) {
183
- return audit23.weight > 0;
182
+ function isScoredAudit(audit26) {
183
+ return audit26.weight > 0;
184
184
  }
185
185
 
186
186
  // src/utils/sort-failed-audits.ts
@@ -236,8 +236,8 @@ function auditStatusDisplayCounts(report) {
236
236
  let pass = 0;
237
237
  let skipped = 0;
238
238
  let totalViolations = 0;
239
- for (const { audit: audit23, result } of report.auditResults) {
240
- if (!isScoredAudit(audit23)) {
239
+ for (const { audit: audit26, result } of report.auditResults) {
240
+ if (!isScoredAudit(audit26)) {
241
241
  continue;
242
242
  }
243
243
  switch (result.status) {
@@ -287,16 +287,16 @@ function computeReport(documentId, results) {
287
287
  const accumulators = Object.fromEntries(
288
288
  ALL_CATEGORIES.map((c) => [c, { totalWeight: 0, passedWeight: 0, total: 0, failed: 0 }])
289
289
  );
290
- for (const { audit: audit23, result } of results) {
290
+ for (const { audit: audit26, result } of results) {
291
291
  if (result.status === "skipped") {
292
292
  continue;
293
293
  }
294
- for (const category of audit23.categories) {
294
+ for (const category of audit26.categories) {
295
295
  const acc = accumulators[category];
296
296
  acc.total++;
297
- acc.totalWeight += audit23.weight;
297
+ acc.totalWeight += audit26.weight;
298
298
  if (result.status === "pass") {
299
- acc.passedWeight += audit23.weight;
299
+ acc.passedWeight += audit26.weight;
300
300
  } else {
301
301
  acc.failed++;
302
302
  }
@@ -320,6 +320,34 @@ function computeReport(documentId, results) {
320
320
  };
321
321
  }
322
322
 
323
+ // src/utils/fetch-rendered-html.ts
324
+ var FETCH_TIMEOUT_MS = 1e4;
325
+ async function fetchRenderedHtml(url) {
326
+ if (!url) {
327
+ return null;
328
+ }
329
+ const timeoutController = new AbortController();
330
+ const timeoutId = setTimeout(() => timeoutController.abort(), FETCH_TIMEOUT_MS);
331
+ try {
332
+ const response = await fetch(url, { credentials: "omit", signal: timeoutController.signal });
333
+ if (!response.ok) {
334
+ return null;
335
+ }
336
+ return await response.text();
337
+ } catch {
338
+ return null;
339
+ } finally {
340
+ clearTimeout(timeoutId);
341
+ }
342
+ }
343
+
344
+ // src/utils/atomic-background-image-sources.ts
345
+ var import_editor_elements = require("@elementor/editor-elements");
346
+ var import_editor_props2 = require("@elementor/editor-props");
347
+
348
+ // src/utils/atomic-image-sources.ts
349
+ var import_editor_props = require("@elementor/editor-props");
350
+
323
351
  // src/utils/walk.ts
324
352
  function walkElements(tree, visit) {
325
353
  const parents = [];
@@ -334,6 +362,89 @@ function recurse(nodes, parents, visit) {
334
362
  }
335
363
  }
336
364
 
365
+ // src/utils/atomic-image-sources.ts
366
+ var ATOMIC_IMAGE_PROP_KEYS = ["image", "poster"];
367
+ function walkAtomicImageSources(tree, visit) {
368
+ walkElements(tree, (node) => {
369
+ if (node.elType !== "widget") {
370
+ return;
371
+ }
372
+ for (const key of ATOMIC_IMAGE_PROP_KEYS) {
373
+ const media = extractAtomicImageMedia(node.settings[key]);
374
+ if (media) {
375
+ visit({ node, media });
376
+ }
377
+ }
378
+ });
379
+ }
380
+ function extractAtomicImageMedia(imageProp) {
381
+ const image = import_editor_props.imagePropTypeUtil.extract(imageProp);
382
+ if (!image) {
383
+ return null;
384
+ }
385
+ const src = import_editor_props.imageSrcPropTypeUtil.extract(image.src);
386
+ if (!src) {
387
+ return null;
388
+ }
389
+ const id = import_editor_props.imageAttachmentIdPropType.extract(src.id) ?? void 0;
390
+ const url = import_editor_props.urlPropTypeUtil.extract(src.url) ?? void 0;
391
+ if (!id && !url) {
392
+ return null;
393
+ }
394
+ const size = import_editor_props.stringPropTypeUtil.extract(image.size) ?? void 0;
395
+ return { id, url, size };
396
+ }
397
+
398
+ // src/utils/atomic-background-image-sources.ts
399
+ function walkAtomicBackgroundImageSources(tree, visit) {
400
+ walkElements(tree, (node) => {
401
+ const styles = (0, import_editor_elements.getElementStyles)(node.id);
402
+ if (!styles) {
403
+ return;
404
+ }
405
+ for (const media of extractBackgroundImageMedia(styles)) {
406
+ visit({ node, media });
407
+ }
408
+ });
409
+ }
410
+ function extractBackgroundImageMedia(styles) {
411
+ const sources = [];
412
+ for (const style of Object.values(styles)) {
413
+ for (const variant of style.variants) {
414
+ const background = import_editor_props2.backgroundPropTypeUtil.extract(variant.props.background);
415
+ const overlays = import_editor_props2.backgroundOverlayPropTypeUtil.extract(background?.["background-overlay"]) ?? [];
416
+ for (const overlay of overlays) {
417
+ const imageOverlay = import_editor_props2.backgroundImageOverlayPropTypeUtil.extract(overlay);
418
+ const media = extractAtomicImageMedia(imageOverlay?.image);
419
+ if (media) {
420
+ sources.push(media);
421
+ }
422
+ }
423
+ }
424
+ }
425
+ return sources;
426
+ }
427
+
428
+ // src/utils/background-image-sources.ts
429
+ var BACKGROUND_IMAGE_SETTING_KEYS = [
430
+ "background_image",
431
+ "background_hover_image",
432
+ "background_overlay_image",
433
+ "background_overlay_hover_image",
434
+ "_background_image",
435
+ "_background_hover_image"
436
+ ];
437
+ function walkBackgroundImageSources(tree, visit) {
438
+ walkElements(tree, (node) => {
439
+ for (const key of BACKGROUND_IMAGE_SETTING_KEYS) {
440
+ const media = node.settings[key];
441
+ if (media?.id || media?.url) {
442
+ visit({ node, media });
443
+ }
444
+ }
445
+ });
446
+ }
447
+
337
448
  // src/utils/image-like-sources.ts
338
449
  var IMAGE_LIKE_WIDGETS = /* @__PURE__ */ new Set(["image", "image-box", "image-carousel", "gallery", "image-gallery"]);
339
450
  function hasPageImages(tree) {
@@ -362,28 +473,38 @@ function collectMediaFromNode(node) {
362
473
  const sources = [];
363
474
  const image = node.settings.image;
364
475
  if (image?.id || image?.url) {
365
- sources.push(image);
476
+ sources.push({ ...image, size: node.settings.image_size });
366
477
  }
367
478
  const gallery = node.settings.carousel ?? node.settings.gallery ?? node.settings.wp_gallery;
368
479
  if (Array.isArray(gallery)) {
480
+ const gallerySize = node.settings.thumbnail_size;
369
481
  for (const item of gallery) {
370
482
  if (item?.id || item?.url) {
371
- sources.push(item);
483
+ sources.push({ ...item, size: gallerySize });
372
484
  }
373
485
  }
374
486
  }
375
487
  return sources;
376
488
  }
377
489
 
490
+ // src/utils/image-size-key.ts
491
+ function buildImageSizeKey({ id, size }) {
492
+ return `${id}:${size || "full"}`;
493
+ }
494
+
378
495
  // src/utils/page-attachments.ts
379
- function extractAttachmentIds(tree) {
380
- const ids = /* @__PURE__ */ new Set();
381
- walkImageLikeSources(tree, ({ media }) => {
496
+ function extractImageSizeRequests(tree) {
497
+ const keys = /* @__PURE__ */ new Set();
498
+ const collect = (media) => {
382
499
  if (media.id) {
383
- ids.add(media.id);
500
+ keys.add(buildImageSizeKey({ id: media.id, size: media.size }));
384
501
  }
385
- });
386
- return Array.from(ids).sort((a, b) => a - b);
502
+ };
503
+ walkImageLikeSources(tree, ({ media }) => collect(media));
504
+ walkBackgroundImageSources(tree, ({ media }) => collect(media));
505
+ walkAtomicImageSources(tree, ({ media }) => collect(media));
506
+ walkAtomicBackgroundImageSources(tree, ({ media }) => collect(media));
507
+ return Array.from(keys).sort();
387
508
  }
388
509
 
389
510
  // src/utils/read-kit-snapshot.ts
@@ -477,18 +598,21 @@ function toSnapshot(element) {
477
598
 
478
599
  // src/runner.ts
479
600
  async function runPageAudit(documentId) {
480
- const tree = buildSnapshotTree((0, import_editor_elements.getElements)());
481
- const attachmentIds = extractAttachmentIds(tree);
482
- const pageContext = await fetchPageContext(documentId, attachmentIds);
601
+ const tree = buildSnapshotTree((0, import_editor_elements2.getElements)());
602
+ const imageSizeRequests = extractImageSizeRequests(tree);
603
+ const pageContext = await fetchPageContext(documentId, imageSizeRequests);
483
604
  const elements = { documentId, tree };
484
- const kit = await readKitSnapshot(pageContext.kit_id);
485
- const ctx = { documentId, elements, pageContext, kit };
605
+ const [kit, renderedHtml] = await Promise.all([
606
+ readKitSnapshot(pageContext.kit_id),
607
+ fetchRenderedHtml(pageContext.frontend_url)
608
+ ]);
609
+ const ctx = { documentId, elements, pageContext, kit, renderedHtml };
486
610
  const registered = getRegisteredAudits();
487
611
  const auditResults = await Promise.all(
488
- registered.map(async (audit23) => {
489
- const { evaluate: _evaluate, ...meta } = audit23;
612
+ registered.map(async (audit26) => {
613
+ const { evaluate: _evaluate, ...meta } = audit26;
490
614
  try {
491
- const result = await audit23.evaluate(ctx);
615
+ const result = await audit26.evaluate(ctx);
492
616
  return { audit: meta, result };
493
617
  } catch (error) {
494
618
  const reason = error instanceof Error ? error.message : "unknown-error";
@@ -563,7 +687,7 @@ function useAuditReport() {
563
687
  const report = (0, import_store2.__useSelector)((state) => selectReport(state));
564
688
  const error = (0, import_store2.__useSelector)((state) => selectError(state));
565
689
  const dispatch = (0, import_store2.__useDispatch)();
566
- const documentId = (0, import_editor_elements2.getCurrentDocumentId)() ?? 0;
690
+ const documentId = (0, import_editor_elements3.getCurrentDocumentId)() ?? 0;
567
691
  (0, import_react.useEffect)(() => {
568
692
  if (!documentId || report?.documentId === documentId) {
569
693
  return;
@@ -820,20 +944,20 @@ function SubpageHeader({ title, onBack, backLabel, icon }) {
820
944
  // src/components/violation-row.tsx
821
945
  var React11 = __toESM(require("react"));
822
946
  var import_react4 = require("react");
823
- var import_editor_elements4 = require("@elementor/editor-elements");
947
+ var import_editor_elements5 = require("@elementor/editor-elements");
824
948
  var import_icons7 = require("@elementor/icons");
825
949
  var import_ui10 = require("@elementor/ui");
826
950
  var import_i18n11 = require("@wordpress/i18n");
827
951
 
828
952
  // src/hooks/focus-violation.ts
829
- var import_editor_elements3 = require("@elementor/editor-elements");
953
+ var import_editor_elements4 = require("@elementor/editor-elements");
830
954
  var import_editor_v1_adapters2 = require("@elementor/editor-v1-adapters");
831
955
  var SCROLL_TO_VIEW_DELAY_MS = 200;
832
956
  function focusViolation(violation) {
833
957
  if (violation.elementId) {
834
- const container = (0, import_editor_elements3.getContainer)(violation.elementId);
958
+ const container = (0, import_editor_elements4.getContainer)(violation.elementId);
835
959
  if (container) {
836
- (0, import_editor_elements3.selectElement)(violation.elementId);
960
+ (0, import_editor_elements4.selectElement)(violation.elementId);
837
961
  const domElement = container.view?.getDomElement?.();
838
962
  if (domElement) {
839
963
  window.elementor?.helpers?.scrollToView?.(domElement, SCROLL_TO_VIEW_DELAY_MS);
@@ -938,13 +1062,13 @@ function SeverityIcon({ severity }) {
938
1062
  // src/components/violation-cta-button.tsx
939
1063
  var React9 = __toESM(require("react"));
940
1064
  var import_ui8 = require("@elementor/ui");
941
- function ViolationCtaButton({ ctaLabel, externalUrl }) {
1065
+ function ViolationCtaButton({ ctaLabel, externalUrl, variant = "outlined" }) {
942
1066
  const handleClick = (event) => {
943
1067
  event.stopPropagation();
944
1068
  event.preventDefault();
945
1069
  window.open(externalUrl, "_blank", "noopener");
946
1070
  };
947
- return /* @__PURE__ */ React9.createElement(import_ui8.Button, { variant: "outlined", color: "secondary", size: "small", onClick: handleClick }, ctaLabel);
1071
+ return /* @__PURE__ */ React9.createElement(import_ui8.Button, { variant, color: "secondary", size: "small", onClick: handleClick }, ctaLabel);
948
1072
  }
949
1073
 
950
1074
  // src/components/violation-icons.tsx
@@ -976,13 +1100,13 @@ function ViolationIcon({ violation, widgetIcon }) {
976
1100
  function SkipReasonTooltip({ reason }) {
977
1101
  return /* @__PURE__ */ React11.createElement(import_ui10.Tooltip, { title: reason, placement: "top" }, /* @__PURE__ */ React11.createElement(import_ui10.Box, { "aria-label": reason, component: "span", sx: { display: "inline-flex", alignItems: "center" } }, /* @__PURE__ */ React11.createElement(import_icons7.HelpIcon, { fontSize: "small", color: "action" })));
978
1102
  }
979
- function StatusIndicator({ audit: audit23, violations }) {
1103
+ function StatusIndicator({ audit: audit26, violations }) {
980
1104
  if (violations) {
981
- return /* @__PURE__ */ React11.createElement(React11.Fragment, null, isScoredAudit(audit23) && /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", color: "text.secondary", fontWeight: "bold" }, violations.length), /* @__PURE__ */ React11.createElement(SeverityIcon, { severity: audit23.severity }));
1105
+ return /* @__PURE__ */ React11.createElement(React11.Fragment, null, isScoredAudit(audit26) && /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", color: "text.secondary", fontWeight: "bold" }, violations.length), /* @__PURE__ */ React11.createElement(SeverityIcon, { severity: audit26.severity }));
982
1106
  }
983
1107
  return /* @__PURE__ */ React11.createElement(import_icons7.CheckIcon, { fontSize: "small", color: "success" });
984
1108
  }
985
- function ViolationRow({ audit: audit23, skipReason, violations }) {
1109
+ function ViolationRow({ audit: audit26, skipReason, violations }) {
986
1110
  const [expanded, setExpanded] = (0, import_react4.useState)(false);
987
1111
  const toggleExpanded = () => setExpanded((value) => !value);
988
1112
  return /* @__PURE__ */ React11.createElement(import_ui10.Box, { sx: { borderBottom: 1, borderColor: "divider", paddingBlock: 0.5 } }, /* @__PURE__ */ React11.createElement(import_ui10.Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React11.createElement(
@@ -998,8 +1122,8 @@ function ViolationRow({ audit: audit23, skipReason, violations }) {
998
1122
  },
999
1123
  onClick: toggleExpanded
1000
1124
  },
1001
- /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "body2", sx: { flex: 1 } }, audit23.title),
1002
- !skipReason && /* @__PURE__ */ React11.createElement(StatusIndicator, { audit: audit23, violations })
1125
+ /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "body2", sx: { flex: 1 } }, audit26.title),
1126
+ !skipReason && /* @__PURE__ */ React11.createElement(StatusIndicator, { audit: audit26, violations })
1003
1127
  ), skipReason && /* @__PURE__ */ React11.createElement(SkipReasonTooltip, { reason: skipReason }), /* @__PURE__ */ React11.createElement(
1004
1128
  import_ui10.IconButton,
1005
1129
  {
@@ -1025,7 +1149,7 @@ function ViolationRow({ audit: audit23, skipReason, violations }) {
1025
1149
  icon: /* @__PURE__ */ React11.createElement(import_icons7.AlertCircleIcon, { fontSize: "small", color: "secondary", "aria-hidden": true })
1026
1150
  },
1027
1151
  /* @__PURE__ */ React11.createElement(import_ui10.AlertTitle, null, /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", component: "p", color: "text.primary", fontWeight: "bold" }, (0, import_i18n11.__)("What's the issue", "elementor"))),
1028
- /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", component: "p", color: "text.secondary" }, audit23.description)
1152
+ /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", component: "p", color: "text.secondary" }, audit26.description)
1029
1153
  ), /* @__PURE__ */ React11.createElement(
1030
1154
  import_ui10.Alert,
1031
1155
  {
@@ -1034,10 +1158,10 @@ function ViolationRow({ audit: audit23, skipReason, violations }) {
1034
1158
  icon: /* @__PURE__ */ React11.createElement(import_icons7.BulbIcon, { fontSize: "small", color: "info", "aria-hidden": true })
1035
1159
  },
1036
1160
  /* @__PURE__ */ React11.createElement(import_ui10.AlertTitle, null, /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", component: "p", color: "text.primary", fontWeight: "bold" }, (0, import_i18n11.__)("How to resolve", "elementor"))),
1037
- /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", component: "p", color: "text.secondary" }, audit23.fixHint)
1161
+ /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", component: "p", color: "text.secondary" }, audit26.fixHint)
1038
1162
  )), violations && violations.length > 0 && /* @__PURE__ */ React11.createElement(import_ui10.Box, { role: "list", sx: { paddingBlockEnd: 1, paddingInlineStart: 2 } }, violations.map((violation, idx) => {
1039
- const widgetIcon = violation.elementId ? (0, import_editor_elements4.getElementIcon)(violation.elementId) : null;
1040
- const elementTitle = violation.elementId ? (0, import_editor_elements4.getElementTitle)(violation.elementId) : null;
1163
+ const widgetIcon = violation.elementId ? (0, import_editor_elements5.getElementIcon)(violation.elementId) : null;
1164
+ const elementTitle = violation.elementId ? (0, import_editor_elements5.getElementTitle)(violation.elementId) : null;
1041
1165
  const rowLabel = elementTitle ? `${elementTitle} - ${violation.label}` : violation.label;
1042
1166
  return /* @__PURE__ */ React11.createElement(
1043
1167
  import_ui10.Box,
@@ -1070,13 +1194,20 @@ function ViolationRow({ audit: audit23, skipReason, violations }) {
1070
1194
  prompt: violation.angiePrompt ?? buildAngiePrompt(rowLabel)
1071
1195
  }
1072
1196
  ),
1073
- violation.ctaLabel && violation.externalUrl ? /* @__PURE__ */ React11.createElement(
1197
+ violation.ctaLabel && violation.externalUrl ? /* @__PURE__ */ React11.createElement(import_ui10.Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React11.createElement(
1074
1198
  ViolationCtaButton,
1075
1199
  {
1076
1200
  ctaLabel: violation.ctaLabel,
1077
1201
  externalUrl: violation.externalUrl
1078
1202
  }
1079
- ) : /* @__PURE__ */ React11.createElement(
1203
+ ), violation.secondaryCtaLabel && violation.secondaryCtaUrl && /* @__PURE__ */ React11.createElement(
1204
+ ViolationCtaButton,
1205
+ {
1206
+ ctaLabel: violation.secondaryCtaLabel,
1207
+ externalUrl: violation.secondaryCtaUrl,
1208
+ variant: "text"
1209
+ }
1210
+ )) : /* @__PURE__ */ React11.createElement(
1080
1211
  import_icons7.EyeIcon,
1081
1212
  {
1082
1213
  className: "violation-hover-icon",
@@ -1174,17 +1305,17 @@ var import_i18n14 = require("@wordpress/i18n");
1174
1305
  var ALL_SEVERITIES = ["error", "warning", "info"];
1175
1306
  function countSeverities(report, category) {
1176
1307
  const counts = { error: 0, warning: 0, info: 0 };
1177
- for (const { audit: audit23, result } of report.auditResults) {
1308
+ for (const { audit: audit26, result } of report.auditResults) {
1178
1309
  if (result.status !== "fail") {
1179
1310
  continue;
1180
1311
  }
1181
- if (category && !audit23.categories.includes(category)) {
1312
+ if (category && !audit26.categories.includes(category)) {
1182
1313
  continue;
1183
1314
  }
1184
- if (!isScoredAudit(audit23)) {
1315
+ if (!isScoredAudit(audit26)) {
1185
1316
  continue;
1186
1317
  }
1187
- counts[audit23.severity] += result.violations.length;
1318
+ counts[audit26.severity] += result.violations.length;
1188
1319
  }
1189
1320
  return counts;
1190
1321
  }
@@ -1487,6 +1618,26 @@ var PROMOTIONS = [
1487
1618
  );
1488
1619
  },
1489
1620
  getCtaUrl: firstFailExternalUrl
1621
+ },
1622
+ {
1623
+ auditId: "audits/images-inefficient-format",
1624
+ icon: import_icons10.ShieldHalfFilledIcon,
1625
+ ctaLabel: (0, import_i18n19.__)("Optimize all", "elementor"),
1626
+ formatSubtitle: (run) => {
1627
+ if (run.result.status !== "fail") {
1628
+ return null;
1629
+ }
1630
+ const count = run.result.metadata?.inefficientImageFormatCount ?? 0;
1631
+ if (count === 0) {
1632
+ return null;
1633
+ }
1634
+ return (0, import_i18n19.sprintf)(
1635
+ /* translators: %d: number of images served in an inefficient format. */
1636
+ (0, import_i18n19._n)("%d image", "%d images", count, "elementor"),
1637
+ count
1638
+ );
1639
+ },
1640
+ getCtaUrl: firstFailExternalUrl
1490
1641
  }
1491
1642
  ];
1492
1643
 
@@ -1768,7 +1919,7 @@ function ReportShell({ report }) {
1768
1919
  // src/components/audit-panel.tsx
1769
1920
  function AuditPanel() {
1770
1921
  const { status, report, error, run } = useAuditReport();
1771
- const currentDocumentId = (0, import_editor_elements5.getCurrentDocumentId)() ?? 0;
1922
+ const currentDocumentId = (0, import_editor_elements6.getCurrentDocumentId)() ?? 0;
1772
1923
  const onRun = () => run(currentDocumentId);
1773
1924
  const lastScanLabel = report ? new Date(report.runAt).toLocaleTimeString() : null;
1774
1925
  return /* @__PURE__ */ React25.createElement(React25.Fragment, null, /* @__PURE__ */ React25.createElement(
@@ -1868,16 +2019,93 @@ var audit = {
1868
2019
  }
1869
2020
  };
1870
2021
 
1871
- // src/audits/cookie-policy.ts
2022
+ // src/audits/accessibility-statement.ts
1872
2023
  var import_i18n26 = require("@wordpress/i18n");
1873
2024
  var audit2 = {
1874
- id: "audits/cookie-policy",
1875
- title: (0, import_i18n26.__)("Cookie policy", "elementor"),
2025
+ id: "audits/accessibility-statement",
2026
+ title: (0, import_i18n26.__)("Create an accessibility statement", "elementor"),
1876
2027
  description: (0, import_i18n26.__)(
2028
+ "An accessibility statement showcases your efforts to create an inclusive online space, highlighting helpful features and a commitment to accessibility.",
2029
+ "elementor"
2030
+ ),
2031
+ fixHint: (0, import_i18n26.__)("Use Ally to create and publish an accessibility statement for your site.", "elementor"),
2032
+ categories: ["accessibility"],
2033
+ severity: "info",
2034
+ weight: 1,
2035
+ evaluate: (ctx) => {
2036
+ const isReady = ctx.pageContext.ally_plugin_active;
2037
+ if (isReady && ctx.pageContext.ally_accessibility_statement_created) {
2038
+ return { status: "pass" };
2039
+ }
2040
+ return {
2041
+ status: "fail",
2042
+ violations: [
2043
+ {
2044
+ auditId: audit2.id,
2045
+ label: (0, import_i18n26.__)("No accessibility statement has been created for this site.", "elementor"),
2046
+ externalUrl: isReady ? ctx.pageContext.ally_accessibility_statement_url : ctx.pageContext.ally_plugin_url,
2047
+ ctaLabel: isReady ? (0, import_i18n26.__)("Create", "elementor") : void 0,
2048
+ secondaryCtaLabel: (0, import_i18n26.__)("Learn more", "elementor"),
2049
+ secondaryCtaUrl: "https://go.elementor.com/acc-plg-learn-more"
2050
+ }
2051
+ ]
2052
+ };
2053
+ }
2054
+ };
2055
+
2056
+ // src/audits/accessibility-widget.ts
2057
+ var import_i18n27 = require("@wordpress/i18n");
2058
+
2059
+ // src/utils/scan-accessibility-widgets.ts
2060
+ var ACCESSIBILITY_WIDGET_SCRIPT_SOURCES = ["cdn.elementor.com/a11y/widget.js"];
2061
+ function hasAccessibilityWidgetScript(html) {
2062
+ return ACCESSIBILITY_WIDGET_SCRIPT_SOURCES.some((src) => html.includes(src));
2063
+ }
2064
+
2065
+ // src/audits/accessibility-widget.ts
2066
+ var audit3 = {
2067
+ id: "audits/accessibility-widget",
2068
+ title: (0, import_i18n27.__)("Add an accessibility widget", "elementor"),
2069
+ description: (0, import_i18n27.__)("Make your site more inclusive by adding an accessibility widget.", "elementor"),
2070
+ fixHint: (0, import_i18n27.__)("Activate Ally\u2019s accessibility widget to give visitors on-page accessibility tools.", "elementor"),
2071
+ categories: ["accessibility"],
2072
+ severity: "info",
2073
+ weight: 1,
2074
+ evaluate: (ctx) => {
2075
+ if (!ctx.pageContext.frontend_url) {
2076
+ return { status: "skipped", reason: (0, import_i18n27.__)("Page is not published.", "elementor") };
2077
+ }
2078
+ if (!ctx.renderedHtml) {
2079
+ return { status: "skipped", reason: (0, import_i18n27.__)("Could not fetch the published page.", "elementor") };
2080
+ }
2081
+ if (hasAccessibilityWidgetScript(ctx.renderedHtml)) {
2082
+ return { status: "pass" };
2083
+ }
2084
+ const isReady = ctx.pageContext.ally_plugin_active;
2085
+ return {
2086
+ status: "fail",
2087
+ violations: [
2088
+ {
2089
+ auditId: audit3.id,
2090
+ label: (0, import_i18n27.__)("No accessibility widget is active on this site.", "elementor"),
2091
+ externalUrl: isReady ? ctx.pageContext.ally_widget_settings_url : ctx.pageContext.ally_plugin_url,
2092
+ ctaLabel: (0, import_i18n27.__)("Activate", "elementor")
2093
+ }
2094
+ ]
2095
+ };
2096
+ }
2097
+ };
2098
+
2099
+ // src/audits/cookie-policy.ts
2100
+ var import_i18n28 = require("@wordpress/i18n");
2101
+ var audit4 = {
2102
+ id: "audits/cookie-policy",
2103
+ title: (0, import_i18n28.__)("Cookie policy", "elementor"),
2104
+ description: (0, import_i18n28.__)(
1877
2105
  "A cookie policy is required by privacy regulations such as GDPR (EU) or CCPA (US) to inform visitors about tracking and data collection.",
1878
2106
  "elementor"
1879
2107
  ),
1880
- fixHint: (0, import_i18n26.__)("Install the Cookiez plugin to add a cookie policy to your website.", "elementor"),
2108
+ fixHint: (0, import_i18n28.__)("Install the Cookiez plugin to add a cookie policy to your website.", "elementor"),
1881
2109
  categories: ["compliance"],
1882
2110
  severity: "info",
1883
2111
  weight: 1,
@@ -1889,8 +2117,8 @@ var audit2 = {
1889
2117
  status: "fail",
1890
2118
  violations: [
1891
2119
  {
1892
- auditId: audit2.id,
1893
- label: (0, import_i18n26.__)("Cookiez plugin is not installed or active.", "elementor"),
2120
+ auditId: audit4.id,
2121
+ label: (0, import_i18n28.__)("Cookiez plugin is not installed or active.", "elementor"),
1894
2122
  externalUrl: ctx.pageContext.cookiez_plugin_url
1895
2123
  }
1896
2124
  ]
@@ -1899,8 +2127,8 @@ var audit2 = {
1899
2127
  };
1900
2128
 
1901
2129
  // src/audits/deep-nesting.ts
1902
- var import_editor_elements6 = require("@elementor/editor-elements");
1903
- var import_i18n27 = require("@wordpress/i18n");
2130
+ var import_editor_elements7 = require("@elementor/editor-elements");
2131
+ var import_i18n29 = require("@wordpress/i18n");
1904
2132
  var MAX_NESTING_DEPTH = 6;
1905
2133
  function isNestingElement(node, widgetsCache) {
1906
2134
  if (node.elType === "container") {
@@ -1908,22 +2136,22 @@ function isNestingElement(node, widgetsCache) {
1908
2136
  }
1909
2137
  return !!widgetsCache?.[node.elType]?.meta?.is_container;
1910
2138
  }
1911
- var audit3 = {
2139
+ var audit5 = {
1912
2140
  id: "audits/deep-nesting",
1913
- title: (0, import_i18n27.__)("Deep container nesting", "elementor"),
1914
- description: (0, import_i18n27.__)(
2141
+ title: (0, import_i18n29.__)("Deep container nesting", "elementor"),
2142
+ description: (0, import_i18n29.__)(
1915
2143
  "Container nesting exceeds the recommended depth. Deep DOM trees hurt rendering performance and readability.",
1916
2144
  "elementor"
1917
2145
  ),
1918
- fixHint: (0, import_i18n27.__)("Flatten the layout by removing unnecessary wrapper containers.", "elementor"),
2146
+ fixHint: (0, import_i18n29.__)("Flatten the layout by removing unnecessary wrapper containers.", "elementor"),
1919
2147
  categories: ["performance", "best-practices"],
1920
2148
  severity: "warning",
1921
2149
  weight: 5,
1922
2150
  evaluate: (ctx) => {
1923
2151
  if (ctx.elements.tree.length === 0) {
1924
- return { status: "skipped", reason: (0, import_i18n27.__)("No elements", "elementor") };
2152
+ return { status: "skipped", reason: (0, import_i18n29.__)("No elements", "elementor") };
1925
2153
  }
1926
- const widgetsCache = (0, import_editor_elements6.getWidgetsCache)();
2154
+ const widgetsCache = (0, import_editor_elements7.getWidgetsCache)();
1927
2155
  const violations = [];
1928
2156
  walkElements(ctx.elements.tree, (node, parents) => {
1929
2157
  if (!isNestingElement(node, widgetsCache)) {
@@ -1932,10 +2160,10 @@ var audit3 = {
1932
2160
  const depth = parents.filter((parent) => isNestingElement(parent, widgetsCache)).length + 1;
1933
2161
  if (depth > MAX_NESTING_DEPTH) {
1934
2162
  violations.push({
1935
- auditId: audit3.id,
2163
+ auditId: audit5.id,
1936
2164
  elementId: node.id,
1937
2165
  targetHint: "element-settings",
1938
- label: (0, import_i18n27.__)("Container nesting is too deep.", "elementor")
2166
+ label: (0, import_i18n29.__)("Container nesting is too deep.", "elementor")
1939
2167
  });
1940
2168
  }
1941
2169
  });
@@ -1944,15 +2172,15 @@ var audit3 = {
1944
2172
  };
1945
2173
 
1946
2174
  // src/audits/default-design-system.ts
1947
- var import_i18n28 = require("@wordpress/i18n");
1948
- var audit4 = {
2175
+ var import_i18n30 = require("@wordpress/i18n");
2176
+ var audit6 = {
1949
2177
  id: "audits/default-design-system",
1950
- title: (0, import_i18n28.__)("Default website kit", "elementor"),
1951
- description: (0, import_i18n28.__)(
2178
+ title: (0, import_i18n30.__)("Default website kit", "elementor"),
2179
+ description: (0, import_i18n30.__)(
1952
2180
  "Your website is using the default design system colors and fonts. Custom branding makes the website feel uniquely yours.",
1953
2181
  "elementor"
1954
2182
  ),
1955
- fixHint: (0, import_i18n28.__)("Open Site Settings and customize your kit (colors, fonts, layout).", "elementor"),
2183
+ fixHint: (0, import_i18n30.__)("Open Site Settings and customize your kit (colors, fonts, layout).", "elementor"),
1956
2184
  categories: ["best-practices"],
1957
2185
  severity: "info",
1958
2186
  weight: 1,
@@ -1964,8 +2192,8 @@ var audit4 = {
1964
2192
  status: "fail",
1965
2193
  violations: [
1966
2194
  {
1967
- auditId: audit4.id,
1968
- label: (0, import_i18n28.__)("Kit appears unchanged from default.", "elementor"),
2195
+ auditId: audit6.id,
2196
+ label: (0, import_i18n30.__)("Kit appears unchanged from default.", "elementor"),
1969
2197
  targetHint: "site-settings"
1970
2198
  }
1971
2199
  ]
@@ -1974,22 +2202,22 @@ var audit4 = {
1974
2202
  };
1975
2203
 
1976
2204
  // src/audits/deprecated-widgets.ts
1977
- var import_editor_elements7 = require("@elementor/editor-elements");
1978
- var import_i18n29 = require("@wordpress/i18n");
2205
+ var import_editor_elements8 = require("@elementor/editor-elements");
2206
+ var import_i18n31 = require("@wordpress/i18n");
1979
2207
  var DEPRECATION_CONTROL_NAME = "deprecation_message";
1980
- var audit5 = {
2208
+ var audit7 = {
1981
2209
  id: "audits/deprecated-widgets",
1982
- title: (0, import_i18n29.__)("Deprecated widgets", "elementor"),
1983
- description: (0, import_i18n29.__)(
2210
+ title: (0, import_i18n31.__)("Deprecated widgets", "elementor"),
2211
+ description: (0, import_i18n31.__)(
1984
2212
  "Deprecated widgets should not be used. For better capabilities use the recommended replacement.",
1985
2213
  "elementor"
1986
2214
  ),
1987
- fixHint: (0, import_i18n29.__)("Replace each deprecated widget with the recommended new widget shown in its panel.", "elementor"),
2215
+ fixHint: (0, import_i18n31.__)("Replace each deprecated widget with the recommended new widget shown in its panel.", "elementor"),
1988
2216
  categories: ["best-practices", "performance"],
1989
2217
  severity: "warning",
1990
2218
  weight: 7,
1991
2219
  evaluate: (ctx) => {
1992
- const widgetsCache = (0, import_editor_elements7.getWidgetsCache)();
2220
+ const widgetsCache = (0, import_editor_elements8.getWidgetsCache)();
1993
2221
  if (!widgetsCache) {
1994
2222
  return { status: "skipped", reason: "widgets-cache-unavailable" };
1995
2223
  }
@@ -2008,10 +2236,10 @@ var audit5 = {
2008
2236
  return;
2009
2237
  }
2010
2238
  violations.push({
2011
- auditId: audit5.id,
2239
+ auditId: audit7.id,
2012
2240
  elementId: node.id,
2013
2241
  targetHint: "element-settings",
2014
- label: (0, import_i18n29.__)("Using deprecated widget.", "elementor")
2242
+ label: (0, import_i18n31.__)("Using deprecated widget.", "elementor")
2015
2243
  });
2016
2244
  });
2017
2245
  return violations.length === 0 ? { status: "pass" } : { status: "fail", violations };
@@ -2019,28 +2247,7 @@ var audit5 = {
2019
2247
  };
2020
2248
 
2021
2249
  // src/audits/google-consent-mode.ts
2022
- var import_i18n30 = require("@wordpress/i18n");
2023
-
2024
- // src/utils/fetch-rendered-html.ts
2025
- var FETCH_TIMEOUT_MS = 1e4;
2026
- async function fetchRenderedHtml(url) {
2027
- if (!url) {
2028
- return null;
2029
- }
2030
- const timeoutController = new AbortController();
2031
- const timeoutId = setTimeout(() => timeoutController.abort(), FETCH_TIMEOUT_MS);
2032
- try {
2033
- const response = await fetch(url, { credentials: "omit", signal: timeoutController.signal });
2034
- if (!response.ok) {
2035
- return null;
2036
- }
2037
- return await response.text();
2038
- } catch {
2039
- return null;
2040
- } finally {
2041
- clearTimeout(timeoutId);
2042
- }
2043
- }
2250
+ var import_i18n32 = require("@wordpress/i18n");
2044
2251
 
2045
2252
  // src/utils/scan-consent-signals.ts
2046
2253
  var GOOGLE_TRACKING_PATTERNS = [
@@ -2061,44 +2268,43 @@ function hasConsentDefaultCall(html) {
2061
2268
  }
2062
2269
 
2063
2270
  // src/audits/google-consent-mode.ts
2064
- var audit6 = {
2271
+ var audit8 = {
2065
2272
  id: "audits/google-consent-mode",
2066
- title: (0, import_i18n30.__)("Google Consent Mode", "elementor"),
2067
- description: (0, import_i18n30.__)(
2273
+ title: (0, import_i18n32.__)("Google Consent Mode", "elementor"),
2274
+ description: (0, import_i18n32.__)(
2068
2275
  "Google Analytics, Ads, or Tag Manager are running on this site and may not receive a consent signal, which risks losing conversion/analytics data and compliance exposure.",
2069
2276
  "elementor"
2070
2277
  ),
2071
- fixHint: (0, import_i18n30.__)(
2278
+ fixHint: (0, import_i18n32.__)(
2072
2279
  "Turn on Google Consent Mode in your cookie consent plugin so Google tags respect visitor consent choices.",
2073
2280
  "elementor"
2074
2281
  ),
2075
2282
  categories: ["compliance"],
2076
2283
  severity: "warning",
2077
2284
  weight: 1,
2078
- evaluate: async (ctx) => {
2285
+ evaluate: (ctx) => {
2079
2286
  if (!ctx.pageContext.frontend_url) {
2080
- return { status: "skipped", reason: (0, import_i18n30.__)("Page is not published.", "elementor") };
2287
+ return { status: "skipped", reason: (0, import_i18n32.__)("Page is not published.", "elementor") };
2081
2288
  }
2082
- const html = await fetchRenderedHtml(ctx.pageContext.frontend_url);
2083
- if (!html) {
2084
- return { status: "skipped", reason: (0, import_i18n30.__)("Could not fetch the published page.", "elementor") };
2289
+ if (!ctx.renderedHtml) {
2290
+ return { status: "skipped", reason: (0, import_i18n32.__)("Could not fetch the published page.", "elementor") };
2085
2291
  }
2086
- if (!hasGoogleTracking(html)) {
2087
- return { status: "skipped", reason: (0, import_i18n30.__)("No Google tracking product detected.", "elementor") };
2292
+ if (!hasGoogleTracking(ctx.renderedHtml)) {
2293
+ return { status: "skipped", reason: (0, import_i18n32.__)("No Google tracking product detected.", "elementor") };
2088
2294
  }
2089
- if (hasConsentDefaultCall(html)) {
2295
+ if (hasConsentDefaultCall(ctx.renderedHtml)) {
2090
2296
  return { status: "pass" };
2091
2297
  }
2092
- const label = (0, import_i18n30.__)("No Google Consent Mode signal was found on the page.", "elementor");
2298
+ const label = (0, import_i18n32.__)("No Google Consent Mode signal was found on the page.", "elementor");
2093
2299
  const isCookiezReady = ctx.pageContext.cookiez_plugin_installed && ctx.pageContext.cookiez_plugin_active;
2094
2300
  if (isCookiezReady) {
2095
2301
  return {
2096
2302
  status: "fail",
2097
2303
  violations: [
2098
2304
  {
2099
- auditId: audit6.id,
2305
+ auditId: audit8.id,
2100
2306
  label,
2101
- ctaLabel: (0, import_i18n30.__)("Enable", "elementor"),
2307
+ ctaLabel: (0, import_i18n32.__)("Enable", "elementor"),
2102
2308
  externalUrl: ctx.pageContext.cookiez_consent_mode_settings_url
2103
2309
  }
2104
2310
  ]
@@ -2108,7 +2314,7 @@ var audit6 = {
2108
2314
  status: "fail",
2109
2315
  violations: [
2110
2316
  {
2111
- auditId: audit6.id,
2317
+ auditId: audit8.id,
2112
2318
  label,
2113
2319
  externalUrl: ctx.pageContext.cookiez_plugin_action_url
2114
2320
  }
@@ -2118,7 +2324,7 @@ var audit6 = {
2118
2324
  };
2119
2325
 
2120
2326
  // src/audits/heading-structure.ts
2121
- var import_i18n31 = require("@wordpress/i18n");
2327
+ var import_i18n33 = require("@wordpress/i18n");
2122
2328
  var HEADING_LEVELS = ["h1", "h2", "h3", "h4", "h5", "h6"];
2123
2329
  var HEADING_CONTROLS_REGISTRY = {
2124
2330
  // Core
@@ -2294,47 +2500,47 @@ function extractPageHeadings(tree) {
2294
2500
  });
2295
2501
  return headings;
2296
2502
  }
2297
- var audit7 = {
2503
+ var audit9 = {
2298
2504
  id: "audits/heading-structure",
2299
- title: (0, import_i18n31.__)("Heading structure", "elementor"),
2300
- description: (0, import_i18n31.__)("Pages should have exactly one H1 and a non-skipping heading order.", "elementor"),
2301
- fixHint: (0, import_i18n31.__)("Ensure your page has one H1 and that heading levels do not skip (no H2 \u2192 H4).", "elementor"),
2505
+ title: (0, import_i18n33.__)("Heading structure", "elementor"),
2506
+ description: (0, import_i18n33.__)("Pages should have exactly one H1 and a non-skipping heading order.", "elementor"),
2507
+ fixHint: (0, import_i18n33.__)("Ensure your page has one H1 and that heading levels do not skip (no H2 \u2192 H4).", "elementor"),
2302
2508
  categories: ["seo", "accessibility"],
2303
2509
  severity: "error",
2304
2510
  weight: 10,
2305
2511
  evaluate: (ctx) => {
2306
2512
  if (ctx.elements.tree.length === 0) {
2307
- return { status: "skipped", reason: (0, import_i18n31.__)("No elements", "elementor") };
2513
+ return { status: "skipped", reason: (0, import_i18n33.__)("No elements", "elementor") };
2308
2514
  }
2309
2515
  const headings = extractPageHeadings(ctx.elements.tree);
2310
2516
  const violations = [];
2311
2517
  if (headings.length === 0) {
2312
2518
  violations.push({
2313
- auditId: audit7.id,
2314
- label: (0, import_i18n31.__)("No headings found on the page.", "elementor")
2519
+ auditId: audit9.id,
2520
+ label: (0, import_i18n33.__)("No headings found on the page.", "elementor")
2315
2521
  });
2316
2522
  } else {
2317
2523
  const h1Count = headings.filter((h) => h.level === 1).length;
2318
2524
  if (h1Count === 0) {
2319
- violations.push({ auditId: audit7.id, label: (0, import_i18n31.__)("No H1 on the page.", "elementor") });
2525
+ violations.push({ auditId: audit9.id, label: (0, import_i18n33.__)("No H1 on the page.", "elementor") });
2320
2526
  }
2321
2527
  if (h1Count > 1) {
2322
2528
  headings.filter((h) => h.level === 1).slice(1).forEach(
2323
2529
  (h) => violations.push({
2324
- auditId: audit7.id,
2530
+ auditId: audit9.id,
2325
2531
  elementId: h.elementId,
2326
2532
  targetHint: "element-settings",
2327
- label: (0, import_i18n31.__)("More than one H1 on the page.", "elementor")
2533
+ label: (0, import_i18n33.__)("More than one H1 on the page.", "elementor")
2328
2534
  })
2329
2535
  );
2330
2536
  }
2331
2537
  for (let i = 1; i < headings.length; i++) {
2332
2538
  if (headings[i].level - headings[i - 1].level > 1) {
2333
2539
  violations.push({
2334
- auditId: audit7.id,
2540
+ auditId: audit9.id,
2335
2541
  elementId: headings[i].elementId,
2336
2542
  targetHint: "element-settings",
2337
- label: (0, import_i18n31.__)("Heading level skipped.", "elementor")
2543
+ label: (0, import_i18n33.__)("Heading level skipped.", "elementor")
2338
2544
  });
2339
2545
  }
2340
2546
  }
@@ -2344,33 +2550,33 @@ var audit7 = {
2344
2550
  };
2345
2551
 
2346
2552
  // src/audits/hidden-elements.ts
2347
- var import_i18n32 = require("@wordpress/i18n");
2553
+ var import_i18n34 = require("@wordpress/i18n");
2348
2554
  function isHiddenOnAllDevices(settings) {
2349
2555
  return Boolean(settings.hide_desktop) && Boolean(settings.hide_tablet) && Boolean(settings.hide_mobile);
2350
2556
  }
2351
- var audit8 = {
2557
+ var audit10 = {
2352
2558
  id: "audits/hidden-elements",
2353
- title: (0, import_i18n32.__)("Hidden elements", "elementor"),
2354
- description: (0, import_i18n32.__)(
2559
+ title: (0, import_i18n34.__)("Hidden elements", "elementor"),
2560
+ description: (0, import_i18n34.__)(
2355
2561
  "Elements hidden on all devices simultaneously are effectively dead code in the page tree.",
2356
2562
  "elementor"
2357
2563
  ),
2358
- fixHint: (0, import_i18n32.__)("Remove the element or make it visible on at least one device breakpoint.", "elementor"),
2564
+ fixHint: (0, import_i18n34.__)("Remove the element or make it visible on at least one device breakpoint.", "elementor"),
2359
2565
  categories: ["best-practices", "performance"],
2360
2566
  severity: "info",
2361
2567
  weight: 3,
2362
2568
  evaluate: (ctx) => {
2363
2569
  if (ctx.elements.tree.length === 0) {
2364
- return { status: "skipped", reason: (0, import_i18n32.__)("No elements", "elementor") };
2570
+ return { status: "skipped", reason: (0, import_i18n34.__)("No elements", "elementor") };
2365
2571
  }
2366
2572
  const violations = [];
2367
2573
  walkElements(ctx.elements.tree, (node) => {
2368
2574
  if (isHiddenOnAllDevices(node.settings)) {
2369
2575
  violations.push({
2370
- auditId: audit8.id,
2576
+ auditId: audit10.id,
2371
2577
  elementId: node.id,
2372
2578
  targetHint: "element-settings",
2373
- label: (0, import_i18n32.__)("Element is hidden on all devices.", "elementor")
2579
+ label: (0, import_i18n34.__)("Element is hidden on all devices.", "elementor")
2374
2580
  });
2375
2581
  }
2376
2582
  });
@@ -2379,7 +2585,7 @@ var audit8 = {
2379
2585
  };
2380
2586
 
2381
2587
  // src/audits/images-alt-text.ts
2382
- var import_i18n33 = require("@wordpress/i18n");
2588
+ var import_i18n35 = require("@wordpress/i18n");
2383
2589
 
2384
2590
  // src/utils/image-alt.ts
2385
2591
  function hasMeaningfulAlt(media, pageContext) {
@@ -2387,7 +2593,8 @@ function hasMeaningfulAlt(media, pageContext) {
2387
2593
  return true;
2388
2594
  }
2389
2595
  if (media.id) {
2390
- const alt = pageContext.image_sizes[media.id]?.alt ?? "";
2596
+ const key = buildImageSizeKey({ id: media.id, size: media.size });
2597
+ const alt = pageContext.image_sizes[key]?.alt ?? "";
2391
2598
  return alt.trim().length > 0;
2392
2599
  }
2393
2600
  return (media.alt ?? "").trim().length > 0;
@@ -2397,17 +2604,17 @@ function isImageSourcePresent(media) {
2397
2604
  }
2398
2605
 
2399
2606
  // src/audits/images-alt-text.ts
2400
- var audit9 = {
2607
+ var audit11 = {
2401
2608
  id: "audits/images-alt-text",
2402
- title: (0, import_i18n33.__)("Images alt text", "elementor"),
2403
- description: (0, import_i18n33.__)("Every image needs a meaningful alt attribute for screen readers and image-search.", "elementor"),
2404
- fixHint: (0, import_i18n33.__)("Open the image's settings and add an Alt Text describing the image.", "elementor"),
2609
+ title: (0, import_i18n35.__)("Images alt text", "elementor"),
2610
+ description: (0, import_i18n35.__)("Every image needs a meaningful alt attribute for screen readers and image-search.", "elementor"),
2611
+ fixHint: (0, import_i18n35.__)("Open the image's settings and add an Alt Text describing the image.", "elementor"),
2405
2612
  categories: ["seo", "accessibility"],
2406
2613
  severity: "error",
2407
2614
  weight: 10,
2408
2615
  evaluate: (ctx) => {
2409
2616
  if (!hasPageImages(ctx.elements.tree)) {
2410
- return { status: "skipped", reason: (0, import_i18n33.__)("No images", "elementor") };
2617
+ return { status: "skipped", reason: (0, import_i18n35.__)("No images", "elementor") };
2411
2618
  }
2412
2619
  const violations = [];
2413
2620
  const failedWidgetIds = /* @__PURE__ */ new Set();
@@ -2420,10 +2627,10 @@ var audit9 = {
2420
2627
  if (!failedWidgetIds.has(node.id)) {
2421
2628
  failedWidgetIds.add(node.id);
2422
2629
  violations.push({
2423
- auditId: audit9.id,
2630
+ auditId: audit11.id,
2424
2631
  elementId: node.id,
2425
2632
  targetHint: "element-settings",
2426
- label: (0, import_i18n33.__)("Image is missing alt text.", "elementor"),
2633
+ label: (0, import_i18n35.__)("Image is missing alt text.", "elementor"),
2427
2634
  externalUrl: ctx.pageContext.ally_plugin_url
2428
2635
  });
2429
2636
  }
@@ -2432,30 +2639,79 @@ var audit9 = {
2432
2639
  }
2433
2640
  };
2434
2641
 
2642
+ // src/audits/images-inefficient-format.ts
2643
+ var import_i18n36 = require("@wordpress/i18n");
2644
+ var LEGACY_RASTER_MIME_TYPES = /* @__PURE__ */ new Set(["image/jpeg", "image/png", "image/gif", "image/bmp", "image/tiff"]);
2645
+ var audit12 = {
2646
+ id: "audits/images-inefficient-format",
2647
+ title: (0, import_i18n36.__)("Inefficient image format", "elementor"),
2648
+ description: (0, import_i18n36.__)("Image isn't served in a modern, efficient format like WebP or AVIF.", "elementor"),
2649
+ fixHint: (0, import_i18n36.__)("Convert the image to a modern format like WebP or AVIF, or enable image optimization.", "elementor"),
2650
+ categories: ["performance"],
2651
+ severity: "info",
2652
+ weight: 1,
2653
+ evaluate: (ctx) => {
2654
+ const offendingElementIds = /* @__PURE__ */ new Set();
2655
+ let hasAnyImage = false;
2656
+ let inefficientImageFormatCount = 0;
2657
+ const evaluateSource = (node, media) => {
2658
+ if (media.id || media.url) {
2659
+ hasAnyImage = true;
2660
+ }
2661
+ const id = media.id;
2662
+ if (!id) {
2663
+ return;
2664
+ }
2665
+ const mime = ctx.pageContext.image_sizes[buildImageSizeKey({ id, size: media.size })]?.mime;
2666
+ if (!mime || !LEGACY_RASTER_MIME_TYPES.has(mime)) {
2667
+ return;
2668
+ }
2669
+ inefficientImageFormatCount++;
2670
+ offendingElementIds.add(node.id);
2671
+ };
2672
+ walkImageLikeSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2673
+ walkBackgroundImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2674
+ walkAtomicImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2675
+ walkAtomicBackgroundImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2676
+ if (!hasAnyImage) {
2677
+ return { status: "skipped", reason: (0, import_i18n36.__)("No images", "elementor") };
2678
+ }
2679
+ const violations = Array.from(offendingElementIds).map((elementId) => ({
2680
+ auditId: audit12.id,
2681
+ elementId,
2682
+ targetHint: "element-settings",
2683
+ label: (0, import_i18n36.__)("Image isn't served in a modern format like WebP or AVIF.", "elementor"),
2684
+ externalUrl: ctx.pageContext.image_optimization_plugin_url
2685
+ }));
2686
+ return violations.length === 0 ? { status: "pass" } : { status: "fail", violations, metadata: { inefficientImageFormatCount } };
2687
+ }
2688
+ };
2689
+
2435
2690
  // src/audits/images-too-large.ts
2436
- var import_i18n34 = require("@wordpress/i18n");
2691
+ var import_i18n37 = require("@wordpress/i18n");
2437
2692
  var SIZE_THRESHOLD_BYTES = 500 * 1024;
2438
2693
  var BYTES_PER_KB = 1024;
2439
- var audit10 = {
2694
+ var audit13 = {
2440
2695
  id: "audits/images-too-large",
2441
- title: (0, import_i18n34.__)("Oversized images", "elementor"),
2442
- description: (0, import_i18n34.__)("Large image files slow down the page.", "elementor"),
2443
- fixHint: (0, import_i18n34.__)("Replace the image with a smaller version or enable image optimization.", "elementor"),
2696
+ title: (0, import_i18n37.__)("Oversized images", "elementor"),
2697
+ description: (0, import_i18n37.__)("Large image files slow down the page.", "elementor"),
2698
+ fixHint: (0, import_i18n37.__)("Replace the image with a smaller version or enable image optimization.", "elementor"),
2444
2699
  categories: ["performance"],
2445
2700
  severity: "warning",
2446
2701
  weight: 7,
2447
2702
  evaluate: (ctx) => {
2448
- if (!hasPageImages(ctx.elements.tree)) {
2449
- return { status: "skipped", reason: (0, import_i18n34.__)("No images", "elementor") };
2450
- }
2451
2703
  const widgetMaxKb = /* @__PURE__ */ new Map();
2452
2704
  let oversizedImageCount = 0;
2453
- walkImageLikeSources(ctx.elements.tree, ({ node, media }) => {
2705
+ let hasAnyImage = false;
2706
+ const evaluateSource = (node, media) => {
2707
+ if (media.id || media.url) {
2708
+ hasAnyImage = true;
2709
+ }
2454
2710
  const id = media.id;
2455
2711
  if (!id) {
2456
2712
  return;
2457
2713
  }
2458
- const size = ctx.pageContext.image_sizes[id];
2714
+ const size = ctx.pageContext.image_sizes[buildImageSizeKey({ id, size: media.size })];
2459
2715
  if (!size || size.filesize_bytes <= SIZE_THRESHOLD_BYTES) {
2460
2716
  return;
2461
2717
  }
@@ -2463,14 +2719,21 @@ var audit10 = {
2463
2719
  const kb = Math.round(size.filesize_bytes / BYTES_PER_KB);
2464
2720
  const currentMax = widgetMaxKb.get(node.id) ?? 0;
2465
2721
  widgetMaxKb.set(node.id, Math.max(currentMax, kb));
2466
- });
2722
+ };
2723
+ walkImageLikeSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2724
+ walkBackgroundImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2725
+ walkAtomicImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2726
+ walkAtomicBackgroundImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2727
+ if (!hasAnyImage) {
2728
+ return { status: "skipped", reason: (0, import_i18n37.__)("No images", "elementor") };
2729
+ }
2467
2730
  const violations = Array.from(widgetMaxKb.entries()).map(([elementId, kb]) => ({
2468
- auditId: audit10.id,
2731
+ auditId: audit13.id,
2469
2732
  elementId,
2470
2733
  targetHint: "element-settings",
2471
- label: (0, import_i18n34.sprintf)(
2734
+ label: (0, import_i18n37.sprintf)(
2472
2735
  /* translators: %d is the image file size in kilobytes. */
2473
- (0, import_i18n34.__)("Image is %d KB (over 500 KB).", "elementor"),
2736
+ (0, import_i18n37.__)("Image is %d KB (over 500 KB).", "elementor"),
2474
2737
  kb
2475
2738
  ),
2476
2739
  externalUrl: ctx.pageContext.image_optimization_plugin_url
@@ -2480,21 +2743,21 @@ var audit10 = {
2480
2743
  };
2481
2744
 
2482
2745
  // src/audits/nested-boxed-containers.ts
2483
- var import_i18n35 = require("@wordpress/i18n");
2746
+ var import_i18n38 = require("@wordpress/i18n");
2484
2747
  function isBoxed(settings) {
2485
2748
  return settings.content_width === "boxed";
2486
2749
  }
2487
- var audit11 = {
2750
+ var audit14 = {
2488
2751
  id: "audits/nested-boxed-containers",
2489
- title: (0, import_i18n35.__)("Boxed container nested inside a boxed parent", "elementor"),
2490
- description: (0, import_i18n35.__)("An inner container does not need to be boxed when its parent already is.", "elementor"),
2491
- fixHint: (0, import_i18n35.__)("Change the inner container's content width to Full Width.", "elementor"),
2752
+ title: (0, import_i18n38.__)("Boxed container nested inside a boxed parent", "elementor"),
2753
+ description: (0, import_i18n38.__)("An inner container does not need to be boxed when its parent already is.", "elementor"),
2754
+ fixHint: (0, import_i18n38.__)("Change the inner container's content width to Full Width.", "elementor"),
2492
2755
  categories: ["best-practices", "performance"],
2493
2756
  severity: "info",
2494
2757
  weight: 5,
2495
2758
  evaluate: (ctx) => {
2496
2759
  if (ctx.elements.tree.length === 0) {
2497
- return { status: "skipped", reason: (0, import_i18n35.__)("No elements", "elementor") };
2760
+ return { status: "skipped", reason: (0, import_i18n38.__)("No elements", "elementor") };
2498
2761
  }
2499
2762
  const violations = [];
2500
2763
  walkElements(ctx.elements.tree, (node, parents) => {
@@ -2504,10 +2767,10 @@ var audit11 = {
2504
2767
  const nearestContainerAncestor = [...parents].reverse().find((p) => p.elType === "container");
2505
2768
  if (nearestContainerAncestor && isBoxed(nearestContainerAncestor.settings)) {
2506
2769
  violations.push({
2507
- auditId: audit11.id,
2770
+ auditId: audit14.id,
2508
2771
  elementId: node.id,
2509
2772
  targetHint: "element-settings",
2510
- label: (0, import_i18n35.__)("Nested boxed container.", "elementor")
2773
+ label: (0, import_i18n38.__)("Nested boxed container.", "elementor")
2511
2774
  });
2512
2775
  }
2513
2776
  });
@@ -2516,12 +2779,12 @@ var audit11 = {
2516
2779
  };
2517
2780
 
2518
2781
  // src/audits/page-excerpt.ts
2519
- var import_i18n36 = require("@wordpress/i18n");
2520
- var audit12 = {
2782
+ var import_i18n39 = require("@wordpress/i18n");
2783
+ var audit15 = {
2521
2784
  id: "audits/page-excerpt",
2522
- title: (0, import_i18n36.__)("Page excerpt", "elementor"),
2523
- description: (0, import_i18n36.__)("A descriptive excerpt helps search engines and previews summarize the page.", "elementor"),
2524
- fixHint: (0, import_i18n36.__)("Open Page Settings and write a short excerpt.", "elementor"),
2785
+ title: (0, import_i18n39.__)("Page excerpt", "elementor"),
2786
+ description: (0, import_i18n39.__)("A descriptive excerpt helps search engines and previews summarize the page.", "elementor"),
2787
+ fixHint: (0, import_i18n39.__)("Open Page Settings and write a short excerpt.", "elementor"),
2525
2788
  categories: ["seo"],
2526
2789
  severity: "warning",
2527
2790
  weight: 5,
@@ -2533,8 +2796,8 @@ var audit12 = {
2533
2796
  status: "fail",
2534
2797
  violations: [
2535
2798
  {
2536
- auditId: audit12.id,
2537
- label: (0, import_i18n36.__)("Page has no excerpt.", "elementor"),
2799
+ auditId: audit15.id,
2800
+ label: (0, import_i18n39.__)("Page has no excerpt.", "elementor"),
2538
2801
  targetHint: "page-settings",
2539
2802
  angieFix: true
2540
2803
  }
@@ -2544,12 +2807,12 @@ var audit12 = {
2544
2807
  };
2545
2808
 
2546
2809
  // src/audits/page-featured-image.ts
2547
- var import_i18n37 = require("@wordpress/i18n");
2548
- var audit13 = {
2810
+ var import_i18n40 = require("@wordpress/i18n");
2811
+ var audit16 = {
2549
2812
  id: "audits/page-featured-image",
2550
- title: (0, import_i18n37.__)("Page featured image", "elementor"),
2551
- description: (0, import_i18n37.__)("Featured images are used by social shares and many themes for hero visuals.", "elementor"),
2552
- fixHint: (0, import_i18n37.__)("Open Page Settings and set a featured image.", "elementor"),
2813
+ title: (0, import_i18n40.__)("Page featured image", "elementor"),
2814
+ description: (0, import_i18n40.__)("Featured images are used by social shares and many themes for hero visuals.", "elementor"),
2815
+ fixHint: (0, import_i18n40.__)("Open Page Settings and set a featured image.", "elementor"),
2553
2816
  categories: ["seo"],
2554
2817
  severity: "info",
2555
2818
  weight: 5,
@@ -2561,8 +2824,8 @@ var audit13 = {
2561
2824
  status: "fail",
2562
2825
  violations: [
2563
2826
  {
2564
- auditId: audit13.id,
2565
- label: (0, import_i18n37.__)("No featured image set.", "elementor"),
2827
+ auditId: audit16.id,
2828
+ label: (0, import_i18n40.__)("No featured image set.", "elementor"),
2566
2829
  targetHint: "page-settings"
2567
2830
  }
2568
2831
  ]
@@ -2571,13 +2834,13 @@ var audit13 = {
2571
2834
  };
2572
2835
 
2573
2836
  // src/audits/page-title.ts
2574
- var import_i18n38 = require("@wordpress/i18n");
2837
+ var import_i18n41 = require("@wordpress/i18n");
2575
2838
  var MAX_TITLE_LENGTH = 60;
2576
- var audit14 = {
2839
+ var audit17 = {
2577
2840
  id: "audits/page-title",
2578
- title: (0, import_i18n38.__)("Page title", "elementor"),
2579
- description: (0, import_i18n38.__)("Pages need a clear title for SEO and screen-reader navigation.", "elementor"),
2580
- fixHint: (0, import_i18n38.__)("Open Page Settings and add a title.", "elementor"),
2841
+ title: (0, import_i18n41.__)("Page title", "elementor"),
2842
+ description: (0, import_i18n41.__)("Pages need a clear title for SEO and screen-reader navigation.", "elementor"),
2843
+ fixHint: (0, import_i18n41.__)("Open Page Settings and add a title.", "elementor"),
2581
2844
  categories: ["seo"],
2582
2845
  severity: "error",
2583
2846
  weight: 10,
@@ -2588,11 +2851,11 @@ var audit14 = {
2588
2851
  status: "fail",
2589
2852
  violations: [
2590
2853
  {
2591
- auditId: audit14.id,
2592
- label: (0, import_i18n38.__)("Page has no title.", "elementor"),
2854
+ auditId: audit17.id,
2855
+ label: (0, import_i18n41.__)("Page has no title.", "elementor"),
2593
2856
  targetHint: "page-settings",
2594
2857
  angieFix: true,
2595
- angiePrompt: (0, import_i18n38.__)(
2858
+ angiePrompt: (0, import_i18n41.__)(
2596
2859
  "Based on this page's contents, generate a title for this page.",
2597
2860
  "elementor"
2598
2861
  )
@@ -2605,8 +2868,8 @@ var audit14 = {
2605
2868
  status: "fail",
2606
2869
  violations: [
2607
2870
  {
2608
- auditId: audit14.id,
2609
- label: (0, import_i18n38.__)("Page title is too long.", "elementor"),
2871
+ auditId: audit17.id,
2872
+ label: (0, import_i18n41.__)("Page title is too long.", "elementor"),
2610
2873
  targetHint: "page-settings",
2611
2874
  angieFix: true
2612
2875
  }
@@ -2618,7 +2881,7 @@ var audit14 = {
2618
2881
  };
2619
2882
 
2620
2883
  // src/audits/prefer-global-colors.ts
2621
- var import_i18n39 = require("@wordpress/i18n");
2884
+ var import_i18n42 = require("@wordpress/i18n");
2622
2885
 
2623
2886
  // src/utils/collect-hardcoded-colors.ts
2624
2887
  var RESERVED_SETTING_KEYS = /* @__PURE__ */ new Set(["__globals__", "__dynamic__"]);
@@ -2684,20 +2947,20 @@ function findMatchingGlobalByValue(value, globals) {
2684
2947
  }
2685
2948
 
2686
2949
  // src/audits/prefer-global-colors.ts
2687
- var audit15 = {
2950
+ var audit18 = {
2688
2951
  id: "audits/prefer-global-colors",
2689
- title: (0, import_i18n39.__)("Prefer global colors", "elementor"),
2690
- description: (0, import_i18n39.__)("Global colors make the design consistent and easy to update site-wide.", "elementor"),
2691
- fixHint: (0, import_i18n39.__)("Replace the hard-coded value with the matching global color.", "elementor"),
2952
+ title: (0, import_i18n42.__)("Prefer global colors", "elementor"),
2953
+ description: (0, import_i18n42.__)("Global colors make the design consistent and easy to update site-wide.", "elementor"),
2954
+ fixHint: (0, import_i18n42.__)("Replace the hard-coded value with the matching global color.", "elementor"),
2692
2955
  categories: ["best-practices"],
2693
2956
  severity: "info",
2694
2957
  weight: 3,
2695
2958
  evaluate: (ctx) => {
2696
2959
  if (ctx.elements.tree.length === 0) {
2697
- return { status: "skipped", reason: (0, import_i18n39.__)("No elements", "elementor") };
2960
+ return { status: "skipped", reason: (0, import_i18n42.__)("No elements", "elementor") };
2698
2961
  }
2699
2962
  if (ctx.kit.globals.colors.length === 0) {
2700
- return { status: "skipped", reason: (0, import_i18n39.__)("No global colors", "elementor") };
2963
+ return { status: "skipped", reason: (0, import_i18n42.__)("No global colors", "elementor") };
2701
2964
  }
2702
2965
  const violations = [];
2703
2966
  walkElements(ctx.elements.tree, (node) => {
@@ -2707,13 +2970,13 @@ var audit15 = {
2707
2970
  continue;
2708
2971
  }
2709
2972
  violations.push({
2710
- auditId: audit15.id,
2973
+ auditId: audit18.id,
2711
2974
  elementId: node.id,
2712
2975
  targetHint: "element-settings",
2713
2976
  angieFix: true,
2714
- label: (0, import_i18n39.sprintf)(
2977
+ label: (0, import_i18n42.sprintf)(
2715
2978
  /* translators: 1: hard-coded color value, 2: global color title. */
2716
- (0, import_i18n39.__)('replace "%1$s" with "%2$s" global color', "elementor"),
2979
+ (0, import_i18n42.__)('replace "%1$s" with "%2$s" global color', "elementor"),
2717
2980
  value,
2718
2981
  global.title
2719
2982
  )
@@ -2725,7 +2988,7 @@ var audit15 = {
2725
2988
  };
2726
2989
 
2727
2990
  // src/audits/prefer-global-fonts.ts
2728
- var import_i18n40 = require("@wordpress/i18n");
2991
+ var import_i18n43 = require("@wordpress/i18n");
2729
2992
 
2730
2993
  // src/utils/collect-hardcoded-fonts.ts
2731
2994
  var RESERVED_SETTING_KEYS2 = /* @__PURE__ */ new Set(["__globals__", "__dynamic__"]);
@@ -2783,20 +3046,20 @@ function collectHardcodedFonts(settings, pathPrefix = "") {
2783
3046
  }
2784
3047
 
2785
3048
  // src/audits/prefer-global-fonts.ts
2786
- var audit16 = {
3049
+ var audit19 = {
2787
3050
  id: "audits/prefer-global-fonts",
2788
- title: (0, import_i18n40.__)("Prefer global fonts", "elementor"),
2789
- description: (0, import_i18n40.__)("Global fonts make the design consistent and easy to update site-wide.", "elementor"),
2790
- fixHint: (0, import_i18n40.__)("Replace the hard-coded value with the matching global font.", "elementor"),
3051
+ title: (0, import_i18n43.__)("Prefer global fonts", "elementor"),
3052
+ description: (0, import_i18n43.__)("Global fonts make the design consistent and easy to update site-wide.", "elementor"),
3053
+ fixHint: (0, import_i18n43.__)("Replace the hard-coded value with the matching global font.", "elementor"),
2791
3054
  categories: ["best-practices", "performance"],
2792
3055
  severity: "info",
2793
3056
  weight: 3,
2794
3057
  evaluate: (ctx) => {
2795
3058
  if (ctx.elements.tree.length === 0) {
2796
- return { status: "skipped", reason: (0, import_i18n40.__)("No elements", "elementor") };
3059
+ return { status: "skipped", reason: (0, import_i18n43.__)("No elements", "elementor") };
2797
3060
  }
2798
3061
  if (ctx.kit.globals.fonts.length === 0) {
2799
- return { status: "skipped", reason: (0, import_i18n40.__)("No global fonts", "elementor") };
3062
+ return { status: "skipped", reason: (0, import_i18n43.__)("No global fonts", "elementor") };
2800
3063
  }
2801
3064
  const violations = [];
2802
3065
  walkElements(ctx.elements.tree, (node) => {
@@ -2806,12 +3069,12 @@ var audit16 = {
2806
3069
  continue;
2807
3070
  }
2808
3071
  violations.push({
2809
- auditId: audit16.id,
3072
+ auditId: audit19.id,
2810
3073
  elementId: node.id,
2811
3074
  targetHint: "element-settings",
2812
- label: (0, import_i18n40.sprintf)(
3075
+ label: (0, import_i18n43.sprintf)(
2813
3076
  /* translators: %s: global font title. */
2814
- (0, import_i18n40.__)('replace hardcoded typography with "%s" global font', "elementor"),
3077
+ (0, import_i18n43.__)('replace hardcoded typography with "%s" global font', "elementor"),
2815
3078
  global.title
2816
3079
  )
2817
3080
  });
@@ -2822,15 +3085,15 @@ var audit16 = {
2822
3085
  };
2823
3086
 
2824
3087
  // src/audits/privacy-policy.ts
2825
- var import_i18n41 = require("@wordpress/i18n");
2826
- var audit17 = {
3088
+ var import_i18n44 = require("@wordpress/i18n");
3089
+ var audit20 = {
2827
3090
  id: "audits/privacy-policy",
2828
- title: (0, import_i18n41.__)("Privacy policy", "elementor"),
2829
- description: (0, import_i18n41.__)(
3091
+ title: (0, import_i18n44.__)("Privacy policy", "elementor"),
3092
+ description: (0, import_i18n44.__)(
2830
3093
  "A privacy policy page is required by privacy regulations such as GDPR (EU) and CCPA (US).",
2831
3094
  "elementor"
2832
3095
  ),
2833
- fixHint: (0, import_i18n41.__)(
3096
+ fixHint: (0, import_i18n44.__)(
2834
3097
  "Go to Settings > Privacy in the WordPress admin and assign a published privacy policy page.",
2835
3098
  "elementor"
2836
3099
  ),
@@ -2845,10 +3108,10 @@ var audit17 = {
2845
3108
  status: "fail",
2846
3109
  violations: [
2847
3110
  {
2848
- auditId: audit17.id,
2849
- label: (0, import_i18n41.__)("No privacy policy page is set.", "elementor"),
3111
+ auditId: audit20.id,
3112
+ label: (0, import_i18n44.__)("No privacy policy page is set.", "elementor"),
2850
3113
  externalUrl: ctx.pageContext.privacy_settings_url,
2851
- ctaLabel: (0, import_i18n41.__)("Create", "elementor")
3114
+ ctaLabel: (0, import_i18n44.__)("Create", "elementor")
2852
3115
  }
2853
3116
  ]
2854
3117
  };
@@ -2856,15 +3119,15 @@ var audit17 = {
2856
3119
  };
2857
3120
 
2858
3121
  // src/audits/robots-noindex.ts
2859
- var import_i18n42 = require("@wordpress/i18n");
2860
- var audit18 = {
3122
+ var import_i18n45 = require("@wordpress/i18n");
3123
+ var audit21 = {
2861
3124
  id: "audits/robots-noindex",
2862
- title: (0, import_i18n42.__)("Search engine visibility", "elementor"),
2863
- description: (0, import_i18n42.__)(
3125
+ title: (0, import_i18n45.__)("Search engine visibility", "elementor"),
3126
+ description: (0, import_i18n45.__)(
2864
3127
  "When search engine visibility is checked in WordPress Settings -> Reading -> Search Engine Visibility, search engines are discouraged from indexing the website.",
2865
3128
  "elementor"
2866
3129
  ),
2867
- fixHint: (0, import_i18n42.__)(
3130
+ fixHint: (0, import_i18n45.__)(
2868
3131
  'Go to Settings \u2192 Reading in the WordPress admin and uncheck "Discourage search engines from indexing this site" when the website should be indexed.',
2869
3132
  "elementor"
2870
3133
  ),
@@ -2879,8 +3142,8 @@ var audit18 = {
2879
3142
  status: "fail",
2880
3143
  violations: [
2881
3144
  {
2882
- auditId: audit18.id,
2883
- label: (0, import_i18n42.__)("Search engines are discouraged from indexing this website.", "elementor"),
3145
+ auditId: audit21.id,
3146
+ label: (0, import_i18n45.__)("Search engines are discouraged from indexing this website.", "elementor"),
2884
3147
  externalUrl: ctx.pageContext.reading_settings_url
2885
3148
  }
2886
3149
  ]
@@ -2889,15 +3152,15 @@ var audit18 = {
2889
3152
  };
2890
3153
 
2891
3154
  // src/audits/scan-for-cookies.ts
2892
- var import_i18n43 = require("@wordpress/i18n");
2893
- var audit19 = {
3155
+ var import_i18n46 = require("@wordpress/i18n");
3156
+ var audit22 = {
2894
3157
  id: "audits/scan-for-cookies",
2895
- title: (0, import_i18n43.__)("Scan for cookies", "elementor"),
2896
- description: (0, import_i18n43.__)(
3158
+ title: (0, import_i18n46.__)("Scan for cookies", "elementor"),
3159
+ description: (0, import_i18n46.__)(
2897
3160
  "Your site may be setting cookies you haven't disclosed. Some countries require a banner even without a full consent policy. Scan to see what needs disclosure.",
2898
3161
  "elementor"
2899
3162
  ),
2900
- fixHint: (0, import_i18n43.__)("Run a Cookiez scan on this page to see which cookies need disclosure.", "elementor"),
3163
+ fixHint: (0, import_i18n46.__)("Run a Cookiez scan on this page to see which cookies need disclosure.", "elementor"),
2901
3164
  categories: ["compliance"],
2902
3165
  severity: "info",
2903
3166
  weight: 0,
@@ -2907,8 +3170,8 @@ var audit19 = {
2907
3170
  status: "fail",
2908
3171
  violations: [
2909
3172
  {
2910
- auditId: audit19.id,
2911
- label: (0, import_i18n43.__)("This page has not been scanned for cookies yet.", "elementor"),
3173
+ auditId: audit22.id,
3174
+ label: (0, import_i18n46.__)("This page has not been scanned for cookies yet.", "elementor"),
2912
3175
  externalUrl: isReady ? ctx.pageContext.cookiez_scan_url : ctx.pageContext.cookiez_plugin_action_url
2913
3176
  }
2914
3177
  ]
@@ -2917,29 +3180,29 @@ var audit19 = {
2917
3180
  };
2918
3181
 
2919
3182
  // src/audits/sections-and-columns.ts
2920
- var import_i18n44 = require("@wordpress/i18n");
2921
- var audit20 = {
3183
+ var import_i18n47 = require("@wordpress/i18n");
3184
+ var audit23 = {
2922
3185
  id: "audits/sections-and-columns",
2923
- title: (0, import_i18n44.__)("Sections and columns", "elementor"),
2924
- description: (0, import_i18n44.__)(
3186
+ title: (0, import_i18n47.__)("Sections and columns", "elementor"),
3187
+ description: (0, import_i18n47.__)(
2925
3188
  "Sections and columns are legacy elements. Containers render fewer DOM nodes and are more flexible.",
2926
3189
  "elementor"
2927
3190
  ),
2928
- fixHint: (0, import_i18n44.__)("Use the Container Converter to replace each section/column with a container.", "elementor"),
3191
+ fixHint: (0, import_i18n47.__)("Use the Container Converter to replace each section/column with a container.", "elementor"),
2929
3192
  categories: ["best-practices", "performance"],
2930
3193
  severity: "warning",
2931
3194
  weight: 7,
2932
3195
  evaluate: (ctx) => {
2933
3196
  if (ctx.elements.tree.length === 0) {
2934
- return { status: "skipped", reason: (0, import_i18n44.__)("No elements", "elementor") };
3197
+ return { status: "skipped", reason: (0, import_i18n47.__)("No elements", "elementor") };
2935
3198
  }
2936
3199
  const violations = [];
2937
3200
  walkElements(ctx.elements.tree, (node) => {
2938
3201
  if (node.elType === "section" || node.elType === "column") {
2939
3202
  violations.push({
2940
- auditId: audit20.id,
3203
+ auditId: audit23.id,
2941
3204
  elementId: node.id,
2942
- label: node.elType === "section" ? (0, import_i18n44.__)("Section element", "elementor") : (0, import_i18n44.__)("Column element", "elementor")
3205
+ label: node.elType === "section" ? (0, import_i18n47.__)("Section element", "elementor") : (0, import_i18n47.__)("Column element", "elementor")
2943
3206
  });
2944
3207
  }
2945
3208
  });
@@ -2948,15 +3211,15 @@ var audit20 = {
2948
3211
  };
2949
3212
 
2950
3213
  // src/audits/site-identity.ts
2951
- var import_i18n45 = require("@wordpress/i18n");
2952
- var audit21 = {
3214
+ var import_i18n48 = require("@wordpress/i18n");
3215
+ var audit24 = {
2953
3216
  id: "audits/site-identity",
2954
- title: (0, import_i18n45.__)("Site identity", "elementor"),
2955
- description: (0, import_i18n45.__)(
3217
+ title: (0, import_i18n48.__)("Site identity", "elementor"),
3218
+ description: (0, import_i18n48.__)(
2956
3219
  "Site name, description, logo, and favicon establish your brand and appear in search results and browser tabs.",
2957
3220
  "elementor"
2958
3221
  ),
2959
- fixHint: (0, import_i18n45.__)("Open Site Settings \u2192 Site Identity and complete all the missing fields.", "elementor"),
3222
+ fixHint: (0, import_i18n48.__)("Open Site Settings \u2192 Site Identity and complete all the missing fields.", "elementor"),
2960
3223
  categories: ["best-practices", "seo"],
2961
3224
  severity: "info",
2962
3225
  weight: 7,
@@ -2965,30 +3228,30 @@ var audit21 = {
2965
3228
  const violations = [];
2966
3229
  if (!identity.site_name_set) {
2967
3230
  violations.push({
2968
- auditId: audit21.id,
2969
- label: (0, import_i18n45.__)("Site name is missing or still uses the default.", "elementor"),
3231
+ auditId: audit24.id,
3232
+ label: (0, import_i18n48.__)("Site name is missing or still uses the default.", "elementor"),
2970
3233
  targetHint: "site-identity-settings"
2971
3234
  });
2972
3235
  }
2973
3236
  if (!identity.site_description_set) {
2974
3237
  violations.push({
2975
- auditId: audit21.id,
2976
- label: (0, import_i18n45.__)("Site description is missing or still uses the default.", "elementor"),
3238
+ auditId: audit24.id,
3239
+ label: (0, import_i18n48.__)("Site description is missing or still uses the default.", "elementor"),
2977
3240
  targetHint: "site-identity-settings",
2978
3241
  angieFix: true
2979
3242
  });
2980
3243
  }
2981
3244
  if (!identity.site_logo_set) {
2982
3245
  violations.push({
2983
- auditId: audit21.id,
2984
- label: (0, import_i18n45.__)("Site logo is not set.", "elementor"),
3246
+ auditId: audit24.id,
3247
+ label: (0, import_i18n48.__)("Site logo is not set.", "elementor"),
2985
3248
  targetHint: "site-identity-settings"
2986
3249
  });
2987
3250
  }
2988
3251
  if (!identity.site_favicon_set) {
2989
3252
  violations.push({
2990
- auditId: audit21.id,
2991
- label: (0, import_i18n45.__)("Site favicon is not set.", "elementor"),
3253
+ auditId: audit24.id,
3254
+ label: (0, import_i18n48.__)("Site favicon is not set.", "elementor"),
2992
3255
  targetHint: "site-identity-settings"
2993
3256
  });
2994
3257
  }
@@ -3003,8 +3266,8 @@ var audit21 = {
3003
3266
  };
3004
3267
 
3005
3268
  // src/audits/too-many-widgets.ts
3006
- var import_editor_elements8 = require("@elementor/editor-elements");
3007
- var import_i18n46 = require("@wordpress/i18n");
3269
+ var import_editor_elements9 = require("@elementor/editor-elements");
3270
+ var import_i18n49 = require("@wordpress/i18n");
3008
3271
  var WIDGET_COUNT_THRESHOLD = 100;
3009
3272
  function isCountableWidget(node, widgetsCache) {
3010
3273
  if (node.elType === "widget") {
@@ -3012,19 +3275,19 @@ function isCountableWidget(node, widgetsCache) {
3012
3275
  }
3013
3276
  return !!widgetsCache?.[node.elType]?.meta?.is_compound;
3014
3277
  }
3015
- var audit22 = {
3278
+ var audit25 = {
3016
3279
  id: "audits/too-many-widgets",
3017
- title: (0, import_i18n46.__)("Too many widgets", "elementor"),
3018
- description: (0, import_i18n46.__)("Excessive DOM size caused by too many widgets degrades rendering performance.", "elementor"),
3019
- fixHint: (0, import_i18n46.__)("Reduce the number of widgets on the page by removing or combining elements.", "elementor"),
3280
+ title: (0, import_i18n49.__)("Too many widgets", "elementor"),
3281
+ description: (0, import_i18n49.__)("Excessive DOM size caused by too many widgets degrades rendering performance.", "elementor"),
3282
+ fixHint: (0, import_i18n49.__)("Reduce the number of widgets on the page by removing or combining elements.", "elementor"),
3020
3283
  categories: ["best-practices", "performance"],
3021
3284
  severity: "warning",
3022
3285
  weight: 5,
3023
3286
  evaluate: (ctx) => {
3024
3287
  if (ctx.elements.tree.length === 0) {
3025
- return { status: "skipped", reason: (0, import_i18n46.__)("No elements", "elementor") };
3288
+ return { status: "skipped", reason: (0, import_i18n49.__)("No elements", "elementor") };
3026
3289
  }
3027
- const widgetsCache = (0, import_editor_elements8.getWidgetsCache)();
3290
+ const widgetsCache = (0, import_editor_elements9.getWidgetsCache)();
3028
3291
  let widgetCount = 0;
3029
3292
  walkElements(ctx.elements.tree, (node) => {
3030
3293
  if (isCountableWidget(node, widgetsCache)) {
@@ -3038,8 +3301,8 @@ var audit22 = {
3038
3301
  status: "fail",
3039
3302
  violations: [
3040
3303
  {
3041
- auditId: audit22.id,
3042
- label: (0, import_i18n46.__)("Page has too many widgets.", "elementor")
3304
+ auditId: audit25.id,
3305
+ label: (0, import_i18n49.__)("Page has too many widgets.", "elementor")
3043
3306
  }
3044
3307
  ]
3045
3308
  };
@@ -3048,32 +3311,35 @@ var audit22 = {
3048
3311
 
3049
3312
  // src/register-audits.ts
3050
3313
  var AUDITS = [
3051
- audit14,
3052
- audit12,
3053
- audit13,
3054
- audit8,
3055
- audit5,
3056
- audit22,
3057
- audit20,
3058
- audit3,
3059
- audit11,
3060
- audit4,
3061
- audit21,
3314
+ audit17,
3062
3315
  audit15,
3063
3316
  audit16,
3064
- audit7,
3065
- audit9,
3066
3317
  audit10,
3318
+ audit7,
3319
+ audit25,
3320
+ audit23,
3321
+ audit5,
3322
+ audit14,
3323
+ audit6,
3324
+ audit24,
3067
3325
  audit18,
3068
- audit17,
3326
+ audit19,
3327
+ audit9,
3328
+ audit11,
3329
+ audit13,
3330
+ audit12,
3331
+ audit21,
3332
+ audit20,
3069
3333
  audit,
3070
3334
  audit2,
3071
- audit19,
3072
- audit6
3335
+ audit3,
3336
+ audit4,
3337
+ audit22,
3338
+ audit8
3073
3339
  ];
3074
3340
  function registerAllAudits() {
3075
- for (const audit23 of AUDITS) {
3076
- registerAudit(audit23);
3341
+ for (const audit26 of AUDITS) {
3342
+ registerAudit(audit26);
3077
3343
  }
3078
3344
  }
3079
3345