@tanstack/react-query
Version:
Hooks for managing, caching and syncing asynchronous and remote data in React
165 lines (135 loc) • 5.1 kB
JavaScript
class UnprocessableKeyError extends Error {
constructor(message) {
super(message)
this.name = 'UnprocessableKeyError'
}
}
module.exports = ({ jscodeshift, root, filePath, keyName = 'queryKey' }) => {
const isArrayExpression = (node) =>
jscodeshift.match(node, { type: jscodeshift.ArrayExpression.name })
const isStringLiteral = (node) =>
jscodeshift.match(node, { type: jscodeshift.StringLiteral.name }) ||
jscodeshift.match(node, { type: jscodeshift.Literal.name })
const isTemplateLiteral = (node) =>
jscodeshift.match(node, { type: jscodeshift.TemplateLiteral.name })
const findVariableDeclaration = (node) => {
const declarations = root
.find(jscodeshift.VariableDeclarator, {
id: {
type: jscodeshift.Identifier.name,
name: node.name,
},
})
.paths()
return declarations.length > 0 ? declarations[0] : null
}
const createKeyValue = (node) => {
// When the node is a string literal we convert it into an array of strings.
if (isStringLiteral(node)) {
return jscodeshift.arrayExpression([
jscodeshift.stringLiteral(node.value),
])
}
// When the node is a template literal we convert it into an array of template literals.
if (isTemplateLiteral(node)) {
return jscodeshift.arrayExpression([
jscodeshift.templateLiteral(node.quasis, node.expressions),
])
}
if (jscodeshift.match(node, { type: jscodeshift.Identifier.name })) {
// When the node is an identifier at first, we try to find its declaration, because we will try
// to guess its type.
const variableDeclaration = findVariableDeclaration(node)
if (!variableDeclaration) {
throw new UnprocessableKeyError(
`In file ${filePath} at line ${node.loc.start.line} the type of identifier \`${node.name}\` couldn't be recognized, so the codemod couldn't be applied. Please migrate manually.`,
)
}
const initializer = variableDeclaration.value.init
// When it's a string, we just wrap it into an array expression.
if (isStringLiteral(initializer) || isTemplateLiteral(initializer)) {
return jscodeshift.arrayExpression([node])
}
}
throw new UnprocessableKeyError(
`In file ${filePath} at line ${node.loc.start.line} the type of the \`${keyName}\` couldn't be recognized, so the codemod couldn't be applied. Please migrate manually.`,
)
}
const createKeyProperty = (node) =>
jscodeshift.property(
'init',
jscodeshift.identifier(keyName),
createKeyValue(node),
)
const getPropertyFromObjectExpression = (objectExpression, propertyName) =>
objectExpression.properties.find(
(property) => property.key.name === propertyName,
) ?? null
const buildWithTypeArguments = (node, builder) => {
const newNode = builder(node)
if (node.typeParameters) {
newNode.typeArguments = node.typeParameters
}
return newNode
}
return ({ node }) => {
// When the node doesn't have the 'original' property, that means the codemod has been already applied,
// so we don't need to do any changes.
if (!node.original) {
return node
}
const methodArguments = node.arguments
// The method call doesn't have any arguments, we have nothing to do in this case.
if (methodArguments.length === 0) {
return node
}
try {
const [firstArgument, ...restOfTheArguments] = methodArguments
if (
jscodeshift.match(firstArgument, {
type: jscodeshift.ObjectExpression.name,
})
) {
const originalKey = getPropertyFromObjectExpression(
firstArgument,
keyName,
)
if (!originalKey) {
throw new UnprocessableKeyError(
`In file ${filePath} at line ${node.loc.start.line} the \`${keyName}\` couldn't be found. Did you forget to add it?`,
)
}
const restOfTheProperties = firstArgument.properties.filter(
(item) => item.key.name !== keyName,
)
return buildWithTypeArguments(node, (originalNode) =>
jscodeshift.callExpression(originalNode.original.callee, [
jscodeshift.objectExpression([
createKeyProperty(originalKey.value),
...restOfTheProperties,
]),
...restOfTheArguments,
]),
)
}
// When the node is an array expression we just simply return it because we want query keys to be arrays.
if (isArrayExpression(firstArgument)) {
return node
}
return buildWithTypeArguments(node, (originalNode) =>
jscodeshift.callExpression(originalNode.original.callee, [
createKeyValue(firstArgument),
...restOfTheArguments,
]),
)
} catch (error) {
if (error.name === 'UnprocessableKeyError') {
if (process.env.NODE_ENV !== 'test') {
console.warn(error.message)
}
return node
}
throw error
}
}
}