@markuplint/ml-core 2.0.0-dev.20211213.2 → 2.0.0-dev.20220107.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 +103 -85
- package/lib/ml-dom/helper/selector.d.ts +3 -1
- package/lib/ml-dom/helper/selector.js +237 -66
- 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 +31 -6
- package/lib/ml-dom/tokens/attribute.d.ts +1 -0
- package/lib/ml-dom/tokens/attribute.js +7 -0
- package/lib/ml-dom/tokens/node.d.ts +6 -5
- package/lib/ml-dom/tokens/node.js +20 -6
- 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,113 +47,86 @@ 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) {
|
|
48
59
|
// Descendant combinator
|
|
49
60
|
case ' ': {
|
|
50
|
-
let ancestor = el.
|
|
61
|
+
let ancestor = el.getParentElement();
|
|
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
|
-
ancestor = ancestor.
|
|
67
|
+
ancestor = ancestor.getParentElement();
|
|
62
68
|
}
|
|
63
|
-
return
|
|
69
|
+
return {
|
|
70
|
+
matched: false,
|
|
71
|
+
};
|
|
64
72
|
}
|
|
65
73
|
// Child combinator
|
|
66
74
|
case '>': {
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
const parentNode = el.getParentElement();
|
|
76
|
+
if (!parentNode) {
|
|
77
|
+
return { matched: false };
|
|
69
78
|
}
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
72
|
-
|
|
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
|
|
83
|
+
return { matched: false };
|
|
80
84
|
}
|
|
81
85
|
// Next-sibling combinator
|
|
82
86
|
case '+': {
|
|
83
87
|
if (!el.previousElementSibling) {
|
|
84
|
-
return
|
|
88
|
+
return { matched: false };
|
|
85
89
|
}
|
|
86
|
-
const
|
|
87
|
-
if (
|
|
88
|
-
|
|
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
|
|
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
|
|
102
|
-
if (
|
|
103
|
-
|
|
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
|
|
106
|
+
return { matched: false };
|
|
113
107
|
}
|
|
114
108
|
// Prev-sibling combinator
|
|
115
109
|
case ':has(+)': {
|
|
116
110
|
if (!el.nextElementSibling) {
|
|
117
|
-
return
|
|
111
|
+
return { matched: false };
|
|
118
112
|
}
|
|
119
|
-
const
|
|
120
|
-
if (
|
|
121
|
-
|
|
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
|
|
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
|
|
135
|
-
if (
|
|
136
|
-
|
|
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
|
|
129
|
+
return { matched: false };
|
|
146
130
|
}
|
|
147
131
|
default: {
|
|
148
132
|
throw new Error(`Unsupported ${this._combinatedFrom.combinator} combinator in selector`);
|
|
@@ -157,18 +141,25 @@ class SelectorTarget {
|
|
|
157
141
|
}
|
|
158
142
|
}
|
|
159
143
|
function uncombinatedRegexSelect(el, selector) {
|
|
160
|
-
let
|
|
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 (
|
|
164
|
-
|
|
151
|
+
if (matchedNodeName) {
|
|
152
|
+
delete matchedNodeName.$0;
|
|
165
153
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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;
|
|
@@ -177,16 +168,16 @@ function uncombinatedRegexSelect(el, selector) {
|
|
|
177
168
|
const matchedAttrName = (0, ml_config_1.regexSelectorMatches)(selectorAttrName, attrName);
|
|
178
169
|
if (matchedAttrName) {
|
|
179
170
|
delete matchedAttrName.$0;
|
|
180
|
-
|
|
181
|
-
...
|
|
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
|
-
|
|
180
|
+
matched = false;
|
|
190
181
|
}
|
|
191
182
|
}
|
|
192
183
|
if (selector.attrValue) {
|
|
@@ -197,19 +188,46 @@ function uncombinatedRegexSelect(el, selector) {
|
|
|
197
188
|
const matchedAttrValue = (0, ml_config_1.regexSelectorMatches)(selectorAttrValue, attrValue);
|
|
198
189
|
if (matchedAttrValue) {
|
|
199
190
|
delete matchedAttrValue.$0;
|
|
200
|
-
|
|
201
|
-
...
|
|
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
|
-
|
|
200
|
+
matched = false;
|
|
212
201
|
}
|
|
213
202
|
}
|
|
214
|
-
|
|
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 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 {};
|