@prantlf/jsonlint 11.1.0 → 11.2.1
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 +9 -3
- package/lib/cli.js +91 -51
- package/package.json +26 -18
- package/web/jsonlint.html +3 -3
package/README.md
CHANGED
|
@@ -74,7 +74,7 @@ By default, `jsonlint` will either report a syntax error with details or pretty-
|
|
|
74
74
|
|
|
75
75
|
$ jsonlint -h
|
|
76
76
|
|
|
77
|
-
Usage: jsonlint [options] [<file
|
|
77
|
+
Usage: jsonlint [options] [<file, directory, pattern> ...]
|
|
78
78
|
|
|
79
79
|
JSON parser, syntax and schema validator and pretty-printer.
|
|
80
80
|
|
|
@@ -95,11 +95,14 @@ By default, `jsonlint` will either report a syntax error with details or pretty-
|
|
|
95
95
|
-V, --validate [file] JSON schema file to use for validation
|
|
96
96
|
-e, --environment [env] which specification of JSON Schema the
|
|
97
97
|
validation file uses
|
|
98
|
-
-
|
|
98
|
+
-l, --log-files print only the parsed file names to stdout
|
|
99
|
+
-q, --quiet do not print the parsed json to stdout
|
|
100
|
+
-n, --continue continue with other files if an error occurs
|
|
99
101
|
-p, --pretty-print prettify the input instead of stringifying
|
|
100
102
|
the parsed object
|
|
101
103
|
-P, --pretty-print-invalid force pretty-printing even for invalid input
|
|
102
104
|
-r, --trailing-newline ensure a line break at the end of the output
|
|
105
|
+
-R, --no-trailing-newline ensure no line break at the end of the output
|
|
103
106
|
--prune-comments omit comments from the prettified output
|
|
104
107
|
--strip-object-keys strip quotes from object keys if possible
|
|
105
108
|
(JSON5)
|
|
@@ -111,6 +114,10 @@ By default, `jsonlint` will either report a syntax error with details or pretty-
|
|
|
111
114
|
-v, --version output the version number
|
|
112
115
|
-h, --help output usage information
|
|
113
116
|
|
|
117
|
+
You can use BASH patterns for including and excluding files (only files).
|
|
118
|
+
Patterns are case-sensitive and have to use slashes as a path separators.
|
|
119
|
+
A pattern to exclude from processing starts with "!".
|
|
120
|
+
|
|
114
121
|
Parsing mode can be "cjson" or "json5" to enable other flags automatically.
|
|
115
122
|
If no files or directories are specified, stdin will be parsed. Environments
|
|
116
123
|
for JSON schema validation are "json-schema-draft-04", "json-schema-draft-06"
|
|
@@ -324,7 +331,6 @@ Licensed under the [MIT License].
|
|
|
324
331
|
|
|
325
332
|
[MIT License]: http://en.wikipedia.org/wiki/MIT_License
|
|
326
333
|
[pure JavaScript version]: http://prantlf.github.com/jsonlint/
|
|
327
|
-
[jsonlint.com]: http://jsonlint.com
|
|
328
334
|
[JSON]: https://tools.ietf.org/html/rfc8259
|
|
329
335
|
[JSON5]: https://spec.json5.org
|
|
330
336
|
[JSON Schema]: https://json-schema.org
|
package/lib/cli.js
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
3
|
+
const { readdirSync, readFileSync, statSync, writeFileSync } = require('fs')
|
|
4
|
+
const { extname, join, normalize } = require('path')
|
|
5
|
+
const { isDynamicPattern, sync } = require('fast-glob')
|
|
6
|
+
const { parse, tokenize } = require('./jsonlint')
|
|
7
|
+
const { format } = require('./formatter')
|
|
8
|
+
const { print } = require('./printer')
|
|
9
|
+
const { sortObject } = require('./sorter')
|
|
10
|
+
const { compile } = require('./validator')
|
|
11
|
+
const { description, version } = require('../package')
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
return extension.split(',')
|
|
14
|
-
}
|
|
13
|
+
const collectValues = extension => extension.split(',')
|
|
15
14
|
|
|
16
15
|
const commander = require('commander')
|
|
17
16
|
.name('jsonlint')
|
|
18
|
-
.usage('[options] [<file
|
|
19
|
-
.description(
|
|
17
|
+
.usage('[options] [<file, directory, pattern> ...]')
|
|
18
|
+
.description(description)
|
|
20
19
|
.option('-s, --sort-keys', 'sort object keys (not when prettifying)')
|
|
21
|
-
.option('-E, --extensions [ext]', 'file extensions to process for directory walk',
|
|
20
|
+
.option('-E, --extensions [ext]', 'file extensions to process for directory walk', collectValues, ['json', 'JSON'])
|
|
22
21
|
.option('-i, --in-place', 'overwrite the input files')
|
|
23
22
|
.option('-t, --indent [num|char]', 'number of spaces or specific characters to use for indentation', 2)
|
|
24
23
|
.option('-c, --compact', 'compact error display')
|
|
@@ -29,17 +28,24 @@ const commander = require('commander')
|
|
|
29
28
|
.option('-D, --no-duplicate-keys', 'report duplicate object keys as an error')
|
|
30
29
|
.option('-V, --validate [file]', 'JSON schema file to use for validation')
|
|
31
30
|
.option('-e, --environment [env]', 'which specification of JSON Schema the validation file uses')
|
|
32
|
-
.option('-
|
|
31
|
+
.option('-l, --log-files', 'print only the parsed file names to stdout')
|
|
32
|
+
.option('-q, --quiet', 'do not print the parsed json to stdout')
|
|
33
|
+
.option('-n, --continue', 'continue with other files if an error occurs')
|
|
33
34
|
.option('-p, --pretty-print', 'prettify the input instead of stringifying the parsed object')
|
|
34
35
|
.option('-P, --pretty-print-invalid', 'force pretty-printing even for invalid input')
|
|
35
36
|
.option('-r, --trailing-newline', 'ensure a line break at the end of the output')
|
|
37
|
+
.option('-R, --no-trailing-newline', 'ensure no line break at the end of the output')
|
|
36
38
|
.option('--prune-comments', 'omit comments from the prettified output')
|
|
37
39
|
.option('--strip-object-keys', 'strip quotes from object keys if possible (JSON5)')
|
|
38
40
|
.option('--enforce-double-quotes', 'surrounds all strings with double quotes')
|
|
39
41
|
.option('--enforce-single-quotes', 'surrounds all strings with single quotes (JSON5)')
|
|
40
42
|
.option('--trim-trailing-commas', 'omit trailing commas from objects and arrays (JSON5)')
|
|
41
|
-
.version(
|
|
43
|
+
.version(version, '-v, --version')
|
|
42
44
|
.on('--help', () => {
|
|
45
|
+
console.log()
|
|
46
|
+
console.log('You can use BASH patterns for including and excluding files (only files).')
|
|
47
|
+
console.log('Patterns are case-sensitive and have to use slashes as a path separators.')
|
|
48
|
+
console.log('A pattern to exclude from processing starts with "!".')
|
|
43
49
|
console.log()
|
|
44
50
|
console.log('Parsing mode can be "cjson" or "json5" to enable other flags automatically.')
|
|
45
51
|
console.log('If no files or directories are specified, stdin will be parsed. Environments')
|
|
@@ -49,8 +55,12 @@ const commander = require('commander')
|
|
|
49
55
|
.parse(process.argv)
|
|
50
56
|
|
|
51
57
|
const options = commander.opts()
|
|
58
|
+
const extensions = options.extensions.map(extension => '.' + extension)
|
|
52
59
|
|
|
53
60
|
function logNormalError (error, file) {
|
|
61
|
+
if (process.exitCode > 0) {
|
|
62
|
+
console.log()
|
|
63
|
+
}
|
|
54
64
|
console.log('File:', file)
|
|
55
65
|
console.error(error.message)
|
|
56
66
|
}
|
|
@@ -60,7 +70,7 @@ function logCompactError (error, file) {
|
|
|
60
70
|
', col ' + error.location.start.column + ', ' + error.reason + '.')
|
|
61
71
|
}
|
|
62
72
|
|
|
63
|
-
function
|
|
73
|
+
function processContents (source, file) {
|
|
64
74
|
let parserOptions, parsed, formatted
|
|
65
75
|
try {
|
|
66
76
|
parserOptions = {
|
|
@@ -73,9 +83,9 @@ function parse (source, file) {
|
|
|
73
83
|
if (options.validate) {
|
|
74
84
|
let validate
|
|
75
85
|
try {
|
|
76
|
-
const schema =
|
|
86
|
+
const schema = readFileSync(normalize(options.validate), 'utf8')
|
|
77
87
|
parserOptions.environment = options.environment
|
|
78
|
-
validate =
|
|
88
|
+
validate = compile(schema, parserOptions)
|
|
79
89
|
} catch (error) {
|
|
80
90
|
const message = 'Loading the JSON schema failed: "' +
|
|
81
91
|
options.validate + '".\n' + error.message
|
|
@@ -83,13 +93,13 @@ function parse (source, file) {
|
|
|
83
93
|
}
|
|
84
94
|
parsed = validate(source, parserOptions)
|
|
85
95
|
} else {
|
|
86
|
-
parsed =
|
|
96
|
+
parsed = parse(source, parserOptions)
|
|
87
97
|
}
|
|
88
98
|
if (options.prettyPrint) {
|
|
89
99
|
parserOptions.rawTokens = true
|
|
90
|
-
const tokens =
|
|
100
|
+
const tokens = tokenize(source, parserOptions)
|
|
91
101
|
// TODO: Support sorting tor the tokenized input too.
|
|
92
|
-
return
|
|
102
|
+
return print(tokens, {
|
|
93
103
|
indent: options.indent,
|
|
94
104
|
pruneComments: options.pruneComments,
|
|
95
105
|
stripObjectKeys: options.stripObjectKeys,
|
|
@@ -99,7 +109,7 @@ function parse (source, file) {
|
|
|
99
109
|
})
|
|
100
110
|
}
|
|
101
111
|
if (options.sortKeys) {
|
|
102
|
-
parsed =
|
|
112
|
+
parsed = sortObject(parsed)
|
|
103
113
|
}
|
|
104
114
|
return JSON.stringify(parsed, null, options.indent)
|
|
105
115
|
} catch (e) {
|
|
@@ -110,9 +120,9 @@ function parse (source, file) {
|
|
|
110
120
|
* manual formatter because the automatic one is faster and probably more reliable.
|
|
111
121
|
*/
|
|
112
122
|
try {
|
|
113
|
-
formatted =
|
|
123
|
+
formatted = format(source, options.indent)
|
|
114
124
|
// Re-parse so exception output gets better line numbers
|
|
115
|
-
parsed =
|
|
125
|
+
parsed = parse(formatted)
|
|
116
126
|
} catch (e) {
|
|
117
127
|
if (options.compact) {
|
|
118
128
|
logCompactError(e, file)
|
|
@@ -129,67 +139,97 @@ function parse (source, file) {
|
|
|
129
139
|
logNormalError(e, file)
|
|
130
140
|
}
|
|
131
141
|
}
|
|
132
|
-
|
|
142
|
+
if (options.continue) {
|
|
143
|
+
process.exitCode = 1
|
|
144
|
+
} else {
|
|
145
|
+
process.exit(1)
|
|
146
|
+
}
|
|
133
147
|
}
|
|
134
148
|
}
|
|
135
149
|
|
|
136
150
|
function processFile (file) {
|
|
137
|
-
file =
|
|
138
|
-
|
|
151
|
+
file = normalize(file)
|
|
152
|
+
if (options.logFiles) {
|
|
153
|
+
console.log(file)
|
|
154
|
+
}
|
|
155
|
+
const original = readFileSync(file, 'utf8')
|
|
156
|
+
let source = processContents(original, file)
|
|
139
157
|
if (options.inPlace) {
|
|
140
|
-
|
|
158
|
+
const lines = original.split(/\r?\n/)
|
|
159
|
+
const newLine = !lines[lines.length - 1]
|
|
160
|
+
if (options.trailingNewline === true ||
|
|
161
|
+
(options.trailingNewline !== false && newLine)) {
|
|
141
162
|
source += '\n'
|
|
142
163
|
}
|
|
143
|
-
|
|
164
|
+
writeFileSync(file, source)
|
|
144
165
|
} else {
|
|
145
|
-
if (!options.quiet) {
|
|
166
|
+
if (!(options.quiet || options.logFiles)) {
|
|
146
167
|
console.log(source)
|
|
147
168
|
}
|
|
148
169
|
}
|
|
149
170
|
}
|
|
150
171
|
|
|
151
|
-
function
|
|
152
|
-
const extensions = options.extensions.map(function (extension) {
|
|
153
|
-
return '.' + extension
|
|
154
|
-
})
|
|
155
|
-
let srcStat
|
|
172
|
+
function processSource (src, checkExtension) {
|
|
156
173
|
try {
|
|
157
|
-
srcStat =
|
|
174
|
+
const srcStat = statSync(src)
|
|
158
175
|
if (srcStat.isFile()) {
|
|
159
176
|
if (checkExtension) {
|
|
160
|
-
const ext =
|
|
177
|
+
const ext = extname(src)
|
|
161
178
|
if (extensions.indexOf(ext) < 0) {
|
|
162
179
|
return
|
|
163
180
|
}
|
|
164
181
|
}
|
|
165
182
|
processFile(src)
|
|
166
183
|
} else if (srcStat.isDirectory()) {
|
|
167
|
-
const sources =
|
|
168
|
-
for (
|
|
169
|
-
|
|
184
|
+
const sources = readdirSync(src)
|
|
185
|
+
for (const source of sources) {
|
|
186
|
+
processSource(join(src, source), true)
|
|
170
187
|
}
|
|
171
188
|
}
|
|
172
|
-
} catch (
|
|
173
|
-
console.log('WARN',
|
|
189
|
+
} catch ({ message }) {
|
|
190
|
+
console.log('WARN', message)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function processPatterns (patterns) {
|
|
195
|
+
const files = sync(patterns, { onlyFiles: true })
|
|
196
|
+
if (!files.length) {
|
|
197
|
+
console.log('no files found')
|
|
198
|
+
process.exit(1)
|
|
199
|
+
}
|
|
200
|
+
for (const file of files) {
|
|
201
|
+
try {
|
|
202
|
+
processFile(file)
|
|
203
|
+
} catch ({ message }) {
|
|
204
|
+
console.log('WARN', message)
|
|
205
|
+
}
|
|
174
206
|
}
|
|
175
207
|
}
|
|
176
208
|
|
|
177
209
|
function main () {
|
|
178
|
-
const files = commander
|
|
179
|
-
let source = ''
|
|
210
|
+
const { args: files } = commander
|
|
180
211
|
if (files.length) {
|
|
181
|
-
|
|
182
|
-
|
|
212
|
+
const dynamic = files.some(file => isDynamicPattern(file))
|
|
213
|
+
if (dynamic) {
|
|
214
|
+
processPatterns(files)
|
|
215
|
+
} else {
|
|
216
|
+
for (const file of files) {
|
|
217
|
+
processSource(file, false)
|
|
218
|
+
}
|
|
183
219
|
}
|
|
184
220
|
} else {
|
|
221
|
+
let source = ''
|
|
185
222
|
const stdin = process.openStdin()
|
|
186
223
|
stdin.setEncoding('utf8')
|
|
187
|
-
stdin.on('data',
|
|
224
|
+
stdin.on('data', chunk => {
|
|
188
225
|
source += chunk.toString('utf8')
|
|
189
226
|
})
|
|
190
|
-
stdin.on('end',
|
|
191
|
-
|
|
192
|
-
|
|
227
|
+
stdin.on('end', () => {
|
|
228
|
+
if (options.logFiles) {
|
|
229
|
+
console.log('<stdin>')
|
|
230
|
+
}
|
|
231
|
+
const parsed = processContents(source, '<stdin>')
|
|
232
|
+
if (!(options.quiet || options.logFiles)) {
|
|
193
233
|
console.log(parsed)
|
|
194
234
|
}
|
|
195
235
|
})
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prantlf/jsonlint",
|
|
3
|
-
"version": "11.1
|
|
3
|
+
"version": "11.2.1",
|
|
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": [
|
|
7
|
+
"Ondrej Medek <xmedeko@gmail.com>",
|
|
7
8
|
"Greg Inman <ginman@itriagehealth.com>",
|
|
8
9
|
"Paul Vollmer <mail@paulvollmer.net> (http://paulvollmer.net)",
|
|
9
10
|
"Zach Carter <zach@carter.name> (http://zaa.ch)"
|
|
@@ -58,28 +59,29 @@
|
|
|
58
59
|
"text"
|
|
59
60
|
]
|
|
60
61
|
},
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
62
|
+
"release": {
|
|
63
|
+
"plugins": [
|
|
64
|
+
"@semantic-release/commit-analyzer",
|
|
65
|
+
"@semantic-release/release-notes-generator",
|
|
66
|
+
"@semantic-release/changelog",
|
|
67
|
+
"@semantic-release/npm",
|
|
68
|
+
[
|
|
69
|
+
"@semantic-release/github",
|
|
70
|
+
{
|
|
71
|
+
"failComment": false
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
"@semantic-release/git"
|
|
70
75
|
]
|
|
71
76
|
},
|
|
72
|
-
"keywords": [
|
|
73
|
-
"json",
|
|
74
|
-
"validation",
|
|
75
|
-
"lint",
|
|
76
|
-
"jsonlint"
|
|
77
|
-
],
|
|
78
77
|
"dependencies": {
|
|
79
78
|
"ajv": "6.12.6",
|
|
80
|
-
"commander": "9.2.0"
|
|
79
|
+
"commander": "9.2.0",
|
|
80
|
+
"fast-glob": "3.2.11"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
|
+
"@semantic-release/changelog": "^6.0.1",
|
|
84
|
+
"@semantic-release/git": "^10.0.1",
|
|
83
85
|
"@types/node": "17.0.30",
|
|
84
86
|
"@typescript-eslint/eslint-plugin": "5.21.0",
|
|
85
87
|
"@typescript-eslint/parser": "5.21.0",
|
|
@@ -94,5 +96,11 @@
|
|
|
94
96
|
"terser": "5.13.1",
|
|
95
97
|
"test": "0.6.0",
|
|
96
98
|
"typescript": "4.6.4"
|
|
97
|
-
}
|
|
99
|
+
},
|
|
100
|
+
"keywords": [
|
|
101
|
+
"json",
|
|
102
|
+
"validation",
|
|
103
|
+
"lint",
|
|
104
|
+
"jsonlint"
|
|
105
|
+
]
|
|
98
106
|
}
|
package/web/jsonlint.html
CHANGED
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
<body>
|
|
80
80
|
<header>
|
|
81
81
|
<h1>JSON Lint</h1>
|
|
82
|
-
<p>Enter data and optionally schema in the form below to validate them
|
|
82
|
+
<p>Enter data and optionally schema in the form below to validate them.</p>
|
|
83
83
|
</header>
|
|
84
84
|
<main>
|
|
85
85
|
<section>
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
</div>
|
|
112
112
|
<div>
|
|
113
113
|
<input type="checkbox" checked id="duplicate-object-keys">
|
|
114
|
-
<label for="
|
|
114
|
+
<label for="duplicate-object-keys">Allow duplicate object keys</label>
|
|
115
115
|
</div>
|
|
116
116
|
</div>
|
|
117
117
|
<div>
|
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
</main>
|
|
180
180
|
<hr>
|
|
181
181
|
<footer>
|
|
182
|
-
<small>Copyright © 2012-
|
|
182
|
+
<small>Copyright © 2012-2022 Zachary Carter, Ferdinand Prantl. See the <a href="https://github.com/prantlf/jsonlint#json-lint">project pages</a> to learn about command-line validation and programmatic usage.</small>
|
|
183
183
|
<!-- See http://tholman.com/github-corners/ -->
|
|
184
184
|
<a href="http://github.com/prantlf/jsonlint" class="github-corner" title="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
|
|
185
185
|
</footer>
|