Skip to content

Commit

Permalink
Add properties and types options to extend syntax dictionary
Browse files Browse the repository at this point in the history
  • Loading branch information
lahmatiy committed Oct 27, 2020
1 parent 528f6da commit e56d739
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## next

- Bumped CSSTree to `^1.0.0` (mdn-data is bumped to `2.0.12`)
- Added `properties` and `types` options to extend syntax dictionary
- Added `ignoreValue` option (#14)

## 1.8.0 (January 24, 2020)
Expand Down
74 changes: 72 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,79 @@ Setup plugin in your [stylelint config](http://stylelint.io/user-guide/configura

### Options

#### properties

Type: `Object` or `null`
Default: `null`

Overrides or/and extends property definition dictionary. CSS [Value Definition Syntax](https://github.com/csstree/csstree/blob/master/docs/definition-syntax.md) is used to define value's syntax. If definition starts with `|` it added to existing definition if any. See [CSS syntax reference](https://csstree.github.io/docs/syntax/) for default definitions.

In the following example we extend `width` property and defines `size`:

```json
{
"plugins": [
"stylelint-csstree-validator"
],
"rules": {
"csstree/validator": {
"properties": {
"width": "| new-keyword | custom-function(<length>, <percentage>)",
"size": "<length-percentage>"
}
}
}
}
```

Using property definitions with the syntax `<any-value>` is an alternative for `ignore` option.

```json
{
"plugins": [
"stylelint-csstree-validator"
],
"rules": {
"csstree/validator": {
"properties": {
"my-custom-property": "<any-value>"
}
}
}
}
```

#### types

Type: `Object` or `null`
Default: `null`

Overrides or/and extends type definition dictionary. CSS [Value Definition Syntax](https://github.com/csstree/csstree/blob/master/docs/definition-syntax.md) is used to define value's syntax. If definition starts with `|` it added to existing definition if any. See [CSS syntax reference](https://csstree.github.io/docs/syntax/) for default definitions.

In the following example we define new functional type `my-fn()` and extend `color` type:

```json
{
"plugins": [
"stylelint-csstree-validator"
],
"rules": {
"csstree/validator": {
"properties": {
"some-property": "<my-fn()>"
},
"types": {
"color": "| darken(<color>, [ <percentage> | <number [0, 1]> ])",
"my-fn()": "my-fn( <length-percentage> )"
}
}
}
}
```

#### ignore

Type: `Array` or `false`
Type: `Array` or `false`
Default: `false`

Defines a list of property names that should be ignored by the validator.
Expand All @@ -54,7 +124,7 @@ In this example, plugin would not test declaration with property name `composes`

#### ignoreValue

Type: `RegExp`
Type: `RegExp`
Default: `false`

Defines a pattern for values that should be ignored by the validator.
Expand Down
22 changes: 21 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,33 @@ const messages = stylelint.utils.ruleMessages(ruleName, {
module.exports = stylelint.createPlugin(ruleName, function(options) {
options = options || {};

const syntax = csstree.lexer;
const ignoreValue = options.ignoreValue && (typeof options.ignoreValue === 'string' || toString.call(options.ignoreValue) === '[object RegExp]')
? new RegExp(options.ignoreValue)
: false;
const ignore = Array.isArray(options.ignore)
? new Set(options.ignore.map(name => String(name).toLowerCase()))
: false;
const syntax = !options.properties && !options.types
? csstree.lexer // default syntax
: csstree.fork((syntaxConfig) => { // syntax with custom properties or/and types
for (const [name, value] of Object.entries(options.properties || {})) {
syntaxConfig.properties[name] = value
.replace(/^\s*\|/, (m, index) => syntaxConfig.properties[name]
? syntaxConfig.properties[name] + ' |'
: ''
);
}

for (const [name, value] of Object.entries(options.types || {})) {
syntaxConfig.types[name] = value
.replace(/^\s*\|/, (m, index) => syntaxConfig.types[name]
? syntaxConfig.types[name] + ' |'
: ''
);
}

return syntaxConfig;
}).lexer;

return function(root, result) {
root.walkDecls(function(decl) {
Expand Down
31 changes: 30 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ css({ ignore: ['foo', 'bar'] }, function(tr) {
tr.notOk('.foo { baz: 1 }', 'Unknown property `baz`');
});

// should ignore by ignoreValue pattern
css({ ignoreValue: "^patternToIgnore$", ignore: ['bar'] }, function(tr) {
tr.ok('.foo { color: red }');
tr.ok('.foo { color: #fff }');
Expand All @@ -83,4 +84,32 @@ css({ ignoreValue: "^patternToIgnore$", ignore: ['bar'] }, function(tr) {
tr.notOk('.foo { color: notMatchingPattern }', messages.invalid('color'));
tr.notOk('.foo { foo: patternToIgnore }', 'Unknown property `foo`');
tr.notOk('.foo { foo: notMatchingPattern }', 'Unknown property `foo`');
})
});

// extend dictionary
css({
properties: {
foo: '<my-fn()>',
bar: '| <my-fn()>',
qux: '<qux>',
relax: '<any-value>'
},
types: {
'my-fn()': 'my-fn(<length-percentage>)',
qux: '| <length>',
color: '| darken(<color>, <percentage>)'
}
}, function(tr) {
tr.ok('.foo { foo: my-fn(10px) }');
tr.ok('.foo { foo: my-fn(10%) }');
tr.ok('.foo { foo: my-fn(0) }');
tr.ok('.bar { bar: my-fn(10px) }');
tr.notOk('.baz { baz: my-fn(10px) }', 'Unknown property `baz`');
tr.ok('.qux { qux: 10px }');
tr.notOk('.foo { color: my-fn(10px) }', messages.invalid('color'));
tr.ok('.foo { color: darken(white, 5%) }');
tr.ok('.foo { color: white }');
tr.notOk('.foo { color: darken(white, .05) }', messages.invalid('color'));
tr.ok('.foo { relax: white }');
tr.ok('.foo { relax: 10px solid whatever }');
});

0 comments on commit e56d739

Please sign in to comment.