Convert JSON schema validation to use Ajv.

This commit is contained in:
David Anson 2023-11-25 17:33:39 -08:00
parent 43330dc04f
commit d2a0f74bbd
2 changed files with 42 additions and 49 deletions

View file

@ -71,7 +71,7 @@
"devDependencies": { "devDependencies": {
"@babel/core": "7.23.3", "@babel/core": "7.23.3",
"@babel/preset-env": "7.23.3", "@babel/preset-env": "7.23.3",
"@hyperjump/json-schema": "1.6.5", "ajv": "8.12.0",
"ava": "5.3.1", "ava": "5.3.1",
"babel-loader": "9.1.3", "babel-loader": "9.1.3",
"c8": "8.0.1", "c8": "8.0.1",

View file

@ -4,14 +4,14 @@
const fs = require("node:fs"); const fs = require("node:fs");
const path = require("node:path"); const path = require("node:path");
const Ajv = require("ajv");
const jsYaml = require("js-yaml"); const jsYaml = require("js-yaml");
const md = require("markdown-it")(); const md = require("markdown-it")();
const pluginInline = require("markdown-it-for-inline"); const pluginInline = require("markdown-it-for-inline");
const pluginSub = require("markdown-it-sub"); const pluginSub = require("markdown-it-sub");
const pluginSup = require("markdown-it-sup"); const pluginSup = require("markdown-it-sup");
const test = require("ava").default; const test = require("ava").default;
const { "exports": packageExports, homepage, version } = const { "exports": packageExports, homepage, version } = require("../package.json");
require("../package.json");
const markdownlint = require("../lib/markdownlint"); const markdownlint = require("../lib/markdownlint");
const constants = require("../lib/constants"); const constants = require("../lib/constants");
const rules = require("../lib/rules"); const rules = require("../lib/rules");
@ -19,12 +19,14 @@ const customRules = require("./rules/rules.js");
const configSchema = require("../schema/markdownlint-config-schema.json"); const configSchema = require("../schema/markdownlint-config-schema.json");
const deprecatedRuleNames = new Set(constants.deprecatedRuleNames); const deprecatedRuleNames = new Set(constants.deprecatedRuleNames);
const jsonSchemaVersion = "http://json-schema.org/draft-07/schema#";
const configSchemaStrict = { const configSchemaStrict = {
...configSchema, ...configSchema,
"$id": `${configSchema.$id}-strict`, "$id": `${configSchema.$id}-strict`,
"additionalProperties": false "additionalProperties": false
}; };
const ajvOptions = {
"allowUnionTypes": true
};
test("simpleAsync", (t) => new Promise((resolve) => { test("simpleAsync", (t) => new Promise((resolve) => {
t.plan(2); t.plan(2);
@ -915,10 +917,9 @@ test("readme", async(t) => {
test("validateJsonUsingConfigSchemaStrict", async(t) => { test("validateJsonUsingConfigSchemaStrict", async(t) => {
t.plan(171); t.plan(171);
const { addSchema, validate } = // @ts-ignore
await import("@hyperjump/json-schema/draft-07"); const ajv = new Ajv(ajvOptions);
addSchema(configSchemaStrict); const validateSchemaStrict = ajv.compile(configSchemaStrict);
const validateConfigSchema = await validate(configSchemaStrict.$id);
const configRe = const configRe =
/^[\s\S]*<!-- markdownlint-configure-file ([\s\S]*) -->[\s\S]*$/; /^[\s\S]*<!-- markdownlint-configure-file ([\s\S]*) -->[\s\S]*$/;
const ignoreFiles = new Set([ const ignoreFiles = new Set([
@ -942,21 +943,21 @@ test("validateJsonUsingConfigSchemaStrict", async(t) => {
const data = fs.readFileSync(file, "utf8"); const data = fs.readFileSync(file, "utf8");
if (configRe.test(data)) { if (configRe.test(data)) {
const config = data.replace(configRe, "$1"); const config = data.replace(configRe, "$1");
const result = validateConfigSchema(JSON.parse(config), "BASIC"); const result = validateSchemaStrict(JSON.parse(config));
t.true( t.truthy(
result.valid, result,
`${file}\n${JSON.stringify(result, null, 2)}` `${file}\n${JSON.stringify(validateSchemaStrict.errors, null, 2)}`
); );
} }
} }
}); });
test("validateConfigSchemaAllowsUnknownProperties", async(t) => { test("validateConfigSchemaAllowsUnknownProperties", (t) => {
t.plan(4); t.plan(4);
const { addSchema, validate } = // @ts-ignore
await import("@hyperjump/json-schema/draft-07"); const ajv = new Ajv(ajvOptions);
addSchema(configSchema); const validateSchema = ajv.compile(configSchema);
addSchema(configSchemaStrict); const validateSchemaStrict = ajv.compile(configSchemaStrict);
const testCases = [ const testCases = [
{ {
"property": true "property": true
@ -968,53 +969,46 @@ test("validateConfigSchemaAllowsUnknownProperties", async(t) => {
} }
]; ];
for (const testCase of testCases) { for (const testCase of testCases) {
const defaultResult = const result = validateSchema(testCase);
// eslint-disable-next-line no-await-in-loop t.truthy(
await validate(configSchema.$id, testCase, "BASIC"); result,
t.true( "Unknown property blocked by default: " + JSON.stringify(validateSchema.errors, null, 2)
defaultResult.valid,
"Unknown property blocked by default: " + JSON.stringify(testCase)
); );
const strictResult = const resultStrict = validateSchemaStrict(testCase);
// eslint-disable-next-line no-await-in-loop t.falsy(
await validate(configSchemaStrict.$id, testCase, "BASIC"); resultStrict,
t.false( "Unknown property allowed when strict: " + JSON.stringify(validateSchemaStrict.errors, null, 2)
strictResult.valid,
"Unknown property allowed when strict: " + JSON.stringify(testCase)
); );
} }
}); });
test("validateConfigSchemaAppliesToUnknownProperties", async(t) => { test("validateConfigSchemaAppliesToUnknownProperties", (t) => {
t.plan(4); t.plan(4);
const { addSchema, validate } = // @ts-ignore
await import("@hyperjump/json-schema/draft-07"); const ajv = new Ajv(ajvOptions);
addSchema(configSchema); const validateSchema = ajv.compile(configSchema);
const validateConfigSchema = await validate(configSchema.$id);
for (const allowed of [ true, {} ]) { for (const allowed of [ true, {} ]) {
t.true( t.truthy(
validateConfigSchema({ "property": allowed }, "BASIC").valid, validateSchema({ "property": allowed }),
`Unknown property value ${allowed} blocked` `Unknown property value ${allowed} blocked`
); );
} }
for (const blocked of [ 2, "string" ]) { for (const blocked of [ 2, "string" ]) {
t.false( t.falsy(
validateConfigSchema({ "property": blocked }, "BASIC").valid, validateSchema({ "property": blocked }),
`Unknown property value ${blocked} allowed` `Unknown property value ${blocked} allowed`
); );
} }
}); });
test("validateConfigExampleJson", async(t) => { test("validateConfigExampleJson", async(t) => {
t.plan(3); t.plan(2);
const { "default": stripJsonComments } = await import("strip-json-comments"); const { "default": stripJsonComments } = await import("strip-json-comments");
// Validate schema // Validate schema
const { addSchema, validate } = // @ts-ignore
await import("@hyperjump/json-schema/draft-07"); const ajv = new Ajv(ajvOptions);
const schemaResult = const validateSchema = ajv.compile(configSchema);
await validate(jsonSchemaVersion, configSchema, "BASIC");
t.true(schemaResult.valid);
// Validate JSONC // Validate JSONC
const fileJson = ".markdownlint.jsonc"; const fileJson = ".markdownlint.jsonc";
@ -1023,11 +1017,10 @@ test("validateConfigExampleJson", async(t) => {
"utf8" "utf8"
); );
const jsonObject = JSON.parse(stripJsonComments(dataJson)); const jsonObject = JSON.parse(stripJsonComments(dataJson));
addSchema(configSchemaStrict); const result = validateSchema(jsonObject);
const result = await validate(configSchemaStrict.$id, jsonObject, "BASIC"); t.truthy(
t.true( result,
result.valid, `${fileJson}\n${JSON.stringify(validateSchema.errors, null, 2)}`
`${fileJson}\n${JSON.stringify(result, null, 2)}`
); );
// Validate YAML // Validate YAML