Skip to content

Commit 589e78d

Browse files
authored
Fix AJV warning, "$ref: keywords ignored in schema" (#392)
1 parent 3814ea7 commit 589e78d

File tree

5 files changed

+55
-12
lines changed

5 files changed

+55
-12
lines changed

test/programs/comments-override/schema.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
},
3333
"sub2": {
3434
"$ref": "#/definitions/MySubObject",
35-
"additionalProperties": false,
3635
"description": "Property-level description"
3736
}
3837
},

test/programs/namespace-deep-1/schema.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
],
5656
"type": "object"
5757
}
58-
},
59-
"type": "object"
58+
}
6059
}
6160

test/programs/namespace-deep-2/schema.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
],
5656
"type": "object"
5757
}
58-
},
59-
"type": "object"
58+
}
6059
}
6160

test/schema.test.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,33 @@ import { resolve } from "path";
55
import { versionMajorMinor as typescriptVersionMajorMinor } from "typescript";
66
import * as TJS from "../typescript-json-schema";
77

8-
const ajv = new Ajv();
8+
let ajvWarnings: string[] = [];
9+
const ajv = new Ajv({
10+
logger: {
11+
log: console.log,
12+
warn: (message) => {
13+
ajvWarnings.push(message);
14+
},
15+
error: (message) => {
16+
throw new Error("AJV error: " + message);
17+
},
18+
},
19+
});
920

1021
const BASE = "test/programs/";
1122

23+
interface AjvTestOptions {
24+
skipCompile: boolean;
25+
expectedWarnings: string[];
26+
}
27+
1228
export function assertSchema(
1329
group: string,
1430
type: string,
1531
settings: TJS.PartialArgs = {},
1632
compilerOptions?: TJS.CompilerOptions,
17-
only?: boolean
33+
only?: boolean,
34+
ajvOptions: Partial<AjvTestOptions> = {}
1835
) {
1936
const run = only ? it.only : it;
2037

@@ -37,7 +54,15 @@ export function assertSchema(
3754
// test against the meta schema
3855
if (actual !== null) {
3956
ajv.validateSchema(actual);
57+
4058
assert.equal(ajv.errors, null, "The schema is not valid");
59+
60+
// Compiling the schema can reveal warnings that validateSchema doesn't.
61+
if (!ajvOptions.skipCompile) {
62+
ajvWarnings = [];
63+
ajv.compile(actual);
64+
assert.deepEqual(ajvWarnings, ajvOptions.expectedWarnings || [], "Got unexpected AJV warnings");
65+
}
4166
}
4267
});
4368
}
@@ -233,11 +258,20 @@ describe("schema", () => {
233258

234259
describe("annotations", () => {
235260
assertSchema("annotation-default", "MyObject");
236-
assertSchema("annotation-ref", "MyObject");
261+
assertSchema("annotation-ref", "MyObject", {}, undefined, undefined, {
262+
skipCompile: true
263+
});
237264
assertSchema("annotation-tjs", "MyObject", {
238265
validationKeywords: ["hide"],
239266
});
240-
assertSchema("annotation-id", "MyObject");
267+
assertSchema("annotation-id", "MyObject", {}, undefined, undefined, {
268+
expectedWarnings: [
269+
"schema id ignored",
270+
"schema id ignored",
271+
"schema id ignored",
272+
"schema id ignored"
273+
]
274+
});
241275
assertSchema("annotation-items", "MyObject");
242276

243277
assertSchema("typeof-keyword", "MyObject", { typeOfKeyword: true });

typescript-json-schema.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,20 @@ const validationKeywords = {
360360
id: true
361361
};
362362

363+
/**
364+
* Subset of descriptive, non-type keywords that are permitted alongside a $ref.
365+
* Prior to JSON Schema draft 2019-09, $ref is a special keyword that doesn't
366+
* permit keywords alongside it, and so AJV may raise warnings if it encounters
367+
* any type-related keywords; see https://github.com/ajv-validator/ajv/issues/1121
368+
*/
369+
const annotationKeywords: { [k in keyof typeof validationKeywords]?: true } = {
370+
description: true,
371+
default: true,
372+
examples: true,
373+
// A JSDoc $ref annotation can appear as a $ref.
374+
$ref: true
375+
};
376+
363377
const subDefinitions = {
364378
items: true,
365379
additionalProperties: true,
@@ -1262,10 +1276,8 @@ export class JsonSchemaGenerator {
12621276
this.recursiveTypeRef.delete(fullTypeName);
12631277
// If the type was recursive (there is reffedDefinitions) - lets replace it to reference
12641278
if (this.reffedDefinitions[fullTypeName]) {
1265-
// Here we may want to filter out all type specific fields
1266-
// and include fields like description etc
12671279
const annotations = Object.entries(returnedDefinition).reduce((acc, [key, value]) => {
1268-
if (validationKeywords[key] && typeof value !== undefined) {
1280+
if (annotationKeywords[key] && typeof value !== undefined) {
12691281
acc[key] = value;
12701282
}
12711283
return acc;

0 commit comments

Comments
 (0)