Skip to content

fix(core): read past lazy-loading placeholders in Layer C - #16

Merged
hami9 merged 1 commit into
mainfrom
claude/focused-chaum-7ce2c2
Aug 25, 2026
Merged

fix(core): read past lazy-loading placeholders in Layer C#16
hami9 merged 1 commit into
mainfrom
claude/focused-chaum-7ce2c2

Conversation

@hami9

@hami9 hami9 commented Aug 24, 2026

Copy link
Copy Markdown
Owner

The bug

A learned profile (Layer C) records src for a product image, because that is the attribute the element the user clicked appeared to hold. On a lazy-loading theme, src holds a 1x1 data: placeholder and the real URL sits in a data attribute. readRule() in packages/core/src/learn/apply.ts read found.attr(attribute) raw, so every product row in the export got the same grey pixel — or an empty cell.

Layer B (microdata) already handled this correctly, via a private imageUrl() helper inside microdata.ts. Layer C did not, because the helper was not shared.

The fix

  • Move the logic to packages/core/src/extract/html.ts as an exported readImageUrl(node).
  • microdata.ts calls it instead of its private copy — no behaviour change there.
  • apply.ts calls it when the profile's recorded attribute is src.

Resolving this at apply time rather than at learn time repairs profiles that were already taught and saved, which is most of them.

When no real URL exists anywhere on the element, the field stays empty. An empty cell is honest; a grey pixel imported into a shop is not. That path is covered by a test.

A theme does not lazy-load differently depending on whether the page also published JSON-LD, so any layer reading src on its own would be the one returning placeholders. Routing every <img> read through one function is what keeps that from recurring.

Attribute names now live in one place

packages/core/src/inspect/images.ts (phase 13) kept its own copy of the lazy attribute names. Its job is genuinely different — it wants every URL an element references, not the first usable one — but the attribute names do not depend on who is asking. It now imports LAZY_IMAGE_ATTRS and SRCSET_ATTRS from html.ts.

Two small additive consequences, both improvements:

  • images.ts gains data-lazy, which its local list was missing.
  • readImageUrl gains a data-lazy-srcset fallback, which only fires when nothing else matched.

Tests

Two new cases in packages/core/test/learn/learn.test.ts cover the lazy fallbacks (data-src, data-lazy-src, srcset) and the placeholder-only case.

npm run check is green: format, lint, typecheck, and 802 tests (800 before, plus the 2 new).

🤖 Generated with Claude Code

A learned profile records `src` for a product image, because that is the
attribute the element the user clicked appeared to hold. On a lazy-loading
theme `src` holds a 1x1 `data:` placeholder and the real URL sits in a data
attribute, so `readRule()` returned the same grey pixel — or nothing — for
every row in the export.

Layer B already handled this, in a private `imageUrl()` inside microdata.ts.
Move it to extract/html.ts as an exported `readImageUrl()` and have both
layers call it. A theme does not lazy-load differently depending on whether
the page also published JSON-LD, so any layer reading `src` on its own would
be the one returning placeholders.

Resolving this at apply time rather than at learn time repairs profiles that
were already taught and saved, which is most of them. When no real URL exists
anywhere on the element the field stays empty: an empty cell is honest, a grey
pixel imported into a shop is not.

Also fold the lazy attribute names into one place. inspect/images.ts had its
own copy — it wants every URL an element references rather than the first
usable one, so the logic genuinely differs, but the attribute names do not.
It now imports LAZY_IMAGE_ATTRS and SRCSET_ATTRS from html.ts, which also
gains it `data-lazy` and gains readImageUrl a `data-lazy-srcset` fallback.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hami9
hami9 merged commit bad8778 into main Aug 25, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant