-
Notifications
You must be signed in to change notification settings - Fork 0
HTML Mapping
HTMLMapper.toComponents(html, params?) turns an HTML string into a typed Component[]. This page explains the pipeline and the default rules; configuration is on Custom Mappings.
← Back to Home · Related: Custom Mappings · Component Types
import { HTMLMapper } from '@canvasflow/feed';
const components = HTMLMapper.toComponents(html, params, root /* optional */);
const scopedHtml = HTMLMapper.getRootElement(html, rootMapping); // string | null| Method | Returns | Purpose |
|---|---|---|
toComponents(html, params?, root?) |
Component[] |
The full HTML → components conversion, with optional root-element scoping. |
getRootElement(html, mapping) |
string | null |
Serialize the first element matching mapping (used to scope extraction). |
The HTML pipeline parses the input exactly once and applies all transformations as pure Node[] passes on that single tree:
-
Parse with
linkedom(viaparser.ts) into aNode[]AST. -
stripBreaklines— strips\r\n/\n/\rfrom text content and attribute values; drops text nodes that become empty. -
hoistAnchorsWithImages— lifts<a>elements containing<img>descendants out of enclosing<p>/heading blocks. -
splitImagesFromParagraphs— splits<p>/h1–h6elements that have<img>direct children so each image becomes a sibling block. -
sanitizeInvalidAnchorHrefs— rewrites<a href>values that fail URL validation tohref="#". -
Root scoping (optional) — if a
rootmapping is passed,getRootElementlocates the matching subtree in the processed tree and narrows to[rootNode]before reduction. -
reduceComponents(params)— walks the node tree and emitsComponent[]; each node is serialized to HTML inline viasanitizeNodes()(no re-parse).
For each element the reducer tries, in order:
-
Exclusion — matches an
excludesmapping, or hasdata-cf-ignore→ element and children skipped. -
Unwrap — matches an
unwrapmapping → the element is dropped but its children are kept and evaluated in its place. Checked ahead of everything below, so it can pierce a wrapper whose tag would otherwise consume the subtree (e.g.<p>). -
Built-in detection — social embeds (Instagram, Twitter/X, TikTok, YouTube, Vimeo, Dailymotion, Infogram, Apple Podcasts),
<table>,<video>,<audio>,<iframe>, buttons, images (<img>,<picture>),<figure>(always produces aFigureContainerComponent), androle="gallery"/role="mosaic". -
Custom mappings — each
mappingsentry, in order; the first match wins. - Default text rules — the tag → text-component table below.
- Descend — otherwise ignore the element and evaluate its children.
A custom mapping is only tested against the element currently being visited — never against its descendants. Combined with step 5, that means a wrapper with a default text mapping swallows everything inside it;
unwrapis how you reach that content without editing the HTML.
| HTML | Component type |
|---|---|
h1 |
headline |
h2 |
title |
h3 |
subtitle |
h4 |
intro |
h5 |
crosshead |
h6 |
byline |
p |
body |
ol |
body |
ul |
body |
a |
body |
blockquote |
blockquote |
footer |
footer |
Any text element's role attribute overrides the default (e.g. <p role="crosshead"> → crosshead, <p role="text12"> → text12).
<hr> and <br> follow a similar but separate default rule — <hr> → divider, <br> → spacer (margin: "margin-20") — and can likewise be matched by any other element via a divider/spacer custom mapping.
Text components keep only phrasing content; styles and classes are stripped. On <a> elements only href, target, and rel survive. Whitespace-only text between inline elements is preserved as a non-breaking space so spacing in markup like <b>foo</b> <i>bar</i> is not collapsed.
| Content | Detected from |
|---|---|
| Image |
<img>, <picture> (uses the fallback <img>). |
| Figure |
<figure> — always produces a FigureContainerComponent (component: 'container', type: 'figure'). Caption and credit are extracted from a <figcaption>; credit nodes are identified by the <small> tag, role="credit", or class="credit". The contained media components (image, video, audio) are nested under components. |
| Gallery |
role="gallery"/role="mosaic" container, or a custom gallery mapping. |
| Video |
<video> (src or first <source>); YouTube/Vimeo/Dailymotion via <iframe>. |
| Audio |
<audio>; Apple Podcasts via <iframe>. |
| Social |
blockquote/a markers for Instagram, Twitter/X, TikTok. |
| Table |
<table> → htmltable (restricted tag allow-list). |
| Button |
<a role="button"> or <button><a></button>. |
To recognise content that does not follow these conventions, define a custom mapping.
HTMLMapper.toComponents() does not rewrite relative src/imageurl values — it has no item URL to resolve them against. That resolution (image/gallery/video/audio URLs made absolute against an item's <link> origin) only happens one layer up, as part of RSSFeed.build(). See RSS Feeds.
Start here
Reference
Operations