@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/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { compareSpecificity } from './compare-specificity.js';
|
|
2
|
-
export { matchSelector
|
|
2
|
+
export { matchSelector } from './match-selector.js';
|
|
3
|
+
export type { SelectorMatches } from './match-selector.js';
|
|
3
4
|
export { createSelector } from './create-selector.js';
|
|
5
|
+
export { Selector } from './selector.js';
|
|
6
|
+
export type { ExtendedPseudoClass } from './selector.js';
|
|
4
7
|
export { InvalidSelectorError } from './invalid-selector-error.js';
|
|
5
|
-
export * from './types.js';
|
|
8
|
+
export type * from './types.js';
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { compareSpecificity } from './compare-specificity.js';
|
|
2
2
|
export { matchSelector } from './match-selector.js';
|
|
3
3
|
export { createSelector } from './create-selector.js';
|
|
4
|
+
export { Selector } from './selector.js';
|
|
4
5
|
export { InvalidSelectorError } from './invalid-selector-error.js';
|
|
5
|
-
export * from './types.js';
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error thrown when a CSS selector string cannot be parsed.
|
|
3
|
+
*/
|
|
1
4
|
export declare class InvalidSelectorError extends Error {
|
|
2
5
|
name: string;
|
|
6
|
+
/** The invalid selector string that caused this error */
|
|
3
7
|
selector: string;
|
|
8
|
+
/**
|
|
9
|
+
* @param selector - The invalid selector string
|
|
10
|
+
* @param message - An optional custom error message
|
|
11
|
+
*/
|
|
4
12
|
constructor(selector: string, message?: string);
|
|
5
13
|
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error thrown when a CSS selector string cannot be parsed.
|
|
3
|
+
*/
|
|
1
4
|
export class InvalidSelectorError extends Error {
|
|
5
|
+
name = 'InvalidSelectorError';
|
|
6
|
+
/** The invalid selector string that caused this error */
|
|
7
|
+
selector;
|
|
8
|
+
/**
|
|
9
|
+
* @param selector - The invalid selector string
|
|
10
|
+
* @param message - An optional custom error message
|
|
11
|
+
*/
|
|
2
12
|
constructor(selector, message) {
|
|
3
13
|
super(message ?? `Invalid selector: "${selector}"`);
|
|
4
|
-
this.name = 'InvalidSelectorError';
|
|
5
14
|
this.selector = selector;
|
|
6
15
|
}
|
|
7
16
|
}
|
package/lib/is.d.ts
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { SelectorElement, SelectorNode } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Checks whether the given node is an Element node.
|
|
4
|
+
*
|
|
5
|
+
* @param node - The node to check
|
|
6
|
+
* @returns `true` if the node is an Element
|
|
7
|
+
*/
|
|
8
|
+
export declare function isElement(node: SelectorNode): node is SelectorElement;
|
|
9
|
+
/**
|
|
10
|
+
* Checks whether the given node is a non-DocumentType child node
|
|
11
|
+
* (i.e., has `previousElementSibling` and `nextElementSibling` properties).
|
|
12
|
+
*
|
|
13
|
+
* @param node - The node to check
|
|
14
|
+
* @returns `true` if the node is an Element or CharacterData
|
|
15
|
+
*/
|
|
16
|
+
export declare function isNonDocumentTypeChildNode(node: SelectorNode): node is SelectorElement;
|
|
3
17
|
/**
|
|
4
18
|
* Checks if the given element is a pure HTML element.
|
|
5
19
|
*
|
|
6
20
|
* If a pure HTML element, `localName` returns lowercase,
|
|
7
21
|
* `nodeName` returns uppercase.
|
|
8
22
|
*
|
|
9
|
-
* @param el The element to check
|
|
10
|
-
* @returns
|
|
23
|
+
* @param el - The element to check
|
|
24
|
+
* @returns `true` if the element is a pure HTML element, `false` otherwise
|
|
11
25
|
*/
|
|
12
|
-
export declare function isPureHTMLElement(el:
|
|
26
|
+
export declare function isPureHTMLElement(el: SelectorElement): boolean;
|
package/lib/is.js
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
node
|
|
4
|
-
|
|
1
|
+
const ELEMENT_NODE = 1;
|
|
2
|
+
/**
|
|
3
|
+
* Checks whether the given node is an Element node.
|
|
4
|
+
*
|
|
5
|
+
* @param node - The node to check
|
|
6
|
+
* @returns `true` if the node is an Element
|
|
7
|
+
*/
|
|
8
|
+
export function isElement(node) {
|
|
9
|
+
return node.nodeType === ELEMENT_NODE;
|
|
5
10
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Checks whether the given node is a non-DocumentType child node
|
|
13
|
+
* (i.e., has `previousElementSibling` and `nextElementSibling` properties).
|
|
14
|
+
*
|
|
15
|
+
* @param node - The node to check
|
|
16
|
+
* @returns `true` if the node is an Element or CharacterData
|
|
17
|
+
*/
|
|
18
|
+
export function isNonDocumentTypeChildNode(node) {
|
|
9
19
|
return 'previousElementSibling' in node && 'nextElementSibling' in node;
|
|
10
20
|
}
|
|
11
21
|
/**
|
|
@@ -14,8 +24,8 @@ node) {
|
|
|
14
24
|
* If a pure HTML element, `localName` returns lowercase,
|
|
15
25
|
* `nodeName` returns uppercase.
|
|
16
26
|
*
|
|
17
|
-
* @param el The element to check
|
|
18
|
-
* @returns
|
|
27
|
+
* @param el - The element to check
|
|
28
|
+
* @returns `true` if the element is a pure HTML element, `false` otherwise
|
|
19
29
|
*/
|
|
20
30
|
export function isPureHTMLElement(
|
|
21
31
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
package/lib/match-selector.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import type { Specificity, RegexSelector } from './types.js';
|
|
1
|
+
import type { SelectorNode, Specificity, RegexSelector } from './types.js';
|
|
2
2
|
import type { MLMLSpec } from '@markuplint/ml-spec';
|
|
3
|
+
/**
|
|
4
|
+
* The result of matching a selector against a node.
|
|
5
|
+
* Either a successful match with specificity and captured data, or an unsuccessful match.
|
|
6
|
+
*/
|
|
3
7
|
export type SelectorMatches = SelectorMatched | SelectorUnmatched;
|
|
4
8
|
type SelectorMatched = {
|
|
5
9
|
readonly matched: true;
|
|
@@ -10,5 +14,17 @@ type SelectorMatched = {
|
|
|
10
14
|
type SelectorUnmatched = {
|
|
11
15
|
readonly matched: false;
|
|
12
16
|
};
|
|
13
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Matches a CSS selector or regex selector against a node.
|
|
19
|
+
*
|
|
20
|
+
* Supports both standard CSS selectors (as strings) and markuplint's
|
|
21
|
+
* {@link RegexSelector} for pattern-based matching with captured groups.
|
|
22
|
+
*
|
|
23
|
+
* @param el - The node to test
|
|
24
|
+
* @param selector - A CSS selector string, a regex selector object, or `undefined`
|
|
25
|
+
* @param scope - The scope element for `:scope` pseudo-class resolution
|
|
26
|
+
* @param specs - The HTML/ARIA specification data for extended pseudo-classes
|
|
27
|
+
* @returns A match result with specificity and captured data, or `{ matched: false }`
|
|
28
|
+
*/
|
|
29
|
+
export declare function matchSelector(el: SelectorNode, selector: string | RegexSelector | undefined, scope?: SelectorNode | null, specs?: MLMLSpec): SelectorMatches;
|
|
14
30
|
export {};
|
package/lib/match-selector.js
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
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 _SelectorTarget_combinedFrom, _SelectorTarget_selector;
|
|
13
1
|
import { isElement, isNonDocumentTypeChildNode, isPureHTMLElement } from './is.js';
|
|
14
2
|
import { regexSelectorMatches } from './regex-selector-matches.js';
|
|
15
3
|
import { createSelector } from './create-selector.js';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Matches a CSS selector or regex selector against a node.
|
|
6
|
+
*
|
|
7
|
+
* Supports both standard CSS selectors (as strings) and markuplint's
|
|
8
|
+
* {@link RegexSelector} for pattern-based matching with captured groups.
|
|
9
|
+
*
|
|
10
|
+
* @param el - The node to test
|
|
11
|
+
* @param selector - A CSS selector string, a regex selector object, or `undefined`
|
|
12
|
+
* @param scope - The scope element for `:scope` pseudo-class resolution
|
|
13
|
+
* @param specs - The HTML/ARIA specification data for extended pseudo-classes
|
|
14
|
+
* @returns A match result with specificity and captured data, or `{ matched: false }`
|
|
15
|
+
*/
|
|
16
|
+
export function matchSelector(el, selector, scope, specs) {
|
|
21
17
|
if (selector == null || selector === '') {
|
|
22
18
|
return {
|
|
23
19
|
matched: false,
|
|
@@ -39,9 +35,7 @@ scope, specs) {
|
|
|
39
35
|
}
|
|
40
36
|
return regexSelect(el, selector);
|
|
41
37
|
}
|
|
42
|
-
function regexSelect(
|
|
43
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
44
|
-
el, selector) {
|
|
38
|
+
function regexSelect(el, selector) {
|
|
45
39
|
let edge = new SelectorTarget(selector);
|
|
46
40
|
let edgeSelector = selector.combination;
|
|
47
41
|
while (edgeSelector) {
|
|
@@ -53,28 +47,26 @@ el, selector) {
|
|
|
53
47
|
return edge.match(el);
|
|
54
48
|
}
|
|
55
49
|
class SelectorTarget {
|
|
50
|
+
#combinedFrom = null;
|
|
51
|
+
#selector;
|
|
56
52
|
constructor(selector) {
|
|
57
|
-
|
|
58
|
-
_SelectorTarget_selector.set(this, void 0);
|
|
59
|
-
__classPrivateFieldSet(this, _SelectorTarget_selector, selector, "f");
|
|
53
|
+
this.#selector = selector;
|
|
60
54
|
}
|
|
61
55
|
from(target, combinator) {
|
|
62
|
-
|
|
56
|
+
this.#combinedFrom = { target, combinator };
|
|
63
57
|
}
|
|
64
|
-
match(
|
|
65
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
66
|
-
el) {
|
|
58
|
+
match(el) {
|
|
67
59
|
const unitCheck = this._matchWithoutCombineChecking(el);
|
|
68
60
|
if (!unitCheck.matched) {
|
|
69
61
|
return unitCheck;
|
|
70
62
|
}
|
|
71
|
-
if (!
|
|
63
|
+
if (!this.#combinedFrom) {
|
|
72
64
|
return unitCheck;
|
|
73
65
|
}
|
|
74
66
|
if (!isNonDocumentTypeChildNode(el)) {
|
|
75
67
|
return unitCheck;
|
|
76
68
|
}
|
|
77
|
-
const { target, combinator } =
|
|
69
|
+
const { target, combinator } = this.#combinedFrom;
|
|
78
70
|
switch (combinator) {
|
|
79
71
|
// Descendant combinator
|
|
80
72
|
case ' ': {
|
|
@@ -149,20 +141,15 @@ class SelectorTarget {
|
|
|
149
141
|
return { matched: false };
|
|
150
142
|
}
|
|
151
143
|
default: {
|
|
152
|
-
throw new Error(`Unsupported ${
|
|
144
|
+
throw new Error(`Unsupported ${this.#combinedFrom.combinator} combinator in selector`);
|
|
153
145
|
}
|
|
154
146
|
}
|
|
155
147
|
}
|
|
156
|
-
_matchWithoutCombineChecking(
|
|
157
|
-
|
|
158
|
-
el) {
|
|
159
|
-
return uncombinedRegexSelect(el, __classPrivateFieldGet(this, _SelectorTarget_selector, "f"));
|
|
148
|
+
_matchWithoutCombineChecking(el) {
|
|
149
|
+
return uncombinedRegexSelect(el, this.#selector);
|
|
160
150
|
}
|
|
161
151
|
}
|
|
162
|
-
|
|
163
|
-
function uncombinedRegexSelect(
|
|
164
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
165
|
-
el, selector) {
|
|
152
|
+
function uncombinedRegexSelect(el, selector) {
|
|
166
153
|
if (!isElement(el)) {
|
|
167
154
|
return {
|
|
168
155
|
matched: false,
|
|
@@ -227,13 +214,13 @@ el, selector) {
|
|
|
227
214
|
specificity[1] += specifiedAttr.size;
|
|
228
215
|
if (matched) {
|
|
229
216
|
return {
|
|
230
|
-
matched,
|
|
217
|
+
matched: true,
|
|
231
218
|
selector: `${tagSelector}${attrSelector}`,
|
|
232
219
|
specificity,
|
|
233
220
|
data,
|
|
234
221
|
};
|
|
235
222
|
}
|
|
236
|
-
return { matched };
|
|
223
|
+
return { matched: false };
|
|
237
224
|
}
|
|
238
225
|
function mergeMatches(a, b, sep, close = false) {
|
|
239
226
|
return {
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests a raw string value against a regex selector pattern and returns
|
|
3
|
+
* the captured groups if matched.
|
|
4
|
+
*
|
|
5
|
+
* Plain strings are treated as exact-match patterns (`^pattern$`).
|
|
6
|
+
* Regex literals (`/pattern/flags`) are used as-is.
|
|
7
|
+
*
|
|
8
|
+
* @param reg - The regex pattern string, or `undefined` to skip matching
|
|
9
|
+
* @param raw - The raw string value to test against the pattern
|
|
10
|
+
* @param ignoreCase - Whether to perform case-insensitive matching
|
|
11
|
+
* @returns An object of captured groups (`$0`, `$1`, ... and named groups), or `null` if unmatched or `reg` is `undefined`
|
|
12
|
+
*/
|
|
1
13
|
export declare function regexSelectorMatches(reg: string | undefined, raw: string, ignoreCase: boolean): {
|
|
2
14
|
[x: string]: string;
|
|
3
15
|
} | null;
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests a raw string value against a regex selector pattern and returns
|
|
3
|
+
* the captured groups if matched.
|
|
4
|
+
*
|
|
5
|
+
* Plain strings are treated as exact-match patterns (`^pattern$`).
|
|
6
|
+
* Regex literals (`/pattern/flags`) are used as-is.
|
|
7
|
+
*
|
|
8
|
+
* @param reg - The regex pattern string, or `undefined` to skip matching
|
|
9
|
+
* @param raw - The raw string value to test against the pattern
|
|
10
|
+
* @param ignoreCase - Whether to perform case-insensitive matching
|
|
11
|
+
* @returns An object of captured groups (`$0`, `$1`, ... and named groups), or `null` if unmatched or `reg` is `undefined`
|
|
12
|
+
*/
|
|
1
13
|
export function regexSelectorMatches(reg, raw, ignoreCase) {
|
|
2
14
|
if (!reg) {
|
|
3
15
|
return null;
|
package/lib/selector.d.ts
CHANGED
|
@@ -1,9 +1,39 @@
|
|
|
1
|
-
import type { SelectorResult, Specificity } from './types.js';
|
|
2
|
-
|
|
1
|
+
import type { SelectorElement, SelectorNode, SelectorResult, Specificity } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Registry of extended pseudo-class handlers keyed by pseudo-class name.
|
|
4
|
+
*
|
|
5
|
+
* Each handler is a curried function: given the pseudo-class content string,
|
|
6
|
+
* it returns a matcher that tests a {@link SelectorElement} and produces
|
|
7
|
+
* a {@link SelectorResult}.
|
|
8
|
+
*/
|
|
9
|
+
export type ExtendedPseudoClass = Readonly<Record<string, (content: string) => (el: SelectorElement) => SelectorResult>>;
|
|
10
|
+
/**
|
|
11
|
+
* CSS selector matcher that parses a selector string and matches it against nodes.
|
|
12
|
+
*
|
|
13
|
+
* Use {@link createSelector} to create cached instances with extended pseudo-class support.
|
|
14
|
+
*/
|
|
3
15
|
export declare class Selector {
|
|
4
16
|
#private;
|
|
17
|
+
/**
|
|
18
|
+
* @param selector - The CSS selector string to parse
|
|
19
|
+
* @param extended - Extended pseudo-class handlers to register
|
|
20
|
+
*/
|
|
5
21
|
constructor(selector: string, extended?: ExtendedPseudoClass);
|
|
6
|
-
|
|
7
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Tests whether the given node matches this selector.
|
|
24
|
+
*
|
|
25
|
+
* @param el - The node to test
|
|
26
|
+
* @param scope - The scope node for `:scope` pseudo-class resolution
|
|
27
|
+
* @returns The specificity of the first matching selector, or `false` if none matched
|
|
28
|
+
*/
|
|
29
|
+
match(el: SelectorNode, scope?: SelectorNode | null): Specificity | false;
|
|
30
|
+
/**
|
|
31
|
+
* Evaluates all comma-separated selectors against the given node
|
|
32
|
+
* and returns each result (matched or unmatched).
|
|
33
|
+
*
|
|
34
|
+
* @param el - The node to test
|
|
35
|
+
* @param scope - The scope node for `:scope` pseudo-class resolution
|
|
36
|
+
* @returns An array of results, one per comma-separated selector alternative
|
|
37
|
+
*/
|
|
38
|
+
search(el: SelectorNode, scope?: SelectorNode | null): SelectorResult[];
|
|
8
39
|
}
|
|
9
|
-
export {};
|