@markuplint/selector
Advanced tools
Comparing version 3.0.0-alpha.2105 to 3.0.0-canary.2421
@@ -5,2 +5,4 @@ "use strict"; | ||
const aria_pseudo_class_1 = require("./extended-selector/aria-pseudo-class"); | ||
const aria_role_pseudo_class_1 = require("./extended-selector/aria-role-pseudo-class"); | ||
const content_model_pseudo_class_1 = require("./extended-selector/content-model-pseudo-class"); | ||
const selector_1 = require("./selector"); | ||
@@ -14,3 +16,5 @@ const caches = new Map(); | ||
instance = new selector_1.Selector(selector, { | ||
aria: (0, aria_pseudo_class_1.ariaPseudoClass)(specs), | ||
model: (0, content_model_pseudo_class_1.contentModelPseudoClass)(specs), | ||
aria: (0, aria_pseudo_class_1.ariaPseudoClass)(), | ||
role: (0, aria_role_pseudo_class_1.ariaRolePseudoClass)(specs), | ||
}); | ||
@@ -17,0 +21,0 @@ caches.set(selector, instance); |
import type { SelectorResult } from '../types'; | ||
import type { MLMLSpec } from '@markuplint/ml-spec'; | ||
export declare function ariaPseudoClass(specs: MLMLSpec): (content: string) => (el: Element) => SelectorResult; | ||
/** | ||
* Version Syntax is not support yet. | ||
*/ | ||
export declare function ariaPseudoClass(): (content: string) => (el: Element) => SelectorResult; |
@@ -5,29 +5,38 @@ "use strict"; | ||
const ml_spec_1 = require("@markuplint/ml-spec"); | ||
const roleIsRegxp = /^roleis/gi; | ||
function ariaPseudoClass(specs) { | ||
/** | ||
* Version Syntax is not support yet. | ||
*/ | ||
function ariaPseudoClass() { | ||
return (content) => (el) => { | ||
var _a, _b; | ||
const aria = ariaPseudoClassParser(content); | ||
const name = (0, ml_spec_1.getAccname)(el); | ||
switch (aria.type) { | ||
case 'hasName': { | ||
const name = (0, ml_spec_1.getAccname)(el); | ||
if (name) { | ||
return { | ||
specificity: [0, 1, 0], | ||
matched: true, | ||
nodes: [el], | ||
has: [], | ||
}; | ||
} | ||
return { | ||
specificity: [0, 1, 0], | ||
matched: !!name, | ||
matched: false, | ||
}; | ||
} | ||
case 'hasNoName': { | ||
const name = (0, ml_spec_1.getAccname)(el); | ||
if (!name) { | ||
return { | ||
specificity: [0, 1, 0], | ||
matched: true, | ||
nodes: [el], | ||
has: [], | ||
}; | ||
} | ||
return { | ||
specificity: [0, 1, 0], | ||
matched: !name, | ||
matched: false, | ||
}; | ||
} | ||
case 'roleIs': { | ||
const computed = (0, ml_spec_1.getComputedRole)(specs, el, (_a = aria.version) !== null && _a !== void 0 ? _a : '1.2'); | ||
return { | ||
specificity: [0, 1, 0], | ||
matched: ((_b = computed.role) === null || _b === void 0 ? void 0 : _b.name) === aria.role, | ||
}; | ||
} | ||
} | ||
@@ -55,14 +64,3 @@ }; | ||
} | ||
if (roleIsRegxp.test(query)) { | ||
const role = query.replace(roleIsRegxp, ''); | ||
if (!role) { | ||
throw new SyntaxError(`Unsupported syntax: ${syntax}`); | ||
} | ||
return { | ||
type: 'roleIs', | ||
role, | ||
version, | ||
}; | ||
} | ||
throw new SyntaxError(`Unsupported syntax: ${syntax}`); | ||
} |
import type { Specificity, RegexSelector } from './types'; | ||
export declare type SelectorMatches = SelectorMatched | SelectorUnmatched; | ||
declare type SelectorMatched = { | ||
export type SelectorMatches = SelectorMatched | SelectorUnmatched; | ||
type SelectorMatched = { | ||
matched: true; | ||
@@ -9,3 +9,3 @@ selector: string; | ||
}; | ||
declare type SelectorUnmatched = { | ||
type SelectorUnmatched = { | ||
matched: false; | ||
@@ -12,0 +12,0 @@ }; |
"use strict"; | ||
var _SelectorTarget_combinedFrom, _SelectorTarget_selector; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.matchSelector = void 0; | ||
const tslib_1 = require("tslib"); | ||
const is_1 = require("./is"); | ||
@@ -43,14 +45,15 @@ const regex_selector_matches_1 = require("./regex-selector-matches"); | ||
constructor(selector) { | ||
this._combinatedFrom = null; | ||
this._selector = selector; | ||
_SelectorTarget_combinedFrom.set(this, null); | ||
_SelectorTarget_selector.set(this, void 0); | ||
tslib_1.__classPrivateFieldSet(this, _SelectorTarget_selector, selector, "f"); | ||
} | ||
from(target, combinator) { | ||
this._combinatedFrom = { target, combinator }; | ||
tslib_1.__classPrivateFieldSet(this, _SelectorTarget_combinedFrom, { target, combinator }, "f"); | ||
} | ||
match(el) { | ||
const unitCheck = this._matchWithoutCombinateChecking(el); | ||
const unitCheck = this._matchWithoutCombineChecking(el); | ||
if (!unitCheck.matched) { | ||
return unitCheck; | ||
} | ||
if (!this._combinatedFrom) { | ||
if (!tslib_1.__classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f")) { | ||
return unitCheck; | ||
@@ -61,3 +64,3 @@ } | ||
} | ||
const { target, combinator } = this._combinatedFrom; | ||
const { target, combinator } = tslib_1.__classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f"); | ||
switch (combinator) { | ||
@@ -137,11 +140,12 @@ // Descendant combinator | ||
default: { | ||
throw new Error(`Unsupported ${this._combinatedFrom.combinator} combinator in selector`); | ||
throw new Error(`Unsupported ${tslib_1.__classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f").combinator} combinator in selector`); | ||
} | ||
} | ||
} | ||
_matchWithoutCombinateChecking(el) { | ||
return uncombinatedRegexSelect(el, this._selector); | ||
_matchWithoutCombineChecking(el) { | ||
return uncombinedRegexSelect(el, tslib_1.__classPrivateFieldGet(this, _SelectorTarget_selector, "f")); | ||
} | ||
} | ||
function uncombinatedRegexSelect(el, selector) { | ||
_SelectorTarget_combinedFrom = new WeakMap(), _SelectorTarget_selector = new WeakMap(); | ||
function uncombinedRegexSelect(el, selector) { | ||
if (!(0, is_1.isElement)(el)) { | ||
@@ -148,0 +152,0 @@ return { |
@@ -6,3 +6,3 @@ "use strict"; | ||
const res = {}; | ||
const pattern = toRegxp(reg); | ||
const pattern = toRegexp(reg); | ||
const regex = new RegExp(pattern instanceof RegExp ? pattern : `^${pattern.trim()}$`, ignoreCase ? 'i' : undefined); | ||
@@ -20,3 +20,3 @@ const matched = regex.exec(raw); | ||
exports.regexSelectorMatches = regexSelectorMatches; | ||
function toRegxp(pattern) { | ||
function toRegexp(pattern) { | ||
const matched = pattern.match(/^\/(.+)\/([ig]*)$/i); | ||
@@ -23,0 +23,0 @@ if (matched) { |
import type { SelectorResult, Specificity } from './types'; | ||
declare type ExtendedPseudoClass = Record<string, (content: string) => (el: Element) => SelectorResult>; | ||
type ExtendedPseudoClass = Record<string, (content: string) => (el: Element) => SelectorResult>; | ||
export declare class Selector { | ||
@@ -7,3 +7,4 @@ #private; | ||
match(el: Node, scope?: ParentNode | null): Specificity | false; | ||
search(el: Node, scope?: ParentNode | null): SelectorResult[]; | ||
} | ||
export {}; |
"use strict"; | ||
var _Selector_ruleset, _Ruleset_selectorGroup, _StructuredSelector_selector, _StructuredSelector_edge, _SelectorTarget_extended, _SelectorTarget_combinatedFrom, _SelectorTarget_isAdded; | ||
var _Selector_ruleset, _Ruleset_selectorGroup, _StructuredSelector_edge, _StructuredSelector_selector, _SelectorTarget_combinedFrom, _SelectorTarget_extended, _SelectorTarget_isAdded; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Selector = void 0; | ||
const tslib_1 = require("tslib"); | ||
const ml_spec_1 = require("@markuplint/ml-spec"); | ||
const postcss_selector_parser_1 = tslib_1.__importStar(require("postcss-selector-parser")); | ||
@@ -19,3 +20,3 @@ const compare_specificity_1 = require("./compare-specificity"); | ||
match(el, scope = (0, is_1.isElement)(el) ? el : null) { | ||
const results = tslib_1.__classPrivateFieldGet(this, _Selector_ruleset, "f").match(el, scope); | ||
const results = this.search(el, scope); | ||
for (const result of results) { | ||
@@ -28,2 +29,5 @@ if (result.matched) { | ||
} | ||
search(el, scope = (0, is_1.isElement)(el) ? el : null) { | ||
return tslib_1.__classPrivateFieldGet(this, _Selector_ruleset, "f").match(el, scope); | ||
} | ||
} | ||
@@ -33,2 +37,17 @@ exports.Selector = Selector; | ||
class Ruleset { | ||
static parse(selector, extended) { | ||
const selectors = []; | ||
try { | ||
(0, postcss_selector_parser_1.default)(root => { | ||
selectors.push(...root.nodes); | ||
}).processSync(selector); | ||
} | ||
catch (e) { | ||
if (e instanceof Error) { | ||
throw new Error(`${e.message} At the selector: "${selector}"`); | ||
} | ||
throw e; | ||
} | ||
return new Ruleset(selectors, extended, 0); | ||
} | ||
constructor(selectors, extended, depth) { | ||
@@ -45,9 +64,2 @@ _Ruleset_selectorGroup.set(this, []); | ||
} | ||
static parse(selector, extended) { | ||
const selectors = []; | ||
(0, postcss_selector_parser_1.default)(root => { | ||
selectors.push(...root.nodes); | ||
}).processSync(selector); | ||
return new Ruleset(selectors, extended, 0); | ||
} | ||
match(el, scope) { | ||
@@ -66,4 +78,4 @@ (0, debug_1.log)('<%s> (%s)', (0, is_1.isElement)(el) ? el.localName : el.nodeName, scope ? ((0, is_1.isElement)(scope) ? scope.localName : scope.nodeName) : null); | ||
constructor(selector, depth, extended) { | ||
_StructuredSelector_edge.set(this, void 0); | ||
_StructuredSelector_selector.set(this, void 0); | ||
_StructuredSelector_edge.set(this, void 0); | ||
tslib_1.__classPrivateFieldSet(this, _StructuredSelector_selector, selector, "f"); | ||
@@ -79,5 +91,5 @@ tslib_1.__classPrivateFieldSet(this, _StructuredSelector_edge, new SelectorTarget(extended, depth), "f"); | ||
case 'combinator': { | ||
const combinatedTarget = new SelectorTarget(extended, depth); | ||
combinatedTarget.from(tslib_1.__classPrivateFieldGet(this, _StructuredSelector_edge, "f"), node); | ||
tslib_1.__classPrivateFieldSet(this, _StructuredSelector_edge, combinatedTarget, "f"); | ||
const combinedTarget = new SelectorTarget(extended, depth); | ||
combinedTarget.from(tslib_1.__classPrivateFieldGet(this, _StructuredSelector_edge, "f"), node); | ||
tslib_1.__classPrivateFieldSet(this, _StructuredSelector_edge, combinedTarget, "f"); | ||
break; | ||
@@ -108,11 +120,11 @@ } | ||
} | ||
_StructuredSelector_selector = new WeakMap(), _StructuredSelector_edge = new WeakMap(); | ||
_StructuredSelector_edge = new WeakMap(), _StructuredSelector_selector = new WeakMap(); | ||
class SelectorTarget { | ||
constructor(extended, depth) { | ||
_SelectorTarget_extended.set(this, void 0); | ||
_SelectorTarget_combinatedFrom.set(this, null); | ||
_SelectorTarget_isAdded.set(this, false); | ||
this.attr = []; | ||
this.class = []; | ||
_SelectorTarget_combinedFrom.set(this, null); | ||
_SelectorTarget_extended.set(this, void 0); | ||
this.id = []; | ||
_SelectorTarget_isAdded.set(this, false); | ||
this.pseudo = []; | ||
@@ -150,3 +162,3 @@ this.tag = null; | ||
from(target, combinator) { | ||
tslib_1.__classPrivateFieldSet(this, _SelectorTarget_combinatedFrom, { target, combinator }, "f"); | ||
tslib_1.__classPrivateFieldSet(this, _SelectorTarget_combinedFrom, { target, combinator }, "f"); | ||
} | ||
@@ -158,3 +170,3 @@ match(el, scope, count) { | ||
const nodeName = el.nodeName; | ||
const selector = ((_a = tslib_1.__classPrivateFieldGet(this, _SelectorTarget_combinatedFrom, "f")) === null || _a === void 0 ? void 0 : _a.target.toString()) || this.toString(); | ||
const selector = ((_a = tslib_1.__classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f")) === null || _a === void 0 ? void 0 : _a.target.toString()) || this.toString(); | ||
const combinator = result.combinator ? ` ${result.combinator}` : ''; | ||
@@ -169,8 +181,18 @@ selLog('The %s element by "%s" => %s (%d)', nodeName, `${selector}${combinator}`, result.matched, count); | ||
} | ||
toString() { | ||
var _a, _b; | ||
return [ | ||
(_b = (_a = this.tag) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : '', | ||
this.id.map(id => `#${id.value}`).join(''), | ||
this.class.map(c => `.${c.value}`).join(''), | ||
this.attr.map(attr => `[${attr.toString()}]`).join(''), | ||
this.pseudo.map(pseudo => pseudo.value).join(''), | ||
].join(''); | ||
} | ||
_match(el, scope, count) { | ||
const unitCheck = this._matchWithoutCombinateChecking(el, scope); | ||
const unitCheck = this._matchWithoutCombineChecking(el, scope); | ||
if (!unitCheck.matched) { | ||
return unitCheck; | ||
} | ||
if (!tslib_1.__classPrivateFieldGet(this, _SelectorTarget_combinatedFrom, "f")) { | ||
if (!tslib_1.__classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f")) { | ||
return unitCheck; | ||
@@ -181,8 +203,10 @@ } | ||
} | ||
const { target, combinator } = tslib_1.__classPrivateFieldGet(this, _SelectorTarget_combinatedFrom, "f"); | ||
const { target, combinator } = tslib_1.__classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f"); | ||
switch (combinator.value) { | ||
// Descendant combinator | ||
case ' ': { | ||
const matchedNodes = []; | ||
const has = []; | ||
const not = []; | ||
let ancestor = el.parentElement; | ||
let matched = false; | ||
let specificity; | ||
@@ -199,4 +223,10 @@ while (ancestor) { | ||
if (res.matched) { | ||
matched = true; | ||
matchedNodes.push(...res.nodes); | ||
has.push(...res.has); | ||
} | ||
else { | ||
if (res.not) { | ||
not.push(...res.not); | ||
} | ||
} | ||
ancestor = ancestor.parentElement; | ||
@@ -212,6 +242,16 @@ } | ||
} | ||
if (matchedNodes.length) { | ||
return { | ||
combinator: '␣', | ||
specificity, | ||
matched: true, | ||
nodes: matchedNodes, | ||
has, | ||
}; | ||
} | ||
return { | ||
combinator: '␣', | ||
specificity, | ||
matched, | ||
matched: false, | ||
not, | ||
}; | ||
@@ -221,3 +261,5 @@ } | ||
case '>': { | ||
let matched; | ||
const matchedNodes = []; | ||
const has = []; | ||
const not = []; | ||
const specificity = unitCheck.specificity; | ||
@@ -230,3 +272,11 @@ const parentNode = el.parentElement; | ||
specificity[2] += res.specificity[2]; | ||
matched = res.matched; | ||
if (res.matched) { | ||
matchedNodes.push(...res.nodes); | ||
has.push(...res.has); | ||
} | ||
else { | ||
if (res.not) { | ||
not.push(...res.not); | ||
} | ||
} | ||
} | ||
@@ -238,8 +288,17 @@ else { | ||
specificity[2] += res.specificity[2]; | ||
matched = false; | ||
} | ||
if (matchedNodes.length) { | ||
return { | ||
combinator: '>', | ||
specificity, | ||
matched: true, | ||
nodes: matchedNodes, | ||
has, | ||
}; | ||
} | ||
return { | ||
combinator: '>', | ||
specificity, | ||
matched, | ||
matched: false, | ||
not, | ||
}; | ||
@@ -249,3 +308,5 @@ } | ||
case '+': { | ||
let matched; | ||
const matchedNodes = []; | ||
const has = []; | ||
const not = []; | ||
const specificity = unitCheck.specificity; | ||
@@ -257,3 +318,11 @@ if (el.previousElementSibling) { | ||
specificity[2] += res.specificity[2]; | ||
matched = res.matched; | ||
if (res.matched) { | ||
matchedNodes.push(...res.nodes); | ||
has.push(...res.has); | ||
} | ||
else { | ||
if (res.not) { | ||
not.push(...res.not); | ||
} | ||
} | ||
} | ||
@@ -265,8 +334,17 @@ else { | ||
specificity[2] += res.specificity[2]; | ||
matched = false; | ||
} | ||
if (matchedNodes.length) { | ||
return { | ||
combinator: '+', | ||
specificity, | ||
matched: true, | ||
nodes: matchedNodes, | ||
has, | ||
}; | ||
} | ||
return { | ||
combinator: '+', | ||
specificity, | ||
matched, | ||
matched: false, | ||
not, | ||
}; | ||
@@ -276,4 +354,6 @@ } | ||
case '~': { | ||
const matchedNodes = []; | ||
const has = []; | ||
const not = []; | ||
let prev = el.previousElementSibling; | ||
let matched = false; | ||
let specificity; | ||
@@ -290,4 +370,10 @@ while (prev) { | ||
if (res.matched) { | ||
matched = true; | ||
matchedNodes.push(...res.nodes); | ||
has.push(...res.has); | ||
} | ||
else { | ||
if (res.not) { | ||
not.push(...res.not); | ||
} | ||
} | ||
prev = prev.previousElementSibling; | ||
@@ -303,6 +389,16 @@ } | ||
} | ||
if (matchedNodes.length) { | ||
return { | ||
combinator: '~', | ||
specificity, | ||
matched: true, | ||
nodes: matchedNodes, | ||
has, | ||
}; | ||
} | ||
return { | ||
combinator: '~', | ||
specificity, | ||
matched, | ||
matched: false, | ||
not, | ||
}; | ||
@@ -315,17 +411,8 @@ } | ||
default: { | ||
throw new Error(`Unsupported ${tslib_1.__classPrivateFieldGet(this, _SelectorTarget_combinatedFrom, "f").combinator.value} combinator in selector`); | ||
throw new Error(`Unsupported ${tslib_1.__classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f").combinator.value} combinator in selector`); | ||
} | ||
} | ||
} | ||
toString() { | ||
var _a, _b; | ||
return [ | ||
(_b = (_a = this.tag) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : '', | ||
this.id.map(id => `#${id.value}`).join(''), | ||
this.class.map(c => `.${c.value}`).join(''), | ||
this.attr.map(attr => `[${attr.toString()}]`).join(''), | ||
this.pseudo.map(pseudo => pseudo.value).join(''), | ||
].join(''); | ||
} | ||
_matchWithoutCombinateChecking(el, scope) { | ||
_matchWithoutCombineChecking(el, scope) { | ||
var _a; | ||
const specificity = [0, 0, 0]; | ||
@@ -338,2 +425,4 @@ if (!(0, is_1.isElement)(el)) { | ||
} | ||
const has = []; | ||
const not = []; | ||
// @ts-ignore | ||
@@ -383,3 +472,7 @@ if (this.tag && this.tag._namespace) { | ||
specificity[2] += pseudoRes.specificity[2]; | ||
if (!pseudoRes.matched) { | ||
if (pseudoRes.matched) { | ||
has.push(...pseudoRes.has); | ||
} | ||
else { | ||
not.push(...((_a = pseudoRes.not) !== null && _a !== void 0 ? _a : [])); | ||
matched = false; | ||
@@ -400,14 +493,29 @@ } | ||
} | ||
if (matched) { | ||
return { | ||
specificity, | ||
matched, | ||
nodes: [el], | ||
has, | ||
}; | ||
} | ||
return { | ||
specificity, | ||
matched, | ||
not, | ||
}; | ||
} | ||
} | ||
_SelectorTarget_extended = new WeakMap(), _SelectorTarget_combinatedFrom = new WeakMap(), _SelectorTarget_isAdded = new WeakMap(); | ||
_SelectorTarget_combinedFrom = new WeakMap(), _SelectorTarget_extended = new WeakMap(), _SelectorTarget_isAdded = new WeakMap(); | ||
function attrMatch(attr, el) { | ||
return Array.from(el.attributes).some(attrOfEl => { | ||
if (attr.attribute !== attrOfEl.name) { | ||
if (attr.attribute !== attrOfEl.localName) { | ||
return false; | ||
} | ||
if (attr.namespace != null && attr.namespace !== true && attr.namespace !== '*') { | ||
const ns = (0, ml_spec_1.resolveNamespace)(attrOfEl.localName, attrOfEl.namespaceURI); | ||
if (attr.namespace !== ns.namespace) { | ||
return false; | ||
} | ||
} | ||
if (attr.value != null) { | ||
@@ -473,6 +581,9 @@ let value = attr.value; | ||
while (parent) { | ||
if (ruleset.match(parent, scope).some(r => r.matched)) { | ||
const matched = ruleset.match(parent, scope).filter((r) => r.matched); | ||
if (matched.length) { | ||
return { | ||
specificity, | ||
matched: true, | ||
nodes: [el], | ||
has: matched, | ||
}; | ||
@@ -494,6 +605,15 @@ } | ||
const specificity = getSpecificity(resList); | ||
const matched = resList.every(r => !r.matched); | ||
const not = resList.filter((r) => r.matched); | ||
if (not.length === 0) { | ||
return { | ||
specificity, | ||
matched: true, | ||
nodes: [el], | ||
has: [], | ||
}; | ||
} | ||
return { | ||
specificity, | ||
matched, | ||
matched: false, | ||
not, | ||
}; | ||
@@ -505,6 +625,8 @@ } | ||
const specificity = getSpecificity(resList); | ||
const matched = resList.some(r => r.matched); | ||
const matched = resList.filter((r) => r.matched); | ||
return { | ||
specificity, | ||
matched, | ||
matched: !!matched.length, | ||
nodes: matched.map(m => m.nodes).flat(), | ||
has: matched.map(m => m.has).flat(), | ||
}; | ||
@@ -518,13 +640,33 @@ } | ||
case '~': { | ||
const matched = getSiblings(el).some(sib => ruleset.match(sib, el).some(m => m.matched)); | ||
const has = getSiblings(el) | ||
.map(sib => ruleset.match(sib, el).filter((m) => m.matched)) | ||
.flat(); | ||
if (has.length) { | ||
return { | ||
specificity, | ||
matched: true, | ||
nodes: [el], | ||
has, | ||
}; | ||
} | ||
return { | ||
specificity, | ||
matched, | ||
matched: false, | ||
}; | ||
} | ||
default: { | ||
const matched = getDescendants(el).some(desc => ruleset.match(desc, el).some(m => m.matched)); | ||
const has = getDescendants(el) | ||
.map(sib => ruleset.match(sib, el).filter((m) => m.matched)) | ||
.flat(); | ||
if (has.length) { | ||
return { | ||
specificity, | ||
matched: true, | ||
nodes: [el], | ||
has, | ||
}; | ||
} | ||
return { | ||
specificity, | ||
matched, | ||
matched: false, | ||
}; | ||
@@ -537,6 +679,8 @@ } | ||
const resList = ruleset.match(el, scope); | ||
const matched = resList.some(r => r.matched); | ||
const matched = resList.filter((r) => r.matched); | ||
return { | ||
specificity: [0, 0, 0], | ||
matched, | ||
matched: !!matched.length, | ||
nodes: matched.map(m => m.nodes).flat(), | ||
has: matched.map(m => m.has).flat(), | ||
}; | ||
@@ -549,2 +693,4 @@ } | ||
matched: true, | ||
nodes: [el], | ||
has: [], | ||
}; | ||
@@ -562,2 +708,4 @@ } | ||
matched: true, | ||
nodes: [el], | ||
has: [], | ||
}; | ||
@@ -648,13 +796,13 @@ } | ||
} | ||
function getSpecificity(result) { | ||
function getSpecificity(results) { | ||
let specificity; | ||
for (const res of result) { | ||
for (const result of results) { | ||
if (specificity) { | ||
const order = (0, compare_specificity_1.compareSpecificity)(specificity, res.specificity); | ||
const order = (0, compare_specificity_1.compareSpecificity)(specificity, result.specificity); | ||
if (order === -1) { | ||
specificity = res.specificity; | ||
specificity = result.specificity; | ||
} | ||
} | ||
else { | ||
specificity = res.specificity; | ||
specificity = result.specificity; | ||
} | ||
@@ -661,0 +809,0 @@ } |
@@ -1,7 +0,15 @@ | ||
export declare type Specificity = [number, number, number]; | ||
export declare type SelectorResult = { | ||
export type Specificity = [number, number, number]; | ||
export type SelectorResult = SelectorMatchedResult | SelectorUnmatchedResult; | ||
export type SelectorMatchedResult = { | ||
specificity: Specificity; | ||
matched: boolean; | ||
matched: true; | ||
nodes: (Element | Text)[]; | ||
has: SelectorMatchedResult[]; | ||
}; | ||
export declare type RegexSelector = RegexSelectorWithoutCompination & { | ||
export type SelectorUnmatchedResult = { | ||
specificity: Specificity; | ||
matched: false; | ||
not?: SelectorMatchedResult[]; | ||
}; | ||
export type RegexSelector = RegexSelectorWithoutCombination & { | ||
combination?: { | ||
@@ -11,4 +19,4 @@ combinator: RegexSelectorCombinator; | ||
}; | ||
export declare type RegexSelectorCombinator = ' ' | '>' | '+' | '~' | ':has(+)' | ':has(~)'; | ||
export declare type RegexSelectorWithoutCompination = { | ||
export type RegexSelectorCombinator = ' ' | '>' | '+' | '~' | ':has(+)' | ':has(~)'; | ||
export type RegexSelectorWithoutCombination = { | ||
nodeName?: string; | ||
@@ -15,0 +23,0 @@ attrName?: string; |
{ | ||
"name": "@markuplint/selector", | ||
"version": "3.0.0-alpha.2105+6cde1134", | ||
"description": "W3C Selector and Regex selector", | ||
"version": "3.0.0-canary.2421+382d1365", | ||
"description": "Extended W3C Selectors matcher", | ||
"repository": "git@github.com:markuplint/markuplint.git", | ||
@@ -25,8 +25,8 @@ "author": "Yusuke Hirao <yusukehirao@me.com>", | ||
"devDependencies": { | ||
"@markuplint/ml-spec": "3.0.0-alpha.82+6cde1134", | ||
"@markuplint/ml-spec": "3.0.0-canary.5+382d1365", | ||
"@types/debug": "^4.1.7", | ||
"@types/jsdom": "^16.2.14", | ||
"jsdom": "^19.0.0" | ||
"@types/jsdom": "16", | ||
"jsdom": "19" | ||
}, | ||
"gitHead": "6cde113402758a8fdbd6a0fcf98e78efd2cdb778" | ||
"gitHead": "382d13653071bd02210d5430403d1a87c840398c" | ||
} |
# @markuplint/selector | ||
[![npm version](https://badge.fury.io/js/%40markuplint%2Fselector.svg)](https://www.npmjs.com/package/@markuplint/selector) | ||
[![Build Status](https://travis-ci.org/markuplint/markuplint.svg?branch=main)](https://travis-ci.org/markuplint/markuplint) | ||
[![Coverage Status](https://coveralls.io/repos/github/markuplint/markuplint/badge.svg?branch=main)](https://coveralls.io/github/markuplint/markuplint?branch=main) | ||
## Install | ||
**Extended [W3C Selectors](https://www.w3.org/TR/selectors-4/) matcher** | ||
```sh | ||
$ npm install @markuplint/selector | ||
$ yarn add @markuplint/selector | ||
``` | ||
## [W3C Selectors](https://www.w3.org/TR/selectors-4/) matcher | ||
Supported selectors and operators: | ||
@@ -77,5 +67,7 @@ | ||
| Selector Type | Code Example | | ||
| ----------------- | ----------------- | | ||
| ARIA pseudo-class | `:aria(has name)` | | ||
| Selector Type | Code Example | | ||
| -------------------------- | --------------------- | | ||
| ARIA pseudo-class | `:aria(has name)` | | ||
| ARIA Role pseudo-class | `:role(heading)` | | ||
| Content Model pseudo-class | `:model(interactive)` | | ||
@@ -86,11 +78,29 @@ ### ARIA pseudo-class | ||
:aria(syntax) | ||
:aria(syntax/version) | ||
``` | ||
| Syntax | Example | Description | | ||
| -------------- | ------------------------------------------------------- | ------------------------------- | | ||
| `has name` | `:aria(has name)`<br>`:aria(has name/1.1)` | It has accessible name | | ||
| `has no name` | `:aria(has no name)`<br>`:aria(has no name/1.1)` | It does'nt have accessible name | | ||
| `role is Role` | `:aria(role is banner)`<br>`:aria(role is generic/1.2)` | It matches the specfied role | | ||
| Syntax | Example | Description | | ||
| ------------- | -------------------- | ---------------------------------------------- | | ||
| `has name` | `:aria(has name)` | Matches the element has accessible name | | ||
| `has no name` | `:aria(has no name)` | Matches the element has **no** accessible name | | ||
### ARIA Role pseudo-class | ||
``` | ||
:role(roleName) | ||
:role(roleName|version) | ||
``` | ||
For example, `:role(button)` matches `<button>` and `<div role="button">`. | ||
You can specify the version of WAI-ARIA by separating the pipe like `:role(form|1.1)`. | ||
### Content Model pseudo-class | ||
``` | ||
:model(interactive) | ||
:model(palpable) | ||
``` | ||
For example, `:role(interactive)` matches `<a>`(with `href` attr), `<button>`, and so on. | ||
## Regex Selector | ||
@@ -105,1 +115,16 @@ | ||
``` | ||
## Install | ||
[`markuplint`](https://www.npmjs.com/package/markuplint) package includes this package. | ||
<details> | ||
<summary>If you are installing purposely, how below:</summary> | ||
```sh | ||
$ npm install @markuplint/selector | ||
$ yarn add @markuplint/selector | ||
``` | ||
</details> |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
128128
1384
128