@prantlf/jsonlint 11.1.1 → 11.2.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 +8 -2
- package/lib/cli.js +86 -51
- package/package.json +3 -2
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,7 +95,9 @@ 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
|
|
@@ -112,6 +114,10 @@ By default, `jsonlint` will either report a syntax error with details or pretty-
|
|
|
112
114
|
-v, --version output the version number
|
|
113
115
|
-h, --help output usage information
|
|
114
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
|
+
|
|
115
121
|
Parsing mode can be "cjson" or "json5" to enable other flags automatically.
|
|
116
122
|
If no files or directories are specified, stdin will be parsed. Environments
|
|
117
123
|
for JSON schema validation are "json-schema-draft-04", "json-schema-draft-06"
|
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,7 +28,9 @@ 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')
|
|
@@ -39,8 +40,12 @@ const commander = require('commander')
|
|
|
39
40
|
.option('--enforce-double-quotes', 'surrounds all strings with double quotes')
|
|
40
41
|
.option('--enforce-single-quotes', 'surrounds all strings with single quotes (JSON5)')
|
|
41
42
|
.option('--trim-trailing-commas', 'omit trailing commas from objects and arrays (JSON5)')
|
|
42
|
-
.version(
|
|
43
|
+
.version(version, '-v, --version')
|
|
43
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 "!".')
|
|
44
49
|
console.log()
|
|
45
50
|
console.log('Parsing mode can be "cjson" or "json5" to enable other flags automatically.')
|
|
46
51
|
console.log('If no files or directories are specified, stdin will be parsed. Environments')
|
|
@@ -50,8 +55,12 @@ const commander = require('commander')
|
|
|
50
55
|
.parse(process.argv)
|
|
51
56
|
|
|
52
57
|
const options = commander.opts()
|
|
58
|
+
const extensions = options.extensions.map(extension => '.' + extension)
|
|
53
59
|
|
|
54
60
|
function logNormalError (error, file) {
|
|
61
|
+
if (process.exitCode > 0) {
|
|
62
|
+
console.log()
|
|
63
|
+
}
|
|
55
64
|
console.log('File:', file)
|
|
56
65
|
console.error(error.message)
|
|
57
66
|
}
|
|
@@ -61,7 +70,7 @@ function logCompactError (error, file) {
|
|
|
61
70
|
', col ' + error.location.start.column + ', ' + error.reason + '.')
|
|
62
71
|
}
|
|
63
72
|
|
|
64
|
-
function
|
|
73
|
+
function processContents (source, file) {
|
|
65
74
|
let parserOptions, parsed, formatted
|
|
66
75
|
try {
|
|
67
76
|
parserOptions = {
|
|
@@ -74,9 +83,9 @@ function parse (source, file) {
|
|
|
74
83
|
if (options.validate) {
|
|
75
84
|
let validate
|
|
76
85
|
try {
|
|
77
|
-
const schema =
|
|
86
|
+
const schema = readFileSync(normalize(options.validate), 'utf8')
|
|
78
87
|
parserOptions.environment = options.environment
|
|
79
|
-
validate =
|
|
88
|
+
validate = compile(schema, parserOptions)
|
|
80
89
|
} catch (error) {
|
|
81
90
|
const message = 'Loading the JSON schema failed: "' +
|
|
82
91
|
options.validate + '".\n' + error.message
|
|
@@ -84,13 +93,13 @@ function parse (source, file) {
|
|
|
84
93
|
}
|
|
85
94
|
parsed = validate(source, parserOptions)
|
|
86
95
|
} else {
|
|
87
|
-
parsed =
|
|
96
|
+
parsed = parse(source, parserOptions)
|
|
88
97
|
}
|
|
89
98
|
if (options.prettyPrint) {
|
|
90
99
|
parserOptions.rawTokens = true
|
|
91
|
-
const tokens =
|
|
100
|
+
const tokens = tokenize(source, parserOptions)
|
|
92
101
|
// TODO: Support sorting tor the tokenized input too.
|
|
93
|
-
return
|
|
102
|
+
return print(tokens, {
|
|
94
103
|
indent: options.indent,
|
|
95
104
|
pruneComments: options.pruneComments,
|
|
96
105
|
stripObjectKeys: options.stripObjectKeys,
|
|
@@ -100,7 +109,7 @@ function parse (source, file) {
|
|
|
100
109
|
})
|
|
101
110
|
}
|
|
102
111
|
if (options.sortKeys) {
|
|
103
|
-
parsed =
|
|
112
|
+
parsed = sortObject(parsed)
|
|
104
113
|
}
|
|
105
114
|
return JSON.stringify(parsed, null, options.indent)
|
|
106
115
|
} catch (e) {
|
|
@@ -111,9 +120,9 @@ function parse (source, file) {
|
|
|
111
120
|
* manual formatter because the automatic one is faster and probably more reliable.
|
|
112
121
|
*/
|
|
113
122
|
try {
|
|
114
|
-
formatted =
|
|
123
|
+
formatted = format(source, options.indent)
|
|
115
124
|
// Re-parse so exception output gets better line numbers
|
|
116
|
-
parsed =
|
|
125
|
+
parsed = parse(formatted)
|
|
117
126
|
} catch (e) {
|
|
118
127
|
if (options.compact) {
|
|
119
128
|
logCompactError(e, file)
|
|
@@ -130,14 +139,21 @@ function parse (source, file) {
|
|
|
130
139
|
logNormalError(e, file)
|
|
131
140
|
}
|
|
132
141
|
}
|
|
133
|
-
|
|
142
|
+
if (options.continue) {
|
|
143
|
+
process.exitCode = 1
|
|
144
|
+
} else {
|
|
145
|
+
process.exit(1)
|
|
146
|
+
}
|
|
134
147
|
}
|
|
135
148
|
}
|
|
136
149
|
|
|
137
150
|
function processFile (file) {
|
|
138
|
-
file =
|
|
139
|
-
|
|
140
|
-
|
|
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)
|
|
141
157
|
if (options.inPlace) {
|
|
142
158
|
const lines = original.split(/\?r\n/)
|
|
143
159
|
const newLine = !lines[lines.length - 1]
|
|
@@ -145,56 +161,75 @@ function processFile (file) {
|
|
|
145
161
|
(options.trailingNewline !== false && newLine)) {
|
|
146
162
|
source += '\n'
|
|
147
163
|
}
|
|
148
|
-
|
|
164
|
+
writeFileSync(file, source)
|
|
149
165
|
} else {
|
|
150
|
-
if (!options.quiet) {
|
|
166
|
+
if (!(options.quiet || options.logFiles)) {
|
|
151
167
|
console.log(source)
|
|
152
168
|
}
|
|
153
169
|
}
|
|
154
170
|
}
|
|
155
171
|
|
|
156
|
-
function
|
|
157
|
-
const extensions = options.extensions.map(function (extension) {
|
|
158
|
-
return '.' + extension
|
|
159
|
-
})
|
|
160
|
-
let srcStat
|
|
172
|
+
function processSource (src, checkExtension) {
|
|
161
173
|
try {
|
|
162
|
-
srcStat =
|
|
174
|
+
const srcStat = statSync(src)
|
|
163
175
|
if (srcStat.isFile()) {
|
|
164
176
|
if (checkExtension) {
|
|
165
|
-
const ext =
|
|
177
|
+
const ext = extname(src)
|
|
166
178
|
if (extensions.indexOf(ext) < 0) {
|
|
167
179
|
return
|
|
168
180
|
}
|
|
169
181
|
}
|
|
170
182
|
processFile(src)
|
|
171
183
|
} else if (srcStat.isDirectory()) {
|
|
172
|
-
const sources =
|
|
173
|
-
for (
|
|
174
|
-
|
|
184
|
+
const sources = readdirSync(src)
|
|
185
|
+
for (const source of sources) {
|
|
186
|
+
processSource(join(src, source), true)
|
|
175
187
|
}
|
|
176
188
|
}
|
|
177
|
-
} catch (
|
|
178
|
-
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
|
+
}
|
|
179
206
|
}
|
|
180
207
|
}
|
|
181
208
|
|
|
182
209
|
function main () {
|
|
183
|
-
const files = commander
|
|
184
|
-
let source = ''
|
|
210
|
+
const { args: files } = commander
|
|
185
211
|
if (files.length) {
|
|
186
|
-
|
|
187
|
-
|
|
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
|
+
}
|
|
188
219
|
}
|
|
189
220
|
} else {
|
|
221
|
+
let source = ''
|
|
190
222
|
const stdin = process.openStdin()
|
|
191
223
|
stdin.setEncoding('utf8')
|
|
192
|
-
stdin.on('data',
|
|
224
|
+
stdin.on('data', chunk => {
|
|
193
225
|
source += chunk.toString('utf8')
|
|
194
226
|
})
|
|
195
|
-
stdin.on('end',
|
|
196
|
-
|
|
197
|
-
|
|
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)) {
|
|
198
233
|
console.log(parsed)
|
|
199
234
|
}
|
|
200
235
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prantlf/jsonlint",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.2.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": [
|
|
@@ -76,7 +76,8 @@
|
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
78
|
"ajv": "6.12.6",
|
|
79
|
-
"commander": "9.2.0"
|
|
79
|
+
"commander": "9.2.0",
|
|
80
|
+
"fast-glob": "3.2.11"
|
|
80
81
|
},
|
|
81
82
|
"devDependencies": {
|
|
82
83
|
"@semantic-release/changelog": "^6.0.1",
|