@sanity/mutator
Version:
A set of models to make it easier to utilize the powerful real time collaborative features of Sanity
64 lines (52 loc) • 1.68 kB
text/typescript
import {type Expr, type PathExpr} from './types'
/**
* Splits an expression into a set of heads, tails. A head is the next leaf node to
* check for matches, and a tail is everything that follows it. Matching is done by
* matching heads, then proceedint to the matching value, splitting the tail into
* heads and tails and checking the heads against the new value, and so on.
*/
export function descend(tail: Expr): [Expr | null, PathExpr | null][] {
const [head, newTail] = splitIfPath(tail)
if (!head) {
throw new Error('Head cannot be null')
}
return spreadIfUnionHead(head, newTail)
}
// Split path in [head, tail]
function splitIfPath(tail: Expr): [Expr | null, PathExpr | null] {
if (tail.type !== 'path') {
return [tail, null]
}
const nodes = tail.nodes
if (nodes.length === 0) {
return [null, null]
}
if (nodes.length === 1) {
return [nodes[0], null]
}
return [nodes[0], {type: 'path', nodes: nodes.slice(1)}]
}
function concatPaths(path1: PathExpr | null, path2: PathExpr | null): PathExpr | null {
if (!path1 && !path2) {
return null
}
const nodes1 = path1 ? path1.nodes : []
const nodes2 = path2 ? path2.nodes : []
return {
type: 'path',
nodes: nodes1.concat(nodes2),
}
}
// Spreads a union head into several heads/tails
function spreadIfUnionHead(head: Expr, tail: PathExpr | null): [Expr | null, PathExpr | null][] {
if (head.type !== 'union') {
return [[head, tail]]
}
return head.nodes.map((node) => {
if (node.type === 'path') {
const [subHead, subTail] = splitIfPath(node)
return [subHead, concatPaths(subTail, tail)]
}
return [node, tail]
})
}