zod->yargs
This commit is contained in:
parent
0762a888fd
commit
8e0723ce9f
@ -5,42 +5,6 @@ import * as z from 'zod';
|
||||
import { runConversion } from '../lib/convert.js';
|
||||
export const command = 'convert';
|
||||
export const desc = 'Convert PDF to images';
|
||||
export const builder = {
|
||||
input: {
|
||||
alias: 'i',
|
||||
type: 'string',
|
||||
description: 'Input PDF file',
|
||||
demandOption: true
|
||||
},
|
||||
output: {
|
||||
alias: 'o',
|
||||
type: 'string',
|
||||
description: 'Output path pattern or directory. Variables like ${SRC_DIR}, ${PAGE} etc. are supported. Uses a default pattern if omitted.',
|
||||
},
|
||||
dpi: {
|
||||
type: 'number',
|
||||
description: 'DPI for output images',
|
||||
default: 300
|
||||
},
|
||||
format: {
|
||||
type: 'string',
|
||||
choices: ['png', 'jpg'],
|
||||
default: 'png',
|
||||
description: 'Output image format'
|
||||
},
|
||||
startPage: {
|
||||
alias: 's',
|
||||
type: 'number',
|
||||
description: 'First page to convert (1-based)',
|
||||
requiresArg: true
|
||||
},
|
||||
endPage: {
|
||||
alias: 'e',
|
||||
type: 'number',
|
||||
description: 'Last page to convert (1-based, inclusive)',
|
||||
requiresArg: true
|
||||
}
|
||||
};
|
||||
export async function handler(argv) {
|
||||
const logger = new Logger();
|
||||
try {
|
||||
@ -49,7 +13,7 @@ export async function handler(argv) {
|
||||
throw new Error(`Input file ${config.input} does not exist`);
|
||||
}
|
||||
logger.info("Calling conversion library function...");
|
||||
const outputFiles = await runConversion(config);
|
||||
const outputFiles = await runConversion(config, logger);
|
||||
logger.info(`Conversion completed successfully`);
|
||||
logger.info(`Generated ${outputFiles.length} images`);
|
||||
}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import yargs from 'yargs';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
import * as convertCommand from './commands/convert.js';
|
||||
import { ConvertCommandArgsSchema } from './types.js';
|
||||
import { toYargs } from '@polymech/commons';
|
||||
const commandModule = {
|
||||
command: convertCommand.command,
|
||||
describe: convertCommand.desc,
|
||||
builder: convertCommand.builder,
|
||||
builder: (yargs) => toYargs(yargs, ConvertCommandArgsSchema),
|
||||
handler: convertCommand.handler
|
||||
};
|
||||
yargs(hideBin(process.argv))
|
||||
|
||||
@ -1,18 +1,17 @@
|
||||
import { Logger } from "tslog";
|
||||
import { statSync } from "node:fs";
|
||||
import { sep, resolve as pathResolve, parse as pathParse, relative as pathRelative } from "node:path";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { DEFAULT_ROOTS, DEFAULT_VARS, pathInfoEx } from "@polymech/commons";
|
||||
import { convertPdfToImages } from "./pdf.js"; // Import the actual PDF conversion function
|
||||
import { DEFAULT_OUTPUT_TEMPLATE } from "../constants.js"; // Import the constant
|
||||
import { convertPdfToImages } from "./pdf.js";
|
||||
import { DEFAULT_OUTPUT_TEMPLATE } from "../constants.js";
|
||||
/**
|
||||
* Runs the PDF to images conversion process.
|
||||
* Generates variables, determines output path, reads PDF, and calls the conversion engine.
|
||||
* @param config - The conversion configuration options.
|
||||
* @param config - The conversion configuration options (inferred from Zod schema).
|
||||
* @param logger - The logger instance to use for logging.
|
||||
* @returns A promise that resolves with an array of generated image file paths.
|
||||
*/
|
||||
export async function runConversion(config) {
|
||||
const logger = config.logger || new Logger();
|
||||
export async function runConversion(config, logger) {
|
||||
const inputPath = pathResolve(config.input);
|
||||
let srcInfo = {};
|
||||
try {
|
||||
|
||||
40
packages/content/ref/pdf-to-images/dist/types.js
vendored
40
packages/content/ref/pdf-to-images/dist/types.js
vendored
@ -1,25 +1,33 @@
|
||||
import { z } from 'zod';
|
||||
export const ConvertCommandSchema = z.object({
|
||||
// Define the base shape for arguments
|
||||
export const ConvertCommandArgsSchema = z.object({
|
||||
input: z.string(),
|
||||
output: z.string().optional(),
|
||||
dpi: z.number().int().positive().default(300),
|
||||
format: z.enum(['png', 'jpg']).default('png'),
|
||||
startPage: z.number().int().positive().optional(),
|
||||
endPage: z.number().int().positive().optional(),
|
||||
i: z.string().optional(),
|
||||
o: z.string().optional(),
|
||||
s: z.number().int().positive().optional(),
|
||||
e: z.number().int().positive().optional(),
|
||||
_: z.array(z.union([z.string(), z.number()])).optional(),
|
||||
$0: z.string().optional()
|
||||
}).transform((data) => ({
|
||||
input: data.input ?? data.i,
|
||||
output: data.output ?? data.o,
|
||||
dpi: data.dpi,
|
||||
format: data.format,
|
||||
startPage: data.startPage ?? data.s,
|
||||
endPage: data.endPage ?? data.e
|
||||
})).refine((data) => {
|
||||
endPage: z.number().int().positive().optional()
|
||||
});
|
||||
// Add refinements, transformations, and catchall for final validation/parsing
|
||||
export const ConvertCommandSchema = ConvertCommandArgsSchema
|
||||
.catchall(z.any()) // Allow var-* and other properties
|
||||
.transform((data) => {
|
||||
// Explicitly pick known fields + extras (var-*)
|
||||
const known = {
|
||||
input: data.input,
|
||||
output: data.output,
|
||||
dpi: data.dpi,
|
||||
format: data.format,
|
||||
startPage: data.startPage,
|
||||
endPage: data.endPage,
|
||||
};
|
||||
// Keep only extra properties (like var-*)
|
||||
const extras = Object.keys(data)
|
||||
.filter(key => !['input', 'output', 'dpi', 'format', 'startPage', 'endPage', '_', '$0'].includes(key))
|
||||
.reduce((acc, key) => { acc[key] = data[key]; return acc; }, {});
|
||||
return { ...known, ...extras };
|
||||
})
|
||||
.refine((data) => {
|
||||
if (data.startPage !== undefined && data.endPage !== undefined) {
|
||||
return data.startPage <= data.endPage;
|
||||
}
|
||||
|
||||
@ -46,8 +46,8 @@
|
||||
"tslog": "^3.3.3",
|
||||
"tsup": "^2.0.3",
|
||||
"yargs": "^17.7.2",
|
||||
"zod": "^3.24.2",
|
||||
"zod-to-json-schema": "^3.24.1",
|
||||
"zod": "^3.24.3",
|
||||
"zod-to-json-schema": "^3.24.5",
|
||||
"zod-to-ts": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
"dev": "tsc -p . --watch",
|
||||
"build": "tsc",
|
||||
"start": "node dist/index.js",
|
||||
"test:pdf": "node dist/index.js convert -i tests/e5dc.pdf -o tests/out/e5dc/ --startPage 3 --endPage 5",
|
||||
"test:pdf": "node dist/index.js convert --input tests/e5dc.pdf -o tests/out/e5dc/ --startPage 3 --endPage 5",
|
||||
"test:basic": "vitest run",
|
||||
"test:variables": "vitest run tests/cli/variables.test.ts"
|
||||
},
|
||||
|
||||
@ -2,64 +2,21 @@ import { Arguments } from 'yargs';
|
||||
import { Logger } from 'tslog';
|
||||
import { ConvertCommandSchema, ConvertCommandConfig } from '../types.js';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { resolve as pathResolve } from 'node:path';
|
||||
import * as z from 'zod';
|
||||
import type { Options } from 'yargs';
|
||||
import { runConversion, IRunConversionOptions } from '../lib/convert.js';
|
||||
import { runConversion } from '../lib/convert.js';
|
||||
|
||||
export const command = 'convert';
|
||||
export const desc = 'Convert PDF to images';
|
||||
|
||||
export const builder: { [key: string]: Options } = {
|
||||
input: {
|
||||
alias: 'i',
|
||||
type: 'string',
|
||||
description: 'Input PDF file',
|
||||
demandOption: true
|
||||
},
|
||||
output: {
|
||||
alias: 'o',
|
||||
type: 'string',
|
||||
description: 'Output path pattern or directory. Variables like ${SRC_DIR}, ${PAGE} etc. are supported. Uses a default pattern if omitted.',
|
||||
},
|
||||
dpi: {
|
||||
type: 'number',
|
||||
description: 'DPI for output images',
|
||||
default: 300
|
||||
},
|
||||
format: {
|
||||
type: 'string',
|
||||
choices: ['png', 'jpg'] as const,
|
||||
default: 'png',
|
||||
description: 'Output image format'
|
||||
},
|
||||
startPage: {
|
||||
alias: 's',
|
||||
type: 'number',
|
||||
description: 'First page to convert (1-based)',
|
||||
requiresArg: true
|
||||
},
|
||||
endPage: {
|
||||
alias: 'e',
|
||||
type: 'number',
|
||||
description: 'Last page to convert (1-based, inclusive)',
|
||||
requiresArg: true
|
||||
}
|
||||
};
|
||||
|
||||
export async function handler(argv: Arguments<ConvertCommandConfig>): Promise<void> {
|
||||
const logger = new Logger();
|
||||
|
||||
const logger = new Logger();
|
||||
try {
|
||||
const config = ConvertCommandSchema.parse(argv);
|
||||
|
||||
const config = ConvertCommandSchema.parse(argv);
|
||||
if (!existsSync(config.input)) {
|
||||
throw new Error(`Input file ${config.input} does not exist`);
|
||||
}
|
||||
|
||||
logger.info("Calling conversion library function...");
|
||||
const outputFiles = await runConversion(config as IRunConversionOptions);
|
||||
|
||||
const outputFiles = await runConversion(config, logger);
|
||||
logger.info(`Conversion completed successfully`);
|
||||
logger.info(`Generated ${outputFiles.length} images`);
|
||||
} catch (error) {
|
||||
|
||||
@ -2,12 +2,13 @@ import yargs from 'yargs';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
import * as convertCommand from './commands/convert.js';
|
||||
import type { CommandModule } from 'yargs';
|
||||
import type { ConvertCommandConfig } from './types.js';
|
||||
import { ConvertCommandConfig, ConvertCommandArgsSchema } from './types.js';
|
||||
import { toYargs } from '@polymech/commons';
|
||||
|
||||
const commandModule: CommandModule<{}, ConvertCommandConfig> = {
|
||||
command: convertCommand.command,
|
||||
describe: convertCommand.desc,
|
||||
builder: convertCommand.builder,
|
||||
builder: (yargs) => toYargs(yargs, ConvertCommandArgsSchema),
|
||||
handler: convertCommand.handler
|
||||
};
|
||||
|
||||
|
||||
@ -1,33 +1,20 @@
|
||||
import { Logger } from "tslog";
|
||||
import { statSync } from "node:fs";
|
||||
import { sep, resolve as pathResolve, parse as pathParse, relative as pathRelative } from "node:path";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { DEFAULT_ROOTS, DEFAULT_VARS, pathInfoEx } from "@polymech/commons";
|
||||
import { convertPdfToImages } from "./pdf.js"; // Import the actual PDF conversion function
|
||||
import { DEFAULT_OUTPUT_TEMPLATE } from "../constants.js"; // Import the constant
|
||||
|
||||
// Define an interface for the configuration options needed by the library function
|
||||
// This might be similar to SimpleOptions or ConvertCommandConfig, but tailored for the library
|
||||
export interface IRunConversionOptions {
|
||||
input: string;
|
||||
output?: string;
|
||||
dpi: number;
|
||||
format: "png" | "jpg";
|
||||
startPage?: number;
|
||||
endPage?: number;
|
||||
logger?: Logger<any>;
|
||||
[key: string]: any; // Allow other properties like var-*
|
||||
}
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { DEFAULT_ROOTS, DEFAULT_VARS, pathInfoEx, resolveVariables } from "@polymech/commons";
|
||||
import { convertPdfToImages } from "./pdf.js";
|
||||
import { DEFAULT_OUTPUT_TEMPLATE } from "../constants.js";
|
||||
import type { ConvertCommandConfig } from "../types.js";
|
||||
|
||||
/**
|
||||
* Runs the PDF to images conversion process.
|
||||
* Generates variables, determines output path, reads PDF, and calls the conversion engine.
|
||||
* @param config - The conversion configuration options.
|
||||
* @param config - The conversion configuration options (inferred from Zod schema).
|
||||
* @param logger - The logger instance to use for logging.
|
||||
* @returns A promise that resolves with an array of generated image file paths.
|
||||
*/
|
||||
export async function runConversion(config: IRunConversionOptions): Promise<string[]> {
|
||||
const logger = config.logger || new Logger<any>();
|
||||
|
||||
export async function runConversion(config: ConvertCommandConfig, logger: Logger<any>): Promise<string[]> {
|
||||
const inputPath = pathResolve(config.input);
|
||||
let srcInfo: any = {};
|
||||
try {
|
||||
|
||||
@ -1,35 +1,44 @@
|
||||
import { z } from 'zod';
|
||||
import type { ImageFormat } from './lib/pdf.js';
|
||||
|
||||
export const ConvertCommandSchema = z.object({
|
||||
// Define the base shape for arguments
|
||||
export const ConvertCommandArgsSchema = z.object({
|
||||
input: z.string(),
|
||||
output: z.string().optional(),
|
||||
dpi: z.number().int().positive().default(300),
|
||||
format: z.enum(['png', 'jpg']).default('png'),
|
||||
startPage: z.number().int().positive().optional(),
|
||||
endPage: z.number().int().positive().optional(),
|
||||
i: z.string().optional(),
|
||||
o: z.string().optional(),
|
||||
s: z.number().int().positive().optional(),
|
||||
e: z.number().int().positive().optional(),
|
||||
_: z.array(z.union([z.string(), z.number()])).optional(),
|
||||
$0: z.string().optional()
|
||||
}).transform((data) => ({
|
||||
input: data.input ?? data.i,
|
||||
output: data.output ?? data.o,
|
||||
dpi: data.dpi,
|
||||
format: data.format,
|
||||
startPage: data.startPage ?? data.s,
|
||||
endPage: data.endPage ?? data.e
|
||||
})).refine((data) => {
|
||||
if (data.startPage !== undefined && data.endPage !== undefined) {
|
||||
return data.startPage <= data.endPage;
|
||||
}
|
||||
return true;
|
||||
}, {
|
||||
message: "startPage must be less than or equal to endPage",
|
||||
path: ["startPage"],
|
||||
endPage: z.number().int().positive().optional()
|
||||
});
|
||||
|
||||
// Add refinements, transformations, and catchall for final validation/parsing
|
||||
export const ConvertCommandSchema = ConvertCommandArgsSchema
|
||||
.catchall(z.any()) // Allow var-* and other properties
|
||||
.transform((data) => {
|
||||
// Explicitly pick known fields + extras (var-*)
|
||||
const known = {
|
||||
input: data.input,
|
||||
output: data.output,
|
||||
dpi: data.dpi,
|
||||
format: data.format,
|
||||
startPage: data.startPage,
|
||||
endPage: data.endPage,
|
||||
};
|
||||
// Keep only extra properties (like var-*)
|
||||
const extras = Object.keys(data)
|
||||
.filter(key => !['input', 'output', 'dpi', 'format', 'startPage', 'endPage', '_', '$0'].includes(key))
|
||||
.reduce((acc, key) => { acc[key] = data[key]; return acc; }, {} as any);
|
||||
|
||||
return { ...known, ...extras };
|
||||
})
|
||||
.refine((data) => {
|
||||
if (data.startPage !== undefined && data.endPage !== undefined) {
|
||||
return data.startPage <= data.endPage;
|
||||
}
|
||||
return true;
|
||||
}, {
|
||||
message: "startPage must be less than or equal to endPage",
|
||||
path: ["startPage"],
|
||||
});
|
||||
|
||||
export type ConvertCommandConfig = z.infer<typeof ConvertCommandSchema>;
|
||||
|
||||
|
||||
47448
packages/content/ref/pdf-to-images/tests/RS485-780_1.png
Normal file
47448
packages/content/ref/pdf-to-images/tests/RS485-780_1.png
Normal file
File diff suppressed because it is too large
Load Diff
55647
packages/content/ref/pdf-to-images/tests/RS485-780_10.png
Normal file
55647
packages/content/ref/pdf-to-images/tests/RS485-780_10.png
Normal file
File diff suppressed because it is too large
Load Diff
62187
packages/content/ref/pdf-to-images/tests/RS485-780_11.png
Normal file
62187
packages/content/ref/pdf-to-images/tests/RS485-780_11.png
Normal file
File diff suppressed because it is too large
Load Diff
58606
packages/content/ref/pdf-to-images/tests/RS485-780_12.png
Normal file
58606
packages/content/ref/pdf-to-images/tests/RS485-780_12.png
Normal file
File diff suppressed because it is too large
Load Diff
53385
packages/content/ref/pdf-to-images/tests/RS485-780_13.png
Normal file
53385
packages/content/ref/pdf-to-images/tests/RS485-780_13.png
Normal file
File diff suppressed because it is too large
Load Diff
48789
packages/content/ref/pdf-to-images/tests/RS485-780_14.png
Normal file
48789
packages/content/ref/pdf-to-images/tests/RS485-780_14.png
Normal file
File diff suppressed because it is too large
Load Diff
63873
packages/content/ref/pdf-to-images/tests/RS485-780_15.png
Normal file
63873
packages/content/ref/pdf-to-images/tests/RS485-780_15.png
Normal file
File diff suppressed because it is too large
Load Diff
51373
packages/content/ref/pdf-to-images/tests/RS485-780_16.png
Normal file
51373
packages/content/ref/pdf-to-images/tests/RS485-780_16.png
Normal file
File diff suppressed because it is too large
Load Diff
54583
packages/content/ref/pdf-to-images/tests/RS485-780_17.png
Normal file
54583
packages/content/ref/pdf-to-images/tests/RS485-780_17.png
Normal file
File diff suppressed because it is too large
Load Diff
45332
packages/content/ref/pdf-to-images/tests/RS485-780_18.png
Normal file
45332
packages/content/ref/pdf-to-images/tests/RS485-780_18.png
Normal file
File diff suppressed because it is too large
Load Diff
41495
packages/content/ref/pdf-to-images/tests/RS485-780_19.png
Normal file
41495
packages/content/ref/pdf-to-images/tests/RS485-780_19.png
Normal file
File diff suppressed because it is too large
Load Diff
51153
packages/content/ref/pdf-to-images/tests/RS485-780_2.png
Normal file
51153
packages/content/ref/pdf-to-images/tests/RS485-780_2.png
Normal file
File diff suppressed because it is too large
Load Diff
41218
packages/content/ref/pdf-to-images/tests/RS485-780_20.png
Normal file
41218
packages/content/ref/pdf-to-images/tests/RS485-780_20.png
Normal file
File diff suppressed because it is too large
Load Diff
47932
packages/content/ref/pdf-to-images/tests/RS485-780_21.png
Normal file
47932
packages/content/ref/pdf-to-images/tests/RS485-780_21.png
Normal file
File diff suppressed because it is too large
Load Diff
35965
packages/content/ref/pdf-to-images/tests/RS485-780_22.png
Normal file
35965
packages/content/ref/pdf-to-images/tests/RS485-780_22.png
Normal file
File diff suppressed because it is too large
Load Diff
37573
packages/content/ref/pdf-to-images/tests/RS485-780_23.png
Normal file
37573
packages/content/ref/pdf-to-images/tests/RS485-780_23.png
Normal file
File diff suppressed because it is too large
Load Diff
52130
packages/content/ref/pdf-to-images/tests/RS485-780_24.png
Normal file
52130
packages/content/ref/pdf-to-images/tests/RS485-780_24.png
Normal file
File diff suppressed because it is too large
Load Diff
47166
packages/content/ref/pdf-to-images/tests/RS485-780_25.png
Normal file
47166
packages/content/ref/pdf-to-images/tests/RS485-780_25.png
Normal file
File diff suppressed because it is too large
Load Diff
48886
packages/content/ref/pdf-to-images/tests/RS485-780_26.png
Normal file
48886
packages/content/ref/pdf-to-images/tests/RS485-780_26.png
Normal file
File diff suppressed because it is too large
Load Diff
52206
packages/content/ref/pdf-to-images/tests/RS485-780_27.png
Normal file
52206
packages/content/ref/pdf-to-images/tests/RS485-780_27.png
Normal file
File diff suppressed because it is too large
Load Diff
49947
packages/content/ref/pdf-to-images/tests/RS485-780_28.png
Normal file
49947
packages/content/ref/pdf-to-images/tests/RS485-780_28.png
Normal file
File diff suppressed because it is too large
Load Diff
40713
packages/content/ref/pdf-to-images/tests/RS485-780_29.png
Normal file
40713
packages/content/ref/pdf-to-images/tests/RS485-780_29.png
Normal file
File diff suppressed because it is too large
Load Diff
45387
packages/content/ref/pdf-to-images/tests/RS485-780_3.png
Normal file
45387
packages/content/ref/pdf-to-images/tests/RS485-780_3.png
Normal file
File diff suppressed because it is too large
Load Diff
35293
packages/content/ref/pdf-to-images/tests/RS485-780_4.png
Normal file
35293
packages/content/ref/pdf-to-images/tests/RS485-780_4.png
Normal file
File diff suppressed because it is too large
Load Diff
56880
packages/content/ref/pdf-to-images/tests/RS485-780_5.png
Normal file
56880
packages/content/ref/pdf-to-images/tests/RS485-780_5.png
Normal file
File diff suppressed because it is too large
Load Diff
41134
packages/content/ref/pdf-to-images/tests/RS485-780_6.png
Normal file
41134
packages/content/ref/pdf-to-images/tests/RS485-780_6.png
Normal file
File diff suppressed because it is too large
Load Diff
49332
packages/content/ref/pdf-to-images/tests/RS485-780_7.png
Normal file
49332
packages/content/ref/pdf-to-images/tests/RS485-780_7.png
Normal file
File diff suppressed because it is too large
Load Diff
43323
packages/content/ref/pdf-to-images/tests/RS485-780_8.png
Normal file
43323
packages/content/ref/pdf-to-images/tests/RS485-780_8.png
Normal file
File diff suppressed because it is too large
Load Diff
47078
packages/content/ref/pdf-to-images/tests/RS485-780_9.png
Normal file
47078
packages/content/ref/pdf-to-images/tests/RS485-780_9.png
Normal file
File diff suppressed because it is too large
Load Diff
@ -31,7 +31,7 @@ describe('CLI Integration Test - Variable Output Path', () => {
|
||||
// Ensure paths in the command are relative to the execution directory if needed,
|
||||
// but here inputPdf is relative, and outputPattern relies on internal resolution.
|
||||
// Quote the output pattern for safety in the shell.
|
||||
const command = `node dist/index.js convert -i "${inputPdf}" -o "${outputPattern}"`;
|
||||
const command = `node dist/index.js convert --input "${inputPdf}" --output "${outputPattern}"`;
|
||||
|
||||
// Execute the command
|
||||
let commandOutput = '';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user