@depup/react-email__render 2.0.6-depup.0 → 2.1.0-depup.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,8 +2,9 @@ import * as html from "prettier/plugins/html";
2
2
  import { format } from "prettier/standalone";
3
3
  import { convert } from "html-to-text";
4
4
  import React, { Suspense } from "react";
5
+ import { decodeHTML, decodeHTMLAttribute } from "entities/lib/decode.js";
6
+ import { SyntaxKind, parse } from "html5parser";
5
7
  import { Fragment, jsx } from "react/jsx-runtime";
6
-
7
8
  //#region src/shared/utils/pretty.ts
8
9
  function getHtmlNode(path) {
9
10
  const topNode = path.node;
@@ -64,7 +65,6 @@ const pretty = (str, options = {}) => {
64
65
  ...options
65
66
  });
66
67
  };
67
-
68
68
  //#endregion
69
69
  //#region src/shared/utils/to-plain-text.ts
70
70
  const plainTextSelectors = [
@@ -82,16 +82,291 @@ const plainTextSelectors = [
82
82
  linkBrackets: false,
83
83
  hideLinkHrefIfSameAsText: true
84
84
  }
85
+ },
86
+ {
87
+ selector: "[data-text-format=\"dataTable\"]",
88
+ format: "dataTable"
85
89
  }
86
90
  ];
87
- function toPlainText(html$1, options) {
88
- return convert(html$1, {
91
+ function toPlainText(html, options) {
92
+ return convert(html, {
89
93
  wordwrap: false,
90
94
  ...options,
91
95
  selectors: [...plainTextSelectors, ...options?.selectors ?? []]
92
96
  });
93
97
  }
94
-
98
+ //#endregion
99
+ //#region src/shared/utils/unstable-to-plain-text.ts
100
+ const SKIPPED_TAGS = new Set([
101
+ "img",
102
+ "noscript",
103
+ "script",
104
+ "style",
105
+ "template"
106
+ ]);
107
+ const WHITESPACE_RUN = /([ \t\n\r\f\u200b]+)/;
108
+ const TAG_BLOCKS = {
109
+ article: {
110
+ open: 2,
111
+ close: 2
112
+ },
113
+ aside: {
114
+ open: 2,
115
+ close: 2
116
+ },
117
+ blockquote: {
118
+ open: 2,
119
+ close: 2,
120
+ prefix: {
121
+ first: "> ",
122
+ rest: "> "
123
+ }
124
+ },
125
+ div: {
126
+ open: 2,
127
+ close: 2
128
+ },
129
+ footer: {
130
+ open: 2,
131
+ close: 2
132
+ },
133
+ form: {
134
+ open: 2,
135
+ close: 2
136
+ },
137
+ h1: {
138
+ open: 3,
139
+ close: 2
140
+ },
141
+ h2: {
142
+ open: 3,
143
+ close: 2
144
+ },
145
+ h3: {
146
+ open: 3,
147
+ close: 2
148
+ },
149
+ h4: {
150
+ open: 3,
151
+ close: 2
152
+ },
153
+ h5: {
154
+ open: 3,
155
+ close: 2
156
+ },
157
+ h6: {
158
+ open: 3,
159
+ close: 2
160
+ },
161
+ header: {
162
+ open: 2,
163
+ close: 2
164
+ },
165
+ hr: {
166
+ open: 2,
167
+ close: 2
168
+ },
169
+ main: {
170
+ open: 2,
171
+ close: 2
172
+ },
173
+ nav: {
174
+ open: 2,
175
+ close: 2
176
+ },
177
+ p: {
178
+ open: 2,
179
+ close: 2
180
+ },
181
+ pre: {
182
+ open: 2,
183
+ close: 2
184
+ },
185
+ section: {
186
+ open: 2,
187
+ close: 2
188
+ },
189
+ table: {
190
+ open: 2,
191
+ close: 2
192
+ }
193
+ };
194
+ function unstableToPlainText(html) {
195
+ const tree = parse(html, { setAttributeMap: true });
196
+ const body = findBody(tree);
197
+ const blocks = [{
198
+ block: {
199
+ open: 0,
200
+ close: 0
201
+ },
202
+ text: [],
203
+ leading: 0,
204
+ stash: 0,
205
+ space: false
206
+ }];
207
+ const stack = [{
208
+ parent: body,
209
+ children: body?.body ?? tree,
210
+ index: 0,
211
+ pre: false,
212
+ opened: false,
213
+ textFrom: 0,
214
+ orderedList: void 0
215
+ }];
216
+ while (stack.length > 0) {
217
+ const frame = stack[stack.length - 1];
218
+ const node = frame.children[frame.index];
219
+ if (node === void 0) {
220
+ stack.pop();
221
+ exitElement(frame.parent, frame);
222
+ continue;
223
+ }
224
+ frame.index += 1;
225
+ enterNode(node, frame);
226
+ }
227
+ function top() {
228
+ return blocks[blocks.length - 1];
229
+ }
230
+ function writeWord(value) {
231
+ const block = top();
232
+ if (block.stash > 0) block.text.push("\n".repeat(block.stash));
233
+ else if (block.space && block.text.length > 0) block.text.push(" ");
234
+ block.stash = 0;
235
+ block.space = false;
236
+ block.text.push(value);
237
+ }
238
+ function enterNode(node, frame) {
239
+ if (node.type === SyntaxKind.Text) {
240
+ const value = decodeHTML(node.value);
241
+ if (frame.pre) {
242
+ if (value.length > 0) writeWord(value);
243
+ } else {
244
+ const segments = value.split(WHITESPACE_RUN);
245
+ for (let i = 0; i < segments.length; i++) {
246
+ const segment = segments[i];
247
+ if (segment.length === 0) continue;
248
+ if (i % 2 === 1) top().space = true;
249
+ else writeWord(segment);
250
+ }
251
+ }
252
+ return;
253
+ }
254
+ if (SKIPPED_TAGS.has(node.name) || decodeHTMLAttribute(node.attributeMap?.["data-skip-in-text"]?.value?.value ?? "") === "true") return;
255
+ const parentTag = frame.parent?.name;
256
+ let block = TAG_BLOCKS[node.name];
257
+ let orderedList;
258
+ if (node.name === "ul") {
259
+ const breaks = parentTag === "li" ? 1 : 2;
260
+ block = {
261
+ open: breaks,
262
+ close: breaks
263
+ };
264
+ } else if (node.name === "li" && parentTag === "ul") block = {
265
+ open: 1,
266
+ close: 1,
267
+ prefix: (stack[stack.length - 2]?.parent)?.name === "li" ? {
268
+ first: "* ",
269
+ rest: " "
270
+ } : {
271
+ first: " * ",
272
+ rest: " "
273
+ }
274
+ };
275
+ else if (node.name === "ol") {
276
+ const nested = parentTag === "li";
277
+ const parsedStart = Number.parseInt(decodeHTMLAttribute(node.attributeMap?.start?.value?.value ?? "1"), 10);
278
+ const start = Number.isNaN(parsedStart) ? 1 : parsedStart;
279
+ const itemCount = node.body?.filter((child) => child.type === SyntaxKind.Tag && child.name === "li").length ?? 0;
280
+ let prefixLength = 0;
281
+ for (let index = start; index < start + itemCount; index++) {
282
+ const prefix = `${nested ? "" : " "}${index}. `;
283
+ prefixLength = Math.max(prefixLength, prefix.length);
284
+ }
285
+ const breaks = nested ? 1 : 2;
286
+ block = {
287
+ open: breaks,
288
+ close: breaks
289
+ };
290
+ orderedList = {
291
+ next: start,
292
+ prefixLength,
293
+ nested
294
+ };
295
+ } else if (node.name === "li" && parentTag === "ol" && frame.orderedList) {
296
+ const list = frame.orderedList;
297
+ block = {
298
+ open: 1,
299
+ close: 1,
300
+ prefix: {
301
+ first: `${list.nested ? "" : " "}${list.next++}. `.padEnd(list.prefixLength),
302
+ rest: " ".repeat(list.prefixLength)
303
+ }
304
+ };
305
+ }
306
+ if (block) blocks.push({
307
+ block,
308
+ text: [],
309
+ leading: block.open,
310
+ stash: 0,
311
+ space: false
312
+ });
313
+ if (node.name === "hr") writeWord("-".repeat(40));
314
+ else if (node.name === "br") {
315
+ top().space = false;
316
+ top().text.push("\n");
317
+ }
318
+ stack.push({
319
+ parent: node,
320
+ children: node.body ?? [],
321
+ index: 0,
322
+ pre: frame.pre || node.name === "pre",
323
+ opened: block !== void 0,
324
+ textFrom: top().text.length,
325
+ orderedList
326
+ });
327
+ }
328
+ function exitElement(element, frame) {
329
+ if (element === void 0) return;
330
+ if (element.name === "a") {
331
+ const href = decodeHTMLAttribute(element.attributeMap?.href?.value?.value ?? "").replace(/^mailto:/, "");
332
+ if (href.length > 0 && !href.startsWith("#")) {
333
+ const anchorText = top().text.slice(frame.textFrom).join("");
334
+ if (anchorText !== href) {
335
+ if (anchorText.length > 0) top().space = true;
336
+ writeWord(href);
337
+ }
338
+ }
339
+ }
340
+ if (frame.opened) closeBlock();
341
+ }
342
+ function closeBlock() {
343
+ const child = blocks.pop();
344
+ if (child === void 0) return;
345
+ const parent = blocks[blocks.length - 1];
346
+ let content = child.text.join("");
347
+ const prefix = child.block.prefix;
348
+ if (prefix !== void 0) {
349
+ const trimmed = content.replace(/^\n+|\n+$/g, "");
350
+ content = prefix.first + trimmed.replaceAll("\n", `\n${prefix.rest}`);
351
+ }
352
+ const breaks = Math.max(parent.stash, child.leading);
353
+ if (parent.text.length > 0) {
354
+ parent.text.push("\n".repeat(breaks));
355
+ if (content.length > 0) parent.text.push(content);
356
+ } else {
357
+ if (content.length > 0) parent.text.push(content);
358
+ parent.leading = breaks;
359
+ }
360
+ parent.stash = Math.max(child.stash, child.block.close);
361
+ }
362
+ return blocks[0].text.join("");
363
+ }
364
+ function findBody(tree) {
365
+ for (const child of tree) {
366
+ if (child.type !== SyntaxKind.Tag || child.name !== "html") continue;
367
+ for (const inner of child.body ?? []) if (inner.type === SyntaxKind.Tag && inner.name === "body") return inner;
368
+ }
369
+ }
95
370
  //#endregion
96
371
  //#region src/shared/error-boundary.tsx
97
372
  function createErrorBoundary(reject) {
@@ -105,7 +380,43 @@ function createErrorBoundary(reject) {
105
380
  }
106
381
  };
107
382
  }
108
-
383
+ //#endregion
384
+ //#region src/shared/utils/strip-image-preload-links.ts
385
+ /**
386
+ * React injects `<link rel="preload" as="image" />` resource hints into the
387
+ * document `<head>` for every `<img>` it renders during server-side rendering.
388
+ *
389
+ * These hints are meant for browsers loading a web page, where they can speed
390
+ * up the initial paint. In an email they are dead weight: email clients ignore
391
+ * `<link rel="preload">`, and the tags only add noise to the rendered HTML.
392
+ *
393
+ * @see https://github.com/resend/react-email/issues/3034
394
+ *
395
+ * This removes only those auto-injected image preload links, leaving every
396
+ * other `<link>` (stylesheets, fonts, user-authored non-image preloads, ...)
397
+ * untouched. It parses each `<link>` tag's attributes instead of relying on a
398
+ * fixed string match, so it is not affected by attribute order or spacing.
399
+ */
400
+ const stripImagePreloadLinks = (html) => {
401
+ return html.replace(/<link\b[^>]*\/?>/gi, (tag) => isImagePreloadLink(tag) ? "" : tag);
402
+ };
403
+ const isImagePreloadLink = (tag) => {
404
+ const attributes = parseAttributes(tag);
405
+ return attributes.rel === "preload" && attributes.as === "image";
406
+ };
407
+ const ATTRIBUTE_PATTERN = /([a-z][a-z0-9-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+)))?/gi;
408
+ /**
409
+ * Parses the attributes of a single, already-isolated HTML tag string (e.g.
410
+ * `<link rel="preload" as="image" href="..." />`) into a name → value map.
411
+ * Attribute names are lower-cased; values are read from double, single, or
412
+ * unquoted forms.
413
+ */
414
+ const parseAttributes = (tag) => {
415
+ const attributeSection = tag.replace(/^<[a-z][a-z0-9-]*/i, "");
416
+ const attributes = {};
417
+ for (const [, name, doubleQuoted, singleQuoted, unquoted] of attributeSection.matchAll(ATTRIBUTE_PATTERN)) attributes[name.toLowerCase()] = doubleQuoted ?? singleQuoted ?? unquoted ?? "";
418
+ return attributes;
419
+ };
109
420
  //#endregion
110
421
  //#region src/shared/read-stream.browser.ts
111
422
  const decoder = new TextDecoder("utf-8");
@@ -132,13 +443,11 @@ const readStream = async (stream) => {
132
443
  });
133
444
  return decoder.decode(mergedChunks);
134
445
  };
135
-
136
446
  //#endregion
137
447
  //#region src/edge/import-react-dom.tsx
138
448
  const importReactDom = () => {
139
449
  return import("react-dom/server.edge").catch(() => import("react-dom/server"));
140
450
  };
141
-
142
451
  //#endregion
143
452
  //#region src/edge/render.tsx
144
453
  const render = async (element, options) => {
@@ -146,7 +455,7 @@ const render = async (element, options) => {
146
455
  if ("default" in m) return m.default;
147
456
  return m;
148
457
  });
149
- const html$1 = await new Promise((resolve, reject) => {
458
+ const html = await new Promise((resolve, reject) => {
150
459
  const ErrorBoundary = createErrorBoundary(reject);
151
460
  reactDOMServer.renderToReadableStream(/* @__PURE__ */ jsx(ErrorBoundary, { children: /* @__PURE__ */ jsx(Suspense, { children: element }) }), {
152
461
  onError(error) {
@@ -156,14 +465,14 @@ const render = async (element, options) => {
156
465
  }).then(async (stream) => {
157
466
  await stream.allReady;
158
467
  return readStream(stream);
159
- }).then(resolve).catch(reject);
468
+ }).then((result) => resolve(stripImagePreloadLinks(result))).catch(reject);
160
469
  });
161
- if (options?.plainText) return toPlainText(html$1, options.htmlToTextOptions);
162
- const document = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">${html$1.replace(/<!DOCTYPE.*?>/, "")}`;
470
+ if (options?.plainText) return options.unstableTextConversion ? unstableToPlainText(html) : toPlainText(html, options.htmlToTextOptions);
471
+ 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.*?>/, "")}`;
163
472
  if (options?.pretty) return pretty(document);
164
473
  return document;
165
474
  };
166
-
167
475
  //#endregion
168
476
  export { plainTextSelectors, pretty, render, toPlainText };
477
+
169
478
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["html","html"],"sources":["../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/shared/error-boundary.tsx","../../src/shared/read-stream.browser.ts","../../src/edge/import-react-dom.tsx","../../src/edge/render.tsx"],"sourcesContent":["import type { Options, Plugin } from 'prettier';\nimport type { builders } from 'prettier/doc';\nimport * as html from 'prettier/plugins/html';\nimport { format } from 'prettier/standalone';\n\ninterface HtmlNode {\n type?: 'element' | 'text' | 'ieConditionalComment';\n kind?: 'element' | 'text' | 'ieConditionalComment' | 'root';\n name?: string;\n sourceSpan: {\n start: { file: unknown[]; offset: number; line: number; col: number };\n end: { file: unknown[]; offset: number; line: number; col: number };\n details: null;\n };\n parent?: HtmlNode;\n}\n\nfunction getHtmlNode(path: {\n node?: HtmlNode;\n stack?: Array<Record<string, unknown>>;\n}) {\n const topNode = path.node;\n if (topNode) {\n return topNode;\n }\n\n return path.stack?.[path.stack.length - 1] as HtmlNode;\n}\n\nfunction recursivelyMapDoc(\n doc: builders.Doc,\n callback: (innerDoc: string | builders.DocCommand) => builders.Doc,\n): builders.Doc {\n if (Array.isArray(doc)) {\n return doc.map((innerDoc) => recursivelyMapDoc(innerDoc, callback));\n }\n\n if (typeof doc === 'object') {\n if (doc.type === 'line') {\n return callback(doc.soft ? '' : ' ');\n }\n\n if (doc.type === 'group') {\n return {\n ...doc,\n contents: recursivelyMapDoc(doc.contents, callback),\n expandedStates: recursivelyMapDoc(\n doc.expandedStates,\n callback,\n ) as builders.Doc[],\n };\n }\n\n if ('contents' in doc) {\n return {\n ...doc,\n contents: recursivelyMapDoc(doc.contents, callback),\n };\n }\n\n if ('parts' in doc) {\n return {\n ...doc,\n parts: recursivelyMapDoc(doc.parts, callback) as builders.Doc[],\n };\n }\n\n if (doc.type === 'if-break') {\n return {\n ...doc,\n breakContents: recursivelyMapDoc(doc.breakContents, callback),\n flatContents: recursivelyMapDoc(doc.flatContents, callback),\n };\n }\n\n const nextDoc = { ...doc } as Record<string, unknown>;\n for (const [key, value] of Object.entries(nextDoc)) {\n if (value && typeof value === 'object') {\n nextDoc[key] = recursivelyMapDoc(value as builders.Doc, callback);\n }\n }\n\n return nextDoc as builders.Doc;\n }\n\n return callback(doc);\n}\n\nconst modifiedHtml = { ...html } as Plugin;\nif (modifiedHtml.printers) {\n const previousPrint = modifiedHtml.printers.html.print;\n modifiedHtml.printers.html.print = (path, options, print, args) => {\n const node = getHtmlNode(\n path as Parameters<Plugin['printers']['html']['print']>[0],\n );\n\n const rawPrintingResult = previousPrint(path, options, print, args);\n\n if (\n node?.type === 'ieConditionalComment' ||\n node?.kind === 'ieConditionalComment'\n ) {\n const printingResult = recursivelyMapDoc(rawPrintingResult, (doc) => {\n if (typeof doc === 'object' && doc.type === 'line') {\n return doc.soft ? '' : ' ';\n }\n\n return doc;\n });\n\n return printingResult;\n }\n\n return rawPrintingResult;\n };\n}\n\nconst defaults: Options = {\n endOfLine: 'lf',\n tabWidth: 2,\n plugins: [modifiedHtml],\n bracketSameLine: true,\n parser: 'html',\n};\n\nexport const pretty = (str: string, options: Options = {}) => {\n return format(str.replaceAll('\\0', ''), {\n ...defaults,\n ...options,\n });\n};\n","import {\n convert,\n type HtmlToTextOptions,\n type SelectorDefinition,\n} from 'html-to-text';\n\nexport const plainTextSelectors: SelectorDefinition[] = [\n { selector: 'img', format: 'skip' },\n { selector: '[data-skip-in-text=true]', format: 'skip' },\n {\n selector: 'a',\n options: { linkBrackets: false, hideLinkHrefIfSameAsText: true },\n },\n];\n\nexport function toPlainText(html: string, options?: HtmlToTextOptions) {\n return convert(html, {\n wordwrap: false,\n ...options,\n selectors: [...plainTextSelectors, ...(options?.selectors ?? [])],\n });\n}\n","import React from 'react';\n\nexport function createErrorBoundary(reject: (error: unknown) => void) {\n // React Server Components don't support React.Component, so it's just not defined here\n if (!React.Component) {\n return (props: { children?: React.ReactNode }) => <>{props.children}</>;\n }\n\n return class ErrorBoundary extends React.Component<{\n children: React.ReactNode;\n }> {\n componentDidCatch(error: unknown) {\n reject(error);\n }\n render() {\n return this.props.children;\n }\n };\n}\n","import type { ReactDOMServerReadableStream } from 'react-dom/server.browser';\n\nconst decoder = new TextDecoder('utf-8');\n\nexport const readStream = async (stream: ReactDOMServerReadableStream) => {\n const chunks: Uint8Array[] = [];\n\n const writableStream = new WritableStream({\n write(chunk: Uint8Array) {\n chunks.push(chunk);\n },\n abort(reason) {\n throw new Error('Stream aborted', {\n cause: {\n reason,\n },\n });\n },\n });\n await stream.pipeTo(writableStream);\n\n let length = 0;\n chunks.forEach((item) => {\n length += item.length;\n });\n const mergedChunks = new Uint8Array(length);\n let offset = 0;\n chunks.forEach((item) => {\n mergedChunks.set(item, offset);\n offset += item.length;\n });\n\n return decoder.decode(mergedChunks);\n};\n","export const importReactDom = () => {\n // We don't use async here because tsup converts it to a generator syntax\n // that esbuild doesn't understand as dealing with the import failing during\n // bundling: https://github.com/evanw/esbuild/issues/3216#issuecomment-1628913722\n return import('react-dom/server.edge').catch(\n () =>\n // This ensures that we still have compatibility with React 18,\n // which doesn't have the `.edge` export.\n import('react-dom/server'),\n );\n};\n","import { Suspense } from 'react';\nimport { pretty } from '../node';\nimport { createErrorBoundary } from '../shared/error-boundary';\nimport type { Options } from '../shared/options';\nimport { readStream } from '../shared/read-stream.browser';\nimport { toPlainText } from '../shared/utils/to-plain-text';\nimport { importReactDom } from './import-react-dom';\n\nexport const render = async (\n element: React.ReactElement,\n options?: Options,\n) => {\n const reactDOMServer = await importReactDom().then((m) => {\n if ('default' in m) {\n return m.default;\n }\n\n return m;\n });\n\n const html = await new Promise<string>((resolve, reject) => {\n const ErrorBoundary = createErrorBoundary(reject);\n reactDOMServer\n .renderToReadableStream(\n <ErrorBoundary>\n <Suspense>{element}</Suspense>\n </ErrorBoundary>,\n {\n onError(error: unknown) {\n reject(error);\n },\n progressiveChunkSize: Number.POSITIVE_INFINITY,\n },\n )\n .then(async (stream) => {\n await stream.allReady;\n return readStream(stream);\n })\n .then(resolve)\n .catch(reject);\n });\n\n if (options?.plainText) {\n return toPlainText(html, options.htmlToTextOptions);\n }\n\n const doctype =\n '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">';\n\n const document = `${doctype}${html.replace(/<!DOCTYPE.*?>/, '')}`;\n\n if (options?.pretty) {\n return pretty(document);\n }\n\n return document;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/shared/utils/unstable-to-plain-text.ts","../../src/shared/error-boundary.tsx","../../src/shared/utils/strip-image-preload-links.ts","../../src/shared/read-stream.browser.ts","../../src/edge/import-react-dom.tsx","../../src/edge/render.tsx"],"sourcesContent":["import type { Options, Plugin } from 'prettier';\nimport type { builders } from 'prettier/doc';\nimport * as html from 'prettier/plugins/html';\nimport { format } from 'prettier/standalone';\n\ninterface HtmlNode {\n type?: 'element' | 'text' | 'ieConditionalComment';\n kind?: 'element' | 'text' | 'ieConditionalComment' | 'root';\n name?: string;\n sourceSpan: {\n start: { file: unknown[]; offset: number; line: number; col: number };\n end: { file: unknown[]; offset: number; line: number; col: number };\n details: null;\n };\n parent?: HtmlNode;\n}\n\nfunction getHtmlNode(path: {\n node?: HtmlNode;\n stack?: Array<Record<string, unknown>>;\n}) {\n const topNode = path.node;\n if (topNode) {\n return topNode;\n }\n\n return path.stack?.[path.stack.length - 1] as unknown as HtmlNode;\n}\n\nfunction recursivelyMapDoc(\n doc: builders.Doc,\n callback: (innerDoc: string | builders.DocCommand) => builders.Doc,\n): builders.Doc {\n if (Array.isArray(doc)) {\n return doc.map((innerDoc) => recursivelyMapDoc(innerDoc, callback));\n }\n\n if (typeof doc === 'object') {\n if (doc.type === 'line') {\n return callback(doc.soft ? '' : ' ');\n }\n\n if (doc.type === 'group') {\n return {\n ...doc,\n contents: recursivelyMapDoc(doc.contents, callback),\n expandedStates: recursivelyMapDoc(\n doc.expandedStates,\n callback,\n ) as builders.Doc[],\n };\n }\n\n if ('contents' in doc) {\n return {\n ...doc,\n contents: recursivelyMapDoc(doc.contents, callback),\n };\n }\n\n if ('parts' in doc) {\n return {\n ...doc,\n parts: recursivelyMapDoc(doc.parts, callback) as builders.Doc[],\n };\n }\n\n if (doc.type === 'if-break') {\n return {\n ...doc,\n breakContents: recursivelyMapDoc(doc.breakContents, callback),\n flatContents: recursivelyMapDoc(doc.flatContents, callback),\n };\n }\n\n const nextDoc = { ...doc } as Record<string, unknown>;\n for (const [key, value] of Object.entries(nextDoc)) {\n if (value && typeof value === 'object') {\n nextDoc[key] = recursivelyMapDoc(value as builders.Doc, callback);\n }\n }\n\n return nextDoc as unknown as builders.Doc;\n }\n\n return callback(doc);\n}\n\nconst modifiedHtml = { ...html } as Plugin;\nif (modifiedHtml.printers) {\n const previousPrint = modifiedHtml.printers.html.print;\n modifiedHtml.printers.html.print = (path, options, print, args) => {\n const node = getHtmlNode(\n path as Parameters<NonNullable<Plugin['printers']>['html']['print']>[0],\n );\n\n const rawPrintingResult = previousPrint(path, options, print, args);\n\n if (\n node?.type === 'ieConditionalComment' ||\n node?.kind === 'ieConditionalComment'\n ) {\n const printingResult = recursivelyMapDoc(rawPrintingResult, (doc) => {\n if (typeof doc === 'object' && doc.type === 'line') {\n return doc.soft ? '' : ' ';\n }\n\n return doc;\n });\n\n return printingResult;\n }\n\n return rawPrintingResult;\n };\n}\n\nconst defaults: Options = {\n endOfLine: 'lf',\n tabWidth: 2,\n plugins: [modifiedHtml],\n bracketSameLine: true,\n parser: 'html',\n};\n\nexport const pretty = (str: string, options: Options = {}) => {\n return format(str.replaceAll('\\0', ''), {\n ...defaults,\n ...options,\n });\n};\n","import {\n convert,\n type HtmlToTextOptions,\n type SelectorDefinition,\n} from 'html-to-text';\n\nexport const plainTextSelectors: SelectorDefinition[] = [\n { selector: 'img', format: 'skip' },\n { selector: '[data-skip-in-text=true]', format: 'skip' },\n {\n selector: 'a',\n options: { linkBrackets: false, hideLinkHrefIfSameAsText: true },\n },\n { selector: '[data-text-format=\"dataTable\"]', format: 'dataTable' },\n];\n\nexport function toPlainText(html: string, options?: HtmlToTextOptions) {\n return convert(html, {\n wordwrap: false,\n ...options,\n selectors: [...plainTextSelectors, ...(options?.selectors ?? [])],\n });\n}\n","import { decodeHTML, decodeHTMLAttribute } from 'entities/lib/decode.js';\nimport { type INode, type ITag, parse, SyntaxKind } from 'html5parser';\n\nconst SKIPPED_TAGS = new Set([\n 'img',\n 'noscript',\n 'script',\n 'style',\n 'template',\n]);\n\nconst WHITESPACE_RUN = /([ \\t\\n\\r\\f\\u200b]+)/;\n\n// A block prefix puts `first` before the block's content and `rest` at the\n// start of every following line: blockquote marks every line (\"> \"), a list\n// item marks the first (\" * \") and indents the rest to align nested content.\ntype BlockPrefix = { first: string; rest: string };\n\n// Everything tag-specific about how a block separates from its surroundings.\ntype Block = { open: number; close: number; prefix?: BlockPrefix };\n\n// html-to-text's default separation for the block tags implemented so far.\n// Tags absent here (and not handled in `enterNode`) are treated as inline\n// and contribute no separation — notably td/tr, which is why table cells run\n// together, matching `toPlainText` today. Only extend this alongside a spec\n// case verifying the behavior against `toPlainText`.\nconst TAG_BLOCKS: Record<string, Block> = {\n article: { open: 2, close: 2 },\n aside: { open: 2, close: 2 },\n blockquote: { open: 2, close: 2, prefix: { first: '> ', rest: '> ' } },\n div: { open: 2, close: 2 },\n footer: { open: 2, close: 2 },\n form: { open: 2, close: 2 },\n h1: { open: 3, close: 2 },\n h2: { open: 3, close: 2 },\n h3: { open: 3, close: 2 },\n h4: { open: 3, close: 2 },\n h5: { open: 3, close: 2 },\n h6: { open: 3, close: 2 },\n header: { open: 2, close: 2 },\n hr: { open: 2, close: 2 },\n main: { open: 2, close: 2 },\n nav: { open: 2, close: 2 },\n p: { open: 2, close: 2 },\n pre: { open: 2, close: 2 },\n section: { open: 2, close: 2 },\n table: { open: 2, close: 2 },\n};\n\ninterface OpenBlock {\n block: Block;\n text: string[];\n leading: number;\n stash: number;\n space: boolean;\n}\n\ninterface OrderedList {\n next: number;\n prefixLength: number;\n nested: boolean;\n}\n\ninterface WalkFrame {\n // the element whose children this frame is iterating, or undefined at root\n parent: ITag | undefined;\n children: INode[];\n index: number;\n pre: boolean;\n // whether this element opened a block, to be closed back on exit\n opened: boolean;\n // content length of the enclosing block when this element was entered;\n // exit of an <a> reads back the text written since\n textFrom: number;\n orderedList: OrderedList | undefined;\n}\n\nexport function unstableToPlainText(html: string): string {\n const tree = parse(html, { setAttributeMap: true });\n const body = findBody(tree);\n\n const blocks: OpenBlock[] = [\n {\n block: { open: 0, close: 0 },\n text: [],\n leading: 0,\n stash: 0,\n space: false,\n },\n ];\n const stack: WalkFrame[] = [\n {\n parent: body,\n children: body?.body ?? tree,\n index: 0,\n pre: false,\n opened: false,\n textFrom: 0,\n orderedList: undefined,\n },\n ];\n\n while (stack.length > 0) {\n const frame = stack[stack.length - 1];\n const node = frame.children[frame.index];\n if (node === undefined) {\n // children exhausted: the element ends here, the only moment its\n // exit effects can go out — they must follow the subtree's content\n stack.pop();\n exitElement(frame.parent, frame);\n continue;\n }\n frame.index += 1;\n enterNode(node, frame);\n }\n\n function top(): OpenBlock {\n return blocks[blocks.length - 1];\n }\n\n function writeWord(value: string) {\n const block = top();\n if (block.stash > 0) block.text.push('\\n'.repeat(block.stash));\n else if (block.space && block.text.length > 0) block.text.push(' ');\n block.stash = 0;\n block.space = false;\n block.text.push(value);\n }\n\n // enterNode descends into an element by pushing its frame; skipped\n // subtrees are skipped by simply not pushing\n function enterNode(node: INode, frame: WalkFrame) {\n if (node.type === SyntaxKind.Text) {\n const value = decodeHTML(node.value);\n if (frame.pre) {\n // whitespace is significant inside <pre>: one verbatim word,\n // newlines and all, so it never goes through collapsing\n if (value.length > 0) {\n writeWord(value);\n }\n } else {\n // splitting on a capturing group alternates strictly: even indices\n // are the words, odd indices are the whitespace runs between them —\n // so parity alone says which, no need to re-match each segment\n const segments = value.split(WHITESPACE_RUN);\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i];\n if (segment.length === 0) continue;\n if (i % 2 === 1) {\n top().space = true;\n } else {\n writeWord(segment);\n }\n }\n }\n return;\n }\n if (\n SKIPPED_TAGS.has(node.name) ||\n decodeHTMLAttribute(\n node.attributeMap?.['data-skip-in-text']?.value?.value ?? '',\n ) === 'true'\n ) {\n return;\n }\n\n const parentTag = frame.parent?.name;\n\n let block: Block | undefined = TAG_BLOCKS[node.name];\n let orderedList: OrderedList | undefined;\n if (node.name === 'ul') {\n // a list directly inside an <li> sits closer to its parent item:\n // single line breaks both ways (html-to-text's isNestedList)\n const breaks = parentTag === 'li' ? 1 : 2;\n block = { open: breaks, close: breaks };\n } else if (node.name === 'li' && parentTag === 'ul') {\n // whether the enclosing list is itself nested decides the item's\n // prefix; `frame` belongs to that list, so the list's own parent is\n // one frame below the top of the stack\n const grandparent = stack[stack.length - 2]?.parent;\n const inNestedList = grandparent?.name === 'li';\n block = {\n open: 1,\n close: 1,\n prefix: inNestedList\n ? { first: '* ', rest: ' ' }\n : { first: ' * ', rest: ' ' },\n };\n } else if (node.name === 'ol') {\n const nested = parentTag === 'li';\n const parsedStart = Number.parseInt(\n decodeHTMLAttribute(node.attributeMap?.start?.value?.value ?? '1'),\n 10,\n );\n const start = Number.isNaN(parsedStart) ? 1 : parsedStart;\n const itemCount =\n node.body?.filter(\n (child) => child.type === SyntaxKind.Tag && child.name === 'li',\n ).length ?? 0;\n let prefixLength = 0;\n for (let index = start; index < start + itemCount; index++) {\n const prefix = `${nested ? '' : ' '}${index}. `;\n prefixLength = Math.max(prefixLength, prefix.length);\n }\n const breaks = nested ? 1 : 2;\n block = { open: breaks, close: breaks };\n orderedList = { next: start, prefixLength, nested };\n } else if (node.name === 'li' && parentTag === 'ol' && frame.orderedList) {\n const list = frame.orderedList;\n const prefix = `${list.nested ? '' : ' '}${list.next++}. `.padEnd(\n list.prefixLength,\n );\n block = {\n open: 1,\n close: 1,\n prefix: { first: prefix, rest: ' '.repeat(list.prefixLength) },\n };\n }\n\n if (block) {\n blocks.push({\n block,\n text: [],\n leading: block.open,\n stash: 0,\n space: false,\n });\n }\n\n if (node.name === 'hr') {\n writeWord('-'.repeat(40));\n } else if (node.name === 'br') {\n top().space = false;\n top().text.push('\\n');\n }\n\n stack.push({\n parent: node,\n children: node.body ?? [],\n index: 0,\n pre: frame.pre || node.name === 'pre',\n opened: block !== undefined,\n textFrom: top().text.length,\n orderedList,\n });\n }\n\n function exitElement(element: ITag | undefined, frame: WalkFrame) {\n if (element === undefined) return;\n\n if (element.name === 'a') {\n const href = decodeHTMLAttribute(\n element.attributeMap?.href?.value?.value ?? '',\n ).replace(/^mailto:/, '');\n // fragment-only hrefs are suppressed (html-to-text's noAnchorUrl)\n if (href.length > 0 && !href.startsWith('#')) {\n // the enclosing block's content doubles as the buffer of the\n // anchor's text: what was written since the anchor opened is\n // compared against the href to decide whether to append it\n // (html-to-text's hideLinkHrefIfSameAsText, which `toPlainText`\n // enables)\n const anchorText = top().text.slice(frame.textFrom).join('');\n if (anchorText !== href) {\n if (anchorText.length > 0) top().space = true;\n writeWord(href);\n }\n }\n }\n\n if (frame.opened) {\n closeBlock();\n }\n }\n\n function closeBlock() {\n const child = blocks.pop();\n if (child === undefined) return;\n const parent = blocks[blocks.length - 1];\n\n let content = child.text.join('');\n const prefix = child.block.prefix;\n if (prefix !== undefined) {\n const trimmed = content.replace(/^\\n+|\\n+$/g, '');\n content = prefix.first + trimmed.replaceAll('\\n', `\\n${prefix.rest}`);\n }\n\n const breaks = Math.max(parent.stash, child.leading);\n if (parent.text.length > 0) {\n parent.text.push('\\n'.repeat(breaks));\n if (content.length > 0) parent.text.push(content);\n } else {\n // nothing to separate from yet: the separation propagates upward\n // instead of materializing — this is edge suppression, and also how\n // empty blocks nested in empty blocks still commit their breaks\n if (content.length > 0) parent.text.push(content);\n parent.leading = breaks;\n }\n parent.stash = Math.max(child.stash, child.block.close);\n }\n\n return blocks[0].text.join('');\n}\n\nfunction findBody(tree: INode[]): ITag | undefined {\n for (const child of tree) {\n if (child.type !== SyntaxKind.Tag || child.name !== 'html') continue;\n for (const inner of child.body ?? []) {\n if (inner.type === SyntaxKind.Tag && inner.name === 'body') return inner;\n }\n }\n return undefined;\n}\n","import React from 'react';\n\nexport function createErrorBoundary(reject: (error: unknown) => void) {\n // React Server Components don't support React.Component, so it's just not defined here\n if (!React.Component) {\n return (props: { children?: React.ReactNode }) => <>{props.children}</>;\n }\n\n return class ErrorBoundary extends React.Component<{\n children: React.ReactNode;\n }> {\n componentDidCatch(error: unknown) {\n reject(error);\n }\n render() {\n return this.props.children;\n }\n };\n}\n","/**\n * React injects `<link rel=\"preload\" as=\"image\" />` resource hints into the\n * document `<head>` for every `<img>` it renders during server-side rendering.\n *\n * These hints are meant for browsers loading a web page, where they can speed\n * up the initial paint. In an email they are dead weight: email clients ignore\n * `<link rel=\"preload\">`, and the tags only add noise to the rendered HTML.\n *\n * @see https://github.com/resend/react-email/issues/3034\n *\n * This removes only those auto-injected image preload links, leaving every\n * other `<link>` (stylesheets, fonts, user-authored non-image preloads, ...)\n * untouched. It parses each `<link>` tag's attributes instead of relying on a\n * fixed string match, so it is not affected by attribute order or spacing.\n */\nexport const stripImagePreloadLinks = (html: string): string => {\n return html.replace(/<link\\b[^>]*\\/?>/gi, (tag) =>\n isImagePreloadLink(tag) ? '' : tag,\n );\n};\n\nconst isImagePreloadLink = (tag: string): boolean => {\n const attributes = parseAttributes(tag);\n return attributes.rel === 'preload' && attributes.as === 'image';\n};\n\nconst ATTRIBUTE_PATTERN =\n /([a-z][a-z0-9-]*)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'>]+)))?/gi;\n\n/**\n * Parses the attributes of a single, already-isolated HTML tag string (e.g.\n * `<link rel=\"preload\" as=\"image\" href=\"...\" />`) into a name → value map.\n * Attribute names are lower-cased; values are read from double, single, or\n * unquoted forms.\n */\nconst parseAttributes = (tag: string): Record<string, string> => {\n // Skip the tag name (`<link`) so it is not read as an attribute.\n const attributeSection = tag.replace(/^<[a-z][a-z0-9-]*/i, '');\n\n const attributes: Record<string, string> = {};\n for (const [\n ,\n name,\n doubleQuoted,\n singleQuoted,\n unquoted,\n ] of attributeSection.matchAll(ATTRIBUTE_PATTERN)) {\n attributes[name.toLowerCase()] =\n doubleQuoted ?? singleQuoted ?? unquoted ?? '';\n }\n\n return attributes;\n};\n","import type { ReactDOMServerReadableStream } from 'react-dom/server.browser';\n\nconst decoder = new TextDecoder('utf-8');\n\nexport const readStream = async (stream: ReactDOMServerReadableStream) => {\n const chunks: Uint8Array[] = [];\n\n const writableStream = new WritableStream({\n write(chunk: Uint8Array) {\n chunks.push(chunk);\n },\n abort(reason) {\n throw new Error('Stream aborted', {\n cause: {\n reason,\n },\n });\n },\n });\n await stream.pipeTo(writableStream);\n\n let length = 0;\n chunks.forEach((item) => {\n length += item.length;\n });\n const mergedChunks = new Uint8Array(length);\n let offset = 0;\n chunks.forEach((item) => {\n mergedChunks.set(item, offset);\n offset += item.length;\n });\n\n return decoder.decode(mergedChunks);\n};\n","export const importReactDom = () => {\n // We don't use async here because tsup converts it to a generator syntax\n // that esbuild doesn't understand as dealing with the import failing during\n // bundling: https://github.com/evanw/esbuild/issues/3216#issuecomment-1628913722\n return import('react-dom/server.edge').catch(\n () =>\n // This ensures that we still have compatibility with React 18,\n // which doesn't have the `.edge` export.\n import('react-dom/server'),\n );\n};\n","import { Suspense } from 'react';\nimport { pretty } from '../node';\nimport { createErrorBoundary } from '../shared/error-boundary';\nimport type { Options } from '../shared/options';\nimport { readStream } from '../shared/read-stream.browser';\nimport { stripImagePreloadLinks } from '../shared/utils/strip-image-preload-links';\nimport { toPlainText } from '../shared/utils/to-plain-text';\nimport { unstableToPlainText } from '../shared/utils/unstable-to-plain-text';\nimport { importReactDom } from './import-react-dom';\n\nexport const render = async (\n element: React.ReactElement,\n options?: Options,\n) => {\n const reactDOMServer = await importReactDom().then((m) => {\n if ('default' in m) {\n return m.default;\n }\n\n return m;\n });\n\n const html = await new Promise<string>((resolve, reject) => {\n const ErrorBoundary = createErrorBoundary(reject);\n reactDOMServer\n .renderToReadableStream(\n <ErrorBoundary>\n <Suspense>{element}</Suspense>\n </ErrorBoundary>,\n {\n onError(error: unknown) {\n reject(error);\n },\n progressiveChunkSize: Number.POSITIVE_INFINITY,\n },\n )\n .then(async (stream) => {\n await stream.allReady;\n return readStream(stream);\n })\n .then((result) => resolve(stripImagePreloadLinks(result)))\n .catch(reject);\n });\n\n if (options?.plainText) {\n return options.unstableTextConversion\n ? unstableToPlainText(html)\n : toPlainText(html, options.htmlToTextOptions);\n }\n\n const doctype =\n '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">';\n\n const document = `${doctype}${html.replace(/<!DOCTYPE.*?>/, '')}`;\n\n if (options?.pretty) {\n return pretty(document);\n }\n\n return document;\n};\n"],"mappings":"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"}