[Proposal] we should provide a function to build a fork with defs extracted from @babel/types
- 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