@elementor/editor-audits 4.4.0-1104 → 4.4.0-1106

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.d.mts CHANGED
@@ -46,7 +46,8 @@ type PageContextResponse = {
46
46
  post_title: string | null;
47
47
  post_excerpt: string | null;
48
48
  featured_image_id: number | null;
49
- image_sizes: Record<number, {
49
+ /** Keyed by the composite `buildImageSizeKey` result (`"{attachmentId}:{size}"`), since one attachment can resolve to different files at different sizes. */
50
+ image_sizes: Record<string, {
50
51
  width: number;
51
52
  height: number;
52
53
  filesize_bytes: number;
package/dist/index.d.ts CHANGED
@@ -46,7 +46,8 @@ type PageContextResponse = {
46
46
  post_title: string | null;
47
47
  post_excerpt: string | null;
48
48
  featured_image_id: number | null;
49
- image_sizes: Record<number, {
49
+ /** Keyed by the composite `buildImageSizeKey` result (`"{attachmentId}:{size}"`), since one attachment can resolve to different files at different sizes. */
50
+ image_sizes: Record<string, {
50
51
  width: number;
51
52
  height: number;
52
53
  filesize_bytes: number;
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,7 +135,7 @@ 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
 
@@ -320,6 +320,13 @@ function computeReport(documentId, results) {
320
320
  };
321
321
  }
322
322
 
323
+ // src/utils/atomic-background-image-sources.ts
324
+ var import_editor_elements = require("@elementor/editor-elements");
325
+ var import_editor_props2 = require("@elementor/editor-props");
326
+
327
+ // src/utils/atomic-image-sources.ts
328
+ var import_editor_props = require("@elementor/editor-props");
329
+
323
330
  // src/utils/walk.ts
324
331
  function walkElements(tree, visit) {
325
332
  const parents = [];
@@ -334,6 +341,89 @@ function recurse(nodes, parents, visit) {
334
341
  }
335
342
  }
336
343
 
344
+ // src/utils/atomic-image-sources.ts
345
+ var ATOMIC_IMAGE_PROP_KEYS = ["image", "poster"];
346
+ function walkAtomicImageSources(tree, visit) {
347
+ walkElements(tree, (node) => {
348
+ if (node.elType !== "widget") {
349
+ return;
350
+ }
351
+ for (const key of ATOMIC_IMAGE_PROP_KEYS) {
352
+ const media = extractAtomicImageMedia(node.settings[key]);
353
+ if (media) {
354
+ visit({ node, media });
355
+ }
356
+ }
357
+ });
358
+ }
359
+ function extractAtomicImageMedia(imageProp) {
360
+ const image = import_editor_props.imagePropTypeUtil.extract(imageProp);
361
+ if (!image) {
362
+ return null;
363
+ }
364
+ const src = import_editor_props.imageSrcPropTypeUtil.extract(image.src);
365
+ if (!src) {
366
+ return null;
367
+ }
368
+ const id = import_editor_props.imageAttachmentIdPropType.extract(src.id) ?? void 0;
369
+ const url = import_editor_props.urlPropTypeUtil.extract(src.url) ?? void 0;
370
+ if (!id && !url) {
371
+ return null;
372
+ }
373
+ const size = import_editor_props.stringPropTypeUtil.extract(image.size) ?? void 0;
374
+ return { id, url, size };
375
+ }
376
+
377
+ // src/utils/atomic-background-image-sources.ts
378
+ function walkAtomicBackgroundImageSources(tree, visit) {
379
+ walkElements(tree, (node) => {
380
+ const styles = (0, import_editor_elements.getElementStyles)(node.id);
381
+ if (!styles) {
382
+ return;
383
+ }
384
+ for (const media of extractBackgroundImageMedia(styles)) {
385
+ visit({ node, media });
386
+ }
387
+ });
388
+ }
389
+ function extractBackgroundImageMedia(styles) {
390
+ const sources = [];
391
+ for (const style of Object.values(styles)) {
392
+ for (const variant of style.variants) {
393
+ const background = import_editor_props2.backgroundPropTypeUtil.extract(variant.props.background);
394
+ const overlays = import_editor_props2.backgroundOverlayPropTypeUtil.extract(background?.["background-overlay"]) ?? [];
395
+ for (const overlay of overlays) {
396
+ const imageOverlay = import_editor_props2.backgroundImageOverlayPropTypeUtil.extract(overlay);
397
+ const media = extractAtomicImageMedia(imageOverlay?.image);
398
+ if (media) {
399
+ sources.push(media);
400
+ }
401
+ }
402
+ }
403
+ }
404
+ return sources;
405
+ }
406
+
407
+ // src/utils/background-image-sources.ts
408
+ var BACKGROUND_IMAGE_SETTING_KEYS = [
409
+ "background_image",
410
+ "background_hover_image",
411
+ "background_overlay_image",
412
+ "background_overlay_hover_image",
413
+ "_background_image",
414
+ "_background_hover_image"
415
+ ];
416
+ function walkBackgroundImageSources(tree, visit) {
417
+ walkElements(tree, (node) => {
418
+ for (const key of BACKGROUND_IMAGE_SETTING_KEYS) {
419
+ const media = node.settings[key];
420
+ if (media?.id || media?.url) {
421
+ visit({ node, media });
422
+ }
423
+ }
424
+ });
425
+ }
426
+
337
427
  // src/utils/image-like-sources.ts
338
428
  var IMAGE_LIKE_WIDGETS = /* @__PURE__ */ new Set(["image", "image-box", "image-carousel", "gallery", "image-gallery"]);
339
429
  function hasPageImages(tree) {
@@ -362,28 +452,38 @@ function collectMediaFromNode(node) {
362
452
  const sources = [];
363
453
  const image = node.settings.image;
364
454
  if (image?.id || image?.url) {
365
- sources.push(image);
455
+ sources.push({ ...image, size: node.settings.image_size });
366
456
  }
367
457
  const gallery = node.settings.carousel ?? node.settings.gallery ?? node.settings.wp_gallery;
368
458
  if (Array.isArray(gallery)) {
459
+ const gallerySize = node.settings.thumbnail_size;
369
460
  for (const item of gallery) {
370
461
  if (item?.id || item?.url) {
371
- sources.push(item);
462
+ sources.push({ ...item, size: gallerySize });
372
463
  }
373
464
  }
374
465
  }
375
466
  return sources;
376
467
  }
377
468
 
469
+ // src/utils/image-size-key.ts
470
+ function buildImageSizeKey({ id, size }) {
471
+ return `${id}:${size || "full"}`;
472
+ }
473
+
378
474
  // src/utils/page-attachments.ts
379
- function extractAttachmentIds(tree) {
380
- const ids = /* @__PURE__ */ new Set();
381
- walkImageLikeSources(tree, ({ media }) => {
475
+ function extractImageSizeRequests(tree) {
476
+ const keys = /* @__PURE__ */ new Set();
477
+ const collect = (media) => {
382
478
  if (media.id) {
383
- ids.add(media.id);
479
+ keys.add(buildImageSizeKey({ id: media.id, size: media.size }));
384
480
  }
385
- });
386
- return Array.from(ids).sort((a, b) => a - b);
481
+ };
482
+ walkImageLikeSources(tree, ({ media }) => collect(media));
483
+ walkBackgroundImageSources(tree, ({ media }) => collect(media));
484
+ walkAtomicImageSources(tree, ({ media }) => collect(media));
485
+ walkAtomicBackgroundImageSources(tree, ({ media }) => collect(media));
486
+ return Array.from(keys).sort();
387
487
  }
388
488
 
389
489
  // src/utils/read-kit-snapshot.ts
@@ -477,9 +577,9 @@ function toSnapshot(element) {
477
577
 
478
578
  // src/runner.ts
479
579
  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);
580
+ const tree = buildSnapshotTree((0, import_editor_elements2.getElements)());
581
+ const imageSizeRequests = extractImageSizeRequests(tree);
582
+ const pageContext = await fetchPageContext(documentId, imageSizeRequests);
483
583
  const elements = { documentId, tree };
484
584
  const kit = await readKitSnapshot(pageContext.kit_id);
485
585
  const ctx = { documentId, elements, pageContext, kit };
@@ -563,7 +663,7 @@ function useAuditReport() {
563
663
  const report = (0, import_store2.__useSelector)((state) => selectReport(state));
564
664
  const error = (0, import_store2.__useSelector)((state) => selectError(state));
565
665
  const dispatch = (0, import_store2.__useDispatch)();
566
- const documentId = (0, import_editor_elements2.getCurrentDocumentId)() ?? 0;
666
+ const documentId = (0, import_editor_elements3.getCurrentDocumentId)() ?? 0;
567
667
  (0, import_react.useEffect)(() => {
568
668
  if (!documentId || report?.documentId === documentId) {
569
669
  return;
@@ -820,20 +920,20 @@ function SubpageHeader({ title, onBack, backLabel, icon }) {
820
920
  // src/components/violation-row.tsx
821
921
  var React11 = __toESM(require("react"));
822
922
  var import_react4 = require("react");
823
- var import_editor_elements4 = require("@elementor/editor-elements");
923
+ var import_editor_elements5 = require("@elementor/editor-elements");
824
924
  var import_icons7 = require("@elementor/icons");
825
925
  var import_ui10 = require("@elementor/ui");
826
926
  var import_i18n11 = require("@wordpress/i18n");
827
927
 
828
928
  // src/hooks/focus-violation.ts
829
- var import_editor_elements3 = require("@elementor/editor-elements");
929
+ var import_editor_elements4 = require("@elementor/editor-elements");
830
930
  var import_editor_v1_adapters2 = require("@elementor/editor-v1-adapters");
831
931
  var SCROLL_TO_VIEW_DELAY_MS = 200;
832
932
  function focusViolation(violation) {
833
933
  if (violation.elementId) {
834
- const container = (0, import_editor_elements3.getContainer)(violation.elementId);
934
+ const container = (0, import_editor_elements4.getContainer)(violation.elementId);
835
935
  if (container) {
836
- (0, import_editor_elements3.selectElement)(violation.elementId);
936
+ (0, import_editor_elements4.selectElement)(violation.elementId);
837
937
  const domElement = container.view?.getDomElement?.();
838
938
  if (domElement) {
839
939
  window.elementor?.helpers?.scrollToView?.(domElement, SCROLL_TO_VIEW_DELAY_MS);
@@ -1036,8 +1136,8 @@ function ViolationRow({ audit: audit23, skipReason, violations }) {
1036
1136
  /* @__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
1137
  /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", component: "p", color: "text.secondary" }, audit23.fixHint)
1038
1138
  )), 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;
1139
+ const widgetIcon = violation.elementId ? (0, import_editor_elements5.getElementIcon)(violation.elementId) : null;
1140
+ const elementTitle = violation.elementId ? (0, import_editor_elements5.getElementTitle)(violation.elementId) : null;
1041
1141
  const rowLabel = elementTitle ? `${elementTitle} - ${violation.label}` : violation.label;
1042
1142
  return /* @__PURE__ */ React11.createElement(
1043
1143
  import_ui10.Box,
@@ -1768,7 +1868,7 @@ function ReportShell({ report }) {
1768
1868
  // src/components/audit-panel.tsx
1769
1869
  function AuditPanel() {
1770
1870
  const { status, report, error, run } = useAuditReport();
1771
- const currentDocumentId = (0, import_editor_elements5.getCurrentDocumentId)() ?? 0;
1871
+ const currentDocumentId = (0, import_editor_elements6.getCurrentDocumentId)() ?? 0;
1772
1872
  const onRun = () => run(currentDocumentId);
1773
1873
  const lastScanLabel = report ? new Date(report.runAt).toLocaleTimeString() : null;
1774
1874
  return /* @__PURE__ */ React25.createElement(React25.Fragment, null, /* @__PURE__ */ React25.createElement(
@@ -1899,7 +1999,7 @@ var audit2 = {
1899
1999
  };
1900
2000
 
1901
2001
  // src/audits/deep-nesting.ts
1902
- var import_editor_elements6 = require("@elementor/editor-elements");
2002
+ var import_editor_elements7 = require("@elementor/editor-elements");
1903
2003
  var import_i18n27 = require("@wordpress/i18n");
1904
2004
  var MAX_NESTING_DEPTH = 6;
1905
2005
  function isNestingElement(node, widgetsCache) {
@@ -1923,7 +2023,7 @@ var audit3 = {
1923
2023
  if (ctx.elements.tree.length === 0) {
1924
2024
  return { status: "skipped", reason: (0, import_i18n27.__)("No elements", "elementor") };
1925
2025
  }
1926
- const widgetsCache = (0, import_editor_elements6.getWidgetsCache)();
2026
+ const widgetsCache = (0, import_editor_elements7.getWidgetsCache)();
1927
2027
  const violations = [];
1928
2028
  walkElements(ctx.elements.tree, (node, parents) => {
1929
2029
  if (!isNestingElement(node, widgetsCache)) {
@@ -1974,7 +2074,7 @@ var audit4 = {
1974
2074
  };
1975
2075
 
1976
2076
  // src/audits/deprecated-widgets.ts
1977
- var import_editor_elements7 = require("@elementor/editor-elements");
2077
+ var import_editor_elements8 = require("@elementor/editor-elements");
1978
2078
  var import_i18n29 = require("@wordpress/i18n");
1979
2079
  var DEPRECATION_CONTROL_NAME = "deprecation_message";
1980
2080
  var audit5 = {
@@ -1989,7 +2089,7 @@ var audit5 = {
1989
2089
  severity: "warning",
1990
2090
  weight: 7,
1991
2091
  evaluate: (ctx) => {
1992
- const widgetsCache = (0, import_editor_elements7.getWidgetsCache)();
2092
+ const widgetsCache = (0, import_editor_elements8.getWidgetsCache)();
1993
2093
  if (!widgetsCache) {
1994
2094
  return { status: "skipped", reason: "widgets-cache-unavailable" };
1995
2095
  }
@@ -2387,7 +2487,8 @@ function hasMeaningfulAlt(media, pageContext) {
2387
2487
  return true;
2388
2488
  }
2389
2489
  if (media.id) {
2390
- const alt = pageContext.image_sizes[media.id]?.alt ?? "";
2490
+ const key = buildImageSizeKey({ id: media.id, size: media.size });
2491
+ const alt = pageContext.image_sizes[key]?.alt ?? "";
2391
2492
  return alt.trim().length > 0;
2392
2493
  }
2393
2494
  return (media.alt ?? "").trim().length > 0;
@@ -2445,17 +2546,18 @@ var audit10 = {
2445
2546
  severity: "warning",
2446
2547
  weight: 7,
2447
2548
  evaluate: (ctx) => {
2448
- if (!hasPageImages(ctx.elements.tree)) {
2449
- return { status: "skipped", reason: (0, import_i18n34.__)("No images", "elementor") };
2450
- }
2451
2549
  const widgetMaxKb = /* @__PURE__ */ new Map();
2452
2550
  let oversizedImageCount = 0;
2453
- walkImageLikeSources(ctx.elements.tree, ({ node, media }) => {
2551
+ let hasAnyImage = false;
2552
+ const evaluateSource = (node, media) => {
2553
+ if (media.id || media.url) {
2554
+ hasAnyImage = true;
2555
+ }
2454
2556
  const id = media.id;
2455
2557
  if (!id) {
2456
2558
  return;
2457
2559
  }
2458
- const size = ctx.pageContext.image_sizes[id];
2560
+ const size = ctx.pageContext.image_sizes[buildImageSizeKey({ id, size: media.size })];
2459
2561
  if (!size || size.filesize_bytes <= SIZE_THRESHOLD_BYTES) {
2460
2562
  return;
2461
2563
  }
@@ -2463,7 +2565,14 @@ var audit10 = {
2463
2565
  const kb = Math.round(size.filesize_bytes / BYTES_PER_KB);
2464
2566
  const currentMax = widgetMaxKb.get(node.id) ?? 0;
2465
2567
  widgetMaxKb.set(node.id, Math.max(currentMax, kb));
2466
- });
2568
+ };
2569
+ walkImageLikeSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2570
+ walkBackgroundImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2571
+ walkAtomicImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2572
+ walkAtomicBackgroundImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2573
+ if (!hasAnyImage) {
2574
+ return { status: "skipped", reason: (0, import_i18n34.__)("No images", "elementor") };
2575
+ }
2467
2576
  const violations = Array.from(widgetMaxKb.entries()).map(([elementId, kb]) => ({
2468
2577
  auditId: audit10.id,
2469
2578
  elementId,
@@ -3003,7 +3112,7 @@ var audit21 = {
3003
3112
  };
3004
3113
 
3005
3114
  // src/audits/too-many-widgets.ts
3006
- var import_editor_elements8 = require("@elementor/editor-elements");
3115
+ var import_editor_elements9 = require("@elementor/editor-elements");
3007
3116
  var import_i18n46 = require("@wordpress/i18n");
3008
3117
  var WIDGET_COUNT_THRESHOLD = 100;
3009
3118
  function isCountableWidget(node, widgetsCache) {
@@ -3024,7 +3133,7 @@ var audit22 = {
3024
3133
  if (ctx.elements.tree.length === 0) {
3025
3134
  return { status: "skipped", reason: (0, import_i18n46.__)("No elements", "elementor") };
3026
3135
  }
3027
- const widgetsCache = (0, import_editor_elements8.getWidgetsCache)();
3136
+ const widgetsCache = (0, import_editor_elements9.getWidgetsCache)();
3028
3137
  let widgetCount = 0;
3029
3138
  walkElements(ctx.elements.tree, (node) => {
3030
3139
  if (isCountableWidget(node, widgetsCache)) {