@markuplint/ml-core 2.0.0-dev.26 → 2.0.0-rc.5

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.
Files changed (36) hide show
  1. package/lib/convert-ruleset.d.ts +1 -1
  2. package/lib/convert-ruleset.js +1 -1
  3. package/lib/debug.js +8 -2
  4. package/lib/index.d.ts +1 -0
  5. package/lib/index.js +3 -1
  6. package/lib/ml-core.d.ts +2 -1
  7. package/lib/ml-core.js +25 -16
  8. package/lib/ml-dom/document.d.ts +5 -2
  9. package/lib/ml-dom/document.js +99 -51
  10. package/lib/ml-dom/helper/match-selector.d.ts +12 -1
  11. package/lib/ml-dom/helper/match-selector.js +108 -90
  12. package/lib/ml-dom/helper/selector.d.ts +5 -3
  13. package/lib/ml-dom/helper/selector.js +275 -79
  14. package/lib/ml-dom/helper/walkers.js +1 -1
  15. package/lib/ml-dom/rule-mapper.d.ts +16 -0
  16. package/lib/ml-dom/rule-mapper.js +57 -0
  17. package/lib/ml-dom/tokens/abstract-element.d.ts +1 -0
  18. package/lib/ml-dom/tokens/abstract-element.js +31 -6
  19. package/lib/ml-dom/tokens/attribute.d.ts +1 -0
  20. package/lib/ml-dom/tokens/attribute.js +7 -0
  21. package/lib/ml-dom/tokens/node.d.ts +8 -7
  22. package/lib/ml-dom/tokens/node.js +21 -13
  23. package/lib/ml-dom/tokens/preprocessor-specific-attribute.d.ts +1 -0
  24. package/lib/ml-dom/tokens/preprocessor-specific-attribute.js +3 -0
  25. package/lib/ml-dom/tokens/token.d.ts +0 -2
  26. package/lib/ml-dom/tokens/token.js +0 -1
  27. package/lib/ml-rule/ml-rule.d.ts +5 -2
  28. package/lib/ml-rule/ml-rule.js +19 -3
  29. package/lib/ml-rule/types.d.ts +3 -3
  30. package/lib/test/index.d.ts +8 -5
  31. package/lib/test/index.js +7 -7
  32. package/markuplint-recommended.json +150 -11
  33. package/package.json +7 -7
  34. package/tsconfig.tsbuildinfo +1 -1
  35. package/lib/create-rule.d.ts +0 -4
  36. package/lib/create-rule.js +0 -8
@@ -3,14 +3,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.matchSelector = void 0;
4
4
  const ml_config_1 = require("@markuplint/ml-config");
5
5
  const selector_1 = require("./selector");
6
- function matchSelector(el, selector) {
6
+ function matchSelector(el, selector, tagNameCaseSensitive) {
7
7
  if (!selector) {
8
- return null;
8
+ return {
9
+ matched: false,
10
+ };
9
11
  }
10
12
  if (typeof selector === 'string') {
11
- const sel = (0, selector_1.createSelector)(selector);
12
- const matched = sel.match(el);
13
- return matched ? { __node: selector } : null;
13
+ const sel = (0, selector_1.createSelector)(selector, tagNameCaseSensitive);
14
+ const specificity = sel.match(el);
15
+ if (specificity) {
16
+ return {
17
+ matched: true,
18
+ selector,
19
+ specificity,
20
+ };
21
+ }
22
+ return {
23
+ matched: false,
24
+ };
14
25
  }
15
26
  return regexSelect(el, selector);
16
27
  }
@@ -36,113 +47,86 @@ class SelectorTarget {
36
47
  };
37
48
  }
38
49
  match(el) {
39
- const matched = this._matchWithoutCombinateChecking(el);
40
- if (!matched) {
41
- return null;
50
+ const unitCheck = this._matchWithoutCombinateChecking(el);
51
+ if (!unitCheck.matched) {
52
+ return unitCheck;
42
53
  }
43
54
  if (!this._combinatedFrom) {
44
- return matched;
55
+ return unitCheck;
45
56
  }
46
57
  const { target, combinator } = this._combinatedFrom;
47
58
  switch (combinator) {
48
59
  // Descendant combinator
49
60
  case ' ': {
50
- let ancestor = el.parentNode;
61
+ let ancestor = el.getParentElement();
51
62
  while (ancestor) {
52
- const ancestorMatched = target.match(ancestor);
53
- if (ancestorMatched) {
54
- const res = {
55
- ...ancestorMatched,
56
- ...matched,
57
- __node: `${ancestorMatched.__node} ${matched.__node}`,
58
- };
59
- return res;
63
+ const matches = target.match(ancestor);
64
+ if (matches.matched) {
65
+ return mergeMatches(matches, unitCheck, ' ');
60
66
  }
61
- ancestor = ancestor.parentNode;
67
+ ancestor = ancestor.getParentElement();
62
68
  }
63
- return null;
69
+ return {
70
+ matched: false,
71
+ };
64
72
  }
65
73
  // Child combinator
66
74
  case '>': {
67
- if (!el.parentNode) {
68
- return null;
75
+ const parentNode = el.getParentElement();
76
+ if (!parentNode) {
77
+ return { matched: false };
69
78
  }
70
- const parentMatched = target.match(el.parentNode);
71
- if (parentMatched) {
72
- const res = {
73
- ...parentMatched,
74
- ...matched,
75
- __node: `${parentMatched.__node} > ${matched.__node}`,
76
- };
77
- return res;
79
+ const matches = target.match(parentNode);
80
+ if (matches.matched) {
81
+ return mergeMatches(matches, unitCheck, ' > ');
78
82
  }
79
- return null;
83
+ return { matched: false };
80
84
  }
81
85
  // Next-sibling combinator
82
86
  case '+': {
83
87
  if (!el.previousElementSibling) {
84
- return null;
88
+ return { matched: false };
85
89
  }
86
- const prevMatched = target.match(el.previousElementSibling);
87
- if (prevMatched) {
88
- const res = {
89
- ...prevMatched,
90
- ...matched,
91
- __node: `${prevMatched.__node} + ${matched.__node}`,
92
- };
93
- return res;
90
+ const matches = target.match(el.previousElementSibling);
91
+ if (matches.matched) {
92
+ return mergeMatches(matches, unitCheck, ' + ');
94
93
  }
95
- return null;
94
+ return { matched: false };
96
95
  }
97
96
  // Subsequent-sibling combinator
98
97
  case '~': {
99
98
  let prev = el.previousElementSibling;
100
99
  while (prev) {
101
- const prevMatched = target.match(prev);
102
- if (prevMatched) {
103
- const res = {
104
- ...prevMatched,
105
- ...matched,
106
- __node: `${prevMatched.__node} ~ ${matched.__node}`,
107
- };
108
- return res;
100
+ const matches = target.match(prev);
101
+ if (matches.matched) {
102
+ return mergeMatches(matches, unitCheck, ' ~ ');
109
103
  }
110
104
  prev = prev.previousElementSibling;
111
105
  }
112
- return null;
106
+ return { matched: false };
113
107
  }
114
108
  // Prev-sibling combinator
115
109
  case ':has(+)': {
116
110
  if (!el.nextElementSibling) {
117
- return null;
111
+ return { matched: false };
118
112
  }
119
- const nextMatched = target.match(el.nextElementSibling);
120
- if (nextMatched) {
121
- const res = {
122
- ...nextMatched,
123
- ...matched,
124
- __node: `${nextMatched.__node}:has(+ ${matched.__node})`,
125
- };
126
- return res;
113
+ const matches = target.match(el.nextElementSibling);
114
+ if (matches.matched) {
115
+ return mergeMatches(matches, unitCheck, ':has(+ ', true);
127
116
  }
128
- return null;
117
+ return { matched: false };
129
118
  }
130
119
  // Subsequent-sibling (in front) combinator
131
120
  case ':has(~)': {
132
121
  let next = el.nextElementSibling;
133
122
  while (next) {
134
- const nextMatched = target.match(next);
135
- if (nextMatched) {
136
- const res = {
137
- ...nextMatched,
138
- ...matched,
139
- __node: `${nextMatched.__node}:has(~ ${matched.__node})`,
140
- };
141
- return res;
123
+ const matches = target.match(next);
124
+ if (matches.matched) {
125
+ return mergeMatches(matches, unitCheck, ':has(~ ', true);
142
126
  }
143
127
  next = next.nextElementSibling;
144
128
  }
145
- return null;
129
+ return { matched: false };
146
130
  }
147
131
  default: {
148
132
  throw new Error(`Unsupported ${this._combinatedFrom.combinator} combinator in selector`);
@@ -157,59 +141,93 @@ class SelectorTarget {
157
141
  }
158
142
  }
159
143
  function uncombinatedRegexSelect(el, selector) {
160
- let matchedMap = {};
144
+ let matched = true;
145
+ let data = {};
146
+ let tagSelector = '';
147
+ const specificity = [0, 0, 0];
148
+ const specifiedAttr = new Map();
161
149
  if (selector.nodeName) {
162
150
  const matchedNodeName = (0, ml_config_1.regexSelectorMatches)(selector.nodeName, el.nodeName);
163
- if (!matchedNodeName) {
164
- return null;
151
+ if (matchedNodeName) {
152
+ delete matchedNodeName.$0;
165
153
  }
166
- delete matchedNodeName.$0;
167
- matchedMap = {
168
- ...matchedMap,
154
+ else {
155
+ matched = false;
156
+ }
157
+ data = {
158
+ ...data,
169
159
  ...matchedNodeName,
170
- __node: el.nodeName,
171
160
  };
161
+ tagSelector = el.nodeName;
162
+ specificity[2] = 1;
172
163
  }
173
164
  if (selector.attrName) {
174
165
  const selectorAttrName = selector.attrName;
175
166
  const matchedAttrNameList = el.attributes.map(attr => {
176
- const attrName = attr.getName().raw;
167
+ const attrName = attr.getName().potential;
177
168
  const matchedAttrName = (0, ml_config_1.regexSelectorMatches)(selectorAttrName, attrName);
178
169
  if (matchedAttrName) {
179
170
  delete matchedAttrName.$0;
180
- matchedMap = {
181
- ...matchedMap,
171
+ data = {
172
+ ...data,
182
173
  ...matchedAttrName,
183
- __node: matchedMap.__node ? `${matchedMap.__node}[${attrName}]` : `[${attrName}]`,
184
174
  };
175
+ specifiedAttr.set(attrName, '');
185
176
  }
186
177
  return matchedAttrName;
187
178
  });
188
179
  if (!matchedAttrNameList.some(_ => !!_)) {
189
- return null;
180
+ matched = false;
190
181
  }
191
182
  }
192
183
  if (selector.attrValue) {
193
184
  const selectorAttrValue = selector.attrValue;
194
185
  const matchedAttrValueList = el.attributes.map(attr => {
195
- const attrName = attr.getName().raw;
196
- const attrValue = attr.getValue().raw;
186
+ const attrName = attr.getName().potential;
187
+ const attrValue = attr.getValue().potential;
197
188
  const matchedAttrValue = (0, ml_config_1.regexSelectorMatches)(selectorAttrValue, attrValue);
198
189
  if (matchedAttrValue) {
199
190
  delete matchedAttrValue.$0;
200
- matchedMap = {
201
- ...matchedMap,
191
+ data = {
192
+ ...data,
202
193
  ...matchedAttrValue,
203
- __node: matchedMap.__node
204
- ? `${matchedMap.__node}[${attrName}="${attrValue}"]`
205
- : `[${attrName}="${attrValue}"]`,
206
194
  };
195
+ specifiedAttr.set(attrName, attrValue);
207
196
  }
208
197
  return matchedAttrValue;
209
198
  });
210
199
  if (!matchedAttrValueList.some(_ => !!_)) {
211
- return null;
200
+ matched = false;
212
201
  }
213
202
  }
214
- return matchedMap;
203
+ const attrSelector = Array.from(specifiedAttr.entries())
204
+ .map(([name, value]) => {
205
+ return `[${name}${value ? `="${value}"` : ''}]`;
206
+ })
207
+ .join('');
208
+ specificity[1] += specifiedAttr.size;
209
+ if (matched) {
210
+ return {
211
+ matched,
212
+ selector: `${tagSelector}${attrSelector}`,
213
+ specificity,
214
+ data,
215
+ };
216
+ }
217
+ return { matched };
218
+ }
219
+ function mergeMatches(a, b, sep, close = false) {
220
+ return {
221
+ matched: true,
222
+ selector: `${a.selector}${sep}${b.selector}${close ? ')' : ''}`,
223
+ specificity: [
224
+ a.specificity[0] + b.specificity[0],
225
+ a.specificity[1] + b.specificity[1],
226
+ a.specificity[2] + b.specificity[2],
227
+ ],
228
+ data: {
229
+ ...a.data,
230
+ ...b.data,
231
+ },
232
+ };
215
233
  }
@@ -1,9 +1,11 @@
1
1
  import type MLDOMAbstractElement from '../tokens/abstract-element';
2
- export declare function createSelector(selector: string): Selector;
2
+ export declare type Specificity = [number, number, number];
3
+ export declare function createSelector(selector: string, tagNameCaseSensitive?: boolean): Selector;
4
+ export declare function compareSpecificity(a: Specificity, b: Specificity): 0 | 1 | -1;
3
5
  declare type TargetElement = MLDOMAbstractElement<any, any>;
4
6
  declare class Selector {
5
7
  #private;
6
- constructor(selector: string);
7
- match(el: TargetElement, caller?: TargetElement | null): boolean;
8
+ constructor(selector: string, tagNameCaseSensitive: boolean);
9
+ match(el: TargetElement, caller?: TargetElement | null): Specificity | false;
8
10
  }
9
11
  export {};