properties-comparator 1.0.5 → 1.0.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/.github/workflows/sonarcloud.yml +75 -0
- package/README.md +3 -3
- package/cli.js +5 -4
- package/index.js +62 -42
- package/package.json +15 -11
- package/sonar-project.properties +19 -0
- package/src/compareUtility.js +62 -42
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
2
|
+
# They are provided by a third-party and are governed by
|
|
3
|
+
# separate terms of service, privacy policy, and support
|
|
4
|
+
# documentation.
|
|
5
|
+
|
|
6
|
+
# This workflow helps you trigger a SonarCloud analysis of your code and populates
|
|
7
|
+
# GitHub Code Scanning alerts with the vulnerabilities found.
|
|
8
|
+
# Free for open source project.
|
|
9
|
+
|
|
10
|
+
# 1. Login to SonarCloud.io using your GitHub account
|
|
11
|
+
|
|
12
|
+
# 2. Import your project on SonarCloud
|
|
13
|
+
# * Add your GitHub organization first, then add your repository as a new project.
|
|
14
|
+
# * Please note that many languages are eligible for automatic analysis,
|
|
15
|
+
# which means that the analysis will start automatically without the need to set up GitHub Actions.
|
|
16
|
+
# * This behavior can be changed in Administration > Analysis Method.
|
|
17
|
+
#
|
|
18
|
+
# 3. Follow the SonarCloud in-product tutorial
|
|
19
|
+
# * a. Copy/paste the Project Key and the Organization Key into the args parameter below
|
|
20
|
+
# (You'll find this information in SonarCloud. Click on "Information" at the bottom left)
|
|
21
|
+
#
|
|
22
|
+
# * b. Generate a new token and add it to your Github repository's secrets using the name SONAR_TOKEN
|
|
23
|
+
# (On SonarCloud, click on your avatar on top-right > My account > Security
|
|
24
|
+
# or go directly to https://sonarcloud.io/account/security/)
|
|
25
|
+
|
|
26
|
+
# Feel free to take a look at our documentation (https://docs.sonarcloud.io/getting-started/github/)
|
|
27
|
+
# or reach out to our community forum if you need some help (https://community.sonarsource.com/c/help/sc/9)
|
|
28
|
+
|
|
29
|
+
name: SonarCloud analysis
|
|
30
|
+
|
|
31
|
+
on:
|
|
32
|
+
push:
|
|
33
|
+
branches: ["main"]
|
|
34
|
+
pull_request:
|
|
35
|
+
branches: ["main"]
|
|
36
|
+
workflow_dispatch:
|
|
37
|
+
|
|
38
|
+
permissions:
|
|
39
|
+
pull-requests: read # allows SonarCloud to decorate PRs with analysis results
|
|
40
|
+
|
|
41
|
+
jobs:
|
|
42
|
+
Analysis:
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
|
|
45
|
+
steps:
|
|
46
|
+
- name: Checkout repository
|
|
47
|
+
uses: actions/checkout@v4
|
|
48
|
+
with:
|
|
49
|
+
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
|
50
|
+
|
|
51
|
+
- name: Setup Node.js
|
|
52
|
+
uses: actions/setup-node@v4
|
|
53
|
+
with:
|
|
54
|
+
node-version: "20"
|
|
55
|
+
|
|
56
|
+
- name: Install dependencies
|
|
57
|
+
run: npm install
|
|
58
|
+
|
|
59
|
+
- name: Run tests with coverage
|
|
60
|
+
run: npm test -- --coverage
|
|
61
|
+
|
|
62
|
+
- name: Analyze with SonarCloud
|
|
63
|
+
uses: SonarSource/sonarcloud-github-action@4006f663ecaf1f8093e8e4abb9227f6041f52216
|
|
64
|
+
env:
|
|
65
|
+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
66
|
+
with:
|
|
67
|
+
args: -Dsonar.projectKey=zackria_properties-comparator
|
|
68
|
+
-Dsonar.organization=zackria
|
|
69
|
+
-Dsonar.sources=index.js,cli.js,src
|
|
70
|
+
-Dsonar.tests=test
|
|
71
|
+
-Dsonar.test.inclusions=test/**/*.test.js
|
|
72
|
+
-Dsonar.exclusions=node_modules/**,coverage/**,index.js
|
|
73
|
+
-Dsonar.cpd.exclusions=index.js
|
|
74
|
+
-Dsonar.javascript.lcov.reportPaths=coverage/lcov.info
|
|
75
|
+
projectBaseDir: .
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A powerful utility for parsing and comparing **.properties** and **.yml/.yaml** files. This tool reads files as key-value pairs, compares values across multiple files, and generates detailed comparison reports in various formats.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/properties-comparator)
|
|
5
|
+
[](https://www.npmjs.com/package/properties-comparator) [](https://sonarcloud.io/summary/new_code?id=zackria_properties-comparator)
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
@@ -79,8 +79,8 @@ This package is thoroughly tested with over 90% code coverage to ensure reliabil
|
|
|
79
79
|
## Compatibility
|
|
80
80
|
|
|
81
81
|
Developed and tested with:
|
|
82
|
-
- npm v11.
|
|
83
|
-
- Node.js
|
|
82
|
+
- npm v11.7.0
|
|
83
|
+
- Node.js v25.2.1
|
|
84
84
|
|
|
85
85
|
## Documentation
|
|
86
86
|
|
package/cli.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { program } from 'commander';
|
|
4
4
|
import { compareFiles } from './index.js';
|
|
5
|
-
import { readFileSync } from 'fs';
|
|
6
|
-
import path from 'path';
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
7
|
|
|
8
8
|
// Get version from package.json
|
|
9
9
|
let version = '1.0.0';
|
|
@@ -13,6 +13,7 @@ try {
|
|
|
13
13
|
version = packageJson.version;
|
|
14
14
|
} catch (err) {
|
|
15
15
|
// Use default version if package.json can't be read
|
|
16
|
+
console.warn(`Warning: Could not read version from package.json (${err.message}). Using default 1.0.0`);
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
// Check if no arguments were provided
|
|
@@ -40,7 +41,7 @@ program
|
|
|
40
41
|
try {
|
|
41
42
|
// Resolve all file paths
|
|
42
43
|
const resolvedPaths = files.map(file => path.resolve(file));
|
|
43
|
-
|
|
44
|
+
|
|
44
45
|
// Prepare options for compareFiles
|
|
45
46
|
const comparisonOptions = {
|
|
46
47
|
format: options.format || 'console',
|
|
@@ -50,7 +51,7 @@ program
|
|
|
50
51
|
|
|
51
52
|
// Run the comparison
|
|
52
53
|
compareFiles(resolvedPaths, comparisonOptions);
|
|
53
|
-
|
|
54
|
+
|
|
54
55
|
} catch (error) {
|
|
55
56
|
console.error('Error:', error.message);
|
|
56
57
|
process.exit(1);
|
package/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
import path from "path";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
6
|
import yaml from "js-yaml";
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -115,7 +115,7 @@ function parseFile(filePath) {
|
|
|
115
115
|
default:
|
|
116
116
|
console.error(
|
|
117
117
|
`Warning: Unsupported file extension "${ext}" for file "${filePath}". ` +
|
|
118
|
-
|
|
118
|
+
`Only .properties, .yml, or .yaml are supported. This file will be treated as empty.`
|
|
119
119
|
);
|
|
120
120
|
return {};
|
|
121
121
|
}
|
|
@@ -147,7 +147,7 @@ function compareFileData(filePaths) {
|
|
|
147
147
|
// Compare values for each key across files
|
|
148
148
|
allKeys.forEach((key) => {
|
|
149
149
|
const values = parsedObjects.map(
|
|
150
|
-
(obj) => obj[key]?.
|
|
150
|
+
(obj) => obj[key]?.replaceAll(/\s+/g, "") || "N/A"
|
|
151
151
|
);
|
|
152
152
|
const matched = values.every((value) => value === values[0]);
|
|
153
153
|
mismatchDetails.push({ key, values, matched });
|
|
@@ -224,10 +224,10 @@ function generateHtmlReport(filePaths, comparisonData) {
|
|
|
224
224
|
<h2>Files Compared:</h2>
|
|
225
225
|
<ol>
|
|
226
226
|
${fileNames
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
227
|
+
.map(
|
|
228
|
+
(name, idx) => `<li>${name} <small>(${filePaths[idx]})</small></li>`
|
|
229
|
+
)
|
|
230
|
+
.join("\n ")}
|
|
231
231
|
</ol>
|
|
232
232
|
</div>
|
|
233
233
|
|
|
@@ -237,8 +237,8 @@ function generateHtmlReport(filePaths, comparisonData) {
|
|
|
237
237
|
<th>Key</th>
|
|
238
238
|
<th>Matched</th>
|
|
239
239
|
${fileNames
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
.map((name, idx) => `<th>File ${idx + 1}: ${name}</th>`)
|
|
241
|
+
.join("\n ")}
|
|
242
242
|
</tr>`;
|
|
243
243
|
|
|
244
244
|
// Add table rows for each key
|
|
@@ -250,9 +250,8 @@ function generateHtmlReport(filePaths, comparisonData) {
|
|
|
250
250
|
// Add values from each file
|
|
251
251
|
values.forEach((value, idx) => {
|
|
252
252
|
const cellClass = matched ? "" : "value-mismatch";
|
|
253
|
-
html += `\n <td class="${cellClass}">${
|
|
254
|
-
|
|
255
|
-
}</td>`;
|
|
253
|
+
html += `\n <td class="${cellClass}">${value === "N/A" ? "<em>N/A</em>" : value
|
|
254
|
+
}</td>`;
|
|
256
255
|
});
|
|
257
256
|
|
|
258
257
|
html += `\n </tr>`;
|
|
@@ -268,9 +267,9 @@ function generateHtmlReport(filePaths, comparisonData) {
|
|
|
268
267
|
} else {
|
|
269
268
|
html += `\n <p>${mismatchCount} key(s) have mismatched values.</p>
|
|
270
269
|
<p><strong>Mismatched keys:</strong> ${mismatchDetails
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
270
|
+
.filter((detail) => !detail.matched)
|
|
271
|
+
.map((detail) => detail.key)
|
|
272
|
+
.join(", ")}</p>`;
|
|
274
273
|
}
|
|
275
274
|
|
|
276
275
|
html += `\n </div>
|
|
@@ -419,46 +418,67 @@ function compareFiles(filePaths, options = {}) {
|
|
|
419
418
|
}
|
|
420
419
|
|
|
421
420
|
/**
|
|
422
|
-
*
|
|
421
|
+
* Prints the usage information for the CLI.
|
|
423
422
|
*/
|
|
424
|
-
function
|
|
425
|
-
|
|
423
|
+
function printUsage() {
|
|
424
|
+
console.error("Please provide file paths as command-line arguments.");
|
|
425
|
+
console.error(
|
|
426
|
+
"Usage: properties-comparator [options] file1 file2 [file3...]"
|
|
427
|
+
);
|
|
428
|
+
console.error("Options:");
|
|
429
|
+
console.error(
|
|
430
|
+
" --format, -f <format> Output format: console, html, or markdown"
|
|
431
|
+
);
|
|
432
|
+
console.error(
|
|
433
|
+
" --output, -o <file> Output file for html or markdown reports"
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Parses command-line arguments.
|
|
439
|
+
* @param {string[]} args - Array of command-line arguments.
|
|
440
|
+
* @returns {{ filePaths: string[], options: Object }} - Parsed paths and options.
|
|
441
|
+
*/
|
|
442
|
+
function parseArgs(args) {
|
|
426
443
|
const options = {
|
|
427
444
|
format: "console",
|
|
428
445
|
outputFile: null,
|
|
429
446
|
};
|
|
430
|
-
|
|
431
|
-
// Parse arguments for format and output file
|
|
432
447
|
const filePaths = [];
|
|
433
|
-
|
|
448
|
+
|
|
449
|
+
let i = 0;
|
|
450
|
+
while (i < args.length) {
|
|
434
451
|
if (args[i] === "--format" || args[i] === "-f") {
|
|
435
452
|
if (i + 1 < args.length) {
|
|
436
453
|
options.format = args[i + 1].toLowerCase();
|
|
437
|
-
i
|
|
454
|
+
i += 2;
|
|
455
|
+
} else {
|
|
456
|
+
i++;
|
|
438
457
|
}
|
|
439
458
|
} else if (args[i] === "--output" || args[i] === "-o") {
|
|
440
459
|
if (i + 1 < args.length) {
|
|
441
460
|
options.outputFile = args[i + 1];
|
|
442
|
-
i
|
|
461
|
+
i += 2;
|
|
462
|
+
} else {
|
|
463
|
+
i++;
|
|
443
464
|
}
|
|
444
465
|
} else {
|
|
445
|
-
// Not an option, treat as file path
|
|
446
466
|
filePaths.push(path.resolve(args[i]));
|
|
467
|
+
i++;
|
|
447
468
|
}
|
|
448
469
|
}
|
|
449
470
|
|
|
471
|
+
return { filePaths, options };
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* CLI entry point for comparing .properties and .yml/.yaml files.
|
|
476
|
+
*/
|
|
477
|
+
function run() {
|
|
478
|
+
const { filePaths, options } = parseArgs(process.argv.slice(2));
|
|
479
|
+
|
|
450
480
|
if (filePaths.length === 0) {
|
|
451
|
-
|
|
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
|
-
);
|
|
481
|
+
printUsage();
|
|
462
482
|
process.exit(1);
|
|
463
483
|
} else if (filePaths.length === 1) {
|
|
464
484
|
console.error("Please provide at least two file paths for comparison.");
|
|
@@ -481,27 +501,27 @@ function run() {
|
|
|
481
501
|
* @param {Object} options - Comparison options
|
|
482
502
|
* @returns {Object} Comparison results in a structured format
|
|
483
503
|
*/
|
|
484
|
-
|
|
504
|
+
function compareProperties(file1, file2, options = {}) {
|
|
485
505
|
const filePaths = [file1, file2];
|
|
486
506
|
const comparisonData = compareFileData(filePaths);
|
|
487
|
-
|
|
507
|
+
|
|
488
508
|
// Process the output based on options
|
|
489
509
|
if (options.output) {
|
|
490
510
|
if (options.json) {
|
|
491
511
|
fs.writeFileSync(options.output, JSON.stringify(comparisonData, null, 2));
|
|
492
512
|
} else {
|
|
493
513
|
const format = path.extname(options.output).toLowerCase() === '.md' ? 'markdown' : 'html';
|
|
494
|
-
const report = format === 'markdown'
|
|
495
|
-
? generateMarkdownReport(filePaths, comparisonData)
|
|
514
|
+
const report = format === 'markdown'
|
|
515
|
+
? generateMarkdownReport(filePaths, comparisonData)
|
|
496
516
|
: generateHtmlReport(filePaths, comparisonData);
|
|
497
517
|
fs.writeFileSync(options.output, report);
|
|
498
518
|
}
|
|
499
|
-
|
|
519
|
+
|
|
500
520
|
if (options.verbose) {
|
|
501
521
|
console.log(`Comparison report saved to ${options.output}`);
|
|
502
522
|
}
|
|
503
523
|
}
|
|
504
|
-
|
|
524
|
+
|
|
505
525
|
return comparisonData;
|
|
506
526
|
}
|
|
507
527
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "properties-comparator",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "This utility provides functionality to parse and compare properties files in the format of key-value pairs. It reads properties files, compares the values for each key across multiple files, and logs the results.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "jest",
|
|
12
|
-
"run": "node index.js"
|
|
12
|
+
"run": "node index.js",
|
|
13
|
+
"clean": "rm -rf node_modules coverage"
|
|
13
14
|
},
|
|
14
15
|
"repository": {
|
|
15
16
|
"type": "git",
|
|
@@ -28,15 +29,18 @@
|
|
|
28
29
|
},
|
|
29
30
|
"homepage": "https://github.com/zackria/properties-comparator#readme",
|
|
30
31
|
"dependencies": {
|
|
31
|
-
"chalk": "^5.
|
|
32
|
-
"commander": "^
|
|
33
|
-
"js-yaml": "^4.1.
|
|
32
|
+
"chalk": "^5.6.2",
|
|
33
|
+
"commander": "^14.0.2",
|
|
34
|
+
"js-yaml": "^4.1.1"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
|
-
"@babel/core": "^7.
|
|
37
|
-
"@babel/preset-env": "^7.
|
|
38
|
-
"babel-jest": "^
|
|
39
|
-
"jest": "^
|
|
40
|
-
"jest-environment-node": "^
|
|
37
|
+
"@babel/core": "^7.28.5",
|
|
38
|
+
"@babel/preset-env": "^7.28.5",
|
|
39
|
+
"babel-jest": "^30.2.0",
|
|
40
|
+
"jest": "^30.2.0",
|
|
41
|
+
"jest-environment-node": "^30.2.0"
|
|
42
|
+
},
|
|
43
|
+
"overrides": {
|
|
44
|
+
"test-exclude": "^7.0.1"
|
|
41
45
|
}
|
|
42
|
-
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
sonar.projectKey=zackria_properties-comparator
|
|
2
|
+
sonar.organization=zackria
|
|
3
|
+
|
|
4
|
+
# This is the name and version displayed in the SonarCloud UI.
|
|
5
|
+
#sonar.projectName=properties-comparator
|
|
6
|
+
#sonar.projectVersion=1.0.6
|
|
7
|
+
|
|
8
|
+
# Path is relative to the sonarproject.properties file. Replace "\" by "/" on Windows.
|
|
9
|
+
sonar.sources=index.js,cli.js,src
|
|
10
|
+
sonar.tests=test
|
|
11
|
+
sonar.test.inclusions=test/**/*.test.js
|
|
12
|
+
|
|
13
|
+
# Encoding of the source code. Default is default system encoding
|
|
14
|
+
sonar.sourceEncoding=UTF-8
|
|
15
|
+
|
|
16
|
+
# JavaScript-specific configurations
|
|
17
|
+
sonar.javascript.lcov.reportPaths=coverage/lcov.info
|
|
18
|
+
sonar.exclusions=node_modules/**, coverage/**, index.js
|
|
19
|
+
sonar.cpd.exclusions=index.js
|
package/src/compareUtility.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
import path from "path";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
6
|
import yaml from "js-yaml";
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -115,7 +115,7 @@ function parseFile(filePath) {
|
|
|
115
115
|
default:
|
|
116
116
|
console.error(
|
|
117
117
|
`Warning: Unsupported file extension "${ext}" for file "${filePath}". ` +
|
|
118
|
-
|
|
118
|
+
`Only .properties, .yml, or .yaml are supported. This file will be treated as empty.`
|
|
119
119
|
);
|
|
120
120
|
return {};
|
|
121
121
|
}
|
|
@@ -147,7 +147,7 @@ function compareFileData(filePaths) {
|
|
|
147
147
|
// Compare values for each key across files
|
|
148
148
|
allKeys.forEach((key) => {
|
|
149
149
|
const values = parsedObjects.map(
|
|
150
|
-
(obj) => obj[key]?.
|
|
150
|
+
(obj) => obj[key]?.replaceAll(/\s+/g, "") || "N/A"
|
|
151
151
|
);
|
|
152
152
|
const matched = values.every((value) => value === values[0]);
|
|
153
153
|
mismatchDetails.push({ key, values, matched });
|
|
@@ -224,10 +224,10 @@ function generateHtmlReport(filePaths, comparisonData) {
|
|
|
224
224
|
<h2>Files Compared:</h2>
|
|
225
225
|
<ol>
|
|
226
226
|
${fileNames
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
227
|
+
.map(
|
|
228
|
+
(name, idx) => `<li>${name} <small>(${filePaths[idx]})</small></li>`
|
|
229
|
+
)
|
|
230
|
+
.join("\n ")}
|
|
231
231
|
</ol>
|
|
232
232
|
</div>
|
|
233
233
|
|
|
@@ -237,8 +237,8 @@ function generateHtmlReport(filePaths, comparisonData) {
|
|
|
237
237
|
<th>Key</th>
|
|
238
238
|
<th>Matched</th>
|
|
239
239
|
${fileNames
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
.map((name, idx) => `<th>File ${idx + 1}: ${name}</th>`)
|
|
241
|
+
.join("\n ")}
|
|
242
242
|
</tr>`;
|
|
243
243
|
|
|
244
244
|
// Add table rows for each key
|
|
@@ -250,9 +250,8 @@ function generateHtmlReport(filePaths, comparisonData) {
|
|
|
250
250
|
// Add values from each file
|
|
251
251
|
values.forEach((value, idx) => {
|
|
252
252
|
const cellClass = matched ? "" : "value-mismatch";
|
|
253
|
-
html += `\n <td class="${cellClass}">${
|
|
254
|
-
|
|
255
|
-
}</td>`;
|
|
253
|
+
html += `\n <td class="${cellClass}">${value === "N/A" ? "<em>N/A</em>" : value
|
|
254
|
+
}</td>`;
|
|
256
255
|
});
|
|
257
256
|
|
|
258
257
|
html += `\n </tr>`;
|
|
@@ -268,9 +267,9 @@ function generateHtmlReport(filePaths, comparisonData) {
|
|
|
268
267
|
} else {
|
|
269
268
|
html += `\n <p>${mismatchCount} key(s) have mismatched values.</p>
|
|
270
269
|
<p><strong>Mismatched keys:</strong> ${mismatchDetails
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
270
|
+
.filter((detail) => !detail.matched)
|
|
271
|
+
.map((detail) => detail.key)
|
|
272
|
+
.join(", ")}</p>`;
|
|
274
273
|
}
|
|
275
274
|
|
|
276
275
|
html += `\n </div>
|
|
@@ -419,46 +418,67 @@ function compareFiles(filePaths, options = {}) {
|
|
|
419
418
|
}
|
|
420
419
|
|
|
421
420
|
/**
|
|
422
|
-
*
|
|
421
|
+
* Prints the usage information for the CLI.
|
|
423
422
|
*/
|
|
424
|
-
function
|
|
425
|
-
|
|
423
|
+
function printUsage() {
|
|
424
|
+
console.error("Please provide file paths as command-line arguments.");
|
|
425
|
+
console.error(
|
|
426
|
+
"Usage: properties-comparator [options] file1 file2 [file3...]"
|
|
427
|
+
);
|
|
428
|
+
console.error("Options:");
|
|
429
|
+
console.error(
|
|
430
|
+
" --format, -f <format> Output format: console, html, or markdown"
|
|
431
|
+
);
|
|
432
|
+
console.error(
|
|
433
|
+
" --output, -o <file> Output file for html or markdown reports"
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Parses command-line arguments.
|
|
439
|
+
* @param {string[]} args - Array of command-line arguments.
|
|
440
|
+
* @returns {{ filePaths: string[], options: Object }} - Parsed paths and options.
|
|
441
|
+
*/
|
|
442
|
+
function parseArgs(args) {
|
|
426
443
|
const options = {
|
|
427
444
|
format: "console",
|
|
428
445
|
outputFile: null,
|
|
429
446
|
};
|
|
430
|
-
|
|
431
|
-
// Parse arguments for format and output file
|
|
432
447
|
const filePaths = [];
|
|
433
|
-
|
|
448
|
+
|
|
449
|
+
let i = 0;
|
|
450
|
+
while (i < args.length) {
|
|
434
451
|
if (args[i] === "--format" || args[i] === "-f") {
|
|
435
452
|
if (i + 1 < args.length) {
|
|
436
453
|
options.format = args[i + 1].toLowerCase();
|
|
437
|
-
i
|
|
454
|
+
i += 2;
|
|
455
|
+
} else {
|
|
456
|
+
i++;
|
|
438
457
|
}
|
|
439
458
|
} else if (args[i] === "--output" || args[i] === "-o") {
|
|
440
459
|
if (i + 1 < args.length) {
|
|
441
460
|
options.outputFile = args[i + 1];
|
|
442
|
-
i
|
|
461
|
+
i += 2;
|
|
462
|
+
} else {
|
|
463
|
+
i++;
|
|
443
464
|
}
|
|
444
465
|
} else {
|
|
445
|
-
// Not an option, treat as file path
|
|
446
466
|
filePaths.push(path.resolve(args[i]));
|
|
467
|
+
i++;
|
|
447
468
|
}
|
|
448
469
|
}
|
|
449
470
|
|
|
471
|
+
return { filePaths, options };
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* CLI entry point for comparing .properties and .yml/.yaml files.
|
|
476
|
+
*/
|
|
477
|
+
function run() {
|
|
478
|
+
const { filePaths, options } = parseArgs(process.argv.slice(2));
|
|
479
|
+
|
|
450
480
|
if (filePaths.length === 0) {
|
|
451
|
-
|
|
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
|
-
);
|
|
481
|
+
printUsage();
|
|
462
482
|
process.exit(1);
|
|
463
483
|
} else if (filePaths.length === 1) {
|
|
464
484
|
console.error("Please provide at least two file paths for comparison.");
|
|
@@ -481,27 +501,27 @@ function run() {
|
|
|
481
501
|
* @param {Object} options - Comparison options
|
|
482
502
|
* @returns {Object} Comparison results in a structured format
|
|
483
503
|
*/
|
|
484
|
-
|
|
504
|
+
function compareProperties(file1, file2, options = {}) {
|
|
485
505
|
const filePaths = [file1, file2];
|
|
486
506
|
const comparisonData = compareFileData(filePaths);
|
|
487
|
-
|
|
507
|
+
|
|
488
508
|
// Process the output based on options
|
|
489
509
|
if (options.output) {
|
|
490
510
|
if (options.json) {
|
|
491
511
|
fs.writeFileSync(options.output, JSON.stringify(comparisonData, null, 2));
|
|
492
512
|
} else {
|
|
493
513
|
const format = path.extname(options.output).toLowerCase() === '.md' ? 'markdown' : 'html';
|
|
494
|
-
const report = format === 'markdown'
|
|
495
|
-
? generateMarkdownReport(filePaths, comparisonData)
|
|
514
|
+
const report = format === 'markdown'
|
|
515
|
+
? generateMarkdownReport(filePaths, comparisonData)
|
|
496
516
|
: generateHtmlReport(filePaths, comparisonData);
|
|
497
517
|
fs.writeFileSync(options.output, report);
|
|
498
518
|
}
|
|
499
|
-
|
|
519
|
+
|
|
500
520
|
if (options.verbose) {
|
|
501
521
|
console.log(`Comparison report saved to ${options.output}`);
|
|
502
522
|
}
|
|
503
523
|
}
|
|
504
|
-
|
|
524
|
+
|
|
505
525
|
return comparisonData;
|
|
506
526
|
}
|
|
507
527
|
|