@solid-email/render 0.1.0
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/LICENSE +21 -0
- package/dist/browser/index.cjs +161 -0
- package/dist/browser/index.d.cts +34 -0
- package/dist/browser/index.d.cts.map +1 -0
- package/dist/browser/index.d.mts +34 -0
- package/dist/browser/index.d.mts.map +1 -0
- package/dist/browser/index.mjs +135 -0
- package/dist/browser/index.mjs.map +1 -0
- package/dist/edge/index.cjs +161 -0
- package/dist/edge/index.d.cts +34 -0
- package/dist/edge/index.d.cts.map +1 -0
- package/dist/edge/index.d.mts +34 -0
- package/dist/edge/index.d.mts.map +1 -0
- package/dist/edge/index.mjs +135 -0
- package/dist/edge/index.mjs.map +1 -0
- package/dist/node/index.cjs +161 -0
- package/dist/node/index.d.cts +34 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.mts +34 -0
- package/dist/node/index.d.mts.map +1 -0
- package/dist/node/index.mjs +135 -0
- package/dist/node/index.mjs.map +1 -0
- package/package.json +127 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { renderToString, renderToStringAsync } from "solid-js/web/dist/server.js";
|
|
2
|
+
import * as html from "prettier/plugins/html";
|
|
3
|
+
import { format } from "prettier/standalone";
|
|
4
|
+
import { convert } from "html-to-text";
|
|
5
|
+
//#region src/shared/utils/pretty.ts
|
|
6
|
+
function getHtmlNode(path) {
|
|
7
|
+
const topNode = path.node;
|
|
8
|
+
if (topNode) return topNode;
|
|
9
|
+
return path.stack?.[path.stack.length - 1];
|
|
10
|
+
}
|
|
11
|
+
function recursivelyMapDoc(doc, callback) {
|
|
12
|
+
if (Array.isArray(doc)) return doc.map((innerDoc) => recursivelyMapDoc(innerDoc, callback));
|
|
13
|
+
if (typeof doc === "object") {
|
|
14
|
+
if (doc.type === "line") return callback(doc.soft ? "" : " ");
|
|
15
|
+
if (doc.type === "group") return {
|
|
16
|
+
...doc,
|
|
17
|
+
contents: recursivelyMapDoc(doc.contents, callback),
|
|
18
|
+
expandedStates: recursivelyMapDoc(doc.expandedStates, callback)
|
|
19
|
+
};
|
|
20
|
+
if ("contents" in doc) return {
|
|
21
|
+
...doc,
|
|
22
|
+
contents: recursivelyMapDoc(doc.contents, callback)
|
|
23
|
+
};
|
|
24
|
+
if ("parts" in doc) return {
|
|
25
|
+
...doc,
|
|
26
|
+
parts: recursivelyMapDoc(doc.parts, callback)
|
|
27
|
+
};
|
|
28
|
+
if (doc.type === "if-break") return {
|
|
29
|
+
...doc,
|
|
30
|
+
breakContents: recursivelyMapDoc(doc.breakContents, callback),
|
|
31
|
+
flatContents: recursivelyMapDoc(doc.flatContents, callback)
|
|
32
|
+
};
|
|
33
|
+
const nextDoc = { ...doc };
|
|
34
|
+
for (const [key, value] of Object.entries(nextDoc)) if (value && typeof value === "object") nextDoc[key] = recursivelyMapDoc(value, callback);
|
|
35
|
+
return nextDoc;
|
|
36
|
+
}
|
|
37
|
+
return callback(doc);
|
|
38
|
+
}
|
|
39
|
+
const modifiedHtml = { ...html };
|
|
40
|
+
const htmlPrinter = modifiedHtml.printers?.html;
|
|
41
|
+
if (htmlPrinter?.print) {
|
|
42
|
+
const previousPrint = htmlPrinter.print;
|
|
43
|
+
htmlPrinter.print = (path, options, print, args) => {
|
|
44
|
+
const node = getHtmlNode(path);
|
|
45
|
+
const rawPrintingResult = previousPrint(path, options, print, args);
|
|
46
|
+
if (node?.type === "ieConditionalComment" || node?.kind === "ieConditionalComment") return recursivelyMapDoc(rawPrintingResult, (doc) => {
|
|
47
|
+
if (typeof doc === "object" && doc.type === "line") return doc.soft ? "" : " ";
|
|
48
|
+
return doc;
|
|
49
|
+
});
|
|
50
|
+
return rawPrintingResult;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const defaults = {
|
|
54
|
+
endOfLine: "lf",
|
|
55
|
+
tabWidth: 2,
|
|
56
|
+
plugins: [modifiedHtml],
|
|
57
|
+
bracketSameLine: true,
|
|
58
|
+
parser: "html"
|
|
59
|
+
};
|
|
60
|
+
const pretty = (str, options = {}) => {
|
|
61
|
+
return format(str.replaceAll("\0", ""), {
|
|
62
|
+
...defaults,
|
|
63
|
+
...options
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/shared/utils/to-plain-text.ts
|
|
68
|
+
const plainTextSelectors = [
|
|
69
|
+
{
|
|
70
|
+
selector: "img",
|
|
71
|
+
format: "skip"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
selector: "[data-skip-in-text=true]",
|
|
75
|
+
format: "skip"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
selector: "a",
|
|
79
|
+
options: {
|
|
80
|
+
linkBrackets: false,
|
|
81
|
+
hideLinkHrefIfSameAsText: true
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
];
|
|
85
|
+
function toPlainText(html, options) {
|
|
86
|
+
return convert(html, {
|
|
87
|
+
wordwrap: false,
|
|
88
|
+
...options,
|
|
89
|
+
selectors: [...plainTextSelectors, ...options?.selectors ?? []]
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/shared/render.ts
|
|
94
|
+
const doctype = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
|
|
95
|
+
function normalizeRenderable(node) {
|
|
96
|
+
return typeof node === "function" ? node : () => node;
|
|
97
|
+
}
|
|
98
|
+
function decodeSerializedString(value) {
|
|
99
|
+
try {
|
|
100
|
+
const jsonSafe = value.replace(/\\x([0-9A-Fa-f]{2})/g, (_match, hex) => String.fromCharCode(Number.parseInt(hex, 16)));
|
|
101
|
+
const parsed = JSON.parse(`"${jsonSafe}"`);
|
|
102
|
+
return typeof parsed === "string" ? parsed : value;
|
|
103
|
+
} catch {
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function removeSolidResourceScripts(html) {
|
|
108
|
+
const errorMatch = /Object\.assign\(new Error\("((?:\\.|[^"\\])*)"\)/.exec(html);
|
|
109
|
+
if (errorMatch) throw new Error(decodeSerializedString(errorMatch[1] ?? ""));
|
|
110
|
+
return html.replace(/<script>self\.\$R=self\.\$R\|\|\[\];[\s\S]*?<\/script>/g, "");
|
|
111
|
+
}
|
|
112
|
+
function renderDocument(html) {
|
|
113
|
+
return `${doctype}${html.replace(/<!DOCTYPE.*?>/, "")}`;
|
|
114
|
+
}
|
|
115
|
+
function renderSyncOutput(html, options) {
|
|
116
|
+
if (options?.plainText) return toPlainText(html, options.htmlToTextOptions);
|
|
117
|
+
return renderDocument(html);
|
|
118
|
+
}
|
|
119
|
+
async function renderOutput(html, options) {
|
|
120
|
+
if (options?.plainText) return toPlainText(html, options.htmlToTextOptions);
|
|
121
|
+
const document = renderDocument(html);
|
|
122
|
+
if (options?.pretty) return pretty(document);
|
|
123
|
+
return document;
|
|
124
|
+
}
|
|
125
|
+
async function render(node, options) {
|
|
126
|
+
return renderOutput(removeSolidResourceScripts(await renderToStringAsync(normalizeRenderable(node))), options);
|
|
127
|
+
}
|
|
128
|
+
function renderSync(node, options) {
|
|
129
|
+
if (options?.pretty) throw new Error("renderSync does not support pretty output; use render.");
|
|
130
|
+
return renderSyncOutput(removeSolidResourceScripts(renderToString(normalizeRenderable(node))), options);
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { plainTextSelectors, pretty, render, renderSync, toPlainText };
|
|
134
|
+
|
|
135
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/shared/render.ts"],"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 | undefined;\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;\nconst htmlPrinter = modifiedHtml.printers?.html;\nif (htmlPrinter?.print) {\n const previousPrint = htmlPrinter.print;\n htmlPrinter.print = (path, options, print, args) => {\n const node = getHtmlNode(path as Parameters<typeof previousPrint>[0]);\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\n// Text-export note: images and\n// preview-only nodes are skipped, and links render without duplicated hrefs.\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 { JSX } from 'solid-js';\nimport {\n renderToString,\n renderToStringAsync,\n} from 'solid-js/web/dist/server.js';\nimport type { Options, RenderSyncOptions } from './options';\nimport { pretty } from './utils/pretty';\nimport { toPlainText } from './utils/to-plain-text';\n\nexport type Renderable = JSX.Element | (() => JSX.Element);\n\nconst doctype =\n '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">';\n\nfunction normalizeRenderable(node: Renderable) {\n return typeof node === 'function' ? (node as () => JSX.Element) : () => node;\n}\n\nfunction decodeSerializedString(value: string): string {\n try {\n const jsonSafe = value.replace(\n /\\\\x([0-9A-Fa-f]{2})/g,\n (_match, hex: string) => String.fromCharCode(Number.parseInt(hex, 16)),\n );\n const parsed: unknown = JSON.parse(`\"${jsonSafe}\"`);\n return typeof parsed === 'string' ? parsed : value;\n } catch {\n return value;\n }\n}\n\nfunction removeSolidResourceScripts(html: string): string {\n const errorMatch = /Object\\.assign\\(new Error\\(\"((?:\\\\.|[^\"\\\\])*)\"\\)/.exec(\n html,\n );\n if (errorMatch) {\n throw new Error(decodeSerializedString(errorMatch[1] ?? ''));\n }\n return html.replace(\n /<script>self\\.\\$R=self\\.\\$R\\|\\|\\[\\];[\\s\\S]*?<\\/script>/g,\n '',\n );\n}\n\nfunction renderDocument(html: string): string {\n return `${doctype}${html.replace(/<!DOCTYPE.*?>/, '')}`;\n}\n\nfunction renderSyncOutput(html: string, options?: RenderSyncOptions): string {\n if (options?.plainText) {\n return toPlainText(html, options.htmlToTextOptions);\n }\n\n return renderDocument(html);\n}\n\nasync function renderOutput(html: string, options?: Options): Promise<string> {\n if (options?.plainText) {\n return toPlainText(html, options.htmlToTextOptions);\n }\n\n const document = renderDocument(html);\n\n if (options?.pretty) {\n return pretty(document);\n }\n\n return document;\n}\n\n// API note: render accepts either a component function or an already\n// constructed node. Keep that API while rendering through Solid SSR.\nexport async function render(\n node: Renderable,\n options?: Options,\n): Promise<string> {\n const html = removeSolidResourceScripts(\n await renderToStringAsync(normalizeRenderable(node)),\n );\n\n return renderOutput(html, options);\n}\n\nexport function renderSync(\n node: Renderable,\n options?: RenderSyncOptions,\n): string {\n if (options?.pretty) {\n throw new Error('renderSync does not support pretty output; use render.');\n }\n\n const html = removeSolidResourceScripts(\n renderToString(normalizeRenderable(node)),\n );\n\n return renderSyncOutput(html, options);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
//#endregion
|
|
24
|
+
let solid_js_web_dist_server_js = require("solid-js/web/dist/server.js");
|
|
25
|
+
let prettier_plugins_html = require("prettier/plugins/html");
|
|
26
|
+
prettier_plugins_html = __toESM(prettier_plugins_html, 1);
|
|
27
|
+
let prettier_standalone = require("prettier/standalone");
|
|
28
|
+
let html_to_text = require("html-to-text");
|
|
29
|
+
//#region src/shared/utils/pretty.ts
|
|
30
|
+
function getHtmlNode(path) {
|
|
31
|
+
const topNode = path.node;
|
|
32
|
+
if (topNode) return topNode;
|
|
33
|
+
return path.stack?.[path.stack.length - 1];
|
|
34
|
+
}
|
|
35
|
+
function recursivelyMapDoc(doc, callback) {
|
|
36
|
+
if (Array.isArray(doc)) return doc.map((innerDoc) => recursivelyMapDoc(innerDoc, callback));
|
|
37
|
+
if (typeof doc === "object") {
|
|
38
|
+
if (doc.type === "line") return callback(doc.soft ? "" : " ");
|
|
39
|
+
if (doc.type === "group") return {
|
|
40
|
+
...doc,
|
|
41
|
+
contents: recursivelyMapDoc(doc.contents, callback),
|
|
42
|
+
expandedStates: recursivelyMapDoc(doc.expandedStates, callback)
|
|
43
|
+
};
|
|
44
|
+
if ("contents" in doc) return {
|
|
45
|
+
...doc,
|
|
46
|
+
contents: recursivelyMapDoc(doc.contents, callback)
|
|
47
|
+
};
|
|
48
|
+
if ("parts" in doc) return {
|
|
49
|
+
...doc,
|
|
50
|
+
parts: recursivelyMapDoc(doc.parts, callback)
|
|
51
|
+
};
|
|
52
|
+
if (doc.type === "if-break") return {
|
|
53
|
+
...doc,
|
|
54
|
+
breakContents: recursivelyMapDoc(doc.breakContents, callback),
|
|
55
|
+
flatContents: recursivelyMapDoc(doc.flatContents, callback)
|
|
56
|
+
};
|
|
57
|
+
const nextDoc = { ...doc };
|
|
58
|
+
for (const [key, value] of Object.entries(nextDoc)) if (value && typeof value === "object") nextDoc[key] = recursivelyMapDoc(value, callback);
|
|
59
|
+
return nextDoc;
|
|
60
|
+
}
|
|
61
|
+
return callback(doc);
|
|
62
|
+
}
|
|
63
|
+
const modifiedHtml = { ...prettier_plugins_html };
|
|
64
|
+
const htmlPrinter = modifiedHtml.printers?.html;
|
|
65
|
+
if (htmlPrinter?.print) {
|
|
66
|
+
const previousPrint = htmlPrinter.print;
|
|
67
|
+
htmlPrinter.print = (path, options, print, args) => {
|
|
68
|
+
const node = getHtmlNode(path);
|
|
69
|
+
const rawPrintingResult = previousPrint(path, options, print, args);
|
|
70
|
+
if (node?.type === "ieConditionalComment" || node?.kind === "ieConditionalComment") return recursivelyMapDoc(rawPrintingResult, (doc) => {
|
|
71
|
+
if (typeof doc === "object" && doc.type === "line") return doc.soft ? "" : " ";
|
|
72
|
+
return doc;
|
|
73
|
+
});
|
|
74
|
+
return rawPrintingResult;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const defaults = {
|
|
78
|
+
endOfLine: "lf",
|
|
79
|
+
tabWidth: 2,
|
|
80
|
+
plugins: [modifiedHtml],
|
|
81
|
+
bracketSameLine: true,
|
|
82
|
+
parser: "html"
|
|
83
|
+
};
|
|
84
|
+
const pretty = (str, options = {}) => {
|
|
85
|
+
return (0, prettier_standalone.format)(str.replaceAll("\0", ""), {
|
|
86
|
+
...defaults,
|
|
87
|
+
...options
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/shared/utils/to-plain-text.ts
|
|
92
|
+
const plainTextSelectors = [
|
|
93
|
+
{
|
|
94
|
+
selector: "img",
|
|
95
|
+
format: "skip"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
selector: "[data-skip-in-text=true]",
|
|
99
|
+
format: "skip"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
selector: "a",
|
|
103
|
+
options: {
|
|
104
|
+
linkBrackets: false,
|
|
105
|
+
hideLinkHrefIfSameAsText: true
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
];
|
|
109
|
+
function toPlainText(html, options) {
|
|
110
|
+
return (0, html_to_text.convert)(html, {
|
|
111
|
+
wordwrap: false,
|
|
112
|
+
...options,
|
|
113
|
+
selectors: [...plainTextSelectors, ...options?.selectors ?? []]
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/shared/render.ts
|
|
118
|
+
const doctype = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
|
|
119
|
+
function normalizeRenderable(node) {
|
|
120
|
+
return typeof node === "function" ? node : () => node;
|
|
121
|
+
}
|
|
122
|
+
function decodeSerializedString(value) {
|
|
123
|
+
try {
|
|
124
|
+
const jsonSafe = value.replace(/\\x([0-9A-Fa-f]{2})/g, (_match, hex) => String.fromCharCode(Number.parseInt(hex, 16)));
|
|
125
|
+
const parsed = JSON.parse(`"${jsonSafe}"`);
|
|
126
|
+
return typeof parsed === "string" ? parsed : value;
|
|
127
|
+
} catch {
|
|
128
|
+
return value;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function removeSolidResourceScripts(html) {
|
|
132
|
+
const errorMatch = /Object\.assign\(new Error\("((?:\\.|[^"\\])*)"\)/.exec(html);
|
|
133
|
+
if (errorMatch) throw new Error(decodeSerializedString(errorMatch[1] ?? ""));
|
|
134
|
+
return html.replace(/<script>self\.\$R=self\.\$R\|\|\[\];[\s\S]*?<\/script>/g, "");
|
|
135
|
+
}
|
|
136
|
+
function renderDocument(html) {
|
|
137
|
+
return `${doctype}${html.replace(/<!DOCTYPE.*?>/, "")}`;
|
|
138
|
+
}
|
|
139
|
+
function renderSyncOutput(html, options) {
|
|
140
|
+
if (options?.plainText) return toPlainText(html, options.htmlToTextOptions);
|
|
141
|
+
return renderDocument(html);
|
|
142
|
+
}
|
|
143
|
+
async function renderOutput(html, options) {
|
|
144
|
+
if (options?.plainText) return toPlainText(html, options.htmlToTextOptions);
|
|
145
|
+
const document = renderDocument(html);
|
|
146
|
+
if (options?.pretty) return pretty(document);
|
|
147
|
+
return document;
|
|
148
|
+
}
|
|
149
|
+
async function render(node, options) {
|
|
150
|
+
return renderOutput(removeSolidResourceScripts(await (0, solid_js_web_dist_server_js.renderToStringAsync)(normalizeRenderable(node))), options);
|
|
151
|
+
}
|
|
152
|
+
function renderSync(node, options) {
|
|
153
|
+
if (options?.pretty) throw new Error("renderSync does not support pretty output; use render.");
|
|
154
|
+
return renderSyncOutput(removeSolidResourceScripts((0, solid_js_web_dist_server_js.renderToString)(normalizeRenderable(node))), options);
|
|
155
|
+
}
|
|
156
|
+
//#endregion
|
|
157
|
+
exports.plainTextSelectors = plainTextSelectors;
|
|
158
|
+
exports.pretty = pretty;
|
|
159
|
+
exports.render = render;
|
|
160
|
+
exports.renderSync = renderSync;
|
|
161
|
+
exports.toPlainText = toPlainText;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { HtmlToTextOptions, SelectorDefinition } from "html-to-text";
|
|
2
|
+
import { JSX } from "solid-js";
|
|
3
|
+
import { Options as Options$1 } from "prettier";
|
|
4
|
+
|
|
5
|
+
//#region src/shared/options.d.ts
|
|
6
|
+
type PlainTextDisabled = {
|
|
7
|
+
plainText?: false;
|
|
8
|
+
};
|
|
9
|
+
type PlainTextEnabled = {
|
|
10
|
+
plainText?: true;
|
|
11
|
+
htmlToTextOptions?: HtmlToTextOptions;
|
|
12
|
+
};
|
|
13
|
+
type PlainTextOptions = PlainTextDisabled | PlainTextEnabled;
|
|
14
|
+
type Options = {
|
|
15
|
+
pretty?: boolean;
|
|
16
|
+
} & PlainTextOptions;
|
|
17
|
+
type RenderSyncOptions = {
|
|
18
|
+
pretty?: false;
|
|
19
|
+
} & PlainTextOptions;
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/shared/render.d.ts
|
|
22
|
+
type Renderable = JSX.Element | (() => JSX.Element);
|
|
23
|
+
declare function render(node: Renderable, options?: Options): Promise<string>;
|
|
24
|
+
declare function renderSync(node: Renderable, options?: RenderSyncOptions): string;
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/shared/utils/pretty.d.ts
|
|
27
|
+
declare const pretty: (str: string, options?: Options$1) => Promise<string>;
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/shared/utils/to-plain-text.d.ts
|
|
30
|
+
declare const plainTextSelectors: SelectorDefinition[];
|
|
31
|
+
declare function toPlainText(html: string, options?: HtmlToTextOptions): string;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { Options, RenderSyncOptions, Renderable, plainTextSelectors, pretty, render, renderSync, toPlainText };
|
|
34
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/shared/options.ts","../../src/shared/render.ts","../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts"],"mappings":";;;;;KAEK,iBAAA;EACH,SAAS;AAAA;AAAA,KAGN,gBAAA;EACH,SAAA;EACA,iBAAA,GAAoB,iBAAiB;AAAA;AAAA,KAGlC,gBAAA,GAAmB,iBAAA,GAAoB,gBAAgB;AAAA,KAEhD,OAAA;EACV,MAAA;AAAA,IACE,gBAAgB;AAAA,KAER,iBAAA;EACV,MAAA;AAAA,IACE,gBAAgB;;;KCVR,UAAA,GAAa,GAAA,CAAI,OAAA,UAAiB,GAAA,CAAI,OAAO;AAAA,iBA+DnC,MAAA,CACpB,IAAA,EAAM,UAAA,EACN,OAAA,GAAU,OAAA,GACT,OAAA;AAAA,iBAQa,UAAA,CACd,IAAA,EAAM,UAAA,EACN,OAAA,GAAU,iBAAiB;;;cCuChB,MAAA,GAAU,GAAA,UAAa,OAAA,GAAS,SAAA,KAAY,OAAA;;;cCpH5C,kBAAA,EAAoB,kBAAkB;AAAA,iBASnC,WAAA,CAAY,IAAA,UAAc,OAAA,GAAU,iBAAiB"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { HtmlToTextOptions, SelectorDefinition } from "html-to-text";
|
|
2
|
+
import { JSX } from "solid-js";
|
|
3
|
+
import { Options as Options$1 } from "prettier";
|
|
4
|
+
|
|
5
|
+
//#region src/shared/options.d.ts
|
|
6
|
+
type PlainTextDisabled = {
|
|
7
|
+
plainText?: false;
|
|
8
|
+
};
|
|
9
|
+
type PlainTextEnabled = {
|
|
10
|
+
plainText?: true;
|
|
11
|
+
htmlToTextOptions?: HtmlToTextOptions;
|
|
12
|
+
};
|
|
13
|
+
type PlainTextOptions = PlainTextDisabled | PlainTextEnabled;
|
|
14
|
+
type Options = {
|
|
15
|
+
pretty?: boolean;
|
|
16
|
+
} & PlainTextOptions;
|
|
17
|
+
type RenderSyncOptions = {
|
|
18
|
+
pretty?: false;
|
|
19
|
+
} & PlainTextOptions;
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/shared/render.d.ts
|
|
22
|
+
type Renderable = JSX.Element | (() => JSX.Element);
|
|
23
|
+
declare function render(node: Renderable, options?: Options): Promise<string>;
|
|
24
|
+
declare function renderSync(node: Renderable, options?: RenderSyncOptions): string;
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/shared/utils/pretty.d.ts
|
|
27
|
+
declare const pretty: (str: string, options?: Options$1) => Promise<string>;
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/shared/utils/to-plain-text.d.ts
|
|
30
|
+
declare const plainTextSelectors: SelectorDefinition[];
|
|
31
|
+
declare function toPlainText(html: string, options?: HtmlToTextOptions): string;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { Options, RenderSyncOptions, Renderable, plainTextSelectors, pretty, render, renderSync, toPlainText };
|
|
34
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/shared/options.ts","../../src/shared/render.ts","../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts"],"mappings":";;;;;KAEK,iBAAA;EACH,SAAS;AAAA;AAAA,KAGN,gBAAA;EACH,SAAA;EACA,iBAAA,GAAoB,iBAAiB;AAAA;AAAA,KAGlC,gBAAA,GAAmB,iBAAA,GAAoB,gBAAgB;AAAA,KAEhD,OAAA;EACV,MAAA;AAAA,IACE,gBAAgB;AAAA,KAER,iBAAA;EACV,MAAA;AAAA,IACE,gBAAgB;;;KCVR,UAAA,GAAa,GAAA,CAAI,OAAA,UAAiB,GAAA,CAAI,OAAO;AAAA,iBA+DnC,MAAA,CACpB,IAAA,EAAM,UAAA,EACN,OAAA,GAAU,OAAA,GACT,OAAA;AAAA,iBAQa,UAAA,CACd,IAAA,EAAM,UAAA,EACN,OAAA,GAAU,iBAAiB;;;cCuChB,MAAA,GAAU,GAAA,UAAa,OAAA,GAAS,SAAA,KAAY,OAAA;;;cCpH5C,kBAAA,EAAoB,kBAAkB;AAAA,iBASnC,WAAA,CAAY,IAAA,UAAc,OAAA,GAAU,iBAAiB"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { renderToString, renderToStringAsync } from "solid-js/web/dist/server.js";
|
|
2
|
+
import * as html from "prettier/plugins/html";
|
|
3
|
+
import { format } from "prettier/standalone";
|
|
4
|
+
import { convert } from "html-to-text";
|
|
5
|
+
//#region src/shared/utils/pretty.ts
|
|
6
|
+
function getHtmlNode(path) {
|
|
7
|
+
const topNode = path.node;
|
|
8
|
+
if (topNode) return topNode;
|
|
9
|
+
return path.stack?.[path.stack.length - 1];
|
|
10
|
+
}
|
|
11
|
+
function recursivelyMapDoc(doc, callback) {
|
|
12
|
+
if (Array.isArray(doc)) return doc.map((innerDoc) => recursivelyMapDoc(innerDoc, callback));
|
|
13
|
+
if (typeof doc === "object") {
|
|
14
|
+
if (doc.type === "line") return callback(doc.soft ? "" : " ");
|
|
15
|
+
if (doc.type === "group") return {
|
|
16
|
+
...doc,
|
|
17
|
+
contents: recursivelyMapDoc(doc.contents, callback),
|
|
18
|
+
expandedStates: recursivelyMapDoc(doc.expandedStates, callback)
|
|
19
|
+
};
|
|
20
|
+
if ("contents" in doc) return {
|
|
21
|
+
...doc,
|
|
22
|
+
contents: recursivelyMapDoc(doc.contents, callback)
|
|
23
|
+
};
|
|
24
|
+
if ("parts" in doc) return {
|
|
25
|
+
...doc,
|
|
26
|
+
parts: recursivelyMapDoc(doc.parts, callback)
|
|
27
|
+
};
|
|
28
|
+
if (doc.type === "if-break") return {
|
|
29
|
+
...doc,
|
|
30
|
+
breakContents: recursivelyMapDoc(doc.breakContents, callback),
|
|
31
|
+
flatContents: recursivelyMapDoc(doc.flatContents, callback)
|
|
32
|
+
};
|
|
33
|
+
const nextDoc = { ...doc };
|
|
34
|
+
for (const [key, value] of Object.entries(nextDoc)) if (value && typeof value === "object") nextDoc[key] = recursivelyMapDoc(value, callback);
|
|
35
|
+
return nextDoc;
|
|
36
|
+
}
|
|
37
|
+
return callback(doc);
|
|
38
|
+
}
|
|
39
|
+
const modifiedHtml = { ...html };
|
|
40
|
+
const htmlPrinter = modifiedHtml.printers?.html;
|
|
41
|
+
if (htmlPrinter?.print) {
|
|
42
|
+
const previousPrint = htmlPrinter.print;
|
|
43
|
+
htmlPrinter.print = (path, options, print, args) => {
|
|
44
|
+
const node = getHtmlNode(path);
|
|
45
|
+
const rawPrintingResult = previousPrint(path, options, print, args);
|
|
46
|
+
if (node?.type === "ieConditionalComment" || node?.kind === "ieConditionalComment") return recursivelyMapDoc(rawPrintingResult, (doc) => {
|
|
47
|
+
if (typeof doc === "object" && doc.type === "line") return doc.soft ? "" : " ";
|
|
48
|
+
return doc;
|
|
49
|
+
});
|
|
50
|
+
return rawPrintingResult;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const defaults = {
|
|
54
|
+
endOfLine: "lf",
|
|
55
|
+
tabWidth: 2,
|
|
56
|
+
plugins: [modifiedHtml],
|
|
57
|
+
bracketSameLine: true,
|
|
58
|
+
parser: "html"
|
|
59
|
+
};
|
|
60
|
+
const pretty = (str, options = {}) => {
|
|
61
|
+
return format(str.replaceAll("\0", ""), {
|
|
62
|
+
...defaults,
|
|
63
|
+
...options
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/shared/utils/to-plain-text.ts
|
|
68
|
+
const plainTextSelectors = [
|
|
69
|
+
{
|
|
70
|
+
selector: "img",
|
|
71
|
+
format: "skip"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
selector: "[data-skip-in-text=true]",
|
|
75
|
+
format: "skip"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
selector: "a",
|
|
79
|
+
options: {
|
|
80
|
+
linkBrackets: false,
|
|
81
|
+
hideLinkHrefIfSameAsText: true
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
];
|
|
85
|
+
function toPlainText(html, options) {
|
|
86
|
+
return convert(html, {
|
|
87
|
+
wordwrap: false,
|
|
88
|
+
...options,
|
|
89
|
+
selectors: [...plainTextSelectors, ...options?.selectors ?? []]
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/shared/render.ts
|
|
94
|
+
const doctype = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
|
|
95
|
+
function normalizeRenderable(node) {
|
|
96
|
+
return typeof node === "function" ? node : () => node;
|
|
97
|
+
}
|
|
98
|
+
function decodeSerializedString(value) {
|
|
99
|
+
try {
|
|
100
|
+
const jsonSafe = value.replace(/\\x([0-9A-Fa-f]{2})/g, (_match, hex) => String.fromCharCode(Number.parseInt(hex, 16)));
|
|
101
|
+
const parsed = JSON.parse(`"${jsonSafe}"`);
|
|
102
|
+
return typeof parsed === "string" ? parsed : value;
|
|
103
|
+
} catch {
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function removeSolidResourceScripts(html) {
|
|
108
|
+
const errorMatch = /Object\.assign\(new Error\("((?:\\.|[^"\\])*)"\)/.exec(html);
|
|
109
|
+
if (errorMatch) throw new Error(decodeSerializedString(errorMatch[1] ?? ""));
|
|
110
|
+
return html.replace(/<script>self\.\$R=self\.\$R\|\|\[\];[\s\S]*?<\/script>/g, "");
|
|
111
|
+
}
|
|
112
|
+
function renderDocument(html) {
|
|
113
|
+
return `${doctype}${html.replace(/<!DOCTYPE.*?>/, "")}`;
|
|
114
|
+
}
|
|
115
|
+
function renderSyncOutput(html, options) {
|
|
116
|
+
if (options?.plainText) return toPlainText(html, options.htmlToTextOptions);
|
|
117
|
+
return renderDocument(html);
|
|
118
|
+
}
|
|
119
|
+
async function renderOutput(html, options) {
|
|
120
|
+
if (options?.plainText) return toPlainText(html, options.htmlToTextOptions);
|
|
121
|
+
const document = renderDocument(html);
|
|
122
|
+
if (options?.pretty) return pretty(document);
|
|
123
|
+
return document;
|
|
124
|
+
}
|
|
125
|
+
async function render(node, options) {
|
|
126
|
+
return renderOutput(removeSolidResourceScripts(await renderToStringAsync(normalizeRenderable(node))), options);
|
|
127
|
+
}
|
|
128
|
+
function renderSync(node, options) {
|
|
129
|
+
if (options?.pretty) throw new Error("renderSync does not support pretty output; use render.");
|
|
130
|
+
return renderSyncOutput(removeSolidResourceScripts(renderToString(normalizeRenderable(node))), options);
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { plainTextSelectors, pretty, render, renderSync, toPlainText };
|
|
134
|
+
|
|
135
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/shared/utils/pretty.ts","../../src/shared/utils/to-plain-text.ts","../../src/shared/render.ts"],"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 | undefined;\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;\nconst htmlPrinter = modifiedHtml.printers?.html;\nif (htmlPrinter?.print) {\n const previousPrint = htmlPrinter.print;\n htmlPrinter.print = (path, options, print, args) => {\n const node = getHtmlNode(path as Parameters<typeof previousPrint>[0]);\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\n// Text-export note: images and\n// preview-only nodes are skipped, and links render without duplicated hrefs.\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 { JSX } from 'solid-js';\nimport {\n renderToString,\n renderToStringAsync,\n} from 'solid-js/web/dist/server.js';\nimport type { Options, RenderSyncOptions } from './options';\nimport { pretty } from './utils/pretty';\nimport { toPlainText } from './utils/to-plain-text';\n\nexport type Renderable = JSX.Element | (() => JSX.Element);\n\nconst doctype =\n '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">';\n\nfunction normalizeRenderable(node: Renderable) {\n return typeof node === 'function' ? (node as () => JSX.Element) : () => node;\n}\n\nfunction decodeSerializedString(value: string): string {\n try {\n const jsonSafe = value.replace(\n /\\\\x([0-9A-Fa-f]{2})/g,\n (_match, hex: string) => String.fromCharCode(Number.parseInt(hex, 16)),\n );\n const parsed: unknown = JSON.parse(`\"${jsonSafe}\"`);\n return typeof parsed === 'string' ? parsed : value;\n } catch {\n return value;\n }\n}\n\nfunction removeSolidResourceScripts(html: string): string {\n const errorMatch = /Object\\.assign\\(new Error\\(\"((?:\\\\.|[^\"\\\\])*)\"\\)/.exec(\n html,\n );\n if (errorMatch) {\n throw new Error(decodeSerializedString(errorMatch[1] ?? ''));\n }\n return html.replace(\n /<script>self\\.\\$R=self\\.\\$R\\|\\|\\[\\];[\\s\\S]*?<\\/script>/g,\n '',\n );\n}\n\nfunction renderDocument(html: string): string {\n return `${doctype}${html.replace(/<!DOCTYPE.*?>/, '')}`;\n}\n\nfunction renderSyncOutput(html: string, options?: RenderSyncOptions): string {\n if (options?.plainText) {\n return toPlainText(html, options.htmlToTextOptions);\n }\n\n return renderDocument(html);\n}\n\nasync function renderOutput(html: string, options?: Options): Promise<string> {\n if (options?.plainText) {\n return toPlainText(html, options.htmlToTextOptions);\n }\n\n const document = renderDocument(html);\n\n if (options?.pretty) {\n return pretty(document);\n }\n\n return document;\n}\n\n// API note: render accepts either a component function or an already\n// constructed node. Keep that API while rendering through Solid SSR.\nexport async function render(\n node: Renderable,\n options?: Options,\n): Promise<string> {\n const html = removeSolidResourceScripts(\n await renderToStringAsync(normalizeRenderable(node)),\n );\n\n return renderOutput(html, options);\n}\n\nexport function renderSync(\n node: Renderable,\n options?: RenderSyncOptions,\n): string {\n if (options?.pretty) {\n throw new Error('renderSync does not support pretty output; use render.');\n }\n\n const html = removeSolidResourceScripts(\n renderToString(normalizeRenderable(node)),\n );\n\n return renderSyncOutput(html, options);\n}\n"],"mappings":"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"}
|