@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
@@ -0,0 +1,1132 @@
1
+ import { readdir, realpath, open, stat, readFile } from 'node:fs/promises';
2
+ import { resolve, join, relative, isAbsolute, posix, sep, dirname, extname, win32 } from 'node:path';
3
+ import { Buffer } from 'node:buffer';
4
+ import { existsSync, statSync } from 'node:fs';
5
+ import { parse, compileScript, compileTemplate, babelParse } from '@vue/compiler-sfc';
6
+ import remapping from '@jridgewell/remapping';
7
+ import { transform } from 'esbuild';
8
+ import { createUnimport } from 'unimport';
9
+ import { PDF_DEFINITION_PROPERTY } from '../../dist/runtime/shared/template.js';
10
+
11
+ const EXCLUDED_ROOT_DIRECTORIES = /* @__PURE__ */ new Set(["assets", "components", "fonts"]);
12
+ const PDF_STRUCTURE_EVENTS = /* @__PURE__ */ new Set(["add", "addDir", "unlink", "unlinkDir"]);
13
+ const normalizePathForIgnore = (path) => path.replaceAll("\\", "/");
14
+ const classifyPdfWatchEvent = (event, absolutePath, layers, isIgnored) => {
15
+ if (isIgnored?.(normalizePathForIgnore(absolutePath))) return "ignore";
16
+ for (const layer of layers) {
17
+ const pdfsDir = join(resolve(layer.rootDir), "pdfs");
18
+ const pathWithinPdfs = relative(pdfsDir, absolutePath);
19
+ if (pathWithinPdfs.startsWith("..") || isAbsolute(pathWithinPdfs)) {
20
+ continue;
21
+ }
22
+ if (PDF_STRUCTURE_EVENTS.has(event)) return "restart";
23
+ if (event !== "change") return "ignore";
24
+ const [rootDirectory] = pathWithinPdfs.split(/[\\/]/);
25
+ return rootDirectory === "assets" || rootDirectory === "fonts" ? "restart" : "refresh";
26
+ }
27
+ return "ignore";
28
+ };
29
+ const compareText = (left, right) => {
30
+ if (left < right) return -1;
31
+ if (left > right) return 1;
32
+ return 0;
33
+ };
34
+ const normalizeRelativePath = (value) => {
35
+ const slashPath = value.replaceAll("\\", "/");
36
+ if (isAbsolute(value) || slashPath.startsWith("/") || /^[a-z]:\//i.test(slashPath) || slashPath.split("/").includes("..")) {
37
+ throw new TypeError(`PDF template path must stay inside pdfs/: "${value}".`);
38
+ }
39
+ const normalized = posix.normalize(slashPath).replace(/^\.\//, "");
40
+ if (normalized === "." || normalized === "") {
41
+ throw new TypeError("PDF template path cannot be empty.");
42
+ }
43
+ return normalized;
44
+ };
45
+ const templateKeyFromRelativePath = (relativePath) => {
46
+ const normalized = normalizeRelativePath(relativePath);
47
+ const [rootDirectory] = normalized.split("/");
48
+ if (rootDirectory && EXCLUDED_ROOT_DIRECTORIES.has(rootDirectory)) {
49
+ return null;
50
+ }
51
+ if (!normalized.endsWith(".vue")) return null;
52
+ const key = normalized.slice(0, -".vue".length);
53
+ if (key === "") {
54
+ throw new TypeError(`Invalid PDF template path: "${relativePath}".`);
55
+ }
56
+ return key;
57
+ };
58
+ const candidateOrder = (left, right) => left.layerIndex - right.layerIndex || compareText(left.relativePath, right.relativePath) || compareText(left.filePath, right.filePath);
59
+ const normalizePdfTemplateCandidates = (candidates) => {
60
+ const templatesByKey = /* @__PURE__ */ new Map();
61
+ const keysWithinLayers = /* @__PURE__ */ new Map();
62
+ for (const candidate of [...candidates].sort(candidateOrder)) {
63
+ if (!Number.isInteger(candidate.layerIndex) || candidate.layerIndex < 0) {
64
+ throw new TypeError("PDF template layerIndex must be a non-negative integer.");
65
+ }
66
+ const relativePath = normalizeRelativePath(candidate.relativePath);
67
+ const key = templateKeyFromRelativePath(relativePath);
68
+ if (key == null) continue;
69
+ if (!isAbsolute(candidate.filePath) && !/^[a-z]:[\\/]/i.test(candidate.filePath)) {
70
+ throw new TypeError(
71
+ `PDF template filePath must be absolute: "${candidate.filePath}".`
72
+ );
73
+ }
74
+ const template = {
75
+ key,
76
+ filePath: candidate.filePath,
77
+ relativePath,
78
+ layerIndex: candidate.layerIndex,
79
+ layerName: candidate.layerName ?? `layer-${candidate.layerIndex}`
80
+ };
81
+ const layerKey = `${candidate.layerIndex}\0${key}`;
82
+ const duplicate = keysWithinLayers.get(layerKey);
83
+ if (duplicate) {
84
+ throw new TypeError(
85
+ `PDF template collision for "${key}" in ${template.layerName}: "${duplicate.filePath}" and "${template.filePath}".`
86
+ );
87
+ }
88
+ keysWithinLayers.set(layerKey, template);
89
+ if (!templatesByKey.has(key)) {
90
+ templatesByKey.set(key, template);
91
+ }
92
+ }
93
+ return [...templatesByKey.values()].sort(
94
+ (left, right) => compareText(left.key, right.key)
95
+ );
96
+ };
97
+ const findFiles = async (directory, options, relativeDirectory = "") => {
98
+ let entries;
99
+ try {
100
+ entries = await readdir(directory, { withFileTypes: true });
101
+ } catch (error) {
102
+ if (error.code === "ENOENT") return [];
103
+ throw error;
104
+ }
105
+ entries.sort((left, right) => compareText(left.name, right.name));
106
+ const files = [];
107
+ for (const entry of entries) {
108
+ const absolutePath = join(directory, entry.name);
109
+ const relativePath = relativeDirectory ? posix.join(relativeDirectory, entry.name) : entry.name;
110
+ if (relativeDirectory === "" && entry.isDirectory() && options.excludedRootDirectories?.has(entry.name)) {
111
+ continue;
112
+ }
113
+ if (options.isIgnored?.(normalizePathForIgnore(absolutePath))) continue;
114
+ if (entry.isDirectory()) {
115
+ files.push(...await findFiles(
116
+ join(directory, entry.name),
117
+ options,
118
+ relativePath
119
+ ));
120
+ } else if (entry.isFile() && options.include(entry.name)) {
121
+ files.push(relativePath);
122
+ }
123
+ }
124
+ return files;
125
+ };
126
+ const VUE_FILES = {
127
+ excludedRootDirectories: EXCLUDED_ROOT_DIRECTORIES,
128
+ include: (filename) => filename.endsWith(".vue")
129
+ };
130
+ const PDF_COMPONENT_FILES = {
131
+ include: (filename) => filename.endsWith(".vue")
132
+ };
133
+ const PDF_IMAGE_FILES = {
134
+ include: (filename) => /\.(?:jpe?g|png)$/i.test(filename)
135
+ };
136
+ const discoverPdfTemplates = async (layers, isIgnored) => {
137
+ const candidates = [];
138
+ for (const [layerIndex, layer] of layers.entries()) {
139
+ const rootDir = resolve(layer.rootDir);
140
+ const pdfsDir = join(rootDir, "pdfs");
141
+ const files = await findFiles(pdfsDir, { ...VUE_FILES, isIgnored });
142
+ for (const relativePath of files) {
143
+ candidates.push({
144
+ filePath: join(pdfsDir, ...relativePath.split("/")),
145
+ relativePath,
146
+ layerIndex,
147
+ layerName: layer.name
148
+ });
149
+ }
150
+ }
151
+ return normalizePdfTemplateCandidates(candidates);
152
+ };
153
+ const discoverPdfComponentFiles = async (layers, isIgnored) => {
154
+ const files = /* @__PURE__ */ new Set();
155
+ for (const layer of layers) {
156
+ const directory = join(resolve(layer.rootDir), "pdfs", "components");
157
+ for (const relativePath of await findFiles(directory, {
158
+ ...PDF_COMPONENT_FILES,
159
+ isIgnored
160
+ })) {
161
+ files.add(join(directory, ...relativePath.split("/")));
162
+ }
163
+ }
164
+ return [...files].sort(compareText);
165
+ };
166
+ const discoverPdfImageFiles = async (layers, isIgnored) => {
167
+ const files = /* @__PURE__ */ new Map();
168
+ for (const [layerIndex, layer] of layers.entries()) {
169
+ const rootDir = join(resolve(layer.rootDir), "pdfs", "assets");
170
+ for (const key of await findFiles(rootDir, {
171
+ ...PDF_IMAGE_FILES,
172
+ isIgnored
173
+ })) {
174
+ if (!files.has(key)) {
175
+ files.set(key, {
176
+ filePath: join(rootDir, ...key.split("/")),
177
+ key,
178
+ rootDir,
179
+ layerIndex
180
+ });
181
+ }
182
+ }
183
+ }
184
+ return [...files.values()].sort((left, right) => compareText(left.key, right.key));
185
+ };
186
+
187
+ const DEFAULT_MAX_PDF_FONT_BYTES = 5 * 1024 * 1024;
188
+ const FONT_WEIGHT_VALUES = {
189
+ black: 900,
190
+ bold: 700,
191
+ demibold: 600,
192
+ extrabold: 800,
193
+ extralight: 200,
194
+ hairline: 100,
195
+ heavy: 900,
196
+ light: 300,
197
+ medium: 500,
198
+ normal: 400,
199
+ semibold: 600,
200
+ thin: 100,
201
+ ultrabold: 800,
202
+ ultralight: 200
203
+ };
204
+ const FONT_STYLES = /* @__PURE__ */ new Set([
205
+ "italic",
206
+ "normal",
207
+ "oblique"
208
+ ]);
209
+ const STANDARD_FONT_FAMILIES = /* @__PURE__ */ new Set([
210
+ "Courier",
211
+ "Courier-Bold",
212
+ "Courier-BoldOblique",
213
+ "Courier-Oblique",
214
+ "Helvetica",
215
+ "Helvetica-Bold",
216
+ "Helvetica-BoldOblique",
217
+ "Helvetica-Oblique",
218
+ "Times-Bold",
219
+ "Times-BoldItalic",
220
+ "Times-Italic",
221
+ "Times-Roman"
222
+ ]);
223
+ const fontError = (source, message) => new TypeError(`Invalid PDF font "${source}": ${message}`);
224
+ const isPdfFontsRoot = (root) => {
225
+ const parts = root.replaceAll("\\", "/").split("/").filter(Boolean);
226
+ return parts.at(-2)?.toLowerCase() === "pdfs" && parts.at(-1)?.toLowerCase() === "fonts";
227
+ };
228
+ const isContained = (root, target) => {
229
+ const pathFromRoot = relative(root, target);
230
+ return pathFromRoot !== "" && pathFromRoot !== ".." && !pathFromRoot.startsWith(`..${sep}`) && !isAbsolute(pathFromRoot);
231
+ };
232
+ const prepareFontRoots = async (roots) => {
233
+ if (roots.length === 0) {
234
+ throw new TypeError("At least one absolute pdfs/fonts root is required.");
235
+ }
236
+ const prepared = [];
237
+ const seen = /* @__PURE__ */ new Set();
238
+ for (const root of roots) {
239
+ if (typeof root !== "string" || !isAbsolute(root) || !isPdfFontsRoot(root)) {
240
+ throw new TypeError(
241
+ `PDF font root "${root}" must be an absolute pdfs/fonts directory.`
242
+ );
243
+ }
244
+ let rootRealPath;
245
+ try {
246
+ rootRealPath = await realpath(root);
247
+ } catch (error) {
248
+ throw new TypeError(`PDF font root "${root}" does not exist.`, {
249
+ cause: error
250
+ });
251
+ }
252
+ const pdfsRealPath = await realpath(dirname(root));
253
+ if (!isContained(pdfsRealPath, rootRealPath)) {
254
+ throw new TypeError(
255
+ `PDF font root "${root}" resolves outside its pdfs directory.`
256
+ );
257
+ }
258
+ const rootStats = await stat(rootRealPath);
259
+ if (!rootStats.isDirectory()) {
260
+ throw new TypeError(`PDF font root "${root}" must be a directory.`);
261
+ }
262
+ if (seen.has(rootRealPath)) continue;
263
+ seen.add(rootRealPath);
264
+ prepared.push({ realPath: rootRealPath, sourcePath: root });
265
+ }
266
+ return prepared;
267
+ };
268
+ const validateRelativeSource = (source) => {
269
+ if (typeof source !== "string" || source.trim() === "") {
270
+ throw fontError(String(source), "src must be a non-empty relative path.");
271
+ }
272
+ const value = source.trim();
273
+ const pathParts = value.split(/[\\/]+/);
274
+ if (value.includes("\0")) {
275
+ throw fontError(value, "null bytes are not allowed in src.");
276
+ }
277
+ if (isAbsolute(value) || win32.isAbsolute(value) || /^[a-z][a-z\d+.-]*:/i.test(value) || value.startsWith("//")) {
278
+ throw fontError(value, "src must be a relative local path.");
279
+ }
280
+ if (pathParts.includes("..")) {
281
+ throw fontError(value, "parent path segments are not allowed.");
282
+ }
283
+ return value;
284
+ };
285
+ const resolveFontFile = async (source, roots) => {
286
+ const pathParts = source.split(/[\\/]+/).filter((part) => part !== ".");
287
+ for (const root of roots) {
288
+ const candidate = resolve(root.sourcePath, ...pathParts);
289
+ let candidateRealPath;
290
+ try {
291
+ candidateRealPath = await realpath(candidate);
292
+ } catch (error) {
293
+ if (error.code === "ENOENT") continue;
294
+ throw fontError(source, "the source file could not be resolved.");
295
+ }
296
+ if (!isContained(root.realPath, candidateRealPath)) {
297
+ throw fontError(source, "the resolved path escapes its pdfs/fonts root.");
298
+ }
299
+ return candidateRealPath;
300
+ }
301
+ throw fontError(source, "the source file was not found in any pdfs/fonts root.");
302
+ };
303
+ const normalizeFontWeight = (source, weight) => {
304
+ if (weight === void 0) return void 0;
305
+ if (typeof weight === "string") {
306
+ const value = FONT_WEIGHT_VALUES[weight];
307
+ if (value === void 0) {
308
+ throw fontError(source, `unsupported fontWeight "${weight}".`);
309
+ }
310
+ return value;
311
+ }
312
+ if (!Number.isInteger(weight) || weight < 1 || weight > 1e3) {
313
+ throw fontError(source, "numeric fontWeight must be an integer from 1 to 1000.");
314
+ }
315
+ return weight;
316
+ };
317
+ const validateFontSource = (src) => {
318
+ if (typeof src === "string" && /^https?:/i.test(src.trim())) {
319
+ throw fontError(
320
+ "<remote-font>",
321
+ "remote fonts are unsupported; use a local file in pdfs/fonts/."
322
+ );
323
+ }
324
+ return validateRelativeSource(src);
325
+ };
326
+ const validateDeclaration = (declaration) => {
327
+ const source = validateFontSource(declaration?.src);
328
+ const label = source;
329
+ if (typeof declaration.family !== "string" || declaration.family.trim() === "") {
330
+ throw fontError(label, "family must be a non-empty string.");
331
+ }
332
+ const family = declaration.family.trim();
333
+ if (Object.prototype.hasOwnProperty.call(Object.prototype, family)) {
334
+ throw fontError(label, `family "${family}" is a reserved object key.`);
335
+ }
336
+ if (STANDARD_FONT_FAMILIES.has(family)) {
337
+ throw fontError(label, `family "${family}" is reserved by a standard PDF font.`);
338
+ }
339
+ if (declaration.fontStyle !== void 0 && !FONT_STYLES.has(declaration.fontStyle)) {
340
+ throw fontError(label, `unsupported fontStyle "${declaration.fontStyle}".`);
341
+ }
342
+ return {
343
+ family,
344
+ fontStyle: declaration.fontStyle,
345
+ fontWeight: normalizeFontWeight(label, declaration.fontWeight),
346
+ source
347
+ };
348
+ };
349
+ const detectFontFormat = (bytes) => {
350
+ if (bytes.byteLength < 12) return void 0;
351
+ const isTrueType = bytes[0] === 0 && bytes[1] === 1 && bytes[2] === 0 && bytes[3] === 0;
352
+ const isOpenType = bytes[0] === 79 && bytes[1] === 84 && bytes[2] === 84 && bytes[3] === 79;
353
+ if (isTrueType) return "ttf";
354
+ if (isOpenType) return "otf";
355
+ return void 0;
356
+ };
357
+ const validateSfntStructure = (source, bytes, format) => {
358
+ const data = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
359
+ const tableCount = data.readUInt16BE(4);
360
+ const directoryEnd = 12 + tableCount * 16;
361
+ if (tableCount < 1 || tableCount > 4096 || directoryEnd > data.byteLength) {
362
+ throw fontError(source, "the SFNT table directory is corrupt or truncated.");
363
+ }
364
+ const tables = /* @__PURE__ */ new Set();
365
+ for (let index = 0; index < tableCount; index += 1) {
366
+ const record = 12 + index * 16;
367
+ const tag = data.toString("latin1", record, record + 4);
368
+ const offset = data.readUInt32BE(record + 8);
369
+ const length = data.readUInt32BE(record + 12);
370
+ const end = offset + length;
371
+ if (!/^[\x20-\x7E]{4}$/u.test(tag) || tables.has(tag) || offset < directoryEnd || !Number.isSafeInteger(end) || end > data.byteLength) {
372
+ throw fontError(source, "the SFNT table directory is corrupt or truncated.");
373
+ }
374
+ tables.add(tag);
375
+ }
376
+ const outlineTable = format === "ttf" ? tables.has("glyf") : tables.has("CFF ") || tables.has("CFF2");
377
+ if (!tables.has("head") || !tables.has("maxp") || !tables.has("cmap") || !outlineTable) {
378
+ throw fontError(source, "the font is missing required SFNT tables.");
379
+ }
380
+ };
381
+ const fontFormat = (source, bytes) => {
382
+ const extension = extname(source).toLowerCase();
383
+ if (extension !== ".otf" && extension !== ".ttf") {
384
+ throw fontError(source, "only .ttf and .otf files are supported.");
385
+ }
386
+ if (bytes.byteLength < 12) {
387
+ throw fontError(source, "the file is too small to be a TTF or OTF font.");
388
+ }
389
+ const format = detectFontFormat(bytes);
390
+ if (!format) {
391
+ throw fontError(source, "the file has an unsupported TTF or OTF signature.");
392
+ }
393
+ if (`.${format}` !== extension) {
394
+ throw fontError(source, "the file extension does not match its TTF or OTF signature.");
395
+ }
396
+ validateSfntStructure(source, bytes, format);
397
+ return format;
398
+ };
399
+ const readFont = async (source, filePath, maxBytes) => {
400
+ const file = await open(filePath, "r");
401
+ let bytes;
402
+ try {
403
+ const fileStats = await file.stat();
404
+ if (!fileStats.isFile()) {
405
+ throw fontError(source, "the resolved source must be a regular file.");
406
+ }
407
+ if (fileStats.size > maxBytes) {
408
+ throw fontError(source, `the source exceeds the ${maxBytes}-byte limit.`);
409
+ }
410
+ bytes = await file.readFile();
411
+ if (bytes.byteLength > maxBytes) {
412
+ throw fontError(source, `the source exceeds the ${maxBytes}-byte limit.`);
413
+ }
414
+ } finally {
415
+ await file.close();
416
+ }
417
+ const format = fontFormat(source, bytes);
418
+ return `data:font/${format};base64,${Buffer.from(bytes).toString("base64")}`;
419
+ };
420
+ const bundlePdfFonts = async (declarations, options) => {
421
+ const maxBytes = options.maxBytes ?? DEFAULT_MAX_PDF_FONT_BYTES;
422
+ if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) {
423
+ throw new TypeError("PDF font maxBytes must be a positive safe integer.");
424
+ }
425
+ if (declarations.length === 0) return Object.freeze([]);
426
+ let preparedRoots;
427
+ const getRoots = async () => preparedRoots ??= await prepareFontRoots(options.fontRoots);
428
+ const result = [];
429
+ const registrations = /* @__PURE__ */ new Set();
430
+ for (const declaration of declarations) {
431
+ const validated = validateDeclaration(declaration);
432
+ const label = validated.source;
433
+ const registration = [
434
+ validated.family,
435
+ validated.fontStyle ?? "normal",
436
+ validated.fontWeight ?? 400
437
+ ].join("\0");
438
+ if (registrations.has(registration)) {
439
+ throw fontError(
440
+ label,
441
+ `duplicates family "${validated.family}" with the same style and weight.`
442
+ );
443
+ }
444
+ const filePath = await resolveFontFile(validated.source, await getRoots());
445
+ const src = await readFont(validated.source, filePath, maxBytes);
446
+ const { family, fontStyle, fontWeight } = validated;
447
+ registrations.add(registration);
448
+ result.push(Object.freeze({ family, fontStyle, fontWeight, src }));
449
+ }
450
+ return Object.freeze(result);
451
+ };
452
+
453
+ const authoringImportContexts = /* @__PURE__ */ new Map();
454
+ class PdfSfcCompileError extends Error {
455
+ column;
456
+ filename;
457
+ line;
458
+ constructor(filename, line, column, message) {
459
+ super(`[nuxt-pdf] ${filename}:${line}:${column} ${message}`);
460
+ this.name = "PdfSfcCompileError";
461
+ this.filename = filename;
462
+ this.line = line;
463
+ this.column = column;
464
+ }
465
+ }
466
+ const COMPONENT_VARIABLE = "__nuxtPdfComponent";
467
+ const VIRTUAL_PREFIX = "\0nuxt-pdf:sfc:";
468
+ const VIRTUAL_SUFFIX = ".mjs";
469
+ const METADATA_KEYS = /* @__PURE__ */ new Set([
470
+ "filename",
471
+ "language",
472
+ "maxPasses",
473
+ "sampleData",
474
+ "scenarios",
475
+ "title"
476
+ ]);
477
+ const PRODUCTION_METADATA_KEYS = /* @__PURE__ */ new Set([
478
+ "filename",
479
+ "language",
480
+ "maxPasses",
481
+ "title"
482
+ ]);
483
+ const createPdfSfcPlugin = (options) => ({
484
+ name: "nuxt-pdf:sfc",
485
+ enforce: "pre",
486
+ resolveId(source, importer) {
487
+ const filename = resolvePdfImport(source, importer);
488
+ if (!filename) return null;
489
+ if (options.files.has(filename)) return virtualPdfId(filename);
490
+ return importer?.startsWith(VIRTUAL_PREFIX) ? resolveRelativeModule(filename) : null;
491
+ },
492
+ async load(id) {
493
+ const filename = filenameFromVirtualId(id);
494
+ if (!filename) return null;
495
+ const kind = options.files.get(filename);
496
+ if (!kind) return null;
497
+ return compilePdfSfc(
498
+ await readFile(filename, "utf8"),
499
+ filename,
500
+ kind,
501
+ options.isProduction,
502
+ options.composablesImport
503
+ );
504
+ },
505
+ async transform(source, id) {
506
+ if (id.includes("?") || id.startsWith(VIRTUAL_PREFIX)) return null;
507
+ const filename = resolve(id);
508
+ const kind = options.files.get(filename);
509
+ if (!kind) return null;
510
+ return compilePdfSfc(
511
+ source,
512
+ filename,
513
+ kind,
514
+ options.isProduction,
515
+ options.composablesImport
516
+ );
517
+ }
518
+ });
519
+ const MODULE_EXTENSIONS = [
520
+ ".ts",
521
+ ".tsx",
522
+ ".js",
523
+ ".jsx",
524
+ ".mjs",
525
+ ".cjs",
526
+ ".json"
527
+ ];
528
+ function resolveRelativeModule(filename) {
529
+ const extension = extname(filename);
530
+ const candidates = MODULE_EXTENSIONS.includes(
531
+ extension
532
+ ) ? [filename] : [
533
+ filename,
534
+ ...MODULE_EXTENSIONS.map((extension2) => `${filename}${extension2}`),
535
+ ...MODULE_EXTENSIONS.map((extension2) => resolve(filename, `index${extension2}`))
536
+ ];
537
+ for (const candidate of candidates) {
538
+ if (candidate.endsWith(".vue") || !existsSync(candidate)) continue;
539
+ try {
540
+ if (statSync(candidate).isFile()) return candidate;
541
+ } catch {
542
+ }
543
+ }
544
+ return null;
545
+ }
546
+ function resolvePdfImport(source, importer) {
547
+ if (source.startsWith("\0") || source.includes("?")) return void 0;
548
+ if (isAbsolute(source)) return resolve(source);
549
+ if (!source.startsWith(".")) return void 0;
550
+ const importerFilename = importer?.startsWith(VIRTUAL_PREFIX) ? filenameFromVirtualId(importer) : importer?.split("?", 1)[0];
551
+ return importerFilename ? resolve(dirname(importerFilename), source) : void 0;
552
+ }
553
+ function virtualPdfId(filename) {
554
+ return `${VIRTUAL_PREFIX}${Buffer.from(filename).toString("base64url")}${VIRTUAL_SUFFIX}`;
555
+ }
556
+ function filenameFromVirtualId(id) {
557
+ if (!id.startsWith(VIRTUAL_PREFIX) || !id.endsWith(VIRTUAL_SUFFIX)) return void 0;
558
+ const encoded = id.slice(VIRTUAL_PREFIX.length, -VIRTUAL_SUFFIX.length);
559
+ try {
560
+ return Buffer.from(encoded, "base64url").toString("utf8");
561
+ } catch {
562
+ return void 0;
563
+ }
564
+ }
565
+ async function compilePdfSfc(source, filename, kind, isProduction = false, composablesImport) {
566
+ const compilerFilename = normalizeCompilerPath(filename);
567
+ const original = parsePdfSfc(source, compilerFilename);
568
+ assertSupportedBlocks(original, compilerFilename);
569
+ assertTemplate(original, compilerFilename);
570
+ assertSynchronousSetup(original, compilerFilename);
571
+ assertSupportedTemplate(original, compilerFilename);
572
+ const metadata = extractMetadata(
573
+ source,
574
+ original,
575
+ compilerFilename,
576
+ kind,
577
+ isProduction
578
+ );
579
+ const cleaned = parsePdfSfc(metadata.source, compilerFilename);
580
+ const component = compileComponent(cleaned, compilerFilename, isProduction);
581
+ const autoImportedComponent = await injectAuthoringImports(
582
+ component.code,
583
+ compilerFilename,
584
+ composablesImport
585
+ );
586
+ const metadataCode = kind === "template" ? `${COMPONENT_VARIABLE}.${PDF_DEFINITION_PROPERTY} = ${metadata.expression}
587
+ ` : "";
588
+ const loader = scriptLoader(cleaned);
589
+ try {
590
+ const result = await transform([
591
+ autoImportedComponent.code,
592
+ metadataCode,
593
+ `export default ${COMPONENT_VARIABLE}`
594
+ ].filter(Boolean).join("\n"), {
595
+ charset: "utf8",
596
+ format: "esm",
597
+ loader,
598
+ sourcefile: compilerFilename,
599
+ sourcemap: true,
600
+ sourcesContent: true,
601
+ target: "es2022"
602
+ });
603
+ const esbuildMap = result.map ? JSON.parse(result.map) : null;
604
+ const map = esbuildMap && autoImportedComponent.map && component.map ? remapping(
605
+ [esbuildMap, autoImportedComponent.map, component.map],
606
+ () => null
607
+ ) : null;
608
+ if (map) map.sourcesContent = map.sources.map(() => source);
609
+ return { code: result.code, map };
610
+ } catch (error) {
611
+ throw normalizeBuildError(error, compilerFilename);
612
+ }
613
+ }
614
+ function normalizeCompilerPath(path) {
615
+ return path.replaceAll("\\", "/");
616
+ }
617
+ function parsePdfSfc(source, filename) {
618
+ const result = parse(source, {
619
+ filename,
620
+ sourceMap: true
621
+ });
622
+ if (result.errors.length > 0) {
623
+ throw normalizeCompilerError(result.errors[0], filename);
624
+ }
625
+ return result.descriptor;
626
+ }
627
+ function assertSupportedBlocks(descriptor, filename) {
628
+ const style = descriptor.styles[0];
629
+ if (style) {
630
+ throw errorAtBlock(
631
+ filename,
632
+ style,
633
+ "<style> blocks are not supported in PDF components."
634
+ );
635
+ }
636
+ const customBlock = descriptor.customBlocks[0];
637
+ if (customBlock) {
638
+ throw errorAtBlock(
639
+ filename,
640
+ customBlock,
641
+ `<${customBlock.type}> custom blocks are not supported in PDF components.`
642
+ );
643
+ }
644
+ }
645
+ function assertTemplate(descriptor, filename) {
646
+ if (descriptor.template) return;
647
+ throw new PdfSfcCompileError(
648
+ filename,
649
+ 1,
650
+ 1,
651
+ "A PDF component must contain a <template> block."
652
+ );
653
+ }
654
+ const FUNCTION_NODE_TYPES = /* @__PURE__ */ new Set([
655
+ "ArrowFunctionExpression",
656
+ "ClassMethod",
657
+ "ClassPrivateMethod",
658
+ "FunctionDeclaration",
659
+ "FunctionExpression",
660
+ "ObjectMethod"
661
+ ]);
662
+ function assertSynchronousSetup(descriptor, filename) {
663
+ const block = descriptor.scriptSetup;
664
+ if (!block) return;
665
+ const program = parseScriptProgram(block, filename);
666
+ walkAst(program, [], (node, ancestors) => {
667
+ const isTopLevelAwait = node.type === "AwaitExpression" || node.type === "ForOfStatement" && node.await === true;
668
+ if (!isTopLevelAwait) return;
669
+ if (ancestors.some((parent) => FUNCTION_NODE_TYPES.has(parent.type))) return;
670
+ throw errorAtNode(
671
+ filename,
672
+ block,
673
+ node,
674
+ "Top-level await is not supported in PDF templates. Load request data before render(props) and pass it through typed props."
675
+ );
676
+ });
677
+ }
678
+ function assertSupportedTemplate(descriptor, filename) {
679
+ const block = descriptor.template;
680
+ const ast = block?.ast;
681
+ if (!block || !ast) return;
682
+ walkTemplateAst(ast, (node) => {
683
+ if (node.type === 1 && node.tag?.toLowerCase() === "teleport") {
684
+ throw errorAtTemplateNode(
685
+ filename,
686
+ node,
687
+ "Teleport is not supported in PDF templates because the PDF renderer has no external host target."
688
+ );
689
+ }
690
+ if (node.type === 7 && node.name === "show") {
691
+ throw errorAtTemplateNode(
692
+ filename,
693
+ node,
694
+ "v-show is not supported in PDF templates. Use v-if; PDF styles are not browser CSS."
695
+ );
696
+ }
697
+ });
698
+ }
699
+ function extractMetadata(source, descriptor, filename, kind, isProduction) {
700
+ const calls = [
701
+ ...findMacroCalls(descriptor.script, filename),
702
+ ...findMacroCalls(descriptor.scriptSetup, filename)
703
+ ];
704
+ if (kind === "component") {
705
+ if (calls[0]) {
706
+ throw errorAtNode(
707
+ filename,
708
+ calls[0].block,
709
+ calls[0].call,
710
+ "definePdf() is only allowed in discovered PDF templates."
711
+ );
712
+ }
713
+ return { expression: "", source };
714
+ }
715
+ if (calls.length === 0) {
716
+ throw new PdfSfcCompileError(
717
+ filename,
718
+ descriptor.scriptSetup?.loc.start.line ?? 1,
719
+ descriptor.scriptSetup?.loc.start.column ?? 1,
720
+ "A PDF template must contain exactly one top-level definePdf({...}) call."
721
+ );
722
+ }
723
+ if (calls.length > 1) {
724
+ throw errorAtNode(
725
+ filename,
726
+ calls[1].block,
727
+ calls[1].call,
728
+ `A PDF template must contain exactly one definePdf() call; found ${calls.length}.`
729
+ );
730
+ }
731
+ const macro = calls[0];
732
+ if (macro.block !== descriptor.scriptSetup) {
733
+ throw errorAtNode(
734
+ filename,
735
+ macro.block,
736
+ macro.call,
737
+ "definePdf() must be a top-level statement in <script setup>."
738
+ );
739
+ }
740
+ if (!macro.statement) {
741
+ throw errorAtNode(
742
+ filename,
743
+ macro.block,
744
+ macro.call,
745
+ "definePdf() must be a standalone top-level statement."
746
+ );
747
+ }
748
+ if (!macro.argument || macro.argument.type !== "ObjectExpression") {
749
+ throw errorAtNode(
750
+ filename,
751
+ macro.block,
752
+ macro.call,
753
+ "definePdf() requires one static object argument."
754
+ );
755
+ }
756
+ const metadataProperties = assertMetadataShape(
757
+ macro.argument,
758
+ filename,
759
+ macro.block
760
+ );
761
+ assertMetadataHoistable(source, descriptor, filename, macro);
762
+ const blockOffset = macro.block.loc.start.offset;
763
+ const argumentStart = requiredOffset(macro.argument.start, filename, macro.block);
764
+ const argumentEnd = requiredOffset(macro.argument.end, filename, macro.block);
765
+ const statementStart = requiredOffset(macro.statement.start, filename, macro.block);
766
+ const statementEnd = requiredOffset(macro.statement.end, filename, macro.block);
767
+ const absoluteStatementStart = blockOffset + statementStart;
768
+ const absoluteStatementEnd = blockOffset + statementEnd;
769
+ return {
770
+ expression: isProduction ? productionMetadataExpression(
771
+ metadataProperties,
772
+ macro.block,
773
+ filename
774
+ ) : macro.block.content.slice(argumentStart, argumentEnd),
775
+ source: replaceWithWhitespace(
776
+ source,
777
+ absoluteStatementStart,
778
+ absoluteStatementEnd
779
+ )
780
+ };
781
+ }
782
+ function findMacroCalls(block, filename, name = "definePdf") {
783
+ if (!block) return [];
784
+ const program = parseScriptProgram(block, filename);
785
+ const calls = [];
786
+ walkAst(program, [], (node, ancestors) => {
787
+ if (node.type !== "CallExpression") return;
788
+ const callee = asAstNode(node.callee);
789
+ if (callee?.type !== "Identifier" || callee.name !== name) return;
790
+ const parent = ancestors.at(-1);
791
+ const grandparent = ancestors.at(-2);
792
+ const isTopLevelStatement = parent?.type === "ExpressionStatement" && parent.expression === node && grandparent?.type === "Program";
793
+ const args = Array.isArray(node.arguments) ? node.arguments : [];
794
+ calls.push({
795
+ argument: args.length === 1 ? asAstNode(args[0]) : void 0,
796
+ block,
797
+ call: node,
798
+ statement: isTopLevelStatement ? parent : void 0
799
+ });
800
+ });
801
+ return calls;
802
+ }
803
+ function parseScriptProgram(block, filename) {
804
+ const plugins = [];
805
+ if (block.lang === "ts" || block.lang === "tsx") plugins.push("typescript");
806
+ if (block.lang === "jsx" || block.lang === "tsx") plugins.push("jsx");
807
+ try {
808
+ return babelParse(block.content, {
809
+ plugins,
810
+ sourceType: "module"
811
+ }).program;
812
+ } catch (error) {
813
+ throw normalizeScriptParseError(error, filename, block);
814
+ }
815
+ }
816
+ function assertMetadataHoistable(source, descriptor, filename, macro) {
817
+ const block = descriptor.scriptSetup;
818
+ if (!block || !macro.argument || !macro.statement) return;
819
+ const blockOffset = block.loc.start.offset;
820
+ const argumentStart = requiredOffset(macro.argument.start, filename, block);
821
+ const argumentEnd = requiredOffset(macro.argument.end, filename, block);
822
+ const replacements = [];
823
+ for (const call of findMacroCalls(block, filename, "defineOptions")) {
824
+ if (!call.statement) continue;
825
+ replacements.push({
826
+ start: blockOffset + requiredOffset(call.statement.start, filename, block),
827
+ end: blockOffset + requiredOffset(call.statement.end, filename, block),
828
+ value: ""
829
+ });
830
+ }
831
+ replacements.push({
832
+ start: blockOffset + requiredOffset(macro.statement.start, filename, block),
833
+ end: blockOffset + requiredOffset(macro.statement.end, filename, block),
834
+ value: `defineOptions(${block.content.slice(argumentStart, argumentEnd)})`
835
+ });
836
+ const validationSource = replacements.sort((left, right) => right.start - left.start).reduce(
837
+ (result, replacement) => result.slice(0, replacement.start) + replacement.value + result.slice(replacement.end),
838
+ source
839
+ );
840
+ try {
841
+ const validation = parsePdfSfc(validationSource, filename);
842
+ compileScript(validation, {
843
+ id: "nuxt-pdf-metadata-hoist",
844
+ sourceMap: false
845
+ });
846
+ } catch (error) {
847
+ if (errorMessage(error).includes(
848
+ "`defineOptions()` in <script setup> cannot reference locally declared variables"
849
+ )) {
850
+ throw errorAtNode(
851
+ filename,
852
+ block,
853
+ macro.call,
854
+ "definePdf() metadata cannot reference locally declared <script setup> bindings because it is evaluated at module scope. Inline the value or import it from a side-effect-free module."
855
+ );
856
+ }
857
+ }
858
+ }
859
+ function assertMetadataShape(object, filename, block) {
860
+ const properties = Array.isArray(object.properties) ? object.properties : [];
861
+ const keys = /* @__PURE__ */ new Map();
862
+ for (const value of properties) {
863
+ const property = asAstNode(value);
864
+ if (!property || property.type === "SpreadElement") {
865
+ throw errorAtNode(
866
+ filename,
867
+ block,
868
+ property ?? object,
869
+ "definePdf() metadata cannot contain spread properties."
870
+ );
871
+ }
872
+ if (property.type !== "ObjectProperty" && property.type !== "ObjectMethod") {
873
+ throw errorAtNode(
874
+ filename,
875
+ block,
876
+ property,
877
+ "definePdf() metadata must use ordinary static properties."
878
+ );
879
+ }
880
+ if (property.computed === true) {
881
+ throw errorAtNode(
882
+ filename,
883
+ block,
884
+ property,
885
+ "definePdf() metadata keys cannot be computed."
886
+ );
887
+ }
888
+ const key = metadataKey(asAstNode(property.key));
889
+ if (!key || !METADATA_KEYS.has(key)) {
890
+ throw errorAtNode(
891
+ filename,
892
+ block,
893
+ property,
894
+ `Unsupported definePdf() metadata key${key ? ` "${key}"` : ""}.`
895
+ );
896
+ }
897
+ if (keys.has(key)) {
898
+ throw errorAtNode(
899
+ filename,
900
+ block,
901
+ property,
902
+ `Duplicate definePdf() metadata key "${key}".`
903
+ );
904
+ }
905
+ if (!isRuntimeMetadataKey(key) && isFunctionProperty(property)) {
906
+ throw errorAtNode(
907
+ filename,
908
+ block,
909
+ property,
910
+ `definePdf() metadata key "${key}" cannot be a function. Functions are only supported for "title" and "filename".`
911
+ );
912
+ }
913
+ keys.set(key, property);
914
+ }
915
+ return keys;
916
+ }
917
+ function productionMetadataExpression(properties, block, filename) {
918
+ const retained = [...properties].filter(([key]) => PRODUCTION_METADATA_KEYS.has(key)).map(([, property]) => {
919
+ const start = requiredOffset(property.start, filename, block);
920
+ const end = requiredOffset(property.end, filename, block);
921
+ return block.content.slice(start, end);
922
+ });
923
+ return `{${retained.join(",")}}`;
924
+ }
925
+ function isRuntimeMetadataKey(key) {
926
+ return key === "title" || key === "filename";
927
+ }
928
+ function isFunctionProperty(property) {
929
+ if (property.type === "ObjectMethod") return true;
930
+ const value = asAstNode(property.value);
931
+ return value?.type === "ArrowFunctionExpression" || value?.type === "FunctionExpression";
932
+ }
933
+ function compileComponent(descriptor, filename, isProduction) {
934
+ if (descriptor.scriptSetup) {
935
+ try {
936
+ const result = compileScript(descriptor, {
937
+ genDefaultAs: COMPONENT_VARIABLE,
938
+ id: "nuxt-pdf",
939
+ inlineTemplate: true,
940
+ isProd: isProduction,
941
+ sourceMap: true,
942
+ templateOptions: {
943
+ filename,
944
+ isProd: isProduction,
945
+ ssr: false,
946
+ transformAssetUrls: false
947
+ }
948
+ });
949
+ return {
950
+ code: result.content,
951
+ map: result.map ? JSON.parse(JSON.stringify(result.map)) : null
952
+ };
953
+ } catch (error) {
954
+ throw normalizeCompilerError(error, filename);
955
+ }
956
+ }
957
+ let scriptCode = `const ${COMPONENT_VARIABLE} = { __name: ${JSON.stringify(componentName(filename))} }`;
958
+ let bindings;
959
+ if (descriptor.script) {
960
+ try {
961
+ const script = compileScript(descriptor, {
962
+ genDefaultAs: COMPONENT_VARIABLE,
963
+ id: "nuxt-pdf",
964
+ isProd: isProduction,
965
+ sourceMap: true
966
+ });
967
+ scriptCode = script.content;
968
+ bindings = script.bindings;
969
+ } catch (error) {
970
+ throw normalizeCompilerError(error, filename);
971
+ }
972
+ }
973
+ const template = compileTemplate({
974
+ compilerOptions: { bindingMetadata: bindings },
975
+ filename,
976
+ id: "nuxt-pdf",
977
+ isProd: isProduction,
978
+ source: descriptor.template.content,
979
+ ssr: false,
980
+ transformAssetUrls: false
981
+ });
982
+ if (template.errors.length > 0) {
983
+ throw normalizeCompilerError(template.errors[0], filename);
984
+ }
985
+ return {
986
+ code: [
987
+ scriptCode,
988
+ template.code,
989
+ `${COMPONENT_VARIABLE}.render = render`
990
+ ].join("\n"),
991
+ // The separately compiled script and template do not share one generated
992
+ // coordinate space. Returning no map is safer than publishing a false one.
993
+ map: null
994
+ };
995
+ }
996
+ async function injectAuthoringImports(componentCode, filename, composablesImport) {
997
+ const normalizedComposablesImport = composablesImport ? normalizeCompilerPath(composablesImport) : void 0;
998
+ const key = normalizedComposablesImport ?? "";
999
+ let context = authoringImportContexts.get(key);
1000
+ if (!context) {
1001
+ context = createUnimport({
1002
+ imports: normalizedComposablesImport ? [{ from: normalizedComposablesImport, name: "usePdfPageNumbers" }] : [],
1003
+ presets: ["vue"]
1004
+ });
1005
+ authoringImportContexts.set(key, context);
1006
+ }
1007
+ const result = await context.injectImports(componentCode, filename);
1008
+ return {
1009
+ code: result.s.toString(),
1010
+ map: JSON.parse(result.s.generateMap({
1011
+ hires: true,
1012
+ includeContent: true,
1013
+ source: filename
1014
+ }).toString())
1015
+ };
1016
+ }
1017
+ function scriptLoader(descriptor) {
1018
+ const languages = [descriptor.script?.lang, descriptor.scriptSetup?.lang];
1019
+ if (languages.includes("tsx")) return "tsx";
1020
+ if (languages.includes("ts")) return "ts";
1021
+ if (languages.includes("jsx")) return "jsx";
1022
+ return "js";
1023
+ }
1024
+ function componentName(filename) {
1025
+ const basename = filename.replaceAll("\\", "/").split("/").at(-1) ?? "PdfComponent.vue";
1026
+ return basename.replace(/\.vue$/i, "");
1027
+ }
1028
+ function metadataKey(node) {
1029
+ if (!node) return void 0;
1030
+ if (node.type === "Identifier" && typeof node.name === "string") return node.name;
1031
+ if (node.type === "StringLiteral" && typeof node.value === "string") return node.value;
1032
+ return void 0;
1033
+ }
1034
+ function walkAst(node, ancestors, visit) {
1035
+ visit(node, ancestors);
1036
+ for (const value of Object.values(node)) {
1037
+ if (Array.isArray(value)) {
1038
+ for (const item of value) {
1039
+ const child2 = asAstNode(item);
1040
+ if (child2) walkAst(child2, [...ancestors, node], visit);
1041
+ }
1042
+ continue;
1043
+ }
1044
+ const child = asAstNode(value);
1045
+ if (child) walkAst(child, [...ancestors, node], visit);
1046
+ }
1047
+ }
1048
+ function walkTemplateAst(node, visit) {
1049
+ visit(node);
1050
+ for (const child of node.props ?? []) walkTemplateAst(child, visit);
1051
+ for (const child of node.children ?? []) walkTemplateAst(child, visit);
1052
+ }
1053
+ function asAstNode(value) {
1054
+ return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string" ? value : void 0;
1055
+ }
1056
+ function requiredOffset(value, filename, block) {
1057
+ if (typeof value === "number") return value;
1058
+ throw errorAtBlock(
1059
+ filename,
1060
+ block,
1061
+ "Unable to determine the definePdf() source range."
1062
+ );
1063
+ }
1064
+ function replaceWithWhitespace(source, start, end) {
1065
+ const whitespace = source.slice(start, end).replace(/[^\r\n]/g, " ");
1066
+ return source.slice(0, start) + whitespace + source.slice(end);
1067
+ }
1068
+ function errorAtNode(filename, block, node, message) {
1069
+ const nodeLine = node.loc?.start.line ?? 1;
1070
+ const line = block.loc.start.line + nodeLine - 1;
1071
+ const column = nodeLine === 1 ? block.loc.start.column + (node.loc?.start.column ?? 0) : (node.loc?.start.column ?? 0) + 1;
1072
+ return new PdfSfcCompileError(filename, line, column, message);
1073
+ }
1074
+ function errorAtBlock(filename, block, message) {
1075
+ return new PdfSfcCompileError(
1076
+ filename,
1077
+ block.loc.start.line,
1078
+ block.loc.start.column,
1079
+ message
1080
+ );
1081
+ }
1082
+ function errorAtTemplateNode(filename, node, message) {
1083
+ return new PdfSfcCompileError(
1084
+ filename,
1085
+ node.loc?.start.line ?? 1,
1086
+ node.loc?.start.column ?? 1,
1087
+ message
1088
+ );
1089
+ }
1090
+ function normalizeScriptParseError(error, filename, block) {
1091
+ const location = errorLocation(error);
1092
+ const line = block.loc.start.line + (location?.line ?? 1) - 1;
1093
+ const column = location?.line === 1 ? block.loc.start.column + (location.column ?? 0) : (location?.column ?? 0) + 1;
1094
+ return new PdfSfcCompileError(filename, line, column, errorMessage(error));
1095
+ }
1096
+ function normalizeCompilerError(error, filename) {
1097
+ if (error instanceof PdfSfcCompileError) return error;
1098
+ const location = errorLocation(error);
1099
+ return new PdfSfcCompileError(
1100
+ filename,
1101
+ location?.line ?? 1,
1102
+ (location?.column ?? 0) + 1,
1103
+ errorMessage(error)
1104
+ );
1105
+ }
1106
+ function normalizeBuildError(error, filename) {
1107
+ const first = isRecord(error) && Array.isArray(error.errors) && error.errors.length > 0 ? error.errors[0] : error;
1108
+ const location = isRecord(first) && isRecord(first.location) ? first.location : void 0;
1109
+ return new PdfSfcCompileError(
1110
+ filename,
1111
+ typeof location?.line === "number" ? location.line : 1,
1112
+ typeof location?.column === "number" ? location.column + 1 : 1,
1113
+ errorMessage(first)
1114
+ );
1115
+ }
1116
+ function errorLocation(error) {
1117
+ if (!isRecord(error) || !isRecord(error.loc)) return void 0;
1118
+ const location = isRecord(error.loc.start) ? error.loc.start : error.loc;
1119
+ return typeof location.line === "number" && typeof location.column === "number" ? { column: location.column, line: location.line } : void 0;
1120
+ }
1121
+ function errorMessage(error) {
1122
+ if (typeof error === "string") return error;
1123
+ if (error instanceof Error) return error.message;
1124
+ if (isRecord(error) && typeof error.text === "string") return error.text;
1125
+ if (isRecord(error) && typeof error.message === "string") return error.message;
1126
+ return "Failed to compile PDF component.";
1127
+ }
1128
+ function isRecord(value) {
1129
+ return typeof value === "object" && value !== null;
1130
+ }
1131
+
1132
+ export { discoverPdfComponentFiles as a, discoverPdfImageFiles as b, bundlePdfFonts as c, discoverPdfTemplates as d, createPdfSfcPlugin as e, classifyPdfWatchEvent as f, compilePdfSfc as g, templateKeyFromRelativePath as t };