pdf-smith 0.1.0 → 0.2.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.
package/dist/cli.js ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/add.ts
4
+ import fs from "fs";
5
+ import path from "path";
6
+ var SLUG_REGEX = /^[a-z0-9]+(-[a-z0-9]+)*$/;
7
+ function toTitle(slug) {
8
+ return slug.split("-").map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
9
+ }
10
+ function addDocument({ slug, root }) {
11
+ if (!SLUG_REGEX.test(slug)) {
12
+ throw new Error(
13
+ `Invalid slug "${slug}". Use lowercase letters, numbers, and hyphens (e.g. "my-report").`
14
+ );
15
+ }
16
+ const docDir = path.join(root, "pdfs", slug);
17
+ if (fs.existsSync(docDir)) {
18
+ throw new Error(`Document "${slug}" already exists at ${docDir}`);
19
+ }
20
+ const pagesDir = path.join(docDir, "pages");
21
+ fs.mkdirSync(pagesDir, { recursive: true });
22
+ const title = toTitle(slug);
23
+ const page1Content = `import '../../../styles.css';
24
+
25
+ export default function Page1() {
26
+ return (
27
+ <div className="h-full flex flex-col items-center justify-center p-12 font-sans">
28
+ <h1 className="text-4xl font-bold tracking-tight text-gray-900">${title}</h1>
29
+ <p className="mt-4 text-gray-500">Start editing this page to build your document.</p>
30
+ </div>
31
+ );
32
+ }
33
+ `;
34
+ const configContent = `import { defineConfig } from 'pdf-smith';
35
+
36
+ export default defineConfig({
37
+ pageNumbers: { enabled: false },
38
+ });
39
+ `;
40
+ const page1Path = path.join(pagesDir, "Page1.tsx");
41
+ const configPath = path.join(docDir, "config.ts");
42
+ fs.writeFileSync(page1Path, page1Content);
43
+ fs.writeFileSync(configPath, configContent);
44
+ return {
45
+ paths: [
46
+ path.relative(root, pagesDir),
47
+ path.relative(root, page1Path),
48
+ path.relative(root, configPath)
49
+ ]
50
+ };
51
+ }
52
+
53
+ // src/cli.ts
54
+ var args = process.argv.slice(2);
55
+ var command = args[0];
56
+ if (command === "add") {
57
+ const slug = args[1];
58
+ if (!slug || slug === "--help") {
59
+ console.log("Usage: pdf-smith add <slug>");
60
+ process.exit(slug ? 0 : 1);
61
+ }
62
+ try {
63
+ addDocument({ slug, root: process.cwd() });
64
+ console.log(`Created document '${slug}'. Start editing at pdfs/${slug}/pages/Page1.tsx`);
65
+ } catch (error) {
66
+ console.error(error.message);
67
+ process.exit(1);
68
+ }
69
+ } else {
70
+ console.log("Usage: pdf-smith add <slug>");
71
+ process.exit(command === "--help" || command === "-h" ? 0 : 1);
72
+ }
package/dist/index.cjs CHANGED
@@ -24,10 +24,16 @@ __export(index_exports, {
24
24
  DEFAULT_PAGE_SIZE: () => DEFAULT_PAGE_SIZE,
25
25
  Document: () => Document,
26
26
  PAGE_SIZES: () => PAGE_SIZES,
27
- Page: () => Page
27
+ Page: () => Page,
28
+ defineConfig: () => defineConfig
28
29
  });
29
30
  module.exports = __toCommonJS(index_exports);
30
31
 
32
+ // src/config.ts
33
+ function defineConfig(config) {
34
+ return config;
35
+ }
36
+
31
37
  // src/constants.ts
32
38
  var PAGE_SIZES = {
33
39
  A4: { width: 210, height: 297 },
@@ -141,6 +147,7 @@ function Page({
141
147
  DEFAULT_PAGE_SIZE,
142
148
  Document,
143
149
  PAGE_SIZES,
144
- Page
150
+ Page,
151
+ defineConfig
145
152
  });
146
153
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/styles.ts","../src/document.tsx","../src/page.tsx"],"sourcesContent":["export { DEFAULT_PADDING, DEFAULT_PAGE_SIZE, PAGE_SIZES } from './constants';\nexport { Document } from './document';\nexport { Page } from './page';\nexport type {\n DocumentProps,\n PageDimensions,\n PagePadding,\n PageProps,\n PageSize,\n PageSizePreset,\n} from './types';\n","import type { PageDimensions, PageSizePreset } from './types';\n\nexport const PAGE_SIZES: Record<PageSizePreset, PageDimensions> = {\n A4: { width: 210, height: 297 },\n Letter: { width: 215.9, height: 279.4 },\n A3: { width: 297, height: 420 },\n};\n\nexport const DEFAULT_PADDING = 0;\n\nexport const DEFAULT_PAGE_SIZE: PageSizePreset = 'A4';\n","import type { PageDimensions, PagePadding } from './types';\n\ninterface ResolvedPadding {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\nexport function resolvePadding(padding: PagePadding): ResolvedPadding {\n if (typeof padding === 'number') {\n return { top: padding, right: padding, bottom: padding, left: padding };\n }\n return {\n top: padding.top ?? 0,\n right: padding.right ?? 0,\n bottom: padding.bottom ?? 0,\n left: padding.left ?? 0,\n };\n}\n\nexport function getPageStyle(\n dimensions: PageDimensions,\n padding: ResolvedPadding,\n): React.CSSProperties {\n return {\n width: `${dimensions.width}mm`,\n height: `${dimensions.height}mm`,\n paddingTop: `${padding.top}mm`,\n paddingRight: `${padding.right}mm`,\n paddingBottom: `${padding.bottom}mm`,\n paddingLeft: `${padding.left}mm`,\n display: 'block',\n overflow: 'hidden',\n background: 'white',\n boxSizing: 'border-box',\n };\n}\n\nexport function getDocumentStyleSheet(dimensions: PageDimensions): string {\n return `\n@page {\n size: ${dimensions.width}mm ${dimensions.height}mm;\n margin: 0;\n}\n\n* {\n print-color-adjust: exact;\n -webkit-print-color-adjust: exact;\n}\n\n@media screen {\n [data-pdf-smith-page] {\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n margin-left: auto;\n margin-right: auto;\n }\n}\n\n@media print {\n [data-pdf-smith-page] {\n box-shadow: none;\n break-after: page;\n overflow: visible;\n height: auto;\n }\n}\n`.trim();\n}\n","import { PAGE_SIZES } from './constants';\nimport { getDocumentStyleSheet } from './styles';\nimport type { DocumentProps } from './types';\n\nexport function Document({ children, className }: DocumentProps): React.ReactElement {\n const styleSheet = getDocumentStyleSheet(PAGE_SIZES.A4);\n\n return (\n <div data-pdf-smith-document=\"\" className={className}>\n {/* biome-ignore lint/security/noDangerouslySetInnerHtml: trusted internal CSS */}\n <style dangerouslySetInnerHTML={{ __html: styleSheet }} />\n {children}\n </div>\n );\n}\n","import { DEFAULT_PADDING, DEFAULT_PAGE_SIZE, PAGE_SIZES } from './constants';\nimport { getPageStyle, resolvePadding } from './styles';\nimport type { PageDimensions, PageProps, PageSize } from './types';\n\nfunction resolveSize(size: PageSize): PageDimensions {\n if (typeof size === 'string') {\n return PAGE_SIZES[size];\n }\n return size;\n}\n\nexport function Page({\n children,\n className,\n size = DEFAULT_PAGE_SIZE,\n padding = DEFAULT_PADDING,\n id,\n}: PageProps): React.ReactElement {\n const dimensions = resolveSize(size);\n const resolvedPadding = resolvePadding(padding);\n const style = getPageStyle(dimensions, resolvedPadding);\n\n return (\n <div\n data-pdf-smith-page=\"\"\n data-page-width={dimensions.width}\n data-page-height={dimensions.height}\n className={className}\n id={id}\n style={style}\n >\n {children}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEO,IAAM,aAAqD;AAAA,EAChE,IAAI,EAAE,OAAO,KAAK,QAAQ,IAAI;AAAA,EAC9B,QAAQ,EAAE,OAAO,OAAO,QAAQ,MAAM;AAAA,EACtC,IAAI,EAAE,OAAO,KAAK,QAAQ,IAAI;AAChC;AAEO,IAAM,kBAAkB;AAExB,IAAM,oBAAoC;;;ACD1C,SAAS,eAAe,SAAuC;AACpE,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO,EAAE,KAAK,SAAS,OAAO,SAAS,QAAQ,SAAS,MAAM,QAAQ;AAAA,EACxE;AACA,SAAO;AAAA,IACL,KAAK,QAAQ,OAAO;AAAA,IACpB,OAAO,QAAQ,SAAS;AAAA,IACxB,QAAQ,QAAQ,UAAU;AAAA,IAC1B,MAAM,QAAQ,QAAQ;AAAA,EACxB;AACF;AAEO,SAAS,aACd,YACA,SACqB;AACrB,SAAO;AAAA,IACL,OAAO,GAAG,WAAW,KAAK;AAAA,IAC1B,QAAQ,GAAG,WAAW,MAAM;AAAA,IAC5B,YAAY,GAAG,QAAQ,GAAG;AAAA,IAC1B,cAAc,GAAG,QAAQ,KAAK;AAAA,IAC9B,eAAe,GAAG,QAAQ,MAAM;AAAA,IAChC,aAAa,GAAG,QAAQ,IAAI;AAAA,IAC5B,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,WAAW;AAAA,EACb;AACF;AAEO,SAAS,sBAAsB,YAAoC;AACxE,SAAO;AAAA;AAAA,UAEC,WAAW,KAAK,MAAM,WAAW,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyB/C,KAAK;AACP;;;AC5DI;AAJG,SAAS,SAAS,EAAE,UAAU,UAAU,GAAsC;AACnF,QAAM,aAAa,sBAAsB,WAAW,EAAE;AAEtD,SACE,6CAAC,SAAI,2BAAwB,IAAG,WAE9B;AAAA,gDAAC,WAAM,yBAAyB,EAAE,QAAQ,WAAW,GAAG;AAAA,IACvD;AAAA,KACH;AAEJ;;;ACSI,IAAAA,sBAAA;AAnBJ,SAAS,YAAY,MAAgC;AACnD,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAO,WAAW,IAAI;AAAA,EACxB;AACA,SAAO;AACT;AAEO,SAAS,KAAK;AAAA,EACnB;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,UAAU;AAAA,EACV;AACF,GAAkC;AAChC,QAAM,aAAa,YAAY,IAAI;AACnC,QAAM,kBAAkB,eAAe,OAAO;AAC9C,QAAM,QAAQ,aAAa,YAAY,eAAe;AAEtD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,uBAAoB;AAAA,MACpB,mBAAiB,WAAW;AAAA,MAC5B,oBAAkB,WAAW;AAAA,MAC7B;AAAA,MACA;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":["import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/constants.ts","../src/styles.ts","../src/document.tsx","../src/page.tsx"],"sourcesContent":["export type { DocumentConfig } from './config';\nexport { defineConfig } from './config';\nexport { DEFAULT_PADDING, DEFAULT_PAGE_SIZE, PAGE_SIZES } from './constants';\nexport { Document } from './document';\nexport { Page } from './page';\nexport type {\n DocumentProps,\n PageDimensions,\n PagePadding,\n PageProps,\n PageSize,\n PageSizePreset,\n} from './types';\n","export interface DocumentConfig {\n pageNumbers?: { enabled: boolean; template?: string };\n output?: string;\n}\n\nexport function defineConfig(config: DocumentConfig): DocumentConfig {\n return config;\n}\n","import type { PageDimensions, PageSizePreset } from './types';\n\nexport const PAGE_SIZES: Record<PageSizePreset, PageDimensions> = {\n A4: { width: 210, height: 297 },\n Letter: { width: 215.9, height: 279.4 },\n A3: { width: 297, height: 420 },\n};\n\nexport const DEFAULT_PADDING = 0;\n\nexport const DEFAULT_PAGE_SIZE: PageSizePreset = 'A4';\n","import type { PageDimensions, PagePadding } from './types';\n\ninterface ResolvedPadding {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\nexport function resolvePadding(padding: PagePadding): ResolvedPadding {\n if (typeof padding === 'number') {\n return { top: padding, right: padding, bottom: padding, left: padding };\n }\n return {\n top: padding.top ?? 0,\n right: padding.right ?? 0,\n bottom: padding.bottom ?? 0,\n left: padding.left ?? 0,\n };\n}\n\nexport function getPageStyle(\n dimensions: PageDimensions,\n padding: ResolvedPadding,\n): React.CSSProperties {\n return {\n width: `${dimensions.width}mm`,\n height: `${dimensions.height}mm`,\n paddingTop: `${padding.top}mm`,\n paddingRight: `${padding.right}mm`,\n paddingBottom: `${padding.bottom}mm`,\n paddingLeft: `${padding.left}mm`,\n display: 'block',\n overflow: 'hidden',\n background: 'white',\n boxSizing: 'border-box',\n };\n}\n\nexport function getDocumentStyleSheet(dimensions: PageDimensions): string {\n return `\n@page {\n size: ${dimensions.width}mm ${dimensions.height}mm;\n margin: 0;\n}\n\n* {\n print-color-adjust: exact;\n -webkit-print-color-adjust: exact;\n}\n\n@media screen {\n [data-pdf-smith-page] {\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n margin-left: auto;\n margin-right: auto;\n }\n}\n\n@media print {\n [data-pdf-smith-page] {\n box-shadow: none;\n break-after: page;\n overflow: visible;\n height: auto;\n }\n}\n`.trim();\n}\n","import { PAGE_SIZES } from './constants';\nimport { getDocumentStyleSheet } from './styles';\nimport type { DocumentProps } from './types';\n\nexport function Document({ children, className }: DocumentProps): React.ReactElement {\n const styleSheet = getDocumentStyleSheet(PAGE_SIZES.A4);\n\n return (\n <div data-pdf-smith-document=\"\" className={className}>\n {/* biome-ignore lint/security/noDangerouslySetInnerHtml: trusted internal CSS */}\n <style dangerouslySetInnerHTML={{ __html: styleSheet }} />\n {children}\n </div>\n );\n}\n","import { DEFAULT_PADDING, DEFAULT_PAGE_SIZE, PAGE_SIZES } from './constants';\nimport { getPageStyle, resolvePadding } from './styles';\nimport type { PageDimensions, PageProps, PageSize } from './types';\n\nfunction resolveSize(size: PageSize): PageDimensions {\n if (typeof size === 'string') {\n return PAGE_SIZES[size];\n }\n return size;\n}\n\nexport function Page({\n children,\n className,\n size = DEFAULT_PAGE_SIZE,\n padding = DEFAULT_PADDING,\n id,\n}: PageProps): React.ReactElement {\n const dimensions = resolveSize(size);\n const resolvedPadding = resolvePadding(padding);\n const style = getPageStyle(dimensions, resolvedPadding);\n\n return (\n <div\n data-pdf-smith-page=\"\"\n data-page-width={dimensions.width}\n data-page-height={dimensions.height}\n className={className}\n id={id}\n style={style}\n >\n {children}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKO,SAAS,aAAa,QAAwC;AACnE,SAAO;AACT;;;ACLO,IAAM,aAAqD;AAAA,EAChE,IAAI,EAAE,OAAO,KAAK,QAAQ,IAAI;AAAA,EAC9B,QAAQ,EAAE,OAAO,OAAO,QAAQ,MAAM;AAAA,EACtC,IAAI,EAAE,OAAO,KAAK,QAAQ,IAAI;AAChC;AAEO,IAAM,kBAAkB;AAExB,IAAM,oBAAoC;;;ACD1C,SAAS,eAAe,SAAuC;AACpE,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO,EAAE,KAAK,SAAS,OAAO,SAAS,QAAQ,SAAS,MAAM,QAAQ;AAAA,EACxE;AACA,SAAO;AAAA,IACL,KAAK,QAAQ,OAAO;AAAA,IACpB,OAAO,QAAQ,SAAS;AAAA,IACxB,QAAQ,QAAQ,UAAU;AAAA,IAC1B,MAAM,QAAQ,QAAQ;AAAA,EACxB;AACF;AAEO,SAAS,aACd,YACA,SACqB;AACrB,SAAO;AAAA,IACL,OAAO,GAAG,WAAW,KAAK;AAAA,IAC1B,QAAQ,GAAG,WAAW,MAAM;AAAA,IAC5B,YAAY,GAAG,QAAQ,GAAG;AAAA,IAC1B,cAAc,GAAG,QAAQ,KAAK;AAAA,IAC9B,eAAe,GAAG,QAAQ,MAAM;AAAA,IAChC,aAAa,GAAG,QAAQ,IAAI;AAAA,IAC5B,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,WAAW;AAAA,EACb;AACF;AAEO,SAAS,sBAAsB,YAAoC;AACxE,SAAO;AAAA;AAAA,UAEC,WAAW,KAAK,MAAM,WAAW,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyB/C,KAAK;AACP;;;AC5DI;AAJG,SAAS,SAAS,EAAE,UAAU,UAAU,GAAsC;AACnF,QAAM,aAAa,sBAAsB,WAAW,EAAE;AAEtD,SACE,6CAAC,SAAI,2BAAwB,IAAG,WAE9B;AAAA,gDAAC,WAAM,yBAAyB,EAAE,QAAQ,WAAW,GAAG;AAAA,IACvD;AAAA,KACH;AAEJ;;;ACSI,IAAAA,sBAAA;AAnBJ,SAAS,YAAY,MAAgC;AACnD,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAO,WAAW,IAAI;AAAA,EACxB;AACA,SAAO;AACT;AAEO,SAAS,KAAK;AAAA,EACnB;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,UAAU;AAAA,EACV;AACF,GAAkC;AAChC,QAAM,aAAa,YAAY,IAAI;AACnC,QAAM,kBAAkB,eAAe,OAAO;AAC9C,QAAM,QAAQ,aAAa,YAAY,eAAe;AAEtD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,uBAAoB;AAAA,MACpB,mBAAiB,WAAW;AAAA,MAC5B,oBAAkB,WAAW;AAAA,MAC7B;AAAA,MACA;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":["import_jsx_runtime"]}
package/dist/index.d.cts CHANGED
@@ -1,3 +1,12 @@
1
+ interface DocumentConfig {
2
+ pageNumbers?: {
3
+ enabled: boolean;
4
+ template?: string;
5
+ };
6
+ output?: string;
7
+ }
8
+ declare function defineConfig(config: DocumentConfig): DocumentConfig;
9
+
1
10
  type PageSizePreset = 'A4' | 'Letter' | 'A3';
2
11
  interface PageDimensions {
3
12
  width: number;
@@ -30,4 +39,4 @@ declare function Document({ children, className }: DocumentProps): React.ReactEl
30
39
 
31
40
  declare function Page({ children, className, size, padding, id, }: PageProps): React.ReactElement;
32
41
 
33
- export { DEFAULT_PADDING, DEFAULT_PAGE_SIZE, Document, type DocumentProps, PAGE_SIZES, Page, type PageDimensions, type PagePadding, type PageProps, type PageSize, type PageSizePreset };
42
+ export { DEFAULT_PADDING, DEFAULT_PAGE_SIZE, Document, type DocumentConfig, type DocumentProps, PAGE_SIZES, Page, type PageDimensions, type PagePadding, type PageProps, type PageSize, type PageSizePreset, defineConfig };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,12 @@
1
+ interface DocumentConfig {
2
+ pageNumbers?: {
3
+ enabled: boolean;
4
+ template?: string;
5
+ };
6
+ output?: string;
7
+ }
8
+ declare function defineConfig(config: DocumentConfig): DocumentConfig;
9
+
1
10
  type PageSizePreset = 'A4' | 'Letter' | 'A3';
2
11
  interface PageDimensions {
3
12
  width: number;
@@ -30,4 +39,4 @@ declare function Document({ children, className }: DocumentProps): React.ReactEl
30
39
 
31
40
  declare function Page({ children, className, size, padding, id, }: PageProps): React.ReactElement;
32
41
 
33
- export { DEFAULT_PADDING, DEFAULT_PAGE_SIZE, Document, type DocumentProps, PAGE_SIZES, Page, type PageDimensions, type PagePadding, type PageProps, type PageSize, type PageSizePreset };
42
+ export { DEFAULT_PADDING, DEFAULT_PAGE_SIZE, Document, type DocumentConfig, type DocumentProps, PAGE_SIZES, Page, type PageDimensions, type PagePadding, type PageProps, type PageSize, type PageSizePreset, defineConfig };
package/dist/index.js CHANGED
@@ -1,3 +1,8 @@
1
+ // src/config.ts
2
+ function defineConfig(config) {
3
+ return config;
4
+ }
5
+
1
6
  // src/constants.ts
2
7
  var PAGE_SIZES = {
3
8
  A4: { width: 210, height: 297 },
@@ -110,6 +115,7 @@ export {
110
115
  DEFAULT_PAGE_SIZE,
111
116
  Document,
112
117
  PAGE_SIZES,
113
- Page
118
+ Page,
119
+ defineConfig
114
120
  };
115
121
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/styles.ts","../src/document.tsx","../src/page.tsx"],"sourcesContent":["import type { PageDimensions, PageSizePreset } from './types';\n\nexport const PAGE_SIZES: Record<PageSizePreset, PageDimensions> = {\n A4: { width: 210, height: 297 },\n Letter: { width: 215.9, height: 279.4 },\n A3: { width: 297, height: 420 },\n};\n\nexport const DEFAULT_PADDING = 0;\n\nexport const DEFAULT_PAGE_SIZE: PageSizePreset = 'A4';\n","import type { PageDimensions, PagePadding } from './types';\n\ninterface ResolvedPadding {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\nexport function resolvePadding(padding: PagePadding): ResolvedPadding {\n if (typeof padding === 'number') {\n return { top: padding, right: padding, bottom: padding, left: padding };\n }\n return {\n top: padding.top ?? 0,\n right: padding.right ?? 0,\n bottom: padding.bottom ?? 0,\n left: padding.left ?? 0,\n };\n}\n\nexport function getPageStyle(\n dimensions: PageDimensions,\n padding: ResolvedPadding,\n): React.CSSProperties {\n return {\n width: `${dimensions.width}mm`,\n height: `${dimensions.height}mm`,\n paddingTop: `${padding.top}mm`,\n paddingRight: `${padding.right}mm`,\n paddingBottom: `${padding.bottom}mm`,\n paddingLeft: `${padding.left}mm`,\n display: 'block',\n overflow: 'hidden',\n background: 'white',\n boxSizing: 'border-box',\n };\n}\n\nexport function getDocumentStyleSheet(dimensions: PageDimensions): string {\n return `\n@page {\n size: ${dimensions.width}mm ${dimensions.height}mm;\n margin: 0;\n}\n\n* {\n print-color-adjust: exact;\n -webkit-print-color-adjust: exact;\n}\n\n@media screen {\n [data-pdf-smith-page] {\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n margin-left: auto;\n margin-right: auto;\n }\n}\n\n@media print {\n [data-pdf-smith-page] {\n box-shadow: none;\n break-after: page;\n overflow: visible;\n height: auto;\n }\n}\n`.trim();\n}\n","import { PAGE_SIZES } from './constants';\nimport { getDocumentStyleSheet } from './styles';\nimport type { DocumentProps } from './types';\n\nexport function Document({ children, className }: DocumentProps): React.ReactElement {\n const styleSheet = getDocumentStyleSheet(PAGE_SIZES.A4);\n\n return (\n <div data-pdf-smith-document=\"\" className={className}>\n {/* biome-ignore lint/security/noDangerouslySetInnerHtml: trusted internal CSS */}\n <style dangerouslySetInnerHTML={{ __html: styleSheet }} />\n {children}\n </div>\n );\n}\n","import { DEFAULT_PADDING, DEFAULT_PAGE_SIZE, PAGE_SIZES } from './constants';\nimport { getPageStyle, resolvePadding } from './styles';\nimport type { PageDimensions, PageProps, PageSize } from './types';\n\nfunction resolveSize(size: PageSize): PageDimensions {\n if (typeof size === 'string') {\n return PAGE_SIZES[size];\n }\n return size;\n}\n\nexport function Page({\n children,\n className,\n size = DEFAULT_PAGE_SIZE,\n padding = DEFAULT_PADDING,\n id,\n}: PageProps): React.ReactElement {\n const dimensions = resolveSize(size);\n const resolvedPadding = resolvePadding(padding);\n const style = getPageStyle(dimensions, resolvedPadding);\n\n return (\n <div\n data-pdf-smith-page=\"\"\n data-page-width={dimensions.width}\n data-page-height={dimensions.height}\n className={className}\n id={id}\n style={style}\n >\n {children}\n </div>\n );\n}\n"],"mappings":";AAEO,IAAM,aAAqD;AAAA,EAChE,IAAI,EAAE,OAAO,KAAK,QAAQ,IAAI;AAAA,EAC9B,QAAQ,EAAE,OAAO,OAAO,QAAQ,MAAM;AAAA,EACtC,IAAI,EAAE,OAAO,KAAK,QAAQ,IAAI;AAChC;AAEO,IAAM,kBAAkB;AAExB,IAAM,oBAAoC;;;ACD1C,SAAS,eAAe,SAAuC;AACpE,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO,EAAE,KAAK,SAAS,OAAO,SAAS,QAAQ,SAAS,MAAM,QAAQ;AAAA,EACxE;AACA,SAAO;AAAA,IACL,KAAK,QAAQ,OAAO;AAAA,IACpB,OAAO,QAAQ,SAAS;AAAA,IACxB,QAAQ,QAAQ,UAAU;AAAA,IAC1B,MAAM,QAAQ,QAAQ;AAAA,EACxB;AACF;AAEO,SAAS,aACd,YACA,SACqB;AACrB,SAAO;AAAA,IACL,OAAO,GAAG,WAAW,KAAK;AAAA,IAC1B,QAAQ,GAAG,WAAW,MAAM;AAAA,IAC5B,YAAY,GAAG,QAAQ,GAAG;AAAA,IAC1B,cAAc,GAAG,QAAQ,KAAK;AAAA,IAC9B,eAAe,GAAG,QAAQ,MAAM;AAAA,IAChC,aAAa,GAAG,QAAQ,IAAI;AAAA,IAC5B,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,WAAW;AAAA,EACb;AACF;AAEO,SAAS,sBAAsB,YAAoC;AACxE,SAAO;AAAA;AAAA,UAEC,WAAW,KAAK,MAAM,WAAW,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyB/C,KAAK;AACP;;;AC5DI,SAEE,KAFF;AAJG,SAAS,SAAS,EAAE,UAAU,UAAU,GAAsC;AACnF,QAAM,aAAa,sBAAsB,WAAW,EAAE;AAEtD,SACE,qBAAC,SAAI,2BAAwB,IAAG,WAE9B;AAAA,wBAAC,WAAM,yBAAyB,EAAE,QAAQ,WAAW,GAAG;AAAA,IACvD;AAAA,KACH;AAEJ;;;ACSI,gBAAAA,YAAA;AAnBJ,SAAS,YAAY,MAAgC;AACnD,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAO,WAAW,IAAI;AAAA,EACxB;AACA,SAAO;AACT;AAEO,SAAS,KAAK;AAAA,EACnB;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,UAAU;AAAA,EACV;AACF,GAAkC;AAChC,QAAM,aAAa,YAAY,IAAI;AACnC,QAAM,kBAAkB,eAAe,OAAO;AAC9C,QAAM,QAAQ,aAAa,YAAY,eAAe;AAEtD,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,uBAAoB;AAAA,MACpB,mBAAiB,WAAW;AAAA,MAC5B,oBAAkB,WAAW;AAAA,MAC7B;AAAA,MACA;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":["jsx"]}
1
+ {"version":3,"sources":["../src/config.ts","../src/constants.ts","../src/styles.ts","../src/document.tsx","../src/page.tsx"],"sourcesContent":["export interface DocumentConfig {\n pageNumbers?: { enabled: boolean; template?: string };\n output?: string;\n}\n\nexport function defineConfig(config: DocumentConfig): DocumentConfig {\n return config;\n}\n","import type { PageDimensions, PageSizePreset } from './types';\n\nexport const PAGE_SIZES: Record<PageSizePreset, PageDimensions> = {\n A4: { width: 210, height: 297 },\n Letter: { width: 215.9, height: 279.4 },\n A3: { width: 297, height: 420 },\n};\n\nexport const DEFAULT_PADDING = 0;\n\nexport const DEFAULT_PAGE_SIZE: PageSizePreset = 'A4';\n","import type { PageDimensions, PagePadding } from './types';\n\ninterface ResolvedPadding {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\nexport function resolvePadding(padding: PagePadding): ResolvedPadding {\n if (typeof padding === 'number') {\n return { top: padding, right: padding, bottom: padding, left: padding };\n }\n return {\n top: padding.top ?? 0,\n right: padding.right ?? 0,\n bottom: padding.bottom ?? 0,\n left: padding.left ?? 0,\n };\n}\n\nexport function getPageStyle(\n dimensions: PageDimensions,\n padding: ResolvedPadding,\n): React.CSSProperties {\n return {\n width: `${dimensions.width}mm`,\n height: `${dimensions.height}mm`,\n paddingTop: `${padding.top}mm`,\n paddingRight: `${padding.right}mm`,\n paddingBottom: `${padding.bottom}mm`,\n paddingLeft: `${padding.left}mm`,\n display: 'block',\n overflow: 'hidden',\n background: 'white',\n boxSizing: 'border-box',\n };\n}\n\nexport function getDocumentStyleSheet(dimensions: PageDimensions): string {\n return `\n@page {\n size: ${dimensions.width}mm ${dimensions.height}mm;\n margin: 0;\n}\n\n* {\n print-color-adjust: exact;\n -webkit-print-color-adjust: exact;\n}\n\n@media screen {\n [data-pdf-smith-page] {\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n margin-left: auto;\n margin-right: auto;\n }\n}\n\n@media print {\n [data-pdf-smith-page] {\n box-shadow: none;\n break-after: page;\n overflow: visible;\n height: auto;\n }\n}\n`.trim();\n}\n","import { PAGE_SIZES } from './constants';\nimport { getDocumentStyleSheet } from './styles';\nimport type { DocumentProps } from './types';\n\nexport function Document({ children, className }: DocumentProps): React.ReactElement {\n const styleSheet = getDocumentStyleSheet(PAGE_SIZES.A4);\n\n return (\n <div data-pdf-smith-document=\"\" className={className}>\n {/* biome-ignore lint/security/noDangerouslySetInnerHtml: trusted internal CSS */}\n <style dangerouslySetInnerHTML={{ __html: styleSheet }} />\n {children}\n </div>\n );\n}\n","import { DEFAULT_PADDING, DEFAULT_PAGE_SIZE, PAGE_SIZES } from './constants';\nimport { getPageStyle, resolvePadding } from './styles';\nimport type { PageDimensions, PageProps, PageSize } from './types';\n\nfunction resolveSize(size: PageSize): PageDimensions {\n if (typeof size === 'string') {\n return PAGE_SIZES[size];\n }\n return size;\n}\n\nexport function Page({\n children,\n className,\n size = DEFAULT_PAGE_SIZE,\n padding = DEFAULT_PADDING,\n id,\n}: PageProps): React.ReactElement {\n const dimensions = resolveSize(size);\n const resolvedPadding = resolvePadding(padding);\n const style = getPageStyle(dimensions, resolvedPadding);\n\n return (\n <div\n data-pdf-smith-page=\"\"\n data-page-width={dimensions.width}\n data-page-height={dimensions.height}\n className={className}\n id={id}\n style={style}\n >\n {children}\n </div>\n );\n}\n"],"mappings":";AAKO,SAAS,aAAa,QAAwC;AACnE,SAAO;AACT;;;ACLO,IAAM,aAAqD;AAAA,EAChE,IAAI,EAAE,OAAO,KAAK,QAAQ,IAAI;AAAA,EAC9B,QAAQ,EAAE,OAAO,OAAO,QAAQ,MAAM;AAAA,EACtC,IAAI,EAAE,OAAO,KAAK,QAAQ,IAAI;AAChC;AAEO,IAAM,kBAAkB;AAExB,IAAM,oBAAoC;;;ACD1C,SAAS,eAAe,SAAuC;AACpE,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO,EAAE,KAAK,SAAS,OAAO,SAAS,QAAQ,SAAS,MAAM,QAAQ;AAAA,EACxE;AACA,SAAO;AAAA,IACL,KAAK,QAAQ,OAAO;AAAA,IACpB,OAAO,QAAQ,SAAS;AAAA,IACxB,QAAQ,QAAQ,UAAU;AAAA,IAC1B,MAAM,QAAQ,QAAQ;AAAA,EACxB;AACF;AAEO,SAAS,aACd,YACA,SACqB;AACrB,SAAO;AAAA,IACL,OAAO,GAAG,WAAW,KAAK;AAAA,IAC1B,QAAQ,GAAG,WAAW,MAAM;AAAA,IAC5B,YAAY,GAAG,QAAQ,GAAG;AAAA,IAC1B,cAAc,GAAG,QAAQ,KAAK;AAAA,IAC9B,eAAe,GAAG,QAAQ,MAAM;AAAA,IAChC,aAAa,GAAG,QAAQ,IAAI;AAAA,IAC5B,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,WAAW;AAAA,EACb;AACF;AAEO,SAAS,sBAAsB,YAAoC;AACxE,SAAO;AAAA;AAAA,UAEC,WAAW,KAAK,MAAM,WAAW,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyB/C,KAAK;AACP;;;AC5DI,SAEE,KAFF;AAJG,SAAS,SAAS,EAAE,UAAU,UAAU,GAAsC;AACnF,QAAM,aAAa,sBAAsB,WAAW,EAAE;AAEtD,SACE,qBAAC,SAAI,2BAAwB,IAAG,WAE9B;AAAA,wBAAC,WAAM,yBAAyB,EAAE,QAAQ,WAAW,GAAG;AAAA,IACvD;AAAA,KACH;AAEJ;;;ACSI,gBAAAA,YAAA;AAnBJ,SAAS,YAAY,MAAgC;AACnD,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAO,WAAW,IAAI;AAAA,EACxB;AACA,SAAO;AACT;AAEO,SAAS,KAAK;AAAA,EACnB;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,UAAU;AAAA,EACV;AACF,GAAkC;AAChC,QAAM,aAAa,YAAY,IAAI;AACnC,QAAM,kBAAkB,eAAe,OAAO;AAC9C,QAAM,QAAQ,aAAa,YAAY,eAAe;AAEtD,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,uBAAoB;AAAA,MACpB,mBAAiB,WAAW;AAAA,MAC5B,oBAAkB,WAAW;AAAA,MAC7B;AAAA,MACA;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":["jsx"]}
@@ -1,13 +1,14 @@
1
1
  // src/preview/preview-plugin.ts
2
- var VIRTUAL_PAGES_ID = "virtual:pdf-smith-pages";
3
- var RESOLVED_VIRTUAL_PAGES_ID = `\0${VIRTUAL_PAGES_ID}`;
4
- function pdfSmithPreviewPlugin({ pkgSrcDir, pagesGlob }) {
2
+ var VIRTUAL_DOCUMENTS_ID = "virtual:pdf-smith-documents";
3
+ var RESOLVED_VIRTUAL_DOCUMENTS_ID = `\0${VIRTUAL_DOCUMENTS_ID}`;
4
+ function pdfSmithPreviewPlugin({ pkgSrcDir }) {
5
5
  return {
6
6
  name: "pdf-smith-preview",
7
7
  configureServer(server) {
8
8
  return () => {
9
9
  server.middlewares.use((req, res, next) => {
10
- if (req.url !== "/" && req.url !== "/index.html") {
10
+ const url = req.url ?? "";
11
+ if (url.includes(".") || url.startsWith("/@") || url.startsWith("/node_modules/")) {
11
12
  next();
12
13
  return;
13
14
  }
@@ -54,24 +55,43 @@ window.__vite_plugin_react_preamble_installed__ = true
54
55
  };
55
56
  },
56
57
  resolveId(id) {
57
- if (id === VIRTUAL_PAGES_ID) {
58
- return RESOLVED_VIRTUAL_PAGES_ID;
58
+ if (id === VIRTUAL_DOCUMENTS_ID) {
59
+ return RESOLVED_VIRTUAL_DOCUMENTS_ID;
59
60
  }
60
61
  },
61
62
  load(id) {
62
- if (id === RESOLVED_VIRTUAL_PAGES_ID) {
63
+ if (id === RESOLVED_VIRTUAL_DOCUMENTS_ID) {
63
64
  return `
64
- const modules = import.meta.glob('${pagesGlob}', { eager: true });
65
+ const pageModules = import.meta.glob('/pdfs/*/pages/**/*.tsx', { eager: true });
66
+ const configModules = import.meta.glob('/pdfs/*/config.ts', { eager: true });
65
67
 
66
- export function getPages() {
67
- const pages = {};
68
- for (const [path, mod] of Object.entries(modules)) {
69
- const name = path
70
- .replace(/^\\/pages\\//, '')
71
- .replace(/\\.[^.]+$/, '');
72
- pages[name] = mod.default;
68
+ export function getDocuments() {
69
+ const documents = {};
70
+
71
+ for (const [path, mod] of Object.entries(pageModules)) {
72
+ const match = path.match(/^\\/pdfs\\/([^/]+)\\/pages\\/(.+)\\.[^.]+$/);
73
+ if (!match) continue;
74
+ const [, slug, pageName] = match;
75
+ if (!documents[slug]) {
76
+ documents[slug] = { slug, pages: {}, config: undefined };
77
+ }
78
+ documents[slug].pages[pageName] = mod.default;
79
+ }
80
+
81
+ for (const [path, mod] of Object.entries(configModules)) {
82
+ const match = path.match(/^\\/pdfs\\/([^/]+)\\/config\\.ts$/);
83
+ if (!match) continue;
84
+ const slug = match[1];
85
+ if (documents[slug]) {
86
+ documents[slug].config = mod.default;
87
+ }
73
88
  }
74
- return pages;
89
+
90
+ return documents;
91
+ }
92
+
93
+ export function getDocumentSlugs() {
94
+ return Object.keys(getDocuments());
75
95
  }
76
96
  `;
77
97
  }
@@ -81,4 +101,4 @@ export function getPages() {
81
101
  export {
82
102
  pdfSmithPreviewPlugin
83
103
  };
84
- //# sourceMappingURL=preview-plugin-QWJEXDW2.js.map
104
+ //# sourceMappingURL=preview-plugin-QDLEMAEE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/preview/preview-plugin.ts"],"sourcesContent":["import type { Plugin } from 'vite';\n\nconst VIRTUAL_DOCUMENTS_ID = 'virtual:pdf-smith-documents';\nconst RESOLVED_VIRTUAL_DOCUMENTS_ID = `\\0${VIRTUAL_DOCUMENTS_ID}`;\n\ninterface PreviewPluginOptions {\n pkgSrcDir: string;\n}\n\nexport function pdfSmithPreviewPlugin({ pkgSrcDir }: PreviewPluginOptions): Plugin {\n return {\n name: 'pdf-smith-preview',\n\n configureServer(server) {\n return () => {\n server.middlewares.use((req, res, next) => {\n const url = req.url ?? '';\n\n // Skip file requests, Vite internals, and node_modules\n if (url.includes('.') || url.startsWith('/@') || url.startsWith('/node_modules/')) {\n next();\n return;\n }\n\n const html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <script type=\"module\">\nimport RefreshRuntime from \"/@react-refresh\"\nRefreshRuntime.injectIntoGlobalHook(window)\nwindow.$RefreshReg$ = () => {}\nwindow.$RefreshSig$ = () => (type) => type\nwindow.__vite_plugin_react_preamble_installed__ = true\n</script>\n <script type=\"module\" src=\"/@vite/client\"></script>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>pdf-smith Preview</title>\n <style>\n @media print {\n [data-pdf-smith-nav] { display: none !important; }\n [data-pdf-smith-container] {\n margin-left: 0 !important;\n padding: 0 !important;\n background: none !important;\n }\n [data-pdf-smith-container] > [data-pdf-smith-document] > div {\n margin-bottom: 0 !important;\n }\n [data-pdf-smith-container] > [data-pdf-smith-document] > div > div:first-child {\n display: none !important;\n }\n }\n </style>\n</head>\n<body>\n <div id=\"root\"></div>\n <script type=\"module\" src=\"/@fs/${pkgSrcDir}/preview/entry.tsx\"></script>\n</body>\n</html>`;\n\n res.setHeader('Content-Type', 'text/html');\n res.statusCode = 200;\n res.end(html);\n });\n };\n },\n\n resolveId(id) {\n if (id === VIRTUAL_DOCUMENTS_ID) {\n return RESOLVED_VIRTUAL_DOCUMENTS_ID;\n }\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_DOCUMENTS_ID) {\n return `\nconst pageModules = import.meta.glob('/pdfs/*/pages/**/*.tsx', { eager: true });\nconst configModules = import.meta.glob('/pdfs/*/config.ts', { eager: true });\n\nexport function getDocuments() {\n const documents = {};\n\n for (const [path, mod] of Object.entries(pageModules)) {\n const match = path.match(/^\\\\/pdfs\\\\/([^/]+)\\\\/pages\\\\/(.+)\\\\.[^.]+$/);\n if (!match) continue;\n const [, slug, pageName] = match;\n if (!documents[slug]) {\n documents[slug] = { slug, pages: {}, config: undefined };\n }\n documents[slug].pages[pageName] = mod.default;\n }\n\n for (const [path, mod] of Object.entries(configModules)) {\n const match = path.match(/^\\\\/pdfs\\\\/([^/]+)\\\\/config\\\\.ts$/);\n if (!match) continue;\n const slug = match[1];\n if (documents[slug]) {\n documents[slug].config = mod.default;\n }\n }\n\n return documents;\n}\n\nexport function getDocumentSlugs() {\n return Object.keys(getDocuments());\n}\n`;\n }\n },\n };\n}\n"],"mappings":";AAEA,IAAM,uBAAuB;AAC7B,IAAM,gCAAgC,KAAK,oBAAoB;AAMxD,SAAS,sBAAsB,EAAE,UAAU,GAAiC;AACjF,SAAO;AAAA,IACL,MAAM;AAAA,IAEN,gBAAgB,QAAQ;AACtB,aAAO,MAAM;AACX,eAAO,YAAY,IAAI,CAAC,KAAK,KAAK,SAAS;AACzC,gBAAM,MAAM,IAAI,OAAO;AAGvB,cAAI,IAAI,SAAS,GAAG,KAAK,IAAI,WAAW,IAAI,KAAK,IAAI,WAAW,gBAAgB,GAAG;AACjF,iBAAK;AACL;AAAA,UACF;AAEA,gBAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oCAiCa,SAAS;AAAA;AAAA;AAInC,cAAI,UAAU,gBAAgB,WAAW;AACzC,cAAI,aAAa;AACjB,cAAI,IAAI,IAAI;AAAA,QACd,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IAEA,UAAU,IAAI;AACZ,UAAI,OAAO,sBAAsB;AAC/B,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA,KAAK,IAAI;AACP,UAAI,OAAO,+BAA+B;AACxC,eAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAiCT;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
package/dist/server.cjs CHANGED
@@ -31,10 +31,39 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
33
  // src/export.ts
34
+ function discoverDocuments(root) {
35
+ const pdfsDir = import_node_path.default.join(root, "pdfs");
36
+ if (!import_node_fs.default.existsSync(pdfsDir)) return [];
37
+ const entries = import_node_fs.default.readdirSync(pdfsDir, { withFileTypes: true });
38
+ const documents = [];
39
+ for (const entry of entries) {
40
+ if (!entry.isDirectory()) continue;
41
+ const pagesDir = import_node_path.default.join(pdfsDir, entry.name, "pages");
42
+ if (!import_node_fs.default.existsSync(pagesDir)) continue;
43
+ const configPath = import_node_path.default.join(pdfsDir, entry.name, "config.ts");
44
+ documents.push({
45
+ slug: entry.name,
46
+ configPath: import_node_fs.default.existsSync(configPath) ? configPath : null
47
+ });
48
+ }
49
+ return documents;
50
+ }
34
51
  async function exportPDF(options) {
35
- const { root, output, pageNumbers, pages } = options;
36
- const outputPath = import_node_path.default.resolve(root, output ?? "./output.pdf");
37
- import_node_fs.default.mkdirSync(import_node_path.default.dirname(outputPath), { recursive: true });
52
+ const { root, document: documentSlug, output } = options;
53
+ const outputDir = import_node_path.default.resolve(root, output ?? "output");
54
+ let documents = discoverDocuments(root);
55
+ if (documentSlug) {
56
+ documents = documents.filter((d) => d.slug === documentSlug);
57
+ if (documents.length === 0) {
58
+ throw new Error(
59
+ `Document "${documentSlug}" not found. Check that pdfs/${documentSlug}/pages/ exists.`
60
+ );
61
+ }
62
+ }
63
+ if (documents.length === 0) {
64
+ throw new Error("No documents found. Create at least one document in pdfs/<name>/pages/.");
65
+ }
66
+ import_node_fs.default.mkdirSync(outputDir, { recursive: true });
38
67
  let playwright;
39
68
  try {
40
69
  playwright = await import("playwright");
@@ -44,33 +73,49 @@ async function exportPDF(options) {
44
73
  );
45
74
  }
46
75
  const { startPreview: startPreview2 } = await Promise.resolve().then(() => (init_server(), server_exports));
47
- const server = await startPreview2({ root, port: 0, pages, open: false });
76
+ const server = await startPreview2({ root, port: 0, open: false });
77
+ const vite = server._vite;
48
78
  const browser = await playwright.chromium.launch({ headless: true });
79
+ const outputs = [];
49
80
  try {
50
- const page = await browser.newPage();
51
- await page.goto(server.url, { waitUntil: "networkidle" });
52
- await page.waitForSelector("[data-pdf-smith-page]", { timeout: 3e4 });
53
- const margin = { top: "0", bottom: "0", left: "0", right: "0" };
54
- const pdfOptions = {
55
- path: outputPath,
56
- preferCSSPageSize: true,
57
- printBackground: true,
58
- margin
59
- };
60
- if (pageNumbers?.enabled) {
61
- pdfOptions.displayHeaderFooter = true;
62
- pdfOptions.headerTemplate = "<span></span>";
63
- pdfOptions.footerTemplate = pageNumbers.template ?? '<div style="font-size:10px;text-align:center;width:100%;"><span class="pageNumber"></span> / <span class="totalPages"></span></div>';
64
- margin.bottom = "40px";
81
+ for (const doc of documents) {
82
+ let config;
83
+ if (doc.configPath && vite) {
84
+ try {
85
+ const configModule = await vite.ssrLoadModule(doc.configPath);
86
+ config = configModule.default;
87
+ } catch {
88
+ }
89
+ }
90
+ const filename = config?.output ?? `${doc.slug}.pdf`;
91
+ const outputPath = import_node_path.default.resolve(outputDir, filename);
92
+ const page = await browser.newPage();
93
+ await page.goto(`${server.url}/${doc.slug}`, { waitUntil: "networkidle" });
94
+ await page.waitForSelector("[data-pdf-smith-page]", { timeout: 3e4 });
95
+ const margin = { top: "0", bottom: "0", left: "0", right: "0" };
96
+ const pdfOptions = {
97
+ path: outputPath,
98
+ preferCSSPageSize: true,
99
+ printBackground: true,
100
+ margin
101
+ };
102
+ if (config?.pageNumbers?.enabled) {
103
+ pdfOptions.displayHeaderFooter = true;
104
+ pdfOptions.headerTemplate = "<span></span>";
105
+ pdfOptions.footerTemplate = config.pageNumbers.template ?? '<div style="font-size:10px;text-align:center;width:100%;"><span class="pageNumber"></span> / <span class="totalPages"></span></div>';
106
+ margin.bottom = "40px";
107
+ }
108
+ await page.pdf(pdfOptions);
109
+ await page.close();
110
+ outputs.push({ document: doc.slug, outputPath });
65
111
  }
66
- await page.pdf(pdfOptions);
67
112
  } finally {
68
113
  await browser.close().catch(() => {
69
114
  });
70
115
  await server.close().catch(() => {
71
116
  });
72
117
  }
73
- return { outputPath };
118
+ return { outputs };
74
119
  }
75
120
  var import_node_fs, import_node_path;
76
121
  var init_export = __esm({
@@ -86,13 +131,14 @@ var preview_plugin_exports = {};
86
131
  __export(preview_plugin_exports, {
87
132
  pdfSmithPreviewPlugin: () => pdfSmithPreviewPlugin
88
133
  });
89
- function pdfSmithPreviewPlugin({ pkgSrcDir, pagesGlob }) {
134
+ function pdfSmithPreviewPlugin({ pkgSrcDir }) {
90
135
  return {
91
136
  name: "pdf-smith-preview",
92
137
  configureServer(server) {
93
138
  return () => {
94
139
  server.middlewares.use((req, res, next) => {
95
- if (req.url !== "/" && req.url !== "/index.html") {
140
+ const url = req.url ?? "";
141
+ if (url.includes(".") || url.startsWith("/@") || url.startsWith("/node_modules/")) {
96
142
  next();
97
143
  return;
98
144
  }
@@ -139,36 +185,55 @@ window.__vite_plugin_react_preamble_installed__ = true
139
185
  };
140
186
  },
141
187
  resolveId(id) {
142
- if (id === VIRTUAL_PAGES_ID) {
143
- return RESOLVED_VIRTUAL_PAGES_ID;
188
+ if (id === VIRTUAL_DOCUMENTS_ID) {
189
+ return RESOLVED_VIRTUAL_DOCUMENTS_ID;
144
190
  }
145
191
  },
146
192
  load(id) {
147
- if (id === RESOLVED_VIRTUAL_PAGES_ID) {
193
+ if (id === RESOLVED_VIRTUAL_DOCUMENTS_ID) {
148
194
  return `
149
- const modules = import.meta.glob('${pagesGlob}', { eager: true });
195
+ const pageModules = import.meta.glob('/pdfs/*/pages/**/*.tsx', { eager: true });
196
+ const configModules = import.meta.glob('/pdfs/*/config.ts', { eager: true });
197
+
198
+ export function getDocuments() {
199
+ const documents = {};
150
200
 
151
- export function getPages() {
152
- const pages = {};
153
- for (const [path, mod] of Object.entries(modules)) {
154
- const name = path
155
- .replace(/^\\/pages\\//, '')
156
- .replace(/\\.[^.]+$/, '');
157
- pages[name] = mod.default;
201
+ for (const [path, mod] of Object.entries(pageModules)) {
202
+ const match = path.match(/^\\/pdfs\\/([^/]+)\\/pages\\/(.+)\\.[^.]+$/);
203
+ if (!match) continue;
204
+ const [, slug, pageName] = match;
205
+ if (!documents[slug]) {
206
+ documents[slug] = { slug, pages: {}, config: undefined };
207
+ }
208
+ documents[slug].pages[pageName] = mod.default;
158
209
  }
159
- return pages;
210
+
211
+ for (const [path, mod] of Object.entries(configModules)) {
212
+ const match = path.match(/^\\/pdfs\\/([^/]+)\\/config\\.ts$/);
213
+ if (!match) continue;
214
+ const slug = match[1];
215
+ if (documents[slug]) {
216
+ documents[slug].config = mod.default;
217
+ }
218
+ }
219
+
220
+ return documents;
221
+ }
222
+
223
+ export function getDocumentSlugs() {
224
+ return Object.keys(getDocuments());
160
225
  }
161
226
  `;
162
227
  }
163
228
  }
164
229
  };
165
230
  }
166
- var VIRTUAL_PAGES_ID, RESOLVED_VIRTUAL_PAGES_ID;
231
+ var VIRTUAL_DOCUMENTS_ID, RESOLVED_VIRTUAL_DOCUMENTS_ID;
167
232
  var init_preview_plugin = __esm({
168
233
  "src/preview/preview-plugin.ts"() {
169
234
  "use strict";
170
- VIRTUAL_PAGES_ID = "virtual:pdf-smith-pages";
171
- RESOLVED_VIRTUAL_PAGES_ID = `\0${VIRTUAL_PAGES_ID}`;
235
+ VIRTUAL_DOCUMENTS_ID = "virtual:pdf-smith-documents";
236
+ RESOLVED_VIRTUAL_DOCUMENTS_ID = `\0${VIRTUAL_DOCUMENTS_ID}`;
172
237
  }
173
238
  });
174
239
 
@@ -180,7 +245,7 @@ __export(server_exports, {
180
245
  });
181
246
  module.exports = __toCommonJS(server_exports);
182
247
  async function startPreview(options) {
183
- const { root, port = 3e3, pages = "/pages/**/*.tsx", open = false } = options;
248
+ const { root, port = 3e3, open = false } = options;
184
249
  const { createServer } = await import("vite");
185
250
  const react = (await import("@vitejs/plugin-react")).default;
186
251
  const tailwindcss = (await import("@tailwindcss/vite")).default;
@@ -206,7 +271,7 @@ async function startPreview(options) {
206
271
  },
207
272
  dedupe: ["react", "react-dom"]
208
273
  },
209
- plugins: [react(), tailwindcss(), pdfSmithPreviewPlugin2({ pkgSrcDir, pagesGlob: pages })],
274
+ plugins: [react(), tailwindcss(), pdfSmithPreviewPlugin2({ pkgSrcDir })],
210
275
  optimizeDeps: {
211
276
  include: ["react", "react-dom", "react-dom/client", "react/jsx-runtime"]
212
277
  }
@@ -218,7 +283,7 @@ async function startPreview(options) {
218
283
  const address = server.httpServer?.address();
219
284
  const resolvedPort = address?.port ?? port;
220
285
  const url = `http://localhost:${resolvedPort}`;
221
- return {
286
+ const previewServer = {
222
287
  close: async () => {
223
288
  const httpServer = server.httpServer;
224
289
  httpServer?.closeAllConnections?.();
@@ -227,6 +292,11 @@ async function startPreview(options) {
227
292
  port: resolvedPort,
228
293
  url
229
294
  };
295
+ Object.defineProperty(previewServer, "_vite", {
296
+ value: server,
297
+ enumerable: false
298
+ });
299
+ return previewServer;
230
300
  }
231
301
  var import_node_path2, import_node_url, import_meta;
232
302
  var init_server = __esm({
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/export.ts","../src/preview/preview-plugin.ts","../src/server.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport type { ExportOptions, ExportResult } from './export-types';\n\nexport async function exportPDF(options: ExportOptions): Promise<ExportResult> {\n const { root, output, pageNumbers, pages } = options;\n const outputPath = path.resolve(root, output ?? './output.pdf');\n\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n\n let playwright: typeof import('playwright');\n try {\n playwright = await import('playwright');\n } catch {\n throw new Error(\n 'Playwright is required for PDF export. Install it with: npm install -D playwright && npx playwright install chromium',\n );\n }\n\n const { startPreview } = await import('./server');\n const server = await startPreview({ root, port: 0, pages, open: false });\n\n const browser = await playwright.chromium.launch({ headless: true });\n try {\n const page = await browser.newPage();\n await page.goto(server.url, { waitUntil: 'networkidle' });\n await page.waitForSelector('[data-pdf-smith-page]', { timeout: 30_000 });\n\n const margin = { top: '0', bottom: '0', left: '0', right: '0' };\n\n const pdfOptions: Parameters<typeof page.pdf>[0] = {\n path: outputPath,\n preferCSSPageSize: true,\n printBackground: true,\n margin,\n };\n\n if (pageNumbers?.enabled) {\n pdfOptions.displayHeaderFooter = true;\n pdfOptions.headerTemplate = '<span></span>';\n pdfOptions.footerTemplate =\n pageNumbers.template ??\n '<div style=\"font-size:10px;text-align:center;width:100%;\"><span class=\"pageNumber\"></span> / <span class=\"totalPages\"></span></div>';\n margin.bottom = '40px';\n }\n\n await page.pdf(pdfOptions);\n } finally {\n await browser.close().catch(() => {});\n await server.close().catch(() => {});\n }\n\n return { outputPath };\n}\n","import type { Plugin } from 'vite';\n\nconst VIRTUAL_PAGES_ID = 'virtual:pdf-smith-pages';\nconst RESOLVED_VIRTUAL_PAGES_ID = `\\0${VIRTUAL_PAGES_ID}`;\n\ninterface PreviewPluginOptions {\n pkgSrcDir: string;\n pagesGlob: string;\n}\n\nexport function pdfSmithPreviewPlugin({ pkgSrcDir, pagesGlob }: PreviewPluginOptions): Plugin {\n return {\n name: 'pdf-smith-preview',\n\n configureServer(server) {\n return () => {\n server.middlewares.use((req, res, next) => {\n if (req.url !== '/' && req.url !== '/index.html') {\n next();\n return;\n }\n\n const html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <script type=\"module\">\nimport RefreshRuntime from \"/@react-refresh\"\nRefreshRuntime.injectIntoGlobalHook(window)\nwindow.$RefreshReg$ = () => {}\nwindow.$RefreshSig$ = () => (type) => type\nwindow.__vite_plugin_react_preamble_installed__ = true\n</script>\n <script type=\"module\" src=\"/@vite/client\"></script>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>pdf-smith Preview</title>\n <style>\n @media print {\n [data-pdf-smith-nav] { display: none !important; }\n [data-pdf-smith-container] {\n margin-left: 0 !important;\n padding: 0 !important;\n background: none !important;\n }\n [data-pdf-smith-container] > [data-pdf-smith-document] > div {\n margin-bottom: 0 !important;\n }\n [data-pdf-smith-container] > [data-pdf-smith-document] > div > div:first-child {\n display: none !important;\n }\n }\n </style>\n</head>\n<body>\n <div id=\"root\"></div>\n <script type=\"module\" src=\"/@fs/${pkgSrcDir}/preview/entry.tsx\"></script>\n</body>\n</html>`;\n\n res.setHeader('Content-Type', 'text/html');\n res.statusCode = 200;\n res.end(html);\n });\n };\n },\n\n resolveId(id) {\n if (id === VIRTUAL_PAGES_ID) {\n return RESOLVED_VIRTUAL_PAGES_ID;\n }\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_PAGES_ID) {\n return `\nconst modules = import.meta.glob('${pagesGlob}', { eager: true });\n\nexport function getPages() {\n const pages = {};\n for (const [path, mod] of Object.entries(modules)) {\n const name = path\n .replace(/^\\\\/pages\\\\//, '')\n .replace(/\\\\.[^.]+$/, '');\n pages[name] = mod.default;\n }\n return pages;\n}\n`;\n }\n },\n };\n}\n","import type { AddressInfo } from 'node:net';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { PreviewOptions, PreviewServer } from './preview/types';\n\nexport { exportPDF } from './export';\nexport type { ExportOptions, ExportResult } from './export-types';\nexport type { PreviewOptions, PreviewServer } from './preview/types';\n\nexport async function startPreview(options: PreviewOptions): Promise<PreviewServer> {\n const { root, port = 3000, pages = '/pages/**/*.tsx', open = false } = options;\n\n const { createServer } = await import('vite');\n const react = (await import('@vitejs/plugin-react')).default;\n const tailwindcss = (await import('@tailwindcss/vite')).default;\n const { pdfSmithPreviewPlugin } = await import('./preview/preview-plugin');\n\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n const pkgRoot = path.resolve(currentDir, '..');\n const pkgSrcDir = path.resolve(pkgRoot, 'src');\n\n const server = await createServer({\n configFile: false,\n appType: 'custom',\n root,\n server: {\n port,\n strictPort: port !== 0,\n open,\n fs: {\n allow: [root, pkgSrcDir, pkgRoot],\n },\n },\n resolve: {\n alias: {\n 'pdf-smith': path.resolve(pkgSrcDir, 'index.ts'),\n },\n dedupe: ['react', 'react-dom'],\n },\n plugins: [react(), tailwindcss(), pdfSmithPreviewPlugin({ pkgSrcDir, pagesGlob: pages })],\n optimizeDeps: {\n include: ['react', 'react-dom', 'react-dom/client', 'react/jsx-runtime'],\n },\n });\n\n await server.listen();\n\n if (port !== 0) {\n server.printUrls();\n }\n\n const address = server.httpServer?.address() as AddressInfo | null;\n const resolvedPort = address?.port ?? port;\n const url = `http://localhost:${resolvedPort}`;\n\n return {\n close: async () => {\n const httpServer = server.httpServer as { closeAllConnections?: () => void } | null;\n httpServer?.closeAllConnections?.();\n await server.close();\n },\n port: resolvedPort,\n url,\n };\n}\n"],"mappings":"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","names":["path","fs","startPreview","pdfSmithPreviewPlugin","path","import_node_path"]}
1
+ {"version":3,"sources":["../src/export.ts","../src/preview/preview-plugin.ts","../src/server.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport type { DocumentConfig } from './config';\nimport type { ExportOptions, ExportResult } from './export-types';\n\ninterface DiscoveredDocument {\n slug: string;\n configPath: string | null;\n}\n\nfunction discoverDocuments(root: string): DiscoveredDocument[] {\n const pdfsDir = path.join(root, 'pdfs');\n if (!fs.existsSync(pdfsDir)) return [];\n\n const entries = fs.readdirSync(pdfsDir, { withFileTypes: true });\n const documents: DiscoveredDocument[] = [];\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const pagesDir = path.join(pdfsDir, entry.name, 'pages');\n if (!fs.existsSync(pagesDir)) continue;\n\n const configPath = path.join(pdfsDir, entry.name, 'config.ts');\n documents.push({\n slug: entry.name,\n configPath: fs.existsSync(configPath) ? configPath : null,\n });\n }\n\n return documents;\n}\n\nexport async function exportPDF(options: ExportOptions): Promise<ExportResult> {\n const { root, document: documentSlug, output } = options;\n const outputDir = path.resolve(root, output ?? 'output');\n\n let documents = discoverDocuments(root);\n\n if (documentSlug) {\n documents = documents.filter((d) => d.slug === documentSlug);\n if (documents.length === 0) {\n throw new Error(\n `Document \"${documentSlug}\" not found. Check that pdfs/${documentSlug}/pages/ exists.`,\n );\n }\n }\n\n if (documents.length === 0) {\n throw new Error('No documents found. Create at least one document in pdfs/<name>/pages/.');\n }\n\n fs.mkdirSync(outputDir, { recursive: true });\n\n let playwright: typeof import('playwright');\n try {\n playwright = await import('playwright');\n } catch {\n throw new Error(\n 'Playwright is required for PDF export. Install it with: npm install -D playwright && npx playwright install chromium',\n );\n }\n\n const { startPreview } = await import('./server');\n const server = await startPreview({ root, port: 0, open: false });\n\n const vite = (\n server as unknown as {\n _vite: { ssrLoadModule: (id: string) => Promise<{ default?: DocumentConfig }> };\n }\n )._vite;\n\n const browser = await playwright.chromium.launch({ headless: true });\n const outputs: ExportResult['outputs'] = [];\n\n try {\n for (const doc of documents) {\n let config: DocumentConfig | undefined;\n if (doc.configPath && vite) {\n try {\n const configModule = await vite.ssrLoadModule(doc.configPath);\n config = configModule.default;\n } catch {\n // Config loading failed, proceed without it\n }\n }\n\n const filename = config?.output ?? `${doc.slug}.pdf`;\n const outputPath = path.resolve(outputDir, filename);\n\n const page = await browser.newPage();\n await page.goto(`${server.url}/${doc.slug}`, { waitUntil: 'networkidle' });\n await page.waitForSelector('[data-pdf-smith-page]', { timeout: 30_000 });\n\n const margin = { top: '0', bottom: '0', left: '0', right: '0' };\n\n const pdfOptions: Parameters<typeof page.pdf>[0] = {\n path: outputPath,\n preferCSSPageSize: true,\n printBackground: true,\n margin,\n };\n\n if (config?.pageNumbers?.enabled) {\n pdfOptions.displayHeaderFooter = true;\n pdfOptions.headerTemplate = '<span></span>';\n pdfOptions.footerTemplate =\n config.pageNumbers.template ??\n '<div style=\"font-size:10px;text-align:center;width:100%;\"><span class=\"pageNumber\"></span> / <span class=\"totalPages\"></span></div>';\n margin.bottom = '40px';\n }\n\n await page.pdf(pdfOptions);\n await page.close();\n\n outputs.push({ document: doc.slug, outputPath });\n }\n } finally {\n await browser.close().catch(() => {});\n await server.close().catch(() => {});\n }\n\n return { outputs };\n}\n","import type { Plugin } from 'vite';\n\nconst VIRTUAL_DOCUMENTS_ID = 'virtual:pdf-smith-documents';\nconst RESOLVED_VIRTUAL_DOCUMENTS_ID = `\\0${VIRTUAL_DOCUMENTS_ID}`;\n\ninterface PreviewPluginOptions {\n pkgSrcDir: string;\n}\n\nexport function pdfSmithPreviewPlugin({ pkgSrcDir }: PreviewPluginOptions): Plugin {\n return {\n name: 'pdf-smith-preview',\n\n configureServer(server) {\n return () => {\n server.middlewares.use((req, res, next) => {\n const url = req.url ?? '';\n\n // Skip file requests, Vite internals, and node_modules\n if (url.includes('.') || url.startsWith('/@') || url.startsWith('/node_modules/')) {\n next();\n return;\n }\n\n const html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <script type=\"module\">\nimport RefreshRuntime from \"/@react-refresh\"\nRefreshRuntime.injectIntoGlobalHook(window)\nwindow.$RefreshReg$ = () => {}\nwindow.$RefreshSig$ = () => (type) => type\nwindow.__vite_plugin_react_preamble_installed__ = true\n</script>\n <script type=\"module\" src=\"/@vite/client\"></script>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>pdf-smith Preview</title>\n <style>\n @media print {\n [data-pdf-smith-nav] { display: none !important; }\n [data-pdf-smith-container] {\n margin-left: 0 !important;\n padding: 0 !important;\n background: none !important;\n }\n [data-pdf-smith-container] > [data-pdf-smith-document] > div {\n margin-bottom: 0 !important;\n }\n [data-pdf-smith-container] > [data-pdf-smith-document] > div > div:first-child {\n display: none !important;\n }\n }\n </style>\n</head>\n<body>\n <div id=\"root\"></div>\n <script type=\"module\" src=\"/@fs/${pkgSrcDir}/preview/entry.tsx\"></script>\n</body>\n</html>`;\n\n res.setHeader('Content-Type', 'text/html');\n res.statusCode = 200;\n res.end(html);\n });\n };\n },\n\n resolveId(id) {\n if (id === VIRTUAL_DOCUMENTS_ID) {\n return RESOLVED_VIRTUAL_DOCUMENTS_ID;\n }\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_DOCUMENTS_ID) {\n return `\nconst pageModules = import.meta.glob('/pdfs/*/pages/**/*.tsx', { eager: true });\nconst configModules = import.meta.glob('/pdfs/*/config.ts', { eager: true });\n\nexport function getDocuments() {\n const documents = {};\n\n for (const [path, mod] of Object.entries(pageModules)) {\n const match = path.match(/^\\\\/pdfs\\\\/([^/]+)\\\\/pages\\\\/(.+)\\\\.[^.]+$/);\n if (!match) continue;\n const [, slug, pageName] = match;\n if (!documents[slug]) {\n documents[slug] = { slug, pages: {}, config: undefined };\n }\n documents[slug].pages[pageName] = mod.default;\n }\n\n for (const [path, mod] of Object.entries(configModules)) {\n const match = path.match(/^\\\\/pdfs\\\\/([^/]+)\\\\/config\\\\.ts$/);\n if (!match) continue;\n const slug = match[1];\n if (documents[slug]) {\n documents[slug].config = mod.default;\n }\n }\n\n return documents;\n}\n\nexport function getDocumentSlugs() {\n return Object.keys(getDocuments());\n}\n`;\n }\n },\n };\n}\n","import type { AddressInfo } from 'node:net';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { PreviewOptions, PreviewServer } from './preview/types';\n\nexport { exportPDF } from './export';\nexport type { ExportOptions, ExportResult } from './export-types';\nexport type { PreviewOptions, PreviewServer } from './preview/types';\n\nexport async function startPreview(options: PreviewOptions): Promise<PreviewServer> {\n const { root, port = 3000, open = false } = options;\n\n const { createServer } = await import('vite');\n const react = (await import('@vitejs/plugin-react')).default;\n const tailwindcss = (await import('@tailwindcss/vite')).default;\n const { pdfSmithPreviewPlugin } = await import('./preview/preview-plugin');\n\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n const pkgRoot = path.resolve(currentDir, '..');\n const pkgSrcDir = path.resolve(pkgRoot, 'src');\n\n const server = await createServer({\n configFile: false,\n appType: 'custom',\n root,\n server: {\n port,\n strictPort: port !== 0,\n open,\n fs: {\n allow: [root, pkgSrcDir, pkgRoot],\n },\n },\n resolve: {\n alias: {\n 'pdf-smith': path.resolve(pkgSrcDir, 'index.ts'),\n },\n dedupe: ['react', 'react-dom'],\n },\n plugins: [react(), tailwindcss(), pdfSmithPreviewPlugin({ pkgSrcDir })],\n optimizeDeps: {\n include: ['react', 'react-dom', 'react-dom/client', 'react/jsx-runtime'],\n },\n });\n\n await server.listen();\n\n if (port !== 0) {\n server.printUrls();\n }\n\n const address = server.httpServer?.address() as AddressInfo | null;\n const resolvedPort = address?.port ?? port;\n const url = `http://localhost:${resolvedPort}`;\n\n const previewServer: PreviewServer = {\n close: async () => {\n const httpServer = server.httpServer as { closeAllConnections?: () => void } | null;\n httpServer?.closeAllConnections?.();\n await server.close();\n },\n port: resolvedPort,\n url,\n };\n\n Object.defineProperty(previewServer, '_vite', {\n value: server,\n enumerable: false,\n });\n\n return previewServer;\n}\n"],"mappings":"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","names":["path","fs","startPreview","pdfSmithPreviewPlugin","path","import_node_path"]}
package/dist/server.d.cts CHANGED
@@ -3,7 +3,6 @@ interface PreviewOptions {
3
3
  root: string;
4
4
  /** Dev server port (default: 3000) */
5
5
  port?: number;
6
- pages?: string;
7
6
  /** Auto-open browser (default: false) */
8
7
  open?: boolean;
9
8
  }
@@ -15,15 +14,14 @@ interface PreviewServer {
15
14
 
16
15
  interface ExportOptions {
17
16
  root: string;
17
+ document?: string;
18
18
  output?: string;
19
- pageNumbers?: {
20
- enabled: boolean;
21
- template?: string;
22
- };
23
- pages?: string;
24
19
  }
25
20
  interface ExportResult {
26
- outputPath: string;
21
+ outputs: Array<{
22
+ document: string;
23
+ outputPath: string;
24
+ }>;
27
25
  }
28
26
 
29
27
  declare function exportPDF(options: ExportOptions): Promise<ExportResult>;