@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 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 | Code Example |
78
- | ----------------- | ----------------- |
79
- | ARIA pseudo-class | `:aria(has name)` |
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 | Example | Description |
89
- | -------------- | ------------------------------------------------------- | ------------------------------- |
90
- | `has name` | `:aria(has name)`<br>`:aria(has name/1.1)` | It has accessible name |
91
- | `has no name` | `:aria(has no name)`<br>`:aria(has no name/1.1)` | It does'nt have accessible name |
92
- | `role is Role` | `:aria(role is banner)`<br>`:aria(role is generic/1.2)` | It matches the specfied role |
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
 
@@ -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
- aria: (0, aria_pseudo_class_1.ariaPseudoClass)(specs),
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
- import type { MLMLSpec } from '@markuplint/ml-spec';
3
- export declare function ariaPseudoClass(specs: MLMLSpec): (content: string) => (el: Element) => SelectorResult;
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
- const roleIsRegxp = /^roleis/gi;
6
- function ariaPseudoClass(specs) {
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
- const name = (0, ml_spec_1.getAccname)(el);
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: !!name,
24
+ matched: false,
16
25
  };
17
26
  }
18
27
  case 'hasNoName': {
19
- const name = (0, ml_spec_1.getAccname)(el);
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: !name,
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,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,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,3 @@
1
+ import type { SelectorResult } from '../types';
2
+ import type { MLMLSpec } from '@markuplint/ml-spec';
3
+ export declare function contentModelPseudoClass(specs: MLMLSpec): (category: string) => (el: Element) => SelectorResult;
@@ -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;
@@ -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._combinatedFrom = null;
44
- this._selector = selector;
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._combinatedFrom = { target, combinator };
50
+ tslib_1.__classPrivateFieldSet(this, _SelectorTarget_combinedFrom, { target, combinator }, "f");
48
51
  }
49
52
  match(el) {
50
- const unitCheck = this._matchWithoutCombinateChecking(el);
53
+ const unitCheck = this._matchWithoutCombineChecking(el);
51
54
  if (!unitCheck.matched) {
52
55
  return unitCheck;
53
56
  }
54
- if (!this._combinatedFrom) {
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._combinatedFrom;
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._combinatedFrom.combinator} combinator in selector`);
138
+ throw new Error(`Unsupported ${tslib_1.__classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f").combinator} combinator in selector`);
136
139
  }
137
140
  }
138
141
  }
139
- _matchWithoutCombinateChecking(el) {
140
- return uncombinatedRegexSelect(el, this._selector);
142
+ _matchWithoutCombineChecking(el) {
143
+ return uncombinedRegexSelect(el, tslib_1.__classPrivateFieldGet(this, _SelectorTarget_selector, "f"));
141
144
  }
142
145
  }
143
- function uncombinatedRegexSelect(el, selector) {
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 = toRegxp(reg);
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 toRegxp(pattern) {
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
@@ -4,5 +4,6 @@ export declare class Selector {
4
4
  #private;
5
5
  constructor(selector: string, extended?: ExtendedPseudoClass);
6
6
  match(el: Node, scope?: ParentNode | null): Specificity | false;
7
+ search(el: Node, scope?: ParentNode | null): SelectorResult[];
7
8
  }
8
9
  export {};