structured-render 0.0.0 → 0.0.1
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-CC0 +121 -0
- package/LICENSE-MIT +21 -0
- package/README.md +13 -0
- package/dist/augments/shadow-styles.d.ts +7 -0
- package/dist/augments/shadow-styles.js +17 -0
- package/dist/elements/vir-markdown.element.d.ts +16 -0
- package/dist/elements/vir-markdown.element.js +73 -0
- package/dist/elements/vir-source.element.d.ts +24 -0
- package/dist/elements/vir-source.element.js +158 -0
- package/dist/elements/vir-structured-render.element.d.ts +19 -0
- package/dist/elements/vir-structured-render.element.js +346 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +26 -0
- package/dist/render/browser-rendering.d.ts +90 -0
- package/dist/render/browser-rendering.js +233 -0
- package/dist/render/html-to-pdf.d.ts +50 -0
- package/dist/render/html-to-pdf.js +7 -0
- package/dist/render/render-html.d.ts +38 -0
- package/dist/render/render-html.js +504 -0
- package/dist/render/render-image.d.ts +24 -0
- package/dist/render/render-image.js +35 -0
- package/dist/render/render-markdown-styles.d.ts +62 -0
- package/dist/render/render-markdown-styles.js +242 -0
- package/dist/render/render-markdown.d.ts +8 -0
- package/dist/render/render-markdown.js +169 -0
- package/dist/render/render-pdf.d.ts +35 -0
- package/dist/render/render-pdf.js +65 -0
- package/dist/render/render-types.d.ts +134 -0
- package/dist/render/render-types.js +41 -0
- package/dist/structured-render-data/create-section.d.ts +23 -0
- package/dist/structured-render-data/create-section.js +16 -0
- package/dist/structured-render-data/sections/code-block.section.d.ts +30 -0
- package/dist/structured-render-data/sections/code-block.section.js +11 -0
- package/dist/structured-render-data/sections/empty.section.d.ts +14 -0
- package/dist/structured-render-data/sections/empty.section.js +9 -0
- package/dist/structured-render-data/sections/icon.section.d.ts +25 -0
- package/dist/structured-render-data/sections/icon.section.js +40 -0
- package/dist/structured-render-data/sections/inline-code.section.d.ts +29 -0
- package/dist/structured-render-data/sections/inline-code.section.js +9 -0
- package/dist/structured-render-data/sections/list.section.d.ts +169 -0
- package/dist/structured-render-data/sections/list.section.js +29 -0
- package/dist/structured-render-data/sections/markdown.section.d.ts +29 -0
- package/dist/structured-render-data/sections/markdown.section.js +9 -0
- package/dist/structured-render-data/sections/processing.section.d.ts +14 -0
- package/dist/structured-render-data/sections/processing.section.js +9 -0
- package/dist/structured-render-data/sections/source.section.d.ts +59 -0
- package/dist/structured-render-data/sections/source.section.js +47 -0
- package/dist/structured-render-data/sections/table.section.d.ts +939 -0
- package/dist/structured-render-data/sections/table.section.js +99 -0
- package/dist/structured-render-data/sections/tag.section.d.ts +39 -0
- package/dist/structured-render-data/sections/tag.section.js +20 -0
- package/dist/structured-render-data/sections/text.section.d.ts +48 -0
- package/dist/structured-render-data/sections/text.section.js +25 -0
- package/dist/structured-render-data/structured-render-card.d.ts +918 -0
- package/dist/structured-render-data/structured-render-card.js +11 -0
- package/dist/structured-render-data/structured-render-data.d.ts +919 -0
- package/dist/structured-render-data/structured-render-data.js +8 -0
- package/dist/structured-render-data/structured-render-section.d.ts +1857 -0
- package/dist/structured-render-data/structured-render-section.js +115 -0
- package/package.json +75 -11
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { assert, assertWrap } from '@augment-vir/assert';
|
|
2
|
+
import { isRuntimeEnv, mergeDefinedProperties, RuntimeEnv, stringify, } from '@augment-vir/common';
|
|
3
|
+
import DOMPurify from 'dompurify';
|
|
4
|
+
import { convertTemplateToString, html } from 'element-vir';
|
|
5
|
+
import { marked } from 'marked';
|
|
6
|
+
import { importHtml2Pdf } from './html-to-pdf.js';
|
|
7
|
+
import { contentDivClass } from './render-markdown-styles.js';
|
|
8
|
+
import { renderStructuredMarkdown } from './render-markdown.js';
|
|
9
|
+
import { defaultRenderMarkdownOptions, } from './render-types.js';
|
|
10
|
+
const globalStyleId = 'structured-rendering-style-id';
|
|
11
|
+
// cspell:disable
|
|
12
|
+
/**
|
|
13
|
+
* Output method types for jsPDF. Extracted from:
|
|
14
|
+
* https://raw.githack.com/MrRio/jsPDF/master/docs/jsPDF.html#output
|
|
15
|
+
*
|
|
16
|
+
* @category Internal
|
|
17
|
+
*/
|
|
18
|
+
export var OutputPdfType;
|
|
19
|
+
(function (OutputPdfType) {
|
|
20
|
+
/** Returns the PDF as an `ArrayBuffer`. */
|
|
21
|
+
OutputPdfType["ArrayBuffer"] = "arraybuffer";
|
|
22
|
+
/** Returns the PDF as a `Blob`. */
|
|
23
|
+
OutputPdfType["Blob"] = "blob";
|
|
24
|
+
/** Returns a blob URI string pointing to the generated PDF. */
|
|
25
|
+
OutputPdfType["BlobUri"] = "bloburi";
|
|
26
|
+
/** Alias for `BlobUri`. Returns a blob URL string pointing to the generated PDF. */
|
|
27
|
+
OutputPdfType["BlobUrl"] = "bloburl";
|
|
28
|
+
/** Returns the PDF as a base-64 data URI string. */
|
|
29
|
+
OutputPdfType["DataUriString"] = "datauristring";
|
|
30
|
+
/** Alias for `DataUriString`. Returns the PDF as a base-64 data URL string. */
|
|
31
|
+
OutputPdfType["DataUrlString"] = "dataurlstring";
|
|
32
|
+
/** Navigates the current page location to the generated data URI. Returns `undefined`. */
|
|
33
|
+
OutputPdfType["DataUri"] = "datauri";
|
|
34
|
+
/**
|
|
35
|
+
* Alias for `DataUri`. Navigates the current page location to the generated data URL. Returns
|
|
36
|
+
* `undefined`.
|
|
37
|
+
*/
|
|
38
|
+
OutputPdfType["DataUrl"] = "dataurl";
|
|
39
|
+
/**
|
|
40
|
+
* Opens the generated PDF data URL in a new window. Returns the `Window` or `null`. Throws if
|
|
41
|
+
* the global is not a window object (e.g. Node).
|
|
42
|
+
*/
|
|
43
|
+
OutputPdfType["DataUrlNewWindow"] = "dataurlnewwindow";
|
|
44
|
+
/**
|
|
45
|
+
* Opens a PDF object in a new window. Returns the `Window` or `null`. Throws if the global is
|
|
46
|
+
* not a window object (e.g. Node).
|
|
47
|
+
*/
|
|
48
|
+
OutputPdfType["PdfObjectNewWindow"] = "pdfobjectnewwindow";
|
|
49
|
+
/** Opens the PDF via pdf.js in a new window. Returns the `Window` or `null`. */
|
|
50
|
+
OutputPdfType["PdfJsNewWindow"] = "pdfjsnewwindow";
|
|
51
|
+
/** Simply download the file. */
|
|
52
|
+
OutputPdfType["Download"] = "download";
|
|
53
|
+
})(OutputPdfType || (OutputPdfType = {}));
|
|
54
|
+
// cspell:enable
|
|
55
|
+
// cspell:disable
|
|
56
|
+
/**
|
|
57
|
+
* Html2Pdf.js image output output types.
|
|
58
|
+
*
|
|
59
|
+
* @category Internal
|
|
60
|
+
*/
|
|
61
|
+
export var OutputImageType;
|
|
62
|
+
(function (OutputImageType) {
|
|
63
|
+
/** Returns an img element as a string. */
|
|
64
|
+
OutputImageType["Img"] = "img";
|
|
65
|
+
/** Returns a data URI string. */
|
|
66
|
+
OutputImageType["DataUriString"] = "datauristring";
|
|
67
|
+
/** Returns a data URI string. Alias for `DataUriString`. */
|
|
68
|
+
OutputImageType["DataUrlString"] = "dataurlstring";
|
|
69
|
+
/** Returns a data URI. */
|
|
70
|
+
OutputImageType["DataUri"] = "datauri";
|
|
71
|
+
/** Returns a data URI. Alias for `DataUri`. */
|
|
72
|
+
OutputImageType["DataUrl"] = "dataurl";
|
|
73
|
+
/** Simply download the file. */
|
|
74
|
+
OutputImageType["Download"] = "download";
|
|
75
|
+
})(OutputImageType || (OutputImageType = {}));
|
|
76
|
+
// cspell:enable
|
|
77
|
+
function createHtml2PdfOptions(fileName) {
|
|
78
|
+
return {
|
|
79
|
+
margin: [
|
|
80
|
+
25,
|
|
81
|
+
15,
|
|
82
|
+
],
|
|
83
|
+
filename: fileName,
|
|
84
|
+
image: {
|
|
85
|
+
type: 'jpeg',
|
|
86
|
+
quality: 0.98,
|
|
87
|
+
},
|
|
88
|
+
html2canvas: {
|
|
89
|
+
scale: 2,
|
|
90
|
+
logging: false,
|
|
91
|
+
scrollX: 0,
|
|
92
|
+
scrollY: 0,
|
|
93
|
+
useCORS: true,
|
|
94
|
+
},
|
|
95
|
+
jsPDF: {
|
|
96
|
+
unit: 'mm',
|
|
97
|
+
format: 'a4',
|
|
98
|
+
orientation: 'portrait',
|
|
99
|
+
},
|
|
100
|
+
// cspell:word pagebreak
|
|
101
|
+
pagebreak: {
|
|
102
|
+
mode: [
|
|
103
|
+
'css',
|
|
104
|
+
'legacy',
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Internal common in-browser structure render renderer.
|
|
111
|
+
*
|
|
112
|
+
* @category Internal
|
|
113
|
+
*/
|
|
114
|
+
export async function renderInBrowser(structuredRenderData, { fileName, outputType, options: userOptions, }) {
|
|
115
|
+
if (!isRuntimeEnv(RuntimeEnv.Web)) {
|
|
116
|
+
throw new Error(`${renderInNode.name} cannot run outside of a browser.`);
|
|
117
|
+
}
|
|
118
|
+
const options = mergeDefinedProperties(defaultRenderMarkdownOptions, userOptions);
|
|
119
|
+
const htmlToPdf = await importHtml2Pdf();
|
|
120
|
+
const dirtyHtml = await marked.parse(renderStructuredMarkdown(structuredRenderData, options));
|
|
121
|
+
const styleElement = assertWrap.instanceOf(globalThis.document.head.querySelector(`#${globalStyleId}`) ||
|
|
122
|
+
globalThis.document.createElement('style'), HTMLStyleElement);
|
|
123
|
+
styleElement.id = globalStyleId;
|
|
124
|
+
if (!styleElement.isConnected) {
|
|
125
|
+
globalThis.document.head.append(styleElement);
|
|
126
|
+
}
|
|
127
|
+
styleElement.textContent = String(options.styles);
|
|
128
|
+
const htmlString = convertTemplateToString(html `
|
|
129
|
+
<div class=${contentDivClass}>${DOMPurify.sanitize(dirtyHtml)}</div>
|
|
130
|
+
`);
|
|
131
|
+
const instance = htmlToPdf().set(createHtml2PdfOptions(fileName)).from(htmlString);
|
|
132
|
+
try {
|
|
133
|
+
if (outputType.pdf) {
|
|
134
|
+
if (outputType.pdf === OutputPdfType.Download) {
|
|
135
|
+
return await instance.save(fileName);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
return await instance.outputPdf(outputType.pdf, {
|
|
139
|
+
filename: fileName,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
143
|
+
}
|
|
144
|
+
else if (outputType.image) {
|
|
145
|
+
if (outputType.image === OutputImageType.Download) {
|
|
146
|
+
return await instance.toImg().save(fileName);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
return await instance.outputImg(outputType.image);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
assert.tsType(outputType).equals();
|
|
154
|
+
throw new Error(`Invalid output type: ${stringify(outputType)}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
styleElement.remove();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Internal common Node.js structure render renderer.
|
|
163
|
+
*
|
|
164
|
+
* @category Internal
|
|
165
|
+
* @returns The output file path.
|
|
166
|
+
*/
|
|
167
|
+
export async function renderInNode(structuredRenderData, { saveLocationPath, outputType, options: userOptions, }) {
|
|
168
|
+
if (isRuntimeEnv(RuntimeEnv.Web)) {
|
|
169
|
+
throw new Error(`${renderInNode.name} cannot run inside of a browser.`);
|
|
170
|
+
}
|
|
171
|
+
const options = mergeDefinedProperties(defaultRenderMarkdownOptions, userOptions);
|
|
172
|
+
const { chromium } = await import('playwright');
|
|
173
|
+
const { readFile, writeFile, mkdir } = await import('node:fs/promises');
|
|
174
|
+
const { createRequire } = await import('node:module');
|
|
175
|
+
const { basename, dirname } = await import('node:path');
|
|
176
|
+
const require = createRequire(import.meta.url);
|
|
177
|
+
const [html2pdfScript, domPurifyScript,] = await Promise.all([
|
|
178
|
+
readFile(require.resolve('html2pdf.js/dist/html2pdf.bundle.min.js'), 'utf-8'),
|
|
179
|
+
readFile(require.resolve('dompurify/dist/purify.min.js'), 'utf-8'),
|
|
180
|
+
]);
|
|
181
|
+
const dirtyHtml = await marked.parse(renderStructuredMarkdown(structuredRenderData, options));
|
|
182
|
+
const baseHtmlString = convertTemplateToString(html `
|
|
183
|
+
<style id="styles">
|
|
184
|
+
${options.styles}
|
|
185
|
+
</style>
|
|
186
|
+
`);
|
|
187
|
+
const browser = await chromium.launch();
|
|
188
|
+
try {
|
|
189
|
+
const page = await browser.newPage();
|
|
190
|
+
await page.setContent(baseHtmlString, { waitUntil: 'networkidle' });
|
|
191
|
+
await page.addScriptTag({ content: html2pdfScript });
|
|
192
|
+
await page.addScriptTag({ content: domPurifyScript });
|
|
193
|
+
const html2pdfOptions = createHtml2PdfOptions(basename(saveLocationPath));
|
|
194
|
+
if (outputType.image) {
|
|
195
|
+
html2pdfOptions.image = { type: 'png' };
|
|
196
|
+
}
|
|
197
|
+
const pdfBase64 = await page.evaluate(async ({ html2pdfOptions, outputType, dirtyMarkdown, wrapperClass, outputImageType, outputPdfType, }) => {
|
|
198
|
+
const cleanHtml = DOMPurify.sanitize(dirtyMarkdown);
|
|
199
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
200
|
+
const instance = html2pdf()
|
|
201
|
+
.set(html2pdfOptions)
|
|
202
|
+
.from(`<div class="${wrapperClass}">${cleanHtml}</div>`);
|
|
203
|
+
if (outputType.image) {
|
|
204
|
+
return await instance.outputImg(outputImageType, {
|
|
205
|
+
filename: html2pdfOptions.filename,
|
|
206
|
+
});
|
|
207
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
208
|
+
}
|
|
209
|
+
else if (outputType.pdf) {
|
|
210
|
+
return await instance.outputPdf(outputPdfType, {
|
|
211
|
+
filename: html2pdfOptions.filename,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
throw new Error('Invalid output type selected.');
|
|
216
|
+
}
|
|
217
|
+
}, {
|
|
218
|
+
html2pdfOptions,
|
|
219
|
+
outputType,
|
|
220
|
+
dirtyMarkdown: dirtyHtml,
|
|
221
|
+
wrapperClass: contentDivClass,
|
|
222
|
+
outputImageType: OutputImageType.DataUriString,
|
|
223
|
+
outputPdfType: OutputPdfType.DataUriString,
|
|
224
|
+
});
|
|
225
|
+
const base64Data = pdfBase64.split(',')[1];
|
|
226
|
+
await mkdir(dirname(saveLocationPath), { recursive: true });
|
|
227
|
+
await writeFile(saveLocationPath, Buffer.from(base64Data, 'base64'));
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
await browser.close();
|
|
231
|
+
}
|
|
232
|
+
return saveLocationPath;
|
|
233
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export declare function importHtml2Pdf(): Promise<Html2Pdf>;
|
|
2
|
+
/** Html2pdf.js's internal types are a complete disaster; we have to reconstruct them all like this. */
|
|
3
|
+
export type Html2PdfOptions = {
|
|
4
|
+
margin?: number | [number, number] | [number, number, number, number];
|
|
5
|
+
filename?: string;
|
|
6
|
+
image?: {
|
|
7
|
+
type?: 'jpeg' | 'png' | 'webp';
|
|
8
|
+
quality?: number;
|
|
9
|
+
};
|
|
10
|
+
enableLinks?: boolean;
|
|
11
|
+
html2canvas?: object;
|
|
12
|
+
jsPDF?: {
|
|
13
|
+
unit?: string;
|
|
14
|
+
format?: string | [number, number];
|
|
15
|
+
orientation?: 'portrait' | 'landscape';
|
|
16
|
+
};
|
|
17
|
+
pagebreak?: {
|
|
18
|
+
mode?: ReadonlyArray<string>;
|
|
19
|
+
before?: string;
|
|
20
|
+
after?: string;
|
|
21
|
+
avoid?: string;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
export interface Html2PdfInstance {
|
|
25
|
+
from(src: HTMLElement | string | HTMLCanvasElement | HTMLImageElement): this;
|
|
26
|
+
to(target: 'container' | 'canvas' | 'img' | 'pdf'): this;
|
|
27
|
+
toContainer(): this;
|
|
28
|
+
toCanvas(): this;
|
|
29
|
+
toImg(): this;
|
|
30
|
+
toPdf(): this;
|
|
31
|
+
output(type?: string, options?: any, src?: 'pdf' | 'img'): Promise<any>;
|
|
32
|
+
outputPdf(type?: string, options?: any): Promise<any>;
|
|
33
|
+
outputImg(type?: string, options?: any): Promise<any>;
|
|
34
|
+
save(filename?: string): Promise<void>;
|
|
35
|
+
set(options: Html2PdfOptions): this;
|
|
36
|
+
get(key: string, cbk?: (value: any) => void): Promise<any>;
|
|
37
|
+
then<T>(onFulfilled?: (value: any) => T | PromiseLike<T>, onRejected?: (reason: any) => any): Promise<T>;
|
|
38
|
+
thenCore<T>(onFulfilled?: (value: any) => T | PromiseLike<T>, onRejected?: (reason: any) => any): Promise<T>;
|
|
39
|
+
thenExternal<T>(onFulfilled?: (value: any) => T | PromiseLike<T>, onRejected?: (reason: any) => any): Promise<T>;
|
|
40
|
+
catch<T>(onRejected?: (reason: any) => T | PromiseLike<T>): Promise<T>;
|
|
41
|
+
catchExternal<T>(onRejected?: (reason: any) => T | PromiseLike<T>): Promise<T>;
|
|
42
|
+
error(msg: string): void;
|
|
43
|
+
}
|
|
44
|
+
export interface Html2Pdf {
|
|
45
|
+
(): Html2PdfInstance;
|
|
46
|
+
new (): Html2PdfInstance;
|
|
47
|
+
(element: HTMLElement, options?: Html2PdfOptions): Promise<void>;
|
|
48
|
+
new (element: HTMLElement, options?: Html2PdfOptions): Promise<void>;
|
|
49
|
+
Worker: new () => Html2PdfInstance;
|
|
50
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type PartialWithUndefined } from '@augment-vir/common';
|
|
2
|
+
import { type HtmlInterpolation } from 'element-vir';
|
|
3
|
+
import { StructuredRenderSectionType } from '../structured-render-data/structured-render-section.js';
|
|
4
|
+
import { type RenderHtmlOptions, type RenderInput } from './render-types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Render Structured Render data to HTML templates.
|
|
7
|
+
*
|
|
8
|
+
* @category Render
|
|
9
|
+
*/
|
|
10
|
+
export declare function renderStructuredHtml(data: Readonly<RenderInput>, options?: Readonly<PartialWithUndefined<RenderHtmlOptions>> | undefined): HtmlInterpolation;
|
|
11
|
+
/**
|
|
12
|
+
* Used for both HTML class names and test ids.
|
|
13
|
+
*
|
|
14
|
+
* @category Internal
|
|
15
|
+
*/
|
|
16
|
+
export declare const structuredRenderSectionHtmlNames: Record<StructuredRenderSectionType, string>;
|
|
17
|
+
/**
|
|
18
|
+
* A test id that you can use to target a section in the Structured Render HTML output.
|
|
19
|
+
*
|
|
20
|
+
* @category Internal
|
|
21
|
+
*/
|
|
22
|
+
export declare const structuredRenderSectionHtmlTestId = "structured-render-section";
|
|
23
|
+
/**
|
|
24
|
+
* This event is emitted when source sections have been expanded and can be used to track which
|
|
25
|
+
* sections are expanded.
|
|
26
|
+
*
|
|
27
|
+
* @category Internal
|
|
28
|
+
*/
|
|
29
|
+
export declare const SourceExpansionEvent: import("element-vir").DefinedTypedEvent<"source-expansion", Readonly<{
|
|
30
|
+
expanded: boolean;
|
|
31
|
+
key: string;
|
|
32
|
+
}>>;
|
|
33
|
+
/**
|
|
34
|
+
* Makes a chain key used to track which HTML sections are opened.
|
|
35
|
+
*
|
|
36
|
+
* @category Internal
|
|
37
|
+
*/
|
|
38
|
+
export declare function makeChainKey(keyChain: ReadonlyArray<PropertyKey>): string;
|