@markuplint/selector 4.7.7 → 5.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.ja.md +259 -0
- package/ARCHITECTURE.md +259 -0
- package/CHANGELOG.md +13 -2
- package/README.md +6 -0
- package/SKILL.md +126 -0
- package/docs/maintenance.ja.md +211 -0
- package/docs/maintenance.md +211 -0
- package/docs/matching.ja.md +211 -0
- package/docs/matching.md +211 -0
- package/lib/compare-specificity.d.ts +9 -1
- package/lib/compare-specificity.js +8 -0
- package/lib/create-selector.d.ts +13 -0
- package/lib/create-selector.js +14 -1
- package/lib/debug.d.ts +5 -0
- package/lib/debug.js +5 -0
- package/lib/extended-selector/aria-pseudo-class.d.ts +26 -3
- package/lib/extended-selector/aria-pseudo-class.js +33 -4
- package/lib/extended-selector/aria-role-pseudo-class.d.ts +11 -2
- package/lib/extended-selector/aria-role-pseudo-class.js +9 -0
- package/lib/extended-selector/content-model-pseudo-class.d.ts +11 -2
- package/lib/extended-selector/content-model-pseudo-class.js +9 -0
- package/lib/index.d.ts +5 -2
- package/lib/index.js +1 -1
- package/lib/invalid-selector-error.d.ts +8 -0
- package/lib/invalid-selector-error.js +10 -1
- package/lib/is.d.ts +19 -5
- package/lib/is.js +19 -9
- package/lib/match-selector.d.ts +18 -2
- package/lib/match-selector.js +27 -40
- package/lib/regex-selector-matches.d.ts +12 -0
- package/lib/regex-selector-matches.js +12 -0
- package/lib/selector.d.ts +35 -5
- package/lib/selector.js +75 -96
- package/lib/types.d.ts +89 -1
- package/package.json +10 -7
package/lib/selector.js
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
-
};
|
|
12
|
-
var _Selector_ruleset, _Ruleset_selectorGroup, _StructuredSelector_edge, _StructuredSelector_selector, _SelectorTarget_combinedFrom, _SelectorTarget_extended, _SelectorTarget_isAdded;
|
|
13
1
|
import { resolveNamespace } from '@markuplint/ml-spec';
|
|
14
2
|
import parser from 'postcss-selector-parser';
|
|
15
3
|
import { compareSpecificity } from './compare-specificity.js';
|
|
@@ -18,16 +6,28 @@ import { InvalidSelectorError } from './invalid-selector-error.js';
|
|
|
18
6
|
import { isElement, isNonDocumentTypeChildNode, isPureHTMLElement } from './is.js';
|
|
19
7
|
const selLog = coreLog.extend('selector');
|
|
20
8
|
const resLog = coreLog.extend('result');
|
|
9
|
+
/**
|
|
10
|
+
* CSS selector matcher that parses a selector string and matches it against nodes.
|
|
11
|
+
*
|
|
12
|
+
* Use {@link createSelector} to create cached instances with extended pseudo-class support.
|
|
13
|
+
*/
|
|
21
14
|
export class Selector {
|
|
15
|
+
#ruleset;
|
|
16
|
+
/**
|
|
17
|
+
* @param selector - The CSS selector string to parse
|
|
18
|
+
* @param extended - Extended pseudo-class handlers to register
|
|
19
|
+
*/
|
|
22
20
|
constructor(selector, extended = {}) {
|
|
23
|
-
|
|
24
|
-
__classPrivateFieldSet(this, _Selector_ruleset, Ruleset.parse(selector, extended), "f");
|
|
21
|
+
this.#ruleset = Ruleset.parse(selector, extended);
|
|
25
22
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Tests whether the given node matches this selector.
|
|
25
|
+
*
|
|
26
|
+
* @param el - The node to test
|
|
27
|
+
* @param scope - The scope node for `:scope` pseudo-class resolution
|
|
28
|
+
* @returns The specificity of the first matching selector, or `false` if none matched
|
|
29
|
+
*/
|
|
30
|
+
match(el, scope) {
|
|
31
31
|
scope = scope ?? (isElement(el) ? el : null);
|
|
32
32
|
const results = this.search(el, scope);
|
|
33
33
|
for (const result of results) {
|
|
@@ -37,16 +37,19 @@ export class Selector {
|
|
|
37
37
|
}
|
|
38
38
|
return false;
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Evaluates all comma-separated selectors against the given node
|
|
42
|
+
* and returns each result (matched or unmatched).
|
|
43
|
+
*
|
|
44
|
+
* @param el - The node to test
|
|
45
|
+
* @param scope - The scope node for `:scope` pseudo-class resolution
|
|
46
|
+
* @returns An array of results, one per comma-separated selector alternative
|
|
47
|
+
*/
|
|
48
|
+
search(el, scope) {
|
|
45
49
|
scope = scope ?? (isElement(el) ? el : null);
|
|
46
|
-
return
|
|
50
|
+
return this.#ruleset.match(el, scope);
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
|
-
_Selector_ruleset = new WeakMap();
|
|
50
53
|
class Ruleset {
|
|
51
54
|
static parse(selector, extended) {
|
|
52
55
|
const selectors = [];
|
|
@@ -63,27 +66,24 @@ class Ruleset {
|
|
|
63
66
|
}
|
|
64
67
|
return new Ruleset(selectors, extended, 0);
|
|
65
68
|
}
|
|
69
|
+
headCombinator;
|
|
70
|
+
#selectorGroup = [];
|
|
66
71
|
constructor(selectors, extended, depth) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const head = __classPrivateFieldGet(this, _Ruleset_selectorGroup, "f")[0];
|
|
72
|
+
this.#selectorGroup.push(...selectors.map(selector => new StructuredSelector(selector, depth, extended)));
|
|
73
|
+
const head = this.#selectorGroup[0];
|
|
70
74
|
this.headCombinator = head?.headCombinator ?? null;
|
|
71
75
|
if (this.headCombinator && depth <= 0) {
|
|
72
|
-
if (
|
|
73
|
-
throw new InvalidSelectorError(
|
|
76
|
+
if (this.#selectorGroup[0]?.selector) {
|
|
77
|
+
throw new InvalidSelectorError(this.#selectorGroup[0]?.selector);
|
|
74
78
|
}
|
|
75
79
|
throw new Error('Combinated selector depth is not expected');
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
|
-
match(
|
|
79
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
80
|
-
el,
|
|
81
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
82
|
-
scope) {
|
|
82
|
+
match(el, scope) {
|
|
83
83
|
if (coreLog.enabled) {
|
|
84
84
|
coreLog('<%s> (%s)', isElement(el) ? el.localName : el.nodeName, scope ? (isElement(scope) ? scope.localName : scope.nodeName) : null);
|
|
85
85
|
}
|
|
86
|
-
return
|
|
86
|
+
return this.#selectorGroup.map(selector => {
|
|
87
87
|
if (selLog.enabled) {
|
|
88
88
|
selLog('"%s"', selector.selector);
|
|
89
89
|
}
|
|
@@ -95,16 +95,16 @@ class Ruleset {
|
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
-
_Ruleset_selectorGroup = new WeakMap();
|
|
99
98
|
class StructuredSelector {
|
|
99
|
+
#edge;
|
|
100
|
+
headCombinator;
|
|
101
|
+
#selector;
|
|
100
102
|
constructor(selector, depth, extended) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
__classPrivateFieldSet(this, _StructuredSelector_selector, selector, "f");
|
|
104
|
-
__classPrivateFieldSet(this, _StructuredSelector_edge, new SelectorTarget(extended, depth), "f");
|
|
103
|
+
this.#selector = selector;
|
|
104
|
+
this.#edge = new SelectorTarget(extended, depth);
|
|
105
105
|
this.headCombinator =
|
|
106
|
-
|
|
107
|
-
const nodes = [...
|
|
106
|
+
this.#selector.nodes[0]?.type === 'combinator' ? (this.#selector.nodes[0].value ?? null) : null;
|
|
107
|
+
const nodes = [...this.#selector.nodes];
|
|
108
108
|
if (0 < depth && this.headCombinator) {
|
|
109
109
|
nodes.unshift(parser.pseudo({ value: ':scope' }));
|
|
110
110
|
}
|
|
@@ -112,8 +112,8 @@ class StructuredSelector {
|
|
|
112
112
|
switch (node.type) {
|
|
113
113
|
case 'combinator': {
|
|
114
114
|
const combinedTarget = new SelectorTarget(extended, depth);
|
|
115
|
-
combinedTarget.from(
|
|
116
|
-
|
|
115
|
+
combinedTarget.from(this.#edge, node);
|
|
116
|
+
this.#edge = combinedTarget;
|
|
117
117
|
break;
|
|
118
118
|
}
|
|
119
119
|
case 'root':
|
|
@@ -127,38 +127,34 @@ class StructuredSelector {
|
|
|
127
127
|
throw new Error(`Unsupported comment in selector: ${selector.toString()}`);
|
|
128
128
|
}
|
|
129
129
|
default: {
|
|
130
|
-
|
|
130
|
+
this.#edge.add(node);
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
get selector() {
|
|
136
|
-
return
|
|
136
|
+
return this.#selector.nodes.join('');
|
|
137
137
|
}
|
|
138
|
-
match(
|
|
139
|
-
|
|
140
|
-
el,
|
|
141
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
142
|
-
scope) {
|
|
143
|
-
return __classPrivateFieldGet(this, _StructuredSelector_edge, "f").match(el, scope, 0);
|
|
138
|
+
match(el, scope) {
|
|
139
|
+
return this.#edge.match(el, scope, 0);
|
|
144
140
|
}
|
|
145
141
|
}
|
|
146
|
-
_StructuredSelector_edge = new WeakMap(), _StructuredSelector_selector = new WeakMap();
|
|
147
142
|
class SelectorTarget {
|
|
143
|
+
attr = [];
|
|
144
|
+
class = [];
|
|
145
|
+
#combinedFrom = null;
|
|
146
|
+
depth;
|
|
147
|
+
#extended;
|
|
148
|
+
id = [];
|
|
149
|
+
#isAdded = false;
|
|
150
|
+
pseudo = [];
|
|
151
|
+
tag = null;
|
|
148
152
|
constructor(extended, depth) {
|
|
149
|
-
this
|
|
150
|
-
this.class = [];
|
|
151
|
-
_SelectorTarget_combinedFrom.set(this, null);
|
|
152
|
-
_SelectorTarget_extended.set(this, void 0);
|
|
153
|
-
this.id = [];
|
|
154
|
-
_SelectorTarget_isAdded.set(this, false);
|
|
155
|
-
this.pseudo = [];
|
|
156
|
-
this.tag = null;
|
|
157
|
-
__classPrivateFieldSet(this, _SelectorTarget_extended, extended, "f");
|
|
153
|
+
this.#extended = extended;
|
|
158
154
|
this.depth = depth;
|
|
159
155
|
}
|
|
160
156
|
add(selector) {
|
|
161
|
-
|
|
157
|
+
this.#isAdded = true;
|
|
162
158
|
switch (selector.type) {
|
|
163
159
|
case 'tag':
|
|
164
160
|
case 'universal': {
|
|
@@ -184,17 +180,13 @@ class SelectorTarget {
|
|
|
184
180
|
}
|
|
185
181
|
}
|
|
186
182
|
from(target, combinator) {
|
|
187
|
-
|
|
183
|
+
this.#combinedFrom = { target, combinator };
|
|
188
184
|
}
|
|
189
|
-
match(
|
|
190
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
191
|
-
el,
|
|
192
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
193
|
-
scope, count) {
|
|
185
|
+
match(el, scope, count) {
|
|
194
186
|
const result = this._match(el, scope, count);
|
|
195
187
|
if (selLog.enabled) {
|
|
196
188
|
const nodeName = el.nodeName;
|
|
197
|
-
const selector =
|
|
189
|
+
const selector = this.#combinedFrom?.target.toString() ?? this.toString();
|
|
198
190
|
const combinator = result.combinator ? ` ${result.combinator}` : '';
|
|
199
191
|
selLog('The %s element by "%s" => %s (%d)', nodeName, `${selector}${combinator}`, result.matched, count);
|
|
200
192
|
if (selector === ':scope') {
|
|
@@ -213,22 +205,18 @@ class SelectorTarget {
|
|
|
213
205
|
this.pseudo.map(pseudo => pseudo.value).join(''),
|
|
214
206
|
].join('');
|
|
215
207
|
}
|
|
216
|
-
_match(
|
|
217
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
218
|
-
el,
|
|
219
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
220
|
-
scope, count) {
|
|
208
|
+
_match(el, scope, count) {
|
|
221
209
|
const unitCheck = this._matchWithoutCombineChecking(el, scope);
|
|
222
210
|
if (!unitCheck.matched) {
|
|
223
211
|
return unitCheck;
|
|
224
212
|
}
|
|
225
|
-
if (!
|
|
213
|
+
if (!this.#combinedFrom) {
|
|
226
214
|
return unitCheck;
|
|
227
215
|
}
|
|
228
216
|
if (!isNonDocumentTypeChildNode(el)) {
|
|
229
217
|
return unitCheck;
|
|
230
218
|
}
|
|
231
|
-
const { target, combinator } =
|
|
219
|
+
const { target, combinator } = this.#combinedFrom;
|
|
232
220
|
switch (combinator.value) {
|
|
233
221
|
// Descendant combinator
|
|
234
222
|
case ' ': {
|
|
@@ -424,7 +412,7 @@ class SelectorTarget {
|
|
|
424
412
|
throw new Error('Unsupported column combinator yet. If you want it, please request it as the issue (https://github.com/markuplint/markuplint/issues/new).');
|
|
425
413
|
}
|
|
426
414
|
default: {
|
|
427
|
-
throw new Error(`Unsupported ${
|
|
415
|
+
throw new Error(`Unsupported ${this.#combinedFrom.combinator.value} combinator in selector`);
|
|
428
416
|
}
|
|
429
417
|
}
|
|
430
418
|
}
|
|
@@ -436,11 +424,7 @@ class SelectorTarget {
|
|
|
436
424
|
* @param scope
|
|
437
425
|
* @private
|
|
438
426
|
*/
|
|
439
|
-
_matchWithoutCombineChecking(
|
|
440
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
441
|
-
el,
|
|
442
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
443
|
-
scope) {
|
|
427
|
+
_matchWithoutCombineChecking(el, scope) {
|
|
444
428
|
const specificity = [0, 0, 0];
|
|
445
429
|
if (!isElement(el)) {
|
|
446
430
|
return {
|
|
@@ -472,7 +456,7 @@ class SelectorTarget {
|
|
|
472
456
|
}
|
|
473
457
|
}
|
|
474
458
|
let matched = true;
|
|
475
|
-
if (!
|
|
459
|
+
if (!this.#isAdded && !isScope(el, scope)) {
|
|
476
460
|
matched = false;
|
|
477
461
|
}
|
|
478
462
|
if (matched && !this.id.every(id => id.value === el.id)) {
|
|
@@ -501,7 +485,7 @@ class SelectorTarget {
|
|
|
501
485
|
specificity[1] += this.attr.length;
|
|
502
486
|
if (matched) {
|
|
503
487
|
for (const pseudo of this.pseudo) {
|
|
504
|
-
const pseudoRes = pseudoMatch(pseudo, el, scope,
|
|
488
|
+
const pseudoRes = pseudoMatch(pseudo, el, scope, this.#extended, this.depth);
|
|
505
489
|
specificity[0] += pseudoRes.specificity[0];
|
|
506
490
|
specificity[1] += pseudoRes.specificity[1];
|
|
507
491
|
specificity[2] += pseudoRes.specificity[2];
|
|
@@ -517,19 +501,18 @@ class SelectorTarget {
|
|
|
517
501
|
if (matched) {
|
|
518
502
|
return {
|
|
519
503
|
specificity,
|
|
520
|
-
matched,
|
|
504
|
+
matched: true,
|
|
521
505
|
nodes: [el],
|
|
522
506
|
has,
|
|
523
507
|
};
|
|
524
508
|
}
|
|
525
509
|
return {
|
|
526
510
|
specificity,
|
|
527
|
-
matched,
|
|
511
|
+
matched: false,
|
|
528
512
|
not,
|
|
529
513
|
};
|
|
530
514
|
}
|
|
531
515
|
}
|
|
532
|
-
_SelectorTarget_combinedFrom = new WeakMap(), _SelectorTarget_extended = new WeakMap(), _SelectorTarget_isAdded = new WeakMap();
|
|
533
516
|
function attrMatch(attr,
|
|
534
517
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
535
518
|
el) {
|
|
@@ -594,9 +577,7 @@ el) {
|
|
|
594
577
|
}
|
|
595
578
|
function pseudoMatch(pseudo,
|
|
596
579
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
597
|
-
el,
|
|
598
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
599
|
-
scope, extended, depth) {
|
|
580
|
+
el, scope, extended, depth) {
|
|
600
581
|
switch (pseudo.value) {
|
|
601
582
|
//
|
|
602
583
|
/**
|
|
@@ -803,9 +784,7 @@ scope, extended, depth) {
|
|
|
803
784
|
}
|
|
804
785
|
function isScope(
|
|
805
786
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
806
|
-
el,
|
|
807
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
808
|
-
scope) {
|
|
787
|
+
el, scope) {
|
|
809
788
|
return el === scope || el.parentNode === null;
|
|
810
789
|
}
|
|
811
790
|
function getDescendants(
|
package/lib/types.d.ts
CHANGED
|
@@ -1,24 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal attribute representation for selector matching.
|
|
3
|
+
* Pure data — no methods, no class instances.
|
|
4
|
+
*
|
|
5
|
+
* In Rust this maps directly to a struct.
|
|
6
|
+
*/
|
|
7
|
+
export interface SelectorAttr {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly localName: string;
|
|
10
|
+
readonly value: string;
|
|
11
|
+
readonly namespaceURI: string | null;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Minimal node representation for selector matching.
|
|
15
|
+
* Pure data with tree-navigation references.
|
|
16
|
+
*
|
|
17
|
+
* In Rust this maps to a trait backed by arena indices.
|
|
18
|
+
*/
|
|
19
|
+
export interface SelectorNode {
|
|
20
|
+
readonly nodeType: number;
|
|
21
|
+
readonly nodeName: string;
|
|
22
|
+
readonly parentNode: SelectorNode | null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Minimal element representation for CSS selector matching.
|
|
26
|
+
* Captures **only** the properties the selector engine actually reads.
|
|
27
|
+
*
|
|
28
|
+
* DOM `Element` and `MLElement` both satisfy this interface,
|
|
29
|
+
* but plain objects can satisfy it too — enabling Rust interop
|
|
30
|
+
* and unit-testing without a full DOM.
|
|
31
|
+
*
|
|
32
|
+
* In Rust this maps to a struct + trait.
|
|
33
|
+
*/
|
|
34
|
+
export interface SelectorElement extends SelectorNode {
|
|
35
|
+
readonly localName: string;
|
|
36
|
+
readonly id: string;
|
|
37
|
+
readonly namespaceURI: string | null;
|
|
38
|
+
readonly classList: {
|
|
39
|
+
contains(className: string): boolean;
|
|
40
|
+
};
|
|
41
|
+
readonly attributes: Iterable<SelectorAttr>;
|
|
42
|
+
readonly parentElement: SelectorElement | null;
|
|
43
|
+
readonly previousElementSibling: SelectorElement | null;
|
|
44
|
+
readonly nextElementSibling: SelectorElement | null;
|
|
45
|
+
readonly children: Iterable<SelectorElement>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A CSS specificity tuple: `[id, class, type]`.
|
|
49
|
+
* Each component counts selectors of the corresponding category.
|
|
50
|
+
*/
|
|
1
51
|
export type Specificity = readonly [number, number, number];
|
|
52
|
+
/**
|
|
53
|
+
* The result of evaluating a parsed selector against a node.
|
|
54
|
+
*/
|
|
2
55
|
export type SelectorResult = SelectorMatchedResult | SelectorUnmatchedResult;
|
|
56
|
+
/**
|
|
57
|
+
* A successful selector match result, including the specificity,
|
|
58
|
+
* the matched nodes, and any `:has()` sub-match results.
|
|
59
|
+
*/
|
|
3
60
|
export type SelectorMatchedResult = {
|
|
61
|
+
/** The computed specificity of the matched selector */
|
|
4
62
|
readonly specificity: Specificity;
|
|
5
63
|
readonly matched: true;
|
|
6
|
-
|
|
64
|
+
/** The elements that were matched */
|
|
65
|
+
readonly nodes: readonly SelectorElement[];
|
|
66
|
+
/** Results from `:has()` pseudo-class sub-matches */
|
|
7
67
|
readonly has: readonly SelectorMatchedResult[];
|
|
8
68
|
};
|
|
69
|
+
/**
|
|
70
|
+
* An unsuccessful selector match result.
|
|
71
|
+
*/
|
|
9
72
|
export type SelectorUnmatchedResult = {
|
|
73
|
+
/** The computed specificity of the unmatched selector */
|
|
10
74
|
readonly specificity: Specificity;
|
|
11
75
|
readonly matched: false;
|
|
76
|
+
/** Results from `:not()` pseudo-class sub-matches that did match */
|
|
12
77
|
readonly not?: readonly SelectorMatchedResult[];
|
|
13
78
|
};
|
|
79
|
+
/**
|
|
80
|
+
* A regex-based selector that matches elements by node name and/or attribute
|
|
81
|
+
* patterns using regular expressions. Supports combinators for chained matching.
|
|
82
|
+
*/
|
|
14
83
|
export type RegexSelector = RegexSelectorWithoutCombination & {
|
|
84
|
+
/** An optional chained selector with a combinator */
|
|
15
85
|
readonly combination?: {
|
|
16
86
|
readonly combinator: RegexSelectorCombinator;
|
|
17
87
|
} & RegexSelector;
|
|
18
88
|
};
|
|
89
|
+
/**
|
|
90
|
+
* CSS combinator types supported by regex selectors.
|
|
91
|
+
*
|
|
92
|
+
* - `' '` – Descendant combinator
|
|
93
|
+
* - `'>'` – Child combinator
|
|
94
|
+
* - `'+'` – Next-sibling combinator
|
|
95
|
+
* - `'~'` – Subsequent-sibling combinator
|
|
96
|
+
* - `':has(+)'`– Previous-sibling combinator (via `:has()`)
|
|
97
|
+
* - `':has(~)'`– Previous subsequent-sibling combinator (via `:has()`)
|
|
98
|
+
*/
|
|
19
99
|
export type RegexSelectorCombinator = ' ' | '>' | '+' | '~' | ':has(+)' | ':has(~)';
|
|
100
|
+
/**
|
|
101
|
+
* The non-combinatorial part of a regex selector.
|
|
102
|
+
* Matches against the element's node name, attribute name, and/or attribute value
|
|
103
|
+
* using regular expression patterns.
|
|
104
|
+
*/
|
|
20
105
|
export type RegexSelectorWithoutCombination = {
|
|
106
|
+
/** Regex pattern to match against the element's local name */
|
|
21
107
|
readonly nodeName?: string;
|
|
108
|
+
/** Regex pattern to match against attribute names */
|
|
22
109
|
readonly attrName?: string;
|
|
110
|
+
/** Regex pattern to match against attribute values */
|
|
23
111
|
readonly attrValue?: string;
|
|
24
112
|
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/selector",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0-alpha.0",
|
|
4
4
|
"description": "Extended W3C Selectors matcher",
|
|
5
5
|
"repository": "git@github.com:markuplint/markuplint.git",
|
|
6
6
|
"author": "Yusuke Hirao <yusukehirao@me.com>",
|
|
7
7
|
"license": "MIT",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22"
|
|
10
|
+
},
|
|
8
11
|
"type": "module",
|
|
9
12
|
"exports": {
|
|
10
13
|
".": {
|
|
@@ -24,15 +27,15 @@
|
|
|
24
27
|
"clean": "tsc --build --clean tsconfig.build.json"
|
|
25
28
|
},
|
|
26
29
|
"dependencies": {
|
|
27
|
-
"@markuplint/ml-spec": "
|
|
30
|
+
"@markuplint/ml-spec": "5.0.0-alpha.0",
|
|
28
31
|
"@types/debug": "4.1.12",
|
|
29
32
|
"debug": "4.4.3",
|
|
30
|
-
"postcss-selector-parser": "7.1.
|
|
31
|
-
"type-fest": "4.
|
|
33
|
+
"postcss-selector-parser": "7.1.1",
|
|
34
|
+
"type-fest": "5.4.4"
|
|
32
35
|
},
|
|
33
36
|
"devDependencies": {
|
|
34
|
-
"@types/jsdom": "
|
|
35
|
-
"jsdom": "
|
|
37
|
+
"@types/jsdom": "27.0.0",
|
|
38
|
+
"jsdom": "28.1.0"
|
|
36
39
|
},
|
|
37
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "13dcfc84ec83d87360c720e253383b60767e1b56"
|
|
38
41
|
}
|