purify-objects
Version:
A powerful TypeScript library for cleaning objects by removing empty values, with support for YAML and CSV formats
204 lines (203 loc) • 6.63 kB
JavaScript
;
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
const isNil = (value) => value === null || value === void 0;
const isObject = (value) => typeof value === "object" && value !== null;
const cloneDeep = (obj) => {
if (!isObject(obj)) return obj;
if (Array.isArray(obj)) {
return obj.map(cloneDeep);
}
return Object.entries(obj).reduce((acc, [key, value]) => ({
...acc,
[key]: cloneDeep(value)
}), {});
};
function parseYAML(content) {
const lines = content.split("\n");
const root = {};
let currentNode = root;
let stack = [[root, -1]];
for (const line of lines) {
if (!line.trim() || line.trim().startsWith("#")) continue;
const indent = line.search(/\S/);
const [key, ...valueParts] = line.trim().split(":");
let value = valueParts.join(":").trim();
while (stack.length > 1 && stack[stack.length - 1][1] >= indent) {
stack.pop();
}
currentNode = stack[stack.length - 1][0];
if (value) {
if (value.startsWith("[") && value.endsWith("]")) {
currentNode[key] = value.slice(1, -1).split(",").map((v) => v.trim());
} else if (value.toLowerCase() === "true") {
currentNode[key] = true;
} else if (value.toLowerCase() === "false") {
currentNode[key] = false;
} else if (value === "null") {
currentNode[key] = null;
} else if (!isNaN(Number(value))) {
currentNode[key] = Number(value);
} else {
currentNode[key] = value.replace(/^["']|["']$/g, "");
}
} else {
currentNode[key] = {};
stack.push([currentNode[key], indent]);
}
}
return root;
}
function parseCSV(content, options) {
const lines = content.split("\n").filter((line) => line.trim());
const delimiter = options.delimiter || ",";
const result = [];
const headers = options.headers ? lines[0].split(delimiter).map((header) => header.trim()) : Array.from({ length: lines[0].split(delimiter).length }, (_, i) => `column${i + 1}`);
const dataLines = options.headers ? lines.slice(1) : lines;
for (const line of dataLines) {
const values = line.split(delimiter).map((value) => {
value = value.trim();
if (value.startsWith('"') && value.endsWith('"')) {
return value.slice(1, -1);
}
if (value === "null") {
return null;
}
if (value === "") {
return value;
}
if (!isNaN(Number(value))) {
return Number(value);
}
if (value.toLowerCase() === "true") {
return true;
}
if (value.toLowerCase() === "false") {
return false;
}
return value;
});
const row = {};
headers.forEach((header, index) => {
row[header] = values[index];
});
result.push(row);
}
return result;
}
function stringifyYAML(obj, indent = 0) {
let result = "";
const spaces = " ".repeat(indent);
for (const [key, value] of Object.entries(obj)) {
if (value === null || value === void 0) {
result += `${spaces}${key}:
`;
} else if (value === "") {
result += `${spaces}${key}: ""
`;
} else if (Array.isArray(value)) {
result += `${spaces}${key}: [${value.join(", ")}]
`;
} else if (typeof value === "object") {
result += `${spaces}${key}:
${stringifyYAML(value, indent + 2)}`;
} else if (typeof value === "string" && value.includes("\n")) {
result += `${spaces}${key}: |
${value.split("\n").map((line) => `${spaces} ${line}`).join("\n")}
`;
} else {
result += `${spaces}${key}: ${value}
`;
}
}
return result;
}
function stringifyCSV(objects, options) {
const delimiter = options.delimiter || ",";
const headers = options.headers ? Object.keys(objects[0]) : Array.from({ length: Object.keys(objects[0]).length }, (_, i) => `column${i + 1}`);
let result = options.headers ? headers.join(delimiter) + "\n" : "";
for (const obj of objects) {
const values = headers.map((header) => {
const value = obj[header];
if (value === null || value === void 0) {
return "";
}
if (typeof value === "string" && (value.includes(delimiter) || value.includes('"'))) {
return `"${value.replace(/"/g, '""')}"`;
}
return String(value);
});
result += values.join(delimiter) + "\n";
}
return result;
}
const INITIAL_OPTIONS = {
customCleaner: void 0,
keepFields: [],
recursive: true,
safe: true
};
const evaluateEmptiness = (value) => isNil(value) || value === "" || Array.isArray(value) && !value.length || isObject(value) && !Object.keys(value).length;
function processNestedObject(target, customCleaner, preservedFields, config) {
const workingCopy = config.safe ? cloneDeep(target) : target;
for (const key in workingCopy) {
const value = workingCopy[key];
if (preservedFields.includes(key)) continue;
if (customCleaner == null ? void 0 : customCleaner(key, value)) {
delete workingCopy[key];
continue;
}
if (evaluateEmptiness(value)) {
delete workingCopy[key];
continue;
}
if (config.recursive && isObject(value) && !Array.isArray(value)) {
const processed = processNestedObject(
value,
customCleaner,
preservedFields,
config
);
if (!Object.keys(processed).length) {
delete workingCopy[key];
} else {
workingCopy[key] = processed;
}
}
}
return workingCopy;
}
function cleanObject(obj, customCleaner, keepFields = [], options = {}) {
const config = { ...INITIAL_OPTIONS, ...options };
return processNestedObject(obj, customCleaner, keepFields, config);
}
function parseContent(content, format, options = {}) {
switch (format) {
case "yaml":
return parseYAML(content);
case "csv":
return parseCSV(content, { delimiter: options.delimiter || ",", headers: options.headers !== false });
default:
return JSON.parse(content);
}
}
function stringifyContent(data, format, options = {}) {
switch (format) {
case "yaml":
return stringifyYAML(data);
case "csv":
return stringifyCSV(Array.isArray(data) ? data : [data], {
delimiter: options.delimiter || ",",
headers: options.headers !== false
});
default:
return JSON.stringify(data, null, 2);
}
}
exports.cleanObject = cleanObject;
exports.default = cleanObject;
exports.parseCSV = parseCSV;
exports.parseContent = parseContent;
exports.parseYAML = parseYAML;
exports.stringifyCSV = stringifyCSV;
exports.stringifyContent = stringifyContent;
exports.stringifyYAML = stringifyYAML;