Skip to main content

Configuring properties

The configuration has the following properties:

{
"extends": [],
"plugins": {},
"parser": {},
"parserOptions": {},
"specs": [],
"excludeFiles": [],
"rules": {},
"nodeRules": [],
"childNodeRules": [],
"pretenders": [],
"overrides": {}
}
PropertyFirst guideInterface
extendsUsing PresetsInterface
pluginsApplying custom rules, Creating custom ruleInterface
parserUsing to besides HTMLInterface
parserOptions-Interface
specsUsing to besides HTMLInterface
excludeFilesIgnoring fileInterface
rulesApplying rulesInterface
nodeRulesApplying to someInterface
childNodeRulesApplying to someInterface
pretendersPretendersInterface
overridesOverriding to disable rulesInterface

Resolving specified paths

extends, plugins, parser, specs, and excludeFiles can specify paths. In extends, plugins, parser, and specs , it can specify a npm package instead of a path.

First, it tries to import it as a package. If it fails, such as the package doesn't exist, or the strings are not a package, it resolves strings as just a path. If it is a relative path, the basis becomes the directory that has the configuration file.

Details each property

extends

If you specify other config file paths, it merges the current setting with them.

{
"extends": [
// load as a local file
"../../.markuplintrc",
// load as a package
"third-party-config"
]
}

The name added the prefix markuplint: loads a preset provided from Markuplint.

{
"extends": ["markuplint:recommended"]
}

The name added the prefix plugin: loads the config provided from any plugins. The before the solidus is a namespace determined by the plugin. The after the solidus is the unique config name on the plugin.

{
"extends": ["plugin:third-party-plugin-name/config-name"],
"plugins": ["third-party-plugin"]
}

Interface

interface Config {
extends?: string[];
}

plugins

You can load any plugins. Specify a package name or a path. Can specify settings if the plugin has it.

{
"plugins": [
"third-party-plugin",
"@third-party/markuplint-plugin",
{
"name": "third-party-plugin2",
"settings": {
"foo": "bar"
}
},
"./path/to/local-plugin.js",
{
"name": "./path/to/local-plugin.js2",
"settings": {
"foo": "bar"
}
}
]
}

Interface

interface Config {
plugins?: (
| string
| {
name: string;
settings?: Record<string, string | number | boolean | Object>;
}
)[];
}

parser

Specify a regex to the key, and the parser file path or a package name to the value. The regex should be specify it matches the target file (ex., the extension part).

{
"parser": {
"\\.pug$": "@markuplint/pug-parser",
"\\.[jt]sx?$": "@markuplint/jsx-parser",
"\\.vue$": "@markuplint/vue-parser",
"\\.svelte$": "@markuplint/svelte-parser",
"\\.ext$": "./path/to/custom-parser/any-lang.js"
}
}

Interface

interface Config {
parser?: {
[regex: string]: string;
};
}

parserOptions

{
"parserOptions": {
"ignoreFrontMatter": true,
"authoredElementName": ["AuthoredElement"]
}
}

ignoreFrontMatter

When set true the parser ignores the Front Matter format part of the source code. Default is false.

---
prop: value
---

<html>
...
</html>

authoredElementName

If you use React, Vue, or more, Markuplint's parser detects a component as a native HTML element if you name it with only lower-case characters. In most cases, components should start naming upper case, but each syntax parser plugin may has a specific pattern (Ex. Vue: Built-in Special Elements). If you need different naming patterns, You can specify the authoredElementName option to resolve. Default is undefined.

{
"parserOptions": {
"authoredElementName": ["custom", "mine"]
}
}
<template>
<custom><!-- It detects as a native HTML element if not specified. --></custom>
<mine><!-- It detects as a native HTML element if not specified. --></mine>
</template>

Interface

interface Config {
parserOptions?: {
ignoreFrontMatter?: boolean;
authoredElementName?: string | RegExp | Function | (string | RegExp | Function)[];
};
}

specs

Specify a regex to the key, and the spec file path or a package name to the value. The regex should be specify it matches the target file (ex., the extension part).

{
"specs": {
"\\.vue$": "@markuplint/vue-spec",
"\\.ext$": "./path/to/custom-specs/any-lang.js"
}
}

Interface

interface Config {
specs?: {
[regex: string]: string;
};
}
Deprecated syntax until v1.x

You can specify it as Array or string, but it's deprecated.

{
// Deprecated
"specs": ["@markuplint/vue-spec", "./path/to/custom-specs/any-lang"]
}
{
// Deprecated
"specs": "@markuplint/vue-spec"
}

excludeFiles

If necessary, files can be excluded. The value requires a relative or absolute path from the configuration file. Paths can also be in the glob format. You can use the ! symbol to denote negation. Entries specified later will take precedence. The pattern operates in accordance with the specification of .gitignore. (Resolved using node-ignore).

{
"excludeFiles": ["./ignore.html", "./ignore/*.html", "!./ignore/no-ignore.html"]
}

Interface

interface Config {
excludeFiles?: string[];
}

rules

Configure to enable or specify details to rules. The value for each rule is either string, number, and array.

The rule becomes disabled if specified as false. It applies as the default value each rule has if specified as true.

{
"rules": {
"rule-name": "value" // Specify the rule name and value to here
}
}

Otherwise, you can specify details by Object:

{
"rules": {
"rule-name": {
"value": "any-value",
"severity": "error",
"options": {
"any-option": "any-optional-value"
}
}
}
}

value

It's optional. It evaluates as the default value each rule has if omit it.

severity

It accepts "error" or "warning". It's optional. It applies as the default severity each rule has if omit it.

options

It accepts Object the rule defines. It's optional. There are cases in which some of its fields have a default value.

Deprecated option field

option field was replaced with options since v3.0.0. It can apply it through option for compatibility but using deprecated. Use options instead.

About the rule name

There are cases in which a rule name includes a solidus. In that case, it indicates the rule is from a plugin. The before the solidus is a namespace determined by the plugin. The after the solidus is the unique rule name on the plugin.

{
"plugins": ["third-party-plugin", "./path/to/local-plugin.js"],
"rules": {
"core-rule-name": true,
"third-party-plugin/rule-name": true,
"named-plugin-imported-form-local/rule-name": true
}
}

Interface

interface Config {
rules?: {
[ruleName: string]: Rule<T, O>;
};
}

type Rule<T, O> =
| boolean
| T
| {
severity?: 'error' | 'warning' | 'info';
value?: T;
option?: O;
reason?: string;
};

nodeRules

If you want only any specific element to apply some rule, you can specify by this property. Be careful to the value is an array.

It requires either selector or regexSelector. And it also requires rules field. It specifies the same value of the rules property.

{
"nodeRules": [
{
"selector": "main",
"rules": {
"class-naming": "/[a-z]+(__[a-z]+)?/"
}
}
]
}

rules

It accepts the same value of the rules property. It's required.

selector

It accepts Selector to matche the target. It's required if no use regexSelector.

regexSelector

It accepts a regular expression to matche the target. It's required if no use selector.

The field has nodeName, attrName, and attrValue fields that accept regular expression optionally. So each of these enables to omit. It is AND condition if combine.

The regular expression format must be nested by solidus. Otherwise, it is applied as just a string.

{
"nodeRules": [
{
"regexSelector": {
"nodeName": "/^[a-z]+$/",
"attrName": "/^[a-z]+$/",
"attrValue": "/^[a-z]+$/"
},
"rules": {
"any-rule": "any-value"
}
}
]
}
tip

It has a powerful feature that captures a string through regular expressions and expands it for the value of the rules property. It expands the capturing incremental number prepended $ mark as a variable. It should specify the value in the Mustache format.

{
"nodeRules": [
{
"regexSelector": {
"attrName": "/^data-([a-z]+)$/"
},
"rules": {
"any-rule": "It is {{ $1 }} data attribute",
"any-rule2": {
"value": "It is {{ $1 }} data attribute",
"severity": "error"
}
}
}
]
}

Of course, you can use the named capture group. It expands the name as a variable.

{
"nodeRules": [
{
"regexSelector": {
"attrName": "/^data-(?<dataName>[a-z]+)$/"
},
"rules": {
"any-rule": "It is {{ dataName }} data attribute"
}
}
]
}
caution

Recommend using named capture. The numbered capture may conflict and be overwritten.

{
"nodeRules": [
{
"regexSelector": {
"attrName": "/^data-([a-z]+)$/", // It will be `$1`.
"attrValue": "/^(.+)$/" // It will be `$1` too. `$1` is overwritten.
},
"rules": {
"any-rule": "It is {{ $1 }} data attribute, and value is {{ $1 }}"
}
},
{
"regexSelector": {
"attrName": "/^data-(?<dataName>[a-z]+)$/", // It will be `dataName`.
"attrValue": "/^(?<dataValue>.+)$/" // It will be `dataValue`.
},
"rules": {
"any-rule": "It is {{ dataName }} data attribute, and value is {{ dataValue }}"
}
}
]
}

You can select the element in complex conditions if you use the combination field.

{
"nodeRules": [
{
"regexSelector": {
"attrName": "img",
"combination": {
"combinator": ":has(~)",
"nodeName": "source"
}
}
}
]
}

The above is the same as CSS selector img:has(~ source).

combinator field supports below:

  • " ": Descendant combinator
  • ">": Child combinator
  • "+": Next-sibling combinator
  • ":has(+)": Prev-sibling combinator
  • "~": Subsequent-sibling combinator
  • ":has(~)": Preceding-sibling combinator

You can define nodes unlimitedly deeply.

{
"nodeRules": [
{
"regexSelector": {
"nodeName": "el1",
"combination": {
"combinator": " ",
"nodeName": "el2",
"combination": {
"combinator": ">",
"nodeName": "el3",
"combination": {
"combinator": "+",
"nodeName": "el4",
"combination": {
"combinator": "~",
"nodeName": "el5"
}
}
}
}
}
}
]
}

The above is the same as CSS selector el1 el2 > el3 + el4 ~ el5.

Interface

interface Config {
nodeRules?: (
| {
selector: string;
rules: {
[ruleName: string]: Rule<T, O>;
};
}
| {
regexSelector: RegexSelector;
rules: {
[ruleName: string]: Rule<T, O>;
};
}
)[];
}

type RegexSelector = {
nodeName?: string;
attrName?: string;
attrValue?: string;
combination?: RegexSelector & {
combinator: ' ' | '>' | '+' | '~' | ':has(+)' | ':has(~)';
};
};

childNodeRules

If you want any specific element's descendants to apply some rule, you can specify by this property. If specifies true to the inheritance field, affects all descendant nodes of the target element, if not, affects only child nodes. Be careful to the value is an array.

note

This property accepts fields of the same as nodeRules property except for having inheritance field.

inheritance

It accepts boolean. It's optional and the default value is false.

Interface

interface Config {
childNodeRules?: (
| {
selector: string;
inheritance?: boolean;
rules: {
[ruleName: string]: Rule<T, O>;
};
}
| {
regexSelector: RegexSelector;
inheritance?: boolean;
rules: {
[ruleName: string]: Rule<T, O>;
};
}
)[];
}

pretenders

The Pretenders feature is what a custom component pretends as a native HTML element. It helps that some rules evaluate it as an element that is the result rendered. Be careful to the value is an array.

selector

It accepts Selector to matche the target component. It's required.

as

It accepts an element name or an element with properties. It's required.

Element name
{
"pretenders": [
{
"selector": "MyComponent",
"as": "div"
}
]
}
Element with properties
{
"pretenders": [
{
"selector": "MyComponent",
"as": {
"element": "div",
"inheritAttrs": true,
"attrs": [
{
"name": "role",
"value": "region"
}
]
}
}
]
}

as.element

It accepts an element name. It's required.

as.inheritAttrs

It accepts boolean. Whether the rendered element should expose the attributes defined on the component. It's optional. The default value is false if omit it.

const MyComponent = props => {
return <div {...props}>{props.children}</div>;
};
{
"pretenders": [
{
"selector": "MyComponent",
"as": {
"element": "div",
"inheritAttrs": true
}
}
]
}
<div>
{/* Evaluate as rendered div element has aria-live="polite" */}
<MyComponent aria-live="polite">Lorem Ipsam</MyComponent>
</div>;

as.attrs

It accepts an array. Evaluate as rendered element has attributes specified. It's optional.

const MyPicture = () => {
return <img src="path/to/file.png" alt="Lorem ipsam" />;
};
{
"pretenders": [
{
"selector": "MyPicture",
"as": {
"element": "img",
"attrs": [
{
"name": "src"
},
{
"name": "alt",
"value": "Lorem ipsam"
}
]
}
}
]
}
<div>
{/* Evaluate as rendered img element has the src attribute and alt="Lorem ipsam" */}
<MyComponent />
</div>;

as.attrs[].name

It accepts an attribute name. It's required.

as.attrs[].value

It accepts an attribute value. It's optional.

as.aria

It accepts Object as ARIA Properties. It has only name field currently. It's optional.

as.aria.name

It accepts boolean or Object as the accessbile name. Specify true if the component has the name clearly. Otherwise, you set the attribute name that refs the name to fromAttr.

const MyIcon = ({ label }) => {
return (
<svg role="img" aria-label={label}>
<rect />
</svg>
);
};
{
"pretenders": [
{
"selector": "MyIcon",
"as": {
"element": "svg",
"aria": {
"name": {
"fromAttr": "label"
}
}
}
}
]
}
<div>
{/* Evaluate as the accessible name is "my icon name" */}
<MyIcon label="my icon name" />
</div>;

Interface

interface Config {
pretenders?: {
selector: string;
as: string | OriginalNode;
}[];
}

type OriginalNode = {
element: string;
namespace?: 'svg';

inheritAttrs?: boolean;
attrs?: {
name: string;
value?:
| string
| {
fromAttr: string;
};
}[];

aria?: {
name?:
| boolean
| {
fromAttr: string;
};
};
};

overrides

You can override configurations to specific files if you specify the overrides option. It applies to glob format paths specified to a key. They are evaluated by minimatch.

{
"rules": {
"any-rule": true
},
"overrides": {
"./path/to/**/*": {
"rules": {
"any-rule": false
}
}
}
}

It can override the following properties:

Interface

interface Config {
overrides?: {
[path: string]: Omit<Config, 'extends' | 'overrides'>;
};
}