Skip to content

Conversation

wooorm
Copy link
Member

@wooorm wooorm commented Jul 25, 2021

Initial checklist

  • I read the support docs
  • I read the contributing guide
  • I agree to follow the code of conduct
  • I searched issues and couldn’t find anything (or linked relevant results below)
  • If applicable, I’ve added docs and tests

Description of changes

While the signature visitParents(tree, 'emphasis', (node: Emphasis) => void) already works (that is, assuming that the tree could potentially include an emphasis node, so not hast, and that the tree does not include an type: emphasis which is otherwise different from Emphasis), the other signatures were not as smart.

Given

const root: Root = {...}
visitParent(root, (node: ?) => {})

? is now filled with any node that is root or (recursively) in root.children.

EDIT: this now also figures out the signature with a test!

@wooorm wooorm added 🦋 type/enhancement This is great to have 🧒 semver/minor This is backwards-compatible change ☂️ area/types This affects typings 🙆 yes/confirmed This is confirmed and ready to be worked on 👍 phase/yes Post is accepted and can be worked on labels Jul 25, 2021
@github-actions github-actions bot added 👋 phase/new Post is being triaged automatically and removed 👍 phase/yes Post is accepted and can be worked on labels Jul 25, 2021
@github-actions

This comment has been minimized.

@github-actions github-actions bot removed 🙆 yes/confirmed This is confirmed and ready to be worked on 👋 phase/new Post is being triaged automatically labels Jul 25, 2021
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions github-actions bot added the 🤞 phase/open Post is being triaged manually label Jul 25, 2021

export type NodeInTree<T extends Node, A = void> = T extends Parent
? T | NodeInTree<Exclude<T['children'][number], A | T>, A | T>
: T
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open to different names.
Previously I used SelfAndChildren.

We could also extract this type. Either in a repo: syntax-tree/typefest? Or in @types/unist.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about Descendant? Naming is hard.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add type utilities for several things in Unist to types/unist perhaps? 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe. Although really NodeInTree isn’t that bad either actually.

@wooorm wooorm force-pushed the get-nodes-in-tree branch from 26e4d73 to 2997a63 Compare July 25, 2021 14:47
@wooorm wooorm added the 👍 phase/yes Post is accepted and can be worked on label Jul 25, 2021
@github-actions github-actions bot removed the 🤞 phase/open Post is being triaged manually label Jul 25, 2021
type: 'text'
value: string
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m thinking it may be better to just use an existing AST for testing to ensure real world compatibility. Actually using @types/mdast here is probably even less effort.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah maybe. I thought it was wise to keep this small and contained

@wooorm
Copy link
Member Author

wooorm commented Jul 28, 2021

Fixed the types. The “catch-all” overload is no longer needed.

Re #10 (comment):
It was needed because Needle (the node type to be found) could only be explicitly given (so through TypeScript), making the “catch-all” overload required for JavaScript.
I’ve now changed the behavior of the types to:

  • find all node types in the tree
  • extract node types that match the given test in the type system.

Take for example the docs in How to narrow Nodes in TypeScript, on lists:

if (is<List>(node, 'list')) {
  // If we’re here, node is List.
  //
  // `'list'` is compared to `node.type` to make sure they match.
  // `true` means a match, `false` means no match.
  //
  // `<List>` tells TypeScript to ensure `'list'` matches `List.type` and that
  // if `'list'` matches both `node.type` and `List.type`, we know that node is
  // `List` within this if condition.
}

(although this code is for Is<>, not for Visit<>).
The <List> is no longer needed explicitly in Visit, it is inferred.

wooorm added 2 commits July 28, 2021 09:17
`assert` isn’t working for some reason in tsd, so use `unist-util-is`
instead.
@ChristianMurphy
Copy link
Member

Nice!
Is visiting a generic Node still supported? Or going forward will be always need to start with a more specific tree/node type?

@wooorm
Copy link
Member Author

wooorm commented Jul 28, 2021

If you mean that visit<SomeNodeType>(anObjectThatLooksLikeANode, 'some-node-type', (node: SomeNodeType) => {}) still works, then yes.
Because that “replaces” Tree from anObjectThatLooksLikeANode with SomeNodeType instead, and assuming that 'some-node-type' matches it, will pass that node type as node.

There are some complexities with for example visit({type: 'heading', depth: 1, children: []}, 'heading') erroring, because depth and children do not exist on Node.
Or visit(typedTree, {type: 'heading'}, (node: Heading) => {}) erroring, because TS {type: 'heading'} is turned into {type: string} which then yields all nodes instead.

expectType<never>(node)
})
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it may be good to have one or two type tests showing it works with plain Node/Parent #10 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some tests for implicit trees.

Unfortunately I was wrong. It doesn’t work :'(

visit<SomeNodeType>(anObjectThatLooksLikeANode, 'some-node-type', (node: SomeNodeType) => {})

Does not work, because anObjectThatLooksLikeANode is probably not assignable to SomeNodeType

@wooorm
Copy link
Member Author

wooorm commented Jul 29, 2021

@ChristianMurphy Still good to go? I think this should be a major?

@ChristianMurphy ChristianMurphy added 🧑 semver/major This is a change and removed 🧒 semver/minor This is backwards-compatible change labels Jul 29, 2021
Copy link
Member

@ChristianMurphy ChristianMurphy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! 👍
And yes, with some potentially breaking cases around generic Node, semver major sounds more appropriate.

@wooorm wooorm changed the title Add types to narrow what’s given to visitor based on tree Change types to base what visitor gets on tree Jul 29, 2021
@wooorm wooorm merged commit ebf54db into main Jul 29, 2021
@wooorm wooorm deleted the get-nodes-in-tree branch July 29, 2021 14:33
@github-actions

This comment has been minimized.

@wooorm wooorm added the 💪 phase/solved Post is done label Jul 29, 2021
@github-actions github-actions bot removed the 👍 phase/yes Post is accepted and can be worked on label Jul 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
☂️ area/types This affects typings 💪 phase/solved Post is done 🧑 semver/major This is a change 🦋 type/enhancement This is great to have
Development

Successfully merging this pull request may close these issues.

3 participants