Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser interprets it as a CallExpression (positional) not a CallExpressionKw. But then if a codemod wants to add a tag to it, it tries adding a kwarg called 'tag', which fails because the CallExpression doesn't need kwargs inserted into it. The fix is: change the node from CallExpression to CallExpressionKw, and update getNodeFromPath to take a 'replacement' arg, so we can replace the old node with the new node in the AST.
This commit is contained in:
committed by
Nick Cameron
parent
cd022fb087
commit
99866b5f9a
@ -51,7 +51,8 @@ export function getNodeFromPath<T>(
|
||||
node: Program,
|
||||
path: PathToNode,
|
||||
stopAt?: SyntaxType | SyntaxType[],
|
||||
returnEarly = false
|
||||
returnEarly = false,
|
||||
replacement?: any
|
||||
):
|
||||
| {
|
||||
node: T
|
||||
@ -63,9 +64,14 @@ export function getNodeFromPath<T>(
|
||||
let stopAtNode = null
|
||||
let successfulPaths: PathToNode = []
|
||||
let pathsExplored: PathToNode = []
|
||||
let parent = null as any
|
||||
let parentEdge = null
|
||||
for (const pathItem of path) {
|
||||
if (typeof currentNode[pathItem[0]] !== 'object') {
|
||||
if (stopAtNode) {
|
||||
if (replacement && parent && parentEdge) {
|
||||
parent[parentEdge] = replacement
|
||||
}
|
||||
return {
|
||||
node: stopAtNode,
|
||||
shallowPath: pathsExplored,
|
||||
@ -74,6 +80,8 @@ export function getNodeFromPath<T>(
|
||||
}
|
||||
return new Error('not an object')
|
||||
}
|
||||
parent = currentNode
|
||||
parentEdge = pathItem[0]
|
||||
currentNode = currentNode?.[pathItem[0]]
|
||||
successfulPaths.push(pathItem)
|
||||
if (!stopAtNode) {
|
||||
@ -97,6 +105,9 @@ export function getNodeFromPath<T>(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (replacement && parent && parentEdge) {
|
||||
parent[parentEdge] = replacement
|
||||
}
|
||||
return {
|
||||
node: stopAtNode || currentNode,
|
||||
shallowPath: pathsExplored,
|
||||
|
||||
@ -2466,15 +2466,30 @@ function addTag(tagIndex = 2): addTagFn {
|
||||
function addTagKw(): addTagFn {
|
||||
return ({ node, pathToNode }) => {
|
||||
const _node = { ...node }
|
||||
const callExpr = getNodeFromPath<Node<CallExpressionKw>>(
|
||||
// We have to allow for the possibility that the path is actually to a call expression.
|
||||
// That's because if the parser reads something like `close()`, it doesn't know if this
|
||||
// is a keyword or positional call.
|
||||
// In fact, even something like `close(%)` could be either (because we allow 1 unlabeled
|
||||
// starting param).
|
||||
const callExpr = getNodeFromPath<Node<CallExpressionKw | CallExpression>>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpressionKw'
|
||||
['CallExpressionKw', 'CallExpression']
|
||||
)
|
||||
if (err(callExpr)) return callExpr
|
||||
|
||||
const { node: primaryCallExp } = callExpr
|
||||
|
||||
// If the original node is a call expression, we'll need to change it to a call with keyword args.
|
||||
const primaryCallExp: CallExpressionKw =
|
||||
callExpr.node.type === 'CallExpressionKw'
|
||||
? callExpr.node
|
||||
: {
|
||||
type: 'CallExpressionKw',
|
||||
callee: callExpr.node.callee,
|
||||
unlabeled: callExpr.node.arguments.length
|
||||
? callExpr.node.arguments[0]
|
||||
: null,
|
||||
arguments: [],
|
||||
}
|
||||
const tagArg = findKwArg(ARG_TAG, primaryCallExp)
|
||||
const tagDeclarator =
|
||||
tagArg || createTagDeclarator(findUniqueName(_node, 'seg', 2))
|
||||
@ -2483,6 +2498,17 @@ function addTagKw(): addTagFn {
|
||||
const labeledArg = createLabeledArg(ARG_TAG, tagDeclarator)
|
||||
primaryCallExp.arguments.push(labeledArg)
|
||||
}
|
||||
|
||||
// If we changed the node, we must replace the old node with the new node in the AST.
|
||||
const mustReplaceNode = primaryCallExp.type !== callExpr.node.type
|
||||
if (mustReplaceNode) {
|
||||
getNodeFromPath(_node, pathToNode, ['CallExpression'], false, {
|
||||
...primaryCallExp,
|
||||
start: callExpr.node.start,
|
||||
end: callExpr.node.end,
|
||||
})
|
||||
}
|
||||
|
||||
if ('value' in tagDeclarator) {
|
||||
// Now TypeScript knows tagDeclarator has a value property
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user