@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 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,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
- -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
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 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,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('-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')
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(pkg.version, '-v, --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 parse (source, file) {
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 = fs.readFileSync(path.normalize(options.validate), 'utf8')
86
+ const schema = readFileSync(normalize(options.validate), 'utf8')
77
87
  parserOptions.environment = options.environment
78
- validate = validator.compile(schema, parserOptions)
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 = parser.parse(source, parserOptions)
96
+ parsed = parse(source, parserOptions)
87
97
  }
88
98
  if (options.prettyPrint) {
89
99
  parserOptions.rawTokens = true
90
- const tokens = parser.tokenize(source, parserOptions)
100
+ const tokens = tokenize(source, parserOptions)
91
101
  // TODO: Support sorting tor the tokenized input too.
92
- return printer.print(tokens, {
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 = sorter.sortObject(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 = formatter.format(source, options.indent)
123
+ formatted = format(source, options.indent)
114
124
  // Re-parse so exception output gets better line numbers
115
- parsed = parser.parse(formatted)
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
- process.exit(1)
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 = path.normalize(file)
138
- let source = parse(fs.readFileSync(file, 'utf8'), 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)
139
157
  if (options.inPlace) {
140
- if (options.trailingNewline) {
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
- fs.writeFileSync(file, source)
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 processSources (src, checkExtension) {
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 = fs.statSync(src)
174
+ const srcStat = statSync(src)
158
175
  if (srcStat.isFile()) {
159
176
  if (checkExtension) {
160
- const ext = path.extname(src)
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 = fs.readdirSync(src)
168
- for (let i = 0; i < sources.length; i++) {
169
- processSources(path.join(src, sources[i]), true)
184
+ const sources = readdirSync(src)
185
+ for (const source of sources) {
186
+ processSource(join(src, source), true)
170
187
  }
171
188
  }
172
- } catch (err) {
173
- 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
+ }
174
206
  }
175
207
  }
176
208
 
177
209
  function main () {
178
- const files = commander.args
179
- let source = ''
210
+ const { args: files } = commander
180
211
  if (files.length) {
181
- for (let i = 0; i < files.length; i++) {
182
- 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
+ }
183
219
  }
184
220
  } else {
221
+ let source = ''
185
222
  const stdin = process.openStdin()
186
223
  stdin.setEncoding('utf8')
187
- stdin.on('data', function (chunk) {
224
+ stdin.on('data', chunk => {
188
225
  source += chunk.toString('utf8')
189
226
  })
190
- stdin.on('end', function () {
191
- const parsed = parse(source, '<stdin>')
192
- 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)) {
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.0",
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
- "standard": {
62
- "ignore": [
63
- "benchmarks/jison",
64
- "benchmarks/pegjs",
65
- "jsonlint*.js",
66
- "test/fails",
67
- "test/passes",
68
- "test/recursive",
69
- "test/v8"
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. A pure JavaScript version of the service provided at <a href="http://jsonlint.com/">jsonlint.com</a>.</p>
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="single-quoted-strings">Allow duplicate object keys</label>
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 &copy; 2012-2019 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>
182
+ <small>Copyright &copy; 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>