@lupinum/nuxt-pdf 0.3.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.
Files changed (75) hide show
  1. package/API_REPORT.md +262 -0
  2. package/CHANGELOG.md +168 -0
  3. package/CONFORMANCE.md +496 -0
  4. package/LICENSE +21 -0
  5. package/README.md +203 -0
  6. package/THIRD_PARTY_NOTICES.md +75 -0
  7. package/dist/module.d.mts +24 -0
  8. package/dist/module.json +12 -0
  9. package/dist/module.mjs +368 -0
  10. package/dist/runtime/authoring.d.ts +141 -0
  11. package/dist/runtime/authoring.js +25 -0
  12. package/dist/runtime/components/_props.d.ts +196 -0
  13. package/dist/runtime/components/_props.js +63 -0
  14. package/dist/runtime/components/document.d.ts +9 -0
  15. package/dist/runtime/components/document.js +26 -0
  16. package/dist/runtime/components/index.d.ts +4 -0
  17. package/dist/runtime/components/index.js +27 -0
  18. package/dist/runtime/components/svg.d.ts +17 -0
  19. package/dist/runtime/components/svg.js +50 -0
  20. package/dist/runtime/composables/index.d.ts +2 -0
  21. package/dist/runtime/composables/index.js +3 -0
  22. package/dist/runtime/composables/use-pdf-page-numbers.d.ts +39 -0
  23. package/dist/runtime/composables/use-pdf-page-numbers.js +17 -0
  24. package/dist/runtime/define-pdf.d.ts +6 -0
  25. package/dist/runtime/define-pdf.js +5 -0
  26. package/dist/runtime/fonts.d.ts +16 -0
  27. package/dist/runtime/fonts.js +0 -0
  28. package/dist/runtime/renderer/index.d.ts +3 -0
  29. package/dist/runtime/renderer/index.js +1 -0
  30. package/dist/runtime/renderer/node-ops.d.ts +5 -0
  31. package/dist/runtime/renderer/node-ops.js +281 -0
  32. package/dist/runtime/renderer/patch-prop.d.ts +3 -0
  33. package/dist/runtime/renderer/patch-prop.js +223 -0
  34. package/dist/runtime/renderer/render-component.d.ts +14 -0
  35. package/dist/runtime/renderer/render-component.js +201 -0
  36. package/dist/runtime/renderer/types.d.ts +33 -0
  37. package/dist/runtime/renderer/types.js +56 -0
  38. package/dist/runtime/renderer/validate-tree.d.ts +3 -0
  39. package/dist/runtime/renderer/validate-tree.js +428 -0
  40. package/dist/runtime/server/assets/errors.d.ts +12 -0
  41. package/dist/runtime/server/assets/errors.js +15 -0
  42. package/dist/runtime/server/assets/remote.d.ts +48 -0
  43. package/dist/runtime/server/assets/remote.js +234 -0
  44. package/dist/runtime/server/assets/resolve-asset.d.ts +53 -0
  45. package/dist/runtime/server/assets/resolve-asset.js +482 -0
  46. package/dist/runtime/server/engine/CONTRACTS.md +386 -0
  47. package/dist/runtime/server/engine/fonts.d.ts +8 -0
  48. package/dist/runtime/server/engine/fonts.js +19 -0
  49. package/dist/runtime/server/engine/layout-passes.d.ts +42 -0
  50. package/dist/runtime/server/engine/layout-passes.js +58 -0
  51. package/dist/runtime/server/engine/react-pdf-pdfkit.d.ts +7 -0
  52. package/dist/runtime/server/engine/render-document.d.ts +31 -0
  53. package/dist/runtime/server/engine/render-document.js +236 -0
  54. package/dist/runtime/server/index.d.ts +5 -0
  55. package/dist/runtime/server/index.js +9 -0
  56. package/dist/runtime/server/preview.d.ts +17 -0
  57. package/dist/runtime/server/preview.js +332 -0
  58. package/dist/runtime/server/registry.d.ts +41 -0
  59. package/dist/runtime/server/registry.js +270 -0
  60. package/dist/runtime/server/render-limits.d.ts +51 -0
  61. package/dist/runtime/server/render-limits.js +143 -0
  62. package/dist/runtime/server/result.d.ts +6 -0
  63. package/dist/runtime/server/result.js +81 -0
  64. package/dist/runtime/server/tsconfig.json +3 -0
  65. package/dist/runtime/shared/errors.d.ts +22 -0
  66. package/dist/runtime/shared/errors.js +22 -0
  67. package/dist/runtime/shared/index.d.ts +4 -0
  68. package/dist/runtime/shared/index.js +7 -0
  69. package/dist/runtime/shared/template.d.ts +63 -0
  70. package/dist/runtime/shared/template.js +1 -0
  71. package/dist/shared/nuxt-pdf.D2ZziYn4.mjs +1132 -0
  72. package/dist/test.d.mts +198 -0
  73. package/dist/test.mjs +696 -0
  74. package/dist/types.d.mts +13 -0
  75. package/package.json +135 -0
package/README.md ADDED
@@ -0,0 +1,203 @@
1
+ <p align="center">
2
+ <img src="docs/public/logo.svg" width="128" alt="Nuxt PDF">
3
+ </p>
4
+
5
+ <h1 align="center">Nuxt PDF</h1>
6
+
7
+ <p align="center">
8
+ Author and render PDFs with Vue components in Nuxt.
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/package/@lupinum/nuxt-pdf"><img src="https://img.shields.io/npm/v/@lupinum/nuxt-pdf?color=315d3b" alt="npm version"></a>
13
+ <a href="https://github.com/lupinum-dev/nuxt-pdf/actions/workflows/ci.yml"><img src="https://github.com/lupinum-dev/nuxt-pdf/actions/workflows/ci.yml/badge.svg" alt="CI status"></a>
14
+ <a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-315d3b" alt="MIT license"></a>
15
+ </p>
16
+
17
+ > [!IMPORTANT]
18
+ > Nuxt PDF is an external alpha. Its tested behavior is stable within each
19
+ > release, but a minor release can contain documented breaking changes before
20
+ > version 1.0.
21
+
22
+ Nuxt PDF discovers Vue Single File Components in `pdfs/`. It renders these
23
+ components on the Node server through a PDF layout engine. The client bundle
24
+ does not contain the renderer or document templates.
25
+
26
+ Use Nuxt PDF for structured documents such as invoices, reports,
27
+ certificates, tickets, and labels.
28
+
29
+ Do not use Nuxt PDF to print an existing web page, display an existing PDF, or
30
+ edit and sign an existing PDF.
31
+
32
+ ## Requirements
33
+
34
+ - Node.js `^22.14.0`, `^24.0.0`, or `^26.0.0`
35
+ - Nuxt `^4.4.8`
36
+ - Vue `^3.5.0`
37
+
38
+ Nuxt 3, Node 20, browser rendering, and edge rendering are outside the current
39
+ support boundary.
40
+
41
+ ## Install
42
+
43
+ Install the module in a Nuxt 4 application:
44
+
45
+ ```bash
46
+ pnpm add @lupinum/nuxt-pdf
47
+ ```
48
+
49
+ Add the module to `nuxt.config.ts`:
50
+
51
+ ```ts
52
+ export default defineNuxtConfig({
53
+ modules: ['@lupinum/nuxt-pdf'],
54
+ })
55
+ ```
56
+
57
+ ## Render a document
58
+
59
+ Create `pdfs/invoice.vue`:
60
+
61
+ ```vue
62
+ <script setup lang="ts">
63
+ type InvoiceProps = {
64
+ invoice: {
65
+ customer: string
66
+ number: string
67
+ total: string
68
+ }
69
+ }
70
+
71
+ const props = defineProps<InvoiceProps>()
72
+
73
+ definePdf<InvoiceProps>({
74
+ title: ({ invoice }) => `Invoice ${invoice.number}`,
75
+ filename: ({ invoice }) => `invoice-${invoice.number}.pdf`,
76
+ sampleData: {
77
+ invoice: {
78
+ customer: 'Ada Lovelace',
79
+ number: 'INV-001',
80
+ total: 'EUR 1,250.00',
81
+ },
82
+ },
83
+ })
84
+ </script>
85
+
86
+ <template>
87
+ <PdfDocument>
88
+ <PdfPage size="A4" :style="{ fontSize: 11, padding: 48 }">
89
+ <PdfText :style="{ fontSize: 24, marginBottom: 24 }">
90
+ Invoice {{ props.invoice.number }}
91
+ </PdfText>
92
+ <PdfText>{{ props.invoice.customer }}</PdfText>
93
+ <PdfText :style="{ marginTop: 12 }">
94
+ Total: {{ props.invoice.total }}
95
+ </PdfText>
96
+ </PdfPage>
97
+ </PdfDocument>
98
+ </template>
99
+ ```
100
+
101
+ Create `server/api/invoice.get.ts`:
102
+
103
+ ```ts
104
+ import { pdf } from '#pdf'
105
+
106
+ export default defineEventHandler(async () => {
107
+ const result = await pdf.invoice.render({
108
+ invoice: {
109
+ customer: 'Ada Lovelace',
110
+ number: 'INV-001',
111
+ total: 'EUR 1,250.00',
112
+ },
113
+ })
114
+
115
+ return result.response()
116
+ })
117
+ ```
118
+
119
+ Start Nuxt. Open these development routes:
120
+
121
+ - `/_pdf` lists the discovered templates.
122
+ - `/_pdf/invoice` shows the document preview.
123
+ - `/api/invoice` returns the production-style PDF response.
124
+
125
+ Restart `nuxt dev` after you first enable the module. This action generates
126
+ the typed `#pdf` registry.
127
+
128
+ ## Authoring model
129
+
130
+ PDF templates use typed props, interpolation, `v-if`, keyed `v-for`, slots,
131
+ and local Vue components. Nuxt PDF provides document, page, text, image, link,
132
+ note, and SVG primitives.
133
+
134
+ Templates run in an isolated Vue application on the Node server. They do not
135
+ inherit Nuxt plugins, app-level provides, browser globals, or DOM components.
136
+ Load request data before the render and pass it through typed props.
137
+
138
+ Nuxt PDF supports local images and fonts. Remote images are disabled until an
139
+ operator configures an explicit HTTPS allowlist.
140
+
141
+ Read the [documentation](https://nuxt-pdf.lupinum.com) for authoring, assets,
142
+ links, bookmarks, tables of contents, testing, and the complete API reference.
143
+ Read [CONFORMANCE.md](./CONFORMANCE.md) for the exact tested behavior and known
144
+ limitations.
145
+
146
+ ## Test a document
147
+
148
+ Install the optional test dependencies:
149
+
150
+ ```bash
151
+ pnpm add -D pdfjs-dist @napi-rs/canvas
152
+ ```
153
+
154
+ Render the real template in a test:
155
+
156
+ ```ts
157
+ import { expectPdf, renderPdfSfc } from '@lupinum/nuxt-pdf/test'
158
+
159
+ const { parsed } = await renderPdfSfc('./pdfs/invoice.vue', {
160
+ invoice: {
161
+ customer: 'Ada Lovelace',
162
+ number: 'INV-001',
163
+ total: 'EUR 1,250.00',
164
+ },
165
+ })
166
+
167
+ expectPdf(parsed)
168
+ .toHavePageCount(1)
169
+ .toContainText('Invoice INV-001')
170
+ ```
171
+
172
+ The test entry can also inspect links, bookmarks, page geometry, and raster
173
+ baselines.
174
+
175
+ ## Develop Nuxt PDF
176
+
177
+ ```bash
178
+ pnpm install --frozen-lockfile
179
+ pnpm dev
180
+ pnpm check
181
+ ```
182
+
183
+ Use `pnpm docs:dev` to run the documentation site. Use
184
+ `pnpm release:verify` only for release preparation.
185
+
186
+ Read [CONTRIBUTING.md](./CONTRIBUTING.md) before you open a pull request.
187
+ Maintainers use [MAINTAINING.md](./MAINTAINING.md) for release and recovery
188
+ procedures.
189
+
190
+ ## Support and security
191
+
192
+ - Read the [documentation](https://nuxt-pdf.lupinum.com).
193
+ - Join the [Lupinum OSS Discord](https://discord.gg/RPH6SeA36N).
194
+ - Open a [GitHub issue](https://github.com/lupinum-dev/nuxt-pdf/issues) for a
195
+ reproducible defect.
196
+ - Follow [SECURITY.md](./SECURITY.md) for a private vulnerability report.
197
+
198
+ ## License
199
+
200
+ [MIT](./LICENSE) © Lupinum OG and contributors.
201
+
202
+ Third-party attributions are in
203
+ [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md).
@@ -0,0 +1,75 @@
1
+ # Third-party notices
2
+
3
+ ## React PDF
4
+
5
+ Nuxt PDF uses the published lower-level React PDF engine packages and carries
6
+ paired test fixtures derived from the React PDF repository at commit
7
+ `d41a8207fb06a56e60fcb53ac0e18ce27e7d32d6`.
8
+
9
+ React PDF is distributed under the following MIT license:
10
+
11
+ ```text
12
+ MIT License
13
+
14
+ Copyright (c) 2017-present Diego Muracciole <diegomuracciole@gmail.com>
15
+
16
+ Permission is hereby granted, free of charge, to any person obtaining a copy
17
+ of this software and associated documentation files (the "Software"), to deal
18
+ in the Software without restriction, including without limitation the rights
19
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20
+ copies of the Software, and to permit persons to whom the Software is
21
+ furnished to do so, subject to the following conditions:
22
+
23
+ The above copyright notice and this permission notice shall be included in all
24
+ copies or substantial portions of the Software.
25
+
26
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32
+ SOFTWARE.
33
+ ```
34
+
35
+ The exact engine packages and versions are recorded in
36
+ `src/runtime/server/engine/CONTRACTS.md`; the package build carries that record
37
+ at `dist/runtime/server/engine/CONTRACTS.md`. Their own package distributions
38
+ remain subject to their included licenses.
39
+
40
+ ## Fixture provenance
41
+
42
+ The following files originate from the React PDF test suite at the commit
43
+ above and are retained only as local conformance, integration, or playground
44
+ fixtures:
45
+
46
+ | Nuxt PDF fixture | React PDF source |
47
+ |---|---|
48
+ | `test/fixtures/assets/Roboto-Regular.ttf` | `packages/layout/tests/assets/font.ttf` |
49
+ | `test/fixtures/assets/sample.png` | `packages/layout/tests/assets/test.png` |
50
+
51
+ The copies under `test/fixtures/basic/pdfs/` use the same fixture bytes. The
52
+ playground copies the font as `pdfs/fonts/Roboto-Regular.ttf` and the image as
53
+ `pdfs/assets/alpine.png`.
54
+
55
+ The font identifies itself as Roboto Regular, copyright 2011 Google Inc., by
56
+ Christian Robertson, and as licensed under the Apache License 2.0:
57
+ <https://www.apache.org/licenses/LICENSE-2.0>.
58
+
59
+ These fixture binaries are not included in the published Nuxt PDF package.
60
+
61
+ ## Source Code Pro test dependency
62
+
63
+ The development-only `source-code-pro` npm package supplies OTF and variable
64
+ font fixtures for executable compatibility tests. Source Code Pro is Copyright
65
+ 2010–2023 Adobe and distributed under the SIL Open Font License 1.1. It is not
66
+ included in the published Nuxt PDF package; its license is included in the npm
67
+ dependency at `source-code-pro/LICENSE.md`.
68
+
69
+ ## Noto international typography subsets
70
+
71
+ The test-only international typography fixture contains small FontTools subsets
72
+ of Noto Sans SC Variable, Noto Sans Arabic Variable, and Noto Emoji Variable.
73
+ They retain only the characters used by the executable calibration document,
74
+ are distributed under the SIL Open Font License 1.1 included beside the files,
75
+ and are not included in the published Nuxt PDF package.
@@ -0,0 +1,24 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+ import { RemoteAssetOptions } from '../dist/runtime/server/assets/remote.js';
3
+ export { RemoteAssetOptions } from '../dist/runtime/server/assets/remote.js';
4
+ import { PdfLimitsOptions } from '../dist/runtime/server/render-limits.js';
5
+ export { PdfLimitsOptions } from '../dist/runtime/server/render-limits.js';
6
+ import { PdfFontDeclaration } from '../dist/runtime/fonts.js';
7
+ export { PdfFontDeclaration, PdfFontStyle, PdfFontWeight, PdfFontWeightName } from '../dist/runtime/fonts.js';
8
+ export { PdfLength, PdfLengthOrPercentage, PdfPercentage, PdfStyle, PdfStyleEntry, PdfStyleValue } from '../dist/runtime/authoring.js';
9
+ export { PdfBaseProps, PdfBookmark, PdfCircleProps, PdfClipPathProps, PdfDefsProps, PdfDocumentProps, PdfEllipseProps, PdfGProps, PdfImageProps, PdfImageSource, PdfLineProps, PdfLinearGradientProps, PdfLinkProps, PdfNoteProps, PdfPageDimension, PdfPageProps, PdfPageSize, PdfPageSizeName, PdfPageUnit, PdfPathProps, PdfPolygonProps, PdfPolylineProps, PdfRadialGradientProps, PdfRectProps, PdfStopProps, PdfSvgLength, PdfSvgNumber, PdfSvgPresentationProps, PdfSvgProps, PdfSvgTransform, PdfSvgTransformOperation, PdfTextProps, PdfTspanProps, PdfViewProps } from '../dist/runtime/components/index.js';
10
+
11
+ interface ModuleOptions {
12
+ fonts?: readonly PdfFontDeclaration[];
13
+ remote?: RemoteAssetOptions;
14
+ /**
15
+ * Canonical render, tree, image, remote-request, and output budgets. Every
16
+ * field is an optional positive safe integer; omitted fields use the
17
+ * documented built-in defaults.
18
+ */
19
+ limits?: PdfLimitsOptions;
20
+ }
21
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
22
+
23
+ export { _default as default };
24
+ export type { ModuleOptions };
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "nuxt-pdf",
3
+ "configKey": "pdf",
4
+ "compatibility": {
5
+ "nuxt": "^4.4.8"
6
+ },
7
+ "version": "0.3.0",
8
+ "builder": {
9
+ "@nuxt/module-builder": "1.0.2",
10
+ "unbuild": "unknown"
11
+ }
12
+ }
@@ -0,0 +1,368 @@
1
+ import { stat } from 'node:fs/promises';
2
+ import { join, isAbsolute, resolve } from 'node:path';
3
+ import { defineNuxtModule, createResolver, getLayerDirectories, createIsIgnored, addServerTemplate, addTemplate, addImports, addTypeTemplate, addServerHandler } from '@nuxt/kit';
4
+ import { d as discoverPdfTemplates, a as discoverPdfComponentFiles, b as discoverPdfImageFiles, c as bundlePdfFonts, e as createPdfSfcPlugin, f as classifyPdfWatchEvent } from './shared/nuxt-pdf.D2ZziYn4.mjs';
5
+ import { Buffer } from 'node:buffer';
6
+ import { joinURL } from 'ufo';
7
+ import { normalizeRemoteAssetPolicy } from '../dist/runtime/server/assets/remote.js';
8
+ import { loadPdfImageAsset } from '../dist/runtime/server/assets/resolve-asset.js';
9
+ import { normalizePdfLimits, DEFAULT_PDF_RENDER_LIMITS } from '../dist/runtime/server/render-limits.js';
10
+ import 'node:fs';
11
+ import '@vue/compiler-sfc';
12
+ import '@jridgewell/remapping';
13
+ import 'esbuild';
14
+ import 'unimport';
15
+ import '../dist/runtime/shared/template.js';
16
+
17
+ const compareText = (left, right) => {
18
+ if (left < right) return -1;
19
+ if (left > right) return 1;
20
+ return 0;
21
+ };
22
+ const quote$1 = (value) => JSON.stringify(value);
23
+ const generatePdfPreviewConfig = (baseURL, buildAssetsDir) => `export const hmrClientPath = ${quote$1(joinURL(
24
+ baseURL.replace(/^\.\//, "/") || "/",
25
+ buildAssetsDir,
26
+ "@vite/client"
27
+ ))}
28
+ `;
29
+ const importPath = (filePath) => filePath.replaceAll("\\", "/");
30
+ const runtimeOptionsSource = (options) => {
31
+ const assets = options.assets ?? [];
32
+ const fonts = options.fonts ?? [];
33
+ const { remote, limits } = options;
34
+ if (assets.length === 0 && fonts.length === 0 && !remote && !limits) return [];
35
+ const lines = ["", "const __pdfRuntimeOptions = Object.freeze({"];
36
+ if (assets.length > 0) {
37
+ lines.push(" assets: Object.freeze({");
38
+ for (const asset of assets) {
39
+ lines.push(
40
+ ` ${quote$1(asset.key)}: Object.freeze({ data: __pdfBuffer.from(${quote$1(Buffer.from(asset.data).toString("base64"))}, 'base64'), format: ${quote$1(asset.format)} }),`
41
+ );
42
+ }
43
+ lines.push(" }),");
44
+ }
45
+ if (fonts.length > 0) {
46
+ lines.push(" fonts: Object.freeze([");
47
+ for (const font of fonts) {
48
+ lines.push(` Object.freeze(${JSON.stringify(font)}),`);
49
+ }
50
+ lines.push(" ]),");
51
+ }
52
+ if (remote) {
53
+ lines.push(` remote: Object.freeze(${JSON.stringify(remote)}),`);
54
+ }
55
+ if (limits) {
56
+ lines.push(` limits: Object.freeze(${JSON.stringify(limits)}),`);
57
+ }
58
+ lines.push("})");
59
+ return lines;
60
+ };
61
+ const orderedTemplates = (templates) => {
62
+ const result = [...templates].sort(
63
+ (left, right) => compareText(left.key, right.key)
64
+ );
65
+ const keys = /* @__PURE__ */ new Map();
66
+ for (const template of result) {
67
+ const duplicate = keys.get(template.key);
68
+ if (duplicate) {
69
+ throw new TypeError(
70
+ `Cannot generate duplicate PDF template key "${template.key}" from "${duplicate.filePath}" and "${template.filePath}".`
71
+ );
72
+ }
73
+ keys.set(template.key, template);
74
+ }
75
+ return result;
76
+ };
77
+ const validateOptions = (options) => {
78
+ if (options.runtimeImport.trim() === "") {
79
+ throw new TypeError("runtimeImport is required to generate the PDF registry.");
80
+ }
81
+ };
82
+ const generatePdfRuntimeRegistry = (templates, options) => {
83
+ validateOptions(options);
84
+ const ordered = orderedTemplates(templates);
85
+ const runtimeImports = options.development ? "createPdfPreviewEntry, createPdfRegistry, createPdfTemplate" : "createPdfRegistry, createPdfTemplate";
86
+ const lines = [
87
+ `import { ${runtimeImports} } from ${quote$1(options.runtimeImport)}`
88
+ ];
89
+ if ((options.assets?.length ?? 0) > 0) {
90
+ lines.push("import { Buffer as __pdfBuffer } from 'node:buffer'");
91
+ }
92
+ ordered.forEach((template, index) => {
93
+ lines.push(
94
+ `import __pdfTemplate${index} from ${quote$1(importPath(template.filePath))}`
95
+ );
96
+ });
97
+ const runtimeOptions = runtimeOptionsSource(options);
98
+ lines.push(...runtimeOptions, "", "const registry = createPdfRegistry({");
99
+ ordered.forEach((template, index) => {
100
+ const file = quote$1(`pdfs/${template.relativePath}`);
101
+ const runtimeArgument = options.development ? runtimeOptions.length > 0 ? `, { ...__pdfRuntimeOptions, file: ${file} }` : `, { file: ${file} }` : runtimeOptions.length > 0 ? ", __pdfRuntimeOptions" : "";
102
+ lines.push(
103
+ ` ${quote$1(template.key)}: createPdfTemplate(${quote$1(template.key)}, __pdfTemplate${index}${runtimeArgument}),`
104
+ );
105
+ });
106
+ lines.push("})");
107
+ if (options.development) {
108
+ lines.push("", "export const pdfPreview = Object.freeze({");
109
+ ordered.forEach((template, index) => {
110
+ lines.push(
111
+ ` ${quote$1(template.key)}: createPdfPreviewEntry(registry.pdf[${quote$1(template.key)}], __pdfTemplate${index}, { file: ${quote$1(`pdfs/${template.relativePath}`)} }),`
112
+ );
113
+ });
114
+ lines.push("})");
115
+ }
116
+ lines.push(
117
+ "",
118
+ "export const pdf = registry.pdf",
119
+ "export const renderPdf = registry.renderPdf",
120
+ "export const getPdfTemplate = registry.getPdfTemplate",
121
+ "export const pdfTemplateKeys = registry.pdfTemplateKeys",
122
+ `export { NuxtPdfError, PDF_ERROR_CODES } from ${quote$1(options.runtimeImport)}`,
123
+ ""
124
+ );
125
+ return lines.join("\n");
126
+ };
127
+ const generatePdfRegistryTypes = (templates, options) => {
128
+ validateOptions(options);
129
+ const ordered = orderedTemplates(templates);
130
+ const lines = [
131
+ `import type { PdfComponentProps, PdfRenderResult, PdfTemplate } from ${quote$1(options.runtimeImport)}`
132
+ ];
133
+ ordered.forEach((template, index) => {
134
+ lines.push(
135
+ "",
136
+ `type PdfComponent${index} = typeof import(${quote$1(importPath(template.filePath))})['default']`,
137
+ `type PdfProps${index} = PdfComponentProps<PdfComponent${index}>`
138
+ );
139
+ });
140
+ lines.push("", "export declare const pdf: {");
141
+ ordered.forEach((template, index) => {
142
+ lines.push(
143
+ ` readonly ${quote$1(template.key)}: PdfTemplate<PdfProps${index}>`
144
+ );
145
+ });
146
+ lines.push("}", "");
147
+ if (ordered.length === 0) {
148
+ lines.push(
149
+ "export declare function renderPdf(name: never, props: never): Promise<PdfRenderResult>",
150
+ "export declare function getPdfTemplate(name: never): never"
151
+ );
152
+ } else {
153
+ ordered.forEach((template, index) => {
154
+ lines.push(
155
+ `export declare function renderPdf(name: ${quote$1(template.key)}, props: PdfProps${index}): Promise<PdfRenderResult>`
156
+ );
157
+ });
158
+ lines.push(
159
+ "export declare function renderPdf(name: string, props: Record<string, unknown>, escapeHatch: { readonly unsafe: true }): Promise<PdfRenderResult>"
160
+ );
161
+ lines.push("");
162
+ ordered.forEach((template, index) => {
163
+ lines.push(
164
+ `export declare function getPdfTemplate(name: ${quote$1(template.key)}): PdfTemplate<PdfProps${index}>`
165
+ );
166
+ });
167
+ }
168
+ lines.push(
169
+ "",
170
+ `export declare const pdfTemplateKeys: readonly [${ordered.map((template) => quote$1(template.key)).join(", ")}]`,
171
+ "export type PdfTemplateKey = typeof pdfTemplateKeys[number]",
172
+ `export { NuxtPdfError, PDF_ERROR_CODES } from ${quote$1(options.runtimeImport)}`,
173
+ `export type { PdfErrorCode } from ${quote$1(options.runtimeImport)}`,
174
+ ""
175
+ );
176
+ return lines.join("\n");
177
+ };
178
+
179
+ const quote = (value) => JSON.stringify(value);
180
+ const existingDirectories = async (directories) => {
181
+ const result = [];
182
+ for (const directory of directories) {
183
+ try {
184
+ if ((await stat(directory)).isDirectory()) result.push(directory);
185
+ } catch (error) {
186
+ if (error.code !== "ENOENT") throw error;
187
+ }
188
+ }
189
+ return result;
190
+ };
191
+ const generateAuthoringTypes = (componentsImport) => `declare module 'vue' {
192
+ interface GlobalComponents {
193
+ PdfDocument: typeof import(${quote(componentsImport)})['PdfDocument']
194
+ PdfImage: typeof import(${quote(componentsImport)})['PdfImage']
195
+ PdfLink: typeof import(${quote(componentsImport)})['PdfLink']
196
+ PdfNote: typeof import(${quote(componentsImport)})['PdfNote']
197
+ PdfPage: typeof import(${quote(componentsImport)})['PdfPage']
198
+ PdfText: typeof import(${quote(componentsImport)})['PdfText']
199
+ PdfView: typeof import(${quote(componentsImport)})['PdfView']
200
+ PdfSvg: typeof import(${quote(componentsImport)})['PdfSvg']
201
+ PdfG: typeof import(${quote(componentsImport)})['PdfG']
202
+ PdfPath: typeof import(${quote(componentsImport)})['PdfPath']
203
+ PdfRect: typeof import(${quote(componentsImport)})['PdfRect']
204
+ PdfCircle: typeof import(${quote(componentsImport)})['PdfCircle']
205
+ PdfEllipse: typeof import(${quote(componentsImport)})['PdfEllipse']
206
+ PdfLine: typeof import(${quote(componentsImport)})['PdfLine']
207
+ PdfPolyline: typeof import(${quote(componentsImport)})['PdfPolyline']
208
+ PdfPolygon: typeof import(${quote(componentsImport)})['PdfPolygon']
209
+ PdfDefs: typeof import(${quote(componentsImport)})['PdfDefs']
210
+ PdfClipPath: typeof import(${quote(componentsImport)})['PdfClipPath']
211
+ PdfLinearGradient: typeof import(${quote(componentsImport)})['PdfLinearGradient']
212
+ PdfRadialGradient: typeof import(${quote(componentsImport)})['PdfRadialGradient']
213
+ PdfStop: typeof import(${quote(componentsImport)})['PdfStop']
214
+ PdfTspan: typeof import(${quote(componentsImport)})['PdfTspan']
215
+ }
216
+ }
217
+
218
+ export {}
219
+ `;
220
+ const module$1 = defineNuxtModule({
221
+ meta: {
222
+ name: "nuxt-pdf",
223
+ configKey: "pdf",
224
+ compatibility: {
225
+ nuxt: "^4.4.8"
226
+ }
227
+ },
228
+ defaults: {
229
+ fonts: []
230
+ },
231
+ async setup(options, nuxt) {
232
+ const resolver = createResolver(import.meta.url);
233
+ const runtimeImport = resolver.resolve("./runtime/server/index");
234
+ const sharedImport = resolver.resolve("./runtime/shared/index");
235
+ const componentsImport = resolver.resolve("./runtime/components/index");
236
+ const composablesImport = resolver.resolve("./runtime/composables/index");
237
+ const definePdfImport = resolver.resolve("./runtime/define-pdf");
238
+ const previewHandler = resolver.resolve("./runtime/server/preview");
239
+ const layers = getLayerDirectories(nuxt).map(
240
+ (directories, index) => ({
241
+ rootDir: directories.root,
242
+ name: index === 0 ? "project" : `layer-${index}`
243
+ })
244
+ );
245
+ const isIgnored = createIsIgnored(nuxt);
246
+ const templates = await discoverPdfTemplates(layers, isIgnored);
247
+ const componentFiles = await discoverPdfComponentFiles(layers, isIgnored);
248
+ const limits = normalizePdfLimits(options.limits);
249
+ const resolvedLimits = limits ?? DEFAULT_PDF_RENDER_LIMITS;
250
+ const imageFiles = await discoverPdfImageFiles(layers, isIgnored);
251
+ const assets = await Promise.all(imageFiles.map(
252
+ (image) => loadPdfImageAsset(image.key, {
253
+ roots: [image.rootDir],
254
+ maxBytes: resolvedLimits.maxImageBytes,
255
+ maxPixels: resolvedLimits.maxImagePixels
256
+ })
257
+ ));
258
+ const fontRoots = await existingDirectories(
259
+ layers.map((layer) => join(layer.rootDir, "pdfs", "fonts"))
260
+ );
261
+ const remote = normalizeRemoteAssetPolicy(options.remote);
262
+ const fonts = await bundlePdfFonts(options.fonts ?? [], { fontRoots });
263
+ const pdfSfcFiles = new Map(
264
+ componentFiles.map((file) => [file, "component"])
265
+ );
266
+ for (const template of templates) {
267
+ pdfSfcFiles.set(template.filePath, "template");
268
+ }
269
+ for (const layer of layers) {
270
+ nuxt.options.watch.push(join(layer.rootDir, "pdfs"));
271
+ }
272
+ addServerTemplate({
273
+ filename: "#pdf",
274
+ getContents: () => generatePdfRuntimeRegistry(templates, {
275
+ assets,
276
+ development: nuxt.options.dev,
277
+ fonts,
278
+ remote,
279
+ limits,
280
+ runtimeImport
281
+ })
282
+ });
283
+ const registryTypeTemplate = addTemplate({
284
+ filename: "types/nuxt-pdf-registry.ts",
285
+ getContents: () => generatePdfRegistryTypes(templates, {
286
+ runtimeImport: sharedImport
287
+ }),
288
+ write: true
289
+ });
290
+ const addRegistryTypePath = (config) => {
291
+ const compilerOptions = config.compilerOptions ||= {};
292
+ const paths = compilerOptions.paths ||= {};
293
+ paths["#pdf"] = [registryTypeTemplate.dst];
294
+ };
295
+ nuxt.hook("prepare:types", ({ nodeTsConfig, tsConfig }) => {
296
+ addRegistryTypePath(tsConfig);
297
+ addRegistryTypePath(nodeTsConfig);
298
+ });
299
+ const nitroTypescript = nuxt.options.nitro.typescript ||= {};
300
+ const nitroTsConfig = nitroTypescript.tsConfig ||= {};
301
+ addRegistryTypePath(nitroTsConfig);
302
+ addImports({
303
+ name: "usePdfPageNumbers",
304
+ as: "usePdfPageNumbers",
305
+ from: composablesImport
306
+ });
307
+ addImports({
308
+ name: "definePdf",
309
+ as: "definePdf",
310
+ from: definePdfImport
311
+ });
312
+ addTypeTemplate({
313
+ filename: "types/nuxt-pdf-authoring.d.ts",
314
+ getContents: () => generateAuthoringTypes(
315
+ componentsImport
316
+ ),
317
+ write: true
318
+ }, { nitro: true, node: true, nuxt: true });
319
+ nuxt.hook("nitro:config", (nitroConfig) => {
320
+ const rollupConfig = nitroConfig.rollupConfig ||= {};
321
+ const existingPlugins = rollupConfig.plugins == null ? [] : Array.isArray(rollupConfig.plugins) ? rollupConfig.plugins : [rollupConfig.plugins];
322
+ rollupConfig.plugins = [
323
+ createPdfSfcPlugin({
324
+ files: pdfSfcFiles,
325
+ isProduction: !nuxt.options.dev,
326
+ composablesImport
327
+ }),
328
+ ...existingPlugins
329
+ ];
330
+ });
331
+ if (nuxt.options.dev) {
332
+ let clientViteServer;
333
+ nuxt.hook("vite:serverCreated", (viteServer, environment) => {
334
+ if (environment.isClient) clientViteServer = viteServer;
335
+ });
336
+ nuxt.hook("builder:watch", async (event, path) => {
337
+ const absolutePath = isAbsolute(path) ? path : resolve(nuxt.options.srcDir, path);
338
+ const action = classifyPdfWatchEvent(
339
+ event,
340
+ absolutePath,
341
+ layers,
342
+ isIgnored
343
+ );
344
+ if (action === "restart") {
345
+ await nuxt.callHook("restart");
346
+ return;
347
+ }
348
+ if (action !== "refresh") return;
349
+ clientViteServer?.ws.send({
350
+ type: "custom",
351
+ event: "nuxt-pdf:update",
352
+ data: {}
353
+ });
354
+ });
355
+ addServerTemplate({
356
+ filename: "#pdf-preview-config",
357
+ getContents: () => generatePdfPreviewConfig(
358
+ nuxt.options.app.baseURL,
359
+ nuxt.options.app.buildAssetsDir
360
+ )
361
+ });
362
+ addServerHandler({ route: "/_pdf", handler: previewHandler });
363
+ addServerHandler({ route: "/_pdf/**", handler: previewHandler });
364
+ }
365
+ }
366
+ });
367
+
368
+ export { module$1 as default };