pdf-smith 0.1.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/LICENSE +21 -0
- package/dist/index.cjs +146 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +33 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +115 -0
- package/dist/index.js.map +1 -0
- package/dist/preview-plugin-QWJEXDW2.js +84 -0
- package/dist/preview-plugin-QWJEXDW2.js.map +1 -0
- package/dist/server.cjs +246 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +33 -0
- package/dist/server.d.ts +33 -0
- package/dist/server.js +104 -0
- package/dist/server.js.map +1 -0
- package/package.json +91 -0
- package/src/constants.ts +11 -0
- package/src/document.tsx +15 -0
- package/src/index.ts +11 -0
- package/src/page.tsx +35 -0
- package/src/preview/app.tsx +52 -0
- package/src/preview/entry.tsx +7 -0
- package/src/preview/navigation.tsx +98 -0
- package/src/preview/preview-plugin.ts +92 -0
- package/src/preview/types.ts +16 -0
- package/src/preview/vite-env.d.ts +3 -0
- package/src/styles.ts +69 -0
- package/src/types.ts +23 -0
package/dist/server.cjs
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
31
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
|
+
|
|
33
|
+
// src/export.ts
|
|
34
|
+
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 });
|
|
38
|
+
let playwright;
|
|
39
|
+
try {
|
|
40
|
+
playwright = await import("playwright");
|
|
41
|
+
} catch {
|
|
42
|
+
throw new Error(
|
|
43
|
+
"Playwright is required for PDF export. Install it with: npm install -D playwright && npx playwright install chromium"
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
const { startPreview: startPreview2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
47
|
+
const server = await startPreview2({ root, port: 0, pages, open: false });
|
|
48
|
+
const browser = await playwright.chromium.launch({ headless: true });
|
|
49
|
+
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";
|
|
65
|
+
}
|
|
66
|
+
await page.pdf(pdfOptions);
|
|
67
|
+
} finally {
|
|
68
|
+
await browser.close().catch(() => {
|
|
69
|
+
});
|
|
70
|
+
await server.close().catch(() => {
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return { outputPath };
|
|
74
|
+
}
|
|
75
|
+
var import_node_fs, import_node_path;
|
|
76
|
+
var init_export = __esm({
|
|
77
|
+
"src/export.ts"() {
|
|
78
|
+
"use strict";
|
|
79
|
+
import_node_fs = __toESM(require("fs"), 1);
|
|
80
|
+
import_node_path = __toESM(require("path"), 1);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// src/preview/preview-plugin.ts
|
|
85
|
+
var preview_plugin_exports = {};
|
|
86
|
+
__export(preview_plugin_exports, {
|
|
87
|
+
pdfSmithPreviewPlugin: () => pdfSmithPreviewPlugin
|
|
88
|
+
});
|
|
89
|
+
function pdfSmithPreviewPlugin({ pkgSrcDir, pagesGlob }) {
|
|
90
|
+
return {
|
|
91
|
+
name: "pdf-smith-preview",
|
|
92
|
+
configureServer(server) {
|
|
93
|
+
return () => {
|
|
94
|
+
server.middlewares.use((req, res, next) => {
|
|
95
|
+
if (req.url !== "/" && req.url !== "/index.html") {
|
|
96
|
+
next();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const html = `<!DOCTYPE html>
|
|
100
|
+
<html lang="en">
|
|
101
|
+
<head>
|
|
102
|
+
<script type="module">
|
|
103
|
+
import RefreshRuntime from "/@react-refresh"
|
|
104
|
+
RefreshRuntime.injectIntoGlobalHook(window)
|
|
105
|
+
window.$RefreshReg$ = () => {}
|
|
106
|
+
window.$RefreshSig$ = () => (type) => type
|
|
107
|
+
window.__vite_plugin_react_preamble_installed__ = true
|
|
108
|
+
</script>
|
|
109
|
+
<script type="module" src="/@vite/client"></script>
|
|
110
|
+
<meta charset="UTF-8" />
|
|
111
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
112
|
+
<title>pdf-smith Preview</title>
|
|
113
|
+
<style>
|
|
114
|
+
@media print {
|
|
115
|
+
[data-pdf-smith-nav] { display: none !important; }
|
|
116
|
+
[data-pdf-smith-container] {
|
|
117
|
+
margin-left: 0 !important;
|
|
118
|
+
padding: 0 !important;
|
|
119
|
+
background: none !important;
|
|
120
|
+
}
|
|
121
|
+
[data-pdf-smith-container] > [data-pdf-smith-document] > div {
|
|
122
|
+
margin-bottom: 0 !important;
|
|
123
|
+
}
|
|
124
|
+
[data-pdf-smith-container] > [data-pdf-smith-document] > div > div:first-child {
|
|
125
|
+
display: none !important;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
</style>
|
|
129
|
+
</head>
|
|
130
|
+
<body>
|
|
131
|
+
<div id="root"></div>
|
|
132
|
+
<script type="module" src="/@fs/${pkgSrcDir}/preview/entry.tsx"></script>
|
|
133
|
+
</body>
|
|
134
|
+
</html>`;
|
|
135
|
+
res.setHeader("Content-Type", "text/html");
|
|
136
|
+
res.statusCode = 200;
|
|
137
|
+
res.end(html);
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
resolveId(id) {
|
|
142
|
+
if (id === VIRTUAL_PAGES_ID) {
|
|
143
|
+
return RESOLVED_VIRTUAL_PAGES_ID;
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
load(id) {
|
|
147
|
+
if (id === RESOLVED_VIRTUAL_PAGES_ID) {
|
|
148
|
+
return `
|
|
149
|
+
const modules = import.meta.glob('${pagesGlob}', { eager: true });
|
|
150
|
+
|
|
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;
|
|
158
|
+
}
|
|
159
|
+
return pages;
|
|
160
|
+
}
|
|
161
|
+
`;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
var VIRTUAL_PAGES_ID, RESOLVED_VIRTUAL_PAGES_ID;
|
|
167
|
+
var init_preview_plugin = __esm({
|
|
168
|
+
"src/preview/preview-plugin.ts"() {
|
|
169
|
+
"use strict";
|
|
170
|
+
VIRTUAL_PAGES_ID = "virtual:pdf-smith-pages";
|
|
171
|
+
RESOLVED_VIRTUAL_PAGES_ID = `\0${VIRTUAL_PAGES_ID}`;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// src/server.ts
|
|
176
|
+
var server_exports = {};
|
|
177
|
+
__export(server_exports, {
|
|
178
|
+
exportPDF: () => exportPDF,
|
|
179
|
+
startPreview: () => startPreview
|
|
180
|
+
});
|
|
181
|
+
module.exports = __toCommonJS(server_exports);
|
|
182
|
+
async function startPreview(options) {
|
|
183
|
+
const { root, port = 3e3, pages = "/pages/**/*.tsx", open = false } = options;
|
|
184
|
+
const { createServer } = await import("vite");
|
|
185
|
+
const react = (await import("@vitejs/plugin-react")).default;
|
|
186
|
+
const tailwindcss = (await import("@tailwindcss/vite")).default;
|
|
187
|
+
const { pdfSmithPreviewPlugin: pdfSmithPreviewPlugin2 } = await Promise.resolve().then(() => (init_preview_plugin(), preview_plugin_exports));
|
|
188
|
+
const currentDir = import_node_path2.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
|
|
189
|
+
const pkgRoot = import_node_path2.default.resolve(currentDir, "..");
|
|
190
|
+
const pkgSrcDir = import_node_path2.default.resolve(pkgRoot, "src");
|
|
191
|
+
const server = await createServer({
|
|
192
|
+
configFile: false,
|
|
193
|
+
appType: "custom",
|
|
194
|
+
root,
|
|
195
|
+
server: {
|
|
196
|
+
port,
|
|
197
|
+
strictPort: port !== 0,
|
|
198
|
+
open,
|
|
199
|
+
fs: {
|
|
200
|
+
allow: [root, pkgSrcDir, pkgRoot]
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
resolve: {
|
|
204
|
+
alias: {
|
|
205
|
+
"pdf-smith": import_node_path2.default.resolve(pkgSrcDir, "index.ts")
|
|
206
|
+
},
|
|
207
|
+
dedupe: ["react", "react-dom"]
|
|
208
|
+
},
|
|
209
|
+
plugins: [react(), tailwindcss(), pdfSmithPreviewPlugin2({ pkgSrcDir, pagesGlob: pages })],
|
|
210
|
+
optimizeDeps: {
|
|
211
|
+
include: ["react", "react-dom", "react-dom/client", "react/jsx-runtime"]
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
await server.listen();
|
|
215
|
+
if (port !== 0) {
|
|
216
|
+
server.printUrls();
|
|
217
|
+
}
|
|
218
|
+
const address = server.httpServer?.address();
|
|
219
|
+
const resolvedPort = address?.port ?? port;
|
|
220
|
+
const url = `http://localhost:${resolvedPort}`;
|
|
221
|
+
return {
|
|
222
|
+
close: async () => {
|
|
223
|
+
const httpServer = server.httpServer;
|
|
224
|
+
httpServer?.closeAllConnections?.();
|
|
225
|
+
await server.close();
|
|
226
|
+
},
|
|
227
|
+
port: resolvedPort,
|
|
228
|
+
url
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
var import_node_path2, import_node_url, import_meta;
|
|
232
|
+
var init_server = __esm({
|
|
233
|
+
"src/server.ts"() {
|
|
234
|
+
import_node_path2 = __toESM(require("path"), 1);
|
|
235
|
+
import_node_url = require("url");
|
|
236
|
+
init_export();
|
|
237
|
+
import_meta = {};
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
init_server();
|
|
241
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
242
|
+
0 && (module.exports = {
|
|
243
|
+
exportPDF,
|
|
244
|
+
startPreview
|
|
245
|
+
});
|
|
246
|
+
//# sourceMappingURL=server.cjs.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
interface PreviewOptions {
|
|
2
|
+
/** User's project root (absolute path) */
|
|
3
|
+
root: string;
|
|
4
|
+
/** Dev server port (default: 3000) */
|
|
5
|
+
port?: number;
|
|
6
|
+
pages?: string;
|
|
7
|
+
/** Auto-open browser (default: false) */
|
|
8
|
+
open?: boolean;
|
|
9
|
+
}
|
|
10
|
+
interface PreviewServer {
|
|
11
|
+
close(): Promise<void>;
|
|
12
|
+
port: number;
|
|
13
|
+
url: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ExportOptions {
|
|
17
|
+
root: string;
|
|
18
|
+
output?: string;
|
|
19
|
+
pageNumbers?: {
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
template?: string;
|
|
22
|
+
};
|
|
23
|
+
pages?: string;
|
|
24
|
+
}
|
|
25
|
+
interface ExportResult {
|
|
26
|
+
outputPath: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
declare function exportPDF(options: ExportOptions): Promise<ExportResult>;
|
|
30
|
+
|
|
31
|
+
declare function startPreview(options: PreviewOptions): Promise<PreviewServer>;
|
|
32
|
+
|
|
33
|
+
export { type ExportOptions, type ExportResult, type PreviewOptions, type PreviewServer, exportPDF, startPreview };
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
interface PreviewOptions {
|
|
2
|
+
/** User's project root (absolute path) */
|
|
3
|
+
root: string;
|
|
4
|
+
/** Dev server port (default: 3000) */
|
|
5
|
+
port?: number;
|
|
6
|
+
pages?: string;
|
|
7
|
+
/** Auto-open browser (default: false) */
|
|
8
|
+
open?: boolean;
|
|
9
|
+
}
|
|
10
|
+
interface PreviewServer {
|
|
11
|
+
close(): Promise<void>;
|
|
12
|
+
port: number;
|
|
13
|
+
url: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ExportOptions {
|
|
17
|
+
root: string;
|
|
18
|
+
output?: string;
|
|
19
|
+
pageNumbers?: {
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
template?: string;
|
|
22
|
+
};
|
|
23
|
+
pages?: string;
|
|
24
|
+
}
|
|
25
|
+
interface ExportResult {
|
|
26
|
+
outputPath: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
declare function exportPDF(options: ExportOptions): Promise<ExportResult>;
|
|
30
|
+
|
|
31
|
+
declare function startPreview(options: PreviewOptions): Promise<PreviewServer>;
|
|
32
|
+
|
|
33
|
+
export { type ExportOptions, type ExportResult, type PreviewOptions, type PreviewServer, exportPDF, startPreview };
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// src/server.ts
|
|
2
|
+
import path2 from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
|
|
5
|
+
// src/export.ts
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
async function exportPDF(options) {
|
|
9
|
+
const { root, output, pageNumbers, pages } = options;
|
|
10
|
+
const outputPath = path.resolve(root, output ?? "./output.pdf");
|
|
11
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
12
|
+
let playwright;
|
|
13
|
+
try {
|
|
14
|
+
playwright = await import("playwright");
|
|
15
|
+
} catch {
|
|
16
|
+
throw new Error(
|
|
17
|
+
"Playwright is required for PDF export. Install it with: npm install -D playwright && npx playwright install chromium"
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
const { startPreview: startPreview2 } = await import("./server.js");
|
|
21
|
+
const server = await startPreview2({ root, port: 0, pages, open: false });
|
|
22
|
+
const browser = await playwright.chromium.launch({ headless: true });
|
|
23
|
+
try {
|
|
24
|
+
const page = await browser.newPage();
|
|
25
|
+
await page.goto(server.url, { waitUntil: "networkidle" });
|
|
26
|
+
await page.waitForSelector("[data-pdf-smith-page]", { timeout: 3e4 });
|
|
27
|
+
const margin = { top: "0", bottom: "0", left: "0", right: "0" };
|
|
28
|
+
const pdfOptions = {
|
|
29
|
+
path: outputPath,
|
|
30
|
+
preferCSSPageSize: true,
|
|
31
|
+
printBackground: true,
|
|
32
|
+
margin
|
|
33
|
+
};
|
|
34
|
+
if (pageNumbers?.enabled) {
|
|
35
|
+
pdfOptions.displayHeaderFooter = true;
|
|
36
|
+
pdfOptions.headerTemplate = "<span></span>";
|
|
37
|
+
pdfOptions.footerTemplate = pageNumbers.template ?? '<div style="font-size:10px;text-align:center;width:100%;"><span class="pageNumber"></span> / <span class="totalPages"></span></div>';
|
|
38
|
+
margin.bottom = "40px";
|
|
39
|
+
}
|
|
40
|
+
await page.pdf(pdfOptions);
|
|
41
|
+
} finally {
|
|
42
|
+
await browser.close().catch(() => {
|
|
43
|
+
});
|
|
44
|
+
await server.close().catch(() => {
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return { outputPath };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/server.ts
|
|
51
|
+
async function startPreview(options) {
|
|
52
|
+
const { root, port = 3e3, pages = "/pages/**/*.tsx", open = false } = options;
|
|
53
|
+
const { createServer } = await import("vite");
|
|
54
|
+
const react = (await import("@vitejs/plugin-react")).default;
|
|
55
|
+
const tailwindcss = (await import("@tailwindcss/vite")).default;
|
|
56
|
+
const { pdfSmithPreviewPlugin } = await import("./preview-plugin-QWJEXDW2.js");
|
|
57
|
+
const currentDir = path2.dirname(fileURLToPath(import.meta.url));
|
|
58
|
+
const pkgRoot = path2.resolve(currentDir, "..");
|
|
59
|
+
const pkgSrcDir = path2.resolve(pkgRoot, "src");
|
|
60
|
+
const server = await createServer({
|
|
61
|
+
configFile: false,
|
|
62
|
+
appType: "custom",
|
|
63
|
+
root,
|
|
64
|
+
server: {
|
|
65
|
+
port,
|
|
66
|
+
strictPort: port !== 0,
|
|
67
|
+
open,
|
|
68
|
+
fs: {
|
|
69
|
+
allow: [root, pkgSrcDir, pkgRoot]
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
resolve: {
|
|
73
|
+
alias: {
|
|
74
|
+
"pdf-smith": path2.resolve(pkgSrcDir, "index.ts")
|
|
75
|
+
},
|
|
76
|
+
dedupe: ["react", "react-dom"]
|
|
77
|
+
},
|
|
78
|
+
plugins: [react(), tailwindcss(), pdfSmithPreviewPlugin({ pkgSrcDir, pagesGlob: pages })],
|
|
79
|
+
optimizeDeps: {
|
|
80
|
+
include: ["react", "react-dom", "react-dom/client", "react/jsx-runtime"]
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
await server.listen();
|
|
84
|
+
if (port !== 0) {
|
|
85
|
+
server.printUrls();
|
|
86
|
+
}
|
|
87
|
+
const address = server.httpServer?.address();
|
|
88
|
+
const resolvedPort = address?.port ?? port;
|
|
89
|
+
const url = `http://localhost:${resolvedPort}`;
|
|
90
|
+
return {
|
|
91
|
+
close: async () => {
|
|
92
|
+
const httpServer = server.httpServer;
|
|
93
|
+
httpServer?.closeAllConnections?.();
|
|
94
|
+
await server.close();
|
|
95
|
+
},
|
|
96
|
+
port: resolvedPort,
|
|
97
|
+
url
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export {
|
|
101
|
+
exportPDF,
|
|
102
|
+
startPreview
|
|
103
|
+
};
|
|
104
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../src/export.ts"],"sourcesContent":["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","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"],"mappings":"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","names":["path","startPreview","path"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pdf-smith",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Build beautiful PDFs with React components and Tailwind CSS",
|
|
5
|
+
"author": "Kareem Elbahrawy",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/kareemaly/pdf-smith.git",
|
|
10
|
+
"directory": "packages/pdf-smith"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/kareemaly/pdf-smith",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"pdf",
|
|
15
|
+
"react",
|
|
16
|
+
"tailwind",
|
|
17
|
+
"vite",
|
|
18
|
+
"playwright",
|
|
19
|
+
"pdf-generation",
|
|
20
|
+
"react-pdf"
|
|
21
|
+
],
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "./dist/index.cjs",
|
|
24
|
+
"module": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"import": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"default": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"require": {
|
|
33
|
+
"types": "./dist/index.d.cts",
|
|
34
|
+
"default": "./dist/index.cjs"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"./server": {
|
|
38
|
+
"import": {
|
|
39
|
+
"types": "./dist/server.d.ts",
|
|
40
|
+
"default": "./dist/server.js"
|
|
41
|
+
},
|
|
42
|
+
"require": {
|
|
43
|
+
"types": "./dist/server.d.cts",
|
|
44
|
+
"default": "./dist/server.cjs"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist",
|
|
50
|
+
"src/preview/",
|
|
51
|
+
"src/index.ts",
|
|
52
|
+
"src/page.tsx",
|
|
53
|
+
"src/document.tsx",
|
|
54
|
+
"src/types.ts",
|
|
55
|
+
"src/constants.ts",
|
|
56
|
+
"src/styles.ts"
|
|
57
|
+
],
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"vite": "^7.0.0",
|
|
60
|
+
"@vitejs/plugin-react": "^5.0.0",
|
|
61
|
+
"@tailwindcss/vite": "^4.0.0"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@testing-library/react": "^16.3.2",
|
|
65
|
+
"@types/react": "^19.0.0",
|
|
66
|
+
"@types/react-dom": "^19.2.3",
|
|
67
|
+
"react": "^19.0.0",
|
|
68
|
+
"react-dom": "^19.2.4",
|
|
69
|
+
"tsup": "^8.5.1"
|
|
70
|
+
},
|
|
71
|
+
"peerDependencies": {
|
|
72
|
+
"react": ">=18",
|
|
73
|
+
"react-dom": ">=18",
|
|
74
|
+
"playwright": ">=1.40.0"
|
|
75
|
+
},
|
|
76
|
+
"peerDependenciesMeta": {
|
|
77
|
+
"react": {
|
|
78
|
+
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"react-dom": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"playwright": {
|
|
84
|
+
"optional": true
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"scripts": {
|
|
88
|
+
"build": "tsup",
|
|
89
|
+
"typecheck": "tsc --noEmit"
|
|
90
|
+
}
|
|
91
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PageDimensions, PageSizePreset } from './types';
|
|
2
|
+
|
|
3
|
+
export const PAGE_SIZES: Record<PageSizePreset, PageDimensions> = {
|
|
4
|
+
A4: { width: 210, height: 297 },
|
|
5
|
+
Letter: { width: 215.9, height: 279.4 },
|
|
6
|
+
A3: { width: 297, height: 420 },
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_PADDING = 0;
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_PAGE_SIZE: PageSizePreset = 'A4';
|
package/src/document.tsx
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PAGE_SIZES } from './constants';
|
|
2
|
+
import { getDocumentStyleSheet } from './styles';
|
|
3
|
+
import type { DocumentProps } from './types';
|
|
4
|
+
|
|
5
|
+
export function Document({ children, className }: DocumentProps): React.ReactElement {
|
|
6
|
+
const styleSheet = getDocumentStyleSheet(PAGE_SIZES.A4);
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<div data-pdf-smith-document="" className={className}>
|
|
10
|
+
{/* biome-ignore lint/security/noDangerouslySetInnerHtml: trusted internal CSS */}
|
|
11
|
+
<style dangerouslySetInnerHTML={{ __html: styleSheet }} />
|
|
12
|
+
{children}
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { DEFAULT_PADDING, DEFAULT_PAGE_SIZE, PAGE_SIZES } from './constants';
|
|
2
|
+
export { Document } from './document';
|
|
3
|
+
export { Page } from './page';
|
|
4
|
+
export type {
|
|
5
|
+
DocumentProps,
|
|
6
|
+
PageDimensions,
|
|
7
|
+
PagePadding,
|
|
8
|
+
PageProps,
|
|
9
|
+
PageSize,
|
|
10
|
+
PageSizePreset,
|
|
11
|
+
} from './types';
|
package/src/page.tsx
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { DEFAULT_PADDING, DEFAULT_PAGE_SIZE, PAGE_SIZES } from './constants';
|
|
2
|
+
import { getPageStyle, resolvePadding } from './styles';
|
|
3
|
+
import type { PageDimensions, PageProps, PageSize } from './types';
|
|
4
|
+
|
|
5
|
+
function resolveSize(size: PageSize): PageDimensions {
|
|
6
|
+
if (typeof size === 'string') {
|
|
7
|
+
return PAGE_SIZES[size];
|
|
8
|
+
}
|
|
9
|
+
return size;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function Page({
|
|
13
|
+
children,
|
|
14
|
+
className,
|
|
15
|
+
size = DEFAULT_PAGE_SIZE,
|
|
16
|
+
padding = DEFAULT_PADDING,
|
|
17
|
+
id,
|
|
18
|
+
}: PageProps): React.ReactElement {
|
|
19
|
+
const dimensions = resolveSize(size);
|
|
20
|
+
const resolvedPadding = resolvePadding(padding);
|
|
21
|
+
const style = getPageStyle(dimensions, resolvedPadding);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div
|
|
25
|
+
data-pdf-smith-page=""
|
|
26
|
+
data-page-width={dimensions.width}
|
|
27
|
+
data-page-height={dimensions.height}
|
|
28
|
+
className={className}
|
|
29
|
+
id={id}
|
|
30
|
+
style={style}
|
|
31
|
+
>
|
|
32
|
+
{children}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { getPages } from 'virtual:pdf-smith-pages';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Document } from '../document';
|
|
4
|
+
import { Page } from '../page';
|
|
5
|
+
import { Navigation } from './navigation';
|
|
6
|
+
|
|
7
|
+
const containerStyle: React.CSSProperties = {
|
|
8
|
+
marginLeft: '240px',
|
|
9
|
+
minHeight: '100vh',
|
|
10
|
+
background: '#e8e8e8',
|
|
11
|
+
padding: '32px',
|
|
12
|
+
boxSizing: 'border-box',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const pageWrapperStyle: React.CSSProperties = {
|
|
16
|
+
marginBottom: '24px',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const pageLabelStyle: React.CSSProperties = {
|
|
20
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
21
|
+
fontSize: '12px',
|
|
22
|
+
color: '#666',
|
|
23
|
+
marginBottom: '8px',
|
|
24
|
+
marginLeft: 'auto',
|
|
25
|
+
marginRight: 'auto',
|
|
26
|
+
width: 'fit-content',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export function PreviewApp() {
|
|
30
|
+
const pages = getPages();
|
|
31
|
+
const [activePage, setActivePage] = useState<string | null>(null);
|
|
32
|
+
|
|
33
|
+
const visiblePages = activePage ? { [activePage]: pages[activePage] } : pages;
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<>
|
|
37
|
+
<Navigation pages={pages} activePage={activePage} onSelectPage={setActivePage} />
|
|
38
|
+
<div data-pdf-smith-container="" style={containerStyle}>
|
|
39
|
+
<Document>
|
|
40
|
+
{Object.entries(visiblePages).map(([name, PageComponent]) => (
|
|
41
|
+
<div key={name} style={pageWrapperStyle}>
|
|
42
|
+
<div style={pageLabelStyle}>{name}</div>
|
|
43
|
+
<Page>
|
|
44
|
+
<PageComponent />
|
|
45
|
+
</Page>
|
|
46
|
+
</div>
|
|
47
|
+
))}
|
|
48
|
+
</Document>
|
|
49
|
+
</div>
|
|
50
|
+
</>
|
|
51
|
+
);
|
|
52
|
+
}
|