@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.
Files changed (3) hide show
  1. package/README.md +8 -2
  2. package/lib/cli.js +86 -51
  3. 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 or directory> ...]
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
- -q, --quiet do not print the parsed json to stdin
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 fs = require('fs')
4
- const path = require('path')
5
- const parser = require('./jsonlint')
6
- const formatter = require('./formatter')
7
- const printer = require('./printer')
8
- const sorter = require('./sorter')
9
- const validator = require('./validator')
10
- const pkg = require('../package')
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
- function collectExtensions (extension) {
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 or directory> ...]')
19
- .description(pkg.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', collectExtensions, ['json', 'JSON'])
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('-q, --quiet', 'do not print the parsed json to stdin')
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(pkg.version, '-v, --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 parse (source, file) {
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 = fs.readFileSync(path.normalize(options.validate), 'utf8')
86
+ const schema = readFileSync(normalize(options.validate), 'utf8')
78
87
  parserOptions.environment = options.environment
79
- validate = validator.compile(schema, parserOptions)
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 = parser.parse(source, parserOptions)
96
+ parsed = parse(source, parserOptions)
88
97
  }
89
98
  if (options.prettyPrint) {
90
99
  parserOptions.rawTokens = true
91
- const tokens = parser.tokenize(source, parserOptions)
100
+ const tokens = tokenize(source, parserOptions)
92
101
  // TODO: Support sorting tor the tokenized input too.
93
- return printer.print(tokens, {
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 = sorter.sortObject(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 = formatter.format(source, options.indent)
123
+ formatted = format(source, options.indent)
115
124
  // Re-parse so exception output gets better line numbers
116
- parsed = parser.parse(formatted)
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
- process.exit(1)
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 = path.normalize(file)
139
- const original = fs.readFileSync(file, 'utf8')
140
- let source = parse(original, file)
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
- fs.writeFileSync(file, source)
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 processSources (src, checkExtension) {
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 = fs.statSync(src)
174
+ const srcStat = statSync(src)
163
175
  if (srcStat.isFile()) {
164
176
  if (checkExtension) {
165
- const ext = path.extname(src)
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 = fs.readdirSync(src)
173
- for (let i = 0; i < sources.length; i++) {
174
- processSources(path.join(src, sources[i]), true)
184
+ const sources = readdirSync(src)
185
+ for (const source of sources) {
186
+ processSource(join(src, source), true)
175
187
  }
176
188
  }
177
- } catch (err) {
178
- console.log('WARN', err.message)
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.args
184
- let source = ''
210
+ const { args: files } = commander
185
211
  if (files.length) {
186
- for (let i = 0; i < files.length; i++) {
187
- processSources(files[i], false)
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', function (chunk) {
224
+ stdin.on('data', chunk => {
193
225
  source += chunk.toString('utf8')
194
226
  })
195
- stdin.on('end', function () {
196
- const parsed = parse(source, '<stdin>')
197
- if (!options.quiet) {
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.1.1",
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",