nexdf 0.1.0 → 0.1.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/README.md +43 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +19 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ Plug-and-play PDF generation for Next.js using customizable HTML/HTMX templates
|
|
|
9
9
|
- Tailwind-friendly templates you can edit directly
|
|
10
10
|
- A4-ready default template
|
|
11
11
|
- Fast runtime path: browser prewarm + page pool reuse + template compile cache
|
|
12
|
+
- Optional DB template resolver with file-template fallback
|
|
12
13
|
|
|
13
14
|
## Install
|
|
14
15
|
|
|
@@ -50,6 +51,7 @@ curl -X POST http://localhost:3000/api/pdf \
|
|
|
50
51
|
-H "content-type: application/json" \
|
|
51
52
|
-d '{
|
|
52
53
|
"filename": "invoice.pdf",
|
|
54
|
+
"templateKey": "basic",
|
|
53
55
|
"templatePath": "basic.html",
|
|
54
56
|
"data": {
|
|
55
57
|
"title": "Invoice #001",
|
|
@@ -86,6 +88,7 @@ Body:
|
|
|
86
88
|
```json
|
|
87
89
|
{
|
|
88
90
|
"filename": "document.pdf",
|
|
91
|
+
"templateKey": "basic",
|
|
89
92
|
"templatePath": "basic.html",
|
|
90
93
|
"data": {
|
|
91
94
|
"title": "Any value"
|
|
@@ -93,6 +96,14 @@ Body:
|
|
|
93
96
|
}
|
|
94
97
|
```
|
|
95
98
|
|
|
99
|
+
`templateKey` is available when you use a custom template resolver.
|
|
100
|
+
|
|
101
|
+
Template resolution behavior:
|
|
102
|
+
|
|
103
|
+
1. If `resolveTemplate` returns HTML, that HTML is rendered.
|
|
104
|
+
2. If `resolveTemplate` returns `null`, file-template rendering is used.
|
|
105
|
+
3. File-template path is `templatePath` or `defaultTemplate`.
|
|
106
|
+
|
|
96
107
|
## Runtime requirements
|
|
97
108
|
|
|
98
109
|
- Next route runtime must be `nodejs`
|
|
@@ -123,4 +134,36 @@ export const POST = createPdfRouteHandler({
|
|
|
123
134
|
defaultTemplate: "basic.html",
|
|
124
135
|
poolSize: 2
|
|
125
136
|
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Database-backed templates
|
|
140
|
+
|
|
141
|
+
Use `resolveTemplate` to fetch HTML from DB and render server-side:
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
export const POST = createPdfRouteHandler({
|
|
145
|
+
templatesDir: process.cwd() + "/templates/pdf",
|
|
146
|
+
defaultTemplate: "basic.html",
|
|
147
|
+
poolSize: 2,
|
|
148
|
+
resolveTemplate: async ({ body }) => {
|
|
149
|
+
const templateKey = body.templateKey ?? "basic";
|
|
150
|
+
const html = await loadTemplateHtmlFromDb(templateKey);
|
|
151
|
+
return html;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
If `resolveTemplate` returns `null`, file-based template loading is used as fallback.
|
|
157
|
+
|
|
158
|
+
Example request body with DB-first + file fallback support:
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"filename": "document.pdf",
|
|
163
|
+
"templateKey": "basic",
|
|
164
|
+
"templatePath": "basic.html",
|
|
165
|
+
"data": {
|
|
166
|
+
"title": "Any value"
|
|
167
|
+
}
|
|
168
|
+
}
|
|
126
169
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -8,13 +8,20 @@ declare function listPlaceholders(template: string): string[];
|
|
|
8
8
|
type PdfRequestBody = {
|
|
9
9
|
data?: TemplateData;
|
|
10
10
|
templatePath?: string;
|
|
11
|
+
templateKey?: string;
|
|
11
12
|
filename?: string;
|
|
12
13
|
};
|
|
14
|
+
type ResolveTemplateContext = {
|
|
15
|
+
body: PdfRequestBody;
|
|
16
|
+
templatesDir: string;
|
|
17
|
+
defaultTemplate: string;
|
|
18
|
+
};
|
|
13
19
|
type CreatePdfRouteHandlerOptions = {
|
|
14
20
|
templatesDir?: string;
|
|
15
21
|
defaultTemplate?: string;
|
|
16
22
|
prewarm?: boolean;
|
|
17
23
|
poolSize?: number;
|
|
24
|
+
resolveTemplate?: (context: ResolveTemplateContext) => Promise<string | null>;
|
|
18
25
|
};
|
|
19
26
|
declare function createPdfRouteHandler(options?: CreatePdfRouteHandlerOptions): (request: Request) => Promise<Response>;
|
|
20
27
|
|
package/dist/index.js
CHANGED
|
@@ -189,16 +189,26 @@ function createPdfRouteHandler(options = {}) {
|
|
|
189
189
|
try {
|
|
190
190
|
const body = await request.json();
|
|
191
191
|
const filename = body.filename || "document.pdf";
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
192
|
+
const resolvedTemplate = options.resolveTemplate ? await options.resolveTemplate({
|
|
193
|
+
body,
|
|
194
|
+
templatesDir,
|
|
195
|
+
defaultTemplate
|
|
196
|
+
}) : null;
|
|
197
|
+
let html;
|
|
198
|
+
if (resolvedTemplate) {
|
|
199
|
+
html = renderTemplateString(resolvedTemplate, body.data ?? {});
|
|
200
|
+
} else {
|
|
201
|
+
const templateName = body.templatePath || defaultTemplate;
|
|
202
|
+
const safeTemplatePath = path.resolve(templatesDir, templateName);
|
|
203
|
+
if (!safeTemplatePath.startsWith(path.resolve(templatesDir))) {
|
|
204
|
+
return Response.json(
|
|
205
|
+
{ error: "Invalid templatePath. Template must be inside templates directory." },
|
|
206
|
+
{ status: 400 }
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
await access(safeTemplatePath, fsConstants.R_OK);
|
|
210
|
+
html = await renderTemplateFile(safeTemplatePath, body.data ?? {});
|
|
199
211
|
}
|
|
200
|
-
await access(safeTemplatePath, fsConstants.R_OK);
|
|
201
|
-
const html = await renderTemplateFile(safeTemplatePath, body.data ?? {});
|
|
202
212
|
const pdfBuffer = await generatePdfBuffer({ html });
|
|
203
213
|
return new Response(new Uint8Array(pdfBuffer), {
|
|
204
214
|
status: 200,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexdf",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Plug-and-play Next.js PDF endpoint with customizable HTML/HTMX templates",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -48,4 +48,4 @@
|
|
|
48
48
|
"tsup": "^8.3.5",
|
|
49
49
|
"typescript": "^5.7.3"
|
|
50
50
|
}
|
|
51
|
-
}
|
|
51
|
+
}
|