basant307/AI_Governance_Project
048
1# selderee2 345[](https://github.com/mxxii/selderee/blob/main/LICENSE)6[](https://www.npmjs.com/package/selderee)7 8**Sel**ectors **de**cision t**ree** - pick matching selectors, fast.9 10----11 12 13## What is it for14 15The problem statement: there are multiple CSS selectors with attached handlers, and a HTML DOM to process. For each HTML Element a matching handler has to be found and applied.16 17The naive approach is to walk through the DOM and test each and every selector against each Element. This means *O(n\*m)* complexity.18 19It is pretty clear though that if we have selectors that share something in common then we can reduce the number of checks.20 21The main `selderee` package offers the selectors tree structure. Runnable decision functions for specific DOM implementations are built via plugins.22 23 24## Limitations25 26- Pseudo-classes and pseudo-elements are not supported by the underlying library [parseley](https://github.com/mxxii/parseley) (yet?);27- General siblings (`~`), descendants (` `) and same column combinators (`||`) are also not supported.28 29 30## `selderee` vs `css-select`31 32[css-select](https://github.com/fb55/css-select) - a CSS selector compiler & engine.33 34| Feature | `selderee` | `css-select` |35| ------------------------------------- | :--------: | :----------: |36| Support for `htmlparser2` DOM AST | plugin | + |37| "Compiles" into a function | + | + |38| Pick selector(s) for a given Element | + | |39| Query Element(s) for a given selector | | + |40 41 42## Packages43 44| Package | Version | Folder | Changelog |45| --------- | --------- | --------- | --------- |46| [selderee](https://www.npmjs.com/package/selderee) | [](https://www.npmjs.com/package/selderee) | [/packages/selderee](https://github.com/mxxii/selderee/tree/main/packages/selderee/) | [changelog](https://github.com/mxxii/selderee/blob/main/packages/selderee/CHANGELOG.md) |47| [@selderee/plugin-htmlparser2](https://www.npmjs.com/package/@selderee/plugin-htmlparser2) | [](https://www.npmjs.com/package/@selderee/plugin-htmlparser2) | [/packages/plugin-htmlparser2](https://github.com/mxxii/selderee/tree/main/packages/plugin-htmlparser2/) | [changelog](https://github.com/mxxii/selderee/blob/main/packages/plugin-htmlparser2/CHANGELOG.md) |48 49 50## Install51 52```shell53> npm i selderee @selderee/plugin-htmlparser254```55 56 57## Documentation58 59- [API](https://github.com/mxxii/selderee/blob/main/docs/index.md)60 61 62## Usage example63 64```js65const htmlparser2 = require('htmlparser2');66const util = require('util');67 68const { DecisionTree, Treeify } = require('selderee');69const { hp2Builder } = require('@selderee/plugin-htmlparser2');70 71const selectorValuePairs = [72 ['p', 'A'],73 ['p.foo[bar]', 'B'],74 ['p[class~=foo]', 'C'],75 ['div.foo', 'D'],76 ['div > p.foo', 'E'],77 ['div > p', 'F'],78 ['#baz', 'G']79];80 81// Make a tree structure from all given selectors.82const selectorsDecisionTree = new DecisionTree(selectorValuePairs);83 84// `treeify` builder produces a string output for testing and debug purposes.85// `treeify` expects string values attached to each selector.86const prettyTree = selectorsDecisionTree.build(Treeify.treeify);87console.log(prettyTree);88 89const html = /*html*/`<html><body>90 <div><p class="foo qux">second</p></div>91</body></html>`;92const dom = htmlparser2.parseDocument(html);93const element = dom.children[0].children[0].children[1].children[0];94 95// `hp2Builder` produces a picker that can pick values96// from the selectors tree.97const picker = selectorsDecisionTree.build(hp2Builder);98 99// Get all matches100const allMatches = picker.pickAll(element);101console.log(util.inspect(allMatches, { breakLength: 70, depth: null }));102 103// or get the value from the most specific match.104const bestMatch = picker.pick1(element);105console.log(`Best matched value: ${bestMatch}`);106```107 108<details><summary>Example output</summary>109 110```text111▽112├─◻ Tag name113│ ╟─◇ = p114│ ║ ┠─▣ Attr value: class115│ ║ ┃ ╙─◈ ~= "foo"116│ ║ ┃ ┠─◨ Attr presence: bar117│ ║ ┃ ┃ ┖─◁ #1 [0,2,1] B118│ ║ ┃ ┠─◁ #2 [0,1,1] C119│ ║ ┃ ┖─◉ Push element: >120│ ║ ┃ └─◻ Tag name121│ ║ ┃ ╙─◇ = div122│ ║ ┃ ┖─◁ #4 [0,1,2] E123│ ║ ┠─◁ #0 [0,0,1] A124│ ║ ┖─◉ Push element: >125│ ║ └─◻ Tag name126│ ║ ╙─◇ = div127│ ║ ┖─◁ #5 [0,0,2] F128│ ╙─◇ = div129│ ┖─▣ Attr value: class130│ ╙─◈ ~= "foo"131│ ┖─◁ #3 [0,1,1] D132└─▣ Attr value: id133 ╙─◈ = "baz"134 ┖─◁ #6 [1,0,0] G135[ { index: 2, value: 'C', specificity: [ 0, 1, 1 ] },136 { index: 4, value: 'E', specificity: [ 0, 1, 2 ] },137 { index: 0, value: 'A', specificity: [ 0, 0, 1 ] },138 { index: 5, value: 'F', specificity: [ 0, 0, 2 ] } ]139Best matched value: E140```141 142*Some gotcha: you may notice the check for `#baz` has to be performed every time the decision tree is called. If it happens to be `p#baz` or `div#baz` or even `.foo#baz` - it would be much better to write it like this. Deeper, narrower tree means less checks on average. (in case of `.foo#baz` the class check might finally outweigh the tag name check and rebalance the tree.)*143 144</details>145 146 147## Development148 149Targeting Node.js version >=14.150 151Monorepo uses NPM v7 workspaces (make sure v7 is installed when used with Node.js v14.)152 