New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embedded Traversers #9438

Open
harrysolovay opened this Issue Feb 1, 2019 · 1 comment

Comments

Projects
None yet
2 participants
@harrysolovay
Copy link

harrysolovay commented Feb 1, 2019

I'm creating a plugin that works with embedded function calls:

const ast = parse(`
  const a = greatGrandParent(
    grandMother(
      motherOne(
        daughterOne(),
        sonOne(),
      ),
      fatherOne(
        daughterTwo(),
        sonTwo(),
      )
    ),
    grandFather(
      motherTwo(
        daughterThree(),
        sonThree(),
      ),
      fatherTwo(
        daughterFour(),
        sonFour(),
      )
    )
  )
`)

And is meant to transform these calls into something like this:

// const a = [
//   greatGrandParent,
//   [
//     [
//       grandMother,
//       [
//         [motherOne, [[daughterOne, []], [sonOne, []]]],
//         [fatherOne, [[daughterTwo, []], [sonTwo, []]]],
//       ],
//     ],
//     [
//       grandFather,
//       [
//         [motherTwo, [[daughterThree, []], [sonThree, []]]],
//         [fatherTwo, [[daughterFour, []], [sonFour, []]]],
//       ],
//     ],
//   ],
// ]

Here's where it gets tricky... some of the functions will be called in a curry-ish form:

embeddedFn()(
  anotherFn()
)

Some of them will be called via tagged template expressions:

someFn`single text arg`

Some of them will be called via both:

someFn({some: 'prop'})`tag contents`

The solution I'd like most is to create a visitor that identifies and extracts the arguments from these varying-form call expressions. It then needs to set some state for each of these call expressions and traverse the argument nodes with the same visitor. Because of the variation of call expressions (I guess some of them aren't technically call expressions...) it's easier to write if I stop the current traversal, and call traverse again on the argument nodes. For the example above, here's what I'm currently trying. Any help would be greatly appreciated!:

const visitor = {
  CallExpression: {
    enter(path) {
      const {callee, arguments: args} = path.node
      path.state = {callee, args}
      if (args.length >= 1) {
        path.get('node.arguments').traverse(visitor) // cannot get `traverse` of undefined
      }
      path.stop() // keeps sibling nodes from being processed
    },
    exit(path) {
      const {callee, args} = path.state
      const replacement = t.arrayExpression([callee, t.arrayExpression(args)])
      path.replaceWith(replacement)
    },
  },
}

traverse(ast, visitor)
console.log(generate(ast).code)

Thank you for taking the time to look this over! Any help would be greatly appreciated :)

@babel-bot

This comment has been minimized.

Copy link
Collaborator

babel-bot commented Feb 1, 2019

Hey @harrysolovay! We really appreciate you taking the time to report an issue. The collaborators
on this project attempt to help as many people as possible, but we're a limited number of volunteers,
so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack
community
that typically always has someone willing to help. You can sign-up here
for an invite.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment