@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/lib/index.d.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  export { compareSpecificity } from './compare-specificity.js';
2
- export { matchSelector, SelectorMatches } from './match-selector.js';
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
- export declare function isElement(node: Node): node is Element;
2
- export declare function isNonDocumentTypeChildNode(node: Node): node is Element | CharacterData;
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 Returns true if the element is a pure HTML element, otherwise returns false.
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: Element): boolean;
26
+ export declare function isPureHTMLElement(el: SelectorElement): boolean;
package/lib/is.js CHANGED
@@ -1,11 +1,21 @@
1
- export function isElement(
2
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
3
- node) {
4
- return node.nodeType === node.ELEMENT_NODE;
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
- export function isNonDocumentTypeChildNode(
7
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
8
- node) {
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 Returns true if the element is a pure HTML element, otherwise returns false.
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
@@ -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
- export declare function matchSelector(el: Node, selector: string | RegexSelector | undefined, scope?: ParentNode | null, specs?: MLMLSpec): SelectorMatches;
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 {};
@@ -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
- export function matchSelector(
17
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
18
- el, selector,
19
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
20
- scope, specs) {
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
- _SelectorTarget_combinedFrom.set(this, null);
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
- __classPrivateFieldSet(this, _SelectorTarget_combinedFrom, { target, combinator }, "f");
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 (!__classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f")) {
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 } = __classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f");
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 ${__classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f").combinator} combinator in selector`);
144
+ throw new Error(`Unsupported ${this.#combinedFrom.combinator} combinator in selector`);
153
145
  }
154
146
  }
155
147
  }
156
- _matchWithoutCombineChecking(
157
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
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
- _SelectorTarget_combinedFrom = new WeakMap(), _SelectorTarget_selector = new WeakMap();
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
- type ExtendedPseudoClass = Readonly<Record<string, (content: string) => (el: Element) => SelectorResult>>;
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
- match(el: Node, scope?: ParentNode | null): Specificity | false;
7
- search(el: Node, scope?: ParentNode | null): SelectorResult[];
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 {};