Skip to content

Commit 0b9130c

Browse files
committed
feat: Support JSON Schema drafts 2019-09 and 2020-12 and JSON Type Definition
Upgrade AJV to the latest version and retain the previous AJV@6 to be able to suport JSON Schema draft 04. BREAKING CHANGE: The default environment recognises only JSON Schema drafts 06 and 07 automatically. Not 04 any more. The environment for JSON Schema drafts 04 has to be selected explicitly. Also, JSON Schema drafts 06 and 07 are handled by AJV@8 instead of AJV@6. It shouldn't make any difference, but the implementation is new and could perform a stricter validation.
1 parent 45f481c commit 0b9130c

21 files changed

+516
-99
lines changed

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,5 @@ benchmarks/nearley/*.js
55
coverage
66
node_modules
77
lib/jsonlint*.js
8-
lib/schema-drafts.js
98
test/types.test.mjs
109
web/*.min.*

.ncurc.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
reject:
2-
- ajv
2+
- ajv6

.vscode/launch.json

+9
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@
2626
"<node_internals>/**/*.js"
2727
]
2828
},
29+
{
30+
"name": "types",
31+
"type": "node",
32+
"request": "launch",
33+
"program": "${workspaceRoot}/test/types.test.mjs",
34+
"skipFiles": [
35+
"<node_internals>/**/*.js"
36+
]
37+
},
2938
{
3039
"name": "fail",
3140
"type": "node",

README.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ Usage: `jsonlint [options] [<file, directory, pattern> ...]`
135135
-S, --single-quoted-strings support single quotes as string delimiters
136136
-T, --trailing-commas ignore trailing commas in objects and arrays
137137
-D, --no-duplicate-keys report duplicate object keys as an error
138-
-V, --validate [file] JSON schema file to use for validation
138+
-V, --validate [file] JSON Schema file to use for validation
139139
-e, --environment [env] which specification of JSON Schema the
140140
validation file uses
141141
-x, --context [num] line count used as the diff context (default: 3)
@@ -164,8 +164,10 @@ A pattern to exclude from processing starts with "!".
164164

165165
Parsing mode can be "cjson" or "json5" to enable other flags automatically.
166166
If no files or directories are specified, stdin will be parsed. Environments
167-
for JSON schema validation are "json-schema-draft-04", "json-schema-draft-06"
168-
or "json-schema-draft-07". If not specified, it will be auto-detected.
167+
for JSON Schema validation are "draft-04", "draft-06", "draft-07",
168+
"draft-2019-09" or "draft-2020-12". The environment may be prefixed
169+
with "json-schema-". JSON Type Definition can be selected by "rfc8927",
170+
"json-type-definition" or "jtd". If not specified, it will be "draft-07".
169171

170172
### Configuration
171173

@@ -272,21 +274,19 @@ The `mode` parameter (string) sets parsing options to match a common format of i
272274

273275
### Schema Validation
274276

275-
You can validate the input against a JSON schema using the `lib/validator` module. The `validate` method accepts either an earlier parsed JSON data or a string with the JSON input:
277+
You can validate the input against a JSON Schema using the `lib/validator` module. The `validate` method accepts either an earlier parsed JSON data or a string with the JSON input:
276278

277279
```js
278280
const { compile } = require('@prantlf/jsonlint/lib/validator')
279-
const validate = compile('string with JSON schema')
281+
const validate = compile('string with JSON Schema')
280282
// Throws an error in case of failure.
281283
const parsed = validate('string with JSON data')
282284
```
283285

284-
If a string is passed to the `validate` method, the same options as for parsing JSON data can be passed as the second parameter. Compiling JSON schema supports the same options as parsing JSON data too (except for `reviver`). They can be passed as the second (object) parameter. The optional second `environment` parameter can be passed either as a string or as an additional property in the options object too:
286+
If a string is passed to the `validate` method, the same options as for parsing JSON data can be passed as the second parameter. Compiling JSON Schema supports the same options as parsing JSON data too (except for `reviver`). They can be passed as the second (object) parameter. The optional second `environment` parameter can be passed either as a string or as an additional property in the options object too:
285287

286288
```js
287-
const validate = compile('string with JSON schema', {
288-
environment: 'json-schema-draft-04'
289-
})
289+
const validate = compile('string with JSON Schema', { environment: 'draft-2020-12' })
290290
```
291291

292292
### Pretty-Printing

lib/cli.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const commander = require('commander')
3131
.option('-S, --single-quoted-strings', 'support single quotes as string delimiters')
3232
.option('-T, --trailing-commas', 'ignore trailing commas in objects and arrays')
3333
.option('-D, --no-duplicate-keys', 'report duplicate object keys as an error')
34-
.option('-V, --validate [file]', 'JSON schema file to use for validation')
34+
.option('-V, --validate [file]', 'JSON Schema file to use for validation')
3535
.option('-e, --environment [env]', 'which specification of JSON Schema the validation file uses')
3636
.option('-x, --context [num]', 'line count used as the diff context', 3)
3737
.option('-l, --log-files', 'print only the parsed file names to stdout')
@@ -55,8 +55,10 @@ const commander = require('commander')
5555
console.log()
5656
console.log('Parsing mode can be "cjson" or "json5" to enable other flags automatically.')
5757
console.log('If no files or directories are specified, stdin will be parsed. Environments')
58-
console.log('for JSON schema validation are "json-schema-draft-04", "json-schema-draft-06"')
59-
console.log('or "json-schema-draft-07". If not specified, it will be auto-detected.')
58+
console.log('for JSON Schema validation are "draft-04", "draft-06", "draft-07",')
59+
console.log('"draft-2019-09" or "draft-2020-12". The environment may be prefixed')
60+
console.log('with "json-schema-". JSON Type Definition can be selected by "rfc8927",')
61+
console.log('"json-type-definition" or "jtd". If not specified, it will be "draft-07".')
6062
})
6163
.parse(process.argv)
6264

@@ -149,7 +151,7 @@ function processContents (source, file) {
149151
parserOptions.environment = options.environment
150152
validate = compile(schema, parserOptions)
151153
} catch (error) {
152-
const message = 'Loading the JSON schema failed: "' +
154+
const message = 'Loading the JSON Schema failed: "' +
153155
options.validate + '".\n' + error.message
154156
throw new Error(message)
155157
}

lib/index.d.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,11 @@ declare module '@prantlf/jsonlint/lib/validator' {
362362
/**
363363
* Identifiers of supported JSON Schema drafts and JSON Type Definition.
364364
*/
365-
type Environment = 'json-schema-draft-04' | 'json-schema-draft-06' | 'json-schema-draft-07'
365+
type Environment = 'json-schema-draft-04' | 'draft-04' |
366+
'json-schema-draft-06' | 'draft-06' | 'json-schema-draft-07' | 'draft-07' |
367+
'json-schema-draft-2019-09' | 'draft-2019-09' |
368+
'json-schema-draft-2020-12' | 'draft-2020-12' |
369+
'json-type-definition' | 'jtd' | 'rfc8927'
366370

367371
/**
368372
* Options to customize a JSON Schema validator.
@@ -414,7 +418,11 @@ declare module '@prantlf/jsonlint/lib/validator' {
414418
/**
415419
* Choose the JSON Schema draft or JSON Type Definition.
416420
*
417-
* Available values: `'json-schema-draft-04' | 'json-schema-draft-06' | 'json-schema-draft-07'`
421+
* Available values: `'json-schema-draft-04' | 'draft-04' |
422+
* 'json-schema-draft-06' | 'draft-06' | 'json-schema-draft-07' | 'draft-07' |
423+
* 'json-schema-draft-2019-09' | 'draft-2019-09' |
424+
* 'json-schema-draft-2020-12' | 'draft-2020-12' |
425+
* 'json-type-definition' | 'jtd' | 'rfc8927'`
418426
*/
419427
environment?: Environment
420428
}

lib/validator.js

+49-35
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,40 @@
11
(function (global, factory) {
22
if (typeof exports === 'object' && typeof module !== 'undefined') {
33
const jsonlint = require('./jsonlint')
4-
const Ajv = require('ajv')
5-
// eslint-disable-next-line no-inner-declarations
6-
function requireSchemaDraft (environment) {
7-
return require('ajv/lib/refs/' + environment + '.json')
4+
const ajv = {
5+
AjvOld: 'ajv6',
6+
Ajv07: 'ajv',
7+
AjvJTD: 'ajv/dist/jtd',
8+
Ajv2019: 'ajv/dist/2019',
9+
Ajv2020: 'ajv/dist/2020',
10+
Schema04: 'ajv6/lib/refs/json-schema-draft-04.json',
11+
Schema06: 'ajv/dist/refs/json-schema-draft-06.json'
812
}
9-
factory(exports, Ajv, jsonlint, requireSchemaDraft)
10-
// eslint-disable-next-line no-undef
13+
const requireAjv = name => {
14+
const exported = require(ajv[name])
15+
return !exported.$schema && exported.default || exported
16+
}
17+
factory(exports, jsonlint, requireAjv)
1118
} else if (typeof define === 'function' && define.amd) {
12-
// eslint-disable-next-line no-undef
13-
define('jsonlint-validator', ['exports', 'ajv', 'jsonlint', 'jsonlint-schema-drafts'],
14-
function (exports, jsonlint, Ajv, schemaDrafts) {
15-
function requireSchemaDraft (environment) {
16-
return schemaDrafts[environment]
19+
define('jsonlint-validator', ['exports', 'jsonlint', 'ajv', 'ajv7'],
20+
function (exports, jsonlint, ajv, ajv7) {
21+
const requireAjv = name => {
22+
if (name === 'AjvOld') return ajv
23+
const exported = ajv7[name]
24+
return !exported.$schema && exported.default || exported
1725
}
18-
factory(exports, Ajv, jsonlint, requireSchemaDraft)
26+
factory(exports, jsonlint, requireAjv)
1927
})
2028
} else {
21-
// eslint-disable-next-line no-undef
2229
global = global || self
23-
const requireSchemaDraft = function (environment) {
24-
return global.jsonlintSchemaDrafts[environment]
30+
const requireAjv = name => {
31+
if (name === 'AjvOld') return global.Ajv
32+
const exported = global.ajv7[name]
33+
return !exported.$schema && exported.default || exported
2534
}
26-
factory(global.jsonlintValidator = {}, global.Ajv, global.jsonlint, requireSchemaDraft)
35+
factory(global.jsonlintValidator = {}, global.jsonlint, requireAjv)
2736
}
28-
}(this, function (exports, Ajv, jsonlint, requireSchemaDraft) {
37+
}(this, function (exports, jsonlint, requireAjv) {
2938
'use strict'
3039

3140
function addErrorLocation (problem, input, tokens, dataPath) {
@@ -96,24 +105,29 @@
96105
}
97106

98107
function createAjv (environment) {
99-
const ajvOptions = { jsonPointers: true }
100108
let ajv
101-
if (!environment) {
102-
ajvOptions.schemaId = 'auto'
103-
ajv = new Ajv(ajvOptions)
104-
ajv.addMetaSchema(requireSchemaDraft('json-schema-draft-04'))
105-
ajv.addMetaSchema(requireSchemaDraft('json-schema-draft-06'))
106-
} else if (environment === 'json-schema-draft-07') {
107-
ajv = new Ajv(ajvOptions)
108-
} else if (environment === 'json-schema-draft-06') {
109-
ajv = new Ajv(ajvOptions)
110-
ajv.addMetaSchema(requireSchemaDraft('json-schema-draft-06'))
111-
} else if (environment === 'json-schema-draft-04') {
112-
ajvOptions.schemaId = 'id'
113-
ajv = new Ajv(ajvOptions)
114-
ajv.addMetaSchema(requireSchemaDraft('json-schema-draft-04'))
109+
if (!environment || environment === 'json-schema-draft-06' || environment === 'draft-06') {
110+
const Ajv = requireAjv('Ajv07')
111+
ajv = new Ajv()
112+
ajv.addMetaSchema(requireAjv('Schema06'))
113+
} else if (environment === 'json-schema-draft-07' || environment === 'draft-07') {
114+
const Ajv = requireAjv('Ajv07')
115+
ajv = new Ajv()
116+
} else if (environment === 'json-schema-draft-04' || environment === 'draft-04') {
117+
const Ajv = requireAjv('AjvOld')
118+
ajv = new Ajv({ schemaId: 'id' })
119+
ajv.addMetaSchema(requireAjv('Schema04'))
120+
} else if (environment === 'json-schema-draft-2019-09' || environment === 'draft-2019-09') {
121+
const Ajv = requireAjv('Ajv2019')
122+
ajv = new Ajv()
123+
} else if (environment === 'json-schema-draft-2020-12' || environment === 'draft-2020-12') {
124+
const Ajv = requireAjv('Ajv2020')
125+
ajv = new Ajv()
126+
} else if (environment === 'json-type-definition' || environment === 'jtd' || environment === 'rfc8927') {
127+
const Ajv = requireAjv('AjvJTD')
128+
ajv = new Ajv()
115129
} else {
116-
throw new RangeError('Unsupported environment for the JSON schema validation: "' +
130+
throw new RangeError('Unsupported environment for the JSON Schema validation: "' +
117131
environment + '".')
118132
}
119133
return ajv
@@ -124,7 +138,7 @@
124138
try {
125139
parsed = jsonlint.parse(schema, parseOptions)
126140
} catch (error) {
127-
error.message = 'Parsing the JSON schema failed.\n' + error.message
141+
error.message = 'Parsing the JSON Schema failed.\n' + error.message
128142
throw error
129143
}
130144
try {
@@ -134,7 +148,7 @@
134148
const betterError = errors
135149
? createError(errors, parsed, schema, parseOptions)
136150
: originalError
137-
betterError.message = 'Compiling the JSON schema failed.\n' + betterError.message
151+
betterError.message = 'Compiling the JSON Schema failed.\n' + betterError.message
138152
throw betterError
139153
}
140154
}

package.json

+10-6
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,11 @@
3939
"node": ">= 14"
4040
},
4141
"scripts": {
42-
"prepare": "npm run build",
43-
"lint": "denolint",
44-
"build": "npm run compile && npm run compile:tests",
45-
"compile": "npm run compile:jsonlint && esbuild --minify --sourcemap --outfile=web/jsonlint.min.js lib/jsonlint.js && esbuild --minify --sourcemap --outfile=web/validator.min.js lib/validator.js && esbuild --minify --sourcemap --outfile=web/formatter.min.js lib/formatter.js && esbuild --minify --sourcemap --outfile=web/sorter.min.js lib/sorter.js && esbuild --minify --sourcemap --outfile=web/printer.min.js lib/printer.js && node scripts/bundle-schema-drafts && esbuild --minify --sourcemap --outfile=web/schema-drafts.min.js lib/schema-drafts.js && esbuild --minify --sourcemap --outfile=web/ajv.min.js node_modules/ajv/dist/ajv.bundle.js",
42+
"prepare": "npm run compile:jsonlint && rollup -c && npm run minify && npm run compile:tests",
4643
"compile:jsonlint": "cat.js src/prefix.js.txt src/unicode.js src/custom-parser.js src/pointer.js src/native-parser.js src/configurable-parser.js src/suffix.js.txt > lib/jsonlint.js",
44+
"minify": "esbuild --minify --sourcemap --outfile=web/jsonlint.min.js lib/jsonlint.js && esbuild --minify --sourcemap --outfile=web/validator.min.js lib/validator.js && esbuild --minify --sourcemap --outfile=web/formatter.min.js lib/formatter.js && esbuild --minify --sourcemap --outfile=web/sorter.min.js lib/sorter.js && esbuild --minify --sourcemap --outfile=web/printer.min.js lib/printer.js && esbuild --minify --sourcemap --outfile=web/ajv.min.js node_modules/ajv6/dist/ajv.bundle.js",
4745
"compile:tests": "tsc --moduleResolution node --module es2022 test/types.test.ts && mv.js test/types.test.js test/types.test.mjs",
48-
"test": "npm run lint && c8 node test/types.test.mjs && c8 --no-clean node test/parse1 && c8 --no-clean node test/parse1 --native-parser && c8 --no-clean node test/parse2 && c8 --no-clean node test/parse3 && c8 --no-clean node test/parse4 && c8 --no-clean node test/parse5 && c8 --no-clean node test/portable && c8 --no-clean node test/tokenize && c8 --no-clean node test/print && c8 --no-clean node lib/cli package.json test/recursive && c8 --no-clean node lib/cli -sq test/passes/hasOwnProperty.json && c8 --no-clean node lib/cli -s -e json-schema-draft-04 -V test/passes/3.schema.json test/passes/3.json && c8 --no-clean node lib/cli -C test/passes/comments.txt && c8 --no-clean node lib/cli -pS test/passes/strings.txt && c8 --no-clean node lib/cli -M json5 test/passes/json5.text && c8 --no-clean node lib/cli -v && c8 --no-clean node lib/cli -h && c8 --no-clean node lib/cli -Pc test/fails/10.json || c8 --no-clean node lib/cli -f test/.jsonrc.yml 'test/**/*.json' '!**/fails' && c8 report",
46+
"test": "denolint && c8 node test/types.test.mjs && c8 --no-clean node test/parse1 && c8 --no-clean node test/parse1 --native-parser && c8 --no-clean node test/parse2 && c8 --no-clean node test/parse3 && c8 --no-clean node test/parse4 && c8 --no-clean node test/parse5 && c8 --no-clean node test/portable && c8 --no-clean node test/tokenize && c8 --no-clean node test/print && c8 --no-clean node lib/cli package.json test/recursive && c8 --no-clean node lib/cli -sq test/passes/hasOwnProperty.json && c8 --no-clean node lib/cli -s -e json-schema-draft-04 -V test/passes/schema-04.json test/passes/data-04.json && c8 --no-clean node lib/cli -s -e json-schema-draft-07 -V test/passes/schema-07.json test/passes/data-07.json && c8 --no-clean node lib/cli -C test/passes/comments.txt && c8 --no-clean node lib/cli -pS test/passes/strings.txt && c8 --no-clean node lib/cli -M json5 test/passes/json5.text && c8 --no-clean node lib/cli -v && c8 --no-clean node lib/cli -h && c8 --no-clean node lib/cli -Pc test/fails/10.json || c8 --no-clean node lib/cli -f test/.jsonrc.yml 'test/**/*.json' '!**/fails' && c8 report",
4947
"start": "http-server -c 5",
5048
"web": "npm run web:sync && npm run web:deploy",
5149
"web:clone": "test ! -d ../jsonlint-pages && git clone --single-branch --branch gh-pages `git remote get-url origin` ../jsonlint-pages",
@@ -75,13 +73,17 @@
7573
]
7674
},
7775
"dependencies": {
78-
"ajv": "6.12.6",
76+
"ajv": "8.12.0",
77+
"ajv6": "npm:[email protected]",
7978
"commander": "10.0.0",
8079
"cosmiconfig": "8.1.0",
8180
"diff": "5.1.0",
8281
"fast-glob": "3.2.12"
8382
},
8483
"devDependencies": {
84+
"@rollup/plugin-commonjs": "24.0.1",
85+
"@rollup/plugin-json": "6.0.0",
86+
"@rollup/plugin-node-resolve": "15.0.1",
8587
"@semantic-release/changelog": "6.0.2",
8688
"@semantic-release/git": "10.0.1",
8789
"@types/node": "18.14.6",
@@ -92,6 +94,8 @@
9294
"esbuild": "0.17.11",
9395
"http-server": "14.1.1",
9496
"js-yaml": "4.1.0",
97+
"rollup": "3.18.0",
98+
"rollup-plugin-swc-minify": "1.0.5",
9599
"tehanu": "1.0.1",
96100
"tehanu-repo-coco": "1.0.0",
97101
"tehanu-teru": "1.0.0",

0 commit comments

Comments
 (0)