@prantlf/jsonlint 11.2.0 → 11.4.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 +146 -59
  2. package/lib/cli.js +97 -15
  3. package/package.json +9 -7
package/README.md CHANGED
@@ -14,13 +14,15 @@ 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].
20
- * Reports errors with rich additional information. From the schema validation too.
21
- * Implements JavaScript modules using [UMD] to work everywhere.
21
+ * Reports errors with rich additional information. From the JSON Schema validation too.
22
+ * Consumes configuration from both command line and [configuration files](configuration).
23
+ * Implements JavaScript modules using [UMD] to work in Node.js, in a browser, everywhere.
22
24
  * Depends on up-to-date npm modules with no installation warnings.
23
- * Small size - 18.2 kB minified, 6.3 kB gzipped.
25
+ * Small size - 18.7 kB minified, 6.54 kB gzipped, 5.16 kB brotlied.
24
26
 
25
27
  **Note:** In comparison with the original project, this package exports only the `parse` method; not the `Parser` object.
26
28
 
@@ -52,9 +54,11 @@ Example of an error message:
52
54
 
53
55
  ## Command-line Interface
54
56
 
55
- Install `jsonlint` with `npm`` globally to be able to use the command-line interface in any directory:
57
+ Install `jsonlint` with `npm`, `pnpm` or `yarn` globally to be able to use the command-line interface in any directory:
56
58
 
57
- npm i @prantlf/jsonlint -g
59
+ npm i -g @prantlf/jsonlint
60
+ pnpm i -g @prantlf/jsonlint
61
+ yarn add --global @prantlf/jsonlint
58
62
 
59
63
  Validate a single file:
60
64
 
@@ -64,64 +68,147 @@ or pipe the JSON input into `stdin`:
64
68
 
65
69
  cat myfile.json | jsonlint
66
70
 
67
- or process all `.json` files in a directory:
71
+ or process all `.json` files in a directory and rewriting them with the pretty-printed output:
68
72
 
69
- jsonlint mydir
73
+ jsonlint --in-place --pretty-print mydir
70
74
 
71
75
  By default, `jsonlint` will either report a syntax error with details or pretty-print the source if it is valid.
72
76
 
73
- ### Options
74
-
75
- $ jsonlint -h
76
-
77
- Usage: jsonlint [options] [<file, directory, pattern> ...]
78
-
79
- JSON parser, syntax and schema validator and pretty-printer.
80
-
81
- Options:
82
- -s, --sort-keys sort object keys (not when prettifying)
83
- -E, --extensions [ext] file extensions to process for directory walk
84
- (default: ["json","JSON"])
85
- -i, --in-place overwrite the input files
86
- -t, --indent [num|char] number of spaces or specific characters
87
- to use for indentation (default: 2)
88
- -c, --compact compact error display
89
- -M, --mode [mode] set other parsing flags according to a format
90
- type (default: "json")
91
- -C, --comments recognize and ignore JavaScript-style comments
92
- -S, --single-quoted-strings support single quotes as string delimiters
93
- -T, --trailing-commas ignore trailing commas in objects and arrays
94
- -D, --no-duplicate-keys report duplicate object keys as an error
95
- -V, --validate [file] JSON schema file to use for validation
96
- -e, --environment [env] which specification of JSON Schema the
97
- validation file uses
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
101
- -p, --pretty-print prettify the input instead of stringifying
102
- the parsed object
103
- -P, --pretty-print-invalid force pretty-printing even for invalid input
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
106
- --prune-comments omit comments from the prettified output
107
- --strip-object-keys strip quotes from object keys if possible
108
- (JSON5)
109
- --enforce-double-quotes surrounds all strings with double quotes
110
- --enforce-single-quotes surrounds all strings with single quotes
111
- (JSON5)
112
- --trim-trailing-commas omit trailing commas from objects and arrays
113
- (JSON5)
114
- -v, --version output the version number
115
- -h, --help output usage information
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
-
121
- Parsing mode can be "cjson" or "json5" to enable other flags automatically.
122
- If no files or directories are specified, stdin will be parsed. Environments
123
- for JSON schema validation are "json-schema-draft-04", "json-schema-draft-06"
124
- or "json-schema-draft-07". If not specified, it will be auto-detected.
77
+ A more complex example: check all JSON files in a Node.js project, except for dependencies in `node_modules`, allow comments (CJSON) and trailing commas, forbid duplicated object keys, print processed files names on the console, print errors on a single line and if an error occurs, continue with other files:
78
+
79
+ jsonlint --comments --trailing-commas --no-duplicate-keys \
80
+ --log-files --compact --continue '**/*.json' '!**/node_modules'
81
+
82
+ The same parameters can be passed from a configuration file:
83
+
84
+ ```json
85
+ {
86
+ "comments": true,
87
+ "trailing-commas": true,
88
+ "duplicate-keys": false,
89
+ "log-files": true,
90
+ "compact": true,
91
+ "continue": true,
92
+ "patterns": ["**/*.json", "!**/node_modules"]
93
+ }
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
+
113
+ ### Usage
114
+
115
+ Usage: `jsonlint [options] [<file, directory, pattern> ...]`
116
+
117
+ #### Options
118
+
119
+ -f, --config [file] read options from a custom configuration file
120
+ -F, --no-config disable searching for configuration file
121
+ -s, --sort-keys sort object keys (not when prettifying)
122
+ -E, --extensions [ext] file extensions to process for directory walk
123
+ (default: ["json","JSON"])
124
+ -i, --in-place overwrite the input files
125
+ -k, --check check that the input is equal to the output
126
+ -t, --indent [num|char] number of spaces or specific characters
127
+ to use for indentation (default: 2)
128
+ -c, --compact compact error display
129
+ -M, --mode [mode] set other parsing flags according to a format
130
+ type (default: "json")
131
+ -C, --comments recognize and ignore JavaScript-style comments
132
+ -S, --single-quoted-strings support single quotes as string delimiters
133
+ -T, --trailing-commas ignore trailing commas in objects and arrays
134
+ -D, --no-duplicate-keys report duplicate object keys as an error
135
+ -V, --validate [file] JSON schema file to use for validation
136
+ -e, --environment [env] which specification of JSON Schema the
137
+ validation file uses
138
+ -l, --log-files print only the parsed file names to stdout
139
+ -q, --quiet do not print the parsed json to stdout
140
+ -n, --continue continue with other files if an error occurs
141
+ -p, --pretty-print prettify the input instead of stringifying
142
+ the parsed object
143
+ -P, --pretty-print-invalid force pretty-printing even for invalid input
144
+ -r, --trailing-newline ensure a line break at the end of the output
145
+ -R, --no-trailing-newline ensure no line break at the end of the output
146
+ --prune-comments omit comments from the prettified output
147
+ --strip-object-keys strip quotes from object keys if possible
148
+ (JSON5)
149
+ --enforce-double-quotes surrounds all strings with double quotes
150
+ --enforce-single-quotes surrounds all strings with single quotes
151
+ (JSON5)
152
+ --trim-trailing-commas omit trailing commas from objects and arrays
153
+ (JSON5)
154
+ -v, --version output the version number
155
+ -h, --help output usage information
156
+
157
+ You can use BASH patterns for including and excluding files (only files).
158
+ Patterns are case-sensitive and have to use slashes as a path separators.
159
+ A pattern to exclude from processing starts with "!".
160
+
161
+ Parsing mode can be "cjson" or "json5" to enable other flags automatically.
162
+ If no files or directories are specified, stdin will be parsed. Environments
163
+ for JSON schema validation are "json-schema-draft-04", "json-schema-draft-06"
164
+ or "json-schema-draft-07". If not specified, it will be auto-detected.
165
+
166
+ ### Configuration
167
+
168
+ In addition to the command line parameters, the options can be supplied from the following files:
169
+
170
+ package.json, key jsonlint
171
+ .jsonlintrc
172
+ .jsonlintrc.json
173
+ .jsonlintrc.yaml
174
+ .jsonlintrc.yml
175
+ .jsonlintrc.js
176
+ .jsonlintrc.cjs
177
+ jsonlint.config.js
178
+ jsonlint.config.cjs
179
+
180
+ The automatic search for one of the following locations above can be disabled by the command-line parameter `-F|--no-config`. A concrete configuration file can be specified by the command-line parameter `-f|--config [file]`. Parameters from the command line will have higher priority than parameters from a configuration file.
181
+
182
+ The configuration is an object with the following properties, described above, which can be entered either in the kebab-case or in the camel-case:
183
+
184
+ | Parameter | Alias |
185
+ | --------- | ----- |
186
+ | patterns | |
187
+ | sort-keys | sortKeys |
188
+ | extensions | |
189
+ | in-place | inPlace |
190
+ | indent | |
191
+ | compact | |
192
+ | mode | |
193
+ | comments | |
194
+ | single-quoted-strings | singleQuotedStrings |
195
+ | trailing-commas | trailingCommas |
196
+ | duplicate-keys | duplicateKeys |
197
+ | validate | |
198
+ | environment | |
199
+ | log-files | logFiles |
200
+ | quiet | |
201
+ | continue | |
202
+ | pretty-print | prettyPrint |
203
+ | pretty-print-invalid | prettyPrintInvalid |
204
+ | trailing-newline | trailingNewline'
205
+ | prune-comments | pruneComments |
206
+ | strip-object-keys | stripObjectKeys |
207
+ | enforce-double-quotes | enforceDoubleQuotes |
208
+ | enforce-single-quotes | enforceSingleQuotes |
209
+ | trim-trailing-commas | trimTrailingCommas |
210
+
211
+ The parameter `config` will be ignored in configuration files. The extra parameter `patterns` can be set to an array of strings with paths or patterns instead of putting them to the command line.
125
212
 
126
213
  ## Module Interface
127
214
 
package/lib/cli.js CHANGED
@@ -16,9 +16,12 @@ const commander = require('commander')
16
16
  .name('jsonlint')
17
17
  .usage('[options] [<file, directory, pattern> ...]')
18
18
  .description(description)
19
+ .option('-f, --config [file]', 'read options from a custom configuration file')
20
+ .option('-F, --no-config', 'disable searching for configuration files')
19
21
  .option('-s, --sort-keys', 'sort object keys (not when prettifying)')
20
22
  .option('-E, --extensions [ext]', 'file extensions to process for directory walk', collectValues, ['json', 'JSON'])
21
23
  .option('-i, --in-place', 'overwrite the input files')
24
+ .option('-k, --check', 'check that the input is equal to the output')
22
25
  .option('-t, --indent [num|char]', 'number of spaces or specific characters to use for indentation', 2)
23
26
  .option('-c, --compact', 'compact error display')
24
27
  .option('-M, --mode [mode]', 'set other parsing flags according to a format type', 'json')
@@ -54,9 +57,57 @@ const commander = require('commander')
54
57
  })
55
58
  .parse(process.argv)
56
59
 
57
- const options = commander.opts()
60
+ const paramNames = {
61
+ 'trailing-commas': 'trailingCommas',
62
+ 'single-quoted-strings': 'singleQuotedStrings',
63
+ 'duplicate-keys': 'duplicateKeys',
64
+ 'pretty-print': 'prettyPrint',
65
+ 'prune-comments': 'pruneComments',
66
+ 'strip-object-keys': 'stripObjectKeys',
67
+ 'enforce-double-quotes': 'enforceDoubleQuotes',
68
+ 'enforce-single-quotes': 'enforceSingleQuotes',
69
+ 'trim-trailing-commas': 'trimTrailingCommas',
70
+ 'sort-keys': 'sortKeys',
71
+ 'pretty-print-invalid': 'prettyPrintInvalid',
72
+ 'log-files': 'logFiles',
73
+ 'in-place': 'inPlace',
74
+ 'trailing-newline': 'trailingNewline'
75
+ }
76
+
77
+ const params = commander.opts()
78
+ let options
79
+ if (params.config === false) {
80
+ options = params
81
+ } else {
82
+ const { cosmiconfigSync } = require('cosmiconfig')
83
+ const configurator = cosmiconfigSync('jsonlint')
84
+ const { config = {} } = (params.config && configurator.load(params.config)) ||
85
+ configurator.search() || {}
86
+ options = mergeOptions({}, convertConfig(config), params)
87
+ }
88
+
58
89
  const extensions = options.extensions.map(extension => '.' + extension)
59
90
 
91
+ function convertConfig (config) {
92
+ const result = {}
93
+ for (const key in config) {
94
+ const name = paramNames[key] || key
95
+ result[name] = config[key]
96
+ }
97
+ return result
98
+ }
99
+
100
+ function mergeOptions (target, ...sources) {
101
+ for (const source of sources) {
102
+ for (const key in source) {
103
+ if (target[key] == null) {
104
+ target[key] = source[key]
105
+ }
106
+ }
107
+ }
108
+ return target
109
+ }
110
+
60
111
  function logNormalError (error, file) {
61
112
  if (process.exitCode > 0) {
62
113
  console.log()
@@ -147,24 +198,49 @@ function processContents (source, file) {
147
198
  }
148
199
  }
149
200
 
201
+ function ensureLineBreak (parsed, source) {
202
+ const lines = source.split(/\r?\n/)
203
+ const newLine = !lines[lines.length - 1]
204
+ if (options.trailingNewline === true ||
205
+ (options.trailingNewline !== false && newLine)) {
206
+ parsed += '\n'
207
+ }
208
+ return parsed
209
+ }
210
+
211
+ function checkContents (file, source, parsed) {
212
+ const { createTwoFilesPatch } = require('diff')
213
+ const diff = createTwoFilesPatch(`${file}.orig`, file, source, parsed, '', '', { context: 3 })
214
+ if (diff.split(/\r?\n/).length > 4) {
215
+ const err = new Error('Formatted output differs')
216
+ if (options.compact) {
217
+ logCompactError(err, file)
218
+ } else {
219
+ logNormalError(err, file)
220
+ }
221
+ console.log(diff)
222
+ if (options.continue) {
223
+ process.exitCode = 1
224
+ } else {
225
+ process.exit(1)
226
+ }
227
+ }
228
+ }
229
+
150
230
  function processFile (file) {
151
231
  file = normalize(file)
152
232
  if (options.logFiles) {
153
233
  console.log(file)
154
234
  }
155
- const original = readFileSync(file, 'utf8')
156
- let source = processContents(original, file)
235
+ const source = readFileSync(file, 'utf8')
236
+ const parsed = processContents(source, file)
157
237
  if (options.inPlace) {
158
- const lines = original.split(/\?r\n/)
159
- const newLine = !lines[lines.length - 1]
160
- if (options.trailingNewline === true ||
161
- (options.trailingNewline !== false && newLine)) {
162
- source += '\n'
163
- }
164
- writeFileSync(file, source)
238
+ writeFileSync(file, ensureLineBreak(parsed, source))
239
+ } else if (options.check) {
240
+ checkContents(file, source, ensureLineBreak(parsed, source))
165
241
  } else {
166
242
  if (!(options.quiet || options.logFiles)) {
167
- console.log(source)
243
+ console.log(parsed)
168
244
  }
169
245
  }
170
246
  }
@@ -207,7 +283,10 @@ function processPatterns (patterns) {
207
283
  }
208
284
 
209
285
  function main () {
210
- const { args: files } = commander
286
+ let { args: files } = commander
287
+ if (!files.length) {
288
+ files = options.patterns || []
289
+ }
211
290
  if (files.length) {
212
291
  const dynamic = files.some(file => isDynamicPattern(file))
213
292
  if (dynamic) {
@@ -225,11 +304,14 @@ function main () {
225
304
  source += chunk.toString('utf8')
226
305
  })
227
306
  stdin.on('end', () => {
307
+ const file = '<stdin>'
228
308
  if (options.logFiles) {
229
- console.log('<stdin>')
309
+ console.log(file)
230
310
  }
231
- const parsed = processContents(source, '<stdin>')
232
- if (!(options.quiet || options.logFiles)) {
311
+ const parsed = processContents(source, file)
312
+ if (options.check) {
313
+ checkContents(file, source, ensureLineBreak(parsed, source))
314
+ } else if (!(options.quiet || options.logFiles)) {
233
315
  console.log(parsed)
234
316
  }
235
317
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prantlf/jsonlint",
3
- "version": "11.2.0",
3
+ "version": "11.4.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": [
@@ -45,7 +45,7 @@
45
45
  "build": "npm run compile && npm run compile:tests",
46
46
  "compile": "node scripts/bundle-jsonlint && terser -o web/jsonlint.min.js --source-map \"filename='jsonlint.js',url='jsonlint.min.js.map',includeSources=true\" lib/jsonlint.js && terser -o web/validator.min.js --source-map \"filename='validator.js',url='validator.min.js.map',includeSources=true\" lib/validator.js && terser -o web/formatter.min.js --source-map \"filename='formatter.js',url='formatter.min.js.map',includeSources=true\" lib/formatter.js && terser -o web/sorter.min.js --source-map \"filename='sorter.js',url='sorter.min.js.map',includeSources=true\" lib/sorter.js && terser -o web/printer.min.js --source-map \"filename='printer.js',url='printer.min.js.map',includeSources=true\" lib/printer.js && node scripts/bundle-schema-drafts && terser -o web/schema-drafts.min.js --source-map \"filename='schema-drafts.js',url='schema-drafts.min.js.map',includeSources=true\" lib/schema-drafts.js && terser -o web/ajv.min.js --source-map \"filename='ajv.js',url='ajv.min.js.map',includeSources=true\" node_modules/ajv/dist/ajv.bundle.js",
47
47
  "compile:tests": "tsc --lib es6 test/typings.test.ts",
48
- "test": "nyc --silent node test/typings.test.js && nyc --silent --no-clean node test/parse1 && nyc --silent --no-clean node test/parse1 --native-parser && nyc --silent --no-clean node test/parse2 && nyc --silent --no-clean node test/parse3 && nyc --silent --no-clean node test/parse4 && nyc --silent --no-clean node test/parse5 && nyc --silent --no-clean node test/portable && nyc --silent --no-clean node test/tokenize && nyc --silent --no-clean node test/print && nyc --silent --no-clean node lib/cli package.json test/recursive && nyc --silent --no-clean node lib/cli -sq test/passes/hasOwnProperty.json && nyc --silent --no-clean node lib/cli -s -e json-schema-draft-04 -V test/passes/3.schema.json test/passes/3.json && nyc --silent --no-clean node lib/cli -C test/passes/comments.txt && nyc --silent --no-clean node lib/cli -pS test/passes/strings.txt && nyc --silent --no-clean node lib/cli -M json5 test/passes/json5.text && nyc --silent --no-clean node lib/cli -v && nyc --silent --no-clean node lib/cli -h && nyc --silent --no-clean node lib/cli -Pc test/fails/10.json || nyc report",
48
+ "test": "nyc --silent node test/typings.test.js && nyc --silent --no-clean node test/parse1 && nyc --silent --no-clean node test/parse1 --native-parser && nyc --silent --no-clean node test/parse2 && nyc --silent --no-clean node test/parse3 && nyc --silent --no-clean node test/parse4 && nyc --silent --no-clean node test/parse5 && nyc --silent --no-clean node test/portable && nyc --silent --no-clean node test/tokenize && nyc --silent --no-clean node test/print && nyc --silent --no-clean node lib/cli package.json test/recursive && nyc --silent --no-clean node lib/cli -sq test/passes/hasOwnProperty.json && nyc --silent --no-clean node lib/cli -s -e json-schema-draft-04 -V test/passes/3.schema.json test/passes/3.json && nyc --silent --no-clean node lib/cli -C test/passes/comments.txt && nyc --silent --no-clean node lib/cli -pS test/passes/strings.txt && nyc --silent --no-clean node lib/cli -M json5 test/passes/json5.text && nyc --silent --no-clean node lib/cli -v && nyc --silent --no-clean node lib/cli -h && nyc --silent --no-clean node lib/cli -Pc test/fails/10.json || nyc --silent --no-clean node lib/cli -f test/.jsonrc.yml 'test/**/*.json' '!**/fails' && nyc report",
49
49
  "start": "http-server -c 5",
50
50
  "web": "npm run web:sync && npm run web:deploy",
51
51
  "web:clone": "test ! -d ../jsonlint-pages && git clone --single-branch --branch gh-pages `git remote get-url origin` ../jsonlint-pages",
@@ -77,14 +77,16 @@
77
77
  "dependencies": {
78
78
  "ajv": "6.12.6",
79
79
  "commander": "9.2.0",
80
+ "cosmiconfig": "7.0.1",
81
+ "diff": "5.0.0",
80
82
  "fast-glob": "3.2.11"
81
83
  },
82
84
  "devDependencies": {
83
- "@semantic-release/changelog": "^6.0.1",
84
- "@semantic-release/git": "^10.0.1",
85
- "@types/node": "17.0.30",
86
- "@typescript-eslint/eslint-plugin": "5.21.0",
87
- "@typescript-eslint/parser": "5.21.0",
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",
88
90
  "eslint": "8.14.0",
89
91
  "eslint-config-standard": "17.0.0",
90
92
  "eslint-plugin-import": "2.26.0",