@markuplint/ml-core 2.0.0-dev.20211213.0 → 2.0.0-dev.20220105.1
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/ml-core.d.ts +1 -1
- package/lib/ml-dom/document.js +20 -13
- package/lib/ml-dom/helper/match-selector.d.ts +12 -1
- package/lib/ml-dom/helper/match-selector.js +99 -82
- package/lib/ml-dom/helper/selector.d.ts +3 -1
- package/lib/ml-dom/helper/selector.js +232 -62
- package/lib/ml-dom/rule-mapper.d.ts +2 -1
- package/lib/ml-dom/rule-mapper.js +8 -8
- package/lib/ml-dom/tokens/abstract-element.js +1 -1
- package/markuplint-recommended.json +9 -9
- package/package.json +7 -7
- package/tsconfig.tsbuildinfo +1 -1
package/lib/ml-core.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export declare type MLCoreParams = {
|
|
|
10
10
|
export declare class MLCore {
|
|
11
11
|
#private;
|
|
12
12
|
constructor({ parser, sourceCode, ruleset, rules, locale, schemas, parserOptions, filename, debug }: MLCoreParams);
|
|
13
|
-
get document(): Document<RuleConfigValue, unknown
|
|
13
|
+
get document(): MLParseError | Document<RuleConfigValue, unknown>;
|
|
14
14
|
verify(fix?: boolean): Promise<Violation[]>;
|
|
15
15
|
setCode(sourceCode: string): void;
|
|
16
16
|
update({ parser, ruleset, rules, locale, schemas, parserOptions }: Partial<MLFabric>): void;
|
package/lib/ml-dom/document.js
CHANGED
|
@@ -138,7 +138,7 @@ class MLDOMDocument {
|
|
|
138
138
|
const rule = ruleset.rules[ruleName];
|
|
139
139
|
ruleMapper.set(node, ruleName, {
|
|
140
140
|
from: 'rules',
|
|
141
|
-
|
|
141
|
+
specificity: [0, 0, 0],
|
|
142
142
|
rule,
|
|
143
143
|
});
|
|
144
144
|
});
|
|
@@ -151,25 +151,32 @@ class MLDOMDocument {
|
|
|
151
151
|
if (!nodeRule.rules) {
|
|
152
152
|
return;
|
|
153
153
|
}
|
|
154
|
+
if (!selectorTarget) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
154
157
|
const selector = nodeRule.selector || nodeRule.regexSelector || nodeRule.tagName;
|
|
155
|
-
const
|
|
158
|
+
const matches =
|
|
156
159
|
/**
|
|
157
160
|
* Forward v1.x compatibility
|
|
158
161
|
*/
|
|
159
162
|
nodeRule.tagName && /^#text$/i.test(nodeRule.tagName) && node.type === 'Text'
|
|
160
|
-
? {
|
|
163
|
+
? {
|
|
164
|
+
matched: true,
|
|
165
|
+
selector: '#text',
|
|
166
|
+
specificity: [0, 0, 0],
|
|
167
|
+
}
|
|
161
168
|
: /**
|
|
162
169
|
* v2.0.0 or later
|
|
163
170
|
*/
|
|
164
|
-
|
|
165
|
-
if (!matched) {
|
|
171
|
+
(0, match_selector_1.matchSelector)(selectorTarget, selector);
|
|
172
|
+
if (!matches.matched) {
|
|
166
173
|
return;
|
|
167
174
|
}
|
|
168
|
-
docLog('Matched nodeRule: <%s>(%s)', 'nodeName' in node ? node.nodeName : node.type,
|
|
175
|
+
docLog('Matched nodeRule: <%s>(%s)', 'nodeName' in node ? node.nodeName : node.type, matches.selector || '*');
|
|
169
176
|
const ruleList = Object.keys(nodeRule.rules);
|
|
170
177
|
for (const ruleName of ruleList) {
|
|
171
178
|
const rule = nodeRule.rules[ruleName];
|
|
172
|
-
const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule,
|
|
179
|
+
const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule, matches.data || {});
|
|
173
180
|
if (convertedRule === undefined) {
|
|
174
181
|
continue;
|
|
175
182
|
}
|
|
@@ -178,7 +185,7 @@ class MLDOMDocument {
|
|
|
178
185
|
ruleLog('↑ nodeRule (%s): %O', ruleName, mergedRule);
|
|
179
186
|
ruleMapper.set(node, ruleName, {
|
|
180
187
|
from: 'nodeRules',
|
|
181
|
-
|
|
188
|
+
specificity: matches.specificity,
|
|
182
189
|
rule: mergedRule,
|
|
183
190
|
});
|
|
184
191
|
}
|
|
@@ -202,15 +209,15 @@ class MLDOMDocument {
|
|
|
202
209
|
if (!selector) {
|
|
203
210
|
return;
|
|
204
211
|
}
|
|
205
|
-
const
|
|
206
|
-
if (!matched) {
|
|
212
|
+
const matches = (0, match_selector_1.matchSelector)(selectorTarget, selector);
|
|
213
|
+
if (!matches.matched) {
|
|
207
214
|
return;
|
|
208
215
|
}
|
|
209
|
-
docLog('Matched childNodeRule: <%s>(%s), inheritance: %o', selectorTarget.nodeName,
|
|
216
|
+
docLog('Matched childNodeRule: <%s>(%s), inheritance: %o', selectorTarget.nodeName, matches.selector || '*', !!nodeRule.inheritance);
|
|
210
217
|
const targetDescendants = nodeRule.inheritance ? descendants : children;
|
|
211
218
|
Object.keys(nodeRuleRules).forEach(ruleName => {
|
|
212
219
|
const rule = nodeRuleRules[ruleName];
|
|
213
|
-
const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule,
|
|
220
|
+
const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule, matches.data || {});
|
|
214
221
|
if (convertedRule === undefined) {
|
|
215
222
|
return;
|
|
216
223
|
}
|
|
@@ -220,7 +227,7 @@ class MLDOMDocument {
|
|
|
220
227
|
targetDescendants.forEach(descendant => {
|
|
221
228
|
ruleMapper.set(descendant, ruleName, {
|
|
222
229
|
from: 'childNodeRules',
|
|
223
|
-
|
|
230
|
+
specificity: matches.specificity,
|
|
224
231
|
rule: mergedRule,
|
|
225
232
|
});
|
|
226
233
|
});
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import type MLDOMAbstractElement from '../tokens/abstract-element';
|
|
2
|
+
import type { Specificity } from './selector';
|
|
2
3
|
import type { RegexSelector } from '@markuplint/ml-config';
|
|
4
|
+
export declare type SelectorMatches = SelectorMatched | SelectorUnmatched;
|
|
5
|
+
declare type SelectorMatched = {
|
|
6
|
+
matched: true;
|
|
7
|
+
selector: string;
|
|
8
|
+
specificity: Specificity;
|
|
9
|
+
data?: Record<string, string>;
|
|
10
|
+
};
|
|
11
|
+
declare type SelectorUnmatched = {
|
|
12
|
+
matched: false;
|
|
13
|
+
};
|
|
3
14
|
declare type TargetElement = MLDOMAbstractElement<any, any>;
|
|
4
|
-
export declare function matchSelector(el: TargetElement, selector: string | RegexSelector | undefined):
|
|
15
|
+
export declare function matchSelector(el: TargetElement, selector: string | RegexSelector | undefined): SelectorMatches;
|
|
5
16
|
export {};
|
|
@@ -5,12 +5,23 @@ const ml_config_1 = require("@markuplint/ml-config");
|
|
|
5
5
|
const selector_1 = require("./selector");
|
|
6
6
|
function matchSelector(el, selector) {
|
|
7
7
|
if (!selector) {
|
|
8
|
-
return
|
|
8
|
+
return {
|
|
9
|
+
matched: false,
|
|
10
|
+
};
|
|
9
11
|
}
|
|
10
12
|
if (typeof selector === 'string') {
|
|
11
13
|
const sel = (0, selector_1.createSelector)(selector);
|
|
12
|
-
const
|
|
13
|
-
|
|
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,12 +47,12 @@ class SelectorTarget {
|
|
|
36
47
|
};
|
|
37
48
|
}
|
|
38
49
|
match(el) {
|
|
39
|
-
const
|
|
40
|
-
if (!matched) {
|
|
41
|
-
return
|
|
50
|
+
const unitCheck = this._matchWithoutCombinateChecking(el);
|
|
51
|
+
if (!unitCheck.matched) {
|
|
52
|
+
return unitCheck;
|
|
42
53
|
}
|
|
43
54
|
if (!this._combinatedFrom) {
|
|
44
|
-
return
|
|
55
|
+
return unitCheck;
|
|
45
56
|
}
|
|
46
57
|
const { target, combinator } = this._combinatedFrom;
|
|
47
58
|
switch (combinator) {
|
|
@@ -49,100 +60,72 @@ class SelectorTarget {
|
|
|
49
60
|
case ' ': {
|
|
50
61
|
let ancestor = el.parentNode;
|
|
51
62
|
while (ancestor) {
|
|
52
|
-
const
|
|
53
|
-
if (
|
|
54
|
-
|
|
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
67
|
ancestor = ancestor.parentNode;
|
|
62
68
|
}
|
|
63
|
-
return
|
|
69
|
+
return {
|
|
70
|
+
matched: false,
|
|
71
|
+
};
|
|
64
72
|
}
|
|
65
73
|
// Child combinator
|
|
66
74
|
case '>': {
|
|
67
75
|
if (!el.parentNode) {
|
|
68
|
-
return
|
|
76
|
+
return { matched: false };
|
|
69
77
|
}
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
...parentMatched,
|
|
74
|
-
...matched,
|
|
75
|
-
__node: `${parentMatched.__node} > ${matched.__node}`,
|
|
76
|
-
};
|
|
77
|
-
return res;
|
|
78
|
+
const matches = target.match(el.parentNode);
|
|
79
|
+
if (matches.matched) {
|
|
80
|
+
return mergeMatches(matches, unitCheck, ' > ');
|
|
78
81
|
}
|
|
79
|
-
return
|
|
82
|
+
return { matched: false };
|
|
80
83
|
}
|
|
81
84
|
// Next-sibling combinator
|
|
82
85
|
case '+': {
|
|
83
86
|
if (!el.previousElementSibling) {
|
|
84
|
-
return
|
|
87
|
+
return { matched: false };
|
|
85
88
|
}
|
|
86
|
-
const
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
...prevMatched,
|
|
90
|
-
...matched,
|
|
91
|
-
__node: `${prevMatched.__node} + ${matched.__node}`,
|
|
92
|
-
};
|
|
93
|
-
return res;
|
|
89
|
+
const matches = target.match(el.previousElementSibling);
|
|
90
|
+
if (matches.matched) {
|
|
91
|
+
return mergeMatches(matches, unitCheck, ' + ');
|
|
94
92
|
}
|
|
95
|
-
return
|
|
93
|
+
return { matched: false };
|
|
96
94
|
}
|
|
97
95
|
// Subsequent-sibling combinator
|
|
98
96
|
case '~': {
|
|
99
97
|
let prev = el.previousElementSibling;
|
|
100
98
|
while (prev) {
|
|
101
|
-
const
|
|
102
|
-
if (
|
|
103
|
-
|
|
104
|
-
...prevMatched,
|
|
105
|
-
...matched,
|
|
106
|
-
__node: `${prevMatched.__node} ~ ${matched.__node}`,
|
|
107
|
-
};
|
|
108
|
-
return res;
|
|
99
|
+
const matches = target.match(prev);
|
|
100
|
+
if (matches.matched) {
|
|
101
|
+
return mergeMatches(matches, unitCheck, ' ~ ');
|
|
109
102
|
}
|
|
110
103
|
prev = prev.previousElementSibling;
|
|
111
104
|
}
|
|
112
|
-
return
|
|
105
|
+
return { matched: false };
|
|
113
106
|
}
|
|
114
107
|
// Prev-sibling combinator
|
|
115
108
|
case ':has(+)': {
|
|
116
109
|
if (!el.nextElementSibling) {
|
|
117
|
-
return
|
|
110
|
+
return { matched: false };
|
|
118
111
|
}
|
|
119
|
-
const
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
...nextMatched,
|
|
123
|
-
...matched,
|
|
124
|
-
__node: `${nextMatched.__node}:has(+ ${matched.__node})`,
|
|
125
|
-
};
|
|
126
|
-
return res;
|
|
112
|
+
const matches = target.match(el.nextElementSibling);
|
|
113
|
+
if (matches.matched) {
|
|
114
|
+
return mergeMatches(matches, unitCheck, ':has(+ ', true);
|
|
127
115
|
}
|
|
128
|
-
return
|
|
116
|
+
return { matched: false };
|
|
129
117
|
}
|
|
130
118
|
// Subsequent-sibling (in front) combinator
|
|
131
119
|
case ':has(~)': {
|
|
132
120
|
let next = el.nextElementSibling;
|
|
133
121
|
while (next) {
|
|
134
|
-
const
|
|
135
|
-
if (
|
|
136
|
-
|
|
137
|
-
...nextMatched,
|
|
138
|
-
...matched,
|
|
139
|
-
__node: `${nextMatched.__node}:has(~ ${matched.__node})`,
|
|
140
|
-
};
|
|
141
|
-
return res;
|
|
122
|
+
const matches = target.match(next);
|
|
123
|
+
if (matches.matched) {
|
|
124
|
+
return mergeMatches(matches, unitCheck, ':has(~ ', true);
|
|
142
125
|
}
|
|
143
126
|
next = next.nextElementSibling;
|
|
144
127
|
}
|
|
145
|
-
return
|
|
128
|
+
return { matched: false };
|
|
146
129
|
}
|
|
147
130
|
default: {
|
|
148
131
|
throw new Error(`Unsupported ${this._combinatedFrom.combinator} combinator in selector`);
|
|
@@ -157,18 +140,25 @@ class SelectorTarget {
|
|
|
157
140
|
}
|
|
158
141
|
}
|
|
159
142
|
function uncombinatedRegexSelect(el, selector) {
|
|
160
|
-
let
|
|
143
|
+
let matched = true;
|
|
144
|
+
let data = {};
|
|
145
|
+
let tagSelector = '';
|
|
146
|
+
const specificity = [0, 0, 0];
|
|
147
|
+
const specifiedAttr = new Map();
|
|
161
148
|
if (selector.nodeName) {
|
|
162
149
|
const matchedNodeName = (0, ml_config_1.regexSelectorMatches)(selector.nodeName, el.nodeName);
|
|
163
|
-
if (
|
|
164
|
-
|
|
150
|
+
if (matchedNodeName) {
|
|
151
|
+
delete matchedNodeName.$0;
|
|
165
152
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
153
|
+
else {
|
|
154
|
+
matched = false;
|
|
155
|
+
}
|
|
156
|
+
data = {
|
|
157
|
+
...data,
|
|
169
158
|
...matchedNodeName,
|
|
170
|
-
__node: el.nodeName,
|
|
171
159
|
};
|
|
160
|
+
tagSelector = el.nodeName;
|
|
161
|
+
specificity[2] = 1;
|
|
172
162
|
}
|
|
173
163
|
if (selector.attrName) {
|
|
174
164
|
const selectorAttrName = selector.attrName;
|
|
@@ -177,16 +167,16 @@ function uncombinatedRegexSelect(el, selector) {
|
|
|
177
167
|
const matchedAttrName = (0, ml_config_1.regexSelectorMatches)(selectorAttrName, attrName);
|
|
178
168
|
if (matchedAttrName) {
|
|
179
169
|
delete matchedAttrName.$0;
|
|
180
|
-
|
|
181
|
-
...
|
|
170
|
+
data = {
|
|
171
|
+
...data,
|
|
182
172
|
...matchedAttrName,
|
|
183
|
-
__node: matchedMap.__node ? `${matchedMap.__node}[${attrName}]` : `[${attrName}]`,
|
|
184
173
|
};
|
|
174
|
+
specifiedAttr.set(attrName, '');
|
|
185
175
|
}
|
|
186
176
|
return matchedAttrName;
|
|
187
177
|
});
|
|
188
178
|
if (!matchedAttrNameList.some(_ => !!_)) {
|
|
189
|
-
|
|
179
|
+
matched = false;
|
|
190
180
|
}
|
|
191
181
|
}
|
|
192
182
|
if (selector.attrValue) {
|
|
@@ -197,19 +187,46 @@ function uncombinatedRegexSelect(el, selector) {
|
|
|
197
187
|
const matchedAttrValue = (0, ml_config_1.regexSelectorMatches)(selectorAttrValue, attrValue);
|
|
198
188
|
if (matchedAttrValue) {
|
|
199
189
|
delete matchedAttrValue.$0;
|
|
200
|
-
|
|
201
|
-
...
|
|
190
|
+
data = {
|
|
191
|
+
...data,
|
|
202
192
|
...matchedAttrValue,
|
|
203
|
-
__node: matchedMap.__node
|
|
204
|
-
? `${matchedMap.__node}[${attrName}="${attrValue}"]`
|
|
205
|
-
: `[${attrName}="${attrValue}"]`,
|
|
206
193
|
};
|
|
194
|
+
specifiedAttr.set(attrName, attrValue);
|
|
207
195
|
}
|
|
208
196
|
return matchedAttrValue;
|
|
209
197
|
});
|
|
210
198
|
if (!matchedAttrValueList.some(_ => !!_)) {
|
|
211
|
-
|
|
199
|
+
matched = false;
|
|
212
200
|
}
|
|
213
201
|
}
|
|
214
|
-
|
|
202
|
+
const attrSelector = Array.from(specifiedAttr.entries())
|
|
203
|
+
.map(([name, value]) => {
|
|
204
|
+
return `[${name}${value ? `="${value}"` : ''}]`;
|
|
205
|
+
})
|
|
206
|
+
.join('');
|
|
207
|
+
specificity[1] += specifiedAttr.size;
|
|
208
|
+
if (matched) {
|
|
209
|
+
return {
|
|
210
|
+
matched,
|
|
211
|
+
selector: `${tagSelector}${attrSelector}`,
|
|
212
|
+
specificity,
|
|
213
|
+
data,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
return { matched };
|
|
217
|
+
}
|
|
218
|
+
function mergeMatches(a, b, sep, close = false) {
|
|
219
|
+
return {
|
|
220
|
+
matched: true,
|
|
221
|
+
selector: `${a.selector}${sep}${b.selector}${close ? ')' : ''}`,
|
|
222
|
+
specificity: [
|
|
223
|
+
a.specificity[0] + b.specificity[0],
|
|
224
|
+
a.specificity[1] + b.specificity[1],
|
|
225
|
+
a.specificity[2] + b.specificity[2],
|
|
226
|
+
],
|
|
227
|
+
data: {
|
|
228
|
+
...a.data,
|
|
229
|
+
...b.data,
|
|
230
|
+
},
|
|
231
|
+
};
|
|
215
232
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type MLDOMAbstractElement from '../tokens/abstract-element';
|
|
2
|
+
export declare type Specificity = [number, number, number];
|
|
2
3
|
export declare function createSelector(selector: string): 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
8
|
constructor(selector: string);
|
|
7
|
-
match(el: TargetElement, caller?: TargetElement | null):
|
|
9
|
+
match(el: TargetElement, caller?: TargetElement | null): Specificity | false;
|
|
8
10
|
}
|
|
9
11
|
export {};
|