@akii09/pdfx-cli 0.1.4 → 0.1.6
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/index.js +688 -237
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -492,8 +492,8 @@ function getErrorMap() {
|
|
|
492
492
|
|
|
493
493
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
494
494
|
var makeIssue = (params) => {
|
|
495
|
-
const { data, path:
|
|
496
|
-
const fullPath = [...
|
|
495
|
+
const { data, path: path12, errorMaps, issueData } = params;
|
|
496
|
+
const fullPath = [...path12, ...issueData.path || []];
|
|
497
497
|
const fullIssue = {
|
|
498
498
|
...issueData,
|
|
499
499
|
path: fullPath
|
|
@@ -609,11 +609,11 @@ var errorUtil;
|
|
|
609
609
|
|
|
610
610
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
|
|
611
611
|
var ParseInputLazyPath = class {
|
|
612
|
-
constructor(parent, value,
|
|
612
|
+
constructor(parent, value, path12, key) {
|
|
613
613
|
this._cachedPath = [];
|
|
614
614
|
this.parent = parent;
|
|
615
615
|
this.data = value;
|
|
616
|
-
this._path =
|
|
616
|
+
this._path = path12;
|
|
617
617
|
this._key = key;
|
|
618
618
|
}
|
|
619
619
|
get path() {
|
|
@@ -4148,13 +4148,16 @@ var configSchema = external_exports.object({
|
|
|
4148
4148
|
registry: external_exports.string().url("registry must be a valid URL"),
|
|
4149
4149
|
theme: external_exports.string().min(1).optional(),
|
|
4150
4150
|
/** Directory where templates are installed. Defaults to ./src/templates/pdfx */
|
|
4151
|
-
templateDir: external_exports.string().min(1).optional()
|
|
4151
|
+
templateDir: external_exports.string().min(1).optional(),
|
|
4152
|
+
/** Directory where blocks are installed. Defaults to ./src/blocks/pdfx */
|
|
4153
|
+
blockDir: external_exports.string().min(1).optional()
|
|
4152
4154
|
});
|
|
4153
4155
|
var registryFileTypes = [
|
|
4154
4156
|
"registry:component",
|
|
4155
4157
|
"registry:lib",
|
|
4156
4158
|
"registry:style",
|
|
4157
|
-
"registry:template"
|
|
4159
|
+
"registry:template",
|
|
4160
|
+
"registry:block"
|
|
4158
4161
|
];
|
|
4159
4162
|
var registryFileSchema = external_exports.object({
|
|
4160
4163
|
path: external_exports.string().min(1),
|
|
@@ -5089,13 +5092,339 @@ async function add(components, options = {}) {
|
|
|
5089
5092
|
}
|
|
5090
5093
|
}
|
|
5091
5094
|
|
|
5092
|
-
// src/commands/
|
|
5093
|
-
import fs4 from "fs";
|
|
5095
|
+
// src/commands/block.ts
|
|
5094
5096
|
import path4 from "path";
|
|
5095
5097
|
import chalk2 from "chalk";
|
|
5096
5098
|
import ora2 from "ora";
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
+
import prompts2 from "prompts";
|
|
5100
|
+
|
|
5101
|
+
// src/constants.ts
|
|
5102
|
+
var DEFAULTS = {
|
|
5103
|
+
REGISTRY_URL: "https://pdfx.akashpise.dev/r",
|
|
5104
|
+
SCHEMA_URL: "https://pdfx.akashpise.dev/schema.json",
|
|
5105
|
+
COMPONENT_DIR: "./src/components/pdfx",
|
|
5106
|
+
THEME_FILE: "./src/lib/pdfx-theme.ts",
|
|
5107
|
+
TEMPLATE_DIR: "./src/templates/pdfx",
|
|
5108
|
+
BLOCK_DIR: "./src/blocks/pdfx"
|
|
5109
|
+
};
|
|
5110
|
+
var REGISTRY_SUBPATHS = {
|
|
5111
|
+
TEMPLATES: "templates"
|
|
5112
|
+
};
|
|
5113
|
+
|
|
5114
|
+
// src/commands/block.ts
|
|
5115
|
+
function readConfig2(configPath) {
|
|
5116
|
+
const raw = readJsonFile(configPath);
|
|
5117
|
+
const result = configSchema.safeParse(raw);
|
|
5118
|
+
if (!result.success) {
|
|
5119
|
+
const issues = result.error.issues.map((i) => i.message).join(", ");
|
|
5120
|
+
throw new ConfigError(
|
|
5121
|
+
`Invalid pdfx.json: ${issues}`,
|
|
5122
|
+
`Fix the config or re-run ${chalk2.cyan("pdfx init")}`
|
|
5123
|
+
);
|
|
5124
|
+
}
|
|
5125
|
+
return result.data;
|
|
5126
|
+
}
|
|
5127
|
+
async function fetchBlock(name, registryUrl) {
|
|
5128
|
+
const url = `${registryUrl}/${REGISTRY_SUBPATHS.TEMPLATES}/${name}.json`;
|
|
5129
|
+
let response;
|
|
5130
|
+
try {
|
|
5131
|
+
response = await fetch(url, { signal: AbortSignal.timeout(1e4) });
|
|
5132
|
+
} catch (err) {
|
|
5133
|
+
const isTimeout = err instanceof Error && err.name === "TimeoutError";
|
|
5134
|
+
throw new NetworkError(
|
|
5135
|
+
isTimeout ? "Registry request timed out" : `Could not reach ${registryUrl}`
|
|
5136
|
+
);
|
|
5137
|
+
}
|
|
5138
|
+
if (!response.ok) {
|
|
5139
|
+
throw new RegistryError(
|
|
5140
|
+
response.status === 404 ? `Block "${name}" not found in registry` : `Registry returned HTTP ${response.status}`
|
|
5141
|
+
);
|
|
5142
|
+
}
|
|
5143
|
+
let data;
|
|
5144
|
+
try {
|
|
5145
|
+
data = await response.json();
|
|
5146
|
+
} catch {
|
|
5147
|
+
throw new RegistryError(`Invalid response for "${name}": not valid JSON`);
|
|
5148
|
+
}
|
|
5149
|
+
const result = registryItemSchema.safeParse(data);
|
|
5150
|
+
if (!result.success) {
|
|
5151
|
+
throw new RegistryError(
|
|
5152
|
+
`Invalid registry entry for "${name}": ${result.error.issues[0]?.message}`
|
|
5153
|
+
);
|
|
5154
|
+
}
|
|
5155
|
+
return result.data;
|
|
5156
|
+
}
|
|
5157
|
+
async function fetchComponent2(name, registryUrl) {
|
|
5158
|
+
const url = `${registryUrl}/${name}.json`;
|
|
5159
|
+
let response;
|
|
5160
|
+
try {
|
|
5161
|
+
response = await fetch(url, { signal: AbortSignal.timeout(1e4) });
|
|
5162
|
+
} catch (err) {
|
|
5163
|
+
const isTimeout = err instanceof Error && err.name === "TimeoutError";
|
|
5164
|
+
throw new NetworkError(
|
|
5165
|
+
isTimeout ? "Registry request timed out" : `Could not reach ${registryUrl}`
|
|
5166
|
+
);
|
|
5167
|
+
}
|
|
5168
|
+
if (!response.ok) {
|
|
5169
|
+
throw new RegistryError(
|
|
5170
|
+
response.status === 404 ? `Component "${name}" not found in registry` : `Registry returned HTTP ${response.status}`
|
|
5171
|
+
);
|
|
5172
|
+
}
|
|
5173
|
+
let data;
|
|
5174
|
+
try {
|
|
5175
|
+
data = await response.json();
|
|
5176
|
+
} catch {
|
|
5177
|
+
throw new RegistryError(`Invalid response for "${name}": not valid JSON`);
|
|
5178
|
+
}
|
|
5179
|
+
const result = registryItemSchema.safeParse(data);
|
|
5180
|
+
if (!result.success) {
|
|
5181
|
+
throw new RegistryError(
|
|
5182
|
+
`Invalid registry entry for "${name}": ${result.error.issues[0]?.message}`
|
|
5183
|
+
);
|
|
5184
|
+
}
|
|
5185
|
+
return result.data;
|
|
5186
|
+
}
|
|
5187
|
+
function resolveBlockImports(content, blockName, config) {
|
|
5188
|
+
const cwd = process.cwd();
|
|
5189
|
+
const blockSubdir = path4.resolve(cwd, config.blockDir ?? DEFAULTS.BLOCK_DIR, blockName);
|
|
5190
|
+
let result = content.replace(
|
|
5191
|
+
/from '\.\.\/\.\.\/components\/pdfx\/([a-z][a-z0-9-]*)\/pdfx-([a-z][a-z0-9-]*)'/g,
|
|
5192
|
+
(_match, componentName) => {
|
|
5193
|
+
const absCompFile = path4.resolve(
|
|
5194
|
+
cwd,
|
|
5195
|
+
config.componentDir,
|
|
5196
|
+
componentName,
|
|
5197
|
+
`pdfx-${componentName}`
|
|
5198
|
+
);
|
|
5199
|
+
let rel = path4.relative(blockSubdir, absCompFile);
|
|
5200
|
+
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
5201
|
+
return `from '${rel}'`;
|
|
5202
|
+
}
|
|
5203
|
+
);
|
|
5204
|
+
if (config.theme) {
|
|
5205
|
+
const absThemePath = path4.resolve(cwd, config.theme).replace(/\.tsx?$/, "");
|
|
5206
|
+
let relTheme = path4.relative(blockSubdir, absThemePath);
|
|
5207
|
+
if (!relTheme.startsWith(".")) relTheme = `./${relTheme}`;
|
|
5208
|
+
const absContextPath = path4.join(
|
|
5209
|
+
path4.dirname(path4.resolve(cwd, config.theme)),
|
|
5210
|
+
"pdfx-theme-context"
|
|
5211
|
+
);
|
|
5212
|
+
let relContext = path4.relative(blockSubdir, absContextPath);
|
|
5213
|
+
if (!relContext.startsWith(".")) relContext = `./${relContext}`;
|
|
5214
|
+
result = result.replace(/from '\.\.\/\.\.\/lib\/pdfx-theme'/g, `from '${relTheme}'`);
|
|
5215
|
+
result = result.replace(/from '\.\.\/\.\.\/lib\/pdfx-theme-context'/g, `from '${relContext}'`);
|
|
5216
|
+
}
|
|
5217
|
+
return result;
|
|
5218
|
+
}
|
|
5219
|
+
async function resolveConflict(fileName, currentDecision) {
|
|
5220
|
+
if (currentDecision === "overwrite-all") return "overwrite-all";
|
|
5221
|
+
const { action } = await prompts2({
|
|
5222
|
+
type: "select",
|
|
5223
|
+
name: "action",
|
|
5224
|
+
message: `${chalk2.yellow(fileName)} already exists. What would you like to do?`,
|
|
5225
|
+
choices: [
|
|
5226
|
+
{ title: "Skip", value: "skip", description: "Keep the existing file unchanged" },
|
|
5227
|
+
{ title: "Overwrite", value: "overwrite", description: "Replace this file only" },
|
|
5228
|
+
{
|
|
5229
|
+
title: "Overwrite all",
|
|
5230
|
+
value: "overwrite-all",
|
|
5231
|
+
description: "Replace all conflicting files"
|
|
5232
|
+
}
|
|
5233
|
+
],
|
|
5234
|
+
initial: 0
|
|
5235
|
+
});
|
|
5236
|
+
if (!action) throw new ValidationError("Cancelled by user");
|
|
5237
|
+
return action;
|
|
5238
|
+
}
|
|
5239
|
+
async function ensurePeerComponents(block, config, force) {
|
|
5240
|
+
const installedPeers = [];
|
|
5241
|
+
const peerWarnings = [];
|
|
5242
|
+
if (!block.peerComponents || block.peerComponents.length === 0) {
|
|
5243
|
+
return { installedPeers, peerWarnings };
|
|
5244
|
+
}
|
|
5245
|
+
const componentBaseDir = path4.resolve(process.cwd(), config.componentDir);
|
|
5246
|
+
for (const componentName of block.peerComponents) {
|
|
5247
|
+
const componentDir = path4.join(componentBaseDir, componentName);
|
|
5248
|
+
const expectedMain = path4.join(componentDir, `pdfx-${componentName}.tsx`);
|
|
5249
|
+
if (checkFileExists(componentDir)) {
|
|
5250
|
+
if (!checkFileExists(expectedMain)) {
|
|
5251
|
+
peerWarnings.push(
|
|
5252
|
+
`${componentName}: directory exists but expected file missing (${path4.basename(expectedMain)})`
|
|
5253
|
+
);
|
|
5254
|
+
} else {
|
|
5255
|
+
peerWarnings.push(
|
|
5256
|
+
`${componentName}: already exists, skipped install (use "pdfx diff ${componentName}" to verify freshness)`
|
|
5257
|
+
);
|
|
5258
|
+
}
|
|
5259
|
+
continue;
|
|
5260
|
+
}
|
|
5261
|
+
const component = await fetchComponent2(componentName, config.registry);
|
|
5262
|
+
ensureDir(componentDir);
|
|
5263
|
+
const componentRelDir = path4.join(config.componentDir, component.name);
|
|
5264
|
+
for (const file of component.files) {
|
|
5265
|
+
const fileName = path4.basename(file.path);
|
|
5266
|
+
const filePath = safePath(componentDir, fileName);
|
|
5267
|
+
let content = file.content;
|
|
5268
|
+
if (config.theme && (content.includes("pdfx-theme") || content.includes("pdfx-theme-context"))) {
|
|
5269
|
+
content = resolveThemeImport(componentRelDir, config.theme, content);
|
|
5270
|
+
}
|
|
5271
|
+
if (checkFileExists(filePath) && !force) {
|
|
5272
|
+
peerWarnings.push(
|
|
5273
|
+
`${componentName}: skipped writing ${fileName} because it already exists (use --force to overwrite)`
|
|
5274
|
+
);
|
|
5275
|
+
continue;
|
|
5276
|
+
}
|
|
5277
|
+
writeFile(filePath, content);
|
|
5278
|
+
}
|
|
5279
|
+
installedPeers.push(componentName);
|
|
5280
|
+
}
|
|
5281
|
+
return { installedPeers, peerWarnings };
|
|
5282
|
+
}
|
|
5283
|
+
async function installBlock(name, config, force) {
|
|
5284
|
+
const block = await fetchBlock(name, config.registry);
|
|
5285
|
+
const peerResult = await ensurePeerComponents(block, config, force);
|
|
5286
|
+
const blockBaseDir = path4.resolve(process.cwd(), config.blockDir ?? DEFAULTS.BLOCK_DIR);
|
|
5287
|
+
const blockDir = path4.join(blockBaseDir, block.name);
|
|
5288
|
+
ensureDir(blockDir);
|
|
5289
|
+
const filesToWrite = [];
|
|
5290
|
+
for (const file of block.files) {
|
|
5291
|
+
const fileName = path4.basename(file.path);
|
|
5292
|
+
const filePath = safePath(blockDir, fileName);
|
|
5293
|
+
let content = file.content;
|
|
5294
|
+
if (/\.(tsx?|jsx?)$/.test(fileName) && content.includes("../../")) {
|
|
5295
|
+
content = resolveBlockImports(content, block.name, config);
|
|
5296
|
+
}
|
|
5297
|
+
filesToWrite.push({ filePath, content });
|
|
5298
|
+
}
|
|
5299
|
+
if (!force) {
|
|
5300
|
+
let globalDecision = null;
|
|
5301
|
+
const resolved = [];
|
|
5302
|
+
for (const file of filesToWrite) {
|
|
5303
|
+
if (checkFileExists(file.filePath)) {
|
|
5304
|
+
const decision = await resolveConflict(path4.basename(file.filePath), globalDecision);
|
|
5305
|
+
if (decision === "overwrite-all") {
|
|
5306
|
+
globalDecision = "overwrite-all";
|
|
5307
|
+
}
|
|
5308
|
+
resolved.push({ ...file, skip: decision === "skip" });
|
|
5309
|
+
} else {
|
|
5310
|
+
resolved.push({ ...file, skip: false });
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
for (const file of resolved) {
|
|
5314
|
+
if (!file.skip) {
|
|
5315
|
+
writeFile(file.filePath, file.content);
|
|
5316
|
+
} else {
|
|
5317
|
+
console.log(chalk2.dim(` skipped ${path4.basename(file.filePath)}`));
|
|
5318
|
+
}
|
|
5319
|
+
}
|
|
5320
|
+
} else {
|
|
5321
|
+
for (const file of filesToWrite) {
|
|
5322
|
+
writeFile(file.filePath, file.content);
|
|
5323
|
+
}
|
|
5324
|
+
}
|
|
5325
|
+
if (block.peerComponents && block.peerComponents.length > 0) {
|
|
5326
|
+
const componentBaseDir = path4.resolve(process.cwd(), config.componentDir);
|
|
5327
|
+
const missing = [];
|
|
5328
|
+
for (const comp of block.peerComponents) {
|
|
5329
|
+
const compDir = path4.join(componentBaseDir, comp);
|
|
5330
|
+
if (!checkFileExists(compDir)) {
|
|
5331
|
+
missing.push(comp);
|
|
5332
|
+
}
|
|
5333
|
+
}
|
|
5334
|
+
if (missing.length > 0) {
|
|
5335
|
+
console.log();
|
|
5336
|
+
console.log(chalk2.yellow(" Missing peer components:"));
|
|
5337
|
+
for (const comp of missing) {
|
|
5338
|
+
console.log(chalk2.dim(` ${comp} \u2192 run: ${chalk2.cyan(`pdfx add ${comp}`)}`));
|
|
5339
|
+
}
|
|
5340
|
+
}
|
|
5341
|
+
}
|
|
5342
|
+
if (config.theme) {
|
|
5343
|
+
const absThemePath = path4.resolve(process.cwd(), config.theme);
|
|
5344
|
+
const contextPath = path4.join(path4.dirname(absThemePath), "pdfx-theme-context.tsx");
|
|
5345
|
+
if (!checkFileExists(contextPath)) {
|
|
5346
|
+
ensureDir(path4.dirname(contextPath));
|
|
5347
|
+
writeFile(contextPath, generateThemeContextFile());
|
|
5348
|
+
}
|
|
5349
|
+
}
|
|
5350
|
+
return peerResult;
|
|
5351
|
+
}
|
|
5352
|
+
async function blockAdd(names, options = {}) {
|
|
5353
|
+
const configPath = path4.join(process.cwd(), "pdfx.json");
|
|
5354
|
+
if (!checkFileExists(configPath)) {
|
|
5355
|
+
console.error(chalk2.red("Error: pdfx.json not found"));
|
|
5356
|
+
console.log(chalk2.yellow("Run: pdfx init"));
|
|
5357
|
+
process.exit(1);
|
|
5358
|
+
}
|
|
5359
|
+
let config;
|
|
5360
|
+
try {
|
|
5361
|
+
config = readConfig2(configPath);
|
|
5362
|
+
} catch (error) {
|
|
5363
|
+
if (error instanceof ConfigError) {
|
|
5364
|
+
console.error(chalk2.red(error.message));
|
|
5365
|
+
if (error.suggestion) console.log(chalk2.yellow(` Hint: ${error.suggestion}`));
|
|
5366
|
+
} else {
|
|
5367
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5368
|
+
console.error(chalk2.red(message));
|
|
5369
|
+
}
|
|
5370
|
+
process.exit(1);
|
|
5371
|
+
}
|
|
5372
|
+
const force = options.force ?? false;
|
|
5373
|
+
const failed = [];
|
|
5374
|
+
for (const blockName of names) {
|
|
5375
|
+
const nameResult = componentNameSchema.safeParse(blockName);
|
|
5376
|
+
if (!nameResult.success) {
|
|
5377
|
+
console.error(chalk2.red(`Invalid block name: "${blockName}"`));
|
|
5378
|
+
console.log(
|
|
5379
|
+
chalk2.dim(' Names must be lowercase alphanumeric with hyphens (e.g., "invoice-classic")')
|
|
5380
|
+
);
|
|
5381
|
+
failed.push(blockName);
|
|
5382
|
+
continue;
|
|
5383
|
+
}
|
|
5384
|
+
const spinner = ora2(`Adding block ${blockName}...`).start();
|
|
5385
|
+
try {
|
|
5386
|
+
const result = await installBlock(blockName, config, force);
|
|
5387
|
+
spinner.succeed(`Added block ${chalk2.cyan(blockName)}`);
|
|
5388
|
+
if (result.installedPeers.length > 0) {
|
|
5389
|
+
console.log(
|
|
5390
|
+
chalk2.green(` Installed required components: ${result.installedPeers.join(", ")}`)
|
|
5391
|
+
);
|
|
5392
|
+
}
|
|
5393
|
+
for (const warning of result.peerWarnings) {
|
|
5394
|
+
console.log(chalk2.yellow(` Warning: ${warning}`));
|
|
5395
|
+
}
|
|
5396
|
+
} catch (error) {
|
|
5397
|
+
if (error instanceof ValidationError && error.message.includes("Cancelled")) {
|
|
5398
|
+
spinner.info("Cancelled");
|
|
5399
|
+
process.exit(0);
|
|
5400
|
+
} else if (error instanceof NetworkError || error instanceof RegistryError || error instanceof ValidationError) {
|
|
5401
|
+
spinner.fail(error.message);
|
|
5402
|
+
if (error.suggestion) {
|
|
5403
|
+
console.log(chalk2.dim(` Hint: ${error.suggestion}`));
|
|
5404
|
+
}
|
|
5405
|
+
} else {
|
|
5406
|
+
spinner.fail(`Failed to add block ${blockName}`);
|
|
5407
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5408
|
+
console.error(chalk2.dim(` ${message}`));
|
|
5409
|
+
}
|
|
5410
|
+
failed.push(blockName);
|
|
5411
|
+
}
|
|
5412
|
+
}
|
|
5413
|
+
console.log();
|
|
5414
|
+
if (failed.length > 0) {
|
|
5415
|
+
console.log(chalk2.yellow(`Failed: ${failed.join(", ")}`));
|
|
5416
|
+
}
|
|
5417
|
+
if (failed.length < names.length) {
|
|
5418
|
+
const resolvedDir = path4.resolve(process.cwd(), config.blockDir ?? DEFAULTS.BLOCK_DIR);
|
|
5419
|
+
console.log(chalk2.green("Done!"));
|
|
5420
|
+
console.log(chalk2.dim(`Blocks installed to: ${resolvedDir}
|
|
5421
|
+
`));
|
|
5422
|
+
}
|
|
5423
|
+
if (failed.length > 0) {
|
|
5424
|
+
process.exit(1);
|
|
5425
|
+
}
|
|
5426
|
+
}
|
|
5427
|
+
async function blockList() {
|
|
5099
5428
|
const configPath = path4.join(process.cwd(), "pdfx.json");
|
|
5100
5429
|
if (!checkFileExists(configPath)) {
|
|
5101
5430
|
console.error(chalk2.red("Error: pdfx.json not found"));
|
|
@@ -5109,14 +5438,85 @@ async function diff(components) {
|
|
|
5109
5438
|
process.exit(1);
|
|
5110
5439
|
}
|
|
5111
5440
|
const config = configResult.data;
|
|
5112
|
-
const
|
|
5441
|
+
const spinner = ora2("Fetching block list...").start();
|
|
5442
|
+
try {
|
|
5443
|
+
let response;
|
|
5444
|
+
try {
|
|
5445
|
+
response = await fetch(`${config.registry}/index.json`, {
|
|
5446
|
+
signal: AbortSignal.timeout(1e4)
|
|
5447
|
+
});
|
|
5448
|
+
} catch (err) {
|
|
5449
|
+
const isTimeout = err instanceof Error && err.name === "TimeoutError";
|
|
5450
|
+
throw new NetworkError(
|
|
5451
|
+
isTimeout ? "Registry request timed out" : `Could not reach ${config.registry}`
|
|
5452
|
+
);
|
|
5453
|
+
}
|
|
5454
|
+
if (!response.ok) {
|
|
5455
|
+
throw new RegistryError(`Registry returned HTTP ${response.status}`);
|
|
5456
|
+
}
|
|
5457
|
+
const data = await response.json();
|
|
5458
|
+
const result = registrySchema.safeParse(data);
|
|
5459
|
+
if (!result.success) {
|
|
5460
|
+
throw new RegistryError("Invalid registry format");
|
|
5461
|
+
}
|
|
5462
|
+
spinner.stop();
|
|
5463
|
+
const blocks = result.data.items.filter((item) => item.type === "registry:block");
|
|
5464
|
+
if (blocks.length === 0) {
|
|
5465
|
+
console.log(chalk2.dim("\n No blocks available in the registry.\n"));
|
|
5466
|
+
return;
|
|
5467
|
+
}
|
|
5468
|
+
const blockBaseDir = path4.resolve(process.cwd(), config.blockDir ?? DEFAULTS.BLOCK_DIR);
|
|
5469
|
+
console.log(chalk2.bold(`
|
|
5470
|
+
Available Blocks (${blocks.length})
|
|
5471
|
+
`));
|
|
5472
|
+
for (const item of blocks) {
|
|
5473
|
+
const blockDir = path4.join(blockBaseDir, item.name);
|
|
5474
|
+
const installed = checkFileExists(blockDir);
|
|
5475
|
+
const status = installed ? chalk2.green("[installed]") : chalk2.dim("[not installed]");
|
|
5476
|
+
console.log(` ${chalk2.cyan(item.name.padEnd(22))} ${item.description ?? ""}`);
|
|
5477
|
+
console.log(` ${"".padEnd(22)} ${status}`);
|
|
5478
|
+
if (item.peerComponents && item.peerComponents.length > 0) {
|
|
5479
|
+
console.log(chalk2.dim(` ${"".padEnd(22)} requires: ${item.peerComponents.join(", ")}`));
|
|
5480
|
+
}
|
|
5481
|
+
console.log();
|
|
5482
|
+
}
|
|
5483
|
+
console.log(chalk2.dim(` Install with: ${chalk2.cyan("pdfx block add <block-name>")}
|
|
5484
|
+
`));
|
|
5485
|
+
} catch (error) {
|
|
5486
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5487
|
+
spinner.fail(message);
|
|
5488
|
+
process.exit(1);
|
|
5489
|
+
}
|
|
5490
|
+
}
|
|
5491
|
+
|
|
5492
|
+
// src/commands/diff.ts
|
|
5493
|
+
import fs4 from "fs";
|
|
5494
|
+
import path5 from "path";
|
|
5495
|
+
import chalk3 from "chalk";
|
|
5496
|
+
import ora3 from "ora";
|
|
5497
|
+
var FETCH_TIMEOUT_MS = 1e4;
|
|
5498
|
+
async function diff(components) {
|
|
5499
|
+
const configPath = path5.join(process.cwd(), "pdfx.json");
|
|
5500
|
+
if (!checkFileExists(configPath)) {
|
|
5501
|
+
console.error(chalk3.red("Error: pdfx.json not found"));
|
|
5502
|
+
console.log(chalk3.yellow("Run: pdfx init"));
|
|
5503
|
+
process.exit(1);
|
|
5504
|
+
}
|
|
5505
|
+
const raw = readJsonFile(configPath);
|
|
5506
|
+
const configResult = configSchema.safeParse(raw);
|
|
5507
|
+
if (!configResult.success) {
|
|
5508
|
+
console.error(chalk3.red("Invalid pdfx.json"));
|
|
5509
|
+
process.exit(1);
|
|
5510
|
+
}
|
|
5511
|
+
const config = configResult.data;
|
|
5512
|
+
const targetDir = path5.resolve(process.cwd(), config.componentDir);
|
|
5113
5513
|
for (const componentName of components) {
|
|
5114
5514
|
const nameResult = componentNameSchema.safeParse(componentName);
|
|
5115
5515
|
if (!nameResult.success) {
|
|
5116
|
-
console.error(
|
|
5516
|
+
console.error(chalk3.red(`Invalid component name: "${componentName}"`));
|
|
5117
5517
|
continue;
|
|
5118
5518
|
}
|
|
5119
|
-
const spinner =
|
|
5519
|
+
const spinner = ora3(`Comparing ${componentName}...`).start();
|
|
5120
5520
|
try {
|
|
5121
5521
|
let response;
|
|
5122
5522
|
try {
|
|
@@ -5127,8 +5527,8 @@ async function diff(components) {
|
|
|
5127
5527
|
const isTimeout = err instanceof Error && err.name === "TimeoutError";
|
|
5128
5528
|
throw new NetworkError(
|
|
5129
5529
|
isTimeout ? `Registry request timed out after 10 seconds.
|
|
5130
|
-
${
|
|
5131
|
-
${
|
|
5530
|
+
${chalk3.dim("Check your internet connection or try again later.")}` : `Could not reach registry at ${config.registry}
|
|
5531
|
+
${chalk3.dim("Verify the URL is correct and you have internet access.")}`
|
|
5132
5532
|
);
|
|
5133
5533
|
}
|
|
5134
5534
|
if (!response.ok) {
|
|
@@ -5143,32 +5543,32 @@ async function diff(components) {
|
|
|
5143
5543
|
}
|
|
5144
5544
|
const component = result.data;
|
|
5145
5545
|
spinner.stop();
|
|
5146
|
-
const componentSubDir =
|
|
5546
|
+
const componentSubDir = path5.join(targetDir, component.name);
|
|
5147
5547
|
for (const file of component.files) {
|
|
5148
|
-
const fileName =
|
|
5548
|
+
const fileName = path5.basename(file.path);
|
|
5149
5549
|
const localPath = safePath(componentSubDir, fileName);
|
|
5150
5550
|
if (!checkFileExists(localPath)) {
|
|
5151
|
-
console.log(
|
|
5551
|
+
console.log(chalk3.yellow(` ${fileName}: not installed locally`));
|
|
5152
5552
|
continue;
|
|
5153
5553
|
}
|
|
5154
5554
|
const localContent = fs4.readFileSync(localPath, "utf-8");
|
|
5155
5555
|
const registryContent = config.theme && (file.content.includes("pdfx-theme") || file.content.includes("pdfx-theme-context")) ? resolveThemeImport(
|
|
5156
|
-
|
|
5556
|
+
path5.join(config.componentDir, component.name),
|
|
5157
5557
|
config.theme,
|
|
5158
5558
|
file.content
|
|
5159
5559
|
) : file.content;
|
|
5160
5560
|
if (localContent === registryContent) {
|
|
5161
|
-
console.log(
|
|
5561
|
+
console.log(chalk3.green(` ${fileName}: up to date`));
|
|
5162
5562
|
} else {
|
|
5163
|
-
console.log(
|
|
5563
|
+
console.log(chalk3.yellow(` ${fileName}: differs from registry`));
|
|
5164
5564
|
const localLines = localContent.split("\n");
|
|
5165
5565
|
const registryLines = registryContent.split("\n");
|
|
5166
5566
|
const lineDiff = localLines.length - registryLines.length;
|
|
5167
|
-
console.log(
|
|
5168
|
-
console.log(
|
|
5567
|
+
console.log(chalk3.dim(` Local: ${localLines.length} lines`));
|
|
5568
|
+
console.log(chalk3.dim(` Registry: ${registryLines.length} lines`));
|
|
5169
5569
|
if (lineDiff !== 0) {
|
|
5170
5570
|
const diffText = lineDiff > 0 ? `${Math.abs(lineDiff)} line${Math.abs(lineDiff) > 1 ? "s" : ""} added locally` : `${Math.abs(lineDiff)} line${Math.abs(lineDiff) > 1 ? "s" : ""} removed locally`;
|
|
5171
|
-
console.log(
|
|
5571
|
+
console.log(chalk3.dim(` \u2192 ${diffText}`));
|
|
5172
5572
|
}
|
|
5173
5573
|
}
|
|
5174
5574
|
}
|
|
@@ -5182,32 +5582,20 @@ async function diff(components) {
|
|
|
5182
5582
|
|
|
5183
5583
|
// src/commands/init.ts
|
|
5184
5584
|
import fs7 from "fs";
|
|
5185
|
-
import
|
|
5186
|
-
import
|
|
5187
|
-
import
|
|
5188
|
-
import
|
|
5189
|
-
|
|
5190
|
-
// src/constants.ts
|
|
5191
|
-
var DEFAULTS = {
|
|
5192
|
-
REGISTRY_URL: "https://pdfx.akashpise.dev/r",
|
|
5193
|
-
SCHEMA_URL: "https://pdfx.akashpise.dev/schema.json",
|
|
5194
|
-
COMPONENT_DIR: "./src/components/pdfx",
|
|
5195
|
-
THEME_FILE: "./src/lib/pdfx-theme.ts",
|
|
5196
|
-
TEMPLATE_DIR: "./src/templates/pdfx"
|
|
5197
|
-
};
|
|
5198
|
-
var REGISTRY_SUBPATHS = {
|
|
5199
|
-
TEMPLATES: "templates"
|
|
5200
|
-
};
|
|
5585
|
+
import path8 from "path";
|
|
5586
|
+
import chalk6 from "chalk";
|
|
5587
|
+
import ora5 from "ora";
|
|
5588
|
+
import prompts4 from "prompts";
|
|
5201
5589
|
|
|
5202
5590
|
// src/utils/install-dependencies.ts
|
|
5203
|
-
import
|
|
5591
|
+
import chalk4 from "chalk";
|
|
5204
5592
|
import { execa } from "execa";
|
|
5205
|
-
import
|
|
5206
|
-
import
|
|
5593
|
+
import ora4 from "ora";
|
|
5594
|
+
import prompts3 from "prompts";
|
|
5207
5595
|
|
|
5208
5596
|
// src/utils/package-manager.ts
|
|
5209
5597
|
import fs5 from "fs";
|
|
5210
|
-
import
|
|
5598
|
+
import path6 from "path";
|
|
5211
5599
|
var PACKAGE_MANAGERS = {
|
|
5212
5600
|
pnpm: {
|
|
5213
5601
|
name: "pnpm",
|
|
@@ -5234,7 +5622,7 @@ function detectPackageManager(cwd = process.cwd()) {
|
|
|
5234
5622
|
const managers = ["pnpm", "yarn", "bun", "npm"];
|
|
5235
5623
|
for (const manager of managers) {
|
|
5236
5624
|
const info = PACKAGE_MANAGERS[manager];
|
|
5237
|
-
const lockfilePath =
|
|
5625
|
+
const lockfilePath = path6.join(cwd, info.lockfile);
|
|
5238
5626
|
if (fs5.existsSync(lockfilePath)) {
|
|
5239
5627
|
return info;
|
|
5240
5628
|
}
|
|
@@ -5258,10 +5646,10 @@ async function promptAndInstallReactPdf(validation, cwd = process.cwd()) {
|
|
|
5258
5646
|
const pm = detectPackageManager(cwd);
|
|
5259
5647
|
const packageName = "@react-pdf/renderer";
|
|
5260
5648
|
const installCmd = getInstallCommand(pm.name, [packageName]);
|
|
5261
|
-
console.log(
|
|
5262
|
-
console.log(
|
|
5649
|
+
console.log(chalk4.yellow("\n \u26A0 @react-pdf/renderer is required but not installed\n"));
|
|
5650
|
+
console.log(chalk4.dim(` This command will run: ${installCmd}
|
|
5263
5651
|
`));
|
|
5264
|
-
const { shouldInstall } = await
|
|
5652
|
+
const { shouldInstall } = await prompts3({
|
|
5265
5653
|
type: "confirm",
|
|
5266
5654
|
name: "shouldInstall",
|
|
5267
5655
|
message: "Install @react-pdf/renderer now?",
|
|
@@ -5271,10 +5659,10 @@ async function promptAndInstallReactPdf(validation, cwd = process.cwd()) {
|
|
|
5271
5659
|
return {
|
|
5272
5660
|
success: false,
|
|
5273
5661
|
message: `Installation cancelled. Please install manually:
|
|
5274
|
-
${
|
|
5662
|
+
${chalk4.cyan(installCmd)}`
|
|
5275
5663
|
};
|
|
5276
5664
|
}
|
|
5277
|
-
const spinner =
|
|
5665
|
+
const spinner = ora4("Installing @react-pdf/renderer...").start();
|
|
5278
5666
|
try {
|
|
5279
5667
|
await execa(pm.name, ["add", packageName], {
|
|
5280
5668
|
cwd,
|
|
@@ -5291,7 +5679,7 @@ async function promptAndInstallReactPdf(validation, cwd = process.cwd()) {
|
|
|
5291
5679
|
return {
|
|
5292
5680
|
success: false,
|
|
5293
5681
|
message: `Installation failed: ${message}
|
|
5294
|
-
Try manually: ${
|
|
5682
|
+
Try manually: ${chalk4.cyan(installCmd)}`
|
|
5295
5683
|
};
|
|
5296
5684
|
}
|
|
5297
5685
|
}
|
|
@@ -5299,7 +5687,7 @@ async function ensureReactPdfRenderer(validation, cwd = process.cwd()) {
|
|
|
5299
5687
|
if (!validation.installed) {
|
|
5300
5688
|
const result = await promptAndInstallReactPdf(validation, cwd);
|
|
5301
5689
|
if (!result.success) {
|
|
5302
|
-
console.error(
|
|
5690
|
+
console.error(chalk4.red(`
|
|
5303
5691
|
${result.message}
|
|
5304
5692
|
`));
|
|
5305
5693
|
return false;
|
|
@@ -5308,10 +5696,10 @@ async function ensureReactPdfRenderer(validation, cwd = process.cwd()) {
|
|
|
5308
5696
|
}
|
|
5309
5697
|
if (!validation.valid) {
|
|
5310
5698
|
console.log(
|
|
5311
|
-
|
|
5699
|
+
chalk4.yellow(
|
|
5312
5700
|
`
|
|
5313
5701
|
\u26A0 ${validation.message}
|
|
5314
|
-
${
|
|
5702
|
+
${chalk4.dim("\u2192")} You may encounter compatibility issues
|
|
5315
5703
|
`
|
|
5316
5704
|
)
|
|
5317
5705
|
);
|
|
@@ -5320,13 +5708,13 @@ async function ensureReactPdfRenderer(validation, cwd = process.cwd()) {
|
|
|
5320
5708
|
}
|
|
5321
5709
|
|
|
5322
5710
|
// src/utils/pre-flight.ts
|
|
5323
|
-
import
|
|
5711
|
+
import chalk5 from "chalk";
|
|
5324
5712
|
|
|
5325
5713
|
// src/utils/environment-validator.ts
|
|
5326
5714
|
import fs6 from "fs";
|
|
5327
|
-
import
|
|
5715
|
+
import path7 from "path";
|
|
5328
5716
|
function validatePackageJson(cwd = process.cwd()) {
|
|
5329
|
-
const pkgPath =
|
|
5717
|
+
const pkgPath = path7.join(cwd, "package.json");
|
|
5330
5718
|
const exists = fs6.existsSync(pkgPath);
|
|
5331
5719
|
return {
|
|
5332
5720
|
valid: exists,
|
|
@@ -5335,7 +5723,7 @@ function validatePackageJson(cwd = process.cwd()) {
|
|
|
5335
5723
|
};
|
|
5336
5724
|
}
|
|
5337
5725
|
function validateReactProject(cwd = process.cwd()) {
|
|
5338
|
-
const pkgPath =
|
|
5726
|
+
const pkgPath = path7.join(cwd, "package.json");
|
|
5339
5727
|
if (!fs6.existsSync(pkgPath)) {
|
|
5340
5728
|
return {
|
|
5341
5729
|
valid: false,
|
|
@@ -5362,7 +5750,7 @@ function validateReactProject(cwd = process.cwd()) {
|
|
|
5362
5750
|
}
|
|
5363
5751
|
}
|
|
5364
5752
|
function validatePdfxConfig(cwd = process.cwd()) {
|
|
5365
|
-
const configPath =
|
|
5753
|
+
const configPath = path7.join(cwd, "pdfx.json");
|
|
5366
5754
|
const exists = fs6.existsSync(configPath);
|
|
5367
5755
|
return {
|
|
5368
5756
|
valid: true,
|
|
@@ -5387,43 +5775,43 @@ function runPreFlightChecks(cwd = process.cwd()) {
|
|
|
5387
5775
|
if (!environment.hasPackageJson.valid) {
|
|
5388
5776
|
blockingErrors.push(
|
|
5389
5777
|
`${environment.hasPackageJson.message}
|
|
5390
|
-
${
|
|
5778
|
+
${chalk5.dim("\u2192")} ${environment.hasPackageJson.fixCommand}`
|
|
5391
5779
|
);
|
|
5392
5780
|
} else if (!environment.isReactProject.valid) {
|
|
5393
5781
|
blockingErrors.push(
|
|
5394
5782
|
`${environment.isReactProject.message}
|
|
5395
|
-
${
|
|
5783
|
+
${chalk5.dim("\u2192")} ${environment.isReactProject.fixCommand}`
|
|
5396
5784
|
);
|
|
5397
5785
|
} else {
|
|
5398
5786
|
if (!dependencies.react.valid && dependencies.react.installed) {
|
|
5399
5787
|
warnings.push(
|
|
5400
5788
|
`${dependencies.react.message}
|
|
5401
|
-
${
|
|
5789
|
+
${chalk5.dim("\u2192")} Current: ${dependencies.react.currentVersion}, Required: ${dependencies.react.requiredVersion}`
|
|
5402
5790
|
);
|
|
5403
5791
|
} else if (!dependencies.react.installed) {
|
|
5404
5792
|
blockingErrors.push(
|
|
5405
5793
|
`${dependencies.react.message}
|
|
5406
|
-
${
|
|
5794
|
+
${chalk5.dim("\u2192")} Install React: npm install react react-dom`
|
|
5407
5795
|
);
|
|
5408
5796
|
}
|
|
5409
5797
|
}
|
|
5410
5798
|
if (!dependencies.nodeJs.valid) {
|
|
5411
5799
|
blockingErrors.push(
|
|
5412
5800
|
`${dependencies.nodeJs.message}
|
|
5413
|
-
${
|
|
5414
|
-
${
|
|
5801
|
+
${chalk5.dim("\u2192")} Current: ${dependencies.nodeJs.currentVersion}, Required: ${dependencies.nodeJs.requiredVersion}
|
|
5802
|
+
${chalk5.dim("\u2192")} Visit https://nodejs.org to upgrade`
|
|
5415
5803
|
);
|
|
5416
5804
|
}
|
|
5417
5805
|
if (dependencies.reactPdfRenderer.installed && !dependencies.reactPdfRenderer.valid) {
|
|
5418
5806
|
warnings.push(
|
|
5419
5807
|
`${dependencies.reactPdfRenderer.message}
|
|
5420
|
-
${
|
|
5808
|
+
${chalk5.dim("\u2192")} Consider upgrading: npm install @react-pdf/renderer@latest`
|
|
5421
5809
|
);
|
|
5422
5810
|
}
|
|
5423
5811
|
if (dependencies.typescript && !dependencies.typescript.valid) {
|
|
5424
5812
|
warnings.push(
|
|
5425
5813
|
`${dependencies.typescript.message}
|
|
5426
|
-
${
|
|
5814
|
+
${chalk5.dim("\u2192")} Install types: npm install -D @types/react-pdf`
|
|
5427
5815
|
);
|
|
5428
5816
|
}
|
|
5429
5817
|
return {
|
|
@@ -5435,58 +5823,58 @@ function runPreFlightChecks(cwd = process.cwd()) {
|
|
|
5435
5823
|
};
|
|
5436
5824
|
}
|
|
5437
5825
|
function displayPreFlightResults(result) {
|
|
5438
|
-
console.log(
|
|
5826
|
+
console.log(chalk5.bold("\n Pre-flight Checks:\n"));
|
|
5439
5827
|
if (result.blockingErrors.length > 0) {
|
|
5440
|
-
console.log(
|
|
5828
|
+
console.log(chalk5.red(" \u2717 Blocking Issues:\n"));
|
|
5441
5829
|
for (const error of result.blockingErrors) {
|
|
5442
|
-
console.log(
|
|
5830
|
+
console.log(chalk5.red(` \u2022 ${error}
|
|
5443
5831
|
`));
|
|
5444
5832
|
}
|
|
5445
5833
|
}
|
|
5446
5834
|
if (result.warnings.length > 0) {
|
|
5447
|
-
console.log(
|
|
5835
|
+
console.log(chalk5.yellow(" \u26A0 Warnings:\n"));
|
|
5448
5836
|
for (const warning of result.warnings) {
|
|
5449
|
-
console.log(
|
|
5837
|
+
console.log(chalk5.yellow(` \u2022 ${warning}
|
|
5450
5838
|
`));
|
|
5451
5839
|
}
|
|
5452
5840
|
}
|
|
5453
5841
|
if (result.blockingErrors.length === 0 && result.warnings.length === 0) {
|
|
5454
|
-
console.log(
|
|
5842
|
+
console.log(chalk5.green(" \u2713 All checks passed!\n"));
|
|
5455
5843
|
}
|
|
5456
5844
|
}
|
|
5457
5845
|
|
|
5458
5846
|
// src/commands/init.ts
|
|
5459
5847
|
async function init() {
|
|
5460
|
-
console.log(
|
|
5848
|
+
console.log(chalk6.bold.cyan("\n Welcome to the pdfx cli\n"));
|
|
5461
5849
|
const preFlightResult = runPreFlightChecks();
|
|
5462
5850
|
displayPreFlightResults(preFlightResult);
|
|
5463
5851
|
if (!preFlightResult.canProceed) {
|
|
5464
5852
|
console.error(
|
|
5465
|
-
|
|
5853
|
+
chalk6.red("\n Cannot proceed due to blocking issues. Please fix them and try again.\n")
|
|
5466
5854
|
);
|
|
5467
5855
|
process.exit(1);
|
|
5468
5856
|
}
|
|
5469
5857
|
const hasReactPdf = await ensureReactPdfRenderer(preFlightResult.dependencies.reactPdfRenderer);
|
|
5470
5858
|
if (!hasReactPdf) {
|
|
5471
5859
|
console.error(
|
|
5472
|
-
|
|
5860
|
+
chalk6.red("\n @react-pdf/renderer is required. Please install it and try again.\n")
|
|
5473
5861
|
);
|
|
5474
5862
|
process.exit(1);
|
|
5475
5863
|
}
|
|
5476
|
-
const existingConfig =
|
|
5864
|
+
const existingConfig = path8.join(process.cwd(), "pdfx.json");
|
|
5477
5865
|
if (fs7.existsSync(existingConfig)) {
|
|
5478
|
-
const { overwrite } = await
|
|
5866
|
+
const { overwrite } = await prompts4({
|
|
5479
5867
|
type: "confirm",
|
|
5480
5868
|
name: "overwrite",
|
|
5481
5869
|
message: "pdfx.json already exists. Overwrite?",
|
|
5482
5870
|
initial: false
|
|
5483
5871
|
});
|
|
5484
5872
|
if (!overwrite) {
|
|
5485
|
-
console.log(
|
|
5873
|
+
console.log(chalk6.yellow("Init cancelled \u2014 existing config preserved."));
|
|
5486
5874
|
return;
|
|
5487
5875
|
}
|
|
5488
5876
|
}
|
|
5489
|
-
const answers = await
|
|
5877
|
+
const answers = await prompts4(
|
|
5490
5878
|
[
|
|
5491
5879
|
{
|
|
5492
5880
|
type: "text",
|
|
@@ -5497,7 +5885,7 @@ async function init() {
|
|
|
5497
5885
|
if (!value || value.trim().length === 0) {
|
|
5498
5886
|
return "Component directory is required";
|
|
5499
5887
|
}
|
|
5500
|
-
if (
|
|
5888
|
+
if (path8.isAbsolute(value)) {
|
|
5501
5889
|
return "Please use a relative path (e.g., ./src/components/pdfx)";
|
|
5502
5890
|
}
|
|
5503
5891
|
if (!value.startsWith(".")) {
|
|
@@ -5506,6 +5894,24 @@ async function init() {
|
|
|
5506
5894
|
return true;
|
|
5507
5895
|
}
|
|
5508
5896
|
},
|
|
5897
|
+
{
|
|
5898
|
+
type: "text",
|
|
5899
|
+
name: "blockDir",
|
|
5900
|
+
message: "Where should we install blocks?",
|
|
5901
|
+
initial: DEFAULTS.BLOCK_DIR,
|
|
5902
|
+
validate: (value) => {
|
|
5903
|
+
if (!value || value.trim().length === 0) {
|
|
5904
|
+
return "Block directory is required";
|
|
5905
|
+
}
|
|
5906
|
+
if (path8.isAbsolute(value)) {
|
|
5907
|
+
return "Please use a relative path (e.g., ./src/blocks/pdfx)";
|
|
5908
|
+
}
|
|
5909
|
+
if (!value.startsWith(".")) {
|
|
5910
|
+
return "Path should start with ./ or ../ (e.g., ./src/blocks/pdfx)";
|
|
5911
|
+
}
|
|
5912
|
+
return true;
|
|
5913
|
+
}
|
|
5914
|
+
},
|
|
5509
5915
|
{
|
|
5510
5916
|
type: "text",
|
|
5511
5917
|
name: "registry",
|
|
@@ -5550,7 +5956,7 @@ async function init() {
|
|
|
5550
5956
|
if (!value || value.trim().length === 0) {
|
|
5551
5957
|
return "Theme path is required";
|
|
5552
5958
|
}
|
|
5553
|
-
if (
|
|
5959
|
+
if (path8.isAbsolute(value)) {
|
|
5554
5960
|
return "Please use a relative path (e.g., ./src/lib/pdfx-theme.ts)";
|
|
5555
5961
|
}
|
|
5556
5962
|
if (!value.endsWith(".ts") && !value.endsWith(".tsx")) {
|
|
@@ -5562,68 +5968,71 @@ async function init() {
|
|
|
5562
5968
|
],
|
|
5563
5969
|
{
|
|
5564
5970
|
onCancel: () => {
|
|
5565
|
-
console.log(
|
|
5971
|
+
console.log(chalk6.yellow("\nSetup cancelled."));
|
|
5566
5972
|
process.exit(0);
|
|
5567
5973
|
}
|
|
5568
5974
|
}
|
|
5569
5975
|
);
|
|
5570
5976
|
if (!answers.componentDir || !answers.registry) {
|
|
5571
|
-
console.error(
|
|
5977
|
+
console.error(chalk6.red("Missing required fields. Run pdfx init again."));
|
|
5572
5978
|
process.exit(1);
|
|
5573
5979
|
}
|
|
5574
5980
|
const config = {
|
|
5575
5981
|
$schema: DEFAULTS.SCHEMA_URL,
|
|
5576
5982
|
componentDir: answers.componentDir,
|
|
5577
5983
|
registry: answers.registry,
|
|
5578
|
-
theme: answers.themePath || DEFAULTS.THEME_FILE
|
|
5984
|
+
theme: answers.themePath || DEFAULTS.THEME_FILE,
|
|
5985
|
+
blockDir: answers.blockDir || DEFAULTS.BLOCK_DIR
|
|
5579
5986
|
};
|
|
5580
5987
|
const validation = configSchema.safeParse(config);
|
|
5581
5988
|
if (!validation.success) {
|
|
5582
5989
|
const issues = validation.error.issues.map((i) => i.message).join(", ");
|
|
5583
|
-
console.error(
|
|
5990
|
+
console.error(chalk6.red(`Invalid configuration: ${issues}`));
|
|
5584
5991
|
process.exit(1);
|
|
5585
5992
|
}
|
|
5586
|
-
const spinner =
|
|
5993
|
+
const spinner = ora5("Creating config and theme files...").start();
|
|
5587
5994
|
try {
|
|
5588
|
-
const componentDirPath =
|
|
5995
|
+
const componentDirPath = path8.resolve(process.cwd(), answers.componentDir);
|
|
5589
5996
|
ensureDir(componentDirPath);
|
|
5590
|
-
fs7.writeFileSync(
|
|
5997
|
+
fs7.writeFileSync(path8.join(process.cwd(), "pdfx.json"), JSON.stringify(config, null, 2));
|
|
5591
5998
|
const presetName = answers.themePreset || "professional";
|
|
5592
5999
|
const preset = themePresets[presetName];
|
|
5593
|
-
const themePath =
|
|
5594
|
-
ensureDir(
|
|
6000
|
+
const themePath = path8.resolve(process.cwd(), config.theme);
|
|
6001
|
+
ensureDir(path8.dirname(themePath));
|
|
5595
6002
|
fs7.writeFileSync(themePath, generateThemeFile(preset), "utf-8");
|
|
5596
|
-
const contextPath =
|
|
6003
|
+
const contextPath = path8.join(path8.dirname(themePath), "pdfx-theme-context.tsx");
|
|
5597
6004
|
fs7.writeFileSync(contextPath, generateThemeContextFile(), "utf-8");
|
|
5598
6005
|
spinner.succeed(`Created pdfx.json + ${config.theme} (${presetName} theme)`);
|
|
5599
|
-
console.log(
|
|
5600
|
-
console.log(
|
|
5601
|
-
console.log(
|
|
5602
|
-
|
|
5603
|
-
|
|
6006
|
+
console.log(chalk6.green("\nSuccess! You can now run:"));
|
|
6007
|
+
console.log(chalk6.cyan(" pdfx add heading"));
|
|
6008
|
+
console.log(chalk6.cyan(" pdfx block add invoice-classic"));
|
|
6009
|
+
console.log(chalk6.dim(`
|
|
6010
|
+
Components: ${path8.resolve(process.cwd(), answers.componentDir)}`));
|
|
6011
|
+
console.log(chalk6.dim(` Blocks: ${path8.resolve(process.cwd(), config.blockDir)}`));
|
|
6012
|
+
console.log(chalk6.dim(` Theme: ${path8.resolve(process.cwd(), config.theme)}
|
|
5604
6013
|
`));
|
|
5605
6014
|
} catch (error) {
|
|
5606
6015
|
spinner.fail("Failed to create config");
|
|
5607
6016
|
const message = error instanceof Error ? error.message : String(error);
|
|
5608
|
-
console.error(
|
|
6017
|
+
console.error(chalk6.dim(` ${message}`));
|
|
5609
6018
|
process.exit(1);
|
|
5610
6019
|
}
|
|
5611
6020
|
}
|
|
5612
6021
|
|
|
5613
6022
|
// src/commands/list.ts
|
|
5614
|
-
import
|
|
5615
|
-
import
|
|
5616
|
-
import
|
|
6023
|
+
import path9 from "path";
|
|
6024
|
+
import chalk7 from "chalk";
|
|
6025
|
+
import ora6 from "ora";
|
|
5617
6026
|
var FETCH_TIMEOUT_MS2 = 1e4;
|
|
5618
6027
|
async function list() {
|
|
5619
|
-
const configPath =
|
|
6028
|
+
const configPath = path9.join(process.cwd(), "pdfx.json");
|
|
5620
6029
|
let config;
|
|
5621
6030
|
let hasLocalProject = false;
|
|
5622
6031
|
if (checkFileExists(configPath)) {
|
|
5623
6032
|
const raw = readJsonFile(configPath);
|
|
5624
6033
|
const configResult = configSchema.safeParse(raw);
|
|
5625
6034
|
if (!configResult.success) {
|
|
5626
|
-
console.error(
|
|
6035
|
+
console.error(chalk7.red("Invalid pdfx.json"));
|
|
5627
6036
|
process.exit(1);
|
|
5628
6037
|
}
|
|
5629
6038
|
config = configResult.data;
|
|
@@ -5634,9 +6043,9 @@ async function list() {
|
|
|
5634
6043
|
componentDir: "./src/components/pdfx",
|
|
5635
6044
|
theme: "./src/lib/pdfx-theme.ts"
|
|
5636
6045
|
};
|
|
5637
|
-
console.log(
|
|
6046
|
+
console.log(chalk7.dim("No pdfx.json found. Listing from default registry.\n"));
|
|
5638
6047
|
}
|
|
5639
|
-
const spinner =
|
|
6048
|
+
const spinner = ora6("Fetching registry...").start();
|
|
5640
6049
|
try {
|
|
5641
6050
|
let response;
|
|
5642
6051
|
try {
|
|
@@ -5647,8 +6056,8 @@ async function list() {
|
|
|
5647
6056
|
const isTimeout = err instanceof Error && err.name === "TimeoutError";
|
|
5648
6057
|
throw new NetworkError(
|
|
5649
6058
|
isTimeout ? `Registry request timed out after 10 seconds.
|
|
5650
|
-
${
|
|
5651
|
-
${
|
|
6059
|
+
${chalk7.dim("Check your internet connection or try again later.")}` : `Could not reach registry at ${config.registry}
|
|
6060
|
+
${chalk7.dim("Verify the URL is correct and you have internet access.")}`
|
|
5652
6061
|
);
|
|
5653
6062
|
}
|
|
5654
6063
|
if (!response.ok) {
|
|
@@ -5661,23 +6070,62 @@ async function list() {
|
|
|
5661
6070
|
}
|
|
5662
6071
|
spinner.stop();
|
|
5663
6072
|
const components = result.data.items.filter((item) => item.type === "registry:ui");
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
6073
|
+
const templates = result.data.items.filter((item) => item.type === "registry:template");
|
|
6074
|
+
const blocks = result.data.items.filter((item) => item.type === "registry:block");
|
|
6075
|
+
const componentBaseDir = path9.resolve(process.cwd(), config.componentDir);
|
|
6076
|
+
const templateBaseDir = path9.resolve(process.cwd(), config.templateDir ?? "./src/templates");
|
|
6077
|
+
console.log(chalk7.bold(`
|
|
6078
|
+
Components (${components.length})`));
|
|
6079
|
+
console.log(chalk7.dim(" Install with: pdfx add <component>\n"));
|
|
5667
6080
|
for (const item of components) {
|
|
6081
|
+
const componentSubDir = path9.join(componentBaseDir, item.name);
|
|
6082
|
+
const localPath = safePath(componentSubDir, `pdfx-${item.name}.tsx`);
|
|
6083
|
+
const installed = hasLocalProject && checkFileExists(localPath);
|
|
6084
|
+
const status = installed ? chalk7.green("[installed]") : chalk7.dim("[not installed]");
|
|
6085
|
+
console.log(` ${chalk7.cyan(item.name.padEnd(20))} ${item.description}`);
|
|
5668
6086
|
if (hasLocalProject) {
|
|
5669
|
-
const targetDir = path8.resolve(process.cwd(), config.componentDir);
|
|
5670
|
-
const componentSubDir = path8.join(targetDir, item.name);
|
|
5671
|
-
const localPath = safePath(componentSubDir, `pdfx-${item.name}.tsx`);
|
|
5672
|
-
const installed = checkFileExists(localPath);
|
|
5673
|
-
const status = installed ? chalk6.green("[installed]") : chalk6.dim("[not installed]");
|
|
5674
|
-
console.log(` ${chalk6.cyan(item.name.padEnd(20))} ${item.description}`);
|
|
5675
6087
|
console.log(` ${"".padEnd(20)} ${status}`);
|
|
5676
|
-
} else {
|
|
5677
|
-
console.log(` ${chalk6.cyan(item.name.padEnd(20))} ${item.description}`);
|
|
5678
6088
|
}
|
|
5679
6089
|
console.log();
|
|
5680
6090
|
}
|
|
6091
|
+
console.log(chalk7.bold(` Templates (${templates.length})`));
|
|
6092
|
+
console.log(chalk7.dim(" Data-driven. Install with: pdfx template add <template>\n"));
|
|
6093
|
+
for (const item of templates) {
|
|
6094
|
+
const templateDir = path9.join(templateBaseDir, item.name);
|
|
6095
|
+
const installed = hasLocalProject && checkFileExists(templateDir);
|
|
6096
|
+
const status = installed ? chalk7.green("[installed]") : chalk7.dim("[not installed]");
|
|
6097
|
+
console.log(` ${chalk7.cyan(item.name.padEnd(22))} ${item.description ?? ""}`);
|
|
6098
|
+
if (hasLocalProject) {
|
|
6099
|
+
console.log(` ${"".padEnd(22)} ${status}`);
|
|
6100
|
+
}
|
|
6101
|
+
console.log();
|
|
6102
|
+
}
|
|
6103
|
+
console.log(chalk7.bold(` Blocks (${blocks.length})`));
|
|
6104
|
+
console.log(chalk7.dim(" Copy-paste designs. Install with: pdfx block add <block>\n"));
|
|
6105
|
+
for (const item of blocks) {
|
|
6106
|
+
const blockDir = path9.join(templateBaseDir, item.name);
|
|
6107
|
+
const installed = hasLocalProject && checkFileExists(blockDir);
|
|
6108
|
+
const status = installed ? chalk7.green("[installed]") : chalk7.dim("[not installed]");
|
|
6109
|
+
console.log(` ${chalk7.cyan(item.name.padEnd(22))} ${item.description ?? ""}`);
|
|
6110
|
+
if (hasLocalProject) {
|
|
6111
|
+
console.log(` ${"".padEnd(22)} ${status}`);
|
|
6112
|
+
}
|
|
6113
|
+
if (item.peerComponents && item.peerComponents.length > 0) {
|
|
6114
|
+
console.log(chalk7.dim(` ${"".padEnd(22)} requires: ${item.peerComponents.join(", ")}`));
|
|
6115
|
+
}
|
|
6116
|
+
console.log();
|
|
6117
|
+
}
|
|
6118
|
+
console.log(chalk7.dim(" Quick Start:"));
|
|
6119
|
+
console.log(
|
|
6120
|
+
chalk7.dim(` pdfx add heading table ${chalk7.dim("# Add components")}`)
|
|
6121
|
+
);
|
|
6122
|
+
console.log(
|
|
6123
|
+
chalk7.dim(` pdfx template add invoice-template ${chalk7.dim("# Add data-driven template")}`)
|
|
6124
|
+
);
|
|
6125
|
+
console.log(
|
|
6126
|
+
chalk7.dim(` pdfx block add invoice-classic ${chalk7.dim("# Add copy-paste block")}`)
|
|
6127
|
+
);
|
|
6128
|
+
console.log();
|
|
5681
6129
|
} catch (error) {
|
|
5682
6130
|
const message = error instanceof Error ? error.message : String(error);
|
|
5683
6131
|
spinner.fail(message);
|
|
@@ -5686,18 +6134,18 @@ async function list() {
|
|
|
5686
6134
|
}
|
|
5687
6135
|
|
|
5688
6136
|
// src/commands/template.ts
|
|
5689
|
-
import
|
|
5690
|
-
import
|
|
5691
|
-
import
|
|
5692
|
-
import
|
|
5693
|
-
function
|
|
6137
|
+
import path10 from "path";
|
|
6138
|
+
import chalk8 from "chalk";
|
|
6139
|
+
import ora7 from "ora";
|
|
6140
|
+
import prompts5 from "prompts";
|
|
6141
|
+
function readConfig3(configPath) {
|
|
5694
6142
|
const raw = readJsonFile(configPath);
|
|
5695
6143
|
const result = configSchema.safeParse(raw);
|
|
5696
6144
|
if (!result.success) {
|
|
5697
6145
|
const issues = result.error.issues.map((i) => i.message).join(", ");
|
|
5698
6146
|
throw new ConfigError(
|
|
5699
6147
|
`Invalid pdfx.json: ${issues}`,
|
|
5700
|
-
`Fix the config or re-run ${
|
|
6148
|
+
`Fix the config or re-run ${chalk8.cyan("pdfx init")}`
|
|
5701
6149
|
);
|
|
5702
6150
|
}
|
|
5703
6151
|
return result.data;
|
|
@@ -5732,7 +6180,7 @@ async function fetchTemplate(name, registryUrl) {
|
|
|
5732
6180
|
}
|
|
5733
6181
|
return result.data;
|
|
5734
6182
|
}
|
|
5735
|
-
async function
|
|
6183
|
+
async function fetchComponent3(name, registryUrl) {
|
|
5736
6184
|
const url = `${registryUrl}/${name}.json`;
|
|
5737
6185
|
let response;
|
|
5738
6186
|
try {
|
|
@@ -5764,7 +6212,7 @@ async function fetchComponent2(name, registryUrl) {
|
|
|
5764
6212
|
}
|
|
5765
6213
|
function resolveTemplateImports(content, templateName, config) {
|
|
5766
6214
|
const cwd = process.cwd();
|
|
5767
|
-
const templateSubdir =
|
|
6215
|
+
const templateSubdir = path10.resolve(
|
|
5768
6216
|
cwd,
|
|
5769
6217
|
config.templateDir ?? DEFAULTS.TEMPLATE_DIR,
|
|
5770
6218
|
templateName
|
|
@@ -5772,38 +6220,38 @@ function resolveTemplateImports(content, templateName, config) {
|
|
|
5772
6220
|
let result = content.replace(
|
|
5773
6221
|
/from '\.\.\/\.\.\/components\/pdfx\/([a-z][a-z0-9-]*)\/pdfx-([a-z][a-z0-9-]*)'/g,
|
|
5774
6222
|
(_match, componentName) => {
|
|
5775
|
-
const absCompFile =
|
|
6223
|
+
const absCompFile = path10.resolve(
|
|
5776
6224
|
cwd,
|
|
5777
6225
|
config.componentDir,
|
|
5778
6226
|
componentName,
|
|
5779
6227
|
`pdfx-${componentName}`
|
|
5780
6228
|
);
|
|
5781
|
-
let rel =
|
|
6229
|
+
let rel = path10.relative(templateSubdir, absCompFile);
|
|
5782
6230
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
5783
6231
|
return `from '${rel}'`;
|
|
5784
6232
|
}
|
|
5785
6233
|
);
|
|
5786
6234
|
if (config.theme) {
|
|
5787
|
-
const absThemePath =
|
|
5788
|
-
let relTheme =
|
|
6235
|
+
const absThemePath = path10.resolve(cwd, config.theme).replace(/\.tsx?$/, "");
|
|
6236
|
+
let relTheme = path10.relative(templateSubdir, absThemePath);
|
|
5789
6237
|
if (!relTheme.startsWith(".")) relTheme = `./${relTheme}`;
|
|
5790
|
-
const absContextPath =
|
|
5791
|
-
|
|
6238
|
+
const absContextPath = path10.join(
|
|
6239
|
+
path10.dirname(path10.resolve(cwd, config.theme)),
|
|
5792
6240
|
"pdfx-theme-context"
|
|
5793
6241
|
);
|
|
5794
|
-
let relContext =
|
|
6242
|
+
let relContext = path10.relative(templateSubdir, absContextPath);
|
|
5795
6243
|
if (!relContext.startsWith(".")) relContext = `./${relContext}`;
|
|
5796
6244
|
result = result.replace(/from '\.\.\/\.\.\/lib\/pdfx-theme'/g, `from '${relTheme}'`);
|
|
5797
6245
|
result = result.replace(/from '\.\.\/\.\.\/lib\/pdfx-theme-context'/g, `from '${relContext}'`);
|
|
5798
6246
|
}
|
|
5799
6247
|
return result;
|
|
5800
6248
|
}
|
|
5801
|
-
async function
|
|
6249
|
+
async function resolveConflict2(fileName, currentDecision) {
|
|
5802
6250
|
if (currentDecision === "overwrite-all") return "overwrite-all";
|
|
5803
|
-
const { action } = await
|
|
6251
|
+
const { action } = await prompts5({
|
|
5804
6252
|
type: "select",
|
|
5805
6253
|
name: "action",
|
|
5806
|
-
message: `${
|
|
6254
|
+
message: `${chalk8.yellow(fileName)} already exists. What would you like to do?`,
|
|
5807
6255
|
choices: [
|
|
5808
6256
|
{ title: "Skip", value: "skip", description: "Keep the existing file unchanged" },
|
|
5809
6257
|
{ title: "Overwrite", value: "overwrite", description: "Replace this file only" },
|
|
@@ -5818,20 +6266,20 @@ async function resolveConflict(fileName, currentDecision) {
|
|
|
5818
6266
|
if (!action) throw new ValidationError("Cancelled by user");
|
|
5819
6267
|
return action;
|
|
5820
6268
|
}
|
|
5821
|
-
async function
|
|
6269
|
+
async function ensurePeerComponents2(template, config, force) {
|
|
5822
6270
|
const installedPeers = [];
|
|
5823
6271
|
const peerWarnings = [];
|
|
5824
6272
|
if (!template.peerComponents || template.peerComponents.length === 0) {
|
|
5825
6273
|
return { installedPeers, peerWarnings };
|
|
5826
6274
|
}
|
|
5827
|
-
const componentBaseDir =
|
|
6275
|
+
const componentBaseDir = path10.resolve(process.cwd(), config.componentDir);
|
|
5828
6276
|
for (const componentName of template.peerComponents) {
|
|
5829
|
-
const componentDir =
|
|
5830
|
-
const expectedMain =
|
|
6277
|
+
const componentDir = path10.join(componentBaseDir, componentName);
|
|
6278
|
+
const expectedMain = path10.join(componentDir, `pdfx-${componentName}.tsx`);
|
|
5831
6279
|
if (checkFileExists(componentDir)) {
|
|
5832
6280
|
if (!checkFileExists(expectedMain)) {
|
|
5833
6281
|
peerWarnings.push(
|
|
5834
|
-
`${componentName}: directory exists but expected file missing (${
|
|
6282
|
+
`${componentName}: directory exists but expected file missing (${path10.basename(expectedMain)})`
|
|
5835
6283
|
);
|
|
5836
6284
|
} else {
|
|
5837
6285
|
peerWarnings.push(
|
|
@@ -5840,11 +6288,11 @@ async function ensurePeerComponents(template, config, force) {
|
|
|
5840
6288
|
}
|
|
5841
6289
|
continue;
|
|
5842
6290
|
}
|
|
5843
|
-
const component = await
|
|
6291
|
+
const component = await fetchComponent3(componentName, config.registry);
|
|
5844
6292
|
ensureDir(componentDir);
|
|
5845
|
-
const componentRelDir =
|
|
6293
|
+
const componentRelDir = path10.join(config.componentDir, component.name);
|
|
5846
6294
|
for (const file of component.files) {
|
|
5847
|
-
const fileName =
|
|
6295
|
+
const fileName = path10.basename(file.path);
|
|
5848
6296
|
const filePath = safePath(componentDir, fileName);
|
|
5849
6297
|
let content = file.content;
|
|
5850
6298
|
if (config.theme && (content.includes("pdfx-theme") || content.includes("pdfx-theme-context"))) {
|
|
@@ -5864,13 +6312,13 @@ async function ensurePeerComponents(template, config, force) {
|
|
|
5864
6312
|
}
|
|
5865
6313
|
async function installTemplate(name, config, force) {
|
|
5866
6314
|
const template = await fetchTemplate(name, config.registry);
|
|
5867
|
-
const peerResult = await
|
|
5868
|
-
const templateBaseDir =
|
|
5869
|
-
const templateDir =
|
|
6315
|
+
const peerResult = await ensurePeerComponents2(template, config, force);
|
|
6316
|
+
const templateBaseDir = path10.resolve(process.cwd(), config.templateDir ?? DEFAULTS.TEMPLATE_DIR);
|
|
6317
|
+
const templateDir = path10.join(templateBaseDir, template.name);
|
|
5870
6318
|
ensureDir(templateDir);
|
|
5871
6319
|
const filesToWrite = [];
|
|
5872
6320
|
for (const file of template.files) {
|
|
5873
|
-
const fileName =
|
|
6321
|
+
const fileName = path10.basename(file.path);
|
|
5874
6322
|
const filePath = safePath(templateDir, fileName);
|
|
5875
6323
|
let content = file.content;
|
|
5876
6324
|
if (/\.(tsx?|jsx?)$/.test(fileName) && content.includes("../../")) {
|
|
@@ -5883,7 +6331,7 @@ async function installTemplate(name, config, force) {
|
|
|
5883
6331
|
const resolved = [];
|
|
5884
6332
|
for (const file of filesToWrite) {
|
|
5885
6333
|
if (checkFileExists(file.filePath)) {
|
|
5886
|
-
const decision = await
|
|
6334
|
+
const decision = await resolveConflict2(path10.basename(file.filePath), globalDecision);
|
|
5887
6335
|
if (decision === "overwrite-all") {
|
|
5888
6336
|
globalDecision = "overwrite-all";
|
|
5889
6337
|
}
|
|
@@ -5896,7 +6344,7 @@ async function installTemplate(name, config, force) {
|
|
|
5896
6344
|
if (!file.skip) {
|
|
5897
6345
|
writeFile(file.filePath, file.content);
|
|
5898
6346
|
} else {
|
|
5899
|
-
console.log(
|
|
6347
|
+
console.log(chalk8.dim(` skipped ${path10.basename(file.filePath)}`));
|
|
5900
6348
|
}
|
|
5901
6349
|
}
|
|
5902
6350
|
} else {
|
|
@@ -5905,49 +6353,49 @@ async function installTemplate(name, config, force) {
|
|
|
5905
6353
|
}
|
|
5906
6354
|
}
|
|
5907
6355
|
if (template.peerComponents && template.peerComponents.length > 0) {
|
|
5908
|
-
const componentBaseDir =
|
|
6356
|
+
const componentBaseDir = path10.resolve(process.cwd(), config.componentDir);
|
|
5909
6357
|
const missing = [];
|
|
5910
6358
|
for (const comp of template.peerComponents) {
|
|
5911
|
-
const compDir =
|
|
6359
|
+
const compDir = path10.join(componentBaseDir, comp);
|
|
5912
6360
|
if (!checkFileExists(compDir)) {
|
|
5913
6361
|
missing.push(comp);
|
|
5914
6362
|
}
|
|
5915
6363
|
}
|
|
5916
6364
|
if (missing.length > 0) {
|
|
5917
6365
|
console.log();
|
|
5918
|
-
console.log(
|
|
6366
|
+
console.log(chalk8.yellow(" Missing peer components:"));
|
|
5919
6367
|
for (const comp of missing) {
|
|
5920
|
-
console.log(
|
|
6368
|
+
console.log(chalk8.dim(` ${comp} \u2192 run: ${chalk8.cyan(`pdfx add ${comp}`)}`));
|
|
5921
6369
|
}
|
|
5922
6370
|
}
|
|
5923
6371
|
}
|
|
5924
6372
|
if (config.theme) {
|
|
5925
|
-
const absThemePath =
|
|
5926
|
-
const contextPath =
|
|
6373
|
+
const absThemePath = path10.resolve(process.cwd(), config.theme);
|
|
6374
|
+
const contextPath = path10.join(path10.dirname(absThemePath), "pdfx-theme-context.tsx");
|
|
5927
6375
|
if (!checkFileExists(contextPath)) {
|
|
5928
|
-
ensureDir(
|
|
6376
|
+
ensureDir(path10.dirname(contextPath));
|
|
5929
6377
|
writeFile(contextPath, generateThemeContextFile());
|
|
5930
6378
|
}
|
|
5931
6379
|
}
|
|
5932
6380
|
return peerResult;
|
|
5933
6381
|
}
|
|
5934
6382
|
async function templateAdd(names, options = {}) {
|
|
5935
|
-
const configPath =
|
|
6383
|
+
const configPath = path10.join(process.cwd(), "pdfx.json");
|
|
5936
6384
|
if (!checkFileExists(configPath)) {
|
|
5937
|
-
console.error(
|
|
5938
|
-
console.log(
|
|
6385
|
+
console.error(chalk8.red("Error: pdfx.json not found"));
|
|
6386
|
+
console.log(chalk8.yellow("Run: pdfx init"));
|
|
5939
6387
|
process.exit(1);
|
|
5940
6388
|
}
|
|
5941
6389
|
let config;
|
|
5942
6390
|
try {
|
|
5943
|
-
config =
|
|
6391
|
+
config = readConfig3(configPath);
|
|
5944
6392
|
} catch (error) {
|
|
5945
6393
|
if (error instanceof ConfigError) {
|
|
5946
|
-
console.error(
|
|
5947
|
-
if (error.suggestion) console.log(
|
|
6394
|
+
console.error(chalk8.red(error.message));
|
|
6395
|
+
if (error.suggestion) console.log(chalk8.yellow(` Hint: ${error.suggestion}`));
|
|
5948
6396
|
} else {
|
|
5949
6397
|
const message = error instanceof Error ? error.message : String(error);
|
|
5950
|
-
console.error(
|
|
6398
|
+
console.error(chalk8.red(message));
|
|
5951
6399
|
}
|
|
5952
6400
|
process.exit(1);
|
|
5953
6401
|
}
|
|
@@ -5956,24 +6404,24 @@ async function templateAdd(names, options = {}) {
|
|
|
5956
6404
|
for (const templateName of names) {
|
|
5957
6405
|
const nameResult = componentNameSchema.safeParse(templateName);
|
|
5958
6406
|
if (!nameResult.success) {
|
|
5959
|
-
console.error(
|
|
6407
|
+
console.error(chalk8.red(`Invalid template name: "${templateName}"`));
|
|
5960
6408
|
console.log(
|
|
5961
|
-
|
|
6409
|
+
chalk8.dim(' Names must be lowercase alphanumeric with hyphens (e.g., "invoice-classic")')
|
|
5962
6410
|
);
|
|
5963
6411
|
failed.push(templateName);
|
|
5964
6412
|
continue;
|
|
5965
6413
|
}
|
|
5966
|
-
const spinner =
|
|
6414
|
+
const spinner = ora7(`Adding template ${templateName}...`).start();
|
|
5967
6415
|
try {
|
|
5968
6416
|
const result = await installTemplate(templateName, config, force);
|
|
5969
|
-
spinner.succeed(`Added template ${
|
|
6417
|
+
spinner.succeed(`Added template ${chalk8.cyan(templateName)}`);
|
|
5970
6418
|
if (result.installedPeers.length > 0) {
|
|
5971
6419
|
console.log(
|
|
5972
|
-
|
|
6420
|
+
chalk8.green(` Installed required components: ${result.installedPeers.join(", ")}`)
|
|
5973
6421
|
);
|
|
5974
6422
|
}
|
|
5975
6423
|
for (const warning of result.peerWarnings) {
|
|
5976
|
-
console.log(
|
|
6424
|
+
console.log(chalk8.yellow(` Warning: ${warning}`));
|
|
5977
6425
|
}
|
|
5978
6426
|
} catch (error) {
|
|
5979
6427
|
if (error instanceof ValidationError && error.message.includes("Cancelled")) {
|
|
@@ -5982,24 +6430,24 @@ async function templateAdd(names, options = {}) {
|
|
|
5982
6430
|
} else if (error instanceof NetworkError || error instanceof RegistryError || error instanceof ValidationError) {
|
|
5983
6431
|
spinner.fail(error.message);
|
|
5984
6432
|
if (error.suggestion) {
|
|
5985
|
-
console.log(
|
|
6433
|
+
console.log(chalk8.dim(` Hint: ${error.suggestion}`));
|
|
5986
6434
|
}
|
|
5987
6435
|
} else {
|
|
5988
6436
|
spinner.fail(`Failed to add template ${templateName}`);
|
|
5989
6437
|
const message = error instanceof Error ? error.message : String(error);
|
|
5990
|
-
console.error(
|
|
6438
|
+
console.error(chalk8.dim(` ${message}`));
|
|
5991
6439
|
}
|
|
5992
6440
|
failed.push(templateName);
|
|
5993
6441
|
}
|
|
5994
6442
|
}
|
|
5995
6443
|
console.log();
|
|
5996
6444
|
if (failed.length > 0) {
|
|
5997
|
-
console.log(
|
|
6445
|
+
console.log(chalk8.yellow(`Failed: ${failed.join(", ")}`));
|
|
5998
6446
|
}
|
|
5999
6447
|
if (failed.length < names.length) {
|
|
6000
|
-
const resolvedDir =
|
|
6001
|
-
console.log(
|
|
6002
|
-
console.log(
|
|
6448
|
+
const resolvedDir = path10.resolve(process.cwd(), config.templateDir ?? DEFAULTS.TEMPLATE_DIR);
|
|
6449
|
+
console.log(chalk8.green("Done!"));
|
|
6450
|
+
console.log(chalk8.dim(`Templates installed to: ${resolvedDir}
|
|
6003
6451
|
`));
|
|
6004
6452
|
}
|
|
6005
6453
|
if (failed.length > 0) {
|
|
@@ -6007,20 +6455,20 @@ async function templateAdd(names, options = {}) {
|
|
|
6007
6455
|
}
|
|
6008
6456
|
}
|
|
6009
6457
|
async function templateList() {
|
|
6010
|
-
const configPath =
|
|
6458
|
+
const configPath = path10.join(process.cwd(), "pdfx.json");
|
|
6011
6459
|
if (!checkFileExists(configPath)) {
|
|
6012
|
-
console.error(
|
|
6013
|
-
console.log(
|
|
6460
|
+
console.error(chalk8.red("Error: pdfx.json not found"));
|
|
6461
|
+
console.log(chalk8.yellow("Run: pdfx init"));
|
|
6014
6462
|
process.exit(1);
|
|
6015
6463
|
}
|
|
6016
6464
|
const raw = readJsonFile(configPath);
|
|
6017
6465
|
const configResult = configSchema.safeParse(raw);
|
|
6018
6466
|
if (!configResult.success) {
|
|
6019
|
-
console.error(
|
|
6467
|
+
console.error(chalk8.red("Invalid pdfx.json"));
|
|
6020
6468
|
process.exit(1);
|
|
6021
6469
|
}
|
|
6022
6470
|
const config = configResult.data;
|
|
6023
|
-
const spinner =
|
|
6471
|
+
const spinner = ora7("Fetching template list...").start();
|
|
6024
6472
|
try {
|
|
6025
6473
|
let response;
|
|
6026
6474
|
try {
|
|
@@ -6044,28 +6492,28 @@ async function templateList() {
|
|
|
6044
6492
|
spinner.stop();
|
|
6045
6493
|
const templates = result.data.items.filter((item) => item.type === "registry:template");
|
|
6046
6494
|
if (templates.length === 0) {
|
|
6047
|
-
console.log(
|
|
6495
|
+
console.log(chalk8.dim("\n No templates available in the registry.\n"));
|
|
6048
6496
|
return;
|
|
6049
6497
|
}
|
|
6050
|
-
const templateBaseDir =
|
|
6498
|
+
const templateBaseDir = path10.resolve(
|
|
6051
6499
|
process.cwd(),
|
|
6052
6500
|
config.templateDir ?? DEFAULTS.TEMPLATE_DIR
|
|
6053
6501
|
);
|
|
6054
|
-
console.log(
|
|
6502
|
+
console.log(chalk8.bold(`
|
|
6055
6503
|
Available Templates (${templates.length})
|
|
6056
6504
|
`));
|
|
6057
6505
|
for (const item of templates) {
|
|
6058
|
-
const templateDir =
|
|
6506
|
+
const templateDir = path10.join(templateBaseDir, item.name);
|
|
6059
6507
|
const installed = checkFileExists(templateDir);
|
|
6060
|
-
const status = installed ?
|
|
6061
|
-
console.log(` ${
|
|
6508
|
+
const status = installed ? chalk8.green("[installed]") : chalk8.dim("[not installed]");
|
|
6509
|
+
console.log(` ${chalk8.cyan(item.name.padEnd(22))} ${item.description ?? ""}`);
|
|
6062
6510
|
console.log(` ${"".padEnd(22)} ${status}`);
|
|
6063
6511
|
if (item.peerComponents && item.peerComponents.length > 0) {
|
|
6064
|
-
console.log(
|
|
6512
|
+
console.log(chalk8.dim(` ${"".padEnd(22)} requires: ${item.peerComponents.join(", ")}`));
|
|
6065
6513
|
}
|
|
6066
6514
|
console.log();
|
|
6067
6515
|
}
|
|
6068
|
-
console.log(
|
|
6516
|
+
console.log(chalk8.dim(` Install with: ${chalk8.cyan("pdfx template add <template-name>")}
|
|
6069
6517
|
`));
|
|
6070
6518
|
} catch (error) {
|
|
6071
6519
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -6076,22 +6524,22 @@ async function templateList() {
|
|
|
6076
6524
|
|
|
6077
6525
|
// src/commands/theme.ts
|
|
6078
6526
|
import fs8 from "fs";
|
|
6079
|
-
import
|
|
6080
|
-
import
|
|
6081
|
-
import
|
|
6082
|
-
import
|
|
6527
|
+
import path11 from "path";
|
|
6528
|
+
import chalk9 from "chalk";
|
|
6529
|
+
import ora8 from "ora";
|
|
6530
|
+
import prompts6 from "prompts";
|
|
6083
6531
|
import ts from "typescript";
|
|
6084
6532
|
async function themeInit() {
|
|
6085
|
-
const configPath =
|
|
6533
|
+
const configPath = path11.join(process.cwd(), "pdfx.json");
|
|
6086
6534
|
if (!fs8.existsSync(configPath)) {
|
|
6087
|
-
console.error(
|
|
6088
|
-
console.log(
|
|
6089
|
-
console.log(
|
|
6090
|
-
console.log(
|
|
6535
|
+
console.error(chalk9.red("\nError: pdfx.json not found"));
|
|
6536
|
+
console.log(chalk9.yellow("\n PDFx is not initialized in this project.\n"));
|
|
6537
|
+
console.log(chalk9.cyan(" Run: pdfx init"));
|
|
6538
|
+
console.log(chalk9.dim(" This will set up your project configuration and theme.\n"));
|
|
6091
6539
|
process.exit(1);
|
|
6092
6540
|
}
|
|
6093
|
-
console.log(
|
|
6094
|
-
const answers = await
|
|
6541
|
+
console.log(chalk9.bold.cyan("\n PDFx Theme Setup\n"));
|
|
6542
|
+
const answers = await prompts6(
|
|
6095
6543
|
[
|
|
6096
6544
|
{
|
|
6097
6545
|
type: "select",
|
|
@@ -6125,27 +6573,27 @@ async function themeInit() {
|
|
|
6125
6573
|
],
|
|
6126
6574
|
{
|
|
6127
6575
|
onCancel: () => {
|
|
6128
|
-
console.log(
|
|
6576
|
+
console.log(chalk9.yellow("\nTheme setup cancelled."));
|
|
6129
6577
|
process.exit(0);
|
|
6130
6578
|
}
|
|
6131
6579
|
}
|
|
6132
6580
|
);
|
|
6133
6581
|
if (!answers.preset || !answers.themePath) {
|
|
6134
|
-
console.error(
|
|
6582
|
+
console.error(chalk9.red("Missing required fields."));
|
|
6135
6583
|
process.exit(1);
|
|
6136
6584
|
}
|
|
6137
6585
|
const presetName = answers.preset;
|
|
6138
6586
|
const themePath = answers.themePath;
|
|
6139
6587
|
const preset = themePresets[presetName];
|
|
6140
|
-
const spinner =
|
|
6588
|
+
const spinner = ora8(`Scaffolding ${presetName} theme...`).start();
|
|
6141
6589
|
try {
|
|
6142
|
-
const absThemePath =
|
|
6143
|
-
ensureDir(
|
|
6590
|
+
const absThemePath = path11.resolve(process.cwd(), themePath);
|
|
6591
|
+
ensureDir(path11.dirname(absThemePath));
|
|
6144
6592
|
fs8.writeFileSync(absThemePath, generateThemeFile(preset), "utf-8");
|
|
6145
|
-
const contextPath =
|
|
6593
|
+
const contextPath = path11.join(path11.dirname(absThemePath), "pdfx-theme-context.tsx");
|
|
6146
6594
|
fs8.writeFileSync(contextPath, generateThemeContextFile(), "utf-8");
|
|
6147
6595
|
spinner.succeed(`Created ${themePath} with ${presetName} theme`);
|
|
6148
|
-
const configPath2 =
|
|
6596
|
+
const configPath2 = path11.join(process.cwd(), "pdfx.json");
|
|
6149
6597
|
if (fs8.existsSync(configPath2)) {
|
|
6150
6598
|
try {
|
|
6151
6599
|
const rawConfig = readJsonFile(configPath2);
|
|
@@ -6153,19 +6601,19 @@ async function themeInit() {
|
|
|
6153
6601
|
if (result.success) {
|
|
6154
6602
|
const updatedConfig = { ...result.data, theme: themePath };
|
|
6155
6603
|
fs8.writeFileSync(configPath2, JSON.stringify(updatedConfig, null, 2), "utf-8");
|
|
6156
|
-
console.log(
|
|
6604
|
+
console.log(chalk9.green(" Updated pdfx.json with theme path"));
|
|
6157
6605
|
}
|
|
6158
6606
|
} catch {
|
|
6159
|
-
console.log(
|
|
6607
|
+
console.log(chalk9.yellow(' Could not update pdfx.json \u2014 add "theme" field manually'));
|
|
6160
6608
|
}
|
|
6161
6609
|
}
|
|
6162
|
-
console.log(
|
|
6610
|
+
console.log(chalk9.dim(`
|
|
6163
6611
|
Edit ${themePath} to customize your theme.
|
|
6164
6612
|
`));
|
|
6165
6613
|
} catch (error) {
|
|
6166
6614
|
spinner.fail("Failed to create theme file");
|
|
6167
6615
|
const message = error instanceof Error ? error.message : String(error);
|
|
6168
|
-
console.error(
|
|
6616
|
+
console.error(chalk9.dim(` ${message}`));
|
|
6169
6617
|
process.exit(1);
|
|
6170
6618
|
}
|
|
6171
6619
|
}
|
|
@@ -6173,56 +6621,56 @@ async function themeSwitch(presetName) {
|
|
|
6173
6621
|
const resolvedPreset = presetName === "default" ? "professional" : presetName;
|
|
6174
6622
|
const validPresets = Object.keys(themePresets);
|
|
6175
6623
|
if (!validPresets.includes(resolvedPreset)) {
|
|
6176
|
-
console.error(
|
|
6177
|
-
console.log(
|
|
6624
|
+
console.error(chalk9.red(`\u2716 Invalid theme preset: "${presetName}"`));
|
|
6625
|
+
console.log(chalk9.dim(` Available presets: ${validPresets.join(", ")}, default
|
|
6178
6626
|
`));
|
|
6179
|
-
console.log(
|
|
6627
|
+
console.log(chalk9.dim(" Usage: pdfx theme switch <preset>"));
|
|
6180
6628
|
process.exit(1);
|
|
6181
6629
|
}
|
|
6182
6630
|
const validatedPreset = resolvedPreset;
|
|
6183
|
-
const configPath =
|
|
6631
|
+
const configPath = path11.join(process.cwd(), "pdfx.json");
|
|
6184
6632
|
if (!fs8.existsSync(configPath)) {
|
|
6185
|
-
console.error(
|
|
6633
|
+
console.error(chalk9.red('No pdfx.json found. Run "pdfx init" first.'));
|
|
6186
6634
|
process.exit(1);
|
|
6187
6635
|
}
|
|
6188
6636
|
const rawConfig = readJsonFile(configPath);
|
|
6189
6637
|
const result = configSchema.safeParse(rawConfig);
|
|
6190
6638
|
if (!result.success) {
|
|
6191
|
-
console.error(
|
|
6639
|
+
console.error(chalk9.red("Invalid pdfx.json configuration."));
|
|
6192
6640
|
process.exit(1);
|
|
6193
6641
|
}
|
|
6194
6642
|
const config = result.data;
|
|
6195
6643
|
if (!config.theme) {
|
|
6196
6644
|
console.error(
|
|
6197
|
-
|
|
6645
|
+
chalk9.red('No theme path in pdfx.json. Run "pdfx theme init" to set up theming.')
|
|
6198
6646
|
);
|
|
6199
6647
|
process.exit(1);
|
|
6200
6648
|
}
|
|
6201
|
-
const answer = await
|
|
6649
|
+
const answer = await prompts6({
|
|
6202
6650
|
type: "confirm",
|
|
6203
6651
|
name: "confirm",
|
|
6204
6652
|
message: `This will overwrite ${config.theme} with the ${validatedPreset} preset. Continue?`,
|
|
6205
6653
|
initial: false
|
|
6206
6654
|
});
|
|
6207
6655
|
if (!answer.confirm) {
|
|
6208
|
-
console.log(
|
|
6656
|
+
console.log(chalk9.yellow("Cancelled."));
|
|
6209
6657
|
return;
|
|
6210
6658
|
}
|
|
6211
|
-
const spinner =
|
|
6659
|
+
const spinner = ora8(`Switching to ${validatedPreset} theme...`).start();
|
|
6212
6660
|
try {
|
|
6213
6661
|
const preset = themePresets[validatedPreset];
|
|
6214
|
-
const absThemePath =
|
|
6662
|
+
const absThemePath = path11.resolve(process.cwd(), config.theme);
|
|
6215
6663
|
fs8.writeFileSync(absThemePath, generateThemeFile(preset), "utf-8");
|
|
6216
|
-
const contextPath =
|
|
6664
|
+
const contextPath = path11.join(path11.dirname(absThemePath), "pdfx-theme-context.tsx");
|
|
6217
6665
|
if (!fs8.existsSync(contextPath)) {
|
|
6218
|
-
ensureDir(
|
|
6666
|
+
ensureDir(path11.dirname(contextPath));
|
|
6219
6667
|
fs8.writeFileSync(contextPath, generateThemeContextFile(), "utf-8");
|
|
6220
6668
|
}
|
|
6221
6669
|
spinner.succeed(`Switched to ${validatedPreset} theme`);
|
|
6222
6670
|
} catch (error) {
|
|
6223
6671
|
spinner.fail("Failed to switch theme");
|
|
6224
6672
|
const message = error instanceof Error ? error.message : String(error);
|
|
6225
|
-
console.error(
|
|
6673
|
+
console.error(chalk9.dim(` ${message}`));
|
|
6226
6674
|
process.exit(1);
|
|
6227
6675
|
}
|
|
6228
6676
|
}
|
|
@@ -6290,48 +6738,48 @@ function parseThemeObject(themePath) {
|
|
|
6290
6738
|
throw new Error("No exported `theme` object found.");
|
|
6291
6739
|
}
|
|
6292
6740
|
async function themeValidate() {
|
|
6293
|
-
const configPath =
|
|
6741
|
+
const configPath = path11.join(process.cwd(), "pdfx.json");
|
|
6294
6742
|
if (!fs8.existsSync(configPath)) {
|
|
6295
|
-
console.error(
|
|
6743
|
+
console.error(chalk9.red('No pdfx.json found. Run "pdfx init" first.'));
|
|
6296
6744
|
process.exit(1);
|
|
6297
6745
|
}
|
|
6298
6746
|
const rawConfig = readJsonFile(configPath);
|
|
6299
6747
|
const configResult = configSchema.safeParse(rawConfig);
|
|
6300
6748
|
if (!configResult.success) {
|
|
6301
|
-
console.error(
|
|
6749
|
+
console.error(chalk9.red("Invalid pdfx.json configuration."));
|
|
6302
6750
|
process.exit(1);
|
|
6303
6751
|
}
|
|
6304
6752
|
if (!configResult.data.theme) {
|
|
6305
6753
|
console.error(
|
|
6306
|
-
|
|
6754
|
+
chalk9.red('No theme path in pdfx.json. Run "pdfx theme init" to set up theming.')
|
|
6307
6755
|
);
|
|
6308
6756
|
process.exit(1);
|
|
6309
6757
|
}
|
|
6310
|
-
const absThemePath =
|
|
6758
|
+
const absThemePath = path11.resolve(process.cwd(), configResult.data.theme);
|
|
6311
6759
|
if (!fs8.existsSync(absThemePath)) {
|
|
6312
|
-
console.error(
|
|
6760
|
+
console.error(chalk9.red(`Theme file not found: ${configResult.data.theme}`));
|
|
6313
6761
|
process.exit(1);
|
|
6314
6762
|
}
|
|
6315
|
-
const spinner =
|
|
6763
|
+
const spinner = ora8("Validating theme file...").start();
|
|
6316
6764
|
try {
|
|
6317
6765
|
const parsedTheme = parseThemeObject(absThemePath);
|
|
6318
6766
|
const result = themeSchema.safeParse(parsedTheme);
|
|
6319
6767
|
if (!result.success) {
|
|
6320
|
-
const issues = result.error.issues.map((issue) => ` \u2192 ${
|
|
6768
|
+
const issues = result.error.issues.map((issue) => ` \u2192 ${chalk9.yellow(issue.path.join("."))}: ${issue.message}`).join("\n");
|
|
6321
6769
|
spinner.fail("Theme validation failed");
|
|
6322
|
-
console.log(
|
|
6770
|
+
console.log(chalk9.red("\n Missing or invalid fields:\n"));
|
|
6323
6771
|
console.log(issues);
|
|
6324
|
-
console.log(
|
|
6772
|
+
console.log(chalk9.dim("\n Fix these fields in your theme file and run validate again.\n"));
|
|
6325
6773
|
process.exit(1);
|
|
6326
6774
|
}
|
|
6327
6775
|
spinner.succeed("Theme file is valid");
|
|
6328
|
-
console.log(
|
|
6776
|
+
console.log(chalk9.dim(`
|
|
6329
6777
|
Validated: ${configResult.data.theme}
|
|
6330
6778
|
`));
|
|
6331
6779
|
} catch (error) {
|
|
6332
6780
|
spinner.fail("Failed to validate theme");
|
|
6333
6781
|
const message = error instanceof Error ? error.message : String(error);
|
|
6334
|
-
console.error(
|
|
6782
|
+
console.error(chalk9.dim(` ${message}`));
|
|
6335
6783
|
process.exit(1);
|
|
6336
6784
|
}
|
|
6337
6785
|
}
|
|
@@ -6361,4 +6809,7 @@ themeCmd.command("validate").description("Validate your theme file").action(them
|
|
|
6361
6809
|
var templateCmd = program.command("template").description("Manage PDF templates");
|
|
6362
6810
|
templateCmd.command("add <templates...>").description("Add a template to your project").option("-f, --force", "Overwrite existing files without prompting").action((templates, options) => templateAdd(templates, options));
|
|
6363
6811
|
templateCmd.command("list").description("List available templates from registry").action(templateList);
|
|
6812
|
+
var blockCmd = program.command("block").description("Manage PDF blocks (copy-paste designs)");
|
|
6813
|
+
blockCmd.command("add <blocks...>").description("Add a block to your project").option("-f, --force", "Overwrite existing files without prompting").action((blocks, options) => blockAdd(blocks, options));
|
|
6814
|
+
blockCmd.command("list").description("List available blocks from registry").action(blockList);
|
|
6364
6815
|
program.parse();
|