@formepdf/renderer 0.7.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.
@@ -0,0 +1,2 @@
1
+ export declare const BUNDLE_DIR: string;
2
+ export declare function bundleFile(filePath: string): Promise<string>;
package/dist/bundle.js ADDED
@@ -0,0 +1,86 @@
1
+ import { build } from 'esbuild';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { dirname, join } from 'node:path';
4
+ const __dirname = dirname(fileURLToPath(import.meta.url));
5
+ /// The temp directory for bundled output — placed inside renderer package
6
+ /// so that Node's module resolution finds @formepdf/react, @formepdf/core, react.
7
+ export const BUNDLE_DIR = join(__dirname, '..');
8
+ /// esbuild plugin that intercepts react/jsx-dev-runtime to capture source
9
+ /// locations in a global WeakMap. React 19 no longer stores _source on
10
+ /// elements, so we wrap jsxDEV to do it ourselves.
11
+ const formeJsxSourcePlugin = {
12
+ name: 'forme-jsx-source',
13
+ setup(pluginBuild) {
14
+ pluginBuild.onResolve({ filter: /^react\/jsx-dev-runtime$/ }, () => ({
15
+ path: 'forme-jsx-dev-runtime',
16
+ namespace: 'forme-jsx',
17
+ }));
18
+ pluginBuild.onLoad({ filter: /.*/, namespace: 'forme-jsx' }, () => {
19
+ const cwd = pluginBuild.initialOptions.absWorkingDir || process.cwd();
20
+ return {
21
+ contents: `
22
+ import { jsx, Fragment } from 'react/jsx-runtime';
23
+ import { resolve, isAbsolute } from 'node:path';
24
+ export { Fragment };
25
+ if (!globalThis.__formeSourceMap) globalThis.__formeSourceMap = new WeakMap();
26
+ const _cwd = ${JSON.stringify(cwd)};
27
+ export function jsxDEV(type, props, key, isStaticChildren, source, self) {
28
+ const el = jsx(type, props, key);
29
+ if (source && source.fileName) {
30
+ try {
31
+ const file = isAbsolute(source.fileName) ? source.fileName : resolve(_cwd, source.fileName);
32
+ globalThis.__formeSourceMap.set(el, { file, line: source.lineNumber, column: source.columnNumber });
33
+ } catch(e) {}
34
+ }
35
+ return el;
36
+ }
37
+ `,
38
+ resolveDir: cwd,
39
+ loader: 'js',
40
+ };
41
+ });
42
+ },
43
+ };
44
+ /// Bundle a TSX/JSX file into an ESM string that can be dynamically imported.
45
+ export async function bundleFile(filePath) {
46
+ try {
47
+ const result = await build({
48
+ entryPoints: [filePath],
49
+ bundle: true,
50
+ format: 'esm',
51
+ platform: 'node',
52
+ write: false,
53
+ jsx: 'automatic',
54
+ jsxDev: true,
55
+ target: 'node20',
56
+ external: ['react', '@formepdf/react', '@formepdf/core'],
57
+ plugins: [formeJsxSourcePlugin],
58
+ });
59
+ return result.outputFiles[0].text;
60
+ }
61
+ catch (err) {
62
+ if (isBuildFailure(err)) {
63
+ const messages = [];
64
+ for (const error of err.errors) {
65
+ let loc = '';
66
+ if (error.location) {
67
+ const { file, line, column, lineText } = error.location;
68
+ loc = ` ${file}:${line}:${column}\n`;
69
+ if (lineText) {
70
+ loc += ` ${lineText}\n`;
71
+ loc += ` ${' '.repeat(column)}^\n`;
72
+ }
73
+ }
74
+ messages.push(`${error.text}\n${loc}`);
75
+ }
76
+ throw new Error(`Build error:\n${messages.join('\n')}`);
77
+ }
78
+ throw err;
79
+ }
80
+ }
81
+ function isBuildFailure(err) {
82
+ return (err !== null &&
83
+ typeof err === 'object' &&
84
+ 'errors' in err &&
85
+ Array.isArray(err.errors));
86
+ }
@@ -0,0 +1,6 @@
1
+ import { type ReactElement } from 'react';
2
+ export interface ResolveElementOptions {
3
+ dataPath?: string;
4
+ data?: unknown;
5
+ }
6
+ export declare function resolveElement(mod: Record<string, unknown>, options?: ResolveElementOptions): Promise<ReactElement>;
@@ -0,0 +1,59 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ import { isValidElement } from 'react';
4
+ /// Validate a module's default export and resolve it to a React element.
5
+ /// Supports both static JSX exports and functions that accept data.
6
+ /// When `data` is provided, it takes precedence over `dataPath`.
7
+ export async function resolveElement(mod, options) {
8
+ const exported = mod.default;
9
+ if (exported === undefined) {
10
+ throw new Error(`No default export found.\n\n` +
11
+ ` Your file must export a Forme element or a function that returns one:\n\n` +
12
+ ` export default (\n` +
13
+ ` <Document>\n` +
14
+ ` <Text>Hello</Text>\n` +
15
+ ` </Document>\n` +
16
+ ` );\n\n` +
17
+ ` Or with data:\n\n` +
18
+ ` export default function Report(data) {\n` +
19
+ ` return <Document><Text>{data.title}</Text></Document>\n` +
20
+ ` }`);
21
+ }
22
+ if (typeof exported === 'function') {
23
+ let data = {};
24
+ if (options?.data !== undefined) {
25
+ data = options.data;
26
+ }
27
+ else if (options?.dataPath) {
28
+ data = await loadJsonData(options.dataPath);
29
+ }
30
+ const result = await exported(data);
31
+ if (!isValidElement(result)) {
32
+ throw new Error(`Default export function did not return a valid Forme element.\n` +
33
+ ` Got: ${typeof result}\n` +
34
+ ` Make sure your function returns a <Document> element.`);
35
+ }
36
+ return result;
37
+ }
38
+ if (isValidElement(exported)) {
39
+ if (options?.dataPath) {
40
+ console.warn(`Warning: --data flag provided but default export is a static element, not a function.\n` +
41
+ ` The data file will be ignored. Export a function to use --data.`);
42
+ }
43
+ return exported;
44
+ }
45
+ throw new Error(`Default export is not a valid Forme element.\n` +
46
+ ` Got: ${typeof exported}\n` +
47
+ ` Expected: a <Document> element or a function that returns one.`);
48
+ }
49
+ async function loadJsonData(dataPath) {
50
+ const absolutePath = resolve(dataPath);
51
+ const raw = await readFile(absolutePath, 'utf-8');
52
+ try {
53
+ return JSON.parse(raw);
54
+ }
55
+ catch {
56
+ throw new Error(`Failed to parse data file as JSON: ${absolutePath}\n` +
57
+ ` Make sure the file contains valid JSON.`);
58
+ }
59
+ }
@@ -0,0 +1,4 @@
1
+ export { renderFromFile, renderFromCode, renderFromElement, type RenderOptions, type RenderResult, } from './render.js';
2
+ export { resolveElement, type ResolveElementOptions, } from './element.js';
3
+ export { bundleFile, } from './bundle.js';
4
+ export { resolveFontSources, resolveImageSources, resolveAllSources, uint8ArrayToBase64, } from './resolve.js';
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { renderFromFile, renderFromCode, renderFromElement, } from './render.js';
2
+ export { resolveElement, } from './element.js';
3
+ export { bundleFile, } from './bundle.js';
4
+ export { resolveFontSources, resolveImageSources, resolveAllSources, uint8ArrayToBase64, } from './resolve.js';