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 +72 -0
- package/dist/index.cjs +9 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/{preview-plugin-QWJEXDW2.js → preview-plugin-QDLEMAEE.js} +37 -17
- package/dist/preview-plugin-QDLEMAEE.js.map +1 -0
- package/dist/server.cjs +111 -41
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +5 -7
- package/dist/server.d.ts +5 -7
- package/dist/server.js +75 -25
- package/dist/server.js.map +1 -1
- package/package.json +9 -2
- package/src/add.ts +72 -0
- package/src/cli.ts +24 -0
- package/src/config.ts +8 -0
- package/src/index.ts +2 -0
- package/src/preview/app.tsx +15 -4
- package/src/preview/entry.tsx +7 -3
- package/src/preview/home.tsx +95 -0
- package/src/preview/navigation.tsx +24 -2
- package/src/preview/preview-plugin.ts +38 -17
- package/src/preview/types.ts +0 -2
- package/src/preview/vite-env.d.ts +8 -2
- package/dist/preview-plugin-QWJEXDW2.js.map +0 -1
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
|
package/dist/index.cjs.map
CHANGED
|
@@ -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;;;
|
|
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":";
|
|
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
|
|
3
|
-
var
|
|
4
|
-
function pdfSmithPreviewPlugin({ pkgSrcDir
|
|
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
|
-
|
|
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 ===
|
|
58
|
-
return
|
|
58
|
+
if (id === VIRTUAL_DOCUMENTS_ID) {
|
|
59
|
+
return RESOLVED_VIRTUAL_DOCUMENTS_ID;
|
|
59
60
|
}
|
|
60
61
|
},
|
|
61
62
|
load(id) {
|
|
62
|
-
if (id ===
|
|
63
|
+
if (id === RESOLVED_VIRTUAL_DOCUMENTS_ID) {
|
|
63
64
|
return `
|
|
64
|
-
const
|
|
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
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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-
|
|
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,
|
|
36
|
-
const
|
|
37
|
-
|
|
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,
|
|
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
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
margin
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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 ===
|
|
143
|
-
return
|
|
188
|
+
if (id === VIRTUAL_DOCUMENTS_ID) {
|
|
189
|
+
return RESOLVED_VIRTUAL_DOCUMENTS_ID;
|
|
144
190
|
}
|
|
145
191
|
},
|
|
146
192
|
load(id) {
|
|
147
|
-
if (id ===
|
|
193
|
+
if (id === RESOLVED_VIRTUAL_DOCUMENTS_ID) {
|
|
148
194
|
return `
|
|
149
|
-
const
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
171
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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({
|
package/dist/server.cjs.map
CHANGED
|
@@ -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
|
-
|
|
21
|
+
outputs: Array<{
|
|
22
|
+
document: string;
|
|
23
|
+
outputPath: string;
|
|
24
|
+
}>;
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
declare function exportPDF(options: ExportOptions): Promise<ExportResult>;
|