Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: config validation #2412

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3f6d95c
feat: basic user config validation
armano2 Jan 10, 2021
786e400
fix: simplify config resolution and fix issue #327
armano2 Jan 10, 2021
6f00690
fix: remove no longer needed function
armano2 Jan 10, 2021
a7f92d8
fix: disable some unwanted validations
armano2 Jan 10, 2021
ea26a8e
fix: improve config validation
armano2 Jan 10, 2021
fdf9ed1
fix: remove redundant validation
armano2 Jan 10, 2021
787fff4
fix: use reduceRight instead of reverse
armano2 Jan 10, 2021
f30080d
fix: rollback some code
armano2 Jan 10, 2021
737704e
fix: drop invalid type casts
armano2 Jan 10, 2021
0a131f7
fix: rollback unnecessary changes
armano2 Jan 10, 2021
f6aab6b
Merge branch 'master' into refactor/load
armano2 Jan 11, 2021
a76e70e
fix: rollback config validation
armano2 Jan 15, 2021
25b98f8
fix: add missing type-guards and restore order
armano2 Jan 15, 2021
f1ce3c5
fix: one more order change
armano2 Jan 15, 2021
8a2169f
fix: add one more missing type guard
armano2 Jan 15, 2021
207e756
fix: remove unused types reference
armano2 Jan 15, 2021
2441177
fix: add additional unit tests
armano2 Jan 16, 2021
b76b844
fix: add additional regression tests
armano2 Jan 16, 2021
c5663fa
fix: remove more unnecessary code changes
armano2 Jan 16, 2021
f35b1c6
fix: correct order of merging plugins
armano2 Jan 16, 2021
124760b
fix: add missing type check
armano2 Jan 16, 2021
c38923e
fix: remove invalid type check
armano2 Jan 17, 2021
ee0650d
fix: remove redundant code
armano2 Jan 17, 2021
5b5285b
feat: implement config validation
armano2 Jan 17, 2021
4d572d4
Merge branch 'master' into feat/validate-configs
armano2 Jan 20, 2021
0c1b6f0
Merge branch 'master' into feat/validate-configs
armano2 Jan 21, 2021
8bfb303
Merge branch 'master' into feat/validate-configs
armano2 Jan 30, 2021
ec96a13
Merge branch 'master' into feat/validate-configs
armano2 Nov 16, 2021
9c70aa8
Merge remote-tracking branch 'origin/master' into feat/validate-configs
armano2 Nov 28, 2021
90bf68b
fix: allow to use function as a rule
armano2 Nov 28, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions @commitlint/cli/src/cli.ts
Expand Up @@ -7,14 +7,15 @@ import resolveGlobal from 'resolve-global';
import yargs, {Arguments} from 'yargs';
import util from 'util';

import {CliFlags, Seed} from './types';
import {CliFlags} from './types';
import {
LintOptions,
LintOutcome,
ParserOptions,
ParserPreset,
QualifiedConfig,
Formatter,
UserConfig,
} from '@commitlint/types';
import {CliError} from './cli-error';

Expand Down Expand Up @@ -364,7 +365,7 @@ function getEditValue(flags: CliFlags) {
return edit;
}

function getSeed(flags: CliFlags): Seed {
function getSeed(flags: CliFlags): UserConfig {
const n = (flags.extends || []).filter(
(i): i is string => typeof i === 'string'
);
Expand Down
5 changes: 0 additions & 5 deletions @commitlint/cli/src/types.ts
Expand Up @@ -18,8 +18,3 @@ export interface CliFlags {
_: (string | number)[];
$0: string;
}

export interface Seed {
extends?: string[];
parserPreset?: string;
}
45 changes: 45 additions & 0 deletions @commitlint/config-validator/package.json
@@ -0,0 +1,45 @@
{
"name": "@commitlint/config-validator",
"version": "15.0.0",
"description": "config validator for commitlint.config.js",
"main": "lib/validate.js",
"types": "lib/validate.d.ts",
"files": [
"lib/"
],
"scripts": {
"deps": "dep-check",
"pkg": "pkg-check --skip-import"
},
"engines": {
"node": ">=v12"
},
"repository": {
"type": "git",
"url": "https://github.com/conventional-changelog/commitlint.git",
"directory": "@commitlint/config-validator"
},
"bugs": {
"url": "https://github.com/conventional-changelog/commitlint/issues"
},
"homepage": "https://commitlint.js.org/",
"keywords": [
"conventional-changelog",
"commitlint",
"library",
"core"
],
"author": {
"name": "Mario Nebl",
"email": "hello@herebecode.com"
},
"license": "MIT",
"devDependencies": {
"@commitlint/utils": "^15.0.0"
},
"dependencies": {
"@commitlint/types": "^15.0.0",
"ajv": "^6.12.6"
},
"gitHead": "d829bf6260304ca8d6811f329fcdd1b6c50e9749"
}
134 changes: 134 additions & 0 deletions @commitlint/config-validator/src/__snapshots__/validate.test.ts.snap
@@ -0,0 +1,134 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`validation should fail for defaultIgnoresNotBoolean 1`] = `
"Commitlint configuration in defaultIgnoresNotBoolean.js is invalid:
- Property \\"defaultIgnores\\" has the wrong type - should be boolean.
"
`;

exports[`validation should fail for extendsAsObject 1`] = `
"Commitlint configuration in extendsAsObject.js is invalid:
- Property \\"extends\\" has the wrong type - should be array.
- Property \\"extends\\" has the wrong type - should be string.
- \\"extends\\" should match exactly one schema in oneOf. Value: {\\"test\\":1}.
"
`;

exports[`validation should fail for extendsWithFunction 1`] = `
"Commitlint configuration in extendsWithFunction.js is invalid:
- Property \\"extends[0]\\" has the wrong type - should be string.
- Property \\"extends\\" has the wrong type - should be string.
- \\"extends\\" should match exactly one schema in oneOf. Value: [null].
"
`;

exports[`validation should fail for formatterAsObject 1`] = `
"Commitlint configuration in formatterAsObject.js is invalid:
- Property \\"formatter\\" has the wrong type - should be string.
"
`;

exports[`validation should fail for helpUrlAsArray 1`] = `
"Commitlint configuration in helpUrlAsArray.js is invalid:
- Property \\"helpUrl\\" has the wrong type - should be string.
"
`;

exports[`validation should fail for helpUrlNotString 1`] = `
"Commitlint configuration in helpUrlNotString.js is invalid:
- Property \\"helpUrl\\" has the wrong type - should be string.
"
`;

exports[`validation should fail for ignoresFunction 1`] = `
"Commitlint configuration in ignoresFunction.js is invalid:
- Property \\"ignores\\" has the wrong type - should be array.
"
`;

exports[`validation should fail for ignoresNotFunction 1`] = `
"Commitlint configuration in ignoresNotFunction.js is invalid:
- \\"ignores[0]\\" should be a function. Value: 1.
"
`;

exports[`validation should fail for parserPreset 1`] = `
"Commitlint configuration in parserPreset.js is invalid:
- Property \\"parserPreset\\" has the wrong type - should be string.
- Property \\"parserPreset\\" has the wrong type - should be object.
- \\"parserPreset\\" should match exactly one schema in oneOf. Value: [].
"
`;

exports[`validation should fail for pluginsNotArray 1`] = `
"Commitlint configuration in pluginsNotArray.js is invalid:
- Property \\"plugins\\" has the wrong type - should be array.
"
`;

exports[`validation should fail for rules1 1`] = `
"Commitlint configuration in rules1.js is invalid:
- \\"rules['a'][0]\\" should be equal to one of the allowed values. Value: 3.
- \\"rules['a']\\" should be a function. Value: [3].
- \\"rules['a']\\" should match exactly one schema in oneOf. Value: [3].
"
`;

exports[`validation should fail for rules2 1`] = `
"Commitlint configuration in rules2.js is invalid:
- \\"rules['b']\\" should NOT have more than 3 items. Value: [1,\\"test\\",2,2].
- \\"rules['b']\\" should be a function. Value: [1,\\"test\\",2,2].
- \\"rules['b']\\" should match exactly one schema in oneOf. Value: [1,\\"test\\",2,2].
"
`;

exports[`validation should fail for rules3 1`] = `
"Commitlint configuration in rules3.js is invalid:
- \\"rules['c']\\" should NOT have fewer than 1 items. Value: [].
- \\"rules['c']\\" should be a function. Value: [].
- \\"rules['c']\\" should match exactly one schema in oneOf. Value: [].
"
`;

exports[`validation should fail for rules4 1`] = `
"Commitlint configuration in rules4.js is invalid:
- Property \\"rules['d'][0]\\" has the wrong type - should be number.
- \\"rules['d'][0]\\" should be equal to one of the allowed values. Value: [].
- \\"rules['d']\\" should be a function. Value: [[],[],[]].
- \\"rules['d']\\" should match exactly one schema in oneOf. Value: [[],[],[]].
"
`;

exports[`validation should fail for rules5 1`] = `
"Commitlint configuration in rules5.js is invalid:
- Property \\"rules['e']\\" has the wrong type - should be array.
- \\"rules['e']\\" should be a function. Value: {}.
- \\"rules['e']\\" should match exactly one schema in oneOf. Value: {}.
"
`;

exports[`validation should fail for rulesAsArray 1`] = `
"Commitlint configuration in rulesAsArray.js is invalid:
- Property \\"rules\\" has the wrong type - should be object.
"
`;

exports[`validation should fail for whenConfigIsNotObject 1`] = `
"Commitlint configuration in whenConfigIsNotObject.js is invalid:
- Config has the wrong type - should be object.
"
`;

exports[`validation should fail for whenConfigIsNotObject2 1`] = `
"Commitlint configuration in whenConfigIsNotObject2.js is invalid:
- Config has the wrong type - should be object.
"
`;

exports[`validation should fail for withPluginsAsObject 1`] = `
"Commitlint configuration in withPluginsAsObject.js is invalid:
- Property \\"plugins[0]\\" has the wrong type - should be string.
- \\"plugins[0]\\" should have required property '.rules'. Value: {}.
- \\"plugins[0]\\" should match some schema in anyOf. Value: {}.
"
`;
100 changes: 100 additions & 0 deletions @commitlint/config-validator/src/commitlint.schema.json
@@ -0,0 +1,100 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"default": {},
"type": "object",
"definitions": {
"rule": {
"oneOf": [
{
"description": "A rule",
"type": "array",
"items": [
{
"description": "Level: 0 disables the rule. For 1 it will be considered a warning, for 2 an error",
"type": "number",
"enum": [0, 1, 2]
},
{
"description": "Applicable: always|never: never inverts the rule",
"type": "string",
"enum": ["always", "never"]
},
{
"description": "Value: the value for this rule"
}
],
"minItems": 1,
"maxItems": 3,
"additionalItems": false
},
{
"description": "A rule",
"typeof": "function"
}
]
}
},
"properties": {
"extends": {
"description": "Resolveable ids to commitlint configurations to extend",
"oneOf": [
{
"type": "array",
"items": {"type": "string"}
},
{"type": "string"}
]
},
"parserPreset": {
"description": "Resolveable id to conventional-changelog parser preset to import and use",
"oneOf": [
{"type": "string"},
{
"type": "object",
"properties": {
"name": {"type": "string"},
"path": {"type": "string"},
"parserOpts": {}
},
"additionalProperties": true
}
]
},
"helpUrl": {
"description": "Custom URL to show upon failure",
"type": "string"
},
"formatter": {
"description": "Resolveable id to package, from node_modules, which formats the output",
"type": "string"
},
"rules": {
"description": "Rules to check against",
"type": "object",
"propertyNames": {"type": "string"},
"additionalProperties": {"$ref": "#/definitions/rule"}
},
"plugins": {
"description": "Resolveable ids of commitlint plugins from node_modules",
"type": "array",
"items": {
"anyOf": [
{"type": "string"},
{
"required": ["rules"],
"rules": {}
}
]
}
},
"ignores": {
"type": "array",
"items": {"typeof": "function"},
"description": "Additional commits to ignore, defined by ignore matchers"
},
"defaultIgnores": {
"description": "Whether commitlint uses the default ignore rules",
"type": "boolean"
}
}
}
45 changes: 45 additions & 0 deletions @commitlint/config-validator/src/formatErrors.ts
@@ -0,0 +1,45 @@
import {ErrorObject} from 'ajv';

/**
* Formats an array of schema validation errors.
* @param errors An array of error messages to format.
* @returns Formatted error message
* Based on https://github.com/eslint/eslint/blob/master/lib/shared/config-validator.js#L237-L261
*/
export function formatErrors(errors: ErrorObject[]): string {
return errors
.map((error) => {
if (
error.keyword === 'additionalProperties' &&
'additionalProperty' in error.params
) {
const formattedPropertyPath = error.dataPath.length
? `${error.dataPath.slice(1)}.${error.params.additionalProperty}`
: error.params.additionalProperty;

return `Unexpected top-level property "${formattedPropertyPath}"`;
}
if (error.keyword === 'type') {
const formattedField = error.dataPath.slice(1);
if (!formattedField) {
return `Config has the wrong type - ${error.message}`;
}
return `Property "${formattedField}" has the wrong type - ${error.message}`;
}
const field =
(error.dataPath[0] === '.'
? error.dataPath.slice(1)
: error.dataPath) || 'Config';
if (error.keyword === 'typeof') {
return `"${field}" should be a ${error.schema}. Value: ${JSON.stringify(
error.data
)}`;
}

return `"${field}" ${error.message}. Value: ${JSON.stringify(
error.data
)}`;
})
.map((message) => `\t- ${message}.\n`)
.join('');
}