Examples
Basic usage
The file .gitignore must have a line with the content /dist/
.
{
"rules": [
{
"files": [".gitignore"],
"includeLines": ["/dist/"]
}
]
}
/dist/
style = "style.json"
project-config self configuration
project-config is defining a valid configuration, forcing the definition of styles
as an array for styles and a valid cache
value.
{
rules: [
{
files: [".project-config.toml"],
JMESPathsMatch: [
// `style` must be defined in the file
["contains(keys(@), 'style')", true],
// `style` must be an array
["type(style)", "array"],
// at least one style configured
["op(length(style), '>', `0`)", true],
// configure cache explicitly
["contains(keys(@), 'cache')", true],
// cache must have a valid value
[
"regex_match('^(\\d+ ((seconds?)|(minutes?)|(hours?)|(days?)|(weeks?)))|(never)$', cache)",
true,
"set(@, 'cache', '5 minutes')",
],
],
},
],
}
style = ["style.json5"]
cache = "5 minutes"
Files absence
The files readme.md and index.md must not exist.
rules:
- files:
not:
readme.md: Users are more used to seeing README.md file name in uppercase
hint: Rename 'readme.md' to 'README.md'
- files:
not:
- index.md
style = "style.yaml"
Conditionals
If .gitignore includes the line __pycache__/
a pyproject.toml file must be present.
{
"rules": [
{
"ifIncludeLines": {
".gitignore": ["__pycache__/"]
},
"files": ["pyproject.toml"]
}
]
}
__pycache__/
style = "style.json"
Intercommunication between rules
Using getenv()
and setenv()
functions to pass arbitrary data between rules.
{
rules: [
{
// Setting environment variables
//
// If `cache` field is of type string, set an environment
// variable `PROJECT_CONFIG_TEST` with the value
files: [".project-config.toml"],
JMESPathsMatch: [
[
"op(op(type(cache), '!=', 'string'), '|', op(type(setenv('PROJECT_CONFIG_TEST', cache)), '==', 'object'))",
true,
],
],
},
{
// Getting environment variables
//
// If the previous environment variable is setted, change `cache`
// to `never`.
files: [".project-config.toml"],
ifJMESPathsMatch: {
".project-config.toml": [
["type(getenv('PROJECT_CONFIG_TEST'))", "string"],
],
},
JMESPathsMatch: [["cache", "never"]],
},
{
// Deleting environment variables
//
// If the previous environment variable is setted, delete it.
files: [".project-config.toml"],
ifJMESPathsMatch: {
".project-config.toml": [
["type(getenv('PROJECT_CONFIG_TEST'))", "string"],
],
},
JMESPathsMatch: [["type(setenv('PROJECT_CONFIG_TEST', null))", "object"]],
},
],
}
style = "style.json5"
cache = "never"
Compare values between files
The version defined in __version__
inside a Python script must match the metadata defined in pyproject.toml file.
[[rules]]
files = ["pyproject.toml"]
crossJMESPathsMatch = [
["project.version", ["script.py", "__version__"], "op([0], '==', [1])", true],
]
[project]
version = "1.0.0"
style = "style.toml"
"""Simple script."""
__version__ = "1.0.0"
JMESPath against online sources
Check that the license
field of package.json file is defined with a valid OSI approved SPDX license identifier.
{
rules: [
{
files: ["package.json"],
crossJMESPathsMatch: [
[
"license",
[
"gh://spdx/license-list-data@v3.25.0/json/licenses.json",
"licenses[?isOsiApproved] | [?!isDeprecatedLicenseId].licenseId",
],
"contains([1], [0])",
true,
],
],
},
],
}
{
"license": "BSD-3-Clause"
}
style = "style.json5"
Assert root directory name
Check that the name of the directory that is the root of the project matches against certain regular expression.
{
rules: [
{
files: [".project-config.toml"],
hint: "The name of the root directory must match the regex '[a-z0-9-]+$'",
JMESPathsMatch: [["regex_match('[a-z0-9-]+$', rootdir_name())", true]],
},
],
}
style = "style.json5"
TOML sections order
Check that the section [foo]
of a TOML file is placed before the section [bar]
.
{
rules: [
{
files: ["pyproject.toml"],
hint: "The section '[foo]' must be defined before the section '[bar]'",
crossJMESPathsMatch: [
[
"`null`",
["pyproject.toml?text", "@"],
"op(op([1], 'indexOf', '[foo]'), '<', op([1], 'indexOf', '[bar]'))",
true,
],
],
},
],
}
[foo]
name = "foo"
[bar]
name = "bar"
style = "style.json5"
Editing a .gitignore file
Enforce the existence of certain lines in a .gitignore file.
{
rules: [
{
files: [".gitignore"], // Enforce the existence of a '.gitignore' file
hint: "The line '__pycache__/' must be present in .gitignore file",
includeLines: ["__pycache__/"],
},
{
files: [".gitignore"],
hint: "As a directory 'tests/' has been found, the line '.pytest_cache/' must be present in the '.gitignore' file",
ifFilesExist: ["tests/"],
includeLines: [".pytest_cache/"],
},
{
files: [".gitignore"],
hint: "As a directory 'tests/' has been found, the line '.pytest_cache' must not be present as it is implicitly naming a directory",
ifFilesExist: ["tests/"],
excludeLines: [".pytest_cache"],
},
{
files: [".gitignore"],
hint: "Enforce '*.egg-info/' at the end of the .gitignore if is not already present",
includeLines: [
[
"*.egg-info/",
"op([?!starts_with(@, '*.egg-info')], `+`, ['*.egg-info/'])",
],
],
},
{
files: [".gitignore"],
hint: "The line 'dist/' must be included in .gitignore",
includeLines: [
[
"dist/",
"op([?!contains(['/dist/', 'dist', 'dist/'], @)], '+', ['dist/'])",
],
"__pycache__/",
],
},
],
}
.pytest_cache/
__pycache__/
*.egg-info/
/dist/
dist/
style = "style.json5"
Replacing code blocks languages in RST documents
- Don’t allow code blocks in RST documentation files:
Bash is not a POSIX compliant shell, use Shell lexer.
Pygments’ JSON5 lexer is not implemented yet, use Javascript lexer.
{
rules: [
{
files: ["file.rst"],
excludeContent: [
[
".. code-block:: ",
"map(&replace(@, 'code-block:: ', 'code-block:: '), @)",
],
[
".. code-block:: bash",
"map(&replace(@, 'code-block:: bash', 'code-block:: sh'), @)",
],
[
".. code-block:: json5",
"map(&replace(@, 'code-block:: json5', 'code-block:: js'), @)",
],
],
},
],
}
.. code-block:: python
foo = "bar"
.. code-block:: js
{}
.. code-block:: sh
#!/bin/sh
curl -X POST http://localhost:8080/api/v1/users
style = "style.json5"
Autofixing .editorconfig
If you run the next example using project-config fix
subcommand without creating an .editorconfig file it will be created and populated with the sections defined in the rule.
{
rules: [
{
files: [".editorconfig"],
JMESPathsMatch: [
["type(@)", "object"],
['type("")', "object"],
['"".root', true],
['"*".end_of_line', "lf"],
['"*".indent_style', "space"],
['"*".charset', "utf-8"],
['"*".trim_trailing_whitespace', true],
],
},
],
}
root = true
[*]
end_of_line = lf
indent_style = space
charset = utf-8
trim_trailing_whitespace = true
style = "style.json5"
Setting hooks in .pre-commit-config.yaml
Set a pre-commit hook for editorconfig-checker
inside .pre-commit-config.yaml.
{
rules: [
{
files: [".pre-commit-config.yaml"],
preCommitHookExists: [
"https://github.com/editorconfig-checker/editorconfig-checker.python",
[
{
id: "editorconfig-checker",
name: "editorconfig-checker",
alias: "ec",
},
],
],
},
],
}
repos:
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python
rev: 2.4.0
hooks:
- id: editorconfig-checker
name: editorconfig-checker
alias: ec
style = "style.json5"
Tip
For more complex examples check my own styles at mondeja/project-config-styles.