properties-comparator 1.0.3 → 1.0.4
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 +33 -25
- package/README.md +5 -1
- package/TEST.md +15 -0
- package/index.js +225 -47
- package/jest.config.js +22 -0
- package/package.json +10 -3
package/DOCUMENTATION.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
# Node.js Properties File Comparison Utility
|
|
1
|
+
# Node.js YAML and Properties File Comparison Utility
|
|
3
2
|
|
|
4
3
|
## Overview
|
|
5
4
|
|
|
6
|
-
This utility
|
|
5
|
+
This utility parses and compares **.properties** and **.yml or .yaml (YAML)** files. It reads each file as key-value pairs, compares the values for each key across multiple files, and logs the results.
|
|
7
6
|
|
|
8
7
|
### Features:
|
|
9
|
-
- Parse properties files into key-value objects.
|
|
10
|
-
-
|
|
8
|
+
- Parse **.properties** files into key-value objects.
|
|
9
|
+
- Parse **.yml or .yaml** (YAML) files into flattened key-value objects (supports nested keys).
|
|
10
|
+
- Compare key values across multiple files (both **.properties** and **.yml or .yaml**).
|
|
11
11
|
- Log detailed comparison results, including mismatches.
|
|
12
12
|
|
|
13
13
|
---
|
|
@@ -16,7 +16,7 @@ This utility provides functionality to parse and compare properties files in the
|
|
|
16
16
|
|
|
17
17
|
### Prerequisites
|
|
18
18
|
- Node.js installed on your system.
|
|
19
|
-
-
|
|
19
|
+
- Files in valid .properties or .yaml or .yml format.
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
@@ -53,23 +53,11 @@ Parses a properties file and returns an object representation of the key-value p
|
|
|
53
53
|
|
|
54
54
|
#### Example:
|
|
55
55
|
```javascript
|
|
56
|
-
// Assuming properties file contains:
|
|
57
|
-
// key1=value1
|
|
58
|
-
// key2=value2
|
|
59
|
-
|
|
60
56
|
const properties = parsePropertiesFile('/path/to/properties/file');
|
|
61
57
|
console.log(properties);
|
|
62
58
|
// Output: { key1: 'value1', key2: 'value2' }
|
|
63
59
|
```
|
|
64
60
|
|
|
65
|
-
#### Implementation:
|
|
66
|
-
- Reads the file content using `fs.readFileSync`.
|
|
67
|
-
- Splits lines by `
|
|
68
|
-
` or `
|
|
69
|
-
`.
|
|
70
|
-
- Filters out empty lines and lines starting with `#`.
|
|
71
|
-
- Processes valid lines into key-value pairs.
|
|
72
|
-
|
|
73
61
|
---
|
|
74
62
|
|
|
75
63
|
### `compareProperties(filePaths)`
|
|
@@ -82,15 +70,9 @@ Compares properties across multiple properties files and logs the results.
|
|
|
82
70
|
#### Returns:
|
|
83
71
|
- None. Outputs comparison results to the console.
|
|
84
72
|
|
|
85
|
-
#### Behavior:
|
|
86
|
-
- Parses each properties file into an object.
|
|
87
|
-
- Collects all unique keys across files.
|
|
88
|
-
- Compares values for each key across all files.
|
|
89
|
-
- Logs whether values match or are mismatched. For mismatches, displays values for each file.
|
|
90
|
-
|
|
91
73
|
#### Example:
|
|
92
74
|
```bash
|
|
93
|
-
node script.js file1.properties file2.
|
|
75
|
+
node script.js file1.properties file2.yaml file3.yml
|
|
94
76
|
```
|
|
95
77
|
|
|
96
78
|
#### Output Example:
|
|
@@ -121,6 +103,31 @@ The script expects file paths as command-line arguments:
|
|
|
121
103
|
|
|
122
104
|
---
|
|
123
105
|
|
|
106
|
+
### Additional Functions
|
|
107
|
+
|
|
108
|
+
#### `parseFile(filePath)`
|
|
109
|
+
Detects file extension and parses `.properties`, `.yaml`, or `.yml` files. Returns an object with flattened key-value pairs or `{}` if unsupported.
|
|
110
|
+
|
|
111
|
+
#### `parseYamlFile(filePath)`
|
|
112
|
+
Parses a `.yml` or `.yaml` file into a flat key-value map. Returns an object with dot-notation keys for nested values.
|
|
113
|
+
|
|
114
|
+
#### `compareFileData(filePaths)`
|
|
115
|
+
Internally compares parsed data from multiple files. Returns an object with `mismatchCount` and detailed info for each key.
|
|
116
|
+
|
|
117
|
+
#### `checkIfAllValuesMatch(filePaths)`
|
|
118
|
+
Checks if all keys match across all provided files. Returns a boolean.
|
|
119
|
+
|
|
120
|
+
#### `getMismatchFields(filePaths)`
|
|
121
|
+
Returns an array of keys whose values differ across files.
|
|
122
|
+
|
|
123
|
+
#### `compareFiles(filePaths)`
|
|
124
|
+
Logs detailed comparison of key-value pairs across files. Prints a summary indicating mismatched keys.
|
|
125
|
+
|
|
126
|
+
#### `run()`
|
|
127
|
+
CLI entry point. Reads file paths, checks existence, and calls `compareFiles`.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
124
131
|
## Error Handling
|
|
125
132
|
|
|
126
133
|
- **No File Paths Provided**: Logs an error and exits.
|
|
@@ -167,6 +174,7 @@ Key: key2 - Mismatched values:
|
|
|
167
174
|
## Dependencies
|
|
168
175
|
|
|
169
176
|
- `fs` module (Node.js File System)
|
|
177
|
+
- `js-yaml` module (Node.js YAML library)
|
|
170
178
|
|
|
171
179
|
---
|
|
172
180
|
|
package/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# properties-comparator
|
|
2
|
-
This utility provides functionality to parse and compare properties files in the format of key-value pairs. It reads
|
|
2
|
+
This utility provides functionality to parse and compare **.properties** and **.yml or .yaml(YAML)** files in the format of key-value pairs. It reads these files, compares the values for each key across multiple files, and logs the results.
|
|
3
|
+
|
|
3
4
|
|
|
4
5
|
Utility is available as NPM Package [https://www.npmjs.com/package/properties-comparator](https://www.npmjs.com/package/properties-comparator)
|
|
5
6
|
|
|
@@ -12,5 +13,8 @@ Utility is available as NPM Package [https://www.npmjs.com/package/properties-co
|
|
|
12
13
|
|
|
13
14
|
```properties-comparator <filePath1> <filePath2>```
|
|
14
15
|
|
|
16
|
+
## Compiled on npm@11.1.0 & node v22.13.0
|
|
17
|
+
`npm install -g npm@11.1.0`
|
|
18
|
+
|
|
15
19
|
|
|
16
20
|
### Check [Documentation](DOCUMENTATION.md) for more details
|
package/TEST.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Automated testing with Jest
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## Run Your Tests
|
|
5
|
+
`npm init -y # if you haven't created a package.json yet`
|
|
6
|
+
`npm install --save-dev jest`
|
|
7
|
+
|
|
8
|
+
`npm test`
|
|
9
|
+
or
|
|
10
|
+
`npx jest`
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Check jest.config.js for testing configuration
|
|
14
|
+
|
|
15
|
+
`npm test -- --coverage`
|
package/index.js
CHANGED
|
@@ -1,75 +1,253 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const fs = require(
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const yaml = require("js-yaml");
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
|
-
* Parses a properties file into an object.
|
|
8
|
+
* Parses a .properties file into an object.
|
|
9
|
+
* Handles any file read/parse errors gracefully.
|
|
7
10
|
* @param {string} filePath - The path to the properties file.
|
|
8
|
-
* @returns {Object} -
|
|
11
|
+
* @returns {Object} - Key-value pairs, or {} on error.
|
|
9
12
|
*/
|
|
10
13
|
function parsePropertiesFile(filePath) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
try {
|
|
15
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
16
|
+
const lines = content.split(/\r?\n/);
|
|
17
|
+
const result = {};
|
|
18
|
+
|
|
19
|
+
for (let line of lines) {
|
|
20
|
+
let trimmedLine = line.trim();
|
|
21
|
+
|
|
22
|
+
// 1) Skip empty lines or lines that *start* with '#'
|
|
23
|
+
if (!trimmedLine || trimmedLine.startsWith("#")) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 2) Remove inline comment: anything after the first '#'
|
|
28
|
+
const hashIndex = trimmedLine.indexOf("#");
|
|
29
|
+
if (hashIndex !== -1) {
|
|
30
|
+
trimmedLine = trimmedLine.slice(0, hashIndex).trim();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 3) Split on the *first* '=' only
|
|
34
|
+
const eqIndex = trimmedLine.indexOf("=");
|
|
35
|
+
if (eqIndex === -1) {
|
|
36
|
+
// No '=' => Not a valid key-value line
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const key = trimmedLine.slice(0, eqIndex).trim();
|
|
41
|
+
const value = trimmedLine.slice(eqIndex + 1).trim();
|
|
42
|
+
|
|
43
|
+
if (key) {
|
|
44
|
+
result[key] = value;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return result;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.error(
|
|
51
|
+
`Error reading/parsing .properties file (${filePath}):`,
|
|
52
|
+
err.message
|
|
53
|
+
);
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
21
56
|
}
|
|
22
57
|
|
|
23
58
|
/**
|
|
24
|
-
*
|
|
59
|
+
* Flattens a nested object into a single-level object using dot-notation for nested keys.
|
|
60
|
+
* @param {Object} obj - The object to flatten.
|
|
61
|
+
* @param {string} [parentKey=''] - The current parent key (used in recursion).
|
|
62
|
+
* @param {Object} [res={}] - The accumulator object.
|
|
63
|
+
* @returns {Object} - A flattened key-value map.
|
|
64
|
+
*/
|
|
65
|
+
function flattenObject(obj, parentKey = "", res = {}) {
|
|
66
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
67
|
+
const newKey = parentKey ? `${parentKey}.${key}` : key;
|
|
68
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
69
|
+
flattenObject(value, newKey, res);
|
|
70
|
+
} else {
|
|
71
|
+
// Ensure all values are strings for consistent comparison
|
|
72
|
+
res[newKey] = String(value);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return res;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Parses a .yml or .yaml file into a flat key-value map.
|
|
80
|
+
* Handles any file read/parse errors gracefully.
|
|
81
|
+
* @param {string} filePath - The path to the YAML file.
|
|
82
|
+
* @returns {Object} - A flattened key-value map, or {} on error.
|
|
83
|
+
*/
|
|
84
|
+
function parseYamlFile(filePath) {
|
|
85
|
+
try {
|
|
86
|
+
const fileContents = fs.readFileSync(filePath, "utf-8");
|
|
87
|
+
const data = yaml.load(fileContents);
|
|
88
|
+
return flattenObject(data);
|
|
89
|
+
} catch (err) {
|
|
90
|
+
console.error(
|
|
91
|
+
`Error reading/parsing YAML file (${filePath}):`,
|
|
92
|
+
err.message
|
|
93
|
+
);
|
|
94
|
+
return {};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Detects file extension and parses the file content into an object.
|
|
100
|
+
* Currently supports .properties, .yaml, and .yml.
|
|
101
|
+
* If extension is unsupported, logs a warning and returns {}.
|
|
102
|
+
* @param {string} filePath - The path to the file.
|
|
103
|
+
* @returns {Object} - Parsed content as a key-value map, or {} if unsupported.
|
|
104
|
+
*/
|
|
105
|
+
function parseFile(filePath) {
|
|
106
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
107
|
+
|
|
108
|
+
switch (ext) {
|
|
109
|
+
case ".properties":
|
|
110
|
+
return parsePropertiesFile(filePath);
|
|
111
|
+
case ".yml":
|
|
112
|
+
case ".yaml":
|
|
113
|
+
return parseYamlFile(filePath);
|
|
114
|
+
default:
|
|
115
|
+
console.error(
|
|
116
|
+
`Warning: Unsupported file extension "${ext}" for file "${filePath}". ` +
|
|
117
|
+
`Only .properties, .yml, or .yaml are supported. This file will be treated as empty.`
|
|
118
|
+
);
|
|
119
|
+
return {};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Internal helper that compares key-value data from multiple files
|
|
125
|
+
* and returns a structured result (without printing to console).
|
|
126
|
+
*
|
|
25
127
|
* @param {string[]} filePaths - Array of file paths.
|
|
128
|
+
* @returns {{
|
|
129
|
+
* mismatchCount: number,
|
|
130
|
+
* mismatchDetails: {
|
|
131
|
+
* key: string,
|
|
132
|
+
* values: string[],
|
|
133
|
+
* matched: boolean
|
|
134
|
+
* }[]
|
|
135
|
+
* }}
|
|
26
136
|
*/
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
137
|
+
function compareFileData(filePaths) {
|
|
138
|
+
// Parse each file
|
|
139
|
+
const parsedObjects = filePaths.map(parseFile);
|
|
140
|
+
|
|
141
|
+
// Collect all unique keys
|
|
142
|
+
const allKeys = new Set(parsedObjects.flatMap((obj) => Object.keys(obj)));
|
|
143
|
+
|
|
144
|
+
const mismatchDetails = [];
|
|
145
|
+
|
|
146
|
+
// Compare values for each key across files
|
|
147
|
+
allKeys.forEach((key) => {
|
|
148
|
+
const values = parsedObjects.map(
|
|
149
|
+
(obj) => obj[key]?.replace(/\s+/g, "") || "N/A"
|
|
150
|
+
);
|
|
151
|
+
const matched = values.every((value) => value === values[0]);
|
|
152
|
+
mismatchDetails.push({ key, values, matched });
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Count mismatches
|
|
156
|
+
const mismatchCount = mismatchDetails.filter((d) => !d.matched).length;
|
|
157
|
+
return { mismatchCount, mismatchDetails };
|
|
46
158
|
}
|
|
47
159
|
|
|
48
160
|
/**
|
|
49
|
-
*
|
|
161
|
+
* Helper function: checks if all values match across the provided files.
|
|
162
|
+
*
|
|
163
|
+
* @param {string[]} filePaths - Array of file paths.
|
|
164
|
+
* @returns {boolean} - True if all properties match across all files, false otherwise.
|
|
50
165
|
*/
|
|
51
|
-
function
|
|
52
|
-
|
|
166
|
+
function checkIfAllValuesMatch(filePaths) {
|
|
167
|
+
const { mismatchCount } = compareFileData(filePaths);
|
|
168
|
+
return mismatchCount === 0;
|
|
169
|
+
}
|
|
53
170
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
171
|
+
/**
|
|
172
|
+
* Helper function: returns a list of fields (keys) that do not match.
|
|
173
|
+
*
|
|
174
|
+
* @param {string[]} filePaths - Array of file paths.
|
|
175
|
+
* @returns {string[]} - List of mismatched keys.
|
|
176
|
+
*/
|
|
177
|
+
function getMismatchFields(filePaths) {
|
|
178
|
+
const { mismatchDetails } = compareFileData(filePaths);
|
|
179
|
+
return mismatchDetails
|
|
180
|
+
.filter((detail) => !detail.matched)
|
|
181
|
+
.map((detail) => detail.key);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* CLI function: compares properties/keys across multiple files,
|
|
186
|
+
* prints details to the console, and provides a summary.
|
|
187
|
+
*
|
|
188
|
+
* @param {string[]} filePaths - Array of file paths.
|
|
189
|
+
*/
|
|
190
|
+
function compareFiles(filePaths) {
|
|
191
|
+
console.log("Comparing properties/keys across files:\n");
|
|
192
|
+
|
|
193
|
+
const { mismatchCount, mismatchDetails } = compareFileData(filePaths);
|
|
58
194
|
|
|
59
|
-
|
|
60
|
-
|
|
195
|
+
// Print detail for each key
|
|
196
|
+
mismatchDetails.forEach(({ key, values, matched }) => {
|
|
197
|
+
if (matched) {
|
|
198
|
+
console.log(`Key: ${key} - Values match: '${values[0]}'`);
|
|
61
199
|
} else {
|
|
62
|
-
|
|
200
|
+
console.log(`Key: ${key} - Mismatched values:`);
|
|
201
|
+
values.forEach((value, idx) => {
|
|
202
|
+
console.log(` File ${idx + 1} (${filePaths[idx]}): ${value}`);
|
|
203
|
+
});
|
|
63
204
|
}
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Summary
|
|
208
|
+
console.log("\n=== Summary ===");
|
|
209
|
+
if (mismatchCount === 0) {
|
|
210
|
+
console.log("All properties match across all files!");
|
|
211
|
+
} else {
|
|
212
|
+
console.log(`${mismatchCount} key(s) have mismatched values.`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* CLI entry point for comparing .properties and .yml/.yaml files.
|
|
218
|
+
*/
|
|
219
|
+
function run() {
|
|
220
|
+
const filePaths = process.argv.slice(2);
|
|
221
|
+
|
|
222
|
+
if (filePaths.length === 0) {
|
|
223
|
+
console.error("Please provide file paths as command-line arguments.");
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Optionally, check if all files exist
|
|
228
|
+
const missing = filePaths.filter((fp) => !fs.existsSync(fp));
|
|
229
|
+
if (missing.length > 0) {
|
|
230
|
+
console.error(`The following file(s) do not exist: ${missing.join(", ")}`);
|
|
231
|
+
// We continue anyway, or we can decide to exit.
|
|
232
|
+
// For now, let's exit to avoid unexpected comparisons:
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
compareFiles(filePaths);
|
|
64
237
|
}
|
|
65
238
|
|
|
66
239
|
module.exports = {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
240
|
+
parsePropertiesFile,
|
|
241
|
+
parseYamlFile,
|
|
242
|
+
parseFile,
|
|
243
|
+
compareFileData,
|
|
244
|
+
checkIfAllValuesMatch,
|
|
245
|
+
getMismatchFields,
|
|
246
|
+
compareFiles,
|
|
247
|
+
run,
|
|
70
248
|
};
|
|
71
249
|
|
|
72
250
|
// If the script is executed directly, run the CLI
|
|
73
251
|
if (require.main === module) {
|
|
74
|
-
|
|
252
|
+
run();
|
|
75
253
|
}
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// jest.config.js
|
|
2
|
+
module.exports = {
|
|
3
|
+
// Tell Jest to collect coverage information
|
|
4
|
+
collectCoverage: true,
|
|
5
|
+
// Or specify which files to collect coverage from
|
|
6
|
+
collectCoverageFrom: ['src/**/*.js'], // adjust for your source folder
|
|
7
|
+
|
|
8
|
+
// Optionally set coverage thresholds
|
|
9
|
+
coverageThreshold: {
|
|
10
|
+
global: {
|
|
11
|
+
branches: 80,
|
|
12
|
+
functions: 80,
|
|
13
|
+
lines: 80,
|
|
14
|
+
statements: 80,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
// Customize output directory or coverage reporters if desired
|
|
19
|
+
coverageDirectory: 'coverage',
|
|
20
|
+
coverageReporters: ['json', 'lcov', 'text', 'clover'],
|
|
21
|
+
};
|
|
22
|
+
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "properties-comparator",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
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
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"properties-comparator": "./index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"test": "
|
|
10
|
+
"test": "jest",
|
|
11
|
+
"run": "node index.js"
|
|
11
12
|
},
|
|
12
13
|
"repository": {
|
|
13
14
|
"type": "git",
|
|
@@ -22,5 +23,11 @@
|
|
|
22
23
|
"bugs": {
|
|
23
24
|
"url": "https://github.com/zackria/properties-comparator/issues"
|
|
24
25
|
},
|
|
25
|
-
"homepage": "https://github.com/zackria/properties-comparator#readme"
|
|
26
|
+
"homepage": "https://github.com/zackria/properties-comparator#readme",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"js-yaml": "^4.1.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"jest": "^29.7.0"
|
|
32
|
+
}
|
|
26
33
|
}
|