@markuplint/selector 3.0.0-alpha.2105 → 3.0.0-alpha.4
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/README.md +29 -9
- package/lib/create-selector.js +5 -1
- package/lib/extended-selector/aria-pseudo-class.d.ts +4 -2
- package/lib/extended-selector/aria-pseudo-class.js +23 -25
- package/lib/extended-selector/aria-role-pseudo-class.d.ts +3 -0
- package/lib/extended-selector/aria-role-pseudo-class.js +32 -0
- package/lib/extended-selector/content-model-pseudo-class.d.ts +3 -0
- package/lib/extended-selector/content-model-pseudo-class.js +57 -0
- package/lib/match-selector.js +14 -10
- package/lib/regex-selector-matches.js +2 -2
- package/lib/selector.d.ts +1 -0
- package/lib/selector.js +211 -63
- package/lib/types.d.ts +12 -4
- package/package.json +5 -5
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/is-pure-html-element.d.ts +0 -1
- package/lib/is-pure-html-element.js +0 -7
- package/lib/utils/is-pure-html-element.d.ts +0 -1
- package/lib/utils/is-pure-html-element.js +0 -7
package/README.md
CHANGED
|
@@ -74,22 +74,42 @@ Supported selectors and operators:
|
|
|
74
74
|
|
|
75
75
|
The below is selectors that are extended by markuplint:
|
|
76
76
|
|
|
77
|
-
| Selector Type
|
|
78
|
-
|
|
|
79
|
-
| ARIA pseudo-class
|
|
77
|
+
| Selector Type | Code Example |
|
|
78
|
+
| -------------------------- | --------------------- |
|
|
79
|
+
| ARIA pseudo-class | `:aria(has name)` |
|
|
80
|
+
| ARIA Role pseudo-class | `:role(heading)` |
|
|
81
|
+
| Content Model pseudo-class | `:model(interactive)` |
|
|
80
82
|
|
|
81
83
|
### ARIA pseudo-class
|
|
82
84
|
|
|
83
85
|
```
|
|
84
86
|
:aria(syntax)
|
|
85
|
-
:aria(syntax/version)
|
|
86
87
|
```
|
|
87
88
|
|
|
88
|
-
| Syntax
|
|
89
|
-
|
|
|
90
|
-
| `has name`
|
|
91
|
-
| `has no name`
|
|
92
|
-
|
|
89
|
+
| Syntax | Example | Description |
|
|
90
|
+
| ------------- | -------------------- | ---------------------------------------------- |
|
|
91
|
+
| `has name` | `:aria(has name)` | Matches the element has accessible name |
|
|
92
|
+
| `has no name` | `:aria(has no name)` | Matches the element has **no** accessible name |
|
|
93
|
+
|
|
94
|
+
### ARIA Role pseudo-class
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
:role(roleName)
|
|
98
|
+
:role(roleName|version)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
For example, `:role(button)` matches `<button>` and `<div role="button">`.
|
|
102
|
+
|
|
103
|
+
You can specify the version of WAI-ARIA by separating the pipe like `:role(form|1.1)`.
|
|
104
|
+
|
|
105
|
+
### Content Model pseudo-class
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
:model(interactive)
|
|
109
|
+
:model(palpable)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
For example, `:role(interactive)` matches `<a>`(with `href` attr), `<button>`, and so on.
|
|
93
113
|
|
|
94
114
|
## Regex Selector
|
|
95
115
|
|
package/lib/create-selector.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createSelector = void 0;
|
|
4
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 content_model_pseudo_class_1 = require("./extended-selector/content-model-pseudo-class");
|
|
5
7
|
const selector_1 = require("./selector");
|
|
6
8
|
const caches = new Map();
|
|
7
9
|
function createSelector(selector, specs) {
|
|
@@ -10,7 +12,9 @@ function createSelector(selector, specs) {
|
|
|
10
12
|
return instance;
|
|
11
13
|
}
|
|
12
14
|
instance = new selector_1.Selector(selector, {
|
|
13
|
-
|
|
15
|
+
model: (0, content_model_pseudo_class_1.contentModelPseudoClass)(specs),
|
|
16
|
+
aria: (0, aria_pseudo_class_1.ariaPseudoClass)(),
|
|
17
|
+
role: (0, aria_role_pseudo_class_1.ariaRolePseudoClass)(specs),
|
|
14
18
|
});
|
|
15
19
|
caches.set(selector, instance);
|
|
16
20
|
return instance;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { SelectorResult } from '../types';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Version Syntax is not support yet.
|
|
4
|
+
*/
|
|
5
|
+
export declare function ariaPseudoClass(): (content: string) => (el: Element) => SelectorResult;
|
|
@@ -2,31 +2,40 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ariaPseudoClass = void 0;
|
|
4
4
|
const ml_spec_1 = require("@markuplint/ml-spec");
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Version Syntax is not support yet.
|
|
7
|
+
*/
|
|
8
|
+
function ariaPseudoClass() {
|
|
7
9
|
return (content) => (el) => {
|
|
8
|
-
var _a, _b;
|
|
9
10
|
const aria = ariaPseudoClassParser(content);
|
|
11
|
+
const name = (0, ml_spec_1.getAccname)(el);
|
|
10
12
|
switch (aria.type) {
|
|
11
13
|
case 'hasName': {
|
|
12
|
-
|
|
14
|
+
if (name) {
|
|
15
|
+
return {
|
|
16
|
+
specificity: [0, 1, 0],
|
|
17
|
+
matched: true,
|
|
18
|
+
nodes: [el],
|
|
19
|
+
has: [],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
13
22
|
return {
|
|
14
23
|
specificity: [0, 1, 0],
|
|
15
|
-
matched:
|
|
24
|
+
matched: false,
|
|
16
25
|
};
|
|
17
26
|
}
|
|
18
27
|
case 'hasNoName': {
|
|
19
|
-
|
|
28
|
+
if (!name) {
|
|
29
|
+
return {
|
|
30
|
+
specificity: [0, 1, 0],
|
|
31
|
+
matched: true,
|
|
32
|
+
nodes: [el],
|
|
33
|
+
has: [],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
20
36
|
return {
|
|
21
37
|
specificity: [0, 1, 0],
|
|
22
|
-
matched:
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
case 'roleIs': {
|
|
26
|
-
const computed = (0, ml_spec_1.getComputedRole)(specs, el, (_a = aria.version) !== null && _a !== void 0 ? _a : '1.2');
|
|
27
|
-
return {
|
|
28
|
-
specificity: [0, 1, 0],
|
|
29
|
-
matched: ((_b = computed.role) === null || _b === void 0 ? void 0 : _b.name) === aria.role,
|
|
38
|
+
matched: false,
|
|
30
39
|
};
|
|
31
40
|
}
|
|
32
41
|
}
|
|
@@ -51,16 +60,5 @@ function ariaPseudoClassParser(syntax) {
|
|
|
51
60
|
};
|
|
52
61
|
}
|
|
53
62
|
}
|
|
54
|
-
if (roleIsRegxp.test(query)) {
|
|
55
|
-
const role = query.replace(roleIsRegxp, '');
|
|
56
|
-
if (!role) {
|
|
57
|
-
throw new SyntaxError(`Unsupported syntax: ${syntax}`);
|
|
58
|
-
}
|
|
59
|
-
return {
|
|
60
|
-
type: 'roleIs',
|
|
61
|
-
role,
|
|
62
|
-
version,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
63
|
throw new SyntaxError(`Unsupported syntax: ${syntax}`);
|
|
66
64
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
if (((_b = computed.role) === null || _b === void 0 ? void 0 : _b.name) === aria.role) {
|
|
11
|
+
return {
|
|
12
|
+
specificity: [0, 1, 0],
|
|
13
|
+
matched: true,
|
|
14
|
+
nodes: [el],
|
|
15
|
+
has: [],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
specificity: [0, 1, 0],
|
|
20
|
+
matched: false,
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
exports.ariaRolePseudoClass = ariaRolePseudoClass;
|
|
25
|
+
function ariaPseudoClassParser(syntax) {
|
|
26
|
+
const [roleName, _version] = syntax.split('|');
|
|
27
|
+
const version = _version === '1.1' ? '1.1' : '1.2';
|
|
28
|
+
return {
|
|
29
|
+
role: roleName.trim().toLowerCase(),
|
|
30
|
+
version,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.contentModelPseudoClass = void 0;
|
|
4
|
+
const ml_spec_1 = require("@markuplint/ml-spec");
|
|
5
|
+
const create_selector_1 = require("../create-selector");
|
|
6
|
+
function contentModelPseudoClass(specs) {
|
|
7
|
+
return (category) => (el) => {
|
|
8
|
+
category = category.trim().toLowerCase();
|
|
9
|
+
const selectors = (0, ml_spec_1.contentModelCategoryToTagNames)(`#${category}`, specs.def);
|
|
10
|
+
const matched = selectors
|
|
11
|
+
.map(selector => {
|
|
12
|
+
if (selector === '#custom') {
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
if (el.isCustomElement) {
|
|
15
|
+
return [
|
|
16
|
+
{
|
|
17
|
+
specificity: [0, 1, 0],
|
|
18
|
+
matched: true,
|
|
19
|
+
nodes: [el],
|
|
20
|
+
has: [],
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
specificity: [0, 1, 0],
|
|
27
|
+
matched: false,
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
if (selector === '#text') {
|
|
32
|
+
return [
|
|
33
|
+
{
|
|
34
|
+
specificity: [0, 1, 0],
|
|
35
|
+
matched: false,
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
return (0, create_selector_1.createSelector)(selector, specs).search(el);
|
|
40
|
+
})
|
|
41
|
+
.flat()
|
|
42
|
+
.filter((m) => m.matched);
|
|
43
|
+
if (matched.length) {
|
|
44
|
+
return {
|
|
45
|
+
specificity: [0, 1, 0],
|
|
46
|
+
matched: true,
|
|
47
|
+
nodes: matched.map(m => (m.matched ? m.nodes : [])).flat(),
|
|
48
|
+
has: matched.map(m => (m.matched ? m.has : [])).flat(),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
specificity: [0, 1, 0],
|
|
53
|
+
matched: false,
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
exports.contentModelPseudoClass = contentModelPseudoClass;
|
package/lib/match-selector.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var _SelectorTarget_combinedFrom, _SelectorTarget_selector;
|
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
4
|
exports.matchSelector = void 0;
|
|
5
|
+
const tslib_1 = require("tslib");
|
|
4
6
|
const is_1 = require("./is");
|
|
5
7
|
const regex_selector_matches_1 = require("./regex-selector-matches");
|
|
6
8
|
const selector_1 = require("./selector");
|
|
@@ -40,24 +42,25 @@ function regexSelect(el, selector) {
|
|
|
40
42
|
}
|
|
41
43
|
class SelectorTarget {
|
|
42
44
|
constructor(selector) {
|
|
43
|
-
this
|
|
44
|
-
this
|
|
45
|
+
_SelectorTarget_combinedFrom.set(this, null);
|
|
46
|
+
_SelectorTarget_selector.set(this, void 0);
|
|
47
|
+
tslib_1.__classPrivateFieldSet(this, _SelectorTarget_selector, selector, "f");
|
|
45
48
|
}
|
|
46
49
|
from(target, combinator) {
|
|
47
|
-
this
|
|
50
|
+
tslib_1.__classPrivateFieldSet(this, _SelectorTarget_combinedFrom, { target, combinator }, "f");
|
|
48
51
|
}
|
|
49
52
|
match(el) {
|
|
50
|
-
const unitCheck = this.
|
|
53
|
+
const unitCheck = this._matchWithoutCombineChecking(el);
|
|
51
54
|
if (!unitCheck.matched) {
|
|
52
55
|
return unitCheck;
|
|
53
56
|
}
|
|
54
|
-
if (!this
|
|
57
|
+
if (!tslib_1.__classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f")) {
|
|
55
58
|
return unitCheck;
|
|
56
59
|
}
|
|
57
60
|
if (!(0, is_1.isNonDocumentTypeChildNode)(el)) {
|
|
58
61
|
return unitCheck;
|
|
59
62
|
}
|
|
60
|
-
const { target, combinator } = this
|
|
63
|
+
const { target, combinator } = tslib_1.__classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f");
|
|
61
64
|
switch (combinator) {
|
|
62
65
|
// Descendant combinator
|
|
63
66
|
case ' ': {
|
|
@@ -132,15 +135,16 @@ class SelectorTarget {
|
|
|
132
135
|
return { matched: false };
|
|
133
136
|
}
|
|
134
137
|
default: {
|
|
135
|
-
throw new Error(`Unsupported ${this.
|
|
138
|
+
throw new Error(`Unsupported ${tslib_1.__classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f").combinator} combinator in selector`);
|
|
136
139
|
}
|
|
137
140
|
}
|
|
138
141
|
}
|
|
139
|
-
|
|
140
|
-
return
|
|
142
|
+
_matchWithoutCombineChecking(el) {
|
|
143
|
+
return uncombinedRegexSelect(el, tslib_1.__classPrivateFieldGet(this, _SelectorTarget_selector, "f"));
|
|
141
144
|
}
|
|
142
145
|
}
|
|
143
|
-
|
|
146
|
+
_SelectorTarget_combinedFrom = new WeakMap(), _SelectorTarget_selector = new WeakMap();
|
|
147
|
+
function uncombinedRegexSelect(el, selector) {
|
|
144
148
|
if (!(0, is_1.isElement)(el)) {
|
|
145
149
|
return {
|
|
146
150
|
matched: false,
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.regexSelectorMatches = void 0;
|
|
4
4
|
function regexSelectorMatches(reg, raw, ignoreCase) {
|
|
5
5
|
const res = {};
|
|
6
|
-
const pattern =
|
|
6
|
+
const pattern = toRegexp(reg);
|
|
7
7
|
const regex = new RegExp(pattern instanceof RegExp ? pattern : `^${pattern.trim()}$`, ignoreCase ? 'i' : undefined);
|
|
8
8
|
const matched = regex.exec(raw);
|
|
9
9
|
if (!matched) {
|
|
@@ -16,7 +16,7 @@ function regexSelectorMatches(reg, raw, ignoreCase) {
|
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
exports.regexSelectorMatches = regexSelectorMatches;
|
|
19
|
-
function
|
|
19
|
+
function toRegexp(pattern) {
|
|
20
20
|
const matched = pattern.match(/^\/(.+)\/([ig]*)$/i);
|
|
21
21
|
if (matched) {
|
|
22
22
|
return new RegExp(matched[1], matched[2]);
|
package/lib/selector.d.ts
CHANGED