pdfx-cli 0.4.0 → 0.4.1
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 +73 -0
- package/dist/index.js +60 -43
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# pdfx-cli
|
|
2
|
+
|
|
3
|
+
> The official CLI tool for [PDFx](https://pdfx.akashpise.dev), professional pre-built PDF React components.
|
|
4
|
+
|
|
5
|
+
Create beautiful, dynamic, and perfectly typed PDFs in React using standard Tailwind-like utility classes and strict property interfaces. PDFx provides a library of copy-pasteable blocks (Invoices, Reports, Receipts) that you fully own and customize inside your project.
|
|
6
|
+
|
|
7
|
+
Built on top of [@react-pdf/renderer](https://react-pdf.org/).
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Initialize the PDFx configuration and setup in your project:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx pdfx-cli@latest init
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Adding Components
|
|
18
|
+
|
|
19
|
+
Add specific PDFx components directly into your local codebase. You own the code!
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx pdfx-cli@latest add badge
|
|
23
|
+
npx pdfx-cli@latest add table form qrcode
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Available Components
|
|
27
|
+
- `alert`
|
|
28
|
+
- `badge`
|
|
29
|
+
- `card`
|
|
30
|
+
- `data-table`
|
|
31
|
+
- `divider`
|
|
32
|
+
- `form`
|
|
33
|
+
- `heading`
|
|
34
|
+
- `keep-together`
|
|
35
|
+
- `key-value`
|
|
36
|
+
- `link`
|
|
37
|
+
- `list`
|
|
38
|
+
- `page-break`
|
|
39
|
+
- `page-footer`
|
|
40
|
+
- `page-header`
|
|
41
|
+
- `page-number`
|
|
42
|
+
- `pdf-image`
|
|
43
|
+
- `qrcode`
|
|
44
|
+
- `section`
|
|
45
|
+
- `signature`
|
|
46
|
+
- `stack`
|
|
47
|
+
- `table`
|
|
48
|
+
- `text`
|
|
49
|
+
- `watermark`
|
|
50
|
+
|
|
51
|
+
## Pre-Composed Blocks
|
|
52
|
+
Start with full, gorgeous templates.
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx pdfx-cli@latest block add invoice-modern
|
|
56
|
+
npx pdfx-cli@latest block add report-financial
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## MCP & AI Agents
|
|
60
|
+
|
|
61
|
+
PDFx comes with first-class AI Agent integration via MCP (Model Context Protocol). Connect your AI IDE or Agent directly to the PDFx registry to instantly gain fluent context about PDFx component structures.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx pdfx-cli@latest mcp init --client cursor
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Documentation
|
|
68
|
+
|
|
69
|
+
Full documentation, real-time PDF previews, and block gallery available at [pdfx.akashpise.dev](https://pdfx.akashpise.dev).
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { readFileSync as readFileSync2 } from "fs";
|
|
5
5
|
import { dirname, join } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
|
-
import
|
|
7
|
+
import chalk11 from "chalk";
|
|
8
8
|
import { Command as Command3 } from "commander";
|
|
9
9
|
|
|
10
10
|
// src/commands/add.ts
|
|
@@ -2218,6 +2218,7 @@ import { existsSync } from "fs";
|
|
|
2218
2218
|
import { mkdir, readFile, writeFile as writeFile2 } from "fs/promises";
|
|
2219
2219
|
import path11 from "path";
|
|
2220
2220
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2221
|
+
import chalk8 from "chalk";
|
|
2221
2222
|
import { Command } from "commander";
|
|
2222
2223
|
import prompts5 from "prompts";
|
|
2223
2224
|
|
|
@@ -3213,6 +3214,14 @@ function isPdfxAlreadyConfigured(config) {
|
|
|
3213
3214
|
return withMcpServers.mcpServers?.pdfx !== void 0 || withServers.servers?.pdfx !== void 0 || withMcp.mcp?.pdfx !== void 0;
|
|
3214
3215
|
}
|
|
3215
3216
|
async function initMcpConfig(opts) {
|
|
3217
|
+
const preFlightResult = runPreFlightChecks();
|
|
3218
|
+
displayPreFlightResults(preFlightResult);
|
|
3219
|
+
if (!preFlightResult.canProceed) {
|
|
3220
|
+
console.error(
|
|
3221
|
+
chalk8.red("\n Cannot proceed due to blocking issues. Please fix them and try again.\n")
|
|
3222
|
+
);
|
|
3223
|
+
process.exit(1);
|
|
3224
|
+
}
|
|
3216
3225
|
let clientName = opts.client;
|
|
3217
3226
|
if (!clientName) {
|
|
3218
3227
|
const response = await prompts5({
|
|
@@ -3294,7 +3303,7 @@ mcpCommand.command("init").description("Add PDFx MCP server config to your AI ed
|
|
|
3294
3303
|
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
3295
3304
|
import { mkdir as mkdir2, writeFile as writeFile3 } from "fs/promises";
|
|
3296
3305
|
import path12 from "path";
|
|
3297
|
-
import
|
|
3306
|
+
import chalk9 from "chalk";
|
|
3298
3307
|
import { Command as Command2 } from "commander";
|
|
3299
3308
|
import prompts6 from "prompts";
|
|
3300
3309
|
|
|
@@ -3997,6 +4006,14 @@ function fileHasPdfxContent(filePath) {
|
|
|
3997
4006
|
}
|
|
3998
4007
|
}
|
|
3999
4008
|
async function skillsInit(opts) {
|
|
4009
|
+
const preFlightResult = runPreFlightChecks();
|
|
4010
|
+
displayPreFlightResults(preFlightResult);
|
|
4011
|
+
if (!preFlightResult.canProceed) {
|
|
4012
|
+
console.error(
|
|
4013
|
+
chalk9.red("\n Cannot proceed due to blocking issues. Please fix them and try again.\n")
|
|
4014
|
+
);
|
|
4015
|
+
process.exit(1);
|
|
4016
|
+
}
|
|
4000
4017
|
let platformName = opts.platform;
|
|
4001
4018
|
if (!platformName) {
|
|
4002
4019
|
const response = await prompts6({
|
|
@@ -4016,10 +4033,10 @@ async function skillsInit(opts) {
|
|
|
4016
4033
|
}
|
|
4017
4034
|
const platform = PLATFORMS.find((p) => p.name === platformName);
|
|
4018
4035
|
if (!platform) {
|
|
4019
|
-
process.stderr.write(
|
|
4036
|
+
process.stderr.write(chalk9.red(`\u2716 Unknown platform: "${platformName}"
|
|
4020
4037
|
`));
|
|
4021
4038
|
process.stderr.write(
|
|
4022
|
-
|
|
4039
|
+
chalk9.dim(` Valid options: ${PLATFORMS.map((p) => p.name).join(", ")}
|
|
4023
4040
|
`)
|
|
4024
4041
|
);
|
|
4025
4042
|
process.exit(1);
|
|
@@ -4043,7 +4060,7 @@ async function skillsInit(opts) {
|
|
|
4043
4060
|
]
|
|
4044
4061
|
});
|
|
4045
4062
|
if (!action2 || action2 === "skip") {
|
|
4046
|
-
process.stdout.write(
|
|
4063
|
+
process.stdout.write(chalk9.dim("\nSkipped \u2014 existing file kept.\n\n"));
|
|
4047
4064
|
return;
|
|
4048
4065
|
}
|
|
4049
4066
|
} else {
|
|
@@ -4061,7 +4078,7 @@ async function skillsInit(opts) {
|
|
|
4061
4078
|
]
|
|
4062
4079
|
});
|
|
4063
4080
|
if (!action2 || action2 === "skip") {
|
|
4064
|
-
process.stdout.write(
|
|
4081
|
+
process.stdout.write(chalk9.dim("\nSkipped \u2014 existing file kept.\n\n"));
|
|
4065
4082
|
return;
|
|
4066
4083
|
}
|
|
4067
4084
|
if (action2 === "append") {
|
|
@@ -4088,15 +4105,15 @@ ${PDFX_SKILLS_CONTENT}`;
|
|
|
4088
4105
|
}
|
|
4089
4106
|
const action = shouldAppend && alreadyExists ? "Appended PDFx context to" : "Wrote";
|
|
4090
4107
|
process.stdout.write(`
|
|
4091
|
-
${
|
|
4108
|
+
${chalk9.green("\u2713")} ${action} ${chalk9.cyan(relativeFile)}
|
|
4092
4109
|
`);
|
|
4093
4110
|
process.stdout.write(`
|
|
4094
|
-
${
|
|
4111
|
+
${chalk9.dim(platform.verifyStep)}
|
|
4095
4112
|
|
|
4096
4113
|
`);
|
|
4097
4114
|
if (platform.name === "claude") {
|
|
4098
4115
|
process.stdout.write(
|
|
4099
|
-
|
|
4116
|
+
chalk9.dim(
|
|
4100
4117
|
"Tip: if you also use the MCP server, CLAUDE.md + MCP gives the AI\nthe best possible PDFx knowledge \u2014 static props reference + live registry.\n\n"
|
|
4101
4118
|
)
|
|
4102
4119
|
);
|
|
@@ -4109,34 +4126,34 @@ skillsCommand.command("init").description("Write the PDFx skills file to your AI
|
|
|
4109
4126
|
).option("-y, --yes", "Overwrite existing file without prompting").option("-a, --append", "Append PDFx context to an existing file instead of overwriting").action(skillsInit);
|
|
4110
4127
|
skillsCommand.command("list").description("List all supported AI editor platforms").action(() => {
|
|
4111
4128
|
process.stdout.write("\n");
|
|
4112
|
-
process.stdout.write(
|
|
4129
|
+
process.stdout.write(chalk9.bold(" Supported platforms\n\n"));
|
|
4113
4130
|
for (const p of PLATFORMS) {
|
|
4114
|
-
process.stdout.write(` ${
|
|
4131
|
+
process.stdout.write(` ${chalk9.cyan(p.name.padEnd(12))} ${chalk9.dim("\u2192")} ${p.file}
|
|
4115
4132
|
`);
|
|
4116
4133
|
}
|
|
4117
4134
|
process.stdout.write("\n");
|
|
4118
4135
|
process.stdout.write(
|
|
4119
|
-
|
|
4136
|
+
chalk9.dim(" Usage: npx pdfx-cli@latest skills init --platform <name>\n\n")
|
|
4120
4137
|
);
|
|
4121
4138
|
});
|
|
4122
4139
|
|
|
4123
4140
|
// src/commands/theme.ts
|
|
4124
4141
|
import fs8 from "fs";
|
|
4125
4142
|
import path13 from "path";
|
|
4126
|
-
import
|
|
4143
|
+
import chalk10 from "chalk";
|
|
4127
4144
|
import ora7 from "ora";
|
|
4128
4145
|
import prompts7 from "prompts";
|
|
4129
4146
|
import ts from "typescript";
|
|
4130
4147
|
async function themeInit() {
|
|
4131
4148
|
const configPath = path13.join(process.cwd(), "pdfx.json");
|
|
4132
4149
|
if (!checkFileExists(configPath)) {
|
|
4133
|
-
console.error(
|
|
4134
|
-
console.log(
|
|
4135
|
-
console.log(
|
|
4136
|
-
console.log(
|
|
4150
|
+
console.error(chalk10.red("\nError: pdfx.json not found"));
|
|
4151
|
+
console.log(chalk10.yellow("\n PDFx is not initialized in this project.\n"));
|
|
4152
|
+
console.log(chalk10.cyan(" Run: pdfx init"));
|
|
4153
|
+
console.log(chalk10.dim(" This will set up your project configuration and theme.\n"));
|
|
4137
4154
|
process.exit(1);
|
|
4138
4155
|
}
|
|
4139
|
-
console.log(
|
|
4156
|
+
console.log(chalk10.bold.cyan("\n PDFx Theme Setup\n"));
|
|
4140
4157
|
const answers = await prompts7(
|
|
4141
4158
|
[
|
|
4142
4159
|
{
|
|
@@ -4173,13 +4190,13 @@ async function themeInit() {
|
|
|
4173
4190
|
],
|
|
4174
4191
|
{
|
|
4175
4192
|
onCancel: () => {
|
|
4176
|
-
console.log(
|
|
4193
|
+
console.log(chalk10.yellow("\nTheme setup cancelled."));
|
|
4177
4194
|
process.exit(0);
|
|
4178
4195
|
}
|
|
4179
4196
|
}
|
|
4180
4197
|
);
|
|
4181
4198
|
if (!answers.preset || !answers.themePath) {
|
|
4182
|
-
console.error(
|
|
4199
|
+
console.error(chalk10.red("Missing required fields."));
|
|
4183
4200
|
process.exit(1);
|
|
4184
4201
|
}
|
|
4185
4202
|
const presetName = answers.preset;
|
|
@@ -4199,19 +4216,19 @@ async function themeInit() {
|
|
|
4199
4216
|
if (result.success) {
|
|
4200
4217
|
const updatedConfig = { ...result.data, theme: themePath };
|
|
4201
4218
|
writeFile(configPath, JSON.stringify(updatedConfig, null, 2));
|
|
4202
|
-
console.log(
|
|
4219
|
+
console.log(chalk10.green(" Updated pdfx.json with theme path"));
|
|
4203
4220
|
}
|
|
4204
4221
|
} catch {
|
|
4205
|
-
console.log(
|
|
4222
|
+
console.log(chalk10.yellow(' Could not update pdfx.json \u2014 add "theme" field manually'));
|
|
4206
4223
|
}
|
|
4207
4224
|
}
|
|
4208
|
-
console.log(
|
|
4225
|
+
console.log(chalk10.dim(`
|
|
4209
4226
|
Edit ${themePath} to customize your theme.
|
|
4210
4227
|
`));
|
|
4211
4228
|
} catch (error) {
|
|
4212
4229
|
spinner.fail("Failed to create theme file");
|
|
4213
4230
|
const message = error instanceof Error ? error.message : String(error);
|
|
4214
|
-
console.error(
|
|
4231
|
+
console.error(chalk10.dim(` ${message}`));
|
|
4215
4232
|
process.exit(1);
|
|
4216
4233
|
}
|
|
4217
4234
|
}
|
|
@@ -4219,28 +4236,28 @@ async function themeSwitch(presetName) {
|
|
|
4219
4236
|
const resolvedPreset = presetName === "default" ? "professional" : presetName;
|
|
4220
4237
|
const validPresets = Object.keys(themePresets);
|
|
4221
4238
|
if (!validPresets.includes(resolvedPreset)) {
|
|
4222
|
-
console.error(
|
|
4223
|
-
console.log(
|
|
4239
|
+
console.error(chalk10.red(`\u2716 Invalid theme preset: "${presetName}"`));
|
|
4240
|
+
console.log(chalk10.dim(` Available presets: ${validPresets.join(", ")}, default
|
|
4224
4241
|
`));
|
|
4225
|
-
console.log(
|
|
4242
|
+
console.log(chalk10.dim(" Usage: pdfx theme switch <preset>"));
|
|
4226
4243
|
process.exit(1);
|
|
4227
4244
|
}
|
|
4228
4245
|
const validatedPreset = resolvedPreset;
|
|
4229
4246
|
const configPath = path13.join(process.cwd(), "pdfx.json");
|
|
4230
4247
|
if (!checkFileExists(configPath)) {
|
|
4231
|
-
console.error(
|
|
4248
|
+
console.error(chalk10.red('No pdfx.json found. Run "npx pdfx-cli@latest init" first.'));
|
|
4232
4249
|
process.exit(1);
|
|
4233
4250
|
}
|
|
4234
4251
|
const rawConfig = readJsonFile(configPath);
|
|
4235
4252
|
const result = configSchema.safeParse(rawConfig);
|
|
4236
4253
|
if (!result.success) {
|
|
4237
|
-
console.error(
|
|
4254
|
+
console.error(chalk10.red("Invalid pdfx.json configuration."));
|
|
4238
4255
|
process.exit(1);
|
|
4239
4256
|
}
|
|
4240
4257
|
const config = result.data;
|
|
4241
4258
|
if (!config.theme) {
|
|
4242
4259
|
console.error(
|
|
4243
|
-
|
|
4260
|
+
chalk10.red(
|
|
4244
4261
|
'No theme path in pdfx.json. Run "npx pdfx-cli@latest theme init" to set up theming.'
|
|
4245
4262
|
)
|
|
4246
4263
|
);
|
|
@@ -4253,7 +4270,7 @@ async function themeSwitch(presetName) {
|
|
|
4253
4270
|
initial: false
|
|
4254
4271
|
});
|
|
4255
4272
|
if (!answer.confirm) {
|
|
4256
|
-
console.log(
|
|
4273
|
+
console.log(chalk10.yellow("Cancelled."));
|
|
4257
4274
|
return;
|
|
4258
4275
|
}
|
|
4259
4276
|
const spinner = ora7(`Switching to ${validatedPreset} theme...`).start();
|
|
@@ -4269,7 +4286,7 @@ async function themeSwitch(presetName) {
|
|
|
4269
4286
|
} catch (error) {
|
|
4270
4287
|
spinner.fail("Failed to switch theme");
|
|
4271
4288
|
const message = error instanceof Error ? error.message : String(error);
|
|
4272
|
-
console.error(
|
|
4289
|
+
console.error(chalk10.dim(` ${message}`));
|
|
4273
4290
|
process.exit(1);
|
|
4274
4291
|
}
|
|
4275
4292
|
}
|
|
@@ -4339,18 +4356,18 @@ function parseThemeObject(themePath) {
|
|
|
4339
4356
|
async function themeValidate() {
|
|
4340
4357
|
const configPath = path13.join(process.cwd(), "pdfx.json");
|
|
4341
4358
|
if (!checkFileExists(configPath)) {
|
|
4342
|
-
console.error(
|
|
4359
|
+
console.error(chalk10.red('No pdfx.json found. Run "npx pdfx-cli@latest init" first.'));
|
|
4343
4360
|
process.exit(1);
|
|
4344
4361
|
}
|
|
4345
4362
|
const rawConfig = readJsonFile(configPath);
|
|
4346
4363
|
const configResult = configSchema.safeParse(rawConfig);
|
|
4347
4364
|
if (!configResult.success) {
|
|
4348
|
-
console.error(
|
|
4365
|
+
console.error(chalk10.red("Invalid pdfx.json configuration."));
|
|
4349
4366
|
process.exit(1);
|
|
4350
4367
|
}
|
|
4351
4368
|
if (!configResult.data.theme) {
|
|
4352
4369
|
console.error(
|
|
4353
|
-
|
|
4370
|
+
chalk10.red(
|
|
4354
4371
|
'No theme path in pdfx.json. Run "npx pdfx-cli@latest theme init" to set up theming.'
|
|
4355
4372
|
)
|
|
4356
4373
|
);
|
|
@@ -4358,7 +4375,7 @@ async function themeValidate() {
|
|
|
4358
4375
|
}
|
|
4359
4376
|
const absThemePath = path13.resolve(process.cwd(), configResult.data.theme);
|
|
4360
4377
|
if (!checkFileExists(absThemePath)) {
|
|
4361
|
-
console.error(
|
|
4378
|
+
console.error(chalk10.red(`Theme file not found: ${configResult.data.theme}`));
|
|
4362
4379
|
process.exit(1);
|
|
4363
4380
|
}
|
|
4364
4381
|
const spinner = ora7("Validating theme file...").start();
|
|
@@ -4366,21 +4383,21 @@ async function themeValidate() {
|
|
|
4366
4383
|
const parsedTheme = parseThemeObject(absThemePath);
|
|
4367
4384
|
const result = themeSchema.safeParse(parsedTheme);
|
|
4368
4385
|
if (!result.success) {
|
|
4369
|
-
const issues = result.error.issues.map((issue) => ` \u2192 ${
|
|
4386
|
+
const issues = result.error.issues.map((issue) => ` \u2192 ${chalk10.yellow(issue.path.join("."))}: ${issue.message}`).join("\n");
|
|
4370
4387
|
spinner.fail("Theme validation failed");
|
|
4371
|
-
console.log(
|
|
4388
|
+
console.log(chalk10.red("\n Missing or invalid fields:\n"));
|
|
4372
4389
|
console.log(issues);
|
|
4373
|
-
console.log(
|
|
4390
|
+
console.log(chalk10.dim("\n Fix these fields in your theme file and run validate again.\n"));
|
|
4374
4391
|
process.exit(1);
|
|
4375
4392
|
}
|
|
4376
4393
|
spinner.succeed("Theme file is valid");
|
|
4377
|
-
console.log(
|
|
4394
|
+
console.log(chalk10.dim(`
|
|
4378
4395
|
Validated: ${configResult.data.theme}
|
|
4379
4396
|
`));
|
|
4380
4397
|
} catch (error) {
|
|
4381
4398
|
spinner.fail("Failed to validate theme");
|
|
4382
4399
|
const message = error instanceof Error ? error.message : String(error);
|
|
4383
|
-
console.error(
|
|
4400
|
+
console.error(chalk10.dim(` ${message}`));
|
|
4384
4401
|
process.exit(1);
|
|
4385
4402
|
}
|
|
4386
4403
|
}
|
|
@@ -4400,7 +4417,7 @@ program.name("pdfx").description("CLI for PDFx components").version(getVersion()
|
|
|
4400
4417
|
program.configureOutput({
|
|
4401
4418
|
writeErr: (str) => {
|
|
4402
4419
|
const message = str.replace(/^error:\s*/i, "").trimEnd();
|
|
4403
|
-
process.stderr.write(
|
|
4420
|
+
process.stderr.write(chalk11.red(`\u2716 ${message}
|
|
4404
4421
|
`));
|
|
4405
4422
|
}
|
|
4406
4423
|
});
|
|
@@ -4423,7 +4440,7 @@ try {
|
|
|
4423
4440
|
await program.parseAsync();
|
|
4424
4441
|
} catch (err) {
|
|
4425
4442
|
const message = err instanceof Error ? err.message : String(err);
|
|
4426
|
-
process.stderr.write(
|
|
4443
|
+
process.stderr.write(chalk11.red(`\u2716 ${message}
|
|
4427
4444
|
`));
|
|
4428
4445
|
process.exitCode = 1;
|
|
4429
4446
|
}
|