UNPKG

purify-objects

Version:

A powerful TypeScript library for cleaning objects by removing empty values, with support for YAML and CSV formats

157 lines (156 loc) 6.83 kB
#!/usr/bin/env node "use strict"; const fs = require("fs"); const path = require("path"); const chalk = require("chalk"); const index = require("./index.js"); const VALID_FORMATS = ["json", "yaml", "csv"]; const FORMAT_EXTENSIONS = { json: ["json"], yaml: ["yaml", "yml"], csv: ["csv"] }; const detectFileFormat = (filename) => { const ext = path.extname(filename).toLowerCase().slice(1); for (const [format, extensions] of Object.entries(FORMAT_EXTENSIONS)) { if (extensions.includes(ext)) { return format; } } return "json"; }; const getDefaultOutputFilename = (inputFile, format) => { const parsedPath = path.parse(inputFile); const extension = format === "yaml" ? ".yaml" : `.${format}`; return path.join(parsedPath.dir, `${parsedPath.name}${extension}`); }; const extractCliOptions = (args) => { const inputFile = args[0]; const formatIndex = args.indexOf("--format"); const formatValue = formatIndex !== -1 ? args[formatIndex + 1] : detectFileFormat(inputFile); const convertToIndex = args.indexOf("--convert-to"); const convertToValue = convertToIndex !== -1 ? args[convertToIndex + 1] : null; if (formatValue && !VALID_FORMATS.includes(formatValue)) { console.error(chalk.red(`Error: Invalid format "${formatValue}". Valid formats are: ${VALID_FORMATS.join(", ")}`)); process.exit(1); } if (convertToValue && !VALID_FORMATS.includes(convertToValue)) { console.error(chalk.red(`Error: Invalid conversion format "${convertToValue}". Valid formats are: ${VALID_FORMATS.join(", ")}`)); process.exit(1); } const outputIndex = args.indexOf("--output"); let outputFile = outputIndex !== -1 ? args[outputIndex + 1] : null; if (convertToValue && !outputFile) { outputFile = getDefaultOutputFilename(inputFile, convertToValue); } return { inputFile, outputFile, compareMode: args.includes("--compare"), safeMode: args.includes("--safe"), format: formatValue, convertTo: convertToValue, delimiter: args.indexOf("--delimiter") !== -1 ? args[args.indexOf("--delimiter") + 1] : ",", headers: !args.includes("--no-headers"), removeZeroValues: args.includes("--remove-zero-values"), noClean: args.includes("--noclean") }; }; const parseContent = (content, format, options) => { try { switch (format) { case "yaml": return index.parseYAML(content); case "csv": return index.parseCSV(content, { delimiter: options.delimiter || ",", headers: options.headers !== false }); default: return JSON.parse(content); } } catch (error) { console.error(chalk.red(`Error parsing ${format.toUpperCase()} content: ${error.message}`)); process.exit(1); } }; const stringifyContent = (data, format, options) => { try { switch (format) { case "yaml": return index.stringifyYAML(data); case "csv": return index.stringifyCSV(Array.isArray(data) ? data : [data], { delimiter: options.delimiter || ",", headers: options.headers !== false }); default: return JSON.stringify(data, null, 2); } } catch (error) { console.error(chalk.red(`Error converting to ${format.toUpperCase()}: ${error.message}`)); process.exit(1); } }; const generateFieldMap = (original, cleaned, prefix = "") => Object.entries(original).reduce((acc, [key, value]) => { const currentPath = prefix ? `${prefix}.${key}` : key; if (!(key in cleaned)) { return [...acc, currentPath]; } if (typeof value === "object" && value !== null && typeof cleaned[key] === "object" && cleaned[key] !== null) { return [...acc, ...generateFieldMap(value, cleaned[key], currentPath)]; } return acc; }, []); const executeCliOperation = (options) => { const { inputFile, outputFile, compareMode, safeMode, format, convertTo, delimiter, headers, removeZeroValues, noClean } = options; if (!inputFile) { console.error(chalk.red("Please provide an input file")); console.log(chalk.yellow( `Usage: npx purify-objects input.file [--output cleaned.file] [--compare] [--safe] [--format ${VALID_FORMATS.join("|")}] [--convert-to ${VALID_FORMATS.join("|")}] [--delimiter ","] [--no-headers] [--remove-zero-values] [--noclean]` )); process.exit(1); } try { const inputPath = path.resolve(process.cwd(), inputFile); if (!fs.existsSync(inputPath)) { console.error(chalk.red(`Error: File not found: ${inputFile}`)); process.exit(1); } const content = fs.readFileSync(inputPath, "utf8"); const sourceData = parseContent(content, format, { delimiter, headers }); const customCleaner = removeZeroValues ? (key, value) => value === 0 : void 0; const processedData = noClean ? sourceData : Array.isArray(sourceData) ? sourceData.map((item) => index.cleanObject(item, customCleaner, [], { safe: safeMode })) : index.cleanObject(sourceData, customCleaner, [], { safe: safeMode }); if (compareMode) { safeMode && console.log(chalk.blue("\nSafe Mode: Original file will not be modified")); console.log(chalk.yellow("\nOriginal data:"), stringifyContent(sourceData, format, { delimiter, headers })); console.log(chalk.green("\nCleaned data (preview):"), stringifyContent(processedData, format, { delimiter, headers })); if (!Array.isArray(sourceData)) { const modifications = generateFieldMap(sourceData, processedData); if (modifications.length) { console.log(chalk.red("\nFields to be removed:")); modifications.forEach((f) => console.log(chalk.red(`- ${f}`))); } } safeMode && console.log(chalk.blue("\nNo changes were made to the original file (Safe Mode)")); process.exit(0); } if (outputFile) { const outputPath = path.resolve(process.cwd(), outputFile); const outputFormat = convertTo || format; fs.writeFileSync(outputPath, stringifyContent(processedData, outputFormat, { delimiter, headers })); if (convertTo) { console.log(chalk.green(` File successfully ${noClean ? "converted" : "converted and cleaned"}:`)); console.log(chalk.blue(`Input: ${inputFile} (${format.toUpperCase()})`)); console.log(chalk.blue(`Output: ${outputFile} (${convertTo.toUpperCase()})`)); } else { safeMode && console.log(chalk.blue("Safe Mode: Created new file without modifying original")); console.log(chalk.green(`${noClean ? "Data" : "Cleaned data"} saved to ${outputFile}`)); } return; } console.log(stringifyContent(processedData, format, { delimiter, headers })); } catch (error) { console.error(chalk.red("Error:"), (error == null ? void 0 : error.message) || "Unknown error occurred"); process.exit(1); } }; executeCliOperation(extractCliOptions(process.argv.slice(2)));