@markuplint/ml-core
Advanced tools
Comparing version 2.0.0-rc.0 to 2.0.0-rc.1
export { RuleInfo, RuleConfig, RuleConfigValue } from '@markuplint/ml-config'; | ||
export { getAttrSpecs } from '@markuplint/ml-spec'; | ||
export * from './convert-ruleset'; | ||
@@ -3,0 +4,0 @@ export * from './ml-core'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.enableDebug = exports.getIndent = exports.Ruleset = exports.MLParseError = void 0; | ||
exports.enableDebug = exports.getIndent = exports.Ruleset = exports.MLParseError = exports.getAttrSpecs = void 0; | ||
const tslib_1 = require("tslib"); | ||
var ml_spec_1 = require("@markuplint/ml-spec"); | ||
Object.defineProperty(exports, "getAttrSpecs", { enumerable: true, get: function () { return ml_spec_1.getAttrSpecs; } }); | ||
(0, tslib_1.__exportStar)(require("./convert-ruleset"), exports); | ||
@@ -6,0 +8,0 @@ (0, tslib_1.__exportStar)(require("./ml-core"), exports); |
@@ -13,3 +13,3 @@ import type { MLFabric } from './types'; | ||
constructor({ parser, sourceCode, ruleset, rules, locale, schemas, parserOptions, filename, debug }: MLCoreParams); | ||
get document(): MLParseError | Document<RuleConfigValue, unknown>; | ||
get document(): Document<RuleConfigValue, unknown> | MLParseError; | ||
verify(fix?: boolean): Promise<Violation[]>; | ||
@@ -16,0 +16,0 @@ setCode(sourceCode: string): void; |
@@ -61,4 +61,4 @@ "use strict"; | ||
for (const rule of (0, tslib_1.__classPrivateFieldGet)(this, _MLCore_rules, "f")) { | ||
const ruleInfo = rule.optimizeOption((0, tslib_1.__classPrivateFieldGet)(this, _MLCore_ruleset, "f").rules[rule.name] || false); | ||
if (ruleInfo.disabled) { | ||
const ruleInfo = rule.getRuleInfo((0, tslib_1.__classPrivateFieldGet)(this, _MLCore_ruleset, "f"), rule.name); | ||
if (ruleInfo.disabled && ruleInfo.nodeRules.length === 0 && ruleInfo.childNodeRules.length === 0) { | ||
continue; | ||
@@ -89,14 +89,16 @@ } | ||
} | ||
const { e, w, i } = violations.reduce((c, v) => { | ||
if (v.severity === 'error') | ||
c.e += 1; | ||
if (v.severity === 'warning') | ||
c.w += 1; | ||
if (v.severity === 'info') | ||
c.i += 1; | ||
return c; | ||
}, { e: 0, w: 0, i: 0 }); | ||
resultLog('Error: %d', e); | ||
resultLog('Warning: %d', w); | ||
resultLog('Info: %d', i); | ||
if (resultLog.enabled) { | ||
const { e, w, i } = violations.reduce((c, v) => { | ||
if (v.severity === 'error') | ||
c.e += 1; | ||
if (v.severity === 'warning') | ||
c.w += 1; | ||
if (v.severity === 'info') | ||
c.i += 1; | ||
return c; | ||
}, { e: 0, w: 0, i: 0 }); | ||
resultLog('Error: %d', e); | ||
resultLog('Warning: %d', w); | ||
resultLog('Info: %d', i); | ||
} | ||
(0, debug_1.log)('verify: end'); | ||
@@ -103,0 +105,0 @@ return violations; |
@@ -132,3 +132,8 @@ "use strict"; | ||
for (const node of this.nodeList) { | ||
docLog('Add rules to node <%s%s>', node.type === 'ElementCloseTag' ? '/' : '', 'nodeName' in node ? node.nodeName : `#${node.type}`); | ||
if (node.type === 'ElementCloseTag') { | ||
continue; | ||
} | ||
if (docLog.enabled) { | ||
docLog('Add rules to node <%s>', 'nodeName' in node ? node.nodeName : `#${node.type}`); | ||
} | ||
// global rules | ||
@@ -139,17 +144,10 @@ Object.keys(ruleset.rules).forEach(ruleName => { | ||
from: 'rules', | ||
index: -1, | ||
specificity: [0, 0, 0], | ||
rule, | ||
}); | ||
}); | ||
if (node.type !== 'Element' && | ||
node.type !== 'OmittedElement' && | ||
node.type !== 'ElementCloseTag' && | ||
node.type !== 'Text') { | ||
if (node.type !== 'Element' && node.type !== 'OmittedElement' && node.type !== 'Text') { | ||
continue; | ||
} | ||
const selectorTarget = node.type === 'Element' || node.type === 'OmittedElement' | ||
? node | ||
: node.type === 'ElementCloseTag' | ||
? node.startTag | ||
: null; | ||
const selectorTarget = node.type === 'Element' || node.type === 'OmittedElement' ? node : null; | ||
// node specs and special rules for node by selector | ||
@@ -160,4 +158,7 @@ ruleset.nodeRules.forEach((nodeRule, i) => { | ||
} | ||
if (!selectorTarget) { | ||
return; | ||
} | ||
const selector = nodeRule.selector || nodeRule.regexSelector || nodeRule.tagName; | ||
const matched = | ||
const matches = | ||
/** | ||
@@ -167,15 +168,21 @@ * Forward v1.x compatibility | ||
nodeRule.tagName && /^#text$/i.test(nodeRule.tagName) && node.type === 'Text' | ||
? { __node: '#text' } | ||
? { | ||
matched: true, | ||
selector: '#text', | ||
specificity: [0, 0, 0], | ||
} | ||
: /** | ||
* v2.0.0 or later | ||
*/ | ||
selectorTarget && (0, match_selector_1.matchSelector)(selectorTarget, selector); | ||
if (!matched) { | ||
(0, match_selector_1.matchSelector)(selectorTarget, selector); | ||
if (!matches.matched) { | ||
return; | ||
} | ||
docLog('Matched nodeRule: <%s%s>(%s)', node.type === 'ElementCloseTag' ? '/' : '', 'nodeName' in node ? node.nodeName : node.type, matched.__node || 'No Selector'); | ||
if (docLog.enabled) { | ||
docLog('Matched nodeRule: <%s>(%s)', 'nodeName' in node ? node.nodeName : node.type, matches.selector || '*'); | ||
} | ||
const ruleList = Object.keys(nodeRule.rules); | ||
for (const ruleName of ruleList) { | ||
const rule = nodeRule.rules[ruleName]; | ||
const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule, matched); | ||
const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule, matches.data || {}); | ||
if (convertedRule === undefined) { | ||
@@ -189,3 +196,3 @@ continue; | ||
from: 'nodeRules', | ||
index: i, | ||
specificity: matches.specificity, | ||
rule: mergedRule, | ||
@@ -214,11 +221,13 @@ }); | ||
} | ||
const matched = (0, match_selector_1.matchSelector)(selectorTarget, selector); | ||
if (!matched) { | ||
const matches = (0, match_selector_1.matchSelector)(selectorTarget, selector); | ||
if (!matches.matched) { | ||
return; | ||
} | ||
docLog('Matched childNodeRule: <%s%s>(%s), inheritance: %o', node.type === 'ElementCloseTag' ? '/' : '', selectorTarget.nodeName, matched.__node || 'No Selector', !!nodeRule.inheritance); | ||
if (docLog.enabled) { | ||
docLog('Matched childNodeRule: <%s>(%s), inheritance: %o', selectorTarget.nodeName, matches.selector || '*', !!nodeRule.inheritance); | ||
} | ||
const targetDescendants = nodeRule.inheritance ? descendants : children; | ||
Object.keys(nodeRuleRules).forEach(ruleName => { | ||
const rule = nodeRuleRules[ruleName]; | ||
const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule, matched); | ||
const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule, matches.data || {}); | ||
if (convertedRule === undefined) { | ||
@@ -233,3 +242,3 @@ return; | ||
from: 'childNodeRules', | ||
index: i, | ||
specificity: matches.specificity, | ||
rule: mergedRule, | ||
@@ -236,0 +245,0 @@ }); |
import type MLDOMAbstractElement from '../tokens/abstract-element'; | ||
import type { Specificity } from './selector'; | ||
import type { RegexSelector } from '@markuplint/ml-config'; | ||
export declare type SelectorMatches = SelectorMatched | SelectorUnmatched; | ||
declare type SelectorMatched = { | ||
matched: true; | ||
selector: string; | ||
specificity: Specificity; | ||
data?: Record<string, string>; | ||
}; | ||
declare type SelectorUnmatched = { | ||
matched: false; | ||
}; | ||
declare type TargetElement = MLDOMAbstractElement<any, any>; | ||
export declare function matchSelector(el: TargetElement, selector: string | RegexSelector | undefined): Record<string, string> | null; | ||
export declare function matchSelector(el: TargetElement, selector: string | RegexSelector | undefined): SelectorMatches; | ||
export {}; |
@@ -8,8 +8,19 @@ "use strict"; | ||
if (!selector) { | ||
return null; | ||
return { | ||
matched: false, | ||
}; | ||
} | ||
if (typeof selector === 'string') { | ||
const sel = (0, selector_1.createSelector)(selector); | ||
const matched = sel.match(el); | ||
return matched ? { __node: selector } : null; | ||
const specificity = sel.match(el); | ||
if (specificity) { | ||
return { | ||
matched: true, | ||
selector, | ||
specificity, | ||
}; | ||
} | ||
return { | ||
matched: false, | ||
}; | ||
} | ||
@@ -40,8 +51,8 @@ return regexSelect(el, selector); | ||
match(el) { | ||
const matched = this._matchWithoutCombinateChecking(el); | ||
if (!matched) { | ||
return null; | ||
const unitCheck = this._matchWithoutCombinateChecking(el); | ||
if (!unitCheck.matched) { | ||
return unitCheck; | ||
} | ||
if (!this._combinatedFrom) { | ||
return matched; | ||
return unitCheck; | ||
} | ||
@@ -52,32 +63,25 @@ const { target, combinator } = this._combinatedFrom; | ||
case ' ': { | ||
let ancestor = el.parentNode; | ||
let ancestor = el.getParentElement(); | ||
while (ancestor) { | ||
const ancestorMatched = target.match(ancestor); | ||
if (ancestorMatched) { | ||
const res = { | ||
...ancestorMatched, | ||
...matched, | ||
__node: `${ancestorMatched.__node} ${matched.__node}`, | ||
}; | ||
return res; | ||
const matches = target.match(ancestor); | ||
if (matches.matched) { | ||
return mergeMatches(matches, unitCheck, ' '); | ||
} | ||
ancestor = ancestor.parentNode; | ||
ancestor = ancestor.getParentElement(); | ||
} | ||
return null; | ||
return { | ||
matched: false, | ||
}; | ||
} | ||
// Child combinator | ||
case '>': { | ||
if (!el.parentNode) { | ||
return null; | ||
const parentNode = el.getParentElement(); | ||
if (!parentNode) { | ||
return { matched: false }; | ||
} | ||
const parentMatched = target.match(el.parentNode); | ||
if (parentMatched) { | ||
const res = { | ||
...parentMatched, | ||
...matched, | ||
__node: `${parentMatched.__node} > ${matched.__node}`, | ||
}; | ||
return res; | ||
const matches = target.match(parentNode); | ||
if (matches.matched) { | ||
return mergeMatches(matches, unitCheck, ' > '); | ||
} | ||
return null; | ||
return { matched: false }; | ||
} | ||
@@ -87,14 +91,9 @@ // Next-sibling combinator | ||
if (!el.previousElementSibling) { | ||
return null; | ||
return { matched: false }; | ||
} | ||
const prevMatched = target.match(el.previousElementSibling); | ||
if (prevMatched) { | ||
const res = { | ||
...prevMatched, | ||
...matched, | ||
__node: `${prevMatched.__node} + ${matched.__node}`, | ||
}; | ||
return res; | ||
const matches = target.match(el.previousElementSibling); | ||
if (matches.matched) { | ||
return mergeMatches(matches, unitCheck, ' + '); | ||
} | ||
return null; | ||
return { matched: false }; | ||
} | ||
@@ -105,14 +104,9 @@ // Subsequent-sibling combinator | ||
while (prev) { | ||
const prevMatched = target.match(prev); | ||
if (prevMatched) { | ||
const res = { | ||
...prevMatched, | ||
...matched, | ||
__node: `${prevMatched.__node} ~ ${matched.__node}`, | ||
}; | ||
return res; | ||
const matches = target.match(prev); | ||
if (matches.matched) { | ||
return mergeMatches(matches, unitCheck, ' ~ '); | ||
} | ||
prev = prev.previousElementSibling; | ||
} | ||
return null; | ||
return { matched: false }; | ||
} | ||
@@ -122,14 +116,9 @@ // Prev-sibling combinator | ||
if (!el.nextElementSibling) { | ||
return null; | ||
return { matched: false }; | ||
} | ||
const nextMatched = target.match(el.nextElementSibling); | ||
if (nextMatched) { | ||
const res = { | ||
...nextMatched, | ||
...matched, | ||
__node: `${nextMatched.__node}:has(+ ${matched.__node})`, | ||
}; | ||
return res; | ||
const matches = target.match(el.nextElementSibling); | ||
if (matches.matched) { | ||
return mergeMatches(matches, unitCheck, ':has(+ ', true); | ||
} | ||
return null; | ||
return { matched: false }; | ||
} | ||
@@ -140,14 +129,9 @@ // Subsequent-sibling (in front) combinator | ||
while (next) { | ||
const nextMatched = target.match(next); | ||
if (nextMatched) { | ||
const res = { | ||
...nextMatched, | ||
...matched, | ||
__node: `${nextMatched.__node}:has(~ ${matched.__node})`, | ||
}; | ||
return res; | ||
const matches = target.match(next); | ||
if (matches.matched) { | ||
return mergeMatches(matches, unitCheck, ':has(~ ', true); | ||
} | ||
next = next.nextElementSibling; | ||
} | ||
return null; | ||
return { matched: false }; | ||
} | ||
@@ -167,14 +151,21 @@ default: { | ||
function uncombinatedRegexSelect(el, selector) { | ||
let matchedMap = {}; | ||
let matched = true; | ||
let data = {}; | ||
let tagSelector = ''; | ||
const specificity = [0, 0, 0]; | ||
const specifiedAttr = new Map(); | ||
if (selector.nodeName) { | ||
const matchedNodeName = (0, ml_config_1.regexSelectorMatches)(selector.nodeName, el.nodeName); | ||
if (!matchedNodeName) { | ||
return null; | ||
if (matchedNodeName) { | ||
delete matchedNodeName.$0; | ||
} | ||
delete matchedNodeName.$0; | ||
matchedMap = { | ||
...matchedMap, | ||
else { | ||
matched = false; | ||
} | ||
data = { | ||
...data, | ||
...matchedNodeName, | ||
__node: el.nodeName, | ||
}; | ||
tagSelector = el.nodeName; | ||
specificity[2] = 1; | ||
} | ||
@@ -188,7 +179,7 @@ if (selector.attrName) { | ||
delete matchedAttrName.$0; | ||
matchedMap = { | ||
...matchedMap, | ||
data = { | ||
...data, | ||
...matchedAttrName, | ||
__node: matchedMap.__node ? `${matchedMap.__node}[${attrName}]` : `[${attrName}]`, | ||
}; | ||
specifiedAttr.set(attrName, ''); | ||
} | ||
@@ -198,3 +189,3 @@ return matchedAttrName; | ||
if (!matchedAttrNameList.some(_ => !!_)) { | ||
return null; | ||
matched = false; | ||
} | ||
@@ -210,9 +201,7 @@ } | ||
delete matchedAttrValue.$0; | ||
matchedMap = { | ||
...matchedMap, | ||
data = { | ||
...data, | ||
...matchedAttrValue, | ||
__node: matchedMap.__node | ||
? `${matchedMap.__node}[${attrName}="${attrValue}"]` | ||
: `[${attrName}="${attrValue}"]`, | ||
}; | ||
specifiedAttr.set(attrName, attrValue); | ||
} | ||
@@ -222,6 +211,35 @@ return matchedAttrValue; | ||
if (!matchedAttrValueList.some(_ => !!_)) { | ||
return null; | ||
matched = false; | ||
} | ||
} | ||
return matchedMap; | ||
const attrSelector = Array.from(specifiedAttr.entries()) | ||
.map(([name, value]) => { | ||
return `[${name}${value ? `="${value}"` : ''}]`; | ||
}) | ||
.join(''); | ||
specificity[1] += specifiedAttr.size; | ||
if (matched) { | ||
return { | ||
matched, | ||
selector: `${tagSelector}${attrSelector}`, | ||
specificity, | ||
data, | ||
}; | ||
} | ||
return { matched }; | ||
} | ||
function mergeMatches(a, b, sep, close = false) { | ||
return { | ||
matched: true, | ||
selector: `${a.selector}${sep}${b.selector}${close ? ')' : ''}`, | ||
specificity: [ | ||
a.specificity[0] + b.specificity[0], | ||
a.specificity[1] + b.specificity[1], | ||
a.specificity[2] + b.specificity[2], | ||
], | ||
data: { | ||
...a.data, | ||
...b.data, | ||
}, | ||
}; | ||
} |
import type MLDOMAbstractElement from '../tokens/abstract-element'; | ||
export declare type Specificity = [number, number, number]; | ||
export declare function createSelector(selector: string): Selector; | ||
export declare function compareSpecificity(a: Specificity, b: Specificity): 0 | 1 | -1; | ||
declare type TargetElement = MLDOMAbstractElement<any, any>; | ||
@@ -7,4 +9,4 @@ declare class Selector { | ||
constructor(selector: string); | ||
match(el: TargetElement, caller?: TargetElement | null): boolean; | ||
match(el: TargetElement, caller?: TargetElement | null): Specificity | false; | ||
} | ||
export {}; |
"use strict"; | ||
var _Selector_ruleset, _Ruleset_selectorGroup, _StructuredSelector_edge, _StructuredSelector_selector, _SelectorTarget_isAdded, _SelectorTarget_combinatedFrom; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createSelector = void 0; | ||
exports.compareSpecificity = exports.createSelector = void 0; | ||
const tslib_1 = require("tslib"); | ||
const postcss_selector_parser_1 = (0, tslib_1.__importDefault)(require("postcss-selector-parser")); | ||
const debug_1 = require("../../debug"); | ||
const log = debug_1.log.extend('selector'); | ||
const resLog = log.extend('result'); | ||
function createSelector(selector) { | ||
@@ -11,2 +14,24 @@ return new Selector(selector); | ||
exports.createSelector = createSelector; | ||
function compareSpecificity(a, b) { | ||
if (a[0] < b[0]) { | ||
return -1; | ||
} | ||
else if (a[0] > b[0]) { | ||
return 1; | ||
} | ||
else if (a[1] < b[1]) { | ||
return -1; | ||
} | ||
else if (a[1] > b[1]) { | ||
return 1; | ||
} | ||
else if (a[2] < b[2]) { | ||
return -1; | ||
} | ||
else if (a[2] > b[2]) { | ||
return 1; | ||
} | ||
return 0; | ||
} | ||
exports.compareSpecificity = compareSpecificity; | ||
class Selector { | ||
@@ -18,3 +43,9 @@ constructor(selector) { | ||
match(el, caller = el) { | ||
return (0, tslib_1.__classPrivateFieldGet)(this, _Selector_ruleset, "f").match(el, caller); | ||
const results = (0, tslib_1.__classPrivateFieldGet)(this, _Selector_ruleset, "f").match(el, caller); | ||
for (const result of results) { | ||
if (result.matched) { | ||
return result.specificity; | ||
} | ||
} | ||
return false; | ||
} | ||
@@ -36,3 +67,8 @@ } | ||
match(el, caller) { | ||
return (0, tslib_1.__classPrivateFieldGet)(this, _Ruleset_selectorGroup, "f").some(selector => selector.match(el, caller)); | ||
log('%s', el.raw); | ||
return (0, tslib_1.__classPrivateFieldGet)(this, _Ruleset_selectorGroup, "f").map(selector => { | ||
const res = selector.match(el, caller); | ||
resLog('%s => %o', selector.selector, res); | ||
return res; | ||
}); | ||
} | ||
@@ -71,2 +107,5 @@ } | ||
} | ||
get selector() { | ||
return (0, tslib_1.__classPrivateFieldGet)(this, _StructuredSelector_selector, "f").nodes.join(''); | ||
} | ||
match(el, caller) { | ||
@@ -88,8 +127,8 @@ return (0, tslib_1.__classPrivateFieldGet)(this, _StructuredSelector_edge, "f").match(el, caller); | ||
match(el, caller) { | ||
const matched = this._matchWithoutCombinateChecking(el, caller); | ||
if (!matched) { | ||
return false; | ||
const unitCheck = this._matchWithoutCombinateChecking(el, caller); | ||
if (!unitCheck.matched) { | ||
return unitCheck; | ||
} | ||
if (!(0, tslib_1.__classPrivateFieldGet)(this, _SelectorTarget_combinatedFrom, "f")) { | ||
return true; | ||
return unitCheck; | ||
} | ||
@@ -100,45 +139,110 @@ const { target, combinator } = (0, tslib_1.__classPrivateFieldGet)(this, _SelectorTarget_combinatedFrom, "f"); | ||
case ' ': { | ||
let ancestor = el.parentNode; | ||
let ancestor = el.getParentElement(); | ||
let matched = false; | ||
let specificity; | ||
while (ancestor) { | ||
const ancestorMatched = target._matchWithoutCombinateChecking(ancestor, caller); | ||
if (ancestorMatched) { | ||
return target.match(ancestor, caller); | ||
const res = target.match(ancestor, caller); | ||
if (!specificity) { | ||
specificity = [ | ||
unitCheck.specificity[0] + res.specificity[0], | ||
unitCheck.specificity[1] + res.specificity[1], | ||
unitCheck.specificity[2] + res.specificity[2], | ||
]; | ||
} | ||
ancestor = ancestor.parentNode; | ||
if (res.matched) { | ||
matched = true; | ||
} | ||
ancestor = ancestor.getParentElement(); | ||
} | ||
return false; | ||
if (!specificity) { | ||
const res = target.match(el, caller); | ||
specificity = [ | ||
unitCheck.specificity[0] + res.specificity[0], | ||
unitCheck.specificity[1] + res.specificity[1], | ||
unitCheck.specificity[2] + res.specificity[2], | ||
]; | ||
} | ||
return { | ||
specificity, | ||
matched, | ||
}; | ||
} | ||
// Child combinator | ||
case '>': { | ||
if (!el.parentNode) { | ||
return false; | ||
let matched; | ||
const specificity = unitCheck.specificity; | ||
const parentNode = el.getParentElement(); | ||
if (parentNode) { | ||
const res = target.match(parentNode, caller); | ||
specificity[0] += res.specificity[0]; | ||
specificity[1] += res.specificity[1]; | ||
specificity[2] += res.specificity[2]; | ||
matched = res.matched; | ||
} | ||
const parentMatched = target._matchWithoutCombinateChecking(el.parentNode, caller); | ||
if (parentMatched) { | ||
return target.match(el.parentNode, caller); | ||
else { | ||
const res = target.match(el, caller); | ||
specificity[0] += res.specificity[0]; | ||
specificity[1] += res.specificity[1]; | ||
specificity[2] += res.specificity[2]; | ||
matched = false; | ||
} | ||
return false; | ||
return { | ||
specificity, | ||
matched, | ||
}; | ||
} | ||
// Next-sibling combinator | ||
case '+': { | ||
if (!el.previousElementSibling) { | ||
return false; | ||
let matched; | ||
const specificity = unitCheck.specificity; | ||
if (el.previousElementSibling) { | ||
const res = target.match(el.previousElementSibling, caller); | ||
specificity[0] += res.specificity[0]; | ||
specificity[1] += res.specificity[1]; | ||
specificity[2] += res.specificity[2]; | ||
matched = res.matched; | ||
} | ||
const prevMatched = target._matchWithoutCombinateChecking(el.previousElementSibling, caller); | ||
if (prevMatched) { | ||
return target.match(el.previousElementSibling, caller); | ||
else { | ||
const res = target.match(el, caller); | ||
specificity[0] += res.specificity[0]; | ||
specificity[1] += res.specificity[1]; | ||
specificity[2] += res.specificity[2]; | ||
matched = false; | ||
} | ||
return false; | ||
return { | ||
specificity, | ||
matched, | ||
}; | ||
} | ||
// Subsequent-sibling combinator | ||
// // Subsequent-sibling combinator | ||
case '~': { | ||
let prev = el.previousElementSibling; | ||
let matched = false; | ||
let specificity; | ||
while (prev) { | ||
const prevMatched = target._matchWithoutCombinateChecking(prev, caller); | ||
if (prevMatched) { | ||
return target.match(prev, caller); | ||
const res = target.match(prev, caller); | ||
if (!specificity) { | ||
specificity = [ | ||
unitCheck.specificity[0] + res.specificity[0], | ||
unitCheck.specificity[1] + res.specificity[1], | ||
unitCheck.specificity[2] + res.specificity[2], | ||
]; | ||
} | ||
if (res.matched) { | ||
matched = true; | ||
} | ||
prev = prev.previousElementSibling; | ||
} | ||
return false; | ||
if (!specificity) { | ||
const res = target.match(el, caller); | ||
specificity = [ | ||
unitCheck.specificity[0] + res.specificity[0], | ||
unitCheck.specificity[1] + res.specificity[1], | ||
unitCheck.specificity[2] + res.specificity[2], | ||
]; | ||
} | ||
return { | ||
specificity, | ||
matched, | ||
}; | ||
} | ||
@@ -155,23 +259,38 @@ // Column combinator | ||
_matchWithoutCombinateChecking(el, caller) { | ||
if (!(0, tslib_1.__classPrivateFieldGet)(this, _SelectorTarget_isAdded, "f")) { | ||
return isScope(el, caller); | ||
const specificity = [0, 0, 0]; | ||
let matched = true; | ||
if (!(0, tslib_1.__classPrivateFieldGet)(this, _SelectorTarget_isAdded, "f") && !isScope(el, caller)) { | ||
matched = false; | ||
} | ||
if (this.tag && this.tag.type === 'tag') { | ||
if (this.tag.value.toLowerCase() !== el.nodeName.toLowerCase()) { | ||
return false; | ||
} | ||
} | ||
if (!this.id.every(id => id.value === el.id)) { | ||
return false; | ||
matched = false; | ||
} | ||
specificity[0] += this.id.length; | ||
if (!this.class.every(className => el.classList.includes(className.value))) { | ||
return false; | ||
matched = false; | ||
} | ||
specificity[1] += this.class.length; | ||
if (!this.attr.every(attr => attrMatch(attr, el))) { | ||
return false; | ||
matched = false; | ||
} | ||
if (!this.pseudo.every(pseudo => pseudoMatch(pseudo, el, caller))) { | ||
return false; | ||
specificity[1] += this.attr.length; | ||
for (const pseudo of this.pseudo) { | ||
const pseudoRes = pseudoMatch(pseudo, el, caller); | ||
specificity[0] += pseudoRes.specificity[0]; | ||
specificity[1] += pseudoRes.specificity[1]; | ||
specificity[2] += pseudoRes.specificity[2]; | ||
if (!pseudoRes.matched) { | ||
matched = false; | ||
} | ||
} | ||
return true; | ||
if (this.tag && this.tag.type === 'tag') { | ||
specificity[2] += 1; | ||
if (this.tag.value.toLowerCase() !== el.nodeName.toLowerCase()) { | ||
matched = false; | ||
} | ||
} | ||
return { | ||
specificity, | ||
matched, | ||
}; | ||
} | ||
@@ -270,10 +389,17 @@ add(selector) { | ||
const ruleset = new Ruleset(pseudo.nodes); | ||
let parent = el.parentNode; | ||
const specificity = getSpecificity(ruleset.match(el, caller)); | ||
let parent = el.getParentElement(); | ||
while (parent) { | ||
if (ruleset.match(parent, caller)) { | ||
return true; | ||
if (ruleset.match(parent, caller).some(r => r.matched)) { | ||
return { | ||
specificity, | ||
matched: true, | ||
}; | ||
} | ||
parent = parent.parentNode; | ||
parent = parent.getParentElement(); | ||
} | ||
return false; | ||
return { | ||
specificity, | ||
matched: false, | ||
}; | ||
} | ||
@@ -285,33 +411,62 @@ /** | ||
const ruleset = new Ruleset(pseudo.nodes); | ||
if (ruleset.match(el, caller)) { | ||
return false; | ||
} | ||
break; | ||
const resList = ruleset.match(el, caller); | ||
const specificity = getSpecificity(resList); | ||
const matched = resList.every(r => !r.matched); | ||
return { | ||
specificity, | ||
matched, | ||
}; | ||
} | ||
case ':is': { | ||
const ruleset = new Ruleset(pseudo.nodes); | ||
if (!ruleset.match(el, caller)) { | ||
return false; | ||
} | ||
break; | ||
const resList = ruleset.match(el, caller); | ||
const specificity = getSpecificity(resList); | ||
const matched = resList.some(r => r.matched); | ||
return { | ||
specificity, | ||
matched, | ||
}; | ||
} | ||
case ':has': { | ||
const ruleset = new Ruleset(pseudo.nodes); | ||
const specificity = getSpecificity(ruleset.match(el, caller)); | ||
const descendants = getDescendants(el); | ||
if (!descendants.some(desc => ruleset.match(desc, caller))) { | ||
return false; | ||
} | ||
break; | ||
const matched = descendants.some(desc => ruleset.match(desc, caller).some(m => m.matched)); | ||
return { | ||
specificity, | ||
matched, | ||
}; | ||
} | ||
case ':where': { | ||
const ruleset = new Ruleset(pseudo.nodes); | ||
const resList = ruleset.match(el, caller); | ||
const matched = resList.some(r => r.matched); | ||
return { | ||
specificity: [0, 0, 0], | ||
matched, | ||
}; | ||
} | ||
case ':scope': { | ||
if (!isScope(el, caller)) { | ||
return false; | ||
return { | ||
specificity: [0, 1, 0], | ||
matched: false, | ||
}; | ||
} | ||
break; | ||
return { | ||
specificity: [0, 1, 0], | ||
matched: true, | ||
}; | ||
} | ||
case ':root': { | ||
if (!(!el.isInFragmentDocument && el.parentNode === null)) { | ||
return false; | ||
return { | ||
specificity: [0, 1, 0], | ||
matched: false, | ||
}; | ||
} | ||
break; | ||
return { | ||
specificity: [0, 1, 0], | ||
matched: true, | ||
}; | ||
} | ||
@@ -349,3 +504,2 @@ case ':enable': | ||
} | ||
case ':where': | ||
case ':dir': | ||
@@ -373,3 +527,2 @@ case ':lang': | ||
} | ||
return true; | ||
} | ||
@@ -382,1 +535,19 @@ function isScope(el, caller) { | ||
} | ||
function getSpecificity(result) { | ||
let specificity; | ||
for (const res of result) { | ||
if (specificity) { | ||
const order = compareSpecificity(specificity, res.specificity); | ||
if (order === -1) { | ||
specificity = res.specificity; | ||
} | ||
} | ||
else { | ||
specificity = res.specificity; | ||
} | ||
} | ||
if (!specificity) { | ||
throw new Error('Result is empty'); | ||
} | ||
return specificity; | ||
} |
@@ -0,1 +1,2 @@ | ||
import type { Specificity } from './helper/selector'; | ||
import type { AnonymousNode } from './types'; | ||
@@ -6,3 +7,3 @@ import type { AnyRule } from '@markuplint/ml-config'; | ||
from: RuleType; | ||
index: number; | ||
specificity: Specificity; | ||
rule: AnyRule; | ||
@@ -9,0 +10,0 @@ }; |
@@ -7,2 +7,3 @@ "use strict"; | ||
const debug_1 = require("../debug"); | ||
const selector_1 = require("./helper/selector"); | ||
const ruleMapperLog = debug_1.log.extend('rule-mapper'); | ||
@@ -18,16 +19,21 @@ const ruleMapperNodeLog = ruleMapperLog.extend('node'); | ||
set(node, ruleName, rule) { | ||
if (node.type === 'ElementCloseTag') { | ||
return; | ||
} | ||
const rules = (0, tslib_1.__classPrivateFieldGet)(this, _RuleMapper_ruleMap, "f").get(node.uuid) || {}; | ||
const currentRule = rules[ruleName]; | ||
if (currentRule) { | ||
if (currentRule.index <= rule.index) { | ||
ruleMapperLog('Unset %o from %s', currentRule, node); | ||
} | ||
else { | ||
ruleMapperLog("Don't set %o (%d vs %d)", rule, currentRule.index, rule.index); | ||
const order = (0, selector_1.compareSpecificity)(currentRule.specificity, rule.specificity); | ||
if (order === 1) { | ||
ruleMapperLog("Don't set %o ([%s] vs [%s])", rule, currentRule.specificity, rule.specificity); | ||
return; | ||
} | ||
ruleMapperLog('Unset %o from %s', currentRule, node); | ||
} | ||
rules[ruleName] = rule; | ||
(0, tslib_1.__classPrivateFieldGet)(this, _RuleMapper_ruleMap, "f").set(node.uuid, rules); | ||
ruleMapperLog('Set %o to %s', rule, node); | ||
if (node.type === 'Element' && node.closeTag) { | ||
(0, tslib_1.__classPrivateFieldGet)(this, _RuleMapper_ruleMap, "f").set(node.closeTag.uuid, rules); | ||
} | ||
ruleMapperLog('Set to %s from %s (%o): %O', node.raw, rule.from, rule.specificity, rule.rule); | ||
} | ||
@@ -41,9 +47,9 @@ apply() { | ||
} | ||
const shash = node.type === 'ElementCloseTag' ? '/' : ''; | ||
const slash = node.type === 'ElementCloseTag' ? '/' : ''; | ||
const nodeName = 'nodeName' in node ? node.nodeName : `#${node.type}`; | ||
ruleMapperNodeLog('<%s%s>', shash, nodeName); | ||
ruleMapperNodeLog('<%s%s>', slash, nodeName); | ||
Object.keys(rules).forEach(ruleName => { | ||
const rule = rules[ruleName]; | ||
node.rules[ruleName] = rule.rule; | ||
ruleMapperNodeRuleLog('[from: %s(%d)] %s: %o', rule.from, rule.index, ruleName, rule.rule); | ||
ruleMapperNodeRuleLog('[from: %s(%s)] %s: %o', rule.from, rule.specificity, ruleName, rule.rule); | ||
}); | ||
@@ -50,0 +56,0 @@ }); |
@@ -43,2 +43,3 @@ import type { AnonymousNode, Document } from '../'; | ||
isDescendantByUUIDList(uuidList: string[]): boolean; | ||
toNormalizeString(): string; | ||
} |
"use strict"; | ||
var _MLDOMAbstractElement_fixedNodeName; | ||
var _MLDOMAbstractElement_fixedNodeName, _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache, _MLDOMAbstractElement_normalizedString; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -18,2 +18,4 @@ const tslib_1 = require("tslib"); | ||
_MLDOMAbstractElement_fixedNodeName.set(this, void 0); | ||
_MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache.set(this, null); | ||
_MLDOMAbstractElement_normalizedString.set(this, null); | ||
this.nodeName = astNode.nodeName; | ||
@@ -117,3 +119,3 @@ (0, tslib_1.__classPrivateFieldSet)(this, _MLDOMAbstractElement_fixedNodeName, astNode.nodeName, "f"); | ||
} | ||
el = el.parentNode; | ||
el = el.getParentElement(); | ||
} while (el !== null && el.type === 'Element'); | ||
@@ -150,3 +152,3 @@ return null; | ||
matches(selector) { | ||
return (0, helper_1.createSelector)(selector).match(this); | ||
return !!(0, helper_1.createSelector)(selector).match(this); | ||
} | ||
@@ -157,2 +159,5 @@ fixNodeName(name) { | ||
getChildElementsAndTextNodeWithoutWhitespaces() { | ||
if ((0, tslib_1.__classPrivateFieldGet)(this, _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache, "f")) { | ||
return (0, tslib_1.__classPrivateFieldGet)(this, _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache, "f"); | ||
} | ||
const filteredNodes = []; | ||
@@ -171,2 +176,3 @@ this.childNodes.forEach(node => { | ||
}); | ||
(0, tslib_1.__classPrivateFieldSet)(this, _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache, filteredNodes, "f"); | ||
return filteredNodes; | ||
@@ -181,3 +187,3 @@ } | ||
isDescendantByUUIDList(uuidList) { | ||
let el = this.parentNode; | ||
let el = this.getParentElement(); | ||
if (el === null) { | ||
@@ -190,8 +196,27 @@ return false; | ||
} | ||
el = el.parentNode; | ||
el = el.getParentElement(); | ||
} while (el !== null && el.type === 'Element'); | ||
return false; | ||
} | ||
toNormalizeString() { | ||
if ((0, tslib_1.__classPrivateFieldGet)(this, _MLDOMAbstractElement_normalizedString, "f")) { | ||
return (0, tslib_1.__classPrivateFieldGet)(this, _MLDOMAbstractElement_normalizedString, "f"); | ||
} | ||
const children = this.getChildElementsAndTextNodeWithoutWhitespaces(); | ||
const attrs = this.attributes.map(attr => attr.toNormalizeString()); | ||
const attrString = attrs.length ? ' ' + attrs.join('') : ''; | ||
const startTag = `<${this.nodeName}${attrString}>`; | ||
const childNodes = children.map(node => { | ||
if (node.type === 'Element') { | ||
return node.toNormalizeString(); | ||
} | ||
return node.originRaw; | ||
}); | ||
const endTag = `</${this.nodeName}>`; | ||
const normalizedString = `${startTag}${childNodes.join('')}${endTag}`; | ||
(0, tslib_1.__classPrivateFieldSet)(this, _MLDOMAbstractElement_normalizedString, normalizedString, "f"); | ||
return normalizedString; | ||
} | ||
} | ||
exports.default = MLDOMAbstractElement; | ||
_MLDOMAbstractElement_fixedNodeName = new WeakMap(); | ||
_MLDOMAbstractElement_fixedNodeName = new WeakMap(), _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache = new WeakMap(), _MLDOMAbstractElement_normalizedString = new WeakMap(); |
@@ -39,2 +39,3 @@ import type { MLASTHTMLAttr, MLToken } from '@markuplint/ml-ast'; | ||
toString(withSpace?: boolean): string; | ||
toNormalizeString(): string; | ||
} |
@@ -72,3 +72,10 @@ "use strict"; | ||
} | ||
toNormalizeString() { | ||
return (this.name.originRaw + | ||
this.equal.originRaw + | ||
this.startQuote.originRaw + | ||
this.value.originRaw + | ||
this.endQuote.originRaw); | ||
} | ||
} | ||
exports.default = MLDOMAttribute; |
import type { Document } from '../'; | ||
import type { RuleInfo } from '../../'; | ||
import type { AnonymousNode, IMLDOMNode, NodeType } from '../types'; | ||
import type { MLDOMElement, MLDOMOmittedElement } from './'; | ||
import type { MLDOMElement, MLDOMOmittedElement, MLDOMPreprocessorSpecificBlock } from './'; | ||
import type { MLASTAbstructNode } from '@markuplint/ml-ast'; | ||
@@ -14,11 +14,12 @@ import type { AnyRule, RuleConfigValue } from '@markuplint/ml-config'; | ||
constructor(astNode: A, document: Document<T, O>); | ||
get parentNode(): MLDOMElement<T, O> | MLDOMOmittedElement<T, O> | null; | ||
get parentNode(): MLDOMElement<T, O> | MLDOMOmittedElement<T, O> | MLDOMPreprocessorSpecificBlock<T, O> | null; | ||
get prevNode(): AnonymousNode<T, O> | null; | ||
get nextNode(): AnonymousNode<T, O> | null; | ||
get syntaxicalParentNode(): MLDOMElement<T, O> | null; | ||
get syntaxicalParentNode(): MLDOMElement<T, O> | MLDOMPreprocessorSpecificBlock<T, O> | null; | ||
get prevToken(): AnonymousNode<T, O> | null; | ||
get nodeStore(): import("../helper").NodeStore; | ||
get rule(): RuleInfo<T, O>; | ||
getParentElement(): MLDOMElement<T, O> | MLDOMOmittedElement<T, O> | null; | ||
is(type: NodeType): boolean; | ||
toString(): string; | ||
is(type: NodeType): boolean; | ||
get rule(): RuleInfo<T, O>; | ||
} |
@@ -73,8 +73,2 @@ "use strict"; | ||
} | ||
toString() { | ||
return this.raw; | ||
} | ||
is(type) { | ||
return this.type === type; | ||
} | ||
get rule() { | ||
@@ -86,9 +80,26 @@ if (!(0, tslib_1.__classPrivateFieldGet)(this, _MLDOMNode_doc, "f").currentRule) { | ||
const rule = this.rules[name]; | ||
if (rule == null) { | ||
throw new Error('Invalid call "rule" property.'); | ||
} | ||
return (0, tslib_1.__classPrivateFieldGet)(this, _MLDOMNode_doc, "f").currentRule.optimizeOption(rule); | ||
} | ||
getParentElement() { | ||
let parent = this.parentNode; | ||
if (!parent) { | ||
return null; | ||
} | ||
while (parent) { | ||
if (parent.type === 'PSBlock') { | ||
parent = parent.parentNode; | ||
continue; | ||
} | ||
return parent; | ||
} | ||
return null; | ||
} | ||
is(type) { | ||
return this.type === type; | ||
} | ||
toString() { | ||
return this.raw; | ||
} | ||
} | ||
exports.default = MLDOMNode; | ||
_MLDOMNode_doc = new WeakMap(), _MLDOMNode_prevToken = new WeakMap(); |
@@ -23,2 +23,3 @@ import type { MLASTPreprocessorSpecificAttr } from '@markuplint/ml-ast'; | ||
toString(): string; | ||
toNormalizeString(): string; | ||
} |
@@ -33,3 +33,6 @@ "use strict"; | ||
} | ||
toNormalizeString() { | ||
return this.raw; | ||
} | ||
} | ||
exports.default = MLDOMPreprocessorSpecificAttribute; |
@@ -0,5 +1,6 @@ | ||
import type { Ruleset } from '..'; | ||
import type Document from '../ml-dom/document'; | ||
import type { RuleSeed } from './types'; | ||
import type { LocaleSet } from '@markuplint/i18n'; | ||
import type { RuleConfig, RuleConfigValue, RuleInfo, Severity, Violation } from '@markuplint/ml-config'; | ||
import type { GlobalRuleInfo, RuleConfig, RuleConfigValue, RuleInfo, Rules, Severity, Violation } from '@markuplint/ml-config'; | ||
export declare class MLRule<T extends RuleConfigValue, O = null> { | ||
@@ -25,4 +26,6 @@ #private; | ||
verify(document: Document<T, O>, locale: LocaleSet, globalRule: RuleInfo<T, O>, fix: boolean): Promise<Violation[]>; | ||
optimizeOption(configSettings: T | RuleConfig<T, O>): RuleInfo<T, O>; | ||
getRuleInfo(ruleSet: Ruleset, ruleName: string): GlobalRuleInfo<T, O>; | ||
_optimize(rules: Rules | undefined, ruleName: string): RuleInfo<T, O>; | ||
optimizeOption(configSettings: T | RuleConfig<T, O> | undefined): RuleInfo<T, O>; | ||
} | ||
export declare type AnyMLRule = MLRule<RuleConfigValue, unknown>; |
@@ -9,2 +9,3 @@ "use strict"; | ||
constructor(o) { | ||
var _a, _b; | ||
_MLRule_v.set(this, void 0); | ||
@@ -14,4 +15,4 @@ _MLRule_f.set(this, void 0); | ||
this.defaultServerity = o.defaultServerity || 'error'; | ||
this.defaultValue = o.defaultValue; | ||
this.defaultOptions = o.defaultOptions; | ||
this.defaultValue = (_a = o.defaultValue) !== null && _a !== void 0 ? _a : true; | ||
this.defaultOptions = (_b = o.defaultOptions) !== null && _b !== void 0 ? _b : null; | ||
(0, tslib_1.__classPrivateFieldSet)(this, _MLRule_v, o.verify, "f"); | ||
@@ -84,4 +85,17 @@ (0, tslib_1.__classPrivateFieldSet)(this, _MLRule_f, o.fix, "f"); | ||
} | ||
getRuleInfo(ruleSet, ruleName) { | ||
const info = this._optimize(ruleSet.rules, ruleName); | ||
return { | ||
...info, | ||
nodeRules: ruleSet.nodeRules.map(r => this._optimize(r.rules, ruleName)).filter(r => !r.disabled), | ||
childNodeRules: ruleSet.childNodeRules.map(r => this._optimize(r.rules, ruleName)).filter(r => !r.disabled), | ||
}; | ||
} | ||
_optimize(rules, ruleName) { | ||
const rule = (rules && rules[ruleName]) || false; | ||
const info = this.optimizeOption(rule); | ||
return info; | ||
} | ||
optimizeOption(configSettings) { | ||
if (typeof configSettings === 'boolean') { | ||
if (configSettings === undefined || typeof configSettings === 'boolean') { | ||
return { | ||
@@ -88,0 +102,0 @@ disabled: !configSettings, |
import type { MLRuleContext } from './ml-rule-context'; | ||
import type { RuleConfigValue, Severity } from '@markuplint/ml-config'; | ||
export declare type RuleSeed<T extends RuleConfigValue, O = null> = { | ||
export declare type RuleSeed<T extends RuleConfigValue = boolean, O = null> = { | ||
defaultServerity?: Severity; | ||
defaultValue: T; | ||
defaultOptions: O; | ||
defaultValue?: T; | ||
defaultOptions?: O; | ||
verify(context: ReturnType<MLRuleContext<T, O>['provide']>): void | Promise<void>; | ||
@@ -8,0 +8,0 @@ fix?(context: ReturnType<MLRuleContext<T, O>['provide']>): void | Promise<void>; |
{ | ||
"rules": { | ||
"attr-duplication": true, | ||
"attr-equal-space-after": false, | ||
"attr-equal-space-before": false, | ||
"attr-spacing": false, | ||
"attr-value-quotes": false, | ||
"case-sensitive-attr-name": false, | ||
"case-sensitive-tag-name": false, | ||
"character-reference": true, | ||
"class-naming": false, | ||
"deprecated-attr": true, | ||
"deprecated-element": true, | ||
"disallowed-element": ["hgroup"], | ||
"doctype": true, | ||
"id-duplication": true, | ||
"indentation": false, | ||
"ineffective-attr": true, | ||
"invalid-attr": { | ||
"option": { | ||
"attrs": { | ||
"autofocus": { | ||
"disallowed": true | ||
}, | ||
"accesskey": { | ||
"disallowed": true | ||
} | ||
} | ||
} | ||
}, | ||
"landmark-roles": true, | ||
"no-boolean-attr-value": true, | ||
"no-default-value": false, | ||
"no-hard-code-id": false, | ||
"no-refer-to-non-existent-id": true, | ||
"no-use-event-handler-attr": false, | ||
"permitted-contents": true, | ||
"required-attr": true, | ||
"invalid-attr": true, | ||
"landmark-roles": true, | ||
"required-element": false, | ||
"required-h1": true, | ||
@@ -18,3 +45,3 @@ "wai-aria": true | ||
{ | ||
"selector": "html", | ||
"selector": ":where(html)", | ||
"rules": { | ||
@@ -25,4 +52,42 @@ "required-attr": ["lang"] | ||
{ | ||
"selector": "script[src]", | ||
"selector": ":where(head)", | ||
"rules": { | ||
"required-element": ["meta[charset=\"UTF-8\" i]"] | ||
} | ||
}, | ||
{ | ||
"selector": ":where(meta[property])", | ||
"rules": { | ||
"invalid-attr": { | ||
"option": { | ||
"attrs": { | ||
"property": { | ||
"pattern": "/^og:[a-z_]+/" | ||
}, | ||
"content": { | ||
"type": "NoEmptyAny" | ||
} | ||
} | ||
} | ||
}, | ||
"required-attr": false | ||
} | ||
}, | ||
{ | ||
"selector": ":where(meta[itemprop])", | ||
"rules": { | ||
"invalid-attr": { | ||
"option": { | ||
"attrs": { | ||
"content": { | ||
"type": "NoEmptyAny" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
{ | ||
"selector": ":where(script[src])", | ||
"rules": { | ||
"required-attr": ["defer"] | ||
@@ -32,26 +97,94 @@ } | ||
{ | ||
"selector": "img", | ||
"selector": "h1, h2, h3, h4, h5, h6", | ||
"rules": { | ||
"required-attr": ["decoding", "width", "height", "alt"] | ||
"disallowed-element": { | ||
"value": ["small"], | ||
"reason": "The small element must not be used for subheadings. https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-small-element" | ||
} | ||
} | ||
}, | ||
{ | ||
"selector": "iframe", | ||
"selector": ":where(img)", | ||
"rules": { | ||
"required-attr": ["loading", "title"] | ||
"required-attr": [ | ||
"width", | ||
"height", | ||
"alt", | ||
{ | ||
"name": "decoding", | ||
"value": "async" | ||
} | ||
] | ||
} | ||
}, | ||
{ | ||
"selector": "a[target=_blank], area[target=_blank]", | ||
"selector": ":where(iframe)", | ||
"rules": { | ||
"required-attr": ["rel"] | ||
"required-attr": [ | ||
"title", | ||
{ | ||
"name": "loading", | ||
"value": "lazy" | ||
} | ||
] | ||
} | ||
}, | ||
{ | ||
"selector": "abbr", | ||
"selector": ":where(a[target=_blank], area[target=_blank])", | ||
"rules": { | ||
"required-attr": [ | ||
{ | ||
"name": "rel", | ||
"value": "noreferrer" | ||
} | ||
] | ||
} | ||
}, | ||
{ | ||
"selector": ":where(abbr)", | ||
"rules": { | ||
"required-attr": ["title"] | ||
} | ||
}, | ||
{ | ||
"selector": ":where(video, audio)", | ||
"rules": { | ||
"required-element": ["track"] | ||
} | ||
}, | ||
{ | ||
"selector": ":where(video[autoplay])", | ||
"rules": { | ||
"required-attr": ["muted"] | ||
} | ||
}, | ||
{ | ||
"selector": ":where(th, td)", | ||
"rules": { | ||
"invalid-attr": { | ||
"option": { | ||
"attrs": { | ||
"span": { | ||
"disallowed": false | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
{ | ||
"selector": ":where(body :not(button))", | ||
"rules": { | ||
"invalid-attr": { | ||
"option": { | ||
"attrs": { | ||
"onclick": { | ||
"disallowed": false | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
] | ||
} |
{ | ||
"name": "@markuplint/ml-core", | ||
"version": "2.0.0-rc.0", | ||
"version": "2.0.0-rc.1", | ||
"description": "The core module of markuplint", | ||
@@ -20,6 +20,6 @@ "repository": "git@github.com:markuplint/markuplint.git", | ||
"dependencies": { | ||
"@markuplint/i18n": "2.0.0-rc.0", | ||
"@markuplint/ml-ast": "2.0.0-rc.0", | ||
"@markuplint/ml-config": "2.0.0-rc.0", | ||
"@markuplint/ml-spec": "2.0.0-rc.0", | ||
"@markuplint/i18n": "2.0.0-rc.1", | ||
"@markuplint/ml-ast": "2.0.0-rc.1", | ||
"@markuplint/ml-config": "2.0.0-rc.1", | ||
"@markuplint/ml-spec": "2.0.0-rc.1", | ||
"debug": "^4.3.2", | ||
@@ -30,6 +30,6 @@ "postcss-selector-parser": "^6.0.6", | ||
"devDependencies": { | ||
"@markuplint/html-parser": "2.0.0-rc.0", | ||
"@markuplint/html-parser": "2.0.0-rc.1", | ||
"@types/debug": "^4.1.7" | ||
}, | ||
"gitHead": "67134f1addbdd271ad1d425de8f296d1875dbf25" | ||
"gitHead": "34dbaf8bc5ee499315a32857c59b9c6f6bb73594" | ||
} |
Sorry, the diff of this file is not supported yet
194403
3444
+ Added@markuplint/i18n@2.0.0-rc.1(transitive)
+ Added@markuplint/ml-ast@2.0.0-rc.1(transitive)
+ Added@markuplint/ml-config@2.0.0-rc.1(transitive)
+ Added@markuplint/ml-spec@2.0.0-rc.1(transitive)
- Removed@markuplint/i18n@2.0.0-rc.0(transitive)
- Removed@markuplint/ml-ast@2.0.0-rc.0(transitive)
- Removed@markuplint/ml-config@2.0.0-rc.0(transitive)
- Removed@markuplint/ml-spec@2.0.0-rc.0(transitive)
Updated@markuplint/i18n@2.0.0-rc.1