@markuplint/ml-core 2.0.0-dev.20211213.1 → 2.0.0-dev.20220106.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/lib/ml-core.d.ts +1 -1
- package/lib/ml-core.js +14 -12
- package/lib/ml-dom/document.js +27 -14
- 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.d.ts +1 -0
- package/lib/ml-dom/tokens/abstract-element.js +28 -3
- package/lib/ml-dom/tokens/attribute.d.ts +1 -0
- package/lib/ml-dom/tokens/attribute.js +7 -0
- package/lib/ml-dom/tokens/preprocessor-specific-attribute.d.ts +1 -0
- package/lib/ml-dom/tokens/preprocessor-specific-attribute.js +3 -0
- 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():
|
|
13
|
+
get document(): Document<RuleConfigValue, unknown> | MLParseError;
|
|
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-core.js
CHANGED
|
@@ -85,18 +85,20 @@ class MLCore {
|
|
|
85
85
|
}
|
|
86
86
|
(0, debug_1.log)('%s Rule: verify end', rule.name);
|
|
87
87
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
if (resultLog.enabled) {
|
|
89
|
+
const { e, w, i } = violations.reduce((c, v) => {
|
|
90
|
+
if (v.severity === 'error')
|
|
91
|
+
c.e += 1;
|
|
92
|
+
if (v.severity === 'warning')
|
|
93
|
+
c.w += 1;
|
|
94
|
+
if (v.severity === 'info')
|
|
95
|
+
c.i += 1;
|
|
96
|
+
return c;
|
|
97
|
+
}, { e: 0, w: 0, i: 0 });
|
|
98
|
+
resultLog('Error: %d', e);
|
|
99
|
+
resultLog('Warning: %d', w);
|
|
100
|
+
resultLog('Info: %d', i);
|
|
101
|
+
}
|
|
100
102
|
(0, debug_1.log)('verify: end');
|
|
101
103
|
return violations;
|
|
102
104
|
}
|
package/lib/ml-dom/document.js
CHANGED
|
@@ -132,13 +132,15 @@ class MLDOMDocument {
|
|
|
132
132
|
if (node.type === 'ElementCloseTag') {
|
|
133
133
|
continue;
|
|
134
134
|
}
|
|
135
|
-
docLog
|
|
135
|
+
if (docLog.enabled) {
|
|
136
|
+
docLog('Add rules to node <%s>', 'nodeName' in node ? node.nodeName : `#${node.type}`);
|
|
137
|
+
}
|
|
136
138
|
// global rules
|
|
137
139
|
Object.keys(ruleset.rules).forEach(ruleName => {
|
|
138
140
|
const rule = ruleset.rules[ruleName];
|
|
139
141
|
ruleMapper.set(node, ruleName, {
|
|
140
142
|
from: 'rules',
|
|
141
|
-
|
|
143
|
+
specificity: [0, 0, 0],
|
|
142
144
|
rule,
|
|
143
145
|
});
|
|
144
146
|
});
|
|
@@ -151,25 +153,34 @@ class MLDOMDocument {
|
|
|
151
153
|
if (!nodeRule.rules) {
|
|
152
154
|
return;
|
|
153
155
|
}
|
|
156
|
+
if (!selectorTarget) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
154
159
|
const selector = nodeRule.selector || nodeRule.regexSelector || nodeRule.tagName;
|
|
155
|
-
const
|
|
160
|
+
const matches =
|
|
156
161
|
/**
|
|
157
162
|
* Forward v1.x compatibility
|
|
158
163
|
*/
|
|
159
164
|
nodeRule.tagName && /^#text$/i.test(nodeRule.tagName) && node.type === 'Text'
|
|
160
|
-
? {
|
|
165
|
+
? {
|
|
166
|
+
matched: true,
|
|
167
|
+
selector: '#text',
|
|
168
|
+
specificity: [0, 0, 0],
|
|
169
|
+
}
|
|
161
170
|
: /**
|
|
162
171
|
* v2.0.0 or later
|
|
163
172
|
*/
|
|
164
|
-
|
|
165
|
-
if (!matched) {
|
|
173
|
+
(0, match_selector_1.matchSelector)(selectorTarget, selector);
|
|
174
|
+
if (!matches.matched) {
|
|
166
175
|
return;
|
|
167
176
|
}
|
|
168
|
-
|
|
177
|
+
if (docLog.enabled) {
|
|
178
|
+
docLog('Matched nodeRule: <%s>(%s)', 'nodeName' in node ? node.nodeName : node.type, matches.selector || '*');
|
|
179
|
+
}
|
|
169
180
|
const ruleList = Object.keys(nodeRule.rules);
|
|
170
181
|
for (const ruleName of ruleList) {
|
|
171
182
|
const rule = nodeRule.rules[ruleName];
|
|
172
|
-
const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule,
|
|
183
|
+
const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule, matches.data || {});
|
|
173
184
|
if (convertedRule === undefined) {
|
|
174
185
|
continue;
|
|
175
186
|
}
|
|
@@ -178,7 +189,7 @@ class MLDOMDocument {
|
|
|
178
189
|
ruleLog('↑ nodeRule (%s): %O', ruleName, mergedRule);
|
|
179
190
|
ruleMapper.set(node, ruleName, {
|
|
180
191
|
from: 'nodeRules',
|
|
181
|
-
|
|
192
|
+
specificity: matches.specificity,
|
|
182
193
|
rule: mergedRule,
|
|
183
194
|
});
|
|
184
195
|
}
|
|
@@ -202,15 +213,17 @@ class MLDOMDocument {
|
|
|
202
213
|
if (!selector) {
|
|
203
214
|
return;
|
|
204
215
|
}
|
|
205
|
-
const
|
|
206
|
-
if (!matched) {
|
|
216
|
+
const matches = (0, match_selector_1.matchSelector)(selectorTarget, selector);
|
|
217
|
+
if (!matches.matched) {
|
|
207
218
|
return;
|
|
208
219
|
}
|
|
209
|
-
|
|
220
|
+
if (docLog.enabled) {
|
|
221
|
+
docLog('Matched childNodeRule: <%s>(%s), inheritance: %o', selectorTarget.nodeName, matches.selector || '*', !!nodeRule.inheritance);
|
|
222
|
+
}
|
|
210
223
|
const targetDescendants = nodeRule.inheritance ? descendants : children;
|
|
211
224
|
Object.keys(nodeRuleRules).forEach(ruleName => {
|
|
212
225
|
const rule = nodeRuleRules[ruleName];
|
|
213
|
-
const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule,
|
|
226
|
+
const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule, matches.data || {});
|
|
214
227
|
if (convertedRule === undefined) {
|
|
215
228
|
return;
|
|
216
229
|
}
|
|
@@ -220,7 +233,7 @@ class MLDOMDocument {
|
|
|
220
233
|
targetDescendants.forEach(descendant => {
|
|
221
234
|
ruleMapper.set(descendant, ruleName, {
|
|
222
235
|
from: 'childNodeRules',
|
|
223
|
-
|
|
236
|
+
specificity: matches.specificity,
|
|
224
237
|
rule: mergedRule,
|
|
225
238
|
});
|
|
226
239
|
});
|
|
@@ -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 {};
|