@adobe/helix-html-pipeline 6.30.1 → 6.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [6.31.0](https://github.com/adobe/helix-html-pipeline/compare/v6.30.1...v6.31.0) (2026-08-17)
2
+
3
+
4
+ ### Features
5
+
6
+ * update og:image from <a> also along with img ([9768a57](https://github.com/adobe/helix-html-pipeline/commit/9768a57e5d96aef18af081e4f9aec67ba9a4d586))
7
+
1
8
  ## [6.30.1](https://github.com/adobe/helix-html-pipeline/compare/v6.30.0...v6.30.1) (2026-08-17)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/helix-html-pipeline",
3
- "version": "6.30.1",
3
+ "version": "6.31.0",
4
4
  "description": "Helix HTML Pipeline",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -19,6 +19,48 @@ import {
19
19
  import { toMetaName } from '../utils/modifiers.js';
20
20
  import { childNodes } from '../utils/hast-utils.js';
21
21
 
22
+ // common web image extensions
23
+ const IMAGE_EXTENSIONS = new Set(['avif', 'webp', 'png', 'jpg', 'jpeg', 'gif']);
24
+
25
+ /**
26
+ * Checks whether an anchor points directly to an image, based on its href ending in a
27
+ * recognizable image extension.
28
+ * @param {Element} $a The anchor element
29
+ * @returns {boolean} true if the anchor should be treated as an image link
30
+ */
31
+ function isImageAnchor($a) {
32
+ try {
33
+ // parse via URL (with a dummy base for relative hrefs) so the extension check is based
34
+ // on the pathname alone, ignoring any query string or fragment
35
+ const { pathname } = new URL($a.properties.href, 'https://localhost/');
36
+ const ext = pathname.slice(pathname.lastIndexOf('.') + 1).toLowerCase();
37
+ return IMAGE_EXTENSIONS.has(ext);
38
+ } catch {
39
+ // href isn't a URL that can be parsed (e.g. mailto:, malformed)
40
+ return false;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Checks whether two URL-ish strings refer to the same resource, e.g. so that a link
46
+ * text that only differs from its href by encoding (say, a literal space vs. "%20") isn't
47
+ * mistaken for a real caption. Falls back to plain string equality if either side can't be
48
+ * parsed as a URL - a genuine caption isn't expected to parse as one anyway.
49
+ * @param {string} a first URL-ish string
50
+ * @param {string} b second URL-ish string
51
+ * @returns {boolean} true if a and b are the same URL, ignoring encoding differences
52
+ */
53
+ function isSameUrl(a, b) {
54
+ if (a === b) {
55
+ return true;
56
+ }
57
+ try {
58
+ return new URL(a, 'https://localhost/').href === new URL(b, 'https://localhost/').href;
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+
22
64
  /**
23
65
  * Cleans up comma-separated string lists and returns an array.
24
66
  * @param {string} list A comma-separated list
@@ -262,11 +304,32 @@ export default function extractMetaData(state, req) {
262
304
 
263
305
  // content.image is not correct if the first image is in a page-block. since the pipeline
264
306
  // only respects the image nodes in the mdast
265
- const $hero = select('div img', hast);
307
+ //
308
+ // Approach B (https://www.aem.live/docs/media#approach-b-asset-management-delivery)
309
+ // images are authored as <a> links, not <img>, so they never show up as image nodes -
310
+ // match the anchor too, via isImageAnchor, which recognizes any href pointing directly
311
+ // at an image file by its extension, regardless of the asset source. Gathered in document
312
+ // order (rather than trying img first, falling back to the anchor only if no img exists at
313
+ // all) so whichever appears first in the page wins - otherwise an unrelated <img> elsewhere
314
+ // on the page (e.g. a card thumbnail below the actual hero anchor) would incorrectly take
315
+ // priority over the real hero.
316
+ const $hero = selectAll('div img, div a[href]', hast)
317
+ .find(($el) => $el.tagName === 'img' || isImageAnchor($el));
266
318
  if ($hero) {
267
- content.image = $hero.properties.src;
268
- if ($hero.properties.alt) {
269
- content.imageAlt = $hero.properties.alt;
319
+ if ($hero.tagName === 'a') {
320
+ content.image = $hero.properties.href;
321
+ // prefer the anchor's title attribute as alt text; fall back to the link text, unless
322
+ // it's empty or authors pasted the raw URL as the caption (see the "no caption" test) -
323
+ // neither is meaningful to read out, so leave it unset rather than exposing the raw URL
324
+ // to assistive tech and social crawlers.
325
+ const text = toString($hero).trim();
326
+ content.imageAlt = $hero.properties.title
327
+ || (text && !isSameUrl(text, $hero.properties.href) ? text : undefined);
328
+ } else {
329
+ content.image = $hero.properties.src;
330
+ if ($hero.properties.alt) {
331
+ content.imageAlt = $hero.properties.alt;
332
+ }
270
333
  }
271
334
  }
272
335