@react-email/render
Version:
Transform React components into HTML email templates
180 lines (173 loc) • 5.67 kB
JavaScript
//#region rolldown:runtime
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
let react = require("react");
react = __toESM(react);
let prettier_plugins_html = require("prettier/plugins/html");
prettier_plugins_html = __toESM(prettier_plugins_html);
let prettier_standalone = require("prettier/standalone");
prettier_standalone = __toESM(prettier_standalone);
let html_to_text = require("html-to-text");
html_to_text = __toESM(html_to_text);
let node_stream = require("node:stream");
node_stream = __toESM(node_stream);
let react_jsx_runtime = require("react/jsx-runtime");
react_jsx_runtime = __toESM(react_jsx_runtime);
//#region src/shared/utils/pretty.ts
function recursivelyMapDoc(doc, callback) {
if (Array.isArray(doc)) return doc.map((innerDoc) => recursivelyMapDoc(innerDoc, callback));
if (typeof doc === "object") {
if (doc.type === "group") return {
...doc,
contents: recursivelyMapDoc(doc.contents, callback),
expandedStates: recursivelyMapDoc(doc.expandedStates, callback)
};
if ("contents" in doc) return {
...doc,
contents: recursivelyMapDoc(doc.contents, callback)
};
if ("parts" in doc) return {
...doc,
parts: recursivelyMapDoc(doc.parts, callback)
};
if (doc.type === "if-break") return {
...doc,
breakContents: recursivelyMapDoc(doc.breakContents, callback),
flatContents: recursivelyMapDoc(doc.flatContents, callback)
};
}
return callback(doc);
}
const modifiedHtml = { ...prettier_plugins_html };
if (modifiedHtml.printers) {
const previousPrint = modifiedHtml.printers.html.print;
modifiedHtml.printers.html.print = (path, options, print, args) => {
const node = path.getNode();
const rawPrintingResult = previousPrint(path, options, print, args);
if (node.type === "ieConditionalComment") return recursivelyMapDoc(rawPrintingResult, (doc) => {
if (typeof doc === "object" && doc.type === "line") return doc.soft ? "" : " ";
return doc;
});
return rawPrintingResult;
};
}
const defaults = {
endOfLine: "lf",
tabWidth: 2,
plugins: [modifiedHtml],
bracketSameLine: true,
parser: "html"
};
const pretty = (str, options = {}) => {
return (0, prettier_standalone.format)(str.replaceAll("\0", ""), {
...defaults,
...options
});
};
//#endregion
//#region src/shared/utils/to-plain-text.ts
const plainTextSelectors = [
{
selector: "img",
format: "skip"
},
{
selector: "[data-skip-in-text=true]",
format: "skip"
},
{
selector: "a",
options: {
linkBrackets: false,
hideLinkHrefIfSameAsText: true
}
}
];
function toPlainText(html, options) {
return (0, html_to_text.convert)(html, {
selectors: plainTextSelectors,
...options
});
}
//#endregion
//#region src/node/read-stream.ts
const decoder = new TextDecoder("utf-8");
const readStream = async (stream) => {
let result = "";
if ("pipeTo" in stream) {
const writableStream = new WritableStream({ write(chunk) {
result += decoder.decode(chunk);
} });
await stream.pipeTo(writableStream);
} else {
const writable = new node_stream.Writable({ write(chunk, _encoding, callback) {
result += decoder.decode(chunk);
callback();
} });
stream.pipe(writable);
await new Promise((resolve, reject) => {
writable.on("error", reject);
writable.on("close", () => {
resolve();
});
});
}
return result;
};
//#endregion
//#region src/node/render.tsx
const render = async (node, options) => {
const suspendedElement = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, { children: node });
const reactDOMServer = await import("react-dom/server").then((m) => m.default);
let html;
if (Object.hasOwn(reactDOMServer, "renderToReadableStream")) html = await readStream(await reactDOMServer.renderToReadableStream(suspendedElement, { progressiveChunkSize: Number.POSITIVE_INFINITY }));
else await new Promise((resolve, reject) => {
const stream = reactDOMServer.renderToPipeableStream(suspendedElement, {
async onAllReady() {
html = await readStream(stream);
resolve();
},
onError(error) {
reject(error);
},
progressiveChunkSize: Number.POSITIVE_INFINITY
});
});
if (options?.plainText) return toPlainText(html, options.htmlToTextOptions);
const document = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">${html.replace(/<!DOCTYPE.*?>/, "")}`;
if (options?.pretty) return pretty(document);
return document;
};
//#endregion
//#region src/node/index.ts
/**
* @deprecated use {@link render}
*/
const renderAsync = (element, options) => {
return render(element, options);
};
//#endregion
exports.plainTextSelectors = plainTextSelectors;
exports.pretty = pretty;
exports.render = render;
exports.renderAsync = renderAsync;
exports.toPlainText = toPlainText;