All notable changes to this project will be documented in this file. See standard-version for commit guidelines.
3.0.5 (2024-03-02)
3.0.4 (2023-12-15)
- align identifier and string parsing and rendering with CSS standards, closes #36, closes #37 (ac0dbc0)
3.0.3 (2023-12-08)
- align identifier parsing with CSS standards and browser behaviour (6087705)
3.0.2 (2023-11-21)
- identifier parsing for ids, classes, pseudo-classes and pseudo-elements (d222dfd)
3.0.1 (2023-11-20)
- build target for mjs (bd13208)
3.0.0 (2023-09-26)
- API is backwards incompatible.
-
Rule.tag
was moved toRule.items
.Example selector:
div
.- Before:
{type: 'Rule', tagName: {type: 'TagName', name: 'div'}}
- After:
{type: 'Rule', items: [{type: 'TagName', name: 'div'}]}
- Before:
-
Rule.classNames
was converted to an AST entity and moved toRule.items
.Example selector:
.user.hidden
- Before:
{type: 'Rule', classNames: ['user', 'hidden']}
- After:
{type: 'Rule', items: [{type: 'ClassName', name: 'user'}, {type: 'ClassName', name: 'hidden'}]}
- Before:
-
Rule.ids
was converted to an AST entity and moved toRule.items
.Example selector:
#root#user-1
- Before:
{type: 'Rule', ids: ['root', 'user-1']}
- After:
{type: 'Rule', items: [{type: 'Id', name: 'root'}, {type: 'Id', name: 'user-1'}]}
- Before:
-
Rule.attributes
was moved toRule.items
.Example selector:
[href^=/][role]
- Before:
{type: 'Rule', attributes: [{type: 'Attribute', name: 'href', operator: '^=', value: {type: 'String', value: '/'}}, {type: 'Attribute', name: 'role'}]}
- After:
{type: 'Rule', items: [{type: 'Attribute', name: 'href', operator: '^=', value: {type: 'String', value: '/'}}, {type: 'Attribute', name: 'role'}]}
- Before:
-
Rule.pseudoClasses
was moved toRule.items
.Example selector:
:hover:lang(en)
- Before:
{type: 'Rule', pseudoClasses: [{type: 'PseudoClass', name: 'hover'}, {type: 'PseudoClass', name: 'lang', value: {type: 'String', value: 'en'}}]}
- After:
{type: 'Rule', items: [{type: 'PseudoClass', name: 'hover'}, {type: 'PseudoClass', name: 'lang', value: {type: 'String', value: 'en'}}]}
- Before:
-
Rule.pseudoElement
was converted to an AST entity and moved toRule.items
.Example selector:
::before
- Before:
{type: 'Rule', pseudoElement: 'before'}
- After:
{type: 'Rule', items: [{type: 'PseudoElement', name: 'before'}]}
- Before:
ast.id
andast.isId
to create and test ast nodes with typeId
.ast.className
andast.isClassName
to create and test ast nodes with typeClassName
.ast.pseudoElement
andast.isPseudoElement
to create and test ast nodes with typePseudoElement
.
pseudoElements.definitions
was updated to accept signatures in otder to support specifying pseudo-elements with an argument. Example:createParser({syntax: {pseudoElements: {definitions: {NoArgument: ['before'], String: ['highlight'], Selector: ['slotted']}}}})
.
In 1.x versions there was CssSelectorParser
class which had to be contructed and then configured.
In 3.x versions there is createParser()
function which returns a parse()
function. All the configutation is passed
to createParser()
params.
Before:
var CssSelectorParser = require('css-selector-parser').CssSelectorParser,
parser = new CssSelectorParser();
parser.registerSelectorPseudos('has');
parser.registerNumericPseudos('nth-child');
parser.registerNestingOperators('>', '+', '~');
parser.registerAttrEqualityMods('^', '$', '*', '~');
const selector = parser.parse('a[href^=/], .container:has(nav) > a[href]:lt($var):nth-child(5)');
After:
import {createParser} from 'css-selector-parser';
const parse = createParser({
syntax: {
pseudoClasses: {
// In 1.x any pseudo-classes were accepted.
// in 2.x parser only accepts known psuedo-classes unless `unknown: accept` was specified.
unknown: 'accept',
definitions: {
// This is a replacement for registerSelectorPseudos().
Selector: ['has'],
// This is a replacement for registerNumericPseudos().
Formula: ['nth-child']
}
},
// This is a replacement for registerNestingOperators().
combinators: ['>', '+', '~'],
attributes: {
// This a replacement for registerAttrEqualityMods().
// Note that equals sign ("=") is included into the operator definitions.
operators: ['^=', '$=', '*=', '~=']
}
},
// This is a replacement for enableSubstitutes()
substitutes: true
});
const selector = parse('a[href^=/], .container:has(nav) > a[href]:lt($var):nth-child(5)');
- All syntax definition options.
- All the psudo-class definition options.
- All the attribute definition options.
You no longer need to make an extensive configuration of css-selector-parser
in order to make it understand
the necessary CSS standards. You can now just define CSS/CSS selectors version directly:
import {createParser} from 'css-selector-parser';
const parse = createParser({syntax: 'css3'});
const selector = parse('a[href^=/], .container:has(nav) > a[href]:nth-child(2n + 1)::before');
Here are the pre-defined CSS standards for your disposal:
css1
: https://www.w3.org/TR/CSS1/css2
: https://www.w3.org/TR/CSS2/css3
/selectors-3
: https://www.w3.org/TR/selectors-3/selectors-4
: https://www.w3.org/TR/selectors-4/latest
: refers toselectors-4
progressive
:latest
+ accepts unknown psudo-classes, psudo-elements and attribute case sensitivity modifiers
CSS selector parser in modern browsers is very forgiving. For instance, it works fine with unclosed attribute
selectors: "[attr=value"
. If you would like to mimic this behavior from browsers, set strict
to false
, i.e.:
import {createParser} from 'css-selector-parser';
const parse = createParser({syntax: 'css3', strict: false});
const selector = parse(':lang(en'); // doesn't crash
render()
method used to be a method of CssSelectorParser
class. Now it can be imported directly and used.
Example:
import {createParser, render} from 'css-selector-parser';
const parse = createParser({syntax: 'progressive'});
const selector = parse('div#user-123.user:lang(en)::before');
console.log(render(selector)); // div#user-123.user:lang(en)::before
AST had a lot of changes.
- Type changed:
selector
->Selector
. - Prop changed:
selectors
->rules
, alsoselectors
containedruleSet[]
, which in turn hasrule
field, and newrules
containsRule[]
directly.
Before: {type: 'selector', selectors: [ {type: 'ruleSet', rule: {<RULE 1 DATA>}}, {type: 'ruleSet', rule: {<RULE 2 DATA>}} ]}
.
After: {type: 'Selector', rules: [ {<RULE 1 DATA>}, {<RULE 2 DATA>} ]}
.
- Type changed:
rule
->Rule
. - Prop changed:
id: string
->items: [{type: 'Id', name: '<ID>'}, ...]
. According to the CSS spec one rule may have more than 1id
, so#root#root
is a valid selector. - Prop renamed:
nestingOperator
->combinator
. A proper name according to CSS spec was chosen. - Prop renamed:
rule
->nestedRule
. A proper name to indicate nesting was chosen. - Prop changed:
tagName: string
->items: [TagName | WildcardTag, ...]
. Using explicit distinction between TagName (i.e.div
) and WildcardTag (*
), because tag name can also be*
if escaped properly (\*
). - Prop changed:
attrs
->items: [<ATTRIBUTE>, ...]
. Attribute type was changed, see below. - Prop changed:
pseudos
->items: [<PSEUDO CLASS>, ...]
. There are pseudo-elements and pseudo-classes, now they are separated properly (there is a separatepseudoElement
type). Pseudo class type was changed, see below.
Before:
({
type: 'rule',
tagName: 'div',
id: 'user-123',
classNames: ['user'],
attrs: [
{name: 'role', operator: '$=', valueType: 'string', value: 'button'}
],
pseudos: [
{name: 'lang', valueType: 'string', value: 'en'}
],
nestingOperator: '>'
})
After:
({
type: 'Rule',
items: [
{type: 'TagName', name: 'div'},
{type: 'Id', name: 'user-123'},
{type: 'ClassName', name: 'user'},
{type: 'Attribute', name: 'role', operator: '$=', value: {type: 'String', value: 'button'}},
{type: 'PseudoClass', name: 'lang', value: {type: 'String', value: 'en'}}
],
combinator: '>'
})
- Type introduced:
Attribute
. - Prop
value
andvalueType
were combined to a single propvalue
with a fieldtype
.
Before: {name: 'role'}
.
After: {type: 'Attribute', name: 'role'}
.
Before: {name: 'role', operator: '$=', valueType: 'string', value: 'button'}
.
After: {type: 'Attribute', name: 'role', operator: '$=', value: {type: 'String', value: 'button'}}
.
Before: {name: 'role', operator: '=', valueType: 'substitute', value: 'var'}
.
After: {type: 'Attribute', name: 'role', operator: '=', value: {type: 'Substitute', name: 'var'}}
.
- Type introduced:
PseudoClass
. - Prop
value
andvalueType
were combined to a single propargument
with a fieldtype
.
Before: {name: 'visited'}
.
After: {type: 'PseudoClass', name: 'visited'}
.
Before: {name: 'lang', valueType: 'string', value: 'en'}
.
After: {type: 'PseudoClass', name: 'lang', argument: {type: 'String', value: 'en'}}
.
Before: {name: 'lang', valueType: 'substitute', value: 'var'}
.
After: {type: 'PseudoClass', name: 'lang', argument: {type: 'Substitute', name: 'var'}}
.
Before: {name: 'has', valueType: 'selector', value: {type: 'selector', selectors: [{type: 'ruleSet', rule: {type: 'rule', tagName: 'div'}}]}}
.
After: {type: 'PseudoClass', name: 'has', argument: {type: 'Selector', rules: [{type: 'Rule', tag: {type: 'TagName', name: 'div'}}]}}
.
- Type introduced:
PseudoElement
.
- upgrade API in order to reflect upcoming complexity in CSS selectors (cece4df)
2.3.2 (2023-06-25)
- fix foruma parsing with negative A, closes #28 (824312f)
- include js file extension into the mjs build, closes #22 (f50b350)
- rendering nested selectors with combinators, closes #27 (40fb434)
2.3.1 (2023-06-24)
- fix file inclusion (925b13a)
2.3.0 (2023-06-24)
- publish hybrid package: CommonJS and ESM modules (16fd8a1)
- Update published docs.
- Full refactoring.
- Switch to typescript.
- Pre-defined CSS syntaxes were included.
- The whole AST was documented.
In 1.x versions there was CssSelectorParser
class which had to be contructed and then configured.
In 2.x versions there is createParser()
function which returns a parse()
function. All the configutation is passed
to createParser()
params.
Before:
var CssSelectorParser = require('css-selector-parser').CssSelectorParser,
parser = new CssSelectorParser();
parser.registerSelectorPseudos('has');
parser.registerNumericPseudos('nth-child');
parser.registerNestingOperators('>', '+', '~');
parser.registerAttrEqualityMods('^', '$', '*', '~');
const selector = parser.parse('a[href^=/], .container:has(nav) > a[href]:lt($var):nth-child(5)');
After:
import {createParser} from 'css-selector-parser';
const parse = createParser({
syntax: {
pseudoClasses: {
// In 1.x any pseudo-classes were accepted.
// in 2.x parser only accepts known psuedo-classes unless `unknown: accept` was specified.
unknown: 'accept',
definitions: {
// This is a replacement for registerSelectorPseudos().
Selector: ['has'],
// This is a replacement for registerNumericPseudos().
Formula: ['nth-child']
}
},
// This is a replacement for registerNestingOperators().
combinators: ['>', '+', '~'],
attributes: {
// This a replacement for registerAttrEqualityMods().
// Note that equals sign ("=") is included into the operator definitions.
operators: ['^=', '$=', '*=', '~=']
}
},
// This is a replacement for enableSubstitutes()
substitutes: true
});
const selector = parse('a[href^=/], .container:has(nav) > a[href]:lt($var):nth-child(5)');
- All syntax definition options.
- All the psudo-class definition options.
- All the attribute definition options.
You no longer need to make an extensive configuration of css-selector-parser
in order to make it understand
the necessary CSS standards. You can now just define CSS/CSS selectors version directly:
import {createParser} from 'css-selector-parser';
const parse = createParser({syntax: 'css3'});
const selector = parse('a[href^=/], .container:has(nav) > a[href]:nth-child(2n + 1)::before');
Here are the pre-defined CSS standards for your disposal:
css1
: https://www.w3.org/TR/CSS1/css2
: https://www.w3.org/TR/CSS2/css3
/selectors-3
: https://www.w3.org/TR/selectors-3/selectors-4
: https://www.w3.org/TR/selectors-4/latest
: refers toselectors-4
progressive
:latest
+ accepts unknown psudo-classes, psudo-elements and attribute case sensitivity modifiers
CSS selector parser in modern browsers is very forgiving. For instance, it works fine with unclosed attribute
selectors: "[attr=value"
. If you would like to mimic this behavior from browsers, set strict
to false
, i.e.:
import {createParser} from 'css-selector-parser';
const parse = createParser({syntax: 'css3', strict: false});
const selector = parse(':lang(en'); // doesn't crash
render()
method used to be a method of CssSelectorParser
class. Now it can be imported directly and used.
Example:
import {createParser, render} from 'css-selector-parser';
const parse = createParser({syntax: 'progressive'});
const selector = parse('div#user-123.user:lang(en)');
console.log(render(selector)); // div#user-123.user:lang(en)
AST had a lot of changes.
- Type changed:
selector
->Selector
. - Prop changed:
selectors
->rules
, alsoselectors
containedruleSet[]
, which in turn hasrule
field, and newrules
containsRule[]
directly.
Before: {type: 'selector', selectors: [ {type: 'ruleSet', rule: {<RULE 1 DATA>}}, {type: 'ruleSet', rule: {<RULE 2 DATA>}} ]}
.
After: {type: 'Selector', rules: [ {<RULE 1 DATA>}, {<RULE 2 DATA>} ]}
.
- Type changed:
rule
->Rule
. - Prop changed:
id: string
->ids: string[]
. According to the CSS spec one rule may have more than 1id
, so#root#root
is a valid selector. - Prop renamed:
nestingOperator
->combinator
. A proper name according to CSS spec was chosen. - Prop renamed:
rule
->nestedRule
. A proper name to indicate nesting was chosen. - Prop changed:
tagName: string
->tag: TagName | WildcardTag
. Using explicit distinction between TagName (i.e.div
) and WildcardTag (*
), because tag name can also be*
if escaped properly (\*
). - Prop changed:
attrs
->attributes
. Attribute type was changed, see below. - Prop changed:
pseudos
->pseudoClasses
. There are pseudo-elements and pseudo-classes, now they are separated properly (there is a separatepseudoElement
property). Pseudo class type was changed, see below.
Before:
({
type: 'rule',
tagName: 'div',
id: 'user-123',
classNames: ['user'],
attrs: [
{name: 'role', operator: '$=', valueType: 'string', value: 'button'}
],
pseudos: [
{name: 'lang', valueType: 'string', value: 'en'}
],
nestingOperator: '>'
})
After:
({
type: 'Rule',
tag: {type: 'TagName', name: 'div'},
ids: ['user-123'],
classNames: ['user'],
attributes: [
{type: 'Attribute', name: 'role', operator: '$=', value: {type: 'String', value: 'button'}}
],
pseudoClasses: [
{type: 'PseudoClass', name: 'lang', value: {type: 'String', value: 'en'}}
],
combinator: '>'
})
- Type introduced:
Attribute
. - Prop
value
andvalueType
were combined to a single propvalue
with a fieldtype
.
Before: {name: 'role'}
.
After: {type: 'Attribute', name: 'role'}
.
Before: {name: 'role', operator: '$=', valueType: 'string', value: 'button'}
.
After: {type: 'Attribute', name: 'role', operator: '$=', value: {type: 'String', value: 'button'}}
.
Before: {name: 'role', operator: '=', valueType: 'substitute', value: 'var'}
.
After: {type: 'Attribute', name: 'role', operator: '=', value: {type: 'Substitute', name: 'var'}}
.
- Type introduced:
PseudoClass
. - Prop
value
andvalueType
were combined to a single propargument
with a fieldtype
.
Before: {name: 'visited'}
.
After: {type: 'PseudoClass', name: 'visited'}
.
Before: {name: 'lang', valueType: 'string', value: 'en'}
.
After: {type: 'PseudoClass', name: 'lang', argument: {type: 'String', value: 'en'}}
.
Before: {name: 'lang', valueType: 'substitute', value: 'var'}
.
After: {type: 'PseudoClass', name: 'lang', argument: {type: 'Substitute', name: 'var'}}
.
Before: {name: 'has', valueType: 'selector', value: {type: 'selector', selectors: [{type: 'ruleSet', rule: {type: 'rule', tagName: 'div'}}]}}
.
After: {type: 'PseudoClass', name: 'has', argument: {type: 'Selector', rules: [{type: 'Rule', tag: {type: 'TagName', name: 'div'}}]}}
.