Hwaro::Content::Seo::Amp
Constants
At-rules AMP accepts inside <style amp-custom>. Anything else
(@view-transition, @starting-style, @layer, @import, …) is a
CSS syntax error that fails the whole page.
Attributes that are legal on the HTML element but not on the AMP element it becomes, so they have to be dropped during conversion rather than carried through with the rest of the attribute string.
loading is the one hwaro emits itself: the built-in youtube /
vimeo / codepen / figure shortcodes hard-code loading="lazy", the
image pipeline adds it, and [markdown] lazy_loading adds it to
every content image. None of amp-img, amp-iframe or amp-video
accepts it — AMP does its own lazy loading — so every AMP page
carrying an image or an embed failed validation on a DISALLOWED_ATTR
the author never wrote. Scoped to the converted tag's attribute
string (not a document-wide gsub) so a loading= appearing inside
<style amp-custom> or JSON-LD text is untouched. decoding and
fetchpriority are the image hints themes write on hero/card
images; amp-img rejects both the same way.
What follows an end tag's name, through its >, in any form
(</iframe >, </iframe x="a>b">): an exact one ran on to the next
element's. A quote opens a value only after =, as in a start tag.
Neither runs past a <: every </iframe before an unclosed tail
rescanned to the end of the page (quadratic).
Font providers AMP allows via <link rel="stylesheet">. Every other
external stylesheet is disallowed and is stripped during conversion.
Quote-aware tag scans: media="(width >= 600px)" or
title="a > b" does not end the tag.
Class methods
Convert standard HTML to AMP-compliant HTML
The path_prefix AMP will actually publish under, or nil when it
must not publish at all. Both refusals below would otherwise let an
AMP variant overwrite the very page it was converted from:
- a "."/".." prefix is not empty, so the emptiness guard never fires for it, yet File.join resolves it straight back to the canonical path — with a rel="amphtml" pointing at itself. Refuse outright, the way every other emitter refuses a traversing output path, rather than reinterpreting the author's intent. The prefix itself is left verbatim (it is also a URL segment, so a literal percent-encoded directory name must survive unchanged); only the decision to proceed is taken on the traversal-safe reading of it;
- a blank/slash-only prefix makes mirror_path collapse onto the canonical path (File.join drops empty components).
Public because the stale-output prune has to resolve the same prefix
to find the mirror of a page that no longer exists; warn: keeps the
log lines on the generating call only.
Generate AMP versions of rendered pages. Reads the already-rendered HTML files, converts to AMP-compliant HTML, and writes them under the configured path prefix.
The mirror generate writes for page, or nil when it writes none
(AMP off, section not enabled, draft/generated/unrendered/redirect page, or
a refused prefix). Same gates as generate, so the builder can
count the mirror among the files a page owns — and delete it with
the page.
The AMP mirror of a canonical output file: the same path with
prefix inserted directly after the output directory. The one
spelling of the AMP layout — generate mirrors each page through it,
and the Finalize phase's stale-output prune mirrors the recorded
output path of a page that is gone, so the two cannot disagree about
where a mirror lives.
Both sides are expanded first: a page's recorded output path is
absolute (OutputGuard resolves it) while output_dir is whatever the
caller passed, usually the relative "public" — and relative_to
across those two spellings raises instead of matching, which silently
left every AMP mirror behind. The result is rejoined onto the
caller's own spelling so it stays comparable with the paths around it.
True when src resolves to the same origin as the AMP document.
Unknown means same-origin. An empty or unreadable src is the one
case we must not guess cross-origin on: guessing wrong there hands
allow-same-origin to a same-origin frame, which is precisely the
AMP violation this function exists to prevent. Guessing the other
way only drops a privilege.
Default sandbox for a converted iframe.
AMP: "An amp-iframe must not be in the same origin as the container
unless they do not allow allow-same-origin in the sandbox
attribute." Third-party embeds (YouTube, CodePen, Gist) are
cross-origin and need allow-same-origin to function, but adding it
unconditionally makes any iframe pointing back at the site itself
fail AMP validation outright.