@prantlf/jsonlint 11.3.0 → 11.6.0
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/README.md +23 -0
- package/lib/cli.js +115 -23
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@ This is a fork of the original project ([zaach/jsonlint](https://github.com/zaac
|
|
|
14
14
|
* Provides 100% compatible interface to the native `JSON.parse` method.
|
|
15
15
|
* Optionally recognizes JavaScript-style comments (CJSON) and single quoted strings (JSON5).
|
|
16
16
|
* Optionally ignores trailing commas and reports duplicate object keys as an error.
|
|
17
|
+
* Optionally checks that also the expected format matches, including sorted object keys.
|
|
17
18
|
* Supports [JSON Schema] drafts 04, 06 and 07.
|
|
18
19
|
* Offers pretty-printing including comment-stripping and object keys without quotes (JSON5).
|
|
19
20
|
* Prefers the native JSON parser if possible to run [7x faster than the custom parser].
|
|
@@ -92,6 +93,23 @@ The same parameters can be passed from a configuration file:
|
|
|
92
93
|
}
|
|
93
94
|
```
|
|
94
95
|
|
|
96
|
+
The input can be checked not only to be a valid JSON, but also to be formatted according to the coding standard. For example, check that there is a trailing li break in each JSON file, in addition to alphabetically sorted keys and no duplicate keys:
|
|
97
|
+
|
|
98
|
+
$ jsonlint -ksDr *.json
|
|
99
|
+
|
|
100
|
+
File: package.json
|
|
101
|
+
Formatted output differs
|
|
102
|
+
===================================================================
|
|
103
|
+
--- package.json.orig
|
|
104
|
+
+++ package.json
|
|
105
|
+
@@ -105,4 +105,4 @@
|
|
106
|
+
"lint",
|
|
107
|
+
"jsonlint"
|
|
108
|
+
]
|
|
109
|
+
-}
|
|
110
|
+
+}
|
|
111
|
+
|
|
112
|
+
|
|
95
113
|
### Usage
|
|
96
114
|
|
|
97
115
|
Usage: `jsonlint [options] [<file, directory, pattern> ...]`
|
|
@@ -104,6 +122,8 @@ Usage: `jsonlint [options] [<file, directory, pattern> ...]`
|
|
|
104
122
|
-E, --extensions [ext] file extensions to process for directory walk
|
|
105
123
|
(default: ["json","JSON"])
|
|
106
124
|
-i, --in-place overwrite the input files
|
|
125
|
+
-j, --diff print difference instead of writing the output
|
|
126
|
+
-k, --check check that the input is equal to the output
|
|
107
127
|
-t, --indent [num|char] number of spaces or specific characters
|
|
108
128
|
to use for indentation (default: 2)
|
|
109
129
|
-c, --compact compact error display
|
|
@@ -116,6 +136,7 @@ Usage: `jsonlint [options] [<file, directory, pattern> ...]`
|
|
|
116
136
|
-V, --validate [file] JSON schema file to use for validation
|
|
117
137
|
-e, --environment [env] which specification of JSON Schema the
|
|
118
138
|
validation file uses
|
|
139
|
+
-x, --context [num] line count used as the diff context (default: 3)
|
|
119
140
|
-l, --log-files print only the parsed file names to stdout
|
|
120
141
|
-q, --quiet do not print the parsed json to stdout
|
|
121
142
|
-n, --continue continue with other files if an error occurs
|
|
@@ -168,6 +189,8 @@ The configuration is an object with the following properties, described above, w
|
|
|
168
189
|
| sort-keys | sortKeys |
|
|
169
190
|
| extensions | |
|
|
170
191
|
| in-place | inPlace |
|
|
192
|
+
| diff | |
|
|
193
|
+
| check | |
|
|
171
194
|
| indent | |
|
|
172
195
|
| compact | |
|
|
173
196
|
| mode | |
|
package/lib/cli.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
const { readdirSync, readFileSync, statSync, writeFileSync } = require('fs')
|
|
4
4
|
const { extname, join, normalize } = require('path')
|
|
5
5
|
const { isDynamicPattern, sync } = require('fast-glob')
|
|
6
|
-
const { cosmiconfigSync } = require('cosmiconfig')
|
|
7
6
|
const { parse, tokenize } = require('./jsonlint')
|
|
8
7
|
const { format } = require('./formatter')
|
|
9
8
|
const { print } = require('./printer')
|
|
@@ -22,6 +21,8 @@ const commander = require('commander')
|
|
|
22
21
|
.option('-s, --sort-keys', 'sort object keys (not when prettifying)')
|
|
23
22
|
.option('-E, --extensions [ext]', 'file extensions to process for directory walk', collectValues, ['json', 'JSON'])
|
|
24
23
|
.option('-i, --in-place', 'overwrite the input files')
|
|
24
|
+
.option('-j, --diff', 'print difference instead of writing the output')
|
|
25
|
+
.option('-k, --check', 'check that the input is equal to the output')
|
|
25
26
|
.option('-t, --indent [num|char]', 'number of spaces or specific characters to use for indentation', 2)
|
|
26
27
|
.option('-c, --compact', 'compact error display')
|
|
27
28
|
.option('-M, --mode [mode]', 'set other parsing flags according to a format type', 'json')
|
|
@@ -31,6 +32,7 @@ const commander = require('commander')
|
|
|
31
32
|
.option('-D, --no-duplicate-keys', 'report duplicate object keys as an error')
|
|
32
33
|
.option('-V, --validate [file]', 'JSON schema file to use for validation')
|
|
33
34
|
.option('-e, --environment [env]', 'which specification of JSON Schema the validation file uses')
|
|
35
|
+
.option('-x, --context [num]', 'line count used as the diff context', 3)
|
|
34
36
|
.option('-l, --log-files', 'print only the parsed file names to stdout')
|
|
35
37
|
.option('-q, --quiet', 'do not print the parsed json to stdout')
|
|
36
38
|
.option('-n, --continue', 'continue with other files if an error occurs')
|
|
@@ -79,6 +81,7 @@ let options
|
|
|
79
81
|
if (params.config === false) {
|
|
80
82
|
options = params
|
|
81
83
|
} else {
|
|
84
|
+
const { cosmiconfigSync } = require('cosmiconfig')
|
|
82
85
|
const configurator = cosmiconfigSync('jsonlint')
|
|
83
86
|
const { config = {} } = (params.config && configurator.load(params.config)) ||
|
|
84
87
|
configurator.search() || {}
|
|
@@ -86,6 +89,7 @@ if (params.config === false) {
|
|
|
86
89
|
}
|
|
87
90
|
|
|
88
91
|
const extensions = options.extensions.map(extension => '.' + extension)
|
|
92
|
+
let reported
|
|
89
93
|
|
|
90
94
|
function convertConfig (config) {
|
|
91
95
|
const result = {}
|
|
@@ -107,11 +111,17 @@ function mergeOptions (target, ...sources) {
|
|
|
107
111
|
return target
|
|
108
112
|
}
|
|
109
113
|
|
|
110
|
-
function
|
|
111
|
-
if (
|
|
114
|
+
function separateBlocks () {
|
|
115
|
+
if (reported) {
|
|
112
116
|
console.log()
|
|
117
|
+
} else {
|
|
118
|
+
reported = true
|
|
113
119
|
}
|
|
114
|
-
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function logNormalError (error, file) {
|
|
123
|
+
separateBlocks()
|
|
124
|
+
console.info('File:', file)
|
|
115
125
|
console.error(error.message)
|
|
116
126
|
}
|
|
117
127
|
|
|
@@ -197,24 +207,96 @@ function processContents (source, file) {
|
|
|
197
207
|
}
|
|
198
208
|
}
|
|
199
209
|
|
|
210
|
+
function ensureLineBreak (parsed, source) {
|
|
211
|
+
const lines = source.split(/\r?\n/)
|
|
212
|
+
const newLine = !lines[lines.length - 1]
|
|
213
|
+
if (options.trailingNewline === true ||
|
|
214
|
+
(options.trailingNewline !== false && newLine)) {
|
|
215
|
+
parsed += '\n'
|
|
216
|
+
}
|
|
217
|
+
return parsed
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function checkContents (file, source, parsed) {
|
|
221
|
+
const { createTwoFilesPatch, structuredPatch } = require('diff')
|
|
222
|
+
const structured = structuredPatch(`${file}.orig`, file, source, parsed, '', '', { context: options.context })
|
|
223
|
+
const length = structured.hunks && structured.hunks.length
|
|
224
|
+
if (length > 0) {
|
|
225
|
+
const hunk = length === 1 ? 'hunk differs' : 'hunks differ'
|
|
226
|
+
const message = `${length} ${hunk}`
|
|
227
|
+
if (options.compact) {
|
|
228
|
+
console.error(`${file}: ${message}`)
|
|
229
|
+
} else {
|
|
230
|
+
separateBlocks()
|
|
231
|
+
console.info('File:', file)
|
|
232
|
+
console.error(message)
|
|
233
|
+
}
|
|
234
|
+
if (!options.quiet) {
|
|
235
|
+
const diff = createTwoFilesPatch(`${file}.orig`, file, source, parsed, '', '', { context: options.context })
|
|
236
|
+
console.log(diff)
|
|
237
|
+
}
|
|
238
|
+
if (options.continue) {
|
|
239
|
+
process.exitCode = 1
|
|
240
|
+
} else {
|
|
241
|
+
process.exit(1)
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
if (options.compact) {
|
|
245
|
+
console.info(`${file}: no difference`)
|
|
246
|
+
} else if (options.logFiles) {
|
|
247
|
+
console.info(file)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function diffContents (file, source, parsed) {
|
|
253
|
+
const { createTwoFilesPatch, structuredPatch } = require('diff')
|
|
254
|
+
const compact = options.quiet || options.compact
|
|
255
|
+
let diff, length
|
|
256
|
+
if (compact) {
|
|
257
|
+
diff = structuredPatch(`${file}.orig`, file, source, parsed, '', '', { context: options.context })
|
|
258
|
+
length = diff.hunks && diff.hunks.length
|
|
259
|
+
} else {
|
|
260
|
+
diff = createTwoFilesPatch(`${file}.orig`, file, source, parsed, '', '', { context: options.context })
|
|
261
|
+
length = diff.split(/\r?\n/).length - 4
|
|
262
|
+
}
|
|
263
|
+
if (length > 0) {
|
|
264
|
+
if (compact) {
|
|
265
|
+
const hunk = length === 1 ? 'hunk differs' : 'hunks differ'
|
|
266
|
+
console.info(`${file}: ${length} ${hunk}`)
|
|
267
|
+
} else {
|
|
268
|
+
separateBlocks()
|
|
269
|
+
console.info('File:', file)
|
|
270
|
+
console.log(diff)
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
if (options.compact) {
|
|
274
|
+
console.info(`${file}: no difference`)
|
|
275
|
+
} else if (options.logFiles) {
|
|
276
|
+
console.info(file)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
200
281
|
function processFile (file) {
|
|
201
282
|
file = normalize(file)
|
|
202
|
-
if (options.logFiles) {
|
|
203
|
-
console.
|
|
283
|
+
if (options.logFiles && !(options.compact || options.check || options.diff)) {
|
|
284
|
+
console.info(file)
|
|
204
285
|
}
|
|
205
|
-
const
|
|
206
|
-
|
|
286
|
+
const source = readFileSync(file, 'utf8')
|
|
287
|
+
const parsed = processContents(source, file)
|
|
207
288
|
if (options.inPlace) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (options.trailingNewline === true ||
|
|
211
|
-
(options.trailingNewline !== false && newLine)) {
|
|
212
|
-
source += '\n'
|
|
289
|
+
if (options.logFiles && options.compact) {
|
|
290
|
+
console.info(file)
|
|
213
291
|
}
|
|
214
|
-
writeFileSync(file, source)
|
|
292
|
+
writeFileSync(file, ensureLineBreak(parsed, source))
|
|
293
|
+
} else if (options.check) {
|
|
294
|
+
checkContents(file, source, ensureLineBreak(parsed, source))
|
|
295
|
+
} else if (options.diff) {
|
|
296
|
+
diffContents(file, source, ensureLineBreak(parsed, source))
|
|
215
297
|
} else {
|
|
216
298
|
if (!(options.quiet || options.logFiles)) {
|
|
217
|
-
console.log(
|
|
299
|
+
console.log(parsed)
|
|
218
300
|
}
|
|
219
301
|
}
|
|
220
302
|
}
|
|
@@ -237,21 +319,21 @@ function processSource (src, checkExtension) {
|
|
|
237
319
|
}
|
|
238
320
|
}
|
|
239
321
|
} catch ({ message }) {
|
|
240
|
-
console.
|
|
322
|
+
console.warn('WARN', message)
|
|
241
323
|
}
|
|
242
324
|
}
|
|
243
325
|
|
|
244
326
|
function processPatterns (patterns) {
|
|
245
327
|
const files = sync(patterns, { onlyFiles: true })
|
|
246
328
|
if (!files.length) {
|
|
247
|
-
console.
|
|
329
|
+
console.error('no files found')
|
|
248
330
|
process.exit(1)
|
|
249
331
|
}
|
|
250
332
|
for (const file of files) {
|
|
251
333
|
try {
|
|
252
334
|
processFile(file)
|
|
253
335
|
} catch ({ message }) {
|
|
254
|
-
console.
|
|
336
|
+
console.warn('WARN', message)
|
|
255
337
|
}
|
|
256
338
|
}
|
|
257
339
|
}
|
|
@@ -278,12 +360,22 @@ function main () {
|
|
|
278
360
|
source += chunk.toString('utf8')
|
|
279
361
|
})
|
|
280
362
|
stdin.on('end', () => {
|
|
281
|
-
|
|
282
|
-
|
|
363
|
+
const file = '<stdin>'
|
|
364
|
+
if (options.logFiles && !(options.compact || options.check || options.diff)) {
|
|
365
|
+
console.info(file)
|
|
283
366
|
}
|
|
284
|
-
const parsed = processContents(source,
|
|
285
|
-
if (
|
|
286
|
-
|
|
367
|
+
const parsed = processContents(source, file)
|
|
368
|
+
if (options.check) {
|
|
369
|
+
checkContents(file, source, ensureLineBreak(parsed, source))
|
|
370
|
+
} else if (options.diff) {
|
|
371
|
+
diffContents(file, source, ensureLineBreak(parsed, source))
|
|
372
|
+
} else {
|
|
373
|
+
if (options.logFiles && options.compact) {
|
|
374
|
+
console.info(file)
|
|
375
|
+
}
|
|
376
|
+
if (!(options.quiet || options.logFiles)) {
|
|
377
|
+
console.log(parsed)
|
|
378
|
+
}
|
|
287
379
|
}
|
|
288
380
|
})
|
|
289
381
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prantlf/jsonlint",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.6.0",
|
|
4
4
|
"description": "JSON/CJSON/JSON5 parser, syntax and schema validator and pretty-printer.",
|
|
5
5
|
"author": "Ferdinand Prantl <prantlf@gmail.com> (http://prantl.tk)",
|
|
6
6
|
"contributors": [
|
|
@@ -77,15 +77,16 @@
|
|
|
77
77
|
"dependencies": {
|
|
78
78
|
"ajv": "6.12.6",
|
|
79
79
|
"commander": "9.2.0",
|
|
80
|
-
"cosmiconfig": "
|
|
80
|
+
"cosmiconfig": "7.0.1",
|
|
81
|
+
"diff": "5.0.0",
|
|
81
82
|
"fast-glob": "3.2.11"
|
|
82
83
|
},
|
|
83
84
|
"devDependencies": {
|
|
84
|
-
"@semantic-release/changelog": "
|
|
85
|
-
"@semantic-release/git": "
|
|
86
|
-
"@types/node": "17.0.
|
|
87
|
-
"@typescript-eslint/eslint-plugin": "5.
|
|
88
|
-
"@typescript-eslint/parser": "5.
|
|
85
|
+
"@semantic-release/changelog": "6.0.1",
|
|
86
|
+
"@semantic-release/git": "10.0.1",
|
|
87
|
+
"@types/node": "17.0.31",
|
|
88
|
+
"@typescript-eslint/eslint-plugin": "5.22.0",
|
|
89
|
+
"@typescript-eslint/parser": "5.22.0",
|
|
89
90
|
"eslint": "8.14.0",
|
|
90
91
|
"eslint-config-standard": "17.0.0",
|
|
91
92
|
"eslint-plugin-import": "2.26.0",
|