@markuplint/selector 3.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017-2019 Yusuke Hirao
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # @markuplint/selector
2
+
3
+ [![npm version](https://badge.fury.io/js/%40markuplint%2Fselector.svg)](https://www.npmjs.com/package/@markuplint/selector)
4
+ [![Build Status](https://travis-ci.org/markuplint/markuplint.svg?branch=main)](https://travis-ci.org/markuplint/markuplint)
5
+ [![Coverage Status](https://coveralls.io/repos/github/markuplint/markuplint/badge.svg?branch=main)](https://coveralls.io/github/markuplint/markuplint?branch=main)
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ $ npm install @markuplint/selector
11
+
12
+ $ yarn add @markuplint/selector
13
+ ```
14
+
15
+ ## [W3C Selectors](https://www.w3.org/TR/selectors-4/) matcher
16
+
17
+ Supported selectors and operators:
18
+
19
+ | Selector Type | Code Example | Support |
20
+ | ------------------------------------------------ | ----------------------------------------------------------------------------------------- | ------- |
21
+ | Universal selector | `*` | ✅ |
22
+ | Type selector | `div` | ✅ |
23
+ | ID selector | `#id` | ✅ |
24
+ | Class selector | `.class` | ✅ |
25
+ | Attribute selector | `[data-attr]` | ✅ |
26
+ | Attribute selector, Exact match | `[data-attr=value]` `[data-attr=value i]` | ✅ |
27
+ | Attribute selector, Include whitespace separated | `[data-attr~=value]` `[data-attr~=value i]` | ✅ |
28
+ | Attribute selector, Subcode match | <code>[data-attr\|=value]</code> <code>[data-attr\|=value i]</code> | ✅ |
29
+ | Attribute selector, Partial match | `[data-attr*=value]` `[data-attr*=value i]` | ✅ |
30
+ | Attribute selector, Forward match | `[data-attr^=value]` `[data-attr^=value i]` | ✅ |
31
+ | Attribute selector, Backward match | `[data-attr$=value]` `[data-attr$=value i]` | ✅ |
32
+ | Negation pseudo-class | `:not(div)` | ✅ |
33
+ | Matches-Any pseudo-class | `:is(div)` | ✅ |
34
+ | Specificity-adjustment pseudo-class | `:where(div)` | ✅ |
35
+ | Relational pseudo-class | `:has(div)` `:has(> div)` `:has(+ div)` `:has(~ div)` | ✅ |
36
+ | Directionality pseudo-class | `:dir(ltr)` | ❌ |
37
+ | Language pseudo-class | `:lang(en)` | ❌ |
38
+ | Hyperlink pseudo-class | `:any-link` | ❌ |
39
+ | Link History pseudo-class | `:link` `:visited` | ❌ |
40
+ | Local link pseudo-class | `:local-link` | ❌ |
41
+ | Target pseudo-class | `:target` | ❌ |
42
+ | Target container pseudo-class | `:target-within` | ❌ |
43
+ | Reference element pseudo-class | `:scope` | ✅ |
44
+ | Current-element pseudo-class | `:current` `:current(div)` | ❌ |
45
+ | Past pseudo-class | `:past` | ❌ |
46
+ | Future pseudo-class | `:future` | ❌ |
47
+ | Interactive pseudo-class | `:active` `:hover` `:focus` `:focus-within` `:focus-visible` | ❌ |
48
+ | Enable and disable pseudo-class | `:enable` `:disable` | ❌ |
49
+ | Mutability pseudo-class | `:read-write` `:read-only` | ❌ |
50
+ | Placeholder-shown pseudo-class | `:placeholder-shown` | ❌ |
51
+ | Default-option pseudo-class | `:default` | ❌ |
52
+ | Selected-option pseudo-class | `:checked` | ❌ |
53
+ | Indeterminate value pseudo-class | `:indeterminate` | ❌ |
54
+ | Validity pseudo-class | `:valid` `:invalid` | ❌ |
55
+ | Range pseudo-class | `:in-range` `:out-of-range` | ❌ |
56
+ | Optionality pseudo-class | `:required` `:optional` | ❌ |
57
+ | Empty-Value pseudo-class | `:blank` | ❌ |
58
+ | User-interaction pseudo-class | `:user-invalid` | ❌ |
59
+ | Root pseudo-class | `:root` | ✅ |
60
+ | Empty pseudo-class | `:empty` | ❌ |
61
+ | Nth-child pseudo-class | `:nth-child(2)` `:nth-last-child(2)` `:first-child` `:last-child` `:only-child` | ❌ |
62
+ | Nth-child pseudo-class (`of El` Syntax) | `:nth-child(2 of div)` `:nth-last-child(2 of div)` | ❌ |
63
+ | Nth-of-type pseudo-class | `:nth-of-type(2)` `:nth-last-of-type(2)` `:first-of-type` `:last-of-type` `:only-of-type` | ❌ |
64
+ | Nth-col pseudo-class | `:nth-col(2)` `:nth-last-col(2)` | ❌ |
65
+ | Pseudo elements | `::before` `::after` | ❌ |
66
+ | Descendant combinator | `div span` | ✅ |
67
+ | Child combinator | `div > span` (`:has(> span)`) | ✅ |
68
+ | Next-sibling combinator | `div + span` (`:has(+ span)`) | ✅ |
69
+ | Subsequent-sibling combinator | `div ~ span` (`:has(~ span)`) | ✅ |
70
+ | Column combinator | <code>div \|\| span</code> | ❌ |
71
+ | Multiple selectors | `div, span` | ✅ |
72
+
73
+ ## Extended Selector
74
+
75
+ The below is selectors that are extended by markuplint:
76
+
77
+ | Selector Type | Code Example |
78
+ | ---------------------- | ----------------- |
79
+ | ARIA pseudo-class | `:aria(has name)` |
80
+ | ARIA Role pseudo-class | `:role(heading)` |
81
+
82
+ ### ARIA pseudo-class
83
+
84
+ ```
85
+ :aria(syntax)
86
+ ```
87
+
88
+ | Syntax | Example | Description |
89
+ | ------------- | -------------------- | ---------------------------------------------- |
90
+ | `has name` | `:aria(has name)` | Matches the element has accessible name |
91
+ | `has no name` | `:aria(has no name)` | Matches the element has **no** accessible name |
92
+
93
+ ### ARIA Role pseudo-class
94
+
95
+ ```
96
+ :role(roleName)
97
+ :role(roleName|version)
98
+ ```
99
+
100
+ For example, `:role(button)` matches `<button>` and `<div role="button">`.
101
+
102
+ You can specify the version of WAI-ARIA by separating the pipe like `:role(form|1.1)`.
103
+
104
+ ## Regex Selector
105
+
106
+ ```json
107
+ {
108
+ "nodeName": "/^[a-z]+$/",
109
+ "attrName": "/^[a-z]+$/",
110
+ "attrValue": "/^[a-z]+$/"
111
+ }
112
+ ```
@@ -0,0 +1,2 @@
1
+ import type { Specificity } from './types';
2
+ export declare function compareSpecificity(a: Specificity, b: Specificity): 0 | 1 | -1;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compareSpecificity = void 0;
4
+ function compareSpecificity(a, b) {
5
+ if (a[0] < b[0]) {
6
+ return -1;
7
+ }
8
+ else if (a[0] > b[0]) {
9
+ return 1;
10
+ }
11
+ else if (a[1] < b[1]) {
12
+ return -1;
13
+ }
14
+ else if (a[1] > b[1]) {
15
+ return 1;
16
+ }
17
+ else if (a[2] < b[2]) {
18
+ return -1;
19
+ }
20
+ else if (a[2] > b[2]) {
21
+ return 1;
22
+ }
23
+ return 0;
24
+ }
25
+ exports.compareSpecificity = compareSpecificity;
@@ -0,0 +1,3 @@
1
+ import type { MLMLSpec } from '@markuplint/ml-spec';
2
+ import { Selector } from './selector';
3
+ export declare function createSelector(selector: string, specs: MLMLSpec): Selector;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSelector = void 0;
4
+ const aria_pseudo_class_1 = require("./extended-selector/aria-pseudo-class");
5
+ const aria_role_pseudo_class_1 = require("./extended-selector/aria-role-pseudo-class");
6
+ const selector_1 = require("./selector");
7
+ const caches = new Map();
8
+ function createSelector(selector, specs) {
9
+ let instance = caches.get(selector);
10
+ if (instance) {
11
+ return instance;
12
+ }
13
+ instance = new selector_1.Selector(selector, {
14
+ aria: (0, aria_pseudo_class_1.ariaPseudoClass)(),
15
+ role: (0, aria_role_pseudo_class_1.ariaRolePseudoClass)(specs),
16
+ });
17
+ caches.set(selector, instance);
18
+ return instance;
19
+ }
20
+ exports.createSelector = createSelector;
package/lib/debug.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import debug from 'debug';
2
+ export declare const log: debug.Debugger;
3
+ export declare function enableDebug(): void;
package/lib/debug.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enableDebug = exports.log = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const debug_1 = tslib_1.__importDefault(require("debug"));
6
+ const CLI_NS = 'markuplint-cli';
7
+ exports.log = (0, debug_1.default)('selector');
8
+ function enableDebug() {
9
+ if (!exports.log.enabled) {
10
+ debug_1.default.enable(`${exports.log.namespace}*`);
11
+ (0, exports.log)(`Debug enable: ${exports.log.namespace}`);
12
+ if (!debug_1.default.enabled(CLI_NS)) {
13
+ debug_1.default.enable(`${exports.log.namespace}*,${CLI_NS}*`);
14
+ (0, exports.log)(`Debug enable: ${exports.log.namespace}, ${CLI_NS}`);
15
+ }
16
+ }
17
+ }
18
+ exports.enableDebug = enableDebug;
@@ -0,0 +1,5 @@
1
+ import type { SelectorResult } from '../types';
2
+ /**
3
+ * Version Syntax is not support yet.
4
+ */
5
+ export declare function ariaPseudoClass(): (content: string) => (el: Element) => SelectorResult;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ariaPseudoClass = void 0;
4
+ const ml_spec_1 = require("@markuplint/ml-spec");
5
+ /**
6
+ * Version Syntax is not support yet.
7
+ */
8
+ function ariaPseudoClass() {
9
+ return (content) => (el) => {
10
+ const aria = ariaPseudoClassParser(content);
11
+ switch (aria.type) {
12
+ case 'hasName': {
13
+ const name = (0, ml_spec_1.getAccname)(el);
14
+ return {
15
+ specificity: [0, 1, 0],
16
+ matched: !!name,
17
+ };
18
+ }
19
+ case 'hasNoName': {
20
+ const name = (0, ml_spec_1.getAccname)(el);
21
+ return {
22
+ specificity: [0, 1, 0],
23
+ matched: !name,
24
+ };
25
+ }
26
+ }
27
+ };
28
+ }
29
+ exports.ariaPseudoClass = ariaPseudoClass;
30
+ function ariaPseudoClassParser(syntax) {
31
+ const [_query, _version] = syntax.split('|');
32
+ const query = _query.replace(/\s+/g, '').toLowerCase();
33
+ const version = _version === '1.1' ? '1.1' : '1.2';
34
+ switch (query) {
35
+ case 'hasname': {
36
+ return {
37
+ type: 'hasName',
38
+ version,
39
+ };
40
+ }
41
+ case 'hasnoname': {
42
+ return {
43
+ type: 'hasNoName',
44
+ version,
45
+ };
46
+ }
47
+ }
48
+ throw new SyntaxError(`Unsupported syntax: ${syntax}`);
49
+ }
@@ -0,0 +1,3 @@
1
+ import type { SelectorResult } from '../types';
2
+ import type { MLMLSpec } from '@markuplint/ml-spec';
3
+ export declare function ariaRolePseudoClass(specs: MLMLSpec): (content: string) => (el: Element) => SelectorResult;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ariaRolePseudoClass = void 0;
4
+ const ml_spec_1 = require("@markuplint/ml-spec");
5
+ function ariaRolePseudoClass(specs) {
6
+ return (content) => (el) => {
7
+ var _a, _b;
8
+ const aria = ariaPseudoClassParser(content);
9
+ const computed = (0, ml_spec_1.getComputedRole)(specs, el, (_a = aria.version) !== null && _a !== void 0 ? _a : '1.2');
10
+ return {
11
+ specificity: [0, 1, 0],
12
+ matched: ((_b = computed.role) === null || _b === void 0 ? void 0 : _b.name) === aria.role,
13
+ };
14
+ };
15
+ }
16
+ exports.ariaRolePseudoClass = ariaRolePseudoClass;
17
+ function ariaPseudoClassParser(syntax) {
18
+ const [roleName, _version] = syntax.split('|');
19
+ const version = _version === '1.1' ? '1.1' : '1.2';
20
+ return {
21
+ role: roleName.trim().toLowerCase(),
22
+ version,
23
+ };
24
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { compareSpecificity } from './compare-specificity';
2
+ export { matchSelector } from './match-selector';
3
+ export { createSelector } from './create-selector';
4
+ export * from './types';
package/lib/index.js ADDED
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSelector = exports.matchSelector = exports.compareSpecificity = void 0;
4
+ const tslib_1 = require("tslib");
5
+ var compare_specificity_1 = require("./compare-specificity");
6
+ Object.defineProperty(exports, "compareSpecificity", { enumerable: true, get: function () { return compare_specificity_1.compareSpecificity; } });
7
+ var match_selector_1 = require("./match-selector");
8
+ Object.defineProperty(exports, "matchSelector", { enumerable: true, get: function () { return match_selector_1.matchSelector; } });
9
+ var create_selector_1 = require("./create-selector");
10
+ Object.defineProperty(exports, "createSelector", { enumerable: true, get: function () { return create_selector_1.createSelector; } });
11
+ tslib_1.__exportStar(require("./types"), exports);
@@ -0,0 +1,3 @@
1
+ export declare class InvalidSelectorError extends Error {
2
+ name: string;
3
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InvalidSelectorError = void 0;
4
+ class InvalidSelectorError extends Error {
5
+ constructor() {
6
+ super(...arguments);
7
+ this.name = 'InvalidSelectorError';
8
+ }
9
+ }
10
+ exports.InvalidSelectorError = InvalidSelectorError;
@@ -0,0 +1 @@
1
+ export declare function isPureHTMLElement(el: Element): boolean;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isPureHTMLElement = void 0;
4
+ function isPureHTMLElement(el) {
5
+ return el.localName !== el.nodeName;
6
+ }
7
+ exports.isPureHTMLElement = isPureHTMLElement;
package/lib/is.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare function isElement(node: Node): node is Element;
2
+ export declare function isNonDocumentTypeChildNode(node: Node): node is Element | CharacterData;
3
+ export declare function isPureHTMLElement(el: Element): boolean;
package/lib/is.js ADDED
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isPureHTMLElement = exports.isNonDocumentTypeChildNode = exports.isElement = void 0;
4
+ function isElement(node) {
5
+ return node.nodeType === node.ELEMENT_NODE;
6
+ }
7
+ exports.isElement = isElement;
8
+ function isNonDocumentTypeChildNode(node) {
9
+ return 'previousElementSibling' in node && 'nextElementSibling' in node;
10
+ }
11
+ exports.isNonDocumentTypeChildNode = isNonDocumentTypeChildNode;
12
+ function isPureHTMLElement(el) {
13
+ return el.localName !== el.nodeName;
14
+ }
15
+ exports.isPureHTMLElement = isPureHTMLElement;
@@ -0,0 +1,13 @@
1
+ import type { Specificity, RegexSelector } from './types';
2
+ export declare type SelectorMatches = SelectorMatched | SelectorUnmatched;
3
+ declare type SelectorMatched = {
4
+ matched: true;
5
+ selector: string;
6
+ specificity: Specificity;
7
+ data?: Record<string, string>;
8
+ };
9
+ declare type SelectorUnmatched = {
10
+ matched: false;
11
+ };
12
+ export declare function matchSelector(el: Node, selector: string | RegexSelector | undefined): SelectorMatches;
13
+ export {};
@@ -0,0 +1,238 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.matchSelector = void 0;
4
+ const is_1 = require("./is");
5
+ const regex_selector_matches_1 = require("./regex-selector-matches");
6
+ const selector_1 = require("./selector");
7
+ function matchSelector(el, selector) {
8
+ if (!selector) {
9
+ return {
10
+ matched: false,
11
+ };
12
+ }
13
+ if (typeof selector === 'string') {
14
+ const sel = new selector_1.Selector(selector);
15
+ const specificity = sel.match(el);
16
+ if (specificity) {
17
+ return {
18
+ matched: true,
19
+ selector,
20
+ specificity,
21
+ };
22
+ }
23
+ return {
24
+ matched: false,
25
+ };
26
+ }
27
+ return regexSelect(el, selector);
28
+ }
29
+ exports.matchSelector = matchSelector;
30
+ function regexSelect(el, selector) {
31
+ let edge = new SelectorTarget(selector);
32
+ let edgeSelector = selector.combination;
33
+ while (edgeSelector) {
34
+ const child = new SelectorTarget(edgeSelector);
35
+ child.from(edge, edgeSelector.combinator);
36
+ edge = child;
37
+ edgeSelector = edgeSelector.combination;
38
+ }
39
+ return edge.match(el);
40
+ }
41
+ class SelectorTarget {
42
+ constructor(selector) {
43
+ this._combinatedFrom = null;
44
+ this._selector = selector;
45
+ }
46
+ from(target, combinator) {
47
+ this._combinatedFrom = { target, combinator };
48
+ }
49
+ match(el) {
50
+ const unitCheck = this._matchWithoutCombinateChecking(el);
51
+ if (!unitCheck.matched) {
52
+ return unitCheck;
53
+ }
54
+ if (!this._combinatedFrom) {
55
+ return unitCheck;
56
+ }
57
+ if (!(0, is_1.isNonDocumentTypeChildNode)(el)) {
58
+ return unitCheck;
59
+ }
60
+ const { target, combinator } = this._combinatedFrom;
61
+ switch (combinator) {
62
+ // Descendant combinator
63
+ case ' ': {
64
+ let ancestor = el.parentElement;
65
+ while (ancestor) {
66
+ const matches = target.match(ancestor);
67
+ if (matches.matched) {
68
+ return mergeMatches(matches, unitCheck, ' ');
69
+ }
70
+ ancestor = ancestor.parentElement;
71
+ }
72
+ return {
73
+ matched: false,
74
+ };
75
+ }
76
+ // Child combinator
77
+ case '>': {
78
+ const parentNode = el.parentElement;
79
+ if (!parentNode) {
80
+ return { matched: false };
81
+ }
82
+ const matches = target.match(parentNode);
83
+ if (matches.matched) {
84
+ return mergeMatches(matches, unitCheck, ' > ');
85
+ }
86
+ return { matched: false };
87
+ }
88
+ // Next-sibling combinator
89
+ case '+': {
90
+ if (!el.previousElementSibling) {
91
+ return { matched: false };
92
+ }
93
+ const matches = target.match(el.previousElementSibling);
94
+ if (matches.matched) {
95
+ return mergeMatches(matches, unitCheck, ' + ');
96
+ }
97
+ return { matched: false };
98
+ }
99
+ // Subsequent-sibling combinator
100
+ case '~': {
101
+ let prev = el.previousElementSibling;
102
+ while (prev) {
103
+ const matches = target.match(prev);
104
+ if (matches.matched) {
105
+ return mergeMatches(matches, unitCheck, ' ~ ');
106
+ }
107
+ prev = prev.previousElementSibling;
108
+ }
109
+ return { matched: false };
110
+ }
111
+ // Prev-sibling combinator
112
+ case ':has(+)': {
113
+ if (!el.nextElementSibling) {
114
+ return { matched: false };
115
+ }
116
+ const matches = target.match(el.nextElementSibling);
117
+ if (matches.matched) {
118
+ return mergeMatches(matches, unitCheck, ':has(+ ', true);
119
+ }
120
+ return { matched: false };
121
+ }
122
+ // Subsequent-sibling (in front) combinator
123
+ case ':has(~)': {
124
+ let next = el.nextElementSibling;
125
+ while (next) {
126
+ const matches = target.match(next);
127
+ if (matches.matched) {
128
+ return mergeMatches(matches, unitCheck, ':has(~ ', true);
129
+ }
130
+ next = next.nextElementSibling;
131
+ }
132
+ return { matched: false };
133
+ }
134
+ default: {
135
+ throw new Error(`Unsupported ${this._combinatedFrom.combinator} combinator in selector`);
136
+ }
137
+ }
138
+ }
139
+ _matchWithoutCombinateChecking(el) {
140
+ return uncombinatedRegexSelect(el, this._selector);
141
+ }
142
+ }
143
+ function uncombinatedRegexSelect(el, selector) {
144
+ if (!(0, is_1.isElement)(el)) {
145
+ return {
146
+ matched: false,
147
+ };
148
+ }
149
+ let matched = true;
150
+ let data = {};
151
+ let tagSelector = '';
152
+ const specificity = [0, 0, 0];
153
+ const specifiedAttr = new Map();
154
+ if (selector.nodeName) {
155
+ const matchedNodeName = (0, regex_selector_matches_1.regexSelectorMatches)(selector.nodeName, el.localName, (0, is_1.isPureHTMLElement)(el));
156
+ if (matchedNodeName) {
157
+ delete matchedNodeName.$0;
158
+ }
159
+ else {
160
+ matched = false;
161
+ }
162
+ data = {
163
+ ...data,
164
+ ...matchedNodeName,
165
+ };
166
+ tagSelector = el.localName;
167
+ specificity[2] = 1;
168
+ }
169
+ if (selector.attrName) {
170
+ const selectorAttrName = selector.attrName;
171
+ const matchedAttrNameList = Array.from(el.attributes).map(attr => {
172
+ const attrName = attr.name;
173
+ const matchedAttrName = (0, regex_selector_matches_1.regexSelectorMatches)(selectorAttrName, attrName, (0, is_1.isPureHTMLElement)(el));
174
+ if (matchedAttrName) {
175
+ delete matchedAttrName.$0;
176
+ data = {
177
+ ...data,
178
+ ...matchedAttrName,
179
+ };
180
+ specifiedAttr.set(attrName, '');
181
+ }
182
+ return matchedAttrName;
183
+ });
184
+ if (!matchedAttrNameList.some(_ => !!_)) {
185
+ matched = false;
186
+ }
187
+ }
188
+ if (selector.attrValue) {
189
+ const selectorAttrValue = selector.attrValue;
190
+ const matchedAttrValueList = Array.from(el.attributes).map(attr => {
191
+ const attrName = attr.name;
192
+ const attrValue = attr.value;
193
+ const matchedAttrValue = (0, regex_selector_matches_1.regexSelectorMatches)(selectorAttrValue, attrValue, (0, is_1.isPureHTMLElement)(el));
194
+ if (matchedAttrValue) {
195
+ delete matchedAttrValue.$0;
196
+ data = {
197
+ ...data,
198
+ ...matchedAttrValue,
199
+ };
200
+ specifiedAttr.set(attrName, attrValue);
201
+ }
202
+ return matchedAttrValue;
203
+ });
204
+ if (!matchedAttrValueList.some(_ => !!_)) {
205
+ matched = false;
206
+ }
207
+ }
208
+ const attrSelector = Array.from(specifiedAttr.entries())
209
+ .map(([name, value]) => {
210
+ return `[${name}${value ? `="${value}"` : ''}]`;
211
+ })
212
+ .join('');
213
+ specificity[1] += specifiedAttr.size;
214
+ if (matched) {
215
+ return {
216
+ matched,
217
+ selector: `${tagSelector}${attrSelector}`,
218
+ specificity,
219
+ data,
220
+ };
221
+ }
222
+ return { matched };
223
+ }
224
+ function mergeMatches(a, b, sep, close = false) {
225
+ return {
226
+ matched: true,
227
+ selector: `${a.selector}${sep}${b.selector}${close ? ')' : ''}`,
228
+ specificity: [
229
+ a.specificity[0] + b.specificity[0],
230
+ a.specificity[1] + b.specificity[1],
231
+ a.specificity[2] + b.specificity[2],
232
+ ],
233
+ data: {
234
+ ...a.data,
235
+ ...b.data,
236
+ },
237
+ };
238
+ }
@@ -0,0 +1,3 @@
1
+ export declare function regexSelectorMatches(reg: string, raw: string, ignoreCase: boolean): {
2
+ [x: string]: string;
3
+ } | null;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.regexSelectorMatches = void 0;
4
+ function regexSelectorMatches(reg, raw, ignoreCase) {
5
+ const res = {};
6
+ const pattern = toRegxp(reg);
7
+ const regex = new RegExp(pattern instanceof RegExp ? pattern : `^${pattern.trim()}$`, ignoreCase ? 'i' : undefined);
8
+ const matched = regex.exec(raw);
9
+ if (!matched) {
10
+ return null;
11
+ }
12
+ matched.forEach((val, i) => (res[`$${i}`] = val));
13
+ return {
14
+ ...res,
15
+ ...matched.groups,
16
+ };
17
+ }
18
+ exports.regexSelectorMatches = regexSelectorMatches;
19
+ function toRegxp(pattern) {
20
+ const matched = pattern.match(/^\/(.+)\/([ig]*)$/i);
21
+ if (matched) {
22
+ return new RegExp(matched[1], matched[2]);
23
+ }
24
+ return pattern;
25
+ }