From 0a2266d4052ebb3272042af7f260de3d572596a7 Mon Sep 17 00:00:00 2001 From: Titus Date: Tue, 1 Jun 2021 11:01:05 +0200 Subject: [PATCH 01/10] Use `pull_request_target` in bb --- .github/workflows/bb.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bb.yml b/.github/workflows/bb.yml index 291ab09..0198fc3 100644 --- a/.github/workflows/bb.yml +++ b/.github/workflows/bb.yml @@ -2,7 +2,7 @@ name: bb on: issues: types: [opened, reopened, edited, closed, labeled, unlabeled] - pull_request: + pull_request_target: types: [opened, reopened, edited, closed, labeled, unlabeled] jobs: main: From 7d8d2b0881ead57e3d106d50865d6546a54ede7d Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 23 Jul 2021 11:59:22 +0200 Subject: [PATCH 02/10] Fix types for changes in `@types/unist` --- lib/util.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/util.js b/lib/util.js index 5a921b2..aaa4583 100644 --- a/lib/util.js +++ b/lib/util.js @@ -12,6 +12,7 @@ import {convertElement} from 'hast-util-is-element' * @returns {node is Parent} */ export function parent(node) { + // @ts-expect-error: hush. return Array.isArray(node.children) } From 45a7ae87544eaf9e9f622dad85c3e2df076f7f42 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 23 Jul 2021 12:10:00 +0200 Subject: [PATCH 03/10] Refactor code-style --- lib/any.js | 16 ++--- lib/attribute.js | 10 +-- lib/class-name.js | 4 +- lib/enter-state.js | 30 ++++---- lib/nest.js | 30 ++++---- lib/parse.js | 18 +++-- lib/pseudo.js | 38 +++++----- package.json | 5 +- test/all.js | 2 +- test/matches.js | 172 +++++++++++++++++++++++++++------------------ test/select-all.js | 66 ++++++++--------- test/select.js | 68 +++++++++--------- test/svg.js | 2 +- 13 files changed, 237 insertions(+), 224 deletions(-) diff --git a/lib/any.js b/lib/any.js index 1f71db8..f986db9 100644 --- a/lib/any.js +++ b/lib/any.js @@ -15,7 +15,7 @@ import {nest} from './nest.js' import {pseudo} from './pseudo.js' import {test} from './test.js' -var type = zwitch('type', { +const type = zwitch('type', { unknown: unknownType, invalid: invalidType, handlers: {selectors, ruleSet, rule} @@ -39,8 +39,8 @@ export function any(query, node, state) { * @returns {Array.} */ function selectors(query, node, state) { - var collector = new Collector(state.one) - var index = -1 + const collector = new Collector(state.one) + let index = -1 while (++index < query.selectors.length) { collector.collectAll(ruleSet(query.selectors[index], node, state)) @@ -66,7 +66,7 @@ function ruleSet(query, node, state) { * @returns {Array.} */ function rule(query, tree, state) { - var collector = new Collector(state.one) + const collector = new Collector(state.one) if (state.shallow && query.rule) { throw new Error('Expected selector without nesting') @@ -94,7 +94,7 @@ function rule(query, tree, state) { /** @type {SelectIterator} */ function iterator(query, node, index, parent, state) { - var exit = enterState(state, node) + const exit = enterState(state, node) if (test(query, node, index, parent, state)) { if (query.rule) { @@ -116,8 +116,8 @@ function rule(query, tree, state) { * @returns {S} */ function configure(query, state) { - var pseudos = query.pseudos || [] - var index = -1 + const pseudos = query.pseudos || [] + let index = -1 while (++index < pseudos.length) { if (pseudo.needsIndex.includes(pseudos[index].name)) { @@ -164,7 +164,7 @@ class Collector { * @param {Array.} elements */ collectAll(elements) { - var index = -1 + let index = -1 while (++index < elements.length) { this.collect(elements[index]) diff --git a/lib/attribute.js b/lib/attribute.js index f49e85f..df596e9 100644 --- a/lib/attribute.js +++ b/lib/attribute.js @@ -13,7 +13,7 @@ import {find} from 'property-information' import {stringify as spaces} from 'space-separated-tokens' import {zwitch} from 'zwitch' -var handle = zwitch('operator', { +const handle = zwitch('operator', { unknown: unknownOperator, invalid: exists, handlers: { @@ -33,8 +33,8 @@ var handle = zwitch('operator', { * @returns {boolean} */ export function attribute(query, element, schema) { - var attrs = query.attrs - var index = -1 + const attrs = query.attrs + let index = -1 while (++index < attrs.length) { if (!handle(attrs[index], element, find(schema, attrs[index].name))) return @@ -79,7 +79,7 @@ function exact(query, element, info) { * @returns {boolean} */ function spaceSeparatedList(query, element, info) { - var value = element.properties[info.property] + const value = element.properties[info.property] return ( // If this is a comma-separated list, and the query is contained in it, return @@ -104,7 +104,7 @@ function spaceSeparatedList(query, element, info) { * @returns {boolean} */ function exactOrPrefix(query, element, info) { - var value = normalizeValue(element.properties[info.property], info) + const value = normalizeValue(element.properties[info.property], info) return ( hasProperty(element, info.property) && diff --git a/lib/class-name.js b/lib/class-name.js index 16daf64..c65589d 100644 --- a/lib/class-name.js +++ b/lib/class-name.js @@ -11,8 +11,8 @@ export function className(query, element) { /** @type {Array.} */ // @ts-ignore Assume array. - var value = element.properties.className || [] - var index = -1 + const value = element.properties.className || [] + let index = -1 while (++index < query.classNames.length) { if (!value.includes(query.classNames[index])) return diff --git a/lib/enter-state.js b/lib/enter-state.js index 679abca..8c023dd 100644 --- a/lib/enter-state.js +++ b/lib/enter-state.js @@ -20,30 +20,24 @@ import {element} from './util.js' */ // eslint-disable-next-line complexity export function enterState(state, node) { - var schema = state.schema - var language = state.language - var currentDirection = state.direction - var editableOrEditingHost = state.editableOrEditingHost + const schema = state.schema + const language = state.language + const currentDirection = state.direction + const editableOrEditingHost = state.editableOrEditingHost /** @type {Direction|null} */ - var dirInferred - /** @type {string} */ - var type + let dirInferred /** @type {boolean} */ - var found - /** @type {string} */ - var lang - /** @type {Direction|null} */ - var dir + let found if (element(node)) { // @ts-ignore Assume string. - lang = node.properties.xmlLang || node.properties.lang + const lang = node.properties.xmlLang || node.properties.lang // @ts-ignore Assume string. - type = node.properties.type || 'text' - dir = dirProperty(node) + const type = node.properties.type || 'text' + const dir = dirProperty(node) if (lang !== undefined && lang !== null) { - state.language = lang + state.language = String(lang) found = true } @@ -138,7 +132,7 @@ export function enterState(state, node) { * @returns {Direction} */ function dirBidi(value) { - var result = direction(value) + const result = direction(value) return result === 'neutral' ? null : result } @@ -147,7 +141,7 @@ function dirBidi(value) { * @returns {Direction} */ function dirProperty(node) { - var value = + const value = element(node) && typeof node.properties.dir === 'string' ? node.properties.dir.toLowerCase() : null diff --git a/lib/nest.js b/lib/nest.js index b0c09d7..a749e29 100644 --- a/lib/nest.js +++ b/lib/nest.js @@ -12,9 +12,9 @@ import {zwitch} from 'zwitch' import {enterState} from './enter-state.js' import {parent, element} from './util.js' -var own = {}.hasOwnProperty +const own = {}.hasOwnProperty -var handle = zwitch('nestingOperator', { +const handle = zwitch('nestingOperator', { unknown: unknownNesting, invalid: topScan, // `undefined` is the top query selector. handlers: { @@ -53,7 +53,7 @@ function topScan(query, node, index, parent, state) { /** @type {Handler} */ function descendant(query, node, index, parent, state) { - var previous = state.iterator + const previous = state.iterator state.iterator = iterator child(query, node, index, parent, state) @@ -103,16 +103,14 @@ function generalSibling(query, _, index, parent, state) { * @param {boolean} [firstElementOnly=false] */ function indexedSearch(query, parent, state, from, firstElementOnly) { - var handle = state.index ? delay : add - var children = parent.children - var elements = 0 - var index = -1 + const handle = state.index ? delay : add + const children = parent.children + let elements = 0 + let index = -1 /** @type {Object.} */ - var types = {} + const types = {} /** @type {Array.} */ - var delayed = [] - /** @type {Node} */ - var child + const delayed = [] // Start looking at `from` if (from === undefined || from === null) from = 0 @@ -123,7 +121,7 @@ function indexedSearch(query, parent, state, from, firstElementOnly) { // If we need to index for types, do so for all elements before `from`. if (state.index) { while (++index < from) { - child = children[index] + const child = children[index] if (element(child)) count(child.tagName) } } @@ -131,7 +129,7 @@ function indexedSearch(query, parent, state, from, firstElementOnly) { index = from - 1 while (++index < children.length) { - child = children[index] + const child = children[index] // Only check elements. // Check either all elements, or only check the first sibling if (element(child)) { @@ -157,8 +155,8 @@ function indexedSearch(query, parent, state, from, firstElementOnly) { * @param {number} childIndex */ function delay(node, childIndex) { - var elementsBefore = elements - var elementsByTypeBefore = own.call(types, node.tagName) + const elementsBefore = elements + const elementsByTypeBefore = own.call(types, node.tagName) ? types[node.tagName] : 0 @@ -184,7 +182,7 @@ function indexedSearch(query, parent, state, from, firstElementOnly) { * @param {number} childIndex */ function add(node, childIndex) { - var exit = enterState(state, node) + const exit = enterState(state, node) state.iterator(query, node, childIndex, parent, state) exit() } diff --git a/lib/parse.js b/lib/parse.js index 2d3112f..60f643a 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -13,18 +13,18 @@ import {zwitch} from 'zwitch' /** @type {import('nth-check').default} */ // @ts-ignore -var nthCheck = fauxEsmNthCheck.default +const nthCheck = fauxEsmNthCheck.default -var nth = new Set([ +const nth = new Set([ 'nth-child', 'nth-last-child', 'nth-of-type', 'nth-last-of-type' ]) -var parser = new CssSelectorParser() +const parser = new CssSelectorParser() -var compile = zwitch('type', {handlers: {selectors, ruleSet, rule}}) +const compile = zwitch('type', {handlers: {selectors, ruleSet, rule}}) parser.registerAttrEqualityMods('~', '|', '^', '$', '*') parser.registerSelectorPseudos('any', 'matches', 'not', 'has') @@ -48,7 +48,7 @@ export function parse(selector) { * @returns {Selectors} */ function selectors(query) { - var index = -1 + let index = -1 while (++index < query.selectors.length) { compile(query.selectors[index]) @@ -70,13 +70,11 @@ function ruleSet(query) { * @returns {Rule} */ function rule(query) { - var pseudos = query.pseudos || [] - var index = -1 - /** @type {RulePseudo|RulePseudoNth} */ - var pseudo + const pseudos = query.pseudos || [] + let index = -1 while (++index < pseudos.length) { - pseudo = pseudos[index] + const pseudo = pseudos[index] if (nth.has(pseudo.name)) { // @ts-ignore Patch a non-primitive type. diff --git a/lib/pseudo.js b/lib/pseudo.js index af360f6..a31ad07 100644 --- a/lib/pseudo.js +++ b/lib/pseudo.js @@ -19,7 +19,7 @@ import {whitespace} from 'hast-util-whitespace' import {zwitch} from 'zwitch' import {any} from './any.js' -var handle = zwitch('name', { +const handle = zwitch('name', { unknown: unknownPseudo, invalid: invalidPseudo, handlers: { @@ -76,8 +76,8 @@ pseudo.needsIndex = [ * @returns {boolean} */ export function pseudo(query, element, index, parent, state) { - var pseudos = query.pseudos - var offset = -1 + const pseudos = query.pseudos + let offset = -1 while (++offset < pseudos.length) { if (!handle(pseudos[offset], element, index, parent, state)) return @@ -95,15 +95,13 @@ export function pseudo(query, element, index, parent, state) { * @returns {boolean} */ function matches(query, element, _1, _2, state) { - var shallow = state.shallow - var one = state.one - /** @type {boolean} */ - var result + const shallow = state.shallow + const one = state.one state.shallow = true state.one = true - result = any(query.value, element, state)[0] === element + const result = any(query.value, element, state)[0] === element state.shallow = shallow state.one = one @@ -457,8 +455,8 @@ function onlyOfType(query, _1, _2, _3, state) { * @returns {boolean} */ function someChildren(element, check) { - var children = element.children - var index = -1 + const children = element.children + let index = -1 while (++index < children.length) { if (check(children[index])) return true @@ -501,18 +499,16 @@ function assertDeep(state, query) { * @returns {boolean} */ function has(query, element, _2, _3, state) { - var shallow = state.shallow - var one = state.one - var scopeElements = state.scopeElements - var value = appendScope(query.value) - /** @type {boolean} */ - var result + const shallow = state.shallow + const one = state.one + const scopeElements = state.scopeElements + const value = appendScope(query.value) state.shallow = false state.one = true state.scopeElements = [element] - result = any(value, element, state).length > 0 + const result = any(value, element, state).length > 0 state.shallow = shallow state.one = one @@ -527,14 +523,12 @@ function has(query, element, _2, _3, state) { */ function appendScope(value) { /** @type {Selectors} */ - var selector = + const selector = value.type === 'ruleSet' ? {type: 'selectors', selectors: [value]} : value - var index = -1 - /** @type {Rule} */ - var rule + let index = -1 while (++index < selector.selectors.length) { - rule = selector.selectors[index].rule + const rule = selector.selectors[index].rule rule.nestingOperator = null if ( diff --git a/package.json b/package.json index f9107b2..f91a948 100644 --- a/package.json +++ b/package.json @@ -88,10 +88,7 @@ "xo": { "prettier": true, "rules": { - "max-params": "off", - "unicorn/no-array-for-each": "off", - "no-var": "off", - "prefer-arrow-callback": "off" + "max-params": "off" } }, "remarkConfig": { diff --git a/test/all.js b/test/all.js index a982238..5de05e1 100644 --- a/test/all.js +++ b/test/all.js @@ -3,7 +3,7 @@ import {u} from 'unist-builder' import {h} from 'hastscript' import {selectAll} from '../index.js' -test('all together now', function (t) { +test('all together now', (t) => { t.deepEqual( selectAll( 'dl > dt.foo:nth-of-type(odd)', diff --git a/test/matches.js b/test/matches.js index fe39468..6fa32a0 100644 --- a/test/matches.js +++ b/test/matches.js @@ -3,10 +3,10 @@ import {u} from 'unist-builder' import {h, s} from 'hastscript' import {matches} from '../index.js' -test('select.matches()', function (t) { - t.test('invalid selector', function (t) { +test('select.matches()', (t) => { + t.test('invalid selector', (t) => { t.throws( - function () { + () => { // @ts-ignore runtime. matches() }, @@ -15,7 +15,7 @@ test('select.matches()', function (t) { ) t.throws( - function () { + () => { // @ts-ignore runtime. matches([], h('')) }, @@ -24,7 +24,7 @@ test('select.matches()', function (t) { ) t.throws( - function () { + () => { matches('@supports (transform-origin: 5% 5%) {}', h('')) }, /Error: Rule expected but "@" found./, @@ -32,7 +32,7 @@ test('select.matches()', function (t) { ) t.throws( - function () { + () => { matches('[foo%=bar]', h('')) }, /Error: Expected "=" but "%" found./, @@ -40,7 +40,7 @@ test('select.matches()', function (t) { ) t.throws( - function () { + () => { matches(':active', h('')) }, /Error: Unknown pseudo-selector `active`/, @@ -48,7 +48,7 @@ test('select.matches()', function (t) { ) t.throws( - function () { + () => { matches(':nth-foo(2n+1)', h('')) }, /Error: Unknown pseudo-selector `nth-foo`/, @@ -56,7 +56,7 @@ test('select.matches()', function (t) { ) t.throws( - function () { + () => { matches('::before', h('')) }, /Error: Unexpected pseudo-element or empty pseudo-class/, @@ -64,7 +64,7 @@ test('select.matches()', function (t) { ) t.throws( - function () { + () => { matches('foo bar', h('')) }, /Error: Expected selector without nesting/, @@ -72,7 +72,7 @@ test('select.matches()', function (t) { ) t.throws( - function () { + () => { matches('foo > bar', h('')) }, /Error: Expected selector without nesting/, @@ -82,8 +82,8 @@ test('select.matches()', function (t) { t.end() }) - t.test('parent-sensitive pseudo-selectors', function (t) { - var simplePseudos = [ + t.test('parent-sensitive pseudo-selectors', (t) => { + const simplePseudos = [ 'first-child', 'first-of-type', 'last-child', @@ -91,38 +91,42 @@ test('select.matches()', function (t) { 'only-child', 'only-of-type' ] - - var functionalPseudos = [ + const functionalPseudos = [ 'nth-child', 'nth-last-child', 'nth-of-type', 'nth-last-of-type' ] + let index = -1 - simplePseudos.forEach(function (pseudo) { + while (++index < simplePseudos.length) { + const pseudo = simplePseudos[index] t.throws( - function () { + () => { matches(':' + pseudo, h('')) }, new RegExp('Error: Cannot use `:' + pseudo + '` without parent'), 'should throw on `' + pseudo + '`' ) - }) + } - functionalPseudos.forEach(function (pseudo) { + index = -1 + + while (++index < functionalPseudos.length) { + const pseudo = functionalPseudos[index] t.throws( - function () { + () => { matches(':' + pseudo + '()', h('')) }, new RegExp('Error: Cannot use `:' + pseudo + '` without parent'), 'should throw on `' + pseudo + '()`' ) - }) + } t.end() }) - t.test('general', function (t) { + t.test('general', (t) => { t.notOk(matches('', h('')), 'false for the empty string as selector') t.notOk(matches(' ', h('')), 'false for a white-space only selector') t.notOk(matches('*'), 'false if not given a node') @@ -134,14 +138,14 @@ test('select.matches()', function (t) { t.end() }) - t.test('multiple selectors', function (t) { + t.test('multiple selectors', (t) => { t.ok(matches('b, i', h('b')), 'true for string') t.notOk(matches('i, s', h('b')), 'false for string') t.end() }) - t.test('tag-names: `div`, `*`', function (t) { + t.test('tag-names: `div`, `*`', (t) => { t.ok(matches('*', h('')), 'true for `*`') t.ok(matches('b', h('b')), 'true if tag-names matches') t.notOk(matches('b', h('i')), 'false if tag-names don’t matches') @@ -149,7 +153,7 @@ test('select.matches()', function (t) { t.end() }) - t.test('id: `#id`', function (t) { + t.test('id: `#id`', (t) => { t.notOk(matches('#one', h('')), 'false if no id exists') t.ok(matches('#one', h('#one')), 'true for matchesing id’s') t.notOk(matches('#two', h('#one')), 'false for mismatchesed id’s') @@ -165,7 +169,7 @@ test('select.matches()', function (t) { t.end() }) - t.test('class: `.class`', function (t) { + t.test('class: `.class`', (t) => { t.notOk(matches('.one', h('')), 'false if no class-name exists') t.ok(matches('.one', h('.one')), 'true for matchesing class-name') t.ok( @@ -180,7 +184,7 @@ test('select.matches()', function (t) { t.end() }) - t.test('attributes, existence: `[attr]`', function (t) { + t.test('attributes, existence: `[attr]`', (t) => { t.ok(matches('[class]', h('.one')), 'true if attribute exists') t.notOk(matches('[for]', h('.one')), 'false if attribute does not exist') t.ok( @@ -199,7 +203,7 @@ test('select.matches()', function (t) { t.end() }) - t.test('attributes, equality: `[attr=value]`', function (t) { + t.test('attributes, equality: `[attr=value]`', (t) => { t.ok( matches('[id=one]', h('#one')), 'true if attribute matches (string value)' @@ -277,7 +281,7 @@ test('select.matches()', function (t) { t.end() }) - t.test('attributes, begins: `[attr^=value]`', function (t) { + t.test('attributes, begins: `[attr^=value]`', (t) => { t.ok( matches('[id^=one]', h('#one')), 'true if attribute matches (string value)' @@ -351,7 +355,7 @@ test('select.matches()', function (t) { t.end() }) - t.test('attributes, ends: `[attr$=value]`', function (t) { + t.test('attributes, ends: `[attr$=value]`', (t) => { t.ok( matches('[id$=one]', h('#one')), 'true if attribute matches (string value)' @@ -425,7 +429,7 @@ test('select.matches()', function (t) { t.end() }) - t.test('attributes, contains: `[attr*=value]`', function (t) { + t.test('attributes, contains: `[attr*=value]`', (t) => { t.ok( matches('[id*=one]', h('#one')), 'true if attribute matches (string value)' @@ -502,7 +506,7 @@ test('select.matches()', function (t) { t.test( 'attributes, contains in space-separated list: `[attr~=value]`', - function (t) { + (t) => { t.ok( matches('[id~=one]', h('#one')), 'true if attribute matches (string value)' @@ -600,7 +604,7 @@ test('select.matches()', function (t) { } ) - t.test('attributes, starts or prefixes: `[attr|=value]`', function (t) { + t.test('attributes, starts or prefixes: `[attr|=value]`', (t) => { t.ok( matches('[id|=one]', h('#one')), 'true if attribute matches (string value)' @@ -698,9 +702,14 @@ test('select.matches()', function (t) { t.end() }) - t.test('pseudo-classes', function (t) { - ;[':any', ':matches'].forEach(function (pseudo) { - t.test(pseudo, function (t) { + t.test('pseudo-classes', (t) => { + const anyMatchesPseudos = [':any', ':matches'] + let index = -1 + + while (++index < anyMatchesPseudos.length) { + const pseudo = anyMatchesPseudos[index] + + t.test(pseudo, (t) => { t.ok( matches(pseudo + '(a, [title], .class)', h('a')), 'true if any matches (type)' @@ -724,9 +733,9 @@ test('select.matches()', function (t) { t.end() }) - }) + } - t.test(':not()', function (t) { + t.test(':not()', (t) => { t.notOk( matches(':not(a, [title], .class)', h('a')), 'false if any matches (type)' @@ -751,12 +760,12 @@ test('select.matches()', function (t) { t.end() }) - t.test(':has', function (t) { - t.doesNotThrow(function () { + t.test(':has', (t) => { + t.doesNotThrow(() => { matches('section:not(:has())', h('p')) }, 'should not throw on empty selectors') - t.doesNotThrow(function () { + t.doesNotThrow(() => { matches('section:has()', h('p')) }, 'should not throw on empty selectors') @@ -876,19 +885,24 @@ test('select.matches()', function (t) { t.end() }) - t.test(':any-link', function (t) { - ;['a', 'area', 'link'].forEach(function (name) { + t.test(':any-link', (t) => { + const links = ['a', 'area', 'link'] + let index = -1 + + while (++index < links.length) { + const name = links[index] + t.ok( matches(':any-link', h(name, {href: '#'})), 'true if w/ href on ' + name ) t.notOk(matches(':any-link', h(name)), 'false if w/o href on ' + name) - }) + } t.end() }) - t.test(':checked', function (t) { + t.test(':checked', (t) => { t.ok( matches(':checked', h('input', {type: 'checkbox', checked: true})), 'true for checkbox inputs w/ `checked`' @@ -936,8 +950,8 @@ test('select.matches()', function (t) { t.end() }) - t.test(':disabled', function (t) { - ;[ + t.test(':disabled', (t) => { + const things = [ 'button', 'input', 'select', @@ -946,7 +960,12 @@ test('select.matches()', function (t) { 'option', 'menuitem', 'fieldset' - ].forEach(function (name) { + ] + let index = -1 + + while (++index < things.length) { + const name = things[index] + t.ok( matches(':disabled', h(name, {disabled: true})), 'true if w/ disabled on ' + name @@ -955,13 +974,13 @@ test('select.matches()', function (t) { matches(':disabled', h(name)), 'false if w/o disabled on ' + name ) - }) + } t.end() }) - t.test(':enabled', function (t) { - ;[ + t.test(':enabled', (t) => { + const things = [ 'button', 'input', 'select', @@ -970,19 +989,27 @@ test('select.matches()', function (t) { 'option', 'menuitem', 'fieldset' - ].forEach(function (name) { + ] + let index = -1 + + while (++index < things.length) { + const name = things[index] t.ok(matches(':enabled', h(name)), 'true if w/o disabled on ' + name) t.notOk( matches(':enabled', h(name, {disabled: true})), 'false if w/ disabled on ' + name ) - }) + } t.end() }) - t.test(':required', function (t) { - ;['input', 'textarea', 'select'].forEach(function (name) { + t.test(':required', (t) => { + const things = ['input', 'textarea', 'select'] + let index = -1 + + while (++index < things.length) { + const name = things[index] t.ok( matches(':required', h(name, {required: true})), 'true if w/ required on ' + name @@ -991,24 +1018,29 @@ test('select.matches()', function (t) { matches(':required', h(name)), 'false if w/o required on ' + name ) - }) + } t.end() }) - t.test(':optional', function (t) { - ;['input', 'textarea', 'select'].forEach(function (name) { + t.test(':optional', (t) => { + const things = ['input', 'textarea', 'select'] + + let index = -1 + + while (++index < things.length) { + const name = things[index] t.ok(matches(':optional', h(name)), 'true if w/o required on ' + name) t.notOk( matches(':optional', h(name, {required: true})), 'false if w/ required on ' + name ) - }) + } t.end() }) - t.test(':empty', function (t) { + t.test(':empty', (t) => { t.ok(matches(':empty', h('')), 'true if w/o children') t.ok( matches(':empty', h('', u('comment', '?'))), @@ -1024,7 +1056,7 @@ test('select.matches()', function (t) { t.end() }) - t.test(':blank', function (t) { + t.test(':blank', (t) => { t.ok(matches(':blank', h('')), 'true if w/o children') t.ok( matches(':blank', h('', u('comment', '?'))), @@ -1040,7 +1072,7 @@ test('select.matches()', function (t) { t.end() }) - t.test(':lang()', function (t) { + t.test(':lang()', (t) => { t.ok( matches(':lang(de, en)', h('html', {xmlLang: 'en'})), 'true if the element has an `xml:lang` attribute' @@ -1101,10 +1133,10 @@ test('select.matches()', function (t) { t.end() }) - t.test(':dir()', function (t) { - var ltr = 'a' - var rtl = 'أ' - var neutral = '!' + t.test(':dir()', (t) => { + const ltr = 'a' + const rtl = 'أ' + const neutral = '!' t.ok( matches(':dir(ltr)', h('html', {dir: 'ltr'})), @@ -1253,7 +1285,7 @@ test('select.matches()', function (t) { t.end() }) - t.test(':root', function (t) { + t.test(':root', (t) => { t.ok(matches(':root', h('html')), 'true if `` in HTML space') t.notOk(matches(':root', h('div')), 'false if not `` in HTML space') @@ -1268,14 +1300,14 @@ test('select.matches()', function (t) { t.end() }) - t.test(':scope', function (t) { + t.test(':scope', (t) => { t.ok(matches(':scope', h('html')), 'always true for elements') t.ok(matches(':scope', h('p')), 'always true for elements') t.notOk(matches(':scope', u('text', '!')), 'always true for elements') t.end() }) - t.test(':read-write', function (t) { + t.test(':read-write', (t) => { t.ok(matches(':read-write', h('input')), 'true on input') t.ok(matches(':read-write', h('input')), 'true on textarea') t.notOk( @@ -1302,7 +1334,7 @@ test('select.matches()', function (t) { t.end() }) - t.test(':read-only', function (t) { + t.test(':read-only', (t) => { t.notOk(matches(':read-only', h('input')), 'false on input') t.notOk(matches(':read-only', h('input')), 'false on textarea') t.ok( diff --git a/test/select-all.js b/test/select-all.js index 8aa0163..fb9c274 100644 --- a/test/select-all.js +++ b/test/select-all.js @@ -3,10 +3,10 @@ import {u} from 'unist-builder' import {h, s} from 'hastscript' import {selectAll} from '../index.js' -test('select.selectAll()', function (t) { - t.test('invalid selectors', function (t) { +test('select.selectAll()', (t) => { + t.test('invalid selectors', (t) => { t.throws( - function () { + () => { // @ts-ignore runtime. selectAll() }, @@ -15,7 +15,7 @@ test('select.selectAll()', function (t) { ) t.throws( - function () { + () => { // @ts-ignore runtime. selectAll([], h('')) }, @@ -24,7 +24,7 @@ test('select.selectAll()', function (t) { ) t.throws( - function () { + () => { selectAll('@supports (transform-origin: 5% 5%) {}', h('')) }, /Error: Rule expected but "@" found./, @@ -32,7 +32,7 @@ test('select.selectAll()', function (t) { ) t.throws( - function () { + () => { selectAll('[foo%=bar]', h('')) }, /Error: Expected "=" but "%" found./, @@ -40,7 +40,7 @@ test('select.selectAll()', function (t) { ) t.throws( - function () { + () => { selectAll(':active', h('')) }, /Error: Unknown pseudo-selector `active`/, @@ -48,7 +48,7 @@ test('select.selectAll()', function (t) { ) t.throws( - function () { + () => { selectAll(':nth-foo(2n+1)', h('')) }, /Error: Unknown pseudo-selector `nth-foo`/, @@ -56,7 +56,7 @@ test('select.selectAll()', function (t) { ) t.throws( - function () { + () => { selectAll('::before', h('')) }, /Error: Unexpected pseudo-element or empty pseudo-class/, @@ -66,7 +66,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test('general', function (t) { + t.test('general', (t) => { t.deepEqual( selectAll('', h('')), [], @@ -87,7 +87,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test('descendant selector', function (t) { + t.test('descendant selector', (t) => { t.deepEqual( selectAll( 'div', @@ -135,7 +135,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test('child selector', function (t) { + t.test('child selector', (t) => { t.deepEqual( selectAll( 'main > article', @@ -166,7 +166,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test('next-sibling selector', function (t) { + t.test('next-sibling selector', (t) => { t.deepEqual( selectAll( 'h1 + p', @@ -220,7 +220,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test('subsequent sibling selector', function (t) { + t.test('subsequent sibling selector', (t) => { t.deepEqual( selectAll( 'h1 ~ p', @@ -283,8 +283,8 @@ test('select.selectAll()', function (t) { t.end() }) - t.test('parent-sensitive pseudo-selectors', function (t) { - t.test(':first-child', function (t) { + t.test('parent-sensitive pseudo-selectors', (t) => { + t.test(':first-child', (t) => { t.deepEqual( selectAll( ':first-child', @@ -321,7 +321,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test(':last-child', function (t) { + t.test(':last-child', (t) => { t.deepEqual( selectAll( ':last-child', @@ -358,7 +358,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test(':only-child', function (t) { + t.test(':only-child', (t) => { t.deepEqual( selectAll( ':only-child', @@ -386,7 +386,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test(':nth-child', function (t) { + t.test(':nth-child', (t) => { t.deepEqual( selectAll( 'li:nth-child(odd)', @@ -454,7 +454,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test(':nth-last-child', function (t) { + t.test(':nth-last-child', (t) => { t.deepEqual( selectAll( 'li:nth-last-child(odd)', @@ -522,7 +522,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test(':nth-of-type', function (t) { + t.test(':nth-of-type', (t) => { t.deepEqual( selectAll( 'dt:nth-of-type(odd)', @@ -590,7 +590,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test(':nth-last-of-type', function (t) { + t.test(':nth-last-of-type', (t) => { t.deepEqual( selectAll( 'dt:nth-last-of-type(odd)', @@ -658,7 +658,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test(':first-of-type', function (t) { + t.test(':first-of-type', (t) => { t.deepEqual( selectAll( 'dt:first-of-type', @@ -684,7 +684,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test(':last-of-type', function (t) { + t.test(':last-of-type', (t) => { t.deepEqual( selectAll( 'dt:last-of-type', @@ -710,7 +710,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test(':only-of-type', function (t) { + t.test(':only-of-type', (t) => { t.deepEqual( selectAll( 'dd:only-of-type', @@ -750,7 +750,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test(':lang()', function (t) { + t.test(':lang()', (t) => { t.deepEqual( selectAll( 'q:lang(en)', @@ -789,9 +789,9 @@ test('select.selectAll()', function (t) { t.end() }) - t.test(':dir()', function (t) { - var ltr = 'a' - var rtl = 'أ' + t.test(':dir()', (t) => { + const ltr = 'a' + const rtl = 'أ' t.deepEqual( selectAll( @@ -811,7 +811,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test(':read-write', function (t) { + t.test(':read-write', (t) => { t.deepEqual( selectAll( 'p:read-write', @@ -839,7 +839,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test(':read-only', function (t) { + t.test(':read-only', (t) => { t.deepEqual( selectAll( 'p:read-only', @@ -867,7 +867,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test(':root', function (t) { + t.test(':root', (t) => { t.deepEqual( selectAll( ':root', @@ -949,7 +949,7 @@ test('select.selectAll()', function (t) { t.end() }) - t.test(':scope', function (t) { + t.test(':scope', (t) => { t.deepEqual( selectAll( ':scope', diff --git a/test/select.js b/test/select.js index 448563b..48fca85 100644 --- a/test/select.js +++ b/test/select.js @@ -3,10 +3,10 @@ import {u} from 'unist-builder' import {h, s} from 'hastscript' import {select} from '../index.js' -test('select.select()', function (t) { - t.test('invalid selectors', function (t) { +test('select.select()', (t) => { + t.test('invalid selectors', (t) => { t.throws( - function () { + () => { // @ts-ignore runtime. select() }, @@ -15,7 +15,7 @@ test('select.select()', function (t) { ) t.throws( - function () { + () => { // @ts-ignore runtime. select([], h('')) }, @@ -24,7 +24,7 @@ test('select.select()', function (t) { ) t.throws( - function () { + () => { select('@supports (transform-origin: 5% 5%) {}', h('')) }, /Error: Rule expected but "@" found./, @@ -32,7 +32,7 @@ test('select.select()', function (t) { ) t.throws( - function () { + () => { select('[foo%=bar]', h('')) }, /Error: Expected "=" but "%" found./, @@ -40,7 +40,7 @@ test('select.select()', function (t) { ) t.throws( - function () { + () => { select(':active', h('')) }, /Error: Unknown pseudo-selector `active`/, @@ -48,7 +48,7 @@ test('select.select()', function (t) { ) t.throws( - function () { + () => { select(':nth-foo(2n+1)', h('')) }, /Error: Unknown pseudo-selector `nth-foo`/, @@ -56,7 +56,7 @@ test('select.select()', function (t) { ) t.throws( - function () { + () => { select('::before', h('')) }, /Error: Unexpected pseudo-element or empty pseudo-class/, @@ -66,7 +66,7 @@ test('select.select()', function (t) { t.end() }) - t.test('general', function (t) { + t.test('general', (t) => { t.equal(select('', h('')), null, 'nothing for the empty string as selector') t.equal(select(' ', h('')), null, 'nothing for a white-space only selector') t.equal(select('*'), null, 'nothing if not given a node') @@ -79,7 +79,7 @@ test('select.select()', function (t) { t.end() }) - t.test('descendant selector', function (t) { + t.test('descendant selector', (t) => { t.deepEqual( select( 'div', @@ -116,7 +116,7 @@ test('select.select()', function (t) { t.end() }) - t.test('child selector', function (t) { + t.test('child selector', (t) => { t.deepEqual( select( 'main > article', @@ -147,7 +147,7 @@ test('select.select()', function (t) { t.end() }) - t.test('next-sibling selector', function (t) { + t.test('next-sibling selector', (t) => { t.deepEqual( select( 'h1 + p', @@ -201,7 +201,7 @@ test('select.select()', function (t) { t.end() }) - t.test('subsequent sibling selector', function (t) { + t.test('subsequent sibling selector', (t) => { t.deepEqual( select( 'h1 ~ p', @@ -264,8 +264,8 @@ test('select.select()', function (t) { t.end() }) - t.test('parent-sensitive pseudo-selectors', function (t) { - t.test(':first-child', function (t) { + t.test('parent-sensitive pseudo-selectors', (t) => { + t.test(':first-child', (t) => { t.deepEqual( select( ':first-child', @@ -293,7 +293,7 @@ test('select.select()', function (t) { t.end() }) - t.test(':last-child', function (t) { + t.test(':last-child', (t) => { t.deepEqual( select( ':last-child', @@ -321,7 +321,7 @@ test('select.select()', function (t) { t.end() }) - t.test(':only-child', function (t) { + t.test(':only-child', (t) => { t.deepEqual( select( ':only-child', @@ -349,7 +349,7 @@ test('select.select()', function (t) { t.end() }) - t.test(':nth-child', function (t) { + t.test(':nth-child', (t) => { t.deepEqual( select( 'li:nth-child(odd)', @@ -417,7 +417,7 @@ test('select.select()', function (t) { t.end() }) - t.test(':nth-last-child', function (t) { + t.test(':nth-last-child', (t) => { t.deepEqual( select( 'li:nth-last-child(odd)', @@ -485,7 +485,7 @@ test('select.select()', function (t) { t.end() }) - t.test(':nth-of-type', function (t) { + t.test(':nth-of-type', (t) => { t.deepEqual( select( 'dt:nth-of-type(odd)', @@ -553,7 +553,7 @@ test('select.select()', function (t) { t.end() }) - t.test(':nth-last-of-type', function (t) { + t.test(':nth-last-of-type', (t) => { t.deepEqual( select( 'dt:nth-last-of-type(odd)', @@ -621,7 +621,7 @@ test('select.select()', function (t) { t.end() }) - t.test(':first-of-type', function (t) { + t.test(':first-of-type', (t) => { t.deepEqual( select( 'dt:first-of-type', @@ -647,7 +647,7 @@ test('select.select()', function (t) { t.end() }) - t.test(':last-of-type', function (t) { + t.test(':last-of-type', (t) => { t.deepEqual( select( 'dt:last-of-type', @@ -673,7 +673,7 @@ test('select.select()', function (t) { t.end() }) - t.test(':only-of-type', function (t) { + t.test(':only-of-type', (t) => { t.deepEqual( select( 'dd:only-of-type', @@ -713,7 +713,7 @@ test('select.select()', function (t) { t.end() }) - t.test(':lang()', function (t) { + t.test(':lang()', (t) => { t.deepEqual( select( 'q:lang(en)', @@ -732,10 +732,10 @@ test('select.select()', function (t) { t.end() }) - t.test(':dir()', function (t) { - var ltr = 'a' - var rtl = 'أ' - var neutral = '!' + t.test(':dir()', (t) => { + const ltr = 'a' + const rtl = 'أ' + const neutral = '!' t.deepEqual( select( @@ -755,7 +755,7 @@ test('select.select()', function (t) { t.end() }) - t.test(':read-write', function (t) { + t.test(':read-write', (t) => { t.deepEqual( select( 'p:read-write', @@ -783,7 +783,7 @@ test('select.select()', function (t) { t.end() }) - t.test(':read-only', function (t) { + t.test(':read-only', (t) => { t.deepEqual( select( 'p:read-only', @@ -811,7 +811,7 @@ test('select.select()', function (t) { t.end() }) - t.test(':root', function (t) { + t.test(':root', (t) => { t.deepEqual( select( ':root', @@ -887,7 +887,7 @@ test('select.select()', function (t) { t.end() }) - t.test(':scope', function (t) { + t.test(':scope', (t) => { t.deepEqual( select( ':scope', diff --git a/test/svg.js b/test/svg.js index ac0bd8c..98bbf10 100644 --- a/test/svg.js +++ b/test/svg.js @@ -3,7 +3,7 @@ import {u} from 'unist-builder' import {h, s} from 'hastscript' import {select, selectAll} from '../index.js' -test('svg', function (t) { +test('svg', (t) => { t.deepEqual( select( '[writing-mode]', From bd62f7db824120c6e0ee28f31028b2d16414366e Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 23 Jul 2021 12:56:24 +0200 Subject: [PATCH 04/10] Add `strict` to `tsconfig.json` --- lib/any.js | 16 ++++---- lib/attribute.js | 66 +++++++++++++++++++++----------- lib/class-name.js | 8 ++-- lib/enter-state.js | 31 ++++++++------- lib/id.js | 2 +- lib/nest.js | 33 ++++++++++++++-- lib/parse.js | 9 +++-- lib/pseudo.js | 95 ++++++++++++++++++++++++++++++++++++---------- lib/test.js | 13 ++++--- lib/util.js | 2 +- test/matches.js | 4 +- test/select-all.js | 4 +- test/select.js | 4 +- tsconfig.json | 3 +- 14 files changed, 202 insertions(+), 88 deletions(-) diff --git a/lib/any.js b/lib/any.js index f986db9..902b44a 100644 --- a/lib/any.js +++ b/lib/any.js @@ -16,19 +16,21 @@ import {pseudo} from './pseudo.js' import {test} from './test.js' const type = zwitch('type', { + // @ts-expect-error: hush. unknown: unknownType, invalid: invalidType, + // @ts-expect-error: hush. handlers: {selectors, ruleSet, rule} }) /** * @param {Selectors|RuleSet|Rule} query - * @param {HastNode} node + * @param {HastNode|undefined} node * @param {SelectState} state * @returns {Array.} */ export function any(query, node, state) { - // @ts-ignore zwitch types are off. + // @ts-expect-error zwitch types are off. return query && node ? type(query, node, state) : [] } @@ -79,10 +81,10 @@ function rule(query, tree, state) { null, configure(query, { schema: state.space === 'svg' ? svg : html, - language: null, + language: undefined, direction: 'ltr', editableOrEditingHost: false, - // @ts-ignore assume elements. + // @ts-expect-error assume elements. scopeElements: tree.type === 'root' ? tree.children : [tree], iterator, one: state.one, @@ -100,7 +102,7 @@ function rule(query, tree, state) { if (query.rule) { nest(query.rule, node, index, parent, configure(query.rule, state)) } else { - // @ts-ignore `test` also asserts `node is Element` + // @ts-expect-error `test` also asserts `node is Element` collector.collect(node) state.found = true } @@ -147,12 +149,12 @@ function invalidType() { class Collector { /** - * @param {boolean} one + * @param {boolean|undefined} [one] */ constructor(one) { /** @type {Array.} */ this.result = [] - /** @type {boolean} */ + /** @type {boolean|undefined} */ this.one = one /** @type {boolean} */ this.found = false diff --git a/lib/attribute.js b/lib/attribute.js index df596e9..6906628 100644 --- a/lib/attribute.js +++ b/lib/attribute.js @@ -14,14 +14,22 @@ import {stringify as spaces} from 'space-separated-tokens' import {zwitch} from 'zwitch' const handle = zwitch('operator', { + // @ts-expect-error: hush. unknown: unknownOperator, + // @ts-expect-error: hush. invalid: exists, handlers: { + // @ts-expect-error: hush. '=': exact, + // @ts-expect-error: hush. '~=': spaceSeparatedList, + // @ts-expect-error: hush. '|=': exactOrPrefix, + // @ts-expect-error: hush. '^=': begins, + // @ts-expect-error: hush. '$=': ends, + // @ts-expect-error: hush. '*=': contains } }) @@ -37,7 +45,9 @@ export function attribute(query, element, schema) { let index = -1 while (++index < attrs.length) { - if (!handle(attrs[index], element, find(schema, attrs[index].name))) return + if (!handle(attrs[index], element, find(schema, attrs[index].name))) { + return false + } } return true @@ -64,9 +74,10 @@ function exists(_, element, info) { * @returns {boolean} */ function exact(query, element, info) { - return ( + return Boolean( hasProperty(element, info.property) && - normalizeValue(element.properties[info.property], info) === query.value + element.properties && + normalizeValue(element.properties[info.property], info) === query.value ) } @@ -79,7 +90,7 @@ function exact(query, element, info) { * @returns {boolean} */ function spaceSeparatedList(query, element, info) { - const value = element.properties[info.property] + const value = element.properties && element.properties[info.property] return ( // If this is a comma-separated list, and the query is contained in it, return @@ -87,6 +98,7 @@ function spaceSeparatedList(query, element, info) { (!info.commaSeparated && value && typeof value === 'object' && + query.value && value.includes(query.value)) || // For all other values (including comma-separated lists), return whether this // is an exact match. @@ -104,13 +116,17 @@ function spaceSeparatedList(query, element, info) { * @returns {boolean} */ function exactOrPrefix(query, element, info) { - const value = normalizeValue(element.properties[info.property], info) + const value = normalizeValue( + element.properties && element.properties[info.property], + info + ) - return ( + return Boolean( hasProperty(element, info.property) && - (value === query.value || - (value.slice(0, query.value.length) === query.value && - value.charAt(query.value.length) === '-')) + query.value && + (value === query.value || + (value.slice(0, query.value.length) === query.value && + value.charAt(query.value.length) === '-')) ) } @@ -123,12 +139,14 @@ function exactOrPrefix(query, element, info) { * @returns {boolean} */ function begins(query, element, info) { - return ( + return Boolean( hasProperty(element, info.property) && - normalizeValue(element.properties[info.property], info).slice( - 0, - query.value.length - ) === query.value + element.properties && + query.value && + normalizeValue(element.properties[info.property], info).slice( + 0, + query.value.length + ) === query.value ) } @@ -141,11 +159,13 @@ function begins(query, element, info) { * @returns {boolean} */ function ends(query, element, info) { - return ( + return Boolean( hasProperty(element, info.property) && - normalizeValue(element.properties[info.property], info).slice( - -query.value.length - ) === query.value + element.properties && + query.value && + normalizeValue(element.properties[info.property], info).slice( + -query.value.length + ) === query.value ) } @@ -158,11 +178,13 @@ function ends(query, element, info) { * @returns {boolean} */ function contains(query, element, info) { - return ( + return Boolean( hasProperty(element, info.property) && - normalizeValue(element.properties[info.property], info).includes( - query.value - ) + element.properties && + query.value && + normalizeValue(element.properties[info.property], info).includes( + query.value + ) ) } diff --git a/lib/class-name.js b/lib/class-name.js index c65589d..988af2c 100644 --- a/lib/class-name.js +++ b/lib/class-name.js @@ -10,12 +10,14 @@ */ export function className(query, element) { /** @type {Array.} */ - // @ts-ignore Assume array. + // @ts-expect-error Assume array. const value = element.properties.className || [] let index = -1 - while (++index < query.classNames.length) { - if (!value.includes(query.classNames[index])) return + if (query.classNames) { + while (++index < query.classNames.length) { + if (!value.includes(query.classNames[index])) return false + } } return true diff --git a/lib/enter-state.js b/lib/enter-state.js index 8c023dd..6464e1f 100644 --- a/lib/enter-state.js +++ b/lib/enter-state.js @@ -8,6 +8,7 @@ import {direction} from 'direction' import {isElement} from 'hast-util-is-element' +// @ts-expect-error: to do type. import toString from 'hast-util-to-string' import {svg} from 'property-information' import {visit, EXIT, SKIP} from 'unist-util-visit' @@ -24,15 +25,13 @@ export function enterState(state, node) { const language = state.language const currentDirection = state.direction const editableOrEditingHost = state.editableOrEditingHost - /** @type {Direction|null} */ + /** @type {Direction|undefined} */ let dirInferred - /** @type {boolean} */ + /** @type {boolean|undefined} */ let found - if (element(node)) { - // @ts-ignore Assume string. + if (element(node) && node.properties) { const lang = node.properties.xmlLang || node.properties.lang - // @ts-ignore Assume string. const type = node.properties.type || 'text' const dir = dirProperty(node) @@ -41,7 +40,7 @@ export function enterState(state, node) { found = true } - if (schema.space === 'html') { + if (schema && schema.space === 'html') { if (node.properties.contentEditable === 'true') { state.editableOrEditingHost = true found = true @@ -78,13 +77,14 @@ export function enterState(state, node) { type === 'text') ) { // Check value of ``. - // @ts-ignore something is `never` in types but this is needed. + // @ts-expect-error something is `never` in types but this is needed. dirInferred = node.properties.value - ? // @ts-ignore Assume string + ? // @ts-expect-error Assume string dirBidi(node.properties.value) : 'ltr' } else { // Check text nodes in `node`. + // @ts-expect-error: fine. visit(node, inferDirectionality) } } @@ -129,23 +129,26 @@ export function enterState(state, node) { /** * @param {string} value - * @returns {Direction} + * @returns {Direction|undefined} */ function dirBidi(value) { const result = direction(value) - return result === 'neutral' ? null : result + return result === 'neutral' ? undefined : result } /** * @param {ElementChild} node - * @returns {Direction} + * @returns {Direction|undefined} */ function dirProperty(node) { const value = - element(node) && typeof node.properties.dir === 'string' + element(node) && node.properties && typeof node.properties.dir === 'string' ? node.properties.dir.toLowerCase() - : null - return value === 'auto' || value === 'ltr' || value === 'rtl' ? value : null + : undefined + + return value === 'auto' || value === 'ltr' || value === 'rtl' + ? value + : undefined } function noop() {} diff --git a/lib/id.js b/lib/id.js index 1b14632..0942da1 100644 --- a/lib/id.js +++ b/lib/id.js @@ -9,5 +9,5 @@ * @returns {boolean} */ export function id(query, element) { - return element.properties.id === query.id + return Boolean(element.properties && element.properties.id === query.id) } diff --git a/lib/nest.js b/lib/nest.js index a749e29..9831cdb 100644 --- a/lib/nest.js +++ b/lib/nest.js @@ -15,19 +15,25 @@ import {parent, element} from './util.js' const own = {}.hasOwnProperty const handle = zwitch('nestingOperator', { + // @ts-expect-error: hush. unknown: unknownNesting, + // @ts-expect-error: hush. invalid: topScan, // `undefined` is the top query selector. handlers: { + // @ts-expect-error: hush. null: descendant, // `null` is the descendant combinator. + // @ts-expect-error: hush. '>': child, + // @ts-expect-error: hush. '+': adjacentSibling, + // @ts-expect-error: hush. '~': generalSibling } }) /** @type {Handler} */ export function nest(query, node, index, parent, state) { - return handle(query, node, index, parent, state) + handle(query, node, index, parent, state) } // Shouldn’t be called, parser gives correct data. @@ -43,10 +49,16 @@ function unknownNesting(query) { function topScan(query, node, index, parent, state) { // Shouldn’t happen. /* c8 ignore next 3 */ - if (parent) { + if (parent || index === null) { throw new Error('topScan is supposed to be called from the root node') } + // Shouldn’t happen. + /* c8 ignore next 3 */ + if (!state.iterator) { + throw new Error('Expected `iterator`') + } + state.iterator(query, node, index, parent, state) if (!state.shallow) descendant(query, node, index, parent, state) } @@ -60,6 +72,12 @@ function descendant(query, node, index, parent, state) { /** @type {SelectIterator} */ function iterator(query, node, index, parent, state) { + // Shouldn’t happen. + /* c8 ignore next 3 */ + if (!previous) { + throw new Error('Expected `iterator`') + } + state.iterator = previous previous(query, node, index, parent, state) state.iterator = iterator @@ -81,7 +99,7 @@ function child(query, node, _1, _2, state) { function adjacentSibling(query, _, index, parent, state) { // Shouldn’t happen. /* c8 ignore next */ - if (!parent) return + if (!parent || index === null) return indexedSearch(query, parent, state, index + 1, true) } @@ -89,7 +107,7 @@ function adjacentSibling(query, _, index, parent, state) { function generalSibling(query, _, index, parent, state) { // Shouldn’t happen. /* c8 ignore next */ - if (!parent) return + if (!parent || index === null) return indexedSearch(query, parent, state, index + 1) } @@ -183,6 +201,13 @@ function indexedSearch(query, parent, state, from, firstElementOnly) { */ function add(node, childIndex) { const exit = enterState(state, node) + + // Shouldn’t happen. + /* c8 ignore next 3 */ + if (!state.iterator) { + throw new Error('Expected `iterator`') + } + state.iterator(query, node, childIndex, parent, state) exit() } diff --git a/lib/parse.js b/lib/parse.js index 60f643a..21bac70 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -12,7 +12,7 @@ import fauxEsmNthCheck from 'nth-check' import {zwitch} from 'zwitch' /** @type {import('nth-check').default} */ -// @ts-ignore +// @ts-expect-error const nthCheck = fauxEsmNthCheck.default const nth = new Set([ @@ -24,6 +24,7 @@ const nth = new Set([ const parser = new CssSelectorParser() +// @ts-expect-error: hush. const compile = zwitch('type', {handlers: {selectors, ruleSet, rule}}) parser.registerAttrEqualityMods('~', '|', '^', '$', '*') @@ -39,7 +40,7 @@ export function parse(selector) { throw new TypeError('Expected `string` as selector, not `' + selector + '`') } - // @ts-ignore types are wrong. + // @ts-expect-error types are wrong. return compile(parser.parse(selector)) } @@ -77,9 +78,9 @@ function rule(query) { const pseudo = pseudos[index] if (nth.has(pseudo.name)) { - // @ts-ignore Patch a non-primitive type. + // @ts-expect-error Patch a non-primitive type. pseudo.value = nthCheck(pseudo.value) - // @ts-ignore Patch a non-primitive type. + // @ts-expect-error Patch a non-primitive type. pseudo.valueType = 'function' } } diff --git a/lib/pseudo.js b/lib/pseudo.js index a31ad07..a54ab0e 100644 --- a/lib/pseudo.js +++ b/lib/pseudo.js @@ -20,36 +20,65 @@ import {zwitch} from 'zwitch' import {any} from './any.js' const handle = zwitch('name', { + // @ts-expect-error: hush. unknown: unknownPseudo, invalid: invalidPseudo, handlers: { + // @ts-expect-error: hush. any: matches, + // @ts-expect-error: hush. 'any-link': anyLink, + // @ts-expect-error: hush. blank, + // @ts-expect-error: hush. checked, + // @ts-expect-error: hush. dir, + // @ts-expect-error: hush. disabled, + // @ts-expect-error: hush. empty, + // @ts-expect-error: hush. enabled, + // @ts-expect-error: hush. 'first-child': firstChild, + // @ts-expect-error: hush. 'first-of-type': firstOfType, + // @ts-expect-error: hush. has, + // @ts-expect-error: hush. lang, + // @ts-expect-error: hush. 'last-child': lastChild, + // @ts-expect-error: hush. 'last-of-type': lastOfType, + // @ts-expect-error: hush. matches, + // @ts-expect-error: hush. not, + // @ts-expect-error: hush. 'nth-child': nthChild, + // @ts-expect-error: hush. 'nth-last-child': nthLastChild, + // @ts-expect-error: hush. 'nth-of-type': nthOfType, + // @ts-expect-error: hush. 'nth-last-of-type': nthLastOfType, + // @ts-expect-error: hush. 'only-child': onlyChild, + // @ts-expect-error: hush. 'only-of-type': onlyOfType, + // @ts-expect-error: hush. optional, + // @ts-expect-error: hush. 'read-only': readOnly, + // @ts-expect-error: hush. 'read-write': readWrite, + // @ts-expect-error: hush. required, + // @ts-expect-error: hush. root, + // @ts-expect-error: hush. scope } }) @@ -80,7 +109,7 @@ export function pseudo(query, element, index, parent, state) { let offset = -1 while (++offset < pseudos.length) { - if (!handle(pseudos[offset], element, index, parent, state)) return + if (!handle(pseudos[offset], element, index, parent, state)) return false } return true @@ -139,14 +168,19 @@ function anyLink(_, element) { */ function checked(_, element) { if (isElement(element, ['input', 'menuitem'])) { - return ( - (element.properties.type === 'checkbox' || - element.properties.type === 'radio') && - hasProperty(element, 'checked') + return Boolean( + element.properties && + (element.properties.type === 'checkbox' || + element.properties.type === 'radio') && + hasProperty(element, 'checked') ) } - if (isElement(element, 'option')) return hasProperty(element, 'selected') + if (isElement(element, 'option')) { + return hasProperty(element, 'selected') + } + + return false } /** @@ -222,7 +256,7 @@ function optional(query, element) { function readWrite(_, element, _1, _2, state) { return isElement(element, ['input', 'textarea']) ? !hasProperty(element, 'readOnly') && !hasProperty(element, 'disabled') - : state.editableOrEditingHost + : Boolean(state.editableOrEditingHost) } /** @@ -246,10 +280,11 @@ function readOnly(query, element, index, parent, state) { * @returns {boolean} */ function root(_, element, _1, parent, state) { - return ( + return Boolean( (!parent || parent.type === 'root') && - (state.schema.space === 'html' || state.schema.space === 'svg') && - isElement(element, ['html', 'svg']) + state.schema && + (state.schema.space === 'html' || state.schema.space === 'svg') && + isElement(element, ['html', 'svg']) ) } @@ -262,7 +297,11 @@ function root(_, element, _1, parent, state) { * @returns {boolean} */ function scope(_, element, _1, _2, state) { - return isElement(element) && state.scopeElements.includes(element) + return Boolean( + isElement(element) && + state.scopeElements && + state.scopeElements.includes(element) + ) } /** @@ -327,7 +366,7 @@ function lang(query, _1, _2, _3, state) { state.language !== '' && state.language !== undefined && state.language !== null && - // @ts-ignore never `selectors`. + // @ts-expect-error never `selectors`. extendedFilter(state.language, commas(query.value)).length > 0 ) } @@ -342,7 +381,9 @@ function lang(query, _1, _2, _3, state) { */ function lastChild(query, _1, _2, _3, state) { assertDeep(state, query) - return state.elementIndex === state.elementCount - 1 + return Boolean( + state.elementCount && state.elementIndex === state.elementCount - 1 + ) } /** @@ -368,7 +409,9 @@ function onlyChild(query, _1, _2, _3, state) { */ function nthChild(query, _1, _2, _3, state) { assertDeep(state, query) - return query.value(state.elementIndex) + return ( + typeof state.elementIndex === 'number' && query.value(state.elementIndex) + ) } /** @@ -381,7 +424,11 @@ function nthChild(query, _1, _2, _3, state) { */ function nthLastChild(query, _1, _2, _3, state) { assertDeep(state, query) - return query.value(state.elementCount - state.elementIndex - 1) + return Boolean( + typeof state.elementCount === 'number' && + typeof state.elementIndex === 'number' && + query.value(state.elementCount - state.elementIndex - 1) + ) } /** @@ -394,7 +441,7 @@ function nthLastChild(query, _1, _2, _3, state) { */ function nthOfType(query, _1, _2, _3, state) { assertDeep(state, query) - return query.value(state.typeIndex) + return typeof state.typeIndex === 'number' && query.value(state.typeIndex) } /** @@ -407,7 +454,11 @@ function nthOfType(query, _1, _2, _3, state) { */ function nthLastOfType(query, _1, _2, _3, state) { assertDeep(state, query) - return query.value(state.typeCount - 1 - state.typeIndex) + return ( + typeof state.typeCount === 'number' && + typeof state.typeIndex === 'number' && + query.value(state.typeCount - 1 - state.typeIndex) + ) } /** @@ -433,7 +484,11 @@ function firstOfType(query, _1, _2, _3, state) { */ function lastOfType(query, _1, _2, _3, state) { assertDeep(state, query) - return state.typeIndex === state.typeCount - 1 + return ( + typeof state.typeIndex === 'number' && + typeof state.typeCount === 'number' && + state.typeIndex === state.typeCount - 1 + ) } /** @@ -461,6 +516,8 @@ function someChildren(element, check) { while (++index < children.length) { if (check(children[index])) return true } + + return false } // Shouldn’t be called, parser gives correct data. @@ -538,7 +595,7 @@ function appendScope(value) { ) { selector.selectors[index] = { type: 'ruleSet', - // @ts-ignore pseudos are fine w/ just a name! + // @ts-expect-error pseudos are fine w/ just a name! rule: {type: 'rule', rule, pseudos: [{name: 'scope'}]} } } diff --git a/lib/test.js b/lib/test.js index b447c6e..b628c2d 100644 --- a/lib/test.js +++ b/lib/test.js @@ -23,12 +23,13 @@ import {element} from './util.js' * @returns {boolean} */ export function test(query, node, index, parent, state) { - return ( + return Boolean( element(node) && - (!query.tagName || name(query, node)) && - (!query.classNames || className(query, node)) && - (!query.id || id(query, node)) && - (!query.attrs || attribute(query, node, state.schema)) && - (!query.pseudos || pseudo(query, node, index, parent, state)) + state.schema && + (!query.tagName || name(query, node)) && + (!query.classNames || className(query, node)) && + (!query.id || id(query, node)) && + (!query.attrs || attribute(query, node, state.schema)) && + (!query.pseudos || pseudo(query, node, index, parent, state)) ) } diff --git a/lib/util.js b/lib/util.js index aaa4583..490600e 100644 --- a/lib/util.js +++ b/lib/util.js @@ -17,5 +17,5 @@ export function parent(node) { } /** @type {IsElement} */ -// @ts-ignore it works. +// @ts-expect-error it works. export const element = convertElement() diff --git a/test/matches.js b/test/matches.js index 6fa32a0..68e781e 100644 --- a/test/matches.js +++ b/test/matches.js @@ -7,7 +7,7 @@ test('select.matches()', (t) => { t.test('invalid selector', (t) => { t.throws( () => { - // @ts-ignore runtime. + // @ts-expect-error runtime. matches() }, /Error: Expected `string` as selector, not `undefined`/, @@ -16,7 +16,7 @@ test('select.matches()', (t) => { t.throws( () => { - // @ts-ignore runtime. + // @ts-expect-error runtime. matches([], h('')) }, /Error: Expected `string` as selector, not ``/, diff --git a/test/select-all.js b/test/select-all.js index fb9c274..5125b90 100644 --- a/test/select-all.js +++ b/test/select-all.js @@ -7,7 +7,7 @@ test('select.selectAll()', (t) => { t.test('invalid selectors', (t) => { t.throws( () => { - // @ts-ignore runtime. + // @ts-expect-error runtime. selectAll() }, /Error: Expected `string` as selector, not `undefined`/, @@ -16,7 +16,7 @@ test('select.selectAll()', (t) => { t.throws( () => { - // @ts-ignore runtime. + // @ts-expect-error runtime. selectAll([], h('')) }, /Error: Expected `string` as selector, not ``/, diff --git a/test/select.js b/test/select.js index 48fca85..6fc3efb 100644 --- a/test/select.js +++ b/test/select.js @@ -7,7 +7,7 @@ test('select.select()', (t) => { t.test('invalid selectors', (t) => { t.throws( () => { - // @ts-ignore runtime. + // @ts-expect-error runtime. select() }, /Error: Expected `string` as selector, not `undefined`/, @@ -16,7 +16,7 @@ test('select.select()', (t) => { t.throws( () => { - // @ts-ignore runtime. + // @ts-expect-error runtime. select([], h('')) }, /Error: Expected `string` as selector, not ``/, diff --git a/tsconfig.json b/tsconfig.json index 2b103bd..f437d29 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "declaration": true, "emitDeclarationOnly": true, "allowSyntheticDefaultImports": true, - "skipLibCheck": true + "skipLibCheck": true, + "strict": true } } From b160bbab166f73920fbb0acec6579c8fc187f50e Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 23 Jul 2021 12:56:58 +0200 Subject: [PATCH 05/10] Update `xo` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f91a948..0d1af03 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "type-coverage": "^2.0.0", "typescript": "^4.0.0", "unist-builder": "^3.0.0", - "xo": "^0.39.0" + "xo": "^0.42.0" }, "scripts": { "prepack": "npm run build && npm run format", From 1f2630ced5ebe8a6ef55943c6e56ec4f0056547b Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 30 Jul 2021 11:09:34 +0200 Subject: [PATCH 06/10] Update `unist-util-visit` --- lib/enter-state.js | 1 - package.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/enter-state.js b/lib/enter-state.js index 6464e1f..42361f9 100644 --- a/lib/enter-state.js +++ b/lib/enter-state.js @@ -84,7 +84,6 @@ export function enterState(state, node) { : 'ltr' } else { // Check text nodes in `node`. - // @ts-expect-error: fine. visit(node, inferDirectionality) } } diff --git a/package.json b/package.json index 0d1af03..068f6f9 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "nth-check": "^2.0.0", "property-information": "^6.0.0", "space-separated-tokens": "^2.0.0", - "unist-util-visit": "^3.0.0", + "unist-util-visit": "^4.0.0", "zwitch": "^2.0.0" }, "devDependencies": { From 1ee84f3c8a673dac8eec79b7e1f24cb8a3e93417 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sun, 1 Aug 2021 21:12:04 +0200 Subject: [PATCH 07/10] Update `hast-util-to-string` --- lib/enter-state.js | 3 +-- package.json | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/enter-state.js b/lib/enter-state.js index 42361f9..0cd65cf 100644 --- a/lib/enter-state.js +++ b/lib/enter-state.js @@ -8,8 +8,7 @@ import {direction} from 'direction' import {isElement} from 'hast-util-is-element' -// @ts-expect-error: to do type. -import toString from 'hast-util-to-string' +import {toString} from 'hast-util-to-string' import {svg} from 'property-information' import {visit, EXIT, SKIP} from 'unist-util-visit' import {element} from './util.js' diff --git a/package.json b/package.json index 068f6f9..cae2326 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "direction": "^2.0.0", "hast-util-has-property": "^2.0.0", "hast-util-is-element": "^2.0.0", - "hast-util-to-string": "^1.0.0", + "hast-util-to-string": "^2.0.0", "hast-util-whitespace": "^2.0.0", "not": "^0.1.0", "nth-check": "^2.0.0", From 724bb40e68e466b42650012f2a1851706c4da566 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 30 Aug 2021 20:33:44 +0200 Subject: [PATCH 08/10] Fix exception on selector list in `select` --- lib/any.js | 2 +- test/select.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/any.js b/lib/any.js index 902b44a..a0c15aa 100644 --- a/lib/any.js +++ b/lib/any.js @@ -182,7 +182,7 @@ class Collector { if (this.one) { // Shouldn’t happen, safeguards performance problems. /* c8 ignore next */ - if (this.found) throw new Error('Cannot collect multiple nodes') + if (this.found) return this.found = true } diff --git a/test/select.js b/test/select.js index 6fc3efb..bb9edd6 100644 --- a/test/select.js +++ b/test/select.js @@ -76,6 +76,12 @@ test('select.select()', (t) => { 'nothing if not given an element' ) + t.deepEqual( + select('h1, h2', h('main', [h('h1', 'Alpha'), h('h2', 'Bravo')])), + h('h1', 'Alpha'), + 'should select one of several elements' + ) + t.end() }) From 4ad03adc9f99c4c578e0188f21c3d296dcf02950 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 30 Aug 2021 20:35:10 +0200 Subject: [PATCH 09/10] Update dev-dependencies --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index cae2326..b06ac51 100644 --- a/package.json +++ b/package.json @@ -60,14 +60,14 @@ "c8": "^7.0.0", "hastscript": "^7.0.0", "prettier": "^2.0.0", - "remark-cli": "^9.0.0", - "remark-preset-wooorm": "^8.0.0", + "remark-cli": "^10.0.0", + "remark-preset-wooorm": "^9.0.0", "rimraf": "^3.0.0", "tape": "^5.0.0", "type-coverage": "^2.0.0", "typescript": "^4.0.0", "unist-builder": "^3.0.0", - "xo": "^0.42.0" + "xo": "^0.44.0" }, "scripts": { "prepack": "npm run build && npm run format", From aeedcaef7b7cf3872f7dd578cfd498527f3fc0ee Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 30 Aug 2021 20:35:24 +0200 Subject: [PATCH 10/10] 5.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b06ac51..d335ca6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hast-util-select", - "version": "5.0.0", + "version": "5.0.1", "description": "hast utility for `querySelector`, `querySelectorAll`, and `matches`", "license": "MIT", "keywords": [