benjamn / benjamn/ast-types

[Proposal] we should provide a function to build a fork with defs extracted from @babel/types

Open
#823 4 comments 3 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
1.2k
Forks
194
Avg merge
22h 43m
Merged PRs (30d)
10

Description

I've been thinking, sometimes Babel nodes and fields are missing from the defs in `ast-types`, it's naturally going to lag behind node types and fields that they add. Why not build the defs from the information in `@babel/types`? Here's what I have so far. Seems to work well!

```ts
import typesPlugin from 'ast-types/lib/types'
import * as t from '@babel/types'
import { mapValues } from 'lodash'
import fork from 'ast-types/fork'
import { Fork } from 'ast-types/types'
import { parse } from '@babel/parser'
import nodePathPlugin from 'ast-types/lib/node-path'

function babel(fork: Fork) {
const types = fork.use(typesPlugin)
const { builtInTypes, Type } = types
const { def, or } = Type

fork.use(nodePathPlugin)

def('Node').field('type', builtInTypes.string)

function tryConvertValidate(validate: any, node?: any): any {
if (validate.type) {
switch (validate.type) {
case 'any':
return {}
case 'string':
return builtInTypes.string
case 'boolean':
return builtInTypes.boolean
case 'number':
return builtInTypes.number
case 'null':
return builtInTypes.null
case 'undefined':
return builtInTypes.undefined
}
}
if (validate.chainOf) {
for (const elem of validate.chainOf) {
const converted = tryConvertValidate(elem)
if (converted) return converted
}
}
if (validate.each) {
return [convertValidate(validate.each)]
}
if (validate.oneOfNodeTypes) {
return or(...validate.oneOfNodeTypes.map((type: string) => def(type)))
}
if (validate.oneOf) {
return node?.optional
? or(...validate.oneOf, null)
: or(...validate.oneOf)
}
if (validate.shapeOf) {
return mapValues(validate.shapeOf, (value) =>
convertValidate(value.validate, value)
)
}
if (validate.oneOfNodeOrValueTypes) {
return or(
...validate.oneOfNodeOrValueTypes.map((type: string) =>
/^[A-Z]/.test(type) ? def(type) : convertValidate({ type })
)
)
}
}

function convertValidate(validate: any, node?: any): any {
const converted = tryConvertValidate(validate, node)
if (!converted) {
throw new Error(
`couldn't determine field def for validate: ${JSON.stringify(validate)}`
)
}
return converted
}

for (const [type, fields] of Object.entries(t.NODE_FIELDS)) {
const d = def(type)
const aliases: string[] | undefined = (t.ALIAS_KEYS as any)[type]
if (aliases) {
for (const alias of aliases) {
def(alias)
}
d.bases('Node', ...aliases)
} else {
d.bases('Node')
}
for (const [field, { validate }] of Object.entries(fields)) {
d.field(field, convertValidate(validate))
}
}
}

const { visit } = fork([babel])

const ast = parse(`
function test(a, b) {
const c = a + b
}
`)

visit(ast, {
visitExpression(path) {
console.log(path.node.type)
this.traverse(path)
},
})

```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by reading the existing fork setup in ast-types/fork, ast-types/lib/types, and ast-types/lib/node-path, then compare it with @babel/types NODE_FIELDS and ALIAS_KEYS. Done should provide a reusable function that builds an ast-types fork from Babel definitions and supports the parsing and visit example shown in the issue.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.