pdfn 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/README.md +9 -1
- package/dist/cli.js +132 -30
- package/dist/cli.js.map +1 -1
- package/dist/server/index.js +9 -1
- package/dist/server/index.js.map +1 -1
- package/package.json +4 -2
- package/templates/inline/contract.tsx +217 -0
- package/templates/inline/invoice.tsx +188 -0
- package/templates/inline/letter.tsx +129 -0
- package/templates/inline/poster.tsx +128 -0
- package/templates/inline/ticket.tsx +138 -0
- /package/templates/{contract.tsx → tailwind/contract.tsx} +0 -0
- /package/templates/{invoice.tsx → tailwind/invoice.tsx} +0 -0
- /package/templates/{letter.tsx → tailwind/letter.tsx} +0 -0
- /package/templates/{poster.tsx → tailwind/poster.tsx} +0 -0
- /package/templates/{ticket.tsx → tailwind/ticket.tsx} +0 -0
package/README.md
CHANGED
|
@@ -56,7 +56,8 @@ npx pdfn serve --max-concurrent 10 # Concurrency limit
|
|
|
56
56
|
Add starter templates to your project.
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
|
-
npx pdfn add invoice # Add invoice template
|
|
59
|
+
npx pdfn add invoice # Add invoice template (inline styles)
|
|
60
|
+
npx pdfn add invoice --tailwind # Add with Tailwind classes
|
|
60
61
|
npx pdfn add letter # Add business letter
|
|
61
62
|
npx pdfn add contract # Add contract template
|
|
62
63
|
npx pdfn add ticket # Add event ticket
|
|
@@ -65,6 +66,13 @@ npx pdfn add --list # Show all templates
|
|
|
65
66
|
npx pdfn add invoice --output ./src/templates
|
|
66
67
|
```
|
|
67
68
|
|
|
69
|
+
| Option | Default | Description |
|
|
70
|
+
|--------|---------|-------------|
|
|
71
|
+
| `--inline` | ✓ | Use inline styles (default, no extra dependencies) |
|
|
72
|
+
| `--tailwind` | | Use Tailwind CSS classes (requires `@pdfn/tailwind`) |
|
|
73
|
+
| `--output` | `./pdf-templates` | Output directory |
|
|
74
|
+
| `--force` | | Overwrite existing files |
|
|
75
|
+
|
|
68
76
|
| Template | Description | Page Size |
|
|
69
77
|
|----------|-------------|-----------|
|
|
70
78
|
| `invoice` | Professional invoice with itemized billing | A4 |
|
package/dist/cli.js
CHANGED
|
@@ -5,8 +5,8 @@ import { Command as Command4 } from "commander";
|
|
|
5
5
|
|
|
6
6
|
// src/commands/dev.ts
|
|
7
7
|
import { Command } from "commander";
|
|
8
|
-
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
9
|
-
import { join, resolve } from "path";
|
|
8
|
+
import { existsSync as existsSync2, readdirSync, readFileSync } from "fs";
|
|
9
|
+
import { join, resolve as resolve2 } from "path";
|
|
10
10
|
import { createServer as createViteServer } from "vite";
|
|
11
11
|
import { WebSocketServer, WebSocket } from "ws";
|
|
12
12
|
import chokidar from "chokidar";
|
|
@@ -411,9 +411,32 @@ function createBaseServer(options = {}) {
|
|
|
411
411
|
|
|
412
412
|
// src/commands/dev.ts
|
|
413
413
|
import { injectDebugSupport } from "@pdfn/react/debug";
|
|
414
|
+
import { pdfnTailwind } from "@pdfn/vite";
|
|
414
415
|
import chalk from "chalk";
|
|
416
|
+
|
|
417
|
+
// src/utils/env.ts
|
|
418
|
+
import { existsSync } from "fs";
|
|
419
|
+
import { resolve } from "path";
|
|
420
|
+
import { config } from "dotenv";
|
|
421
|
+
function loadEnv(mode) {
|
|
422
|
+
const cwd = process.cwd();
|
|
423
|
+
const envFiles = [
|
|
424
|
+
".env",
|
|
425
|
+
".env.local",
|
|
426
|
+
`.env.${mode}`,
|
|
427
|
+
`.env.${mode}.local`
|
|
428
|
+
];
|
|
429
|
+
for (const file of envFiles) {
|
|
430
|
+
const filePath = resolve(cwd, file);
|
|
431
|
+
if (existsSync(filePath)) {
|
|
432
|
+
config({ path: filePath, override: true, quiet: true });
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// src/commands/dev.ts
|
|
415
438
|
async function scanTemplates(templatesDir) {
|
|
416
|
-
if (!
|
|
439
|
+
if (!existsSync2(templatesDir)) {
|
|
417
440
|
return [];
|
|
418
441
|
}
|
|
419
442
|
let configData = {};
|
|
@@ -423,11 +446,11 @@ async function scanTemplates(templatesDir) {
|
|
|
423
446
|
join(templatesDir, "templates.json")
|
|
424
447
|
];
|
|
425
448
|
for (const configPath of configPaths) {
|
|
426
|
-
if (
|
|
449
|
+
if (existsSync2(configPath)) {
|
|
427
450
|
try {
|
|
428
|
-
const
|
|
429
|
-
if (
|
|
430
|
-
for (const t of
|
|
451
|
+
const config2 = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
452
|
+
if (config2.templates && Array.isArray(config2.templates)) {
|
|
453
|
+
for (const t of config2.templates) {
|
|
431
454
|
if (t.id && t.sampleData) {
|
|
432
455
|
configData[t.id] = t.sampleData;
|
|
433
456
|
}
|
|
@@ -1440,8 +1463,8 @@ function createPreviewHTML(templates, activeTemplate) {
|
|
|
1440
1463
|
</html>`;
|
|
1441
1464
|
}
|
|
1442
1465
|
async function startDevServer(options) {
|
|
1443
|
-
const { port, templatesDir, open } = options;
|
|
1444
|
-
const absoluteTemplatesDir =
|
|
1466
|
+
const { port, templatesDir, open, mode } = options;
|
|
1467
|
+
const absoluteTemplatesDir = resolve2(process.cwd(), templatesDir);
|
|
1445
1468
|
console.log(chalk.bold("\n pdfn dev\n"));
|
|
1446
1469
|
console.log(chalk.dim(" Initializing..."));
|
|
1447
1470
|
let templates = await scanTemplates(absoluteTemplatesDir);
|
|
@@ -1473,6 +1496,12 @@ async function startDevServer(options) {
|
|
|
1473
1496
|
clients.add(ws);
|
|
1474
1497
|
ws.on("close", () => clients.delete(ws));
|
|
1475
1498
|
});
|
|
1499
|
+
wss.on("error", (err) => {
|
|
1500
|
+
if (err.code === "EADDRINUSE") {
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1503
|
+
console.error(chalk.red(" \u2717 WebSocket error:"), err.message);
|
|
1504
|
+
});
|
|
1476
1505
|
function broadcast(message) {
|
|
1477
1506
|
const data = JSON.stringify(message);
|
|
1478
1507
|
clients.forEach((client) => {
|
|
@@ -1496,7 +1525,12 @@ async function startDevServer(options) {
|
|
|
1496
1525
|
};
|
|
1497
1526
|
const vite = await createViteServer({
|
|
1498
1527
|
root: process.cwd(),
|
|
1499
|
-
|
|
1528
|
+
mode,
|
|
1529
|
+
server: {
|
|
1530
|
+
middlewareMode: true,
|
|
1531
|
+
hmr: { server }
|
|
1532
|
+
// Use our HTTP server for Vite's WebSocket
|
|
1533
|
+
},
|
|
1500
1534
|
appType: "custom",
|
|
1501
1535
|
customLogger: viteLogger,
|
|
1502
1536
|
optimizeDeps: {
|
|
@@ -1510,6 +1544,12 @@ async function startDevServer(options) {
|
|
|
1510
1544
|
noExternal: ["@pdfn/react", "@pdfn/tailwind", "server-only"]
|
|
1511
1545
|
},
|
|
1512
1546
|
plugins: [
|
|
1547
|
+
// Pre-compile Tailwind CSS for edge compatibility
|
|
1548
|
+
pdfnTailwind({
|
|
1549
|
+
templates: [
|
|
1550
|
+
join(templatesDir, "**/*.tsx")
|
|
1551
|
+
]
|
|
1552
|
+
}),
|
|
1513
1553
|
{
|
|
1514
1554
|
name: "mock-server-only",
|
|
1515
1555
|
enforce: "pre",
|
|
@@ -1549,6 +1589,17 @@ async function startDevServer(options) {
|
|
|
1549
1589
|
const fileName = filePath.split("/").pop() || filePath;
|
|
1550
1590
|
if (isCodeFile(filePath)) {
|
|
1551
1591
|
console.log(chalk.blue(" \u21BB"), fileName, chalk.dim("changed"));
|
|
1592
|
+
const mod = vite.moduleGraph.getModuleById(filePath);
|
|
1593
|
+
if (mod) {
|
|
1594
|
+
vite.moduleGraph.invalidateModule(mod);
|
|
1595
|
+
}
|
|
1596
|
+
const virtualMod = vite.moduleGraph.getModuleById("\0virtual:pdfn-tailwind-css");
|
|
1597
|
+
if (virtualMod) {
|
|
1598
|
+
vite.moduleGraph.invalidateModule(virtualMod);
|
|
1599
|
+
for (const importer of virtualMod.importers) {
|
|
1600
|
+
vite.moduleGraph.invalidateModule(importer);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1552
1603
|
}
|
|
1553
1604
|
templates = await scanTemplates(absoluteTemplatesDir);
|
|
1554
1605
|
broadcast({ type: "reload" });
|
|
@@ -1738,8 +1789,20 @@ async function startDevServer(options) {
|
|
|
1738
1789
|
res.status(500).json({ error: `Error generating PDF: ${error}` });
|
|
1739
1790
|
}
|
|
1740
1791
|
});
|
|
1741
|
-
await new Promise((
|
|
1742
|
-
server.
|
|
1792
|
+
await new Promise((resolve3, reject) => {
|
|
1793
|
+
server.on("error", (err) => {
|
|
1794
|
+
if (err.code === "EADDRINUSE") {
|
|
1795
|
+
console.error(chalk.red(`
|
|
1796
|
+
\u2717 Port ${port} is already in use.
|
|
1797
|
+
`));
|
|
1798
|
+
console.error(chalk.dim(` Either stop the existing server or use a different port:`));
|
|
1799
|
+
console.error(chalk.dim(` npx pdfn dev --port ${port + 1}
|
|
1800
|
+
`));
|
|
1801
|
+
process.exit(1);
|
|
1802
|
+
}
|
|
1803
|
+
reject(err);
|
|
1804
|
+
});
|
|
1805
|
+
server.listen(port, () => resolve3());
|
|
1743
1806
|
});
|
|
1744
1807
|
await browserManager.getBrowser();
|
|
1745
1808
|
console.log(chalk.dim(` Templates: ${displayPath} (${templateCount})`));
|
|
@@ -1762,12 +1825,14 @@ async function startDevServer(options) {
|
|
|
1762
1825
|
process.on("SIGTERM", shutdown);
|
|
1763
1826
|
process.on("SIGINT", shutdown);
|
|
1764
1827
|
}
|
|
1765
|
-
var devCommand = new Command("dev").description("Start development server with live preview").option("--port <number>", "Server port (env: PDFN_PORT)", process.env.PDFN_PORT ?? "3456").option("--templates <path>", "Templates directory", "./pdf-templates").option("--open", "Open browser automatically").action(async (options) => {
|
|
1828
|
+
var devCommand = new Command("dev").description("Start development server with live preview").option("--port <number>", "Server port (env: PDFN_PORT)", process.env.PDFN_PORT ?? "3456").option("--templates <path>", "Templates directory", "./pdf-templates").option("--open", "Open browser automatically").option("--mode <mode>", "Environment mode (loads .env.[mode])", "development").action(async (options) => {
|
|
1829
|
+
loadEnv(options.mode);
|
|
1766
1830
|
const port = parseInt(options.port, 10);
|
|
1767
1831
|
await startDevServer({
|
|
1768
1832
|
port,
|
|
1769
1833
|
templatesDir: options.templates,
|
|
1770
|
-
open: options.open ?? false
|
|
1834
|
+
open: options.open ?? false,
|
|
1835
|
+
mode: options.mode
|
|
1771
1836
|
});
|
|
1772
1837
|
});
|
|
1773
1838
|
|
|
@@ -1978,10 +2043,18 @@ For development with live preview, use:
|
|
|
1978
2043
|
logger.browser("launching");
|
|
1979
2044
|
await browserManager.getBrowser();
|
|
1980
2045
|
logger.browser("ready");
|
|
1981
|
-
return new Promise((
|
|
2046
|
+
return new Promise((resolve3, reject) => {
|
|
1982
2047
|
server = app.listen(port, () => {
|
|
1983
2048
|
logger.banner(port, maxConcurrent, timeout);
|
|
1984
|
-
|
|
2049
|
+
resolve3();
|
|
2050
|
+
});
|
|
2051
|
+
server.on("error", (err) => {
|
|
2052
|
+
if (err.code === "EADDRINUSE") {
|
|
2053
|
+
logger.error(`Port ${port} is already in use`);
|
|
2054
|
+
logger.info(`Either stop the existing server or use a different port`);
|
|
2055
|
+
process.exit(1);
|
|
2056
|
+
}
|
|
2057
|
+
reject(err);
|
|
1985
2058
|
});
|
|
1986
2059
|
});
|
|
1987
2060
|
},
|
|
@@ -1989,8 +2062,8 @@ For development with live preview, use:
|
|
|
1989
2062
|
await browserManager.close();
|
|
1990
2063
|
logger.browser("closed");
|
|
1991
2064
|
if (server) {
|
|
1992
|
-
await new Promise((
|
|
1993
|
-
server.close(() =>
|
|
2065
|
+
await new Promise((resolve3) => {
|
|
2066
|
+
server.close(() => resolve3());
|
|
1994
2067
|
});
|
|
1995
2068
|
server = null;
|
|
1996
2069
|
}
|
|
@@ -2000,7 +2073,8 @@ For development with live preview, use:
|
|
|
2000
2073
|
}
|
|
2001
2074
|
|
|
2002
2075
|
// src/commands/serve.ts
|
|
2003
|
-
var serveCommand = new Command2("serve").description("Start production server (headless, no UI)").option("--port <number>", "Server port (env: PDFN_PORT)", "3456").option("--max-concurrent <number>", "Max concurrent pages (env: PDFN_MAX_CONCURRENT)", "5").option("--timeout <ms>", "Request timeout in ms (env: PDFN_TIMEOUT)", "30000").action(async (options) => {
|
|
2076
|
+
var serveCommand = new Command2("serve").description("Start production server (headless, no UI)").option("--port <number>", "Server port (env: PDFN_PORT)", "3456").option("--max-concurrent <number>", "Max concurrent pages (env: PDFN_MAX_CONCURRENT)", "5").option("--timeout <ms>", "Request timeout in ms (env: PDFN_TIMEOUT)", "30000").option("--mode <mode>", "Environment mode (loads .env.[mode])", "production").action(async (options) => {
|
|
2077
|
+
loadEnv(options.mode);
|
|
2004
2078
|
const port = parseInt(options.port, 10);
|
|
2005
2079
|
const maxConcurrent = parseInt(options.maxConcurrent, 10);
|
|
2006
2080
|
const timeout = parseInt(options.timeout, 10);
|
|
@@ -2017,7 +2091,7 @@ var serveCommand = new Command2("serve").description("Start production server (h
|
|
|
2017
2091
|
|
|
2018
2092
|
// src/commands/add.ts
|
|
2019
2093
|
import { Command as Command3 } from "commander";
|
|
2020
|
-
import { existsSync as
|
|
2094
|
+
import { existsSync as existsSync3, mkdirSync, copyFileSync } from "fs";
|
|
2021
2095
|
import { join as join2, dirname } from "path";
|
|
2022
2096
|
import { fileURLToPath } from "url";
|
|
2023
2097
|
import chalk2 from "chalk";
|
|
@@ -2049,17 +2123,30 @@ var TEMPLATES = {
|
|
|
2049
2123
|
pageSize: "Tabloid"
|
|
2050
2124
|
}
|
|
2051
2125
|
};
|
|
2052
|
-
function getTemplatesDir() {
|
|
2053
|
-
return join2(__dirname, "..", "templates");
|
|
2126
|
+
function getTemplatesDir(style) {
|
|
2127
|
+
return join2(__dirname, "..", "templates", style);
|
|
2128
|
+
}
|
|
2129
|
+
function isTailwindInstalled(cwd) {
|
|
2130
|
+
try {
|
|
2131
|
+
const tailwindPath = join2(cwd, "node_modules", "@pdfn", "tailwind");
|
|
2132
|
+
return existsSync3(tailwindPath);
|
|
2133
|
+
} catch {
|
|
2134
|
+
return false;
|
|
2135
|
+
}
|
|
2054
2136
|
}
|
|
2055
|
-
var addCommand = new Command3("add").description("Add a starter template to your project").argument("[template]", "Template name (e.g., invoice, letter, contract)").option("--list", "List available templates").option("--output <path>", "Output directory", "./pdf-templates").option("--force", "Overwrite existing files").action(async (template, options) => {
|
|
2137
|
+
var addCommand = new Command3("add").description("Add a starter template to your project").argument("[template]", "Template name (e.g., invoice, letter, contract)").option("--list", "List available templates").option("--tailwind", "Use Tailwind CSS styling (requires @pdfn/tailwind)").option("--inline", "Use inline styles (default)").option("--output <path>", "Output directory", "./pdf-templates").option("--force", "Overwrite existing files").action(async (template, options) => {
|
|
2138
|
+
const cwd = process.cwd();
|
|
2056
2139
|
if (options.list || !template) {
|
|
2057
2140
|
console.log(chalk2.bold("\nAvailable templates:\n"));
|
|
2058
2141
|
for (const [id, info] of Object.entries(TEMPLATES)) {
|
|
2059
2142
|
console.log(` ${chalk2.cyan(id.padEnd(12))} ${info.description} ${chalk2.dim(`(${info.pageSize})`)}`);
|
|
2060
2143
|
}
|
|
2061
|
-
console.log(chalk2.dim("\nUsage: pdfn add <template>"));
|
|
2062
|
-
console.log(chalk2.dim("Example: pdfn add invoice
|
|
2144
|
+
console.log(chalk2.dim("\nUsage: pdfn add <template> [--tailwind]"));
|
|
2145
|
+
console.log(chalk2.dim("Example: pdfn add invoice"));
|
|
2146
|
+
console.log(chalk2.dim("Example: pdfn add invoice --tailwind\n"));
|
|
2147
|
+
console.log(chalk2.bold("Options:"));
|
|
2148
|
+
console.log(chalk2.dim(" --inline Use inline styles (default)"));
|
|
2149
|
+
console.log(chalk2.dim(" --tailwind Use Tailwind CSS (requires @pdfn/tailwind)\n"));
|
|
2063
2150
|
return;
|
|
2064
2151
|
}
|
|
2065
2152
|
if (!TEMPLATES[template]) {
|
|
@@ -2068,21 +2155,32 @@ Error: Unknown template "${template}"`));
|
|
|
2068
2155
|
console.log(chalk2.dim("Run 'pdfn add --list' to see available templates\n"));
|
|
2069
2156
|
process.exit(1);
|
|
2070
2157
|
}
|
|
2071
|
-
const
|
|
2158
|
+
const style = options.tailwind ? "tailwind" : "inline";
|
|
2159
|
+
if (style === "tailwind" && !isTailwindInstalled(cwd)) {
|
|
2160
|
+
console.error(chalk2.yellow(`
|
|
2161
|
+
\u26A0 @pdfn/tailwind is not installed.`));
|
|
2162
|
+
console.log(chalk2.dim("Install it first to use Tailwind templates:\n"));
|
|
2163
|
+
console.log(chalk2.cyan(" npm install @pdfn/tailwind\n"));
|
|
2164
|
+
console.log(chalk2.dim("Or use inline styles (default):\n"));
|
|
2165
|
+
console.log(chalk2.cyan(` pdfn add ${template}
|
|
2166
|
+
`));
|
|
2167
|
+
process.exit(1);
|
|
2168
|
+
}
|
|
2169
|
+
const templatesDir = getTemplatesDir(style);
|
|
2072
2170
|
const sourceFile = join2(templatesDir, `${template}.tsx`);
|
|
2073
2171
|
const outputDir = options.output;
|
|
2074
2172
|
const outputFile = join2(outputDir, `${template}.tsx`);
|
|
2075
|
-
if (!
|
|
2173
|
+
if (!existsSync3(sourceFile)) {
|
|
2076
2174
|
console.error(chalk2.red(`
|
|
2077
2175
|
Error: Template file not found: ${sourceFile}`));
|
|
2078
2176
|
console.log(chalk2.dim("This may be a package installation issue.\n"));
|
|
2079
2177
|
process.exit(1);
|
|
2080
2178
|
}
|
|
2081
|
-
if (!
|
|
2179
|
+
if (!existsSync3(outputDir)) {
|
|
2082
2180
|
mkdirSync(outputDir, { recursive: true });
|
|
2083
2181
|
console.log(chalk2.dim(`Created ${outputDir}/`));
|
|
2084
2182
|
}
|
|
2085
|
-
if (
|
|
2183
|
+
if (existsSync3(outputFile) && !options.force) {
|
|
2086
2184
|
console.error(chalk2.yellow(`
|
|
2087
2185
|
File already exists: ${outputFile}`));
|
|
2088
2186
|
console.log(chalk2.dim("Use --force to overwrite\n"));
|
|
@@ -2091,14 +2189,18 @@ File already exists: ${outputFile}`));
|
|
|
2091
2189
|
try {
|
|
2092
2190
|
copyFileSync(sourceFile, outputFile);
|
|
2093
2191
|
const info = TEMPLATES[template];
|
|
2192
|
+
const styleLabel = style === "tailwind" ? chalk2.cyan(" (Tailwind)") : chalk2.dim(" (inline styles)");
|
|
2094
2193
|
console.log(chalk2.green(`
|
|
2095
|
-
\u2713 Added ${info.name} template`));
|
|
2194
|
+
\u2713 Added ${info.name} template`) + styleLabel);
|
|
2096
2195
|
console.log(chalk2.dim(` ${outputFile}
|
|
2097
2196
|
`));
|
|
2098
2197
|
console.log(chalk2.bold("Next steps:"));
|
|
2099
2198
|
console.log(chalk2.dim(` 1. Edit ${outputFile} to customize`));
|
|
2100
2199
|
console.log(chalk2.dim(` 2. Run 'npx pdfn dev' to preview
|
|
2101
2200
|
`));
|
|
2201
|
+
if (style === "tailwind") {
|
|
2202
|
+
console.log(chalk2.dim("Note: Tailwind templates require @pdfn/tailwind to be installed.\n"));
|
|
2203
|
+
}
|
|
2102
2204
|
} catch (error) {
|
|
2103
2205
|
console.error(chalk2.red(`
|
|
2104
2206
|
Error copying template: ${error}`));
|