Skip to content

Commit 7af978b

Browse files
authored
Merge pull request #30 from mdevils/3.x.x
feat: upgrade API in order to reflect upcoming complexity in CSS selectors
2 parents 314eca5 + 93bb1a2 commit 7af978b

20 files changed

+1301
-609
lines changed

CHANGELOG.md

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,310 @@
22

33
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
44

5+
## [3.0.0](https://github.com/mdevils/css-selector-parser/compare/v2.3.2...v3.0.0) (2023-09-26)
6+
7+
8+
### ⚠ BREAKING CHANGES
9+
10+
* API is backwards incompatible.
11+
12+
#### Migrating from 2.x to 3.x
13+
14+
1. `Rule.tag` was moved to `Rule.items`.
15+
16+
Example selector: `div`.
17+
* Before: `{type: 'Rule', tagName: {type: 'TagName', name: 'div'}}`
18+
* After: `{type: 'Rule', items: [{type: 'TagName', name: 'div'}]}`
19+
20+
2. `Rule.classNames` was converted to an AST entity and moved to `Rule.items`.
21+
22+
Example selector: `.user.hidden`
23+
* Before: `{type: 'Rule', classNames: ['user', 'hidden']}`
24+
* After: `{type: 'Rule', items: [{type: 'ClassName', name: 'user'}, {type: 'ClassName', name: 'hidden'}]}`
25+
26+
3. `Rule.ids` was converted to an AST entity and moved to `Rule.items`.
27+
28+
Example selector: `#root#user-1`
29+
* Before: `{type: 'Rule', ids: ['root', 'user-1']}`
30+
* After: `{type: 'Rule', items: [{type: 'Id', name: 'root'}, {type: 'Id', name: 'user-1'}]}`
31+
32+
4. `Rule.attributes` was moved to `Rule.items`.
33+
34+
Example selector: `[href^=/][role]`
35+
* Before: `{type: 'Rule', attributes: [{type: 'Attribute', name: 'href', operator: '^=', value: {type: 'String', value: '/'}}, {type: 'Attribute', name: 'role'}]}`
36+
* After: `{type: 'Rule', items: [{type: 'Attribute', name: 'href', operator: '^=', value: {type: 'String', value: '/'}}, {type: 'Attribute', name: 'role'}]}`
37+
38+
5. `Rule.pseudoClasses` was moved to `Rule.items`.
39+
40+
Example selector: `:hover:lang(en)`
41+
* Before: `{type: 'Rule', pseudoClasses: [{type: 'PseudoClass', name: 'hover'}, {type: 'PseudoClass', name: 'lang', value: {type: 'String', value: 'en'}}]}`
42+
* After: `{type: 'Rule', items: [{type: 'PseudoClass', name: 'hover'}, {type: 'PseudoClass', name: 'lang', value: {type: 'String', value: 'en'}}]}`
43+
44+
6. `Rule.pseudoElement` was converted to an AST entity and moved to `Rule.items`.
45+
46+
Example selector: `::before`
47+
* Before: `{type: 'Rule', pseudoElement: 'before'}`
48+
* After: `{type: 'Rule', items: [{type: 'PseudoElement', name: 'before'}]}`
49+
50+
#### New AST methods
51+
52+
* `ast.id` and `ast.isId` to create and test ast nodes with type `Id`.
53+
* `ast.className` and `ast.isClassName` to create and test ast nodes with type `ClassName`.
54+
* `ast.pseudoElement` and `ast.isPseudoElement` to create and test ast nodes with type `PseudoElement`.
55+
56+
#### New Syntax Definition configuration
57+
58+
* `pseudoElements.definitions` was updated to accept signatures in otder to support specifying pseudo-elements with
59+
an argument.
60+
Example: `createParser({syntax: {pseudoElements: {definitions: {NoArgument: ['before'], String: ['highlight'], Selector: ['slotted']}}}})`.
61+
62+
#### Migrating from 1.x to 3.x
63+
64+
#### `CssSelectorParser` -> `createParser`
65+
66+
In 1.x versions there was `CssSelectorParser` class which had to be contructed and then configured.
67+
In 3.x versions there is `createParser()` function which returns a `parse()` function. All the configutation is passed
68+
to `createParser()` params.
69+
70+
Before:
71+
72+
```javascript
73+
var CssSelectorParser = require('css-selector-parser').CssSelectorParser,
74+
parser = new CssSelectorParser();
75+
parser.registerSelectorPseudos('has');
76+
parser.registerNumericPseudos('nth-child');
77+
parser.registerNestingOperators('>', '+', '~');
78+
parser.registerAttrEqualityMods('^', '$', '*', '~');
79+
80+
const selector = parser.parse('a[href^=/], .container:has(nav) > a[href]:lt($var):nth-child(5)');
81+
```
82+
83+
After:
84+
85+
```javascript
86+
import {createParser} from 'css-selector-parser';
87+
88+
const parse = createParser({
89+
syntax: {
90+
pseudoClasses: {
91+
// In 1.x any pseudo-classes were accepted.
92+
// in 2.x parser only accepts known psuedo-classes unless `unknown: accept` was specified.
93+
unknown: 'accept',
94+
definitions: {
95+
// This is a replacement for registerSelectorPseudos().
96+
Selector: ['has'],
97+
// This is a replacement for registerNumericPseudos().
98+
Formula: ['nth-child']
99+
}
100+
},
101+
// This is a replacement for registerNestingOperators().
102+
combinators: ['>', '+', '~'],
103+
attributes: {
104+
// This a replacement for registerAttrEqualityMods().
105+
// Note that equals sign ("=") is included into the operator definitions.
106+
operators: ['^=', '$=', '*=', '~=']
107+
}
108+
},
109+
// This is a replacement for enableSubstitutes()
110+
substitutes: true
111+
});
112+
113+
const selector = parse('a[href^=/], .container:has(nav) > a[href]:lt($var):nth-child(5)');
114+
```
115+
116+
* [All syntax definition options.](docs/interfaces/SyntaxDefinition.md)
117+
* [All the psudo-class definition options.](docs/interfaces/SyntaxDefinition.md#pseudoclasses)
118+
* [All the attribute definition options.](docs/interfaces/SyntaxDefinition.md#attributes)
119+
120+
#### Predefined CSS syntax definitions
121+
122+
You no longer need to make an extensive configuration of `css-selector-parser` in order to make it understand
123+
the necessary CSS standards. You can now just define CSS/CSS selectors version directly:
124+
125+
```javascript
126+
import {createParser} from 'css-selector-parser';
127+
128+
const parse = createParser({syntax: 'css3'});
129+
130+
const selector = parse('a[href^=/], .container:has(nav) > a[href]:nth-child(2n + 1)::before');
131+
```
132+
133+
Here are the pre-defined CSS standards for your disposal:
134+
135+
* `css1`: https://www.w3.org/TR/CSS1/
136+
* `css2`: https://www.w3.org/TR/CSS2/
137+
* `css3`/`selectors-3`: https://www.w3.org/TR/selectors-3/
138+
* `selectors-4`: https://www.w3.org/TR/selectors-4/
139+
* `latest`: refers to `selectors-4`
140+
* `progressive`: `latest` + accepts unknown psudo-classes, psudo-elements and attribute case sensitivity modifiers
141+
142+
#### Make sure you use proper `strict` value
143+
144+
CSS selector parser in modern browsers is very forgiving. For instance, it works fine with unclosed attribute
145+
selectors: `"[attr=value"`. If you would like to mimic this behavior from browsers, set `strict` to `false`, i.e.:
146+
147+
```javascript
148+
import {createParser} from 'css-selector-parser';
149+
150+
const parse = createParser({syntax: 'css3', strict: false});
151+
152+
const selector = parse(':lang(en'); // doesn't crash
153+
```
154+
155+
#### Render is now a separate export
156+
157+
`render()` method used to be a method of `CssSelectorParser` class. Now it can be imported directly and used.
158+
159+
Example:
160+
161+
```javascript
162+
import {createParser, render} from 'css-selector-parser';
163+
164+
const parse = createParser({syntax: 'progressive'});
165+
166+
const selector = parse('div#user-123.user:lang(en)::before');
167+
168+
console.log(render(selector)); // div#user-123.user:lang(en)::before
169+
```
170+
171+
#### AST changes
172+
173+
AST had a lot of changes.
174+
175+
#### Selector
176+
177+
[New type info.](docs/interfaces/AstSelector.md)
178+
179+
1. Type changed: `selector` -> `Selector`.
180+
2. Prop changed: `selectors` -> `rules`, also `selectors` contained `ruleSet[]`, which in turn has `rule` field,
181+
and new `rules` contains `Rule[]` directly.
182+
183+
Before: `{type: 'selector', selectors: [ {type: 'ruleSet', rule: {<RULE 1 DATA>}}, {type: 'ruleSet', rule: {<RULE 2 DATA>}} ]}`.
184+
185+
After: `{type: 'Selector', rules: [ {<RULE 1 DATA>}, {<RULE 2 DATA>} ]}`.
186+
187+
#### Rule
188+
189+
[New type info.](docs/interfaces/AstRule.md)
190+
191+
1. Type changed: `rule` -> `Rule`.
192+
2. Prop changed: `id: string` -> `items: [{type: 'Id', name: '<ID>'}, ...]`. According to the CSS spec one rule may have more
193+
than 1 `id`, so `#root#root` is a valid selector.
194+
3. Prop renamed: `nestingOperator` -> `combinator`. A proper name according to CSS spec was chosen.
195+
4. Prop renamed: `rule` -> `nestedRule`. A proper name to indicate nesting was chosen.
196+
5. Prop changed: `tagName: string` -> `items: [TagName | WildcardTag, ...]`. Using explicit distinction between
197+
TagName (i.e. `div`) and WildcardTag (`*`), because tag name can also be `*` if escaped properly (`\*`).
198+
6. Prop changed: `attrs` -> `attributes`. Attribute type was changed, see below.
199+
7. Prop changed: `pseudos` -> `pseudoClasses`. There are pseudo-elements and pseudo-classes, now they are separated
200+
properly (there is a separate `pseudoElement` type). Pseudo class type was changed, see below.
201+
202+
Before:
203+
204+
```javascript
205+
({
206+
type: 'rule',
207+
tagName: 'div',
208+
id: 'user-123',
209+
classNames: ['user'],
210+
attrs: [
211+
{name: 'role', operator: '$=', valueType: 'string', value: 'button'}
212+
],
213+
pseudos: [
214+
{name: 'lang', valueType: 'string', value: 'en'}
215+
],
216+
nestingOperator: '>'
217+
})
218+
```
219+
220+
After:
221+
222+
```javascript
223+
({
224+
type: 'Rule',
225+
items: [
226+
{type: 'TagName', name: 'div'},
227+
{type: 'Id', name: 'user-123'},
228+
{type: 'ClassName', name: 'user'},
229+
{type: 'Attribute', name: 'role', operator: '$=', value: {type: 'String', value: 'button'}},
230+
{type: 'PseudoClass', name: 'lang', value: {type: 'String', value: 'en'}}
231+
],
232+
combinator: '>'
233+
})
234+
```
235+
236+
#### Attribute
237+
238+
[New type info.](docs/interfaces/AstAttribute.md)
239+
240+
1. Type introduced: `Attribute`.
241+
2. Prop `value` and `valueType` were combined to a single prop `value` with a field `type`.
242+
243+
[All possible value types.](docs/interfaces/AstAttribute.md#value)
244+
245+
246+
##### Example 1
247+
248+
Before: `{name: 'role'}`.
249+
250+
After: `{type: 'Attribute', name: 'role'}`.
251+
252+
##### Example 2
253+
254+
Before: `{name: 'role', operator: '$=', valueType: 'string', value: 'button'}`.
255+
256+
After: `{type: 'Attribute', name: 'role', operator: '$=', value: {type: 'String', value: 'button'}}`.
257+
258+
##### Example 3
259+
260+
Before: `{name: 'role', operator: '=', valueType: 'substitute', value: 'var'}`.
261+
262+
After: `{type: 'Attribute', name: 'role', operator: '=', value: {type: 'Substitute', name: 'var'}}`.
263+
264+
#### Pseudo Classes
265+
266+
[New type info.](docs/interfaces/AstPseudoClass.md)
267+
268+
1. Type introduced: `PseudoClass`.
269+
2. Prop `value` and `valueType` were combined to a single prop `argument` with a field `type`.
270+
271+
[All possible argument types.](docs/interfaces/AstPseudoClass.md#argument)
272+
273+
##### Example 1
274+
275+
Before: `{name: 'visited'}`.
276+
277+
After: `{type: 'PseudoClass', name: 'visited'}`.
278+
279+
##### Example 2
280+
281+
Before: `{name: 'lang', valueType: 'string', value: 'en'}`.
282+
283+
After: `{type: 'PseudoClass', name: 'lang', argument: {type: 'String', value: 'en'}}`.
284+
285+
##### Example 3
286+
287+
Before: `{name: 'lang', valueType: 'substitute', value: 'var'}`.
288+
289+
After: `{type: 'PseudoClass', name: 'lang', argument: {type: 'Substitute', name: 'var'}}`.
290+
291+
##### Example 4
292+
293+
Before: `{name: 'has', valueType: 'selector', value: {type: 'selector', selectors: [{type: 'ruleSet', rule: {type: 'rule', tagName: 'div'}}]}}`.
294+
295+
After: `{type: 'PseudoClass', name: 'has', argument: {type: 'Selector', rules: [{type: 'Rule', tag: {type: 'TagName', name: 'div'}}]}}`.
296+
297+
#### Pseudo Elements
298+
299+
[New type info.](docs/interfaces/AstPseudoElement.md)
300+
301+
1. Type introduced: `PseudoElement`.
302+
303+
[All possible argument types.](docs/interfaces/AstPseudoElement.md#argument)
304+
305+
### Features
306+
307+
* upgrade API in order to reflect upcoming complexity in CSS selectors ([cece4df](https://github.com/mdevils/css-selector-parser/commit/cece4dff647b19c6211dd6c9defbd7887eca62b5))
308+
5309
### [2.3.2](https://github.com/mdevils/css-selector-parser/compare/v2.3.1...v2.3.2) (2023-06-25)
6310

7311

0 commit comments

Comments
 (0)