Query any object with CSS-like selectors. Particularly useful for querying ASTs of programming languages.
import { js, pipe, select, all } from 'affe'
const config = await readFile('eslint.config.js', 'utf8')
//
// 👇 Let's find out which eslint rules are specified
// in the config.
//
const rules = await pipe(
// 👇 parse the config as JS to AST
js(config),
// 👇 select linting rules
select(`
export property[name=rules]
> value > object > property > key > *
`),
// 👇 get all of the names of the rules
all(),
)
console.log(rules)
// > semi
// > prefer-const
Node:
npm i affe
Browser / Deno:
import { js } from 'https://esm.sh/affe'
affe
transforms objects to a DOM-inspired format, so you can query them with CSS-like selectors. This can be paired with parsers of differnt languages to conveniently inspect code written in said language.
affe
provides out-of-the-box support for JS/JSX (using espree):
import { jsx, pipe, select, pick, all } from 'affe'
const code = jsx`
export default ({ name, style }) => (
<div className={style}>Hello, {name}!</div>
)
`
//
// 👇 Let's find out the name of the properties
// of the exported component.
//
const params = pipe(
code,
select('export params property key *'),
pick(node => node.name ?? node.value),
all(),
)
console.log(params)
// > [name, style]
👉 Use tag
to create a language tag for any other language:
import * as csstree from 'css-tree'
import { tag } from 'affe'
export const css = tag({
parse: csstree.parse,
})
Which can then be used like this:
import { css } from './css'
import { pipe, select, pick, all } from 'affe'
const code = css`
.foo {
color: red;
}
.bar {
color: blue;
}
`
const classes = await pipe(
code,
select('ClassSelector'),
pick(node => node.name),
all(),
)
console.log(classess)
// > [ foo, bar ]
👉 Use query
to inspect any object:
import { pipe, query, select, pick, first } from 'affe'
const graph = {
vertices: [
{
id: 'a',
label: 'Node A',
edges: [
{ to: 'a', label: 'loopback' },
{ to: 'b' }
]
},
{
id: 'b',
label: 'Node B',
edges: [
{ to: 'a' }
]
}
]
}
// 👇 lets find a node with a labeled edge
const label = await pipe(
query(() => graph),
select('node:has(>edges [label])'),
pick(node => node.label),
first(),
)
console.log(label)
// > Node A
// 👇 now lets find nodes with an
// unlabeled edge going to them
const ids = await pipe(
query(() => graph),
select('edges :not([label])'),
pick(node => node.to),
all(),
)
console.log(ids)
// > [ a, b ]
You need node, NPM to start and git to start.
# clone the code
git clone git@github.com:loreanvictor/affe.git
# install stuff
npm i
Make sure all checks are successful on your PRs. This includes all tests passing, high code coverage, correct typings and abiding all the linting rules. The code is typed with TypeScript, Jest is used for testing and coverage reports, ESLint and TypeScript ESLint are used for linting. Subsequently, IDE integrations for TypeScript and ESLint would make your life much easier (for example, VSCode supports TypeScript out of the box and has this nice ESLint plugin), but you could also use the following commands:
# run tests
npm test
# check code coverage
npm run coverage
# run linter
npm run lint
# run type checker
npm run typecheck