Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: syntax-tree/unist-util-select
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: d447d05867f93c30f5b4e2b6b6308063275ea34c
Choose a base ref
...
head repository: syntax-tree/unist-util-select
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2269b1c4fc8dc8c824d2757612c7c398ae0da80a
Choose a head ref

Commits on Jun 12, 2017

  1. Update Node in Travis

    wooorm committed Jun 12, 2017
    Copy the full SHA
    ec34f16 View commit details
  2. Refactor markdown

    wooorm committed Jun 12, 2017
    Copy the full SHA
    1a66124 View commit details
  3. Update example in README.md

    wooorm committed Jun 12, 2017
    Copy the full SHA
    9ee2539 View commit details

Commits on Oct 22, 2017

  1. Copy the full SHA
    55a090b View commit details
  2. chore(.dir-locals.el): init

    eush77 committed Oct 22, 2017
    Copy the full SHA
    9de42d9 View commit details
  3. chore(LICENSE): update

    eush77 committed Oct 22, 2017
    Copy the full SHA
    dbbae7c View commit details

Commits on Nov 8, 2018

  1. Update build process

    wooorm committed Nov 8, 2018
    Copy the full SHA
    4c1c17e View commit details
  2. Rewrite library

    wooorm committed Nov 8, 2018
    Copy the full SHA
    22df6d4 View commit details
  3. Rewrite readme

    wooorm committed Nov 8, 2018
    Copy the full SHA
    00eccf9 View commit details
  4. Refactor support

    wooorm committed Nov 8, 2018
    Copy the full SHA
    fb26027 View commit details
  5. 2.0.0

    wooorm committed Nov 8, 2018
    Copy the full SHA
    2ed381f View commit details

Commits on May 23, 2019

  1. Fix tests on Windows

    wooorm committed May 23, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    495cb4b View commit details
  2. Update Node in Travis

    wooorm committed May 23, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    d84880d View commit details
  3. Update dev-dependencies

    wooorm committed May 23, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    51dfc48 View commit details
  4. Remove unused dependency

    wooorm committed May 23, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    2e5b7d6 View commit details
  5. Refactor comment-style

    wooorm committed May 23, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    f38c805 View commit details
  6. Add more badges

    wooorm committed May 23, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    27ca2ce View commit details
  7. Refactor prose

    wooorm committed May 23, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    4e5a749 View commit details
  8. Add list of related projects

    wooorm committed May 23, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    9c9053a View commit details
  9. 2.0.1

    wooorm committed May 23, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    7a3f642 View commit details

Commits on May 31, 2019

  1. Update unist-util-is

    wooorm committed May 31, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    de0cee8 View commit details
  2. 2.0.2

    wooorm committed May 31, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    ba5fbb2 View commit details

Commits on Oct 28, 2019

  1. Add types

    Closes GH-7.
    
    Reviewed-by: Titus Wormer <tituswormer@gmail.com>
    ChristianMurphy authored and wooorm committed Oct 28, 2019
    Copy the full SHA
    3100da1 View commit details
  2. Update unist-util-is

    wooorm committed Oct 28, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    fe1e107 View commit details
  3. Update dev-dependencies

    wooorm committed Oct 28, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    529d73f View commit details
  4. 3.0.0

    wooorm committed Oct 28, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    019f2f9 View commit details

Commits on Feb 10, 2020

  1. Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    416750c View commit details
  2. Update dev-dependencies

    wooorm committed Feb 10, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    56c265a View commit details
  3. Refactor prose

    wooorm committed Feb 10, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    56febd2 View commit details
  4. 3.0.1

    wooorm committed Feb 10, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    e4b1c6e View commit details

Commits on Feb 17, 2020

  1. Refactor prose

    wooorm committed Feb 17, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    c7dc44b View commit details
  2. Update dev-dependencies

    wooorm committed Feb 17, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    e855199 View commit details

Commits on Apr 24, 2020

  1. Change nully to nullish

    wooorm committed Apr 24, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    b57766e View commit details
  2. Update dev-dependencies

    wooorm committed Apr 24, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    b592826 View commit details

Commits on Jun 15, 2020

  1. Update dev-dependencies

    wooorm committed Jun 15, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    d561a97 View commit details
  2. Update links

    wooorm committed Jun 15, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    29eba69 View commit details

Commits on Jun 18, 2020

  1. Change master in links to HEAD

    wooorm committed Jun 18, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    52a32fa View commit details

Commits on Aug 21, 2020

  1. Add Discussions

    wooorm authored Aug 21, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    38bcbd7 View commit details

Commits on Oct 30, 2020

  1. Update dev-dependencies

    wooorm committed Oct 30, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    afebbaa View commit details
  2. Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    d48ac1c View commit details
  3. 3.0.2

    wooorm committed Oct 30, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    0f2d796 View commit details

Commits on Dec 2, 2020

  1. Update nth-check

    wooorm committed Dec 2, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    732da9a View commit details
  2. Update dev-dependencies

    wooorm committed Dec 2, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    a9fc891 View commit details
  3. Use Actions

    wooorm committed Dec 2, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    171a675 View commit details
  4. 3.0.3

    wooorm committed Dec 2, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    2269b1c View commit details
Showing with 3,281 additions and 1,276 deletions.
  1. +9 −0 .editorconfig
  2. +21 −0 .github/workflows/main.yml
  3. +6 −1 .gitignore
  4. +1 −0 .npmrc
  5. +3 −0 .prettierignore
  6. +0 −5 .travis.yml
  7. +0 −107 README.md
  8. +15 −35 index.js
  9. +128 −0 lib/any.js
  10. +0 −150 lib/ast-walkers.js
  11. +88 −0 lib/attribute.js
  12. +0 −32 lib/collector.js
  13. +0 −107 lib/match-node.js
  14. +7 −0 lib/name.js
  15. +181 −0 lib/nest.js
  16. +65 −0 lib/parse.js
  17. +203 −0 lib/pseudo.js
  18. +0 −74 lib/select.js
  19. +0 −52 lib/selector.js
  20. +24 −0 lib/test.js
  21. +0 −23 lib/type-index.js
  22. +1 −1 LICENSE → license
  23. +93 −33 package.json
  24. +233 −0 readme.md
  25. +90 −0 test/all.js
  26. +0 −15 test/collector.js
  27. +0 −13 test/curried.js
  28. +8 −0 test/index.js
  29. +0 −224 test/lib/ast.js
  30. +0 −8 test/lib/path.js
  31. +612 −0 test/matches.js
  32. +734 −0 test/select-all.js
  33. +0 −17 test/select-one.js
  34. +700 −352 test/select.js
  35. +0 −27 test/type-index.js
  36. +29 −0 types/index.d.ts
  37. +10 −0 types/tsconfig.json
  38. +7 −0 types/tslint.json
  39. +13 −0 types/unist-util-select-tests.ts
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
21 changes: 21 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: main
on:
- pull_request
- push
jobs:
main:
name: ${{matrix.node}}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: dcodeIO/setup-node-nvm@master
with:
node-version: ${{matrix.node}}
- run: npm install
- run: npm test
- uses: codecov/codecov-action@v1
strategy:
matrix:
node:
- lts/dubnium
- node
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
/node_modules/
.DS_Store
*.log
.nyc_output/
coverage/
node_modules/
yarn.lock
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
coverage/
*.json
*.md
5 changes: 0 additions & 5 deletions .travis.yml

This file was deleted.

107 changes: 0 additions & 107 deletions README.md

This file was deleted.

50 changes: 15 additions & 35 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,20 @@
'use strict';
'use strict'

var parseSelector = require('./lib/selector'),
matchSelector = require('./lib/select');
exports.matches = matches
exports.selectAll = selectAll
exports.select = select

var debug = require('debug')('unist-util-select');
var any = require('./lib/any')
var parse = require('./lib/parse')

function matches(selector, node) {
return Boolean(any(parse(selector), node, {one: true, shallow: true})[0])
}

var select = function select (ast, selector) {
if (arguments.length == 1) {
return select.bind(this, ast);
}
function select(selector, node) {
return any(parse(selector), node, {one: true})[0] || null
}

debug('Selector: %j', selector);
selector = parseSelector(selector);
debug('AST: %s',
JSON.stringify(selector, null, 2).replace(/(^|\n)/g, '\n '));
return selector ? matchSelector[selector.type](selector, ast) : [];
};


select.one = function selectOne (ast, selector) {
if (arguments.length == 1) {
return selectOne.bind(this, ast);
}

var nodes = select(ast, selector);

if (!nodes.length) {
throw Error('Node not found by ' + JSON.stringify(selector));
}
if (nodes.length > 1) {
throw Error('Node matched by ' + JSON.stringify(selector) + ' is not unique');
}

return nodes[0];
};


module.exports = select;
function selectAll(selector, node) {
return any(parse(selector), node, {})
}
128 changes: 128 additions & 0 deletions lib/any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
'use strict'

module.exports = match

var zwitch = require('zwitch')
var pseudo = require('./pseudo')
var test = require('./test')
var nest = require('./nest')

var type = zwitch('type', {
unknown: unknownType,
invalid: invalidType,
handlers: {
selectors: selectors,
ruleSet: ruleSet,
rule: rule
}
})

function match(query, node, state) {
return query && node ? type(query, node, state) : []
}

function selectors(query, node, state) {
var collect = collector(state.one)
var index = -1

while (++index < query.selectors.length) {
collect(ruleSet(query.selectors[index], node, state))
}

return collect.result
}

function ruleSet(query, node, state) {
return rule(query.rule, node, state)
}

function rule(query, tree, state) {
var collect = collector(state.one)

if (state.shallow && query.rule) {
throw new Error('Expected selector without nesting')
}

nest(
query,
tree,
0,
null,
configure(query, {
scopeNodes: tree.type === 'root' ? tree.children : [tree],
iterator: iterator,
one: state.one,
shallow: state.shallow
})
)

return collect.result

function iterator(query, node, index, parent, state) {
if (test(query, node, index, parent, state)) {
if (query.rule) {
nest(query.rule, node, index, parent, configure(query.rule, state))
} else {
collect(node)
state.found = true
}
}
}
}

function configure(query, state) {
var pseudos = query.pseudos || []
var index = -1

while (++index < pseudos.length) {
if (pseudo.needsIndex.indexOf(pseudos[index].name) > -1) {
state.index = true
break
}
}

return state
}

/* istanbul ignore next - Shouldn’t be invoked, all data is handled. */
function unknownType(query) {
throw new Error('Unknown type `' + query.type + '`')
}

/* istanbul ignore next - Shouldn’t be invoked, parser gives correct data. */
function invalidType() {
throw new Error('Invalid type')
}

function collector(one) {
var result = []
var found

collect.result = result

return collect

/* Append nodes to array, filtering out duplicates. */
function collect(node) {
var index = -1

if ('length' in node) {
while (++index < node.length) {
collectOne(node[index])
}
} else {
collectOne(node)
}
}

function collectOne(node) {
if (one) {
/* istanbul ignore if - shouldn’t happen, safeguards performance problems. */
if (found) throw new Error('Cannot collect multiple nodes')

found = true
}

if (result.indexOf(node) < 0) result.push(node)
}
}
150 changes: 0 additions & 150 deletions lib/ast-walkers.js

This file was deleted.

88 changes: 88 additions & 0 deletions lib/attribute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
'use strict'

module.exports = match

var zwitch = require('zwitch')

var handle = zwitch('operator', {
unknown: unknownOperator,
invalid: exists,
handlers: {
'=': exact,
'^=': begins,
'$=': ends,
'*=': containsString,
'~=': containsArray
}
})

function match(query, node) {
var attrs = query.attrs
var index = -1

while (++index < attrs.length) {
if (!handle(attrs[index], node)) return false
}

return true
}

// [attr]
function exists(query, node) {
return node[query.name] != null
}

// [attr=value]
function exact(query, node) {
return node[query.name] != null && String(node[query.name]) === query.value
}

// [attr~=value]
function containsArray(query, node) {
var value = node[query.name]

if (value == null) return false

// If this is an array, and the query is contained in it, return true.
if (
typeof value === 'object' &&
'length' in value &&
value.indexOf(query.value) > -1
) {
return true
}

// For all other values, return whether this is an exact match.
return String(value) === query.value
}

// [attr^=value]
function begins(query, node) {
var value = node[query.name]

return (
typeof value === 'string' &&
value.slice(0, query.value.length) === query.value
)
}

// [attr$=value]
function ends(query, node) {
var value = node[query.name]

return (
typeof value === 'string' &&
value.slice(-query.value.length) === query.value
)
}

// [attr*=value]
function containsString(query, node) {
var value = node[query.name]
return typeof value === 'string' && value.indexOf(query.value) > -1
}

/* istanbul ignore next - Shouldn’t be invoked, Parser throws an error instead. */
function unknownOperator(query) {
throw new Error('Unknown operator `' + query.operator + '`')
}
32 changes: 0 additions & 32 deletions lib/collector.js

This file was deleted.

107 changes: 0 additions & 107 deletions lib/match-node.js

This file was deleted.

7 changes: 7 additions & 0 deletions lib/name.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict'

module.exports = match

function match(query, node) {
return query.tagName === '*' || query.tagName === node.type
}
181 changes: 181 additions & 0 deletions lib/nest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
'use strict'

module.exports = match

var zwitch = require('zwitch')

var own = {}.hasOwnProperty

var handle = zwitch('nestingOperator', {
unknown: unknownNesting,
invalid: topScan, // `undefined` is the top query selector.
handlers: {
null: descendant, // `null` is the descendant combinator.
'>': child,
'+': adjacentSibling,
'~': generalSibling
}
})

function match(query, node, index, parent, state) {
return handle(query, node, index, parent, state)
}

/* istanbul ignore next - Shouldn’t be invoked, parser gives correct data. */
function unknownNesting(query) {
throw new Error('Unexpected nesting `' + query.nestingOperator + '`')
}

function topScan(query, node, index, parent, state) {
/* istanbul ignore if - Shouldn’t happen. */
if (parent) {
throw new Error('topScan is supposed to be called from the root node')
}

state.iterator.apply(null, arguments)

if (!state.shallow) descendant.apply(this, arguments)
}

function descendant(query, node, index, parent, state) {
var previous = state.iterator

state.iterator = iterator

child.apply(this, arguments)

function iterator(_, node, index, parent, state) {
state.iterator = previous
previous.apply(this, arguments)
state.iterator = iterator

if (state.one && state.found) return

child.call(this, query, node, index, parent, state)
}
}

function child(query, node, index, parent, state) {
if (!node.children || !node.children.length) return

walkIterator(query, node, state).each().done()
}

function adjacentSibling(query, node, index, parent, state) {
/* istanbul ignore if - Shouldn’t happen. */
if (!parent) return

walkIterator(query, parent, state)
.prefillTypeIndex(0, ++index)
.each(index, ++index)
.prefillTypeIndex(index)
.done()
}

function generalSibling(query, node, index, parent, state) {
/* istanbul ignore if - Shouldn’t happen. */
if (!parent) return

walkIterator(query, parent, state)
.prefillTypeIndex(0, ++index)
.each(index)
.done()
}

// Handles typeIndex and typeCount properties for every walker.
function walkIterator(query, parent, state) {
var typeIndex = state.index && createTypeIndex()
var siblings = parent.children
var delayed = []

return {
prefillTypeIndex: rangeDefaults(prefillTypeIndex),
each: rangeDefaults(each),
done: done
}

function done() {
var index = -1

while (++index < delayed.length) {
delayed[index]()
if (state.one && state.found) break
}

return this
}

function prefillTypeIndex(start, end) {
if (typeIndex) {
while (start < end) {
typeIndex(siblings[start])
start++
}
}

return this
}

function each(start, end) {
var child = siblings[start]
var index
var nodeIndex

if (start >= end) return this

if (typeIndex) {
nodeIndex = typeIndex.nodes
index = typeIndex(child)
delayed.push(delay)
} else {
state.iterator(query, child, start, parent, state)
}

// Stop if we’re looking for one node and it’s already found.
if (state.one && state.found) return this

return each.call(this, start + 1, end)

function delay() {
state.typeIndex = index
state.nodeIndex = nodeIndex
state.typeCount = typeIndex.count(child)
state.nodeCount = typeIndex.nodes
state.iterator(query, child, start, parent, state)
}
}

function rangeDefaults(iterator) {
return rangeDefault

function rangeDefault(start, end) {
if (start == null || start < 0) start = 0
if (end == null || end > siblings.length) end = siblings.length
return iterator.call(this, start, end)
}
}
}

function createTypeIndex() {
var counts = {}

index.count = count
index.nodes = 0

return index

function index(node) {
var type = node.type

index.nodes++

if (!own.call(counts, type)) counts[type] = 0

// Note: `++` is intended to be postfixed!
return counts[type]++
}

function count(node) {
return counts[node.type]
}
}
65 changes: 65 additions & 0 deletions lib/parse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'use strict'

module.exports = parse

var Parser = require('css-selector-parser').CssSelectorParser
var zwitch = require('zwitch')
var nthCheck = require('nth-check').default

var nth = ['nth-child', 'nth-last-child', 'nth-of-type', 'nth-last-of-type']

var parser = new Parser()

var compile = zwitch('type', {
handlers: {
selectors: selectors,
ruleSet: ruleSet,
rule: rule
}
})

parser.registerAttrEqualityMods('~', '^', '$', '*')
parser.registerSelectorPseudos('any', 'matches', 'not', 'has')
parser.registerNestingOperators('>', '+', '~')

function parse(selector) {
if (typeof selector !== 'string') {
throw new TypeError('Expected `string` as selector, not `' + selector + '`')
}

return compile(parser.parse(selector))
}

function selectors(query) {
var selectors = query.selectors
var index = -1

while (++index < selectors.length) {
compile(selectors[index])
}

return query
}

function ruleSet(query) {
return rule(query.rule)
}

function rule(query) {
var pseudos = query.pseudos || []
var index = -1
var pseudo

while (++index < pseudos.length) {
pseudo = pseudos[index]

if (nth.indexOf(pseudo.name) > -1) {
pseudo.value = nthCheck(pseudo.value)
pseudo.valueType = 'function'
}
}

compile(query.rule)

return query
}
203 changes: 203 additions & 0 deletions lib/pseudo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
'use strict'

module.exports = match

var zwitch = require('zwitch')
var not = require('not')
var convert = require('unist-util-is/convert')
var anything = require('./any')

var is = convert()

match.needsIndex = [
'first-child',
'first-of-type',
'last-child',
'last-of-type',
'nth-child',
'nth-last-child',
'nth-of-type',
'nth-last-of-type',
'only-child',
'only-of-type'
]

var handle = zwitch('name', {
unknown: unknownPseudo,
invalid: invalidPseudo,
handlers: {
any: matches,
blank: empty,
empty: empty,
'first-child': firstChild,
'first-of-type': firstOfType,
has: hasSelector,
'last-child': lastChild,
'last-of-type': lastOfType,
matches: matches,
not: not(matches),
'nth-child': nthChild,
'nth-last-child': nthLastChild,
'nth-of-type': nthOfType,
'nth-last-of-type': nthLastOfType,
'only-child': onlyChild,
'only-of-type': onlyOfType,
root: root,
scope: scope
}
})

function match(query, node, index, parent, state) {
var pseudos = query.pseudos
var offset = -1

while (++offset < pseudos.length) {
if (!handle(pseudos[offset], node, index, parent, state)) return false
}

return true
}

function matches(query, node, index, parent, state) {
var shallow = state.shallow
var one = state.one
var result

state.one = true
state.shallow = true

result = anything(query.value, node, state)[0] === node

state.shallow = shallow
state.one = one

return result
}

function root(query, node, index, parent) {
return is(node) && !parent
}

function scope(query, node, index, parent, state) {
return is(node) && state.scopeNodes.indexOf(node) > -1
}

function empty(query, node) {
return node.children ? !node.children.length : !('value' in node)
}

function firstChild(query, node, index, parent, state) {
assertDeep(state, query)
return state.nodeIndex === 0 // Specifically `0`, not falsey.
}

function lastChild(query, node, index, parent, state) {
assertDeep(state, query)
return state.nodeIndex === state.nodeCount - 1
}

function onlyChild(query, node, index, parent, state) {
assertDeep(state, query)
return state.nodeCount === 1
}

function nthChild(query, node, index, parent, state) {
assertDeep(state, query)
return query.value(state.nodeIndex)
}

function nthLastChild(query, node, index, parent, state) {
assertDeep(state, query)
return query.value(state.nodeCount - state.nodeIndex - 1)
}

function nthOfType(query, node, index, parent, state) {
assertDeep(state, query)
return query.value(state.typeIndex)
}

function nthLastOfType(query, node, index, parent, state) {
assertDeep(state, query)
return query.value(state.typeCount - 1 - state.typeIndex)
}

function firstOfType(query, node, index, parent, state) {
assertDeep(state, query)
return state.typeIndex === 0
}

function lastOfType(query, node, index, parent, state) {
assertDeep(state, query)
return state.typeIndex === state.typeCount - 1
}

function onlyOfType(query, node, index, parent, state) {
assertDeep(state, query)
return state.typeCount === 1
}

/* istanbul ignore next - Shouldn’t be invoked, parser gives correct data. */
function invalidPseudo() {
throw new Error('Invalid pseudo-selector')
}

function unknownPseudo(query) {
if (query.name) {
throw new Error('Unknown pseudo-selector `' + query.name + '`')
}

throw new Error('Unexpected pseudo-element or empty pseudo-class')
}

function assertDeep(state, query) {
if (state.shallow) {
throw new Error('Cannot use `:' + query.name + '` without parent')
}
}

function hasSelector(query, node, index, parent, state) {
var shallow = state.shallow
var one = state.one
var scopeNodes = state.scopeNodes
var value = appendScope(query.value)
var result

state.shallow = false
state.one = true
state.scopeNodes = [node]

result = anything(value, node, state)[0]

state.shallow = shallow
state.one = one
state.scopeNodes = scopeNodes

return result
}

function appendScope(value) {
var selector =
value.type === 'ruleSet' ? {type: 'selectors', selectors: [value]} : value
var index = -1
var rule

while (++index < selector.selectors.length) {
rule = selector.selectors[index].rule
rule.nestingOperator = null

/* istanbul ignore else - needed if new pseudo’s are added that accepts commas (such as, `:lang(en, nl)`) */
if (
!rule.pseudos ||
rule.pseudos.length !== 1 ||
rule.pseudos[0].name !== 'scope'
) {
selector.selectors[index] = {
type: 'rule',
rule: rule,
pseudos: [{name: 'scope'}]
}
}
}

return selector
}
74 changes: 0 additions & 74 deletions lib/select.js

This file was deleted.

52 changes: 0 additions & 52 deletions lib/selector.js

This file was deleted.

24 changes: 24 additions & 0 deletions lib/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict'

module.exports = test

var name = require('./name')
var attributes = require('./attribute')
var pseudos = require('./pseudo')

function test(query, node, index, parent, state) {
if (query.id) {
throw new Error('Invalid selector: id')
}

if (query.classNames) {
throw new Error('Invalid selector: class')
}

return Boolean(
node &&
(!query.tagName || name(query, node)) &&
(!query.attrs || attributes(query, node)) &&
(!query.pseudos || pseudos(query, node, index, parent, state))
)
}
23 changes: 0 additions & 23 deletions lib/type-index.js

This file was deleted.

2 changes: 1 addition & 1 deletion LICENSE → license
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2015, 2016 Eugene Sharygin
Copyright (c) 2015 Eugene Sharygin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
126 changes: 93 additions & 33 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
{
"name": "unist-util-select",
"version": "1.5.0",
"description": "Select unist nodes using css-like selectors",
"author": "Eugene Sharygin <eush77@gmail.com>",
"version": "3.0.3",
"description": "unist utility to select nodes with CSS-like selectors",
"license": "MIT",
"scripts": {
"test": "tape test/*.js"
},
"files": [
"index.js",
"lib/"
],
"homepage": "https://github.com/eush77/unist-util-select",
"repository": "eush77/unist-util-select",
"bugs": {
"url": "https://github.com/eush77/unist-util-select/issues"
},
"keywords": [
"unist",
"unist-util",
"util",
"utility",
"visit",
"tree",
"ast",
"node",
"visit",
"walk",
"select",
"selector",
"child",
"descendant",
"sibling",
@@ -25,27 +24,88 @@
"expression",
"filter",
"find",
"match",
"ast",
"mdast",
"node",
"retext",
"select",
"selector",
"tree",
"unist",
"util",
"utility",
"visit",
"walk"
"match"
],
"repository": "syntax-tree/unist-util-select",
"bugs": "https://github.com/syntax-tree/unist-util-select/issues",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
},
"author": "Eugene Sharygin <eush77@gmail.com>",
"contributors": [
"Eugene Sharygin <eush77@gmail.com>",
"Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)",
"Christian Murphy <christian.murphy.42@gmail.com>"
],
"files": [
"index.js",
"lib/",
"types/index.d.ts"
],
"types": "types/index.d.ts",
"dependencies": {
"css-selector-parser": "^1.1.0",
"debug": "^2.2.0",
"nth-check": "^1.0.1"
"css-selector-parser": "^1.0.0",
"not": "^0.1.0",
"nth-check": "^2.0.0",
"unist-util-is": "^4.0.0",
"zwitch": "^1.0.0"
},
"devDependencies": {
"tape": "^4.2.0",
"unist-builder": "^1.0.1"
"dtslint": "^4.0.0",
"nyc": "^15.0.0",
"prettier": "^2.0.0",
"remark-cli": "^9.0.0",
"remark-preset-wooorm": "^8.0.0",
"tape": "^5.0.0",
"unist-builder": "^2.0.0",
"xo": "^0.35.0"
},
"scripts": {
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
"test-api": "node test",
"test-coverage": "nyc --reporter lcov tape test/index.js",
"test-types": "dtslint types",
"test": "npm run format && npm run test-coverage && npm run test-types"
},
"nyc": {
"check-coverage": true,
"lines": 100,
"functions": 100,
"branches": 100
},
"prettier": {
"tabWidth": 2,
"useTabs": false,
"singleQuote": true,
"bracketSpacing": false,
"semi": false,
"trailingComma": "none"
},
"xo": {
"prettier": true,
"esnext": false,
"ignore": [
"types"
],
"rules": {
"eqeqeq": [
"error",
"always",
{
"null": "ignore"
}
],
"no-eq-null": "off",
"max-params": "off",
"unicorn/explicit-length-check": "off",
"unicorn/prefer-includes": "off",
"unicorn/prefer-reflect-apply": "off"
}
},
"remarkConfig": {
"plugins": [
"preset-wooorm"
]
}
}
233 changes: 233 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
# unist-util-select

[![Build][build-badge]][build]
[![Coverage][coverage-badge]][coverage]
[![Downloads][downloads-badge]][downloads]
[![Size][size-badge]][size]
[![Sponsors][sponsors-badge]][collective]
[![Backers][backers-badge]][collective]
[![Chat][chat-badge]][chat]

[**unist**][unist] utility with equivalents for `querySelector`,
`querySelectorAll`, and `matches`.

Note that the DOM has references to their parent nodes, meaning that
`document.body.matches(':last-child')` can be evaluated.
This information is not stored in unist, so selectors like that don’t work.

[View the list of supported selectors »][support]

## Install

[npm][]:

```sh
npm install unist-util-select
```

## API

### `select.matches(selector, node)`

Check that the given [node][] matches `selector`.
Returns `boolean`, whether the node matches or not.

This only checks the element itself, not the surrounding tree.
Thus, nesting in selectors is not supported (`paragraph strong`,
`paragraph > strong`), nor are selectors like `:first-child`, etc.
This only checks that the given element matches the selector.

```js
var u = require('unist-builder')
var matches = require('unist-util-select').matches

matches('strong, em', u('strong', [u('text', 'important')])) // => true
matches('[lang]', u('code', {lang: 'js'}, 'console.log(1)')) // => true
```

### `select.select(selector, tree)`

Select the first node matching `selector` in the given `tree` (could be the
tree itself).
Returns the found [node][], if any.

```js
var u = require('unist-builder')
var select = require('unist-util-select').select

console.log(
select(
'code ~ :nth-child(even)',
u('blockquote', [
u('paragraph', [u('text', 'Alpha')]),
u('paragraph', [u('text', 'Bravo')]),
u('code', 'Charlie'),
u('paragraph', [u('text', 'Delta')]),
u('paragraph', [u('text', 'Echo')])
])
)
)
```

Yields:

```js
{type: 'paragraph', children: [{type: 'text', value: 'Delta'}]}
```

### `select.selectAll(selector, tree)`

Select all nodes matching `selector` in the given `tree` (could include the
tree itself).
Returns all found [node][]s, if any.

```js
var u = require('unist-builder')
var selectAll = require('unist-util-select').selectAll

console.log(
selectAll(
'code ~ :nth-child(even)',
u('blockquote', [
u('paragraph', [u('text', 'Alpha')]),
u('paragraph', [u('text', 'Bravo')]),
u('code', 'Charlie'),
u('paragraph', [u('text', 'Delta')]),
u('paragraph', [u('text', 'Echo')]),
u('paragraph', [u('text', 'Foxtrot')]),
u('paragraph', [u('text', 'Golf')])
])
)
)
```

Yields:

```js
[
{type: 'paragraph', children: [{type: 'text', value: 'Delta'}]},
{type: 'paragraph', children: [{type: 'text', value: 'Foxtrot'}]}
]
```

## Support

* [x] `*` (universal selector)
* [x] `,` (multiple selector)
* [x] `paragraph` (type selector)
* [x] `blockquote paragraph` (combinator: descendant selector)
* [x] `blockquote > paragraph` (combinator: child selector)
* [x] `code + paragraph` (combinator: adjacent sibling selector)
* [x] `code ~ paragraph` (combinator: general sibling selector)
* [x] `[attr]` (attribute existence, checks that the value on the tree is not
nullish)
* [x] `[attr=value]` (attribute equality, this stringifies values on the tree)
* [x] `[attr^=value]` (attribute begins with, only works on strings)
* [x] `[attr$=value]` (attribute ends with, only works on strings)
* [x] `[attr*=value]` (attribute contains, only works on strings)
* [x] `[attr~=value]` (attribute contains, checks if `value` is in the array,
if there’s an array on the tree, otherwise same as attribute equality)
* [x] `:any()` (functional pseudo-class, use `:matches` instead)
* [x] `:has()` (functional pseudo-class)
Relative selectors (`:has(> img)`) are not supported, but `:scope` is
* [x] `:matches()` (functional pseudo-class)
* [x] `:not()` (functional pseudo-class)
* [x] `:blank` (pseudo-class, blank and empty are the same: a parent without
children, or a node without value)
* [x] `:empty` (pseudo-class, blank and empty are the same: a parent without
children, or a node without value)
* [x] `:root` (pseudo-class, matches the given node)
* [x] `:scope` (pseudo-class, matches the given node)
* [x] \* `:first-child` (pseudo-class)
* [x] \* `:first-of-type` (pseudo-class)
* [x] \* `:last-child` (pseudo-class)
* [x] \* `:last-of-type` (pseudo-class)
* [x] \* `:only-child` (pseudo-class)
* [x] \* `:only-of-type` (pseudo-class)
* [x] \* `:nth-child()` (functional pseudo-class)
* [x] \* `:nth-last-child()` (functional pseudo-class)
* [x] \* `:nth-last-of-type()` (functional pseudo-class)
* [x] \* `:nth-of-type()` (functional pseudo-class)

###### Notes

* \* — Not supported in `matches`

## Related

* [`unist-util-filter`](https://github.com/syntax-tree/unist-util-filter)
— Create a new tree with all nodes that pass a test
* [`unist-util-map`](https://github.com/syntax-tree/unist-util-map)
— Create a new tree with all nodes mapped by a given function
* [`unist-util-flatmap`](https://gitlab.com/staltz/unist-util-flatmap)
— Create a new tree by mapping (to an array) with the given function
* [`unist-util-is`](https://github.com/syntax-tree/unist-util-is)
— Check if a node passes a test
* [`unist-util-remove`](https://github.com/syntax-tree/unist-util-remove)
— Remove nodes from trees
* [`unist-util-remove-position`](https://github.com/syntax-tree/unist-util-remove-position)
— Remove positional info from trees
* [`unist-util-visit`](https://github.com/syntax-tree/unist-util-visit)
— Recursively walk over nodes
* [`unist-util-visit-parents`](https://github.com/syntax-tree/unist-util-visit-parents)
— Like `visit`, but with a stack of parents
* [`unist-builder`](https://github.com/syntax-tree/unist-builder)
— Helper for creating trees

## Contribute

See [`contributing.md` in `syntax-tree/.github`][contributing] for ways to get
started.
See [`support.md`][help] for ways to get help.

This project has a [code of conduct][coc].
By interacting with this repository, organization, or community you agree to
abide by its terms.

## License

[MIT][license] © Eugene Sharygin

<!-- Definitions -->

[build-badge]: https://github.com/syntax-tree/unist-util-select/workflows/main/badge.svg

[build]: https://github.com/syntax-tree/unist-util-select/actions

[coverage-badge]: https://img.shields.io/codecov/c/github/syntax-tree/unist-util-select.svg

[coverage]: https://codecov.io/github/syntax-tree/unist-util-select

[downloads-badge]: https://img.shields.io/npm/dm/unist-util-select.svg

[downloads]: https://www.npmjs.com/package/unist-util-select

[size-badge]: https://img.shields.io/bundlephobia/minzip/unist-util-select.svg

[size]: https://bundlephobia.com/result?p=unist-util-select

[sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg

[backers-badge]: https://opencollective.com/unified/backers/badge.svg

[collective]: https://opencollective.com/unified

[chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg

[chat]: https://github.com/syntax-tree/unist/discussions

[npm]: https://docs.npmjs.com/cli/install

[license]: license

[contributing]: https://github.com/syntax-tree/.github/blob/HEAD/contributing.md

[help]: https://github.com/syntax-tree/.github/blob/HEAD/support.md

[coc]: https://github.com/syntax-tree/.github/blob/HEAD/code-of-conduct.md

[unist]: https://github.com/syntax-tree/unist

[node]: https://github.com/syntax-tree/unist#node

[support]: #support
90 changes: 90 additions & 0 deletions test/all.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
'use strict'

var test = require('tape')
var u = require('unist-builder')
var selectAll = require('..').selectAll

test('all together now', function (t) {
t.deepEqual(
selectAll(
'a > b[d]:nth-of-type(odd)',
u('root', [
u('a', [
u('b', {d: 1}, 'Alpha'),
u('c', 'Bravo'),
u('b', 'Charlie'),
u('c', 'Delta'),
u('b', 'Echo'),
u('c', 'Foxtrot')
])
])
),
[u('b', {d: 1}, 'Alpha')]
)

t.deepEqual(
selectAll(
'[d] ~ c:nth-of-type(even)',
u('root', [
u('a', [
u('b', 'Alpha'),
u('c', 'Bravo'),
u('b', {d: 1}, 'Charlie'),
u('c', 'Delta'),
u('b', 'Echo'),
u('c', 'Foxtrot'),
u('b', 'Golf'),
u('c', 'Hotel')
])
])
),
[u('c', 'Delta'), u('c', 'Hotel')]
)

t.deepEqual(
selectAll(
'[d] + c:nth-of-type(even)',
u('root', [
u('a', [
u('b', 'Alpha'),
u('c', 'Bravo'),
u('b', {d: 1}, 'Charlie'),
u('c', 'Delta'),
u('b', 'Echo'),
u('c', 'Foxtrot'),
u('b', 'Golf'),
u('c', 'Hotel')
])
])
),
[u('c', 'Delta')]
)

t.deepEqual(
selectAll(
'[d], :nth-of-type(even), [e]',
u('root', [
u('a', [
u('b', {e: 3}, 'Alpha'),
u('c', 'Bravo'),
u('b', {d: 1}, 'Charlie'),
u('c', 'Delta'),
u('b', 'Echo'),
u('c', {d: 2, e: 4}, 'Foxtrot'),
u('b', 'Golf'),
u('c', 'Hotel')
])
])
),
[
u('b', {d: 1}, 'Charlie'),
u('c', {d: 2, e: 4}, 'Foxtrot'),
u('c', 'Delta'),
u('b', 'Golf'),
u('c', 'Hotel'),
u('b', {e: 3}, 'Alpha')
]
)

t.end()
})
15 changes: 0 additions & 15 deletions test/collector.js

This file was deleted.

13 changes: 0 additions & 13 deletions test/curried.js

This file was deleted.

8 changes: 8 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict'

/* eslint-disable import/no-unassigned-import */
require('./matches')
require('./select')
require('./select-all')
require('./all')
/* eslint-enable import/no-unassigned-import */
224 changes: 0 additions & 224 deletions test/lib/ast.js

This file was deleted.

8 changes: 0 additions & 8 deletions test/lib/path.js

This file was deleted.

612 changes: 612 additions & 0 deletions test/matches.js

Large diffs are not rendered by default.

734 changes: 734 additions & 0 deletions test/select-all.js

Large diffs are not rendered by default.

17 changes: 0 additions & 17 deletions test/select-one.js

This file was deleted.

1,052 changes: 700 additions & 352 deletions test/select.js

Large diffs are not rendered by default.

27 changes: 0 additions & 27 deletions test/type-index.js

This file was deleted.

29 changes: 29 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// TypeScript Version: 3.5

import {Node} from 'unist'

/**
* Is there a match for the given selector in the Unist tree
*
* @param selector CSS-like selector
* @param tree Unist node or tree of nodes to search
*/
declare function matches(selector: string, tree: Node): boolean

/**
* Find first Node that matches selector
*
* @param selector CSS-like selector
* @param tree Unist node or tree of nodes to search
*/
declare function select(selector: string, tree: Node): Node | null

/**
* Find all Nodes that match selector
*
* @param selector CSS-like selector
* @param tree Unist node or tree of nodes to search
*/
declare function selectAll(selector: string, tree: Node): Node[]

export {matches, select, selectAll}
10 changes: 10 additions & 0 deletions types/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"lib": ["es2015"],
"strict": true,
"baseUrl": ".",
"paths": {
"unist-util-select": ["index.d.ts"]
}
}
}
7 changes: 7 additions & 0 deletions types/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "dtslint/dtslint.json",
"rules": {
"whitespace": false,
"semicolon": false
}
}
13 changes: 13 additions & 0 deletions types/unist-util-select-tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {matches, select, selectAll} from 'unist-util-select'

matches() // $ExpectError
matches('*') // $ExpectError
matches('*', {type: 'root'}) // $ExpectType boolean

select() // $ExpectError
select('*') // $ExpectError
select('*', {type: 'root'}) // $ExpectType Node | null

selectAll() // $ExpectError
selectAll('*') // $ExpectError
selectAll('*', {type: 'root'}) // $ExpectType Node[]