@maizzle/framework
Version:
Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.
100 lines (99 loc) • 2.91 kB
JavaScript
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