UNPKG

@maizzle/framework

Version:

Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.

100 lines (99 loc) 2.91 kB
import { parse as parse$1 } from "../utils/ast/parser.js"; import { walk } from "../utils/ast/walker.js"; import { serialize } from "../utils/ast/serializer.js"; import "../utils/ast/index.js"; import { readFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; //#region src/transformers/inlineLink.ts /** * Inline `<link rel="stylesheet">` tags as `<style>` tags. * * - Local file paths are inlined when `filePath` is provided (resolved * relative to it). * - Remote URLs (`http://` / `https://`) are only inlined when the link * carries an `inline` attribute, e.g. `<link rel="stylesheet" inline href="…">`. * * @param html HTML string to transform. * @param filePath Path of the source file the HTML came from, used as the * base for resolving relative `href` values. Required for * local-file inlining; remote `inline` links work without it. * @returns The transformed HTML string. * * @example * import { inlineLink } from '@maizzle/framework' * * const out = await inlineLink( * '<link rel="stylesheet" href="./styles.css">', * '/path/to/template.html', * ) */ async function inlineLink(html, filePath) { return serialize(await inlineLinkDom(parse$1(html), filePath)); } /** * DOM-form of {@link inlineLink} used by the internal transformer pipeline. * Takes a parsed DOM, returns a parsed DOM — avoids redundant * serialize/parse round-trips when chained with other transformers. */ async function inlineLinkDom(dom, filePath) { const links = []; walk(dom, (node) => { if (node.name !== "link") return; const el = node; const attrs = el.attribs; if (attrs.rel !== "stylesheet" || !attrs.href) return; const parent = el.parent; if (parent && "children" in parent) { const index = parent.children.indexOf(el); if (index !== -1) links.push({ node: el, parent, index }); } else { const index = dom.indexOf(el); if (index !== -1) links.push({ node: el, parent: null, index }); } }); for (const { node, parent, index } of links) { const href = node.attribs.href; const isRemote = href.startsWith("http://") || href.startsWith("https://"); let css; if (isRemote) { if (!("inline" in node.attribs)) continue; try { css = await (await fetch(href)).text(); } catch { continue; } } else { if (!filePath) continue; try { css = readFileSync(resolve(dirname(filePath), href), "utf8"); } catch { continue; } } const styleNode = { type: "tag", name: "style", attribs: {}, children: [{ type: "text", data: css, parent: null }], parent: parent || null }; styleNode.children[0].parent = styleNode; (parent && "children" in parent ? parent.children : dom).splice(index, 1, styleNode); } return dom; } //#endregion export { inlineLink, inlineLinkDom }; //# sourceMappingURL=inlineLink.js.map