properties-comparator 1.0.3 → 1.0.5
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/DOCUMENTATION.md +215 -74
- package/PUBLISH.MD +3 -1
- package/README.md +82 -7
- package/TEST.md +62 -0
- package/babel.config.js +3 -0
- package/cli.js +60 -0
- package/index.js +495 -46
- package/jest.config.js +9 -0
- package/package.json +21 -5
- package/src/compareUtility.js +520 -0
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import yaml from "js-yaml";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parses a .properties file into an object.
|
|
10
|
+
* Handles any file read/parse errors gracefully.
|
|
11
|
+
* @param {string} filePath - The path to the properties file.
|
|
12
|
+
* @returns {Object} - Key-value pairs, or {} on error.
|
|
13
|
+
*/
|
|
14
|
+
function parsePropertiesFile(filePath) {
|
|
15
|
+
try {
|
|
16
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
17
|
+
const lines = content.split(/\r?\n/);
|
|
18
|
+
const result = {};
|
|
19
|
+
|
|
20
|
+
for (let line of lines) {
|
|
21
|
+
let trimmedLine = line.trim();
|
|
22
|
+
|
|
23
|
+
// 1) Skip empty lines or lines that *start* with '#'
|
|
24
|
+
if (!trimmedLine || trimmedLine.startsWith("#")) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 2) Remove inline comment: anything after the first '#'
|
|
29
|
+
const hashIndex = trimmedLine.indexOf("#");
|
|
30
|
+
if (hashIndex !== -1) {
|
|
31
|
+
trimmedLine = trimmedLine.slice(0, hashIndex).trim();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 3) Split on the *first* '=' only
|
|
35
|
+
const eqIndex = trimmedLine.indexOf("=");
|
|
36
|
+
if (eqIndex === -1) {
|
|
37
|
+
// No '=' => Not a valid key-value line
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const key = trimmedLine.slice(0, eqIndex).trim();
|
|
42
|
+
const value = trimmedLine.slice(eqIndex + 1).trim();
|
|
43
|
+
|
|
44
|
+
if (key) {
|
|
45
|
+
result[key] = value;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return result;
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error(
|
|
52
|
+
`Error reading/parsing .properties file (${filePath}):`,
|
|
53
|
+
err.message
|
|
54
|
+
);
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Flattens a nested object into a single-level object using dot-notation for nested keys.
|
|
61
|
+
* @param {Object} obj - The object to flatten.
|
|
62
|
+
* @param {string} [parentKey=''] - The current parent key (used in recursion).
|
|
63
|
+
* @param {Object} [res={}] - The accumulator object.
|
|
64
|
+
* @returns {Object} - A flattened key-value map.
|
|
65
|
+
*/
|
|
66
|
+
function flattenObject(obj, parentKey = "", res = {}) {
|
|
67
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
68
|
+
const newKey = parentKey ? `${parentKey}.${key}` : key;
|
|
69
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
70
|
+
flattenObject(value, newKey, res);
|
|
71
|
+
} else {
|
|
72
|
+
// Ensure all values are strings for consistent comparison
|
|
73
|
+
res[newKey] = String(value);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return res;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Parses a .yml or .yaml file into a flat key-value map.
|
|
81
|
+
* Handles any file read/parse errors gracefully.
|
|
82
|
+
* @param {string} filePath - The path to the YAML file.
|
|
83
|
+
* @returns {Object} - A flattened key-value map, or {} on error.
|
|
84
|
+
*/
|
|
85
|
+
function parseYamlFile(filePath) {
|
|
86
|
+
try {
|
|
87
|
+
const fileContents = fs.readFileSync(filePath, "utf-8");
|
|
88
|
+
const data = yaml.load(fileContents);
|
|
89
|
+
return flattenObject(data);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error(
|
|
92
|
+
`Error reading/parsing YAML file (${filePath}):`,
|
|
93
|
+
err.message
|
|
94
|
+
);
|
|
95
|
+
return {};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Detects file extension and parses the file content into an object.
|
|
101
|
+
* Currently supports .properties, .yaml, and .yml.
|
|
102
|
+
* If extension is unsupported, logs a warning and returns {}.
|
|
103
|
+
* @param {string} filePath - The path to the file.
|
|
104
|
+
* @returns {Object} - Parsed content as a key-value map, or {} if unsupported.
|
|
105
|
+
*/
|
|
106
|
+
function parseFile(filePath) {
|
|
107
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
108
|
+
|
|
109
|
+
switch (ext) {
|
|
110
|
+
case ".properties":
|
|
111
|
+
return parsePropertiesFile(filePath);
|
|
112
|
+
case ".yml":
|
|
113
|
+
case ".yaml":
|
|
114
|
+
return parseYamlFile(filePath);
|
|
115
|
+
default:
|
|
116
|
+
console.error(
|
|
117
|
+
`Warning: Unsupported file extension "${ext}" for file "${filePath}". ` +
|
|
118
|
+
`Only .properties, .yml, or .yaml are supported. This file will be treated as empty.`
|
|
119
|
+
);
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Internal helper that compares key-value data from multiple files
|
|
126
|
+
* and returns a structured result (without printing to console).
|
|
127
|
+
*
|
|
128
|
+
* @param {string[]} filePaths - Array of file paths.
|
|
129
|
+
* @returns {{
|
|
130
|
+
* mismatchCount: number,
|
|
131
|
+
* mismatchDetails: {
|
|
132
|
+
* key: string,
|
|
133
|
+
* values: string[],
|
|
134
|
+
* matched: boolean
|
|
135
|
+
* }[]
|
|
136
|
+
* }}
|
|
137
|
+
*/
|
|
138
|
+
function compareFileData(filePaths) {
|
|
139
|
+
// Parse each file
|
|
140
|
+
const parsedObjects = filePaths.map(parseFile);
|
|
141
|
+
|
|
142
|
+
// Collect all unique keys
|
|
143
|
+
const allKeys = new Set(parsedObjects.flatMap((obj) => Object.keys(obj)));
|
|
144
|
+
|
|
145
|
+
const mismatchDetails = [];
|
|
146
|
+
|
|
147
|
+
// Compare values for each key across files
|
|
148
|
+
allKeys.forEach((key) => {
|
|
149
|
+
const values = parsedObjects.map(
|
|
150
|
+
(obj) => obj[key]?.replace(/\s+/g, "") || "N/A"
|
|
151
|
+
);
|
|
152
|
+
const matched = values.every((value) => value === values[0]);
|
|
153
|
+
mismatchDetails.push({ key, values, matched });
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Count mismatches
|
|
157
|
+
const mismatchCount = mismatchDetails.filter((d) => !d.matched).length;
|
|
158
|
+
return { mismatchCount, mismatchDetails };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Helper function: checks if all values match across the provided files.
|
|
163
|
+
*
|
|
164
|
+
* @param {string[]} filePaths - Array of file paths.
|
|
165
|
+
* @returns {boolean} - True if all properties match across all files, false otherwise.
|
|
166
|
+
*/
|
|
167
|
+
function checkIfAllValuesMatch(filePaths) {
|
|
168
|
+
const { mismatchCount } = compareFileData(filePaths);
|
|
169
|
+
return mismatchCount === 0;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Helper function: returns a list of fields (keys) that do not match.
|
|
174
|
+
*
|
|
175
|
+
* @param {string[]} filePaths - Array of file paths.
|
|
176
|
+
* @returns {string[]} - List of mismatched keys.
|
|
177
|
+
*/
|
|
178
|
+
function getMismatchFields(filePaths) {
|
|
179
|
+
const { mismatchDetails } = compareFileData(filePaths);
|
|
180
|
+
return mismatchDetails
|
|
181
|
+
.filter((detail) => !detail.matched)
|
|
182
|
+
.map((detail) => detail.key);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Generates an HTML report for the comparison results.
|
|
187
|
+
*
|
|
188
|
+
* @param {Array} filePaths - Array of file paths that were compared
|
|
189
|
+
* @param {Object} comparisonData - The output from compareFileData function
|
|
190
|
+
* @returns {string} - HTML document as string
|
|
191
|
+
*/
|
|
192
|
+
function generateHtmlReport(filePaths, comparisonData) {
|
|
193
|
+
const { mismatchCount, mismatchDetails } = comparisonData;
|
|
194
|
+
const fileNames = filePaths.map((fp) => path.basename(fp));
|
|
195
|
+
|
|
196
|
+
// Start HTML document
|
|
197
|
+
let html = `<!DOCTYPE html>
|
|
198
|
+
<html lang="en">
|
|
199
|
+
<head>
|
|
200
|
+
<meta charset="UTF-8">
|
|
201
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
202
|
+
<title>Properties Comparison Report</title>
|
|
203
|
+
<style>
|
|
204
|
+
body { font-family: Arial, sans-serif; margin: 20px; line-height: 1.6; color: #333; }
|
|
205
|
+
h1, h2 { color: #0066cc; }
|
|
206
|
+
table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
|
|
207
|
+
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
|
208
|
+
th { background-color: #f2f2f2; }
|
|
209
|
+
tr:nth-child(even) { background-color: #f9f9f9; }
|
|
210
|
+
tr:hover { background-color: #f2f2f2; }
|
|
211
|
+
.mismatch { background-color: #ffe6e6; }
|
|
212
|
+
.matched { background-color: #e6ffe6; }
|
|
213
|
+
.value-mismatch { color: #cc0000; font-weight: bold; }
|
|
214
|
+
.summary { margin: 20px 0; padding: 15px; border-radius: 5px; }
|
|
215
|
+
.summary.success { background-color: #e6ffe6; border: 1px solid #99cc99; }
|
|
216
|
+
.summary.error { background-color: #ffe6e6; border: 1px solid #cc9999; }
|
|
217
|
+
.file-list { margin-bottom: 20px; }
|
|
218
|
+
</style>
|
|
219
|
+
</head>
|
|
220
|
+
<body>
|
|
221
|
+
<h1>Properties Comparison Report</h1>
|
|
222
|
+
|
|
223
|
+
<div class="file-list">
|
|
224
|
+
<h2>Files Compared:</h2>
|
|
225
|
+
<ol>
|
|
226
|
+
${fileNames
|
|
227
|
+
.map(
|
|
228
|
+
(name, idx) => `<li>${name} <small>(${filePaths[idx]})</small></li>`
|
|
229
|
+
)
|
|
230
|
+
.join("\n ")}
|
|
231
|
+
</ol>
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
<h2>Comparison Results</h2>
|
|
235
|
+
<table>
|
|
236
|
+
<tr>
|
|
237
|
+
<th>Key</th>
|
|
238
|
+
<th>Matched</th>
|
|
239
|
+
${fileNames
|
|
240
|
+
.map((name, idx) => `<th>File ${idx + 1}: ${name}</th>`)
|
|
241
|
+
.join("\n ")}
|
|
242
|
+
</tr>`;
|
|
243
|
+
|
|
244
|
+
// Add table rows for each key
|
|
245
|
+
mismatchDetails.forEach(({ key, values, matched }) => {
|
|
246
|
+
html += `\n <tr class="${matched ? "matched" : "mismatch"}">
|
|
247
|
+
<td>${key}</td>
|
|
248
|
+
<td>${matched ? "Yes" : "No"}</td>`;
|
|
249
|
+
|
|
250
|
+
// Add values from each file
|
|
251
|
+
values.forEach((value, idx) => {
|
|
252
|
+
const cellClass = matched ? "" : "value-mismatch";
|
|
253
|
+
html += `\n <td class="${cellClass}">${
|
|
254
|
+
value === "N/A" ? "<em>N/A</em>" : value
|
|
255
|
+
}</td>`;
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
html += `\n </tr>`;
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
html += `\n </table>
|
|
262
|
+
|
|
263
|
+
<div class="summary ${mismatchCount === 0 ? "success" : "error"}">
|
|
264
|
+
<h2>Summary</h2>`;
|
|
265
|
+
|
|
266
|
+
if (mismatchCount === 0) {
|
|
267
|
+
html += `\n <p>All properties match across all files!</p>`;
|
|
268
|
+
} else {
|
|
269
|
+
html += `\n <p>${mismatchCount} key(s) have mismatched values.</p>
|
|
270
|
+
<p><strong>Mismatched keys:</strong> ${mismatchDetails
|
|
271
|
+
.filter((detail) => !detail.matched)
|
|
272
|
+
.map((detail) => detail.key)
|
|
273
|
+
.join(", ")}</p>`;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
html += `\n </div>
|
|
277
|
+
</body>
|
|
278
|
+
</html>`;
|
|
279
|
+
|
|
280
|
+
return html;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Generates a Markdown report for the comparison results.
|
|
285
|
+
*
|
|
286
|
+
* @param {Array} filePaths - Array of file paths that were compared
|
|
287
|
+
* @param {Object} comparisonData - The output from compareFileData function
|
|
288
|
+
* @returns {string} - Markdown document as string
|
|
289
|
+
*/
|
|
290
|
+
function generateMarkdownReport(filePaths, comparisonData) {
|
|
291
|
+
const { mismatchCount, mismatchDetails } = comparisonData;
|
|
292
|
+
const fileNames = filePaths.map((fp) => path.basename(fp));
|
|
293
|
+
|
|
294
|
+
let markdown = `# Properties Comparison Report\n\n`;
|
|
295
|
+
|
|
296
|
+
// Files compared
|
|
297
|
+
markdown += `## Files Compared\n\n`;
|
|
298
|
+
filePaths.forEach((fp, idx) => {
|
|
299
|
+
markdown += `${idx + 1}. ${fileNames[idx]} (${fp})\n`;
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Comparison results table
|
|
303
|
+
markdown += `\n## Comparison Results\n\n`;
|
|
304
|
+
|
|
305
|
+
// Table header
|
|
306
|
+
markdown += `| Key | Matched | ${fileNames
|
|
307
|
+
.map((name, idx) => `File ${idx + 1}: ${name}`)
|
|
308
|
+
.join(" | ")} |\n`;
|
|
309
|
+
markdown += `| --- | --- | ${fileNames.map(() => "---").join(" | ")} |\n`;
|
|
310
|
+
|
|
311
|
+
// Table content
|
|
312
|
+
mismatchDetails.forEach(({ key, values, matched }) => {
|
|
313
|
+
markdown += `| ${key} | ${matched ? "Yes" : "No"} | ${values
|
|
314
|
+
.map((v) => (v === "N/A" ? "*N/A*" : v))
|
|
315
|
+
.join(" | ")} |\n`;
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// Summary
|
|
319
|
+
markdown += `\n## Summary\n\n`;
|
|
320
|
+
if (mismatchCount === 0) {
|
|
321
|
+
markdown += `✅ All properties match across all files!\n`;
|
|
322
|
+
} else {
|
|
323
|
+
markdown += `❌ ${mismatchCount} key(s) have mismatched values.\n\n`;
|
|
324
|
+
markdown += `**Mismatched keys:** ${mismatchDetails
|
|
325
|
+
.filter((detail) => !detail.matched)
|
|
326
|
+
.map((detail) => detail.key)
|
|
327
|
+
.join(", ")}\n`;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return markdown;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* CLI function: compares properties/keys across multiple files,
|
|
335
|
+
* prints details to the console in a tabular format, and provides a summary.
|
|
336
|
+
*
|
|
337
|
+
* @param {string[]} filePaths - Array of file paths.
|
|
338
|
+
* @param {Object} options - Options for the comparison.
|
|
339
|
+
* @param {string} [options.format] - Output format ('console', 'html', or 'markdown').
|
|
340
|
+
* @param {string} [options.outputFile] - Path to save the report (for html and markdown).
|
|
341
|
+
*/
|
|
342
|
+
function compareFiles(filePaths, options = {}) {
|
|
343
|
+
const format = options.format || "console";
|
|
344
|
+
const outputFile = options.outputFile;
|
|
345
|
+
|
|
346
|
+
const comparisonData = compareFileData(filePaths);
|
|
347
|
+
|
|
348
|
+
if (format === "console") {
|
|
349
|
+
console.log("Comparing properties/keys across files:\n");
|
|
350
|
+
|
|
351
|
+
// Prepare data for tabular output
|
|
352
|
+
const tableData = comparisonData.mismatchDetails.map(
|
|
353
|
+
({ key, values, matched }) => {
|
|
354
|
+
const valueColumns = values.reduce((acc, value, idx) => {
|
|
355
|
+
acc[`File ${idx + 1}`] = value;
|
|
356
|
+
return acc;
|
|
357
|
+
}, {});
|
|
358
|
+
return {
|
|
359
|
+
Key: key,
|
|
360
|
+
Matched: matched ? "Yes" : "No",
|
|
361
|
+
...valueColumns,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
// Print the table
|
|
367
|
+
console.table(tableData);
|
|
368
|
+
|
|
369
|
+
// Custom print for mismatched rows
|
|
370
|
+
console.log("\n=== Highlighted Mismatched Rows ===");
|
|
371
|
+
comparisonData.mismatchDetails.forEach(({ key, values, matched }) => {
|
|
372
|
+
if (!matched) {
|
|
373
|
+
const coloredValues = values.map((value, idx) =>
|
|
374
|
+
chalk.red(`File ${idx + 1}: ${value}`)
|
|
375
|
+
);
|
|
376
|
+
console.log(
|
|
377
|
+
chalk.yellow(`Key: ${key}`),
|
|
378
|
+
"|",
|
|
379
|
+
coloredValues.join(" | ")
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Summary
|
|
385
|
+
console.log("\n=== Summary ===");
|
|
386
|
+
if (comparisonData.mismatchCount === 0) {
|
|
387
|
+
console.log("All properties match across all files!");
|
|
388
|
+
} else {
|
|
389
|
+
console.log(
|
|
390
|
+
`${comparisonData.mismatchCount} key(s) have mismatched values.`
|
|
391
|
+
);
|
|
392
|
+
const mismatchedKeys = comparisonData.mismatchDetails
|
|
393
|
+
.filter((detail) => !detail.matched)
|
|
394
|
+
.map((detail) => detail.key);
|
|
395
|
+
console.log("Mismatched keys:", mismatchedKeys.join(", "));
|
|
396
|
+
}
|
|
397
|
+
} else if (format === "html") {
|
|
398
|
+
const htmlReport = generateHtmlReport(filePaths, comparisonData);
|
|
399
|
+
if (outputFile) {
|
|
400
|
+
fs.writeFileSync(outputFile, htmlReport);
|
|
401
|
+
console.log(`HTML report saved to: ${outputFile}`);
|
|
402
|
+
} else {
|
|
403
|
+
console.log(htmlReport);
|
|
404
|
+
}
|
|
405
|
+
} else if (format === "markdown") {
|
|
406
|
+
const markdownReport = generateMarkdownReport(filePaths, comparisonData);
|
|
407
|
+
if (outputFile) {
|
|
408
|
+
fs.writeFileSync(outputFile, markdownReport);
|
|
409
|
+
console.log(`Markdown report saved to: ${outputFile}`);
|
|
410
|
+
} else {
|
|
411
|
+
console.log(markdownReport);
|
|
412
|
+
}
|
|
413
|
+
} else {
|
|
414
|
+
console.error(
|
|
415
|
+
`Unsupported format: ${format}. Using console output instead.`
|
|
416
|
+
);
|
|
417
|
+
compareFiles(filePaths); // Fallback to console output
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* CLI entry point for comparing .properties and .yml/.yaml files.
|
|
423
|
+
*/
|
|
424
|
+
function run() {
|
|
425
|
+
const args = process.argv.slice(2);
|
|
426
|
+
const options = {
|
|
427
|
+
format: "console",
|
|
428
|
+
outputFile: null,
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
// Parse arguments for format and output file
|
|
432
|
+
const filePaths = [];
|
|
433
|
+
for (let i = 0; i < args.length; i++) {
|
|
434
|
+
if (args[i] === "--format" || args[i] === "-f") {
|
|
435
|
+
if (i + 1 < args.length) {
|
|
436
|
+
options.format = args[i + 1].toLowerCase();
|
|
437
|
+
i++; // Skip the next argument as it's the format value
|
|
438
|
+
}
|
|
439
|
+
} else if (args[i] === "--output" || args[i] === "-o") {
|
|
440
|
+
if (i + 1 < args.length) {
|
|
441
|
+
options.outputFile = args[i + 1];
|
|
442
|
+
i++; // Skip the next argument as it's the output file path
|
|
443
|
+
}
|
|
444
|
+
} else {
|
|
445
|
+
// Not an option, treat as file path
|
|
446
|
+
filePaths.push(path.resolve(args[i]));
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (filePaths.length === 0) {
|
|
451
|
+
console.error("Please provide file paths as command-line arguments.");
|
|
452
|
+
console.error(
|
|
453
|
+
"Usage: properties-comparator [options] file1 file2 [file3...]"
|
|
454
|
+
);
|
|
455
|
+
console.error("Options:");
|
|
456
|
+
console.error(
|
|
457
|
+
" --format, -f <format> Output format: console, html, or markdown"
|
|
458
|
+
);
|
|
459
|
+
console.error(
|
|
460
|
+
" --output, -o <file> Output file for html or markdown reports"
|
|
461
|
+
);
|
|
462
|
+
process.exit(1);
|
|
463
|
+
} else if (filePaths.length === 1) {
|
|
464
|
+
console.error("Please provide at least two file paths for comparison.");
|
|
465
|
+
process.exit(1);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const missing = filePaths.filter((fp) => !fs.existsSync(fp));
|
|
469
|
+
if (missing.length > 0) {
|
|
470
|
+
console.error(`The following file(s) do not exist: ${missing.join(", ")}`);
|
|
471
|
+
process.exit(1);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
compareFiles(filePaths, options);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* API function to compare properties between two files.
|
|
479
|
+
* @param {string} file1 - Path to the first file
|
|
480
|
+
* @param {string} file2 - Path to the second file
|
|
481
|
+
* @param {Object} options - Comparison options
|
|
482
|
+
* @returns {Object} Comparison results in a structured format
|
|
483
|
+
*/
|
|
484
|
+
async function compareProperties(file1, file2, options = {}) {
|
|
485
|
+
const filePaths = [file1, file2];
|
|
486
|
+
const comparisonData = compareFileData(filePaths);
|
|
487
|
+
|
|
488
|
+
// Process the output based on options
|
|
489
|
+
if (options.output) {
|
|
490
|
+
if (options.json) {
|
|
491
|
+
fs.writeFileSync(options.output, JSON.stringify(comparisonData, null, 2));
|
|
492
|
+
} else {
|
|
493
|
+
const format = path.extname(options.output).toLowerCase() === '.md' ? 'markdown' : 'html';
|
|
494
|
+
const report = format === 'markdown'
|
|
495
|
+
? generateMarkdownReport(filePaths, comparisonData)
|
|
496
|
+
: generateHtmlReport(filePaths, comparisonData);
|
|
497
|
+
fs.writeFileSync(options.output, report);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (options.verbose) {
|
|
501
|
+
console.log(`Comparison report saved to ${options.output}`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return comparisonData;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export {
|
|
509
|
+
parsePropertiesFile,
|
|
510
|
+
parseYamlFile,
|
|
511
|
+
parseFile,
|
|
512
|
+
compareFileData,
|
|
513
|
+
checkIfAllValuesMatch,
|
|
514
|
+
getMismatchFields,
|
|
515
|
+
compareFiles,
|
|
516
|
+
generateHtmlReport,
|
|
517
|
+
generateMarkdownReport,
|
|
518
|
+
compareProperties,
|
|
519
|
+
run,
|
|
520
|
+
};
|