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.
Files changed (3) hide show
  1. package/README.md +73 -0
  2. package/dist/index.js +60 -43
  3. 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 chalk10 from "chalk";
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 chalk8 from "chalk";
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(chalk8.red(`\u2716 Unknown platform: "${platformName}"
4036
+ process.stderr.write(chalk9.red(`\u2716 Unknown platform: "${platformName}"
4020
4037
  `));
4021
4038
  process.stderr.write(
4022
- chalk8.dim(` Valid options: ${PLATFORMS.map((p) => p.name).join(", ")}
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(chalk8.dim("\nSkipped \u2014 existing file kept.\n\n"));
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(chalk8.dim("\nSkipped \u2014 existing file kept.\n\n"));
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
- ${chalk8.green("\u2713")} ${action} ${chalk8.cyan(relativeFile)}
4108
+ ${chalk9.green("\u2713")} ${action} ${chalk9.cyan(relativeFile)}
4092
4109
  `);
4093
4110
  process.stdout.write(`
4094
- ${chalk8.dim(platform.verifyStep)}
4111
+ ${chalk9.dim(platform.verifyStep)}
4095
4112
 
4096
4113
  `);
4097
4114
  if (platform.name === "claude") {
4098
4115
  process.stdout.write(
4099
- chalk8.dim(
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(chalk8.bold(" Supported platforms\n\n"));
4129
+ process.stdout.write(chalk9.bold(" Supported platforms\n\n"));
4113
4130
  for (const p of PLATFORMS) {
4114
- process.stdout.write(` ${chalk8.cyan(p.name.padEnd(12))} ${chalk8.dim("\u2192")} ${p.file}
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
- chalk8.dim(" Usage: npx pdfx-cli@latest skills init --platform <name>\n\n")
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 chalk9 from "chalk";
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(chalk9.red("\nError: pdfx.json not found"));
4134
- console.log(chalk9.yellow("\n PDFx is not initialized in this project.\n"));
4135
- console.log(chalk9.cyan(" Run: pdfx init"));
4136
- console.log(chalk9.dim(" This will set up your project configuration and theme.\n"));
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(chalk9.bold.cyan("\n PDFx Theme Setup\n"));
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(chalk9.yellow("\nTheme setup cancelled."));
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(chalk9.red("Missing required fields."));
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(chalk9.green(" Updated pdfx.json with theme path"));
4219
+ console.log(chalk10.green(" Updated pdfx.json with theme path"));
4203
4220
  }
4204
4221
  } catch {
4205
- console.log(chalk9.yellow(' Could not update pdfx.json \u2014 add "theme" field manually'));
4222
+ console.log(chalk10.yellow(' Could not update pdfx.json \u2014 add "theme" field manually'));
4206
4223
  }
4207
4224
  }
4208
- console.log(chalk9.dim(`
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(chalk9.dim(` ${message}`));
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(chalk9.red(`\u2716 Invalid theme preset: "${presetName}"`));
4223
- console.log(chalk9.dim(` Available presets: ${validPresets.join(", ")}, default
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(chalk9.dim(" Usage: pdfx theme switch <preset>"));
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(chalk9.red('No pdfx.json found. Run "npx pdfx-cli@latest init" first.'));
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(chalk9.red("Invalid pdfx.json configuration."));
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
- chalk9.red(
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(chalk9.yellow("Cancelled."));
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(chalk9.dim(` ${message}`));
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(chalk9.red('No pdfx.json found. Run "npx pdfx-cli@latest init" first.'));
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(chalk9.red("Invalid pdfx.json configuration."));
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
- chalk9.red(
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(chalk9.red(`Theme file not found: ${configResult.data.theme}`));
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 ${chalk9.yellow(issue.path.join("."))}: ${issue.message}`).join("\n");
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(chalk9.red("\n Missing or invalid fields:\n"));
4388
+ console.log(chalk10.red("\n Missing or invalid fields:\n"));
4372
4389
  console.log(issues);
4373
- console.log(chalk9.dim("\n Fix these fields in your theme file and run validate again.\n"));
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(chalk9.dim(`
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(chalk9.dim(` ${message}`));
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(chalk10.red(`\u2716 ${message}
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(chalk10.red(`\u2716 ${message}
4443
+ process.stderr.write(chalk11.red(`\u2716 ${message}
4427
4444
  `));
4428
4445
  process.exitCode = 1;
4429
4446
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdfx-cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "CLI for PDFx components",
5
5
  "type": "module",
6
6
  "bin": {