@react-email/render 2.0.2 → 2.0.4
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.
- package/dist/browser/index.d.mts.map +1 -1
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +16 -2
- package/dist/browser/index.mjs +18 -4
- package/dist/browser/index.mjs.map +1 -1
- package/dist/edge/index.d.mts.map +1 -1
- package/dist/edge/index.d.ts.map +1 -1
- package/dist/edge/index.js +16 -2
- package/dist/edge/index.mjs +18 -4
- package/dist/edge/index.mjs.map +1 -1
- package/dist/node/index.d.mts.map +1 -1
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +40 -14
- package/dist/node/index.mjs +40 -14
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/shared/options.ts","../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/browser/render.tsx"],"sourcesContent":[],"mappings":";;;;KAIY,OAAA;;;AAAZ;;;;AC0FA;;;;ECxFa;AASb;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/shared/options.ts","../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/browser/render.tsx"],"sourcesContent":[],"mappings":";;;;KAIY,OAAA;;;AAAZ;;;;AC0FA;;;;ECxFa;AASb;;;;ACTA;;;;;sBHqB0B;;;;cCmEb,gCAAgC,cAAY;;;cCxF5C,oBAAoB;iBASjB,WAAA,yBAAoC;;;cCTvC,eAAsB,KAAA,CAAM,qBAAqB,YAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/shared/options.ts","../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/browser/render.tsx"],"sourcesContent":[],"mappings":";;;;KAIY,OAAA;;;AAAZ;;;;AC0FA;;;;ECxFa;AASb;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/shared/options.ts","../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/browser/render.tsx"],"sourcesContent":[],"mappings":";;;;KAIY,OAAA;;;AAAZ;;;;AC0FA;;;;ECxFa;AASb;;;;ACTA;;;;;sBHqB0B;;;;cCmEb,gCAAgC,cAAY;;;cCxF5C,oBAAoB;iBASjB,WAAA,yBAAoC;;;cCTvC,eAAsB,KAAA,CAAM,qBAAqB,YAAO"}
|
package/dist/browser/index.js
CHANGED
|
@@ -111,6 +111,20 @@ function toPlainText(html, options) {
|
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/shared/error-boundary.tsx
|
|
116
|
+
function createErrorBoundary(reject) {
|
|
117
|
+
if (!react.default.Component) return (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: props.children });
|
|
118
|
+
return class ErrorBoundary extends react.default.Component {
|
|
119
|
+
componentDidCatch(error) {
|
|
120
|
+
reject(error);
|
|
121
|
+
}
|
|
122
|
+
render() {
|
|
123
|
+
return this.props.children;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
114
128
|
//#endregion
|
|
115
129
|
//#region src/shared/read-stream.browser.ts
|
|
116
130
|
const decoder = new TextDecoder("utf-8");
|
|
@@ -141,13 +155,13 @@ const readStream = async (stream) => {
|
|
|
141
155
|
//#endregion
|
|
142
156
|
//#region src/browser/render.tsx
|
|
143
157
|
const render = async (node, options) => {
|
|
144
|
-
const suspendedElement = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, { children: node });
|
|
145
158
|
const reactDOMServer = await import("react-dom/server").then((m) => {
|
|
146
159
|
if ("default" in m) return m.default;
|
|
147
160
|
return m;
|
|
148
161
|
});
|
|
149
162
|
const html = await new Promise((resolve, reject) => {
|
|
150
|
-
|
|
163
|
+
const ErrorBoundary = createErrorBoundary(reject);
|
|
164
|
+
reactDOMServer.renderToReadableStream(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorBoundary, { children: node }) }), {
|
|
151
165
|
onError(error) {
|
|
152
166
|
reject(error);
|
|
153
167
|
},
|
package/dist/browser/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as html from "prettier/plugins/html";
|
|
2
2
|
import { format } from "prettier/standalone";
|
|
3
3
|
import { convert } from "html-to-text";
|
|
4
|
-
import { Suspense } from "react";
|
|
5
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import React, { Suspense } from "react";
|
|
5
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
6
6
|
|
|
7
7
|
//#region src/shared/utils/pretty.ts
|
|
8
8
|
function recursivelyMapDoc(doc, callback) {
|
|
@@ -83,6 +83,20 @@ function toPlainText(html$1, options) {
|
|
|
83
83
|
});
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/shared/error-boundary.tsx
|
|
88
|
+
function createErrorBoundary(reject) {
|
|
89
|
+
if (!React.Component) return (props) => /* @__PURE__ */ jsx(Fragment, { children: props.children });
|
|
90
|
+
return class ErrorBoundary extends React.Component {
|
|
91
|
+
componentDidCatch(error) {
|
|
92
|
+
reject(error);
|
|
93
|
+
}
|
|
94
|
+
render() {
|
|
95
|
+
return this.props.children;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
86
100
|
//#endregion
|
|
87
101
|
//#region src/shared/read-stream.browser.ts
|
|
88
102
|
const decoder = new TextDecoder("utf-8");
|
|
@@ -113,13 +127,13 @@ const readStream = async (stream) => {
|
|
|
113
127
|
//#endregion
|
|
114
128
|
//#region src/browser/render.tsx
|
|
115
129
|
const render = async (node, options) => {
|
|
116
|
-
const suspendedElement = /* @__PURE__ */ jsx(Suspense, { children: node });
|
|
117
130
|
const reactDOMServer = await import("react-dom/server").then((m) => {
|
|
118
131
|
if ("default" in m) return m.default;
|
|
119
132
|
return m;
|
|
120
133
|
});
|
|
121
134
|
const html$1 = await new Promise((resolve, reject) => {
|
|
122
|
-
|
|
135
|
+
const ErrorBoundary = createErrorBoundary(reject);
|
|
136
|
+
reactDOMServer.renderToReadableStream(/* @__PURE__ */ jsx(Suspense, { children: /* @__PURE__ */ jsx(ErrorBoundary, { children: node }) }), {
|
|
123
137
|
onError(error) {
|
|
124
138
|
reject(error);
|
|
125
139
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["defaults: Options","plainTextSelectors: SelectorDefinition[]","html","chunks: Uint8Array[]","html"],"sources":["../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/shared/read-stream.browser.ts","../../src/browser/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 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 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 === '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\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 = path.getNode() as HtmlNode;\n\n const rawPrintingResult = previousPrint(path, options, print, args);\n\n if (node.type === 'ieConditionalComment') {\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 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","import { Suspense } from 'react';\nimport { pretty, toPlainText } from '../node';\nimport type { Options } from '../shared/options';\nimport { readStream } from '../shared/read-stream.browser';\n\nexport const render = async (node: React.ReactNode, options?: Options) => {\n const
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["defaults: Options","plainTextSelectors: SelectorDefinition[]","html","chunks: Uint8Array[]","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/browser/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 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 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 === '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\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 = path.getNode() as HtmlNode;\n\n const rawPrintingResult = previousPrint(path, options, print, args);\n\n if (node.type === 'ieConditionalComment') {\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","import { Suspense } from 'react';\nimport { pretty, toPlainText } from '../node';\nimport { createErrorBoundary } from '../shared/error-boundary';\nimport type { Options } from '../shared/options';\nimport { readStream } from '../shared/read-stream.browser';\n\nexport const render = async (node: React.ReactNode, options?: Options) => {\n const reactDOMServer = await import('react-dom/server').then((m) => {\n if ('default' in m) {\n return m.default;\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 <Suspense>\n <ErrorBoundary>{node}</ErrorBoundary>\n </Suspense>,\n {\n onError(error: unknown) {\n reject(error);\n },\n progressiveChunkSize: Number.POSITIVE_INFINITY,\n },\n )\n .then(readStream)\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":";;;;;;;AAgBA,SAAS,kBACP,KACA,UACc;AACd,KAAI,MAAM,QAAQ,IAAI,CACpB,QAAO,IAAI,KAAK,aAAa,kBAAkB,UAAU,SAAS,CAAC;AAGrE,KAAI,OAAO,QAAQ,UAAU;AAC3B,MAAI,IAAI,SAAS,QACf,QAAO;GACL,GAAG;GACH,UAAU,kBAAkB,IAAI,UAAU,SAAS;GACnD,gBAAgB,kBACd,IAAI,gBACJ,SACD;GACF;AAGH,MAAI,cAAc,IAChB,QAAO;GACL,GAAG;GACH,UAAU,kBAAkB,IAAI,UAAU,SAAS;GACpD;AAGH,MAAI,WAAW,IACb,QAAO;GACL,GAAG;GACH,OAAO,kBAAkB,IAAI,OAAO,SAAS;GAC9C;AAGH,MAAI,IAAI,SAAS,WACf,QAAO;GACL,GAAG;GACH,eAAe,kBAAkB,IAAI,eAAe,SAAS;GAC7D,cAAc,kBAAkB,IAAI,cAAc,SAAS;GAC5D;;AAIL,QAAO,SAAS,IAAI;;AAGtB,MAAM,eAAe,EAAE,GAAG,MAAM;AAChC,IAAI,aAAa,UAAU;CACzB,MAAM,gBAAgB,aAAa,SAAS,KAAK;AACjD,cAAa,SAAS,KAAK,SAAS,MAAM,SAAS,OAAO,SAAS;EACjE,MAAM,OAAO,KAAK,SAAS;EAE3B,MAAM,oBAAoB,cAAc,MAAM,SAAS,OAAO,KAAK;AAEnE,MAAI,KAAK,SAAS,uBAShB,QARuB,kBAAkB,oBAAoB,QAAQ;AACnE,OAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,OAC1C,QAAO,IAAI,OAAO,KAAK;AAGzB,UAAO;IACP;AAKJ,SAAO;;;AAIX,MAAMA,WAAoB;CACxB,WAAW;CACX,UAAU;CACV,SAAS,CAAC,aAAa;CACvB,iBAAiB;CACjB,QAAQ;CACT;AAED,MAAa,UAAU,KAAa,UAAmB,EAAE,KAAK;AAC5D,QAAO,OAAO,IAAI,WAAW,MAAM,GAAG,EAAE;EACtC,GAAG;EACH,GAAG;EACJ,CAAC;;;;;AC5FJ,MAAaC,qBAA2C;CACtD;EAAE,UAAU;EAAO,QAAQ;EAAQ;CACnC;EAAE,UAAU;EAA4B,QAAQ;EAAQ;CACxD;EACE,UAAU;EACV,SAAS;GAAE,cAAc;GAAO,0BAA0B;GAAM;EACjE;CACF;AAED,SAAgB,YAAY,QAAc,SAA6B;AACrE,QAAO,QAAQC,QAAM;EACnB,UAAU;EACV,GAAG;EACH,WAAW,CAAC,GAAG,oBAAoB,GAAI,SAAS,aAAa,EAAE,CAAE;EAClE,CAAC;;;;;AClBJ,SAAgB,oBAAoB,QAAkC;AAEpE,KAAI,CAAC,MAAM,UACT,SAAQ,UAA0C,0CAAG,MAAM,WAAY;AAGzE,QAAO,MAAM,sBAAsB,MAAM,UAEtC;EACD,kBAAkB,OAAgB;AAChC,UAAO,MAAM;;EAEf,SAAS;AACP,UAAO,KAAK,MAAM;;;;;;;ACbxB,MAAM,UAAU,IAAI,YAAY,QAAQ;AAExC,MAAa,aAAa,OAAO,WAAyC;CACxE,MAAMC,SAAuB,EAAE;CAE/B,MAAM,iBAAiB,IAAI,eAAe;EACxC,MAAM,OAAmB;AACvB,UAAO,KAAK,MAAM;;EAEpB,MAAM,QAAQ;AACZ,SAAM,IAAI,MAAM,kBAAkB,EAChC,OAAO,EACL,QACD,EACF,CAAC;;EAEL,CAAC;AACF,OAAM,OAAO,OAAO,eAAe;CAEnC,IAAI,SAAS;AACb,QAAO,SAAS,SAAS;AACvB,YAAU,KAAK;GACf;CACF,MAAM,eAAe,IAAI,WAAW,OAAO;CAC3C,IAAI,SAAS;AACb,QAAO,SAAS,SAAS;AACvB,eAAa,IAAI,MAAM,OAAO;AAC9B,YAAU,KAAK;GACf;AAEF,QAAO,QAAQ,OAAO,aAAa;;;;;AC1BrC,MAAa,SAAS,OAAO,MAAuB,YAAsB;CACxE,MAAM,iBAAiB,MAAM,OAAO,oBAAoB,MAAM,MAAM;AAClE,MAAI,aAAa,EACf,QAAO,EAAE;AAEX,SAAO;GACP;CAEF,MAAMC,SAAO,MAAM,IAAI,SAAiB,SAAS,WAAW;EAC1D,MAAM,gBAAgB,oBAAoB,OAAO;AACjD,iBACG,uBACC,oBAAC,sBACC,oBAAC,2BAAe,OAAqB,GAC5B,EACX;GACE,QAAQ,OAAgB;AACtB,WAAO,MAAM;;GAEf,sBAAsB,OAAO;GAC9B,CACF,CACA,KAAK,WAAW,CAChB,KAAK,QAAQ,CACb,MAAM,OAAO;GAChB;AAEF,KAAI,SAAS,UACX,QAAO,YAAYA,QAAM,QAAQ,kBAAkB;CAMrD,MAAM,WAAW,4HAAaA,OAAK,QAAQ,iBAAiB,GAAG;AAE/D,KAAI,SAAS,OACX,QAAO,OAAO,SAAS;AAGzB,QAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/shared/options.ts","../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/edge/render.tsx"],"sourcesContent":[],"mappings":";;;;KAIY,OAAA;;;AAAZ;;;;AC0FA;;;;ECxFa;AASb;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/shared/options.ts","../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/edge/render.tsx"],"sourcesContent":[],"mappings":";;;;KAIY,OAAA;;;AAAZ;;;;AC0FA;;;;ECxFa;AASb;;;;ACPA;;;;;sBHmB0B;;;;cCmEb,gCAAgC,cAAY;;;cCxF5C,oBAAoB;iBASjB,WAAA,yBAAoC;;;cCPvC,kBACF,KAAA,CAAM,wBACL,YAAO"}
|
package/dist/edge/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/shared/options.ts","../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/edge/render.tsx"],"sourcesContent":[],"mappings":";;;;KAIY,OAAA;;;AAAZ;;;;AC0FA;;;;ECxFa;AASb;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/shared/options.ts","../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/edge/render.tsx"],"sourcesContent":[],"mappings":";;;;KAIY,OAAA;;;AAAZ;;;;AC0FA;;;;ECxFa;AASb;;;;ACPA;;;;;sBHmB0B;;;;cCmEb,gCAAgC,cAAY;;;cCxF5C,oBAAoB;iBASjB,WAAA,yBAAoC;;;cCPvC,kBACF,KAAA,CAAM,wBACL,YAAO"}
|
package/dist/edge/index.js
CHANGED
|
@@ -111,6 +111,20 @@ function toPlainText(html, options) {
|
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/shared/error-boundary.tsx
|
|
116
|
+
function createErrorBoundary(reject) {
|
|
117
|
+
if (!react.default.Component) return (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: props.children });
|
|
118
|
+
return class ErrorBoundary extends react.default.Component {
|
|
119
|
+
componentDidCatch(error) {
|
|
120
|
+
reject(error);
|
|
121
|
+
}
|
|
122
|
+
render() {
|
|
123
|
+
return this.props.children;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
114
128
|
//#endregion
|
|
115
129
|
//#region src/shared/read-stream.browser.ts
|
|
116
130
|
const decoder = new TextDecoder("utf-8");
|
|
@@ -147,13 +161,13 @@ const importReactDom = () => {
|
|
|
147
161
|
//#endregion
|
|
148
162
|
//#region src/edge/render.tsx
|
|
149
163
|
const render = async (element, options) => {
|
|
150
|
-
const suspendedElement = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, { children: element });
|
|
151
164
|
const reactDOMServer = await importReactDom().then((m) => {
|
|
152
165
|
if ("default" in m) return m.default;
|
|
153
166
|
return m;
|
|
154
167
|
});
|
|
155
168
|
const html = await new Promise((resolve, reject) => {
|
|
156
|
-
|
|
169
|
+
const ErrorBoundary = createErrorBoundary(reject);
|
|
170
|
+
reactDOMServer.renderToReadableStream(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorBoundary, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, { children: element }) }), {
|
|
157
171
|
onError(error) {
|
|
158
172
|
reject(error);
|
|
159
173
|
},
|
package/dist/edge/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as html from "prettier/plugins/html";
|
|
2
2
|
import { format } from "prettier/standalone";
|
|
3
3
|
import { convert } from "html-to-text";
|
|
4
|
-
import { Suspense } from "react";
|
|
5
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import React, { Suspense } from "react";
|
|
5
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
6
6
|
|
|
7
7
|
//#region src/shared/utils/pretty.ts
|
|
8
8
|
function recursivelyMapDoc(doc, callback) {
|
|
@@ -83,6 +83,20 @@ function toPlainText(html$1, options) {
|
|
|
83
83
|
});
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/shared/error-boundary.tsx
|
|
88
|
+
function createErrorBoundary(reject) {
|
|
89
|
+
if (!React.Component) return (props) => /* @__PURE__ */ jsx(Fragment, { children: props.children });
|
|
90
|
+
return class ErrorBoundary extends React.Component {
|
|
91
|
+
componentDidCatch(error) {
|
|
92
|
+
reject(error);
|
|
93
|
+
}
|
|
94
|
+
render() {
|
|
95
|
+
return this.props.children;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
86
100
|
//#endregion
|
|
87
101
|
//#region src/shared/read-stream.browser.ts
|
|
88
102
|
const decoder = new TextDecoder("utf-8");
|
|
@@ -119,13 +133,13 @@ const importReactDom = () => {
|
|
|
119
133
|
//#endregion
|
|
120
134
|
//#region src/edge/render.tsx
|
|
121
135
|
const render = async (element, options) => {
|
|
122
|
-
const suspendedElement = /* @__PURE__ */ jsx(Suspense, { children: element });
|
|
123
136
|
const reactDOMServer = await importReactDom().then((m) => {
|
|
124
137
|
if ("default" in m) return m.default;
|
|
125
138
|
return m;
|
|
126
139
|
});
|
|
127
140
|
const html$1 = await new Promise((resolve, reject) => {
|
|
128
|
-
|
|
141
|
+
const ErrorBoundary = createErrorBoundary(reject);
|
|
142
|
+
reactDOMServer.renderToReadableStream(/* @__PURE__ */ jsx(ErrorBoundary, { children: /* @__PURE__ */ jsx(Suspense, { children: element }) }), {
|
|
129
143
|
onError(error) {
|
|
130
144
|
reject(error);
|
|
131
145
|
},
|
package/dist/edge/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["defaults: Options","plainTextSelectors: SelectorDefinition[]","html","chunks: Uint8Array[]","html"],"sources":["../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.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 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 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 === '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\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 = path.getNode() as HtmlNode;\n\n const rawPrintingResult = previousPrint(path, options, print, args);\n\n if (node.type === 'ieConditionalComment') {\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 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 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
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["defaults: Options","plainTextSelectors: SelectorDefinition[]","html","chunks: Uint8Array[]","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 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 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 === '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\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 = path.getNode() as HtmlNode;\n\n const rawPrintingResult = previousPrint(path, options, print, args);\n\n if (node.type === 'ieConditionalComment') {\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(readStream)\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/shared/options.ts","../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/node/render.tsx"],"sourcesContent":[],"mappings":";;;;KAIY,OAAA;;;AAAZ;;;;AC0FA;;;;ECxFa;AASb;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/shared/options.ts","../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/node/render.tsx"],"sourcesContent":[],"mappings":";;;;KAIY,OAAA;;;AAAZ;;;;AC0FA;;;;ECxFa;AASb;;;;ACRA;;;;;sBHoB0B;;;;cCmEb,gCAAgC,cAAY;;;cCxF5C,oBAAoB;iBASjB,WAAA,yBAAoC;;;cCRvC,eAAsB,KAAA,CAAM,qBAAqB,YAAO"}
|
package/dist/node/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/shared/options.ts","../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/node/render.tsx"],"sourcesContent":[],"mappings":";;;;KAIY,OAAA;;;AAAZ;;;;AC0FA;;;;ECxFa;AASb;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/shared/options.ts","../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/node/render.tsx"],"sourcesContent":[],"mappings":";;;;KAIY,OAAA;;;AAAZ;;;;AC0FA;;;;ECxFa;AASb;;;;ACRA;;;;;sBHoB0B;;;;cCmEb,gCAAgC,cAAY;;;cCxF5C,oBAAoB;iBASjB,WAAA,yBAAoC;;;cCRvC,eAAsB,KAAA,CAAM,qBAAqB,YAAO"}
|
package/dist/node/index.js
CHANGED
|
@@ -29,10 +29,10 @@ let html_to_text = require("html-to-text");
|
|
|
29
29
|
html_to_text = __toESM(html_to_text);
|
|
30
30
|
let react = require("react");
|
|
31
31
|
react = __toESM(react);
|
|
32
|
-
let node_stream = require("node:stream");
|
|
33
|
-
node_stream = __toESM(node_stream);
|
|
34
32
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
35
33
|
react_jsx_runtime = __toESM(react_jsx_runtime);
|
|
34
|
+
let node_stream = require("node:stream");
|
|
35
|
+
node_stream = __toESM(node_stream);
|
|
36
36
|
|
|
37
37
|
//#region src/shared/utils/pretty.ts
|
|
38
38
|
function recursivelyMapDoc(doc, callback) {
|
|
@@ -113,6 +113,20 @@ function toPlainText(html, options) {
|
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/shared/error-boundary.tsx
|
|
118
|
+
function createErrorBoundary(reject) {
|
|
119
|
+
if (!react.default.Component) return (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: props.children });
|
|
120
|
+
return class ErrorBoundary extends react.default.Component {
|
|
121
|
+
componentDidCatch(error) {
|
|
122
|
+
reject(error);
|
|
123
|
+
}
|
|
124
|
+
render() {
|
|
125
|
+
return this.props.children;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
116
130
|
//#endregion
|
|
117
131
|
//#region src/node/read-stream.ts
|
|
118
132
|
const readStream = async (stream) => {
|
|
@@ -158,24 +172,36 @@ const readStream = async (stream) => {
|
|
|
158
172
|
//#endregion
|
|
159
173
|
//#region src/node/render.tsx
|
|
160
174
|
const render = async (node, options) => {
|
|
161
|
-
const suspendedElement = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, { children: node });
|
|
162
175
|
const reactDOMServer = await import("react-dom/server").then((m) => {
|
|
163
176
|
if ("default" in m) return m.default;
|
|
164
177
|
return m;
|
|
165
178
|
});
|
|
166
179
|
let html;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
180
|
+
await new Promise((resolve, reject) => {
|
|
181
|
+
if (Object.hasOwn(reactDOMServer, "renderToReadableStream") && typeof WritableStream !== "undefined") {
|
|
182
|
+
const ErrorBoundary = createErrorBoundary(reject);
|
|
183
|
+
reactDOMServer.renderToReadableStream(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorBoundary, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, { children: node }) }), {
|
|
184
|
+
progressiveChunkSize: Number.POSITIVE_INFINITY,
|
|
185
|
+
onError(error) {
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
}).then((stream) => readStream(stream)).then((result) => {
|
|
189
|
+
html = result;
|
|
172
190
|
resolve();
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
},
|
|
177
|
-
|
|
178
|
-
|
|
191
|
+
}).catch(reject);
|
|
192
|
+
} else {
|
|
193
|
+
const ErrorBoundary = createErrorBoundary(reject);
|
|
194
|
+
const stream = reactDOMServer.renderToPipeableStream(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorBoundary, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, { children: node }) }), {
|
|
195
|
+
async onAllReady() {
|
|
196
|
+
html = await readStream(stream);
|
|
197
|
+
resolve();
|
|
198
|
+
},
|
|
199
|
+
onError(error) {
|
|
200
|
+
reject(error);
|
|
201
|
+
},
|
|
202
|
+
progressiveChunkSize: Number.POSITIVE_INFINITY
|
|
203
|
+
});
|
|
204
|
+
}
|
|
179
205
|
});
|
|
180
206
|
if (options?.plainText) return toPlainText(html, options.htmlToTextOptions);
|
|
181
207
|
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.*?>/, "")}`;
|
package/dist/node/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as html from "prettier/plugins/html";
|
|
2
2
|
import { format } from "prettier/standalone";
|
|
3
3
|
import { convert } from "html-to-text";
|
|
4
|
-
import { Suspense } from "react";
|
|
4
|
+
import React, { Suspense } from "react";
|
|
5
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
5
6
|
import { Writable } from "node:stream";
|
|
6
|
-
import { jsx } from "react/jsx-runtime";
|
|
7
7
|
|
|
8
8
|
//#region src/shared/utils/pretty.ts
|
|
9
9
|
function recursivelyMapDoc(doc, callback) {
|
|
@@ -84,6 +84,20 @@ function toPlainText(html$1, options) {
|
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/shared/error-boundary.tsx
|
|
89
|
+
function createErrorBoundary(reject) {
|
|
90
|
+
if (!React.Component) return (props) => /* @__PURE__ */ jsx(Fragment, { children: props.children });
|
|
91
|
+
return class ErrorBoundary extends React.Component {
|
|
92
|
+
componentDidCatch(error) {
|
|
93
|
+
reject(error);
|
|
94
|
+
}
|
|
95
|
+
render() {
|
|
96
|
+
return this.props.children;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
87
101
|
//#endregion
|
|
88
102
|
//#region src/node/read-stream.ts
|
|
89
103
|
const readStream = async (stream) => {
|
|
@@ -129,24 +143,36 @@ const readStream = async (stream) => {
|
|
|
129
143
|
//#endregion
|
|
130
144
|
//#region src/node/render.tsx
|
|
131
145
|
const render = async (node, options) => {
|
|
132
|
-
const suspendedElement = /* @__PURE__ */ jsx(Suspense, { children: node });
|
|
133
146
|
const reactDOMServer = await import("react-dom/server").then((m) => {
|
|
134
147
|
if ("default" in m) return m.default;
|
|
135
148
|
return m;
|
|
136
149
|
});
|
|
137
150
|
let html$1;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
151
|
+
await new Promise((resolve, reject) => {
|
|
152
|
+
if (Object.hasOwn(reactDOMServer, "renderToReadableStream") && typeof WritableStream !== "undefined") {
|
|
153
|
+
const ErrorBoundary = createErrorBoundary(reject);
|
|
154
|
+
reactDOMServer.renderToReadableStream(/* @__PURE__ */ jsx(ErrorBoundary, { children: /* @__PURE__ */ jsx(Suspense, { children: node }) }), {
|
|
155
|
+
progressiveChunkSize: Number.POSITIVE_INFINITY,
|
|
156
|
+
onError(error) {
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
}).then((stream) => readStream(stream)).then((result) => {
|
|
160
|
+
html$1 = result;
|
|
143
161
|
resolve();
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
},
|
|
148
|
-
|
|
149
|
-
|
|
162
|
+
}).catch(reject);
|
|
163
|
+
} else {
|
|
164
|
+
const ErrorBoundary = createErrorBoundary(reject);
|
|
165
|
+
const stream = reactDOMServer.renderToPipeableStream(/* @__PURE__ */ jsx(ErrorBoundary, { children: /* @__PURE__ */ jsx(Suspense, { children: node }) }), {
|
|
166
|
+
async onAllReady() {
|
|
167
|
+
html$1 = await readStream(stream);
|
|
168
|
+
resolve();
|
|
169
|
+
},
|
|
170
|
+
onError(error) {
|
|
171
|
+
reject(error);
|
|
172
|
+
},
|
|
173
|
+
progressiveChunkSize: Number.POSITIVE_INFINITY
|
|
174
|
+
});
|
|
175
|
+
}
|
|
150
176
|
});
|
|
151
177
|
if (options?.plainText) return toPlainText(html$1, options.htmlToTextOptions);
|
|
152
178
|
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.*?>/, "")}`;
|
package/dist/node/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["defaults: Options","plainTextSelectors: SelectorDefinition[]","html","html!: string","html"],"sources":["../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/node/read-stream.ts","../../src/node/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 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 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 === '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\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 = path.getNode() as HtmlNode;\n\n const rawPrintingResult = previousPrint(path, options, print, args);\n\n if (node.type === 'ieConditionalComment') {\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 { Writable } from 'node:stream';\nimport type {\n PipeableStream,\n ReactDOMServerReadableStream,\n} from 'react-dom/server.browser';\n\nexport const readStream = async (\n stream: PipeableStream | ReactDOMServerReadableStream,\n) => {\n let result = '';\n // Create a single TextDecoder instance to handle streaming properly\n // This fixes issues with multi-byte characters (e.g., CJK) being split across chunks\n const decoder = new TextDecoder('utf-8');\n\n if ('pipeTo' in stream) {\n // means it's a readable stream\n const writableStream = new WritableStream({\n write(chunk: BufferSource) {\n // Use stream: true to handle multi-byte characters split across chunks\n result += decoder.decode(chunk, { stream: true });\n },\n close() {\n // Flush any remaining bytes\n result += decoder.decode();\n },\n });\n await stream.pipeTo(writableStream);\n } else {\n const writable = new Writable({\n write(chunk: BufferSource, _encoding, callback) {\n // Use stream: true to handle multi-byte characters split across chunks\n result += decoder.decode(chunk, { stream: true });\n\n callback();\n },\n final(callback) {\n // Flush any remaining bytes\n result += decoder.decode();\n callback();\n },\n });\n await new Promise<void>((resolve, reject) => {\n writable.on('pipe', (source) => {\n source.on('error', (err: Error) => {\n writable.destroy(err);\n });\n });\n writable.on('error', reject);\n writable.on('close', () => {\n resolve();\n });\n\n stream.pipe(writable);\n });\n }\n\n return result;\n};\n","import { Suspense } from 'react';\nimport type { Options } from '../shared/options';\nimport { pretty } from '../shared/utils/pretty';\nimport { toPlainText } from '../shared/utils/to-plain-text';\nimport { readStream } from './read-stream';\n\nexport const render = async (node: React.ReactNode, options?: Options) => {\n const suspendedElement = <Suspense>{node}</Suspense>;\n const reactDOMServer = await import('react-dom/server').then((m) => {\n if ('default' in m) {\n return m.default;\n }\n return m;\n });\n\n let html!: string;\n if (\n Object.hasOwn(reactDOMServer, 'renderToReadableStream') &&\n typeof WritableStream !== 'undefined'\n ) {\n html = await readStream(\n await reactDOMServer.renderToReadableStream(suspendedElement, {\n progressiveChunkSize: Number.POSITIVE_INFINITY,\n }),\n );\n } else {\n await new Promise<void>((resolve, reject) => {\n const stream = reactDOMServer.renderToPipeableStream(suspendedElement, {\n async onAllReady() {\n html = await readStream(stream);\n resolve();\n },\n onError(error) {\n reject(error as Error);\n },\n progressiveChunkSize: Number.POSITIVE_INFINITY,\n });\n });\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":["defaults: Options","plainTextSelectors: SelectorDefinition[]","html","html!: string","html"],"sources":["../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/shared/error-boundary.tsx","../../src/node/read-stream.ts","../../src/node/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 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 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 === '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\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 = path.getNode() as HtmlNode;\n\n const rawPrintingResult = previousPrint(path, options, print, args);\n\n if (node.type === 'ieConditionalComment') {\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 { Writable } from 'node:stream';\nimport type {\n PipeableStream,\n ReactDOMServerReadableStream,\n} from 'react-dom/server.browser';\n\nexport const readStream = async (\n stream: PipeableStream | ReactDOMServerReadableStream,\n) => {\n let result = '';\n // Create a single TextDecoder instance to handle streaming properly\n // This fixes issues with multi-byte characters (e.g., CJK) being split across chunks\n const decoder = new TextDecoder('utf-8');\n\n if ('pipeTo' in stream) {\n // means it's a readable stream\n const writableStream = new WritableStream({\n write(chunk: BufferSource) {\n // Use stream: true to handle multi-byte characters split across chunks\n result += decoder.decode(chunk, { stream: true });\n },\n close() {\n // Flush any remaining bytes\n result += decoder.decode();\n },\n });\n await stream.pipeTo(writableStream);\n } else {\n const writable = new Writable({\n write(chunk: BufferSource, _encoding, callback) {\n // Use stream: true to handle multi-byte characters split across chunks\n result += decoder.decode(chunk, { stream: true });\n\n callback();\n },\n final(callback) {\n // Flush any remaining bytes\n result += decoder.decode();\n callback();\n },\n });\n await new Promise<void>((resolve, reject) => {\n writable.on('pipe', (source) => {\n source.on('error', (err: Error) => {\n writable.destroy(err);\n });\n });\n writable.on('error', reject);\n writable.on('close', () => {\n resolve();\n });\n\n stream.pipe(writable);\n });\n }\n\n return result;\n};\n","import { Suspense } from 'react';\nimport { createErrorBoundary } from '../shared/error-boundary';\nimport type { Options } from '../shared/options';\nimport { pretty } from '../shared/utils/pretty';\nimport { toPlainText } from '../shared/utils/to-plain-text';\nimport { readStream } from './read-stream';\n\nexport const render = async (node: React.ReactNode, options?: Options) => {\n const reactDOMServer = await import('react-dom/server').then((m) => {\n if ('default' in m) {\n return m.default;\n }\n return m;\n });\n\n let html!: string;\n await new Promise<void>((resolve, reject) => {\n if (\n Object.hasOwn(reactDOMServer, 'renderToReadableStream') &&\n typeof WritableStream !== 'undefined'\n ) {\n const ErrorBoundary = createErrorBoundary(reject);\n reactDOMServer\n .renderToReadableStream(\n <ErrorBoundary>\n <Suspense>{node}</Suspense>\n </ErrorBoundary>,\n {\n progressiveChunkSize: Number.POSITIVE_INFINITY,\n onError(error) {\n // Throw immediately when an error occurs to prevent CSR fallback\n throw error;\n },\n },\n )\n .then((stream) => readStream(stream))\n .then((result) => {\n html = result;\n resolve();\n })\n .catch(reject);\n } else {\n const ErrorBoundary = createErrorBoundary(reject);\n const stream = reactDOMServer.renderToPipeableStream(\n <ErrorBoundary>\n <Suspense>{node}</Suspense>\n </ErrorBoundary>,\n {\n async onAllReady() {\n html = await readStream(stream);\n resolve();\n },\n onError(error) {\n reject(error);\n },\n progressiveChunkSize: Number.POSITIVE_INFINITY,\n },\n );\n }\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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"}
|