@formepdf/cli 0.3.0 → 0.4.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/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import { parseArgs } from 'node:util';
4
4
  import { resolve } from 'node:path';
5
5
  import { startDevServer } from './dev.js';
6
6
  import { buildPdf } from './build.js';
7
+ import { buildTemplate } from './template-build.js';
7
8
  const USAGE = `
8
9
  forme - Page-native PDF rendering engine
9
10
 
@@ -12,8 +13,9 @@ Usage:
12
13
  forme build <file.tsx> Render PDF to disk
13
14
 
14
15
  Options:
15
- -o, --output <path> Output PDF path (build only, default: output.pdf)
16
+ -o, --output <path> Output path (default: output.pdf, or <name>.template.json with -t)
16
17
  -d, --data <path> JSON data file to pass to template function
18
+ -t, --template Compile to template JSON instead of rendering PDF
17
19
  -p, --port <number> Dev server port (default: 4242)
18
20
  -h, --help Show this help message
19
21
 
@@ -37,8 +39,9 @@ function main() {
37
39
  const { values, positionals } = parseArgs({
38
40
  allowPositionals: true,
39
41
  options: {
40
- output: { type: 'string', short: 'o', default: 'output.pdf' },
42
+ output: { type: 'string', short: 'o' },
41
43
  data: { type: 'string', short: 'd' },
44
+ template: { type: 'boolean', short: 't', default: false },
42
45
  port: { type: 'string', short: 'p', default: '4242' },
43
46
  help: { type: 'boolean', short: 'h', default: false },
44
47
  },
@@ -73,7 +76,12 @@ function main() {
73
76
  startDevServer(inputPath, { port: Number(values.port), dataPath });
74
77
  break;
75
78
  case 'build':
76
- buildPdf(inputPath, { output: values.output, dataPath });
79
+ if (values.template) {
80
+ buildTemplate(inputPath, { output: values.output });
81
+ }
82
+ else {
83
+ buildPdf(inputPath, { output: values.output ?? 'output.pdf', dataPath });
84
+ }
77
85
  break;
78
86
  default:
79
87
  console.error(`Unknown command: ${command}\n`);
@@ -0,0 +1,4 @@
1
+ export interface TemplateBuildOptions {
2
+ output?: string;
3
+ }
4
+ export declare function buildTemplate(inputPath: string, options: TemplateBuildOptions): Promise<void>;
@@ -0,0 +1,81 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ import { resolve, join, basename } from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { isValidElement } from 'react';
5
+ import { bundleFile, BUNDLE_DIR } from './bundle.js';
6
+ export async function buildTemplate(inputPath, options) {
7
+ const absoluteInput = resolve(inputPath);
8
+ console.log(`Building template from ${absoluteInput}...`);
9
+ try {
10
+ const code = await bundleFile(absoluteInput);
11
+ const tmpFile = join(BUNDLE_DIR, `.forme-template-${Date.now()}.mjs`);
12
+ await writeFile(tmpFile, code);
13
+ try {
14
+ const mod = await import(pathToFileURL(tmpFile).href);
15
+ const exported = mod.default;
16
+ if (exported === undefined) {
17
+ throw new Error(`No default export found.\n\n` +
18
+ ` Your template file must export a function that takes data and returns JSX:\n\n` +
19
+ ` export default function Invoice(data) {\n` +
20
+ ` return <Document><Text>{data.title}</Text></Document>\n` +
21
+ ` }`);
22
+ }
23
+ if (typeof exported !== 'function') {
24
+ throw new Error(`Default export must be a function for template compilation.\n` +
25
+ ` Got: ${typeof exported}\n\n` +
26
+ ` Export a function that takes data and returns a <Document>:\n\n` +
27
+ ` export default function Template(data) {\n` +
28
+ ` return <Document>...</Document>\n` +
29
+ ` }`);
30
+ }
31
+ // Create a recording proxy and call the template function
32
+ const { createDataProxy, serializeTemplate } = await import('@formepdf/react');
33
+ const dataProxy = createDataProxy();
34
+ const element = exported(dataProxy);
35
+ if (!isValidElement(element)) {
36
+ throw new Error(`Template function did not return a valid Forme element.\n` +
37
+ ` Got: ${typeof element}\n` +
38
+ ` Make sure your function returns a <Document> element.`);
39
+ }
40
+ const templateJson = serializeTemplate(element);
41
+ // Resolve fonts to base64 for portability
42
+ await resolveFontPaths(templateJson, absoluteInput);
43
+ const outputPath = resolve(options.output ?? defaultTemplateName(inputPath));
44
+ const json = JSON.stringify(templateJson, null, 2);
45
+ await writeFile(outputPath, json);
46
+ console.log(`Written template (${json.length} bytes) to ${outputPath}`);
47
+ }
48
+ finally {
49
+ const { unlink } = await import('node:fs/promises');
50
+ await unlink(tmpFile).catch(() => { });
51
+ }
52
+ }
53
+ catch (err) {
54
+ const message = err instanceof Error ? err.message : String(err);
55
+ console.error(`\n ${message.split('\n').join('\n ')}\n`);
56
+ process.exit(1);
57
+ }
58
+ }
59
+ function defaultTemplateName(inputPath) {
60
+ const base = basename(inputPath).replace(/\.(tsx|jsx|ts|js)$/, '');
61
+ return `${base}.template.json`;
62
+ }
63
+ async function resolveFontPaths(doc, templatePath) {
64
+ const { dirname } = await import('node:path');
65
+ const { readFile } = await import('node:fs/promises');
66
+ const { resolve: resolvePath } = await import('node:path');
67
+ const templateDir = dirname(templatePath);
68
+ const fonts = doc.fonts;
69
+ if (!fonts?.length)
70
+ return;
71
+ for (const font of fonts) {
72
+ if (font.src instanceof Uint8Array) {
73
+ font.src = Buffer.from(font.src).toString('base64');
74
+ }
75
+ else if (typeof font.src === 'string' && !font.src.startsWith('data:')) {
76
+ const fontPath = resolvePath(templateDir, font.src);
77
+ const bytes = await readFile(fontPath);
78
+ font.src = Buffer.from(bytes).toString('base64');
79
+ }
80
+ }
81
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formepdf/cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "CLI for Forme PDF rendering engine — dev server with live preview and build command",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,8 +14,8 @@
14
14
  "dist"
15
15
  ],
16
16
  "dependencies": {
17
- "@formepdf/core": "0.3.0",
18
- "@formepdf/react": "0.3.0",
17
+ "@formepdf/core": "0.4.1",
18
+ "@formepdf/react": "0.4.1",
19
19
  "esbuild": "^0.24.0",
20
20
  "chokidar": "^4.0.0",
21
21
  "ws": "^8.18.0",