Create strict JSON Schema for configuration (standalone for ease of use) and corresponding strict TypeScript type (via extends for conciseness) (fixes #1248).

This commit is contained in:
David Anson 2024-08-27 20:47:33 -07:00
parent bbca3ad209
commit 9c8e7156e1
10 changed files with 3070 additions and 1207 deletions

View file

@ -3140,6 +3140,13 @@ module.exports = markdownlint;
* @typedef {import("./configuration").Configuration} Configuration
*/
/**
* Configuration object for linting rules strictly. For the JSON schema, see
* {@link ../schema/markdownlint-config-schema-strict.json}.
*
* @typedef {import("./configuration-strict").ConfigurationStrict} ConfigurationStrict
*/
/**
* Rule configuration object.
*

View file

@ -164,3 +164,20 @@ markdownlint(options, assertLintResultsCallback);
(async () => {
assertLintResultsCallback(null, await markdownlint.promises.markdownlint(options));
})();
const configuration: markdownlint.Configuration = {
"custom-rule": true,
"no-hard-tabs": false,
"heading-style": {
"style": "consistent"
}
};
assert(configuration);
const configurationStrict: markdownlint.ConfigurationStrict = {
// "custom-rule": true,
"no-hard-tabs": false,
"heading-style": {
"style": "consistent"
}
};
assert(configurationStrict);

1185
lib/configuration-strict.d.ts vendored Normal file

File diff suppressed because it is too large Load diff

1184
lib/configuration.d.ts vendored

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,7 @@ export = markdownlint;
*/
declare function markdownlint(options: Options | null, callback: LintCallback): void;
declare namespace markdownlint {
export { markdownlintSync as sync, readConfig, readConfigSync, getVersion, promises, GetMarkdownIt, RuleFunction, RuleParams, MarkdownParsers, ParserMarkdownIt, ParserMicromark, MarkdownItToken, MicromarkTokenType, MicromarkToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintContentCallback, LintCallback, Configuration, RuleConfiguration, ConfigurationParser, ReadConfigCallback, ResolveConfigExtendsCallback };
export { markdownlintSync as sync, readConfig, readConfigSync, getVersion, promises, GetMarkdownIt, RuleFunction, RuleParams, MarkdownParsers, ParserMarkdownIt, ParserMicromark, MarkdownItToken, MicromarkTokenType, MicromarkToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintContentCallback, LintCallback, Configuration, ConfigurationStrict, RuleConfiguration, ConfigurationParser, ReadConfigCallback, ResolveConfigExtendsCallback };
}
/**
* Lint specified Markdown files synchronously.
@ -439,6 +439,11 @@ type LintCallback = (error: Error | null, results?: LintResults) => void;
* {@link ../schema/markdownlint-config-schema.json}.
*/
type Configuration = import("./configuration").Configuration;
/**
* Configuration object for linting rules strictly. For the JSON schema, see
* {@link ../schema/markdownlint-config-schema-strict.json}.
*/
type ConfigurationStrict = import("./configuration-strict").ConfigurationStrict;
/**
* Rule configuration.
*/

View file

@ -1532,6 +1532,13 @@ module.exports = markdownlint;
* @typedef {import("./configuration").Configuration} Configuration
*/
/**
* Configuration object for linting rules strictly. For the JSON schema, see
* {@link ../schema/markdownlint-config-schema-strict.json}.
*
* @typedef {import("./configuration-strict").ConfigurationStrict} ConfigurationStrict
*/
/**
* Rule configuration object.
*

View file

@ -1,7 +1,7 @@
# Validating Configuration
A [JSON Schema][json-schema] is provided to enable validating configuration
objects: [`markdownlint-config-schema.json`][markdownlint-config-schema]
objects: [`markdownlint-config-schema.json`][markdownlint-config-schema].
Some editors automatically use a JSON Schema with files that reference it. For
example, a `.markdownlint.json` file with:
@ -16,13 +16,11 @@ A JSON Schema validator can be used to check configuration files like so:
npx ajv-cli validate -s ./markdownlint/schema/markdownlint-config-schema.json -d "**/.markdownlint.{json,yaml}" --strict=false
```
By default, any rule name is valid in order to allow for custom rules. To ensure
that only built-in rules are used, change the value of `#/additionalProperties`
(at the bottom of the schema file) to `false` before validating:
```json
"additionalProperties": false
```
By default, any rule name is valid because of custom rules. To allow only
built-in rules, use the
[`markdownlint-config-schema-strict.json`][markdownlint-config-schema-strict]
JSON Schema instead.
[json-schema]: https://json-schema.org
[markdownlint-config-schema]: markdownlint-config-schema.json
[markdownlint-config-schema-strict]: markdownlint-config-schema-strict.json

View file

@ -9,7 +9,10 @@ const rules = require("../lib/rules");
const jsonSchemaToTypeScript = require("json-schema-to-typescript");
const { version } = require("../lib/constants");
const schemaUri = `https://raw.githubusercontent.com/DavidAnson/markdownlint/v${version}/schema/markdownlint-config-schema.json`;
const schemaName = "markdownlint-config-schema.json";
const schemaUri = `https://raw.githubusercontent.com/DavidAnson/markdownlint/v${version}/schema/${schemaName}`;
const schemaStrictName = "markdownlint-config-schema-strict.json";
const schemaStrictUri = `https://raw.githubusercontent.com/DavidAnson/markdownlint/v${version}/schema/${schemaStrictName}`;
// Schema scaffolding
const schema = {
@ -574,20 +577,24 @@ for (const [ tag, tagTags ] of Object.entries(tags)) {
}
// Write schema
const schemaFile = path.join(__dirname, "markdownlint-config-schema.json");
const schemaFile = path.join(__dirname, schemaName);
fs.writeFileSync(schemaFile, JSON.stringify(schema, null, " "));
// Write TypeScript declaration
// See https://github.com/bcherny/json-schema-to-typescript/issues/356 for why
// additionalProperties is deleted
const schemaDeclaration =
path.join(__dirname, "..", "lib", "configuration.d.ts");
// @ts-ignore
delete schema.additionalProperties;
schema.title = "Configuration";
// Create and write strict schema
const schemaStrict = {
...schema,
"$id": schemaStrictUri,
"additionalProperties": false
};
const schemaFileStrict = path.join(__dirname, schemaStrictName);
fs.writeFileSync(schemaFileStrict, JSON.stringify(schemaStrict, null, " "));
// Write TypeScript declaration file
const declarationStrictName = path.join(__dirname, "..", "lib", "configuration-strict.d.ts");
schemaStrict.title = "ConfigurationStrict";
jsonSchemaToTypeScript.compile(
// @ts-ignore
schema,
schemaStrict,
"UNUSED"
// eslint-disable-next-line unicorn/prefer-top-level-await
).then((declaration) => fs.writeFileSync(schemaDeclaration, declaration));
).then((declaration) => fs.writeFileSync(declarationStrictName, declaration));

File diff suppressed because it is too large Load diff

View file

@ -18,13 +18,9 @@ const constants = require("../lib/constants");
const rules = require("../lib/rules");
const customRules = require("./rules/rules.js");
const configSchema = require("../schema/markdownlint-config-schema.json");
const configSchemaStrict = require("../schema/markdownlint-config-schema-strict.json");
const deprecatedRuleNames = new Set(constants.deprecatedRuleNames);
const configSchemaStrict = {
...configSchema,
"$id": `${configSchema.$id}-strict`,
"additionalProperties": false
};
const ajvOptions = {
"allowUnionTypes": true
};