snapshot of https://github.com/TanStack/query.git (main, 2026-09-16; blobs > 131072B omitted)
This commit is contained in:
208
packages/query-codemods/src/utils/index.cjs
Normal file
208
packages/query-codemods/src/utils/index.cjs
Normal file
@@ -0,0 +1,208 @@
|
||||
module.exports = ({ root, jscodeshift }) => {
|
||||
const findImportIdentifierOf = (importSpecifiers, identifier) => {
|
||||
const specifier = importSpecifiers
|
||||
.filter((node) => node.value.imported.name === identifier)
|
||||
.paths()
|
||||
|
||||
if (specifier.length > 0) {
|
||||
return specifier[0].value.local
|
||||
}
|
||||
|
||||
return jscodeshift.identifier(identifier)
|
||||
}
|
||||
|
||||
const findImportSpecifiers = (packageName) =>
|
||||
root
|
||||
.find(jscodeshift.ImportDeclaration, {
|
||||
source: {
|
||||
value: packageName,
|
||||
},
|
||||
})
|
||||
.find(jscodeshift.ImportSpecifier, {})
|
||||
|
||||
const locateImports = (
|
||||
identifiers,
|
||||
packageName = '@tanstack/react-query',
|
||||
) => {
|
||||
const findNamespaceImportIdentifier = () => {
|
||||
const specifier = root
|
||||
.find(jscodeshift.ImportDeclaration, {
|
||||
source: {
|
||||
value: packageName,
|
||||
},
|
||||
})
|
||||
.find(jscodeshift.ImportNamespaceSpecifier)
|
||||
.paths()
|
||||
|
||||
return specifier.length > 0 ? specifier[0].value.local : null
|
||||
}
|
||||
|
||||
/**
|
||||
* First, we search for the namespace import identifier because if we have any, we assume the consumer uses
|
||||
* namespace imports. In this case, we won't search for named imports at all.
|
||||
*/
|
||||
const namespaceImportIdentifier = findNamespaceImportIdentifier()
|
||||
|
||||
if (namespaceImportIdentifier) {
|
||||
const identifierMap = {}
|
||||
|
||||
for (const identifier of identifiers) {
|
||||
identifierMap[identifier] = jscodeshift.identifier(identifier)
|
||||
}
|
||||
|
||||
return {
|
||||
namespace: namespaceImportIdentifier,
|
||||
...identifierMap,
|
||||
}
|
||||
}
|
||||
|
||||
const importSpecifiers = findImportSpecifiers(packageName)
|
||||
const identifierMap = {}
|
||||
|
||||
for (const identifier of identifiers) {
|
||||
identifierMap[identifier] = findImportIdentifierOf(
|
||||
importSpecifiers,
|
||||
identifier,
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
namespace: null,
|
||||
...identifierMap,
|
||||
}
|
||||
}
|
||||
|
||||
const findAllMethodCalls = () =>
|
||||
root
|
||||
// First, we need to find all method calls.
|
||||
.find(jscodeshift.CallExpression, {
|
||||
callee: {
|
||||
type: jscodeshift.MemberExpression.name,
|
||||
property: {
|
||||
type: jscodeshift.Identifier.name,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const findQueryClientIdentifiers = (importIdentifiers) =>
|
||||
root
|
||||
.find(jscodeshift.VariableDeclarator, {})
|
||||
.filter((node) => {
|
||||
if (node.value.init) {
|
||||
const initializer = node.value.init
|
||||
|
||||
return (
|
||||
isClassInstantiationOf(
|
||||
initializer,
|
||||
getSelectorByImports(importIdentifiers, 'QueryClient'),
|
||||
) ||
|
||||
isFunctionCallOf(
|
||||
initializer,
|
||||
getSelectorByImports(importIdentifiers, 'useQueryClient'),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
.paths()
|
||||
.map((node) => node.value.id.name)
|
||||
|
||||
const isCallExpression = (node) =>
|
||||
jscodeshift.match(node, { type: jscodeshift.CallExpression.name })
|
||||
|
||||
const isIdentifier = (node) =>
|
||||
jscodeshift.match(node, { type: jscodeshift.Identifier.name })
|
||||
|
||||
const isMemberExpression = (node) =>
|
||||
jscodeshift.match(node, { type: jscodeshift.MemberExpression.name })
|
||||
|
||||
const isNewExpression = (node) =>
|
||||
jscodeshift.match(node, { type: jscodeshift.NewExpression.name })
|
||||
|
||||
const isArrayExpression = (node) =>
|
||||
jscodeshift.match(node, { type: jscodeshift.ArrayExpression.name })
|
||||
|
||||
const isObjectExpression = (node) =>
|
||||
jscodeshift.match(node, { type: jscodeshift.ObjectExpression.name })
|
||||
|
||||
const isObjectProperty = (node) =>
|
||||
jscodeshift.match(node, { type: jscodeshift.ObjectProperty.name })
|
||||
|
||||
const isSpreadElement = (node) =>
|
||||
jscodeshift.match(node, { type: jscodeshift.SpreadElement.name })
|
||||
|
||||
/**
|
||||
* @param {import('jscodeshift').Node} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isFunctionDefinition = (node) => {
|
||||
const isArrowFunctionExpression = jscodeshift.match(node, {
|
||||
type: jscodeshift.ArrowFunctionExpression.name,
|
||||
})
|
||||
const isFunctionExpression = jscodeshift.match(node, {
|
||||
type: jscodeshift.FunctionExpression.name,
|
||||
})
|
||||
|
||||
return isArrowFunctionExpression || isFunctionExpression
|
||||
}
|
||||
|
||||
const warn = (message) => {
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
console.warn(message)
|
||||
}
|
||||
}
|
||||
|
||||
const isClassInstantiationOf = (node, selector) => {
|
||||
if (!isNewExpression(node)) {
|
||||
return false
|
||||
}
|
||||
|
||||
const parts = selector.split('.')
|
||||
|
||||
return parts.length === 1
|
||||
? isIdentifier(node.callee) && node.callee.name === parts[0]
|
||||
: isMemberExpression(node.callee) &&
|
||||
node.callee.object.name === parts[0] &&
|
||||
node.callee.property.name === parts[1]
|
||||
}
|
||||
|
||||
const isFunctionCallOf = (node, selector) => {
|
||||
if (!isCallExpression(node)) {
|
||||
return false
|
||||
}
|
||||
|
||||
const parts = selector.split('.')
|
||||
|
||||
return parts.length === 1
|
||||
? isIdentifier(node.callee) && node.callee.name === parts[0]
|
||||
: isMemberExpression(node.callee) &&
|
||||
node.callee.object.name === parts[0] &&
|
||||
node.callee.property.name === parts[1]
|
||||
}
|
||||
|
||||
const getSelectorByImports = (imports, path) =>
|
||||
imports.namespace
|
||||
? `${imports.namespace.name}.${imports[path].name}`
|
||||
: imports[path].name
|
||||
|
||||
return {
|
||||
findAllMethodCalls,
|
||||
getSelectorByImports,
|
||||
isCallExpression,
|
||||
isClassInstantiationOf,
|
||||
isFunctionCallOf,
|
||||
isIdentifier,
|
||||
isMemberExpression,
|
||||
isArrayExpression,
|
||||
isObjectExpression,
|
||||
isObjectProperty,
|
||||
isSpreadElement,
|
||||
isFunctionDefinition,
|
||||
locateImports,
|
||||
warn,
|
||||
queryClient: {
|
||||
findQueryClientIdentifiers,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
module.exports = ({
|
||||
jscodeshift,
|
||||
utils,
|
||||
root,
|
||||
packageName = '@tanstack/react-query',
|
||||
}) => {
|
||||
const isGetQueryCacheMethodCall = (
|
||||
initializer,
|
||||
importIdentifiers,
|
||||
knownQueryClientIds,
|
||||
) => {
|
||||
const isKnownQueryClient = (node) =>
|
||||
utils.isIdentifier(node) && knownQueryClientIds.includes(node.name)
|
||||
|
||||
const isGetQueryCacheIdentifier = (node) =>
|
||||
utils.isIdentifier(node) && node.name === 'getQueryCache'
|
||||
|
||||
const isValidInitializer = (node) =>
|
||||
utils.isCallExpression(node) && utils.isMemberExpression(node.callee)
|
||||
|
||||
if (isValidInitializer(initializer)) {
|
||||
const instance = initializer.callee.object
|
||||
|
||||
return (
|
||||
isGetQueryCacheIdentifier(initializer.callee.property) &&
|
||||
(isKnownQueryClient(instance) ||
|
||||
utils.isFunctionCallOf(
|
||||
instance,
|
||||
utils.getSelectorByImports(importIdentifiers, 'useQueryClient'),
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const findQueryCacheInstantiations = (
|
||||
importIdentifiers,
|
||||
knownQueryClientIds,
|
||||
) =>
|
||||
root.find(jscodeshift.VariableDeclarator, {}).filter((node) => {
|
||||
if (node.value.init) {
|
||||
const initializer = node.value.init
|
||||
|
||||
return (
|
||||
utils.isClassInstantiationOf(
|
||||
initializer,
|
||||
utils.getSelectorByImports(importIdentifiers, 'QueryCache'),
|
||||
) ||
|
||||
isGetQueryCacheMethodCall(
|
||||
initializer,
|
||||
importIdentifiers,
|
||||
knownQueryClientIds,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
const filterQueryCacheMethodCalls = (node) =>
|
||||
utils.isIdentifier(node) && ['find', 'findAll'].includes(node.name)
|
||||
|
||||
const findQueryCacheMethodCalls = (importIdentifiers) => {
|
||||
/**
|
||||
* Here we collect all query client instantiations. We have to make aware of them because the query cache can be
|
||||
* accessed by the query client as well.
|
||||
*/
|
||||
const queryClientIdentifiers =
|
||||
utils.queryClient.findQueryClientIdentifiers(importIdentifiers)
|
||||
/**
|
||||
* Here we collect all query cache instantiations. The reason is simple: the methods can be called on query cache
|
||||
* instances, to locate the possible usages we need to be aware of the identifier names.
|
||||
*/
|
||||
const queryCacheIdentifiers = findQueryCacheInstantiations(
|
||||
importIdentifiers,
|
||||
queryClientIdentifiers,
|
||||
)
|
||||
.paths()
|
||||
.map((node) => node.value.id.name)
|
||||
|
||||
return (
|
||||
utils
|
||||
// First, we need to find all method calls.
|
||||
.findAllMethodCalls()
|
||||
// Then we narrow the collection to all `fetch` and `fetchAll` methods.
|
||||
.filter((node) =>
|
||||
filterQueryCacheMethodCalls(node.value.callee.property),
|
||||
)
|
||||
.filter((node) => {
|
||||
const object = node.value.callee.object
|
||||
|
||||
// If the method is called on a `QueryCache` instance, we keep it in the collection.
|
||||
if (utils.isIdentifier(object)) {
|
||||
return queryCacheIdentifiers.includes(object.name)
|
||||
}
|
||||
|
||||
// If the method is called on a `QueryClient` instance, we keep it in the collection.
|
||||
if (utils.isCallExpression(object)) {
|
||||
return isGetQueryCacheMethodCall(
|
||||
object,
|
||||
importIdentifiers,
|
||||
queryClientIdentifiers,
|
||||
)
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
const execute = (replacer) => {
|
||||
findQueryCacheMethodCalls(
|
||||
utils.locateImports(
|
||||
['QueryCache', 'QueryClient', 'useQueryClient'],
|
||||
packageName,
|
||||
),
|
||||
).replaceWith(replacer)
|
||||
}
|
||||
|
||||
return {
|
||||
execute,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
module.exports = ({
|
||||
jscodeshift,
|
||||
utils,
|
||||
root,
|
||||
packageName = '@tanstack/react-query',
|
||||
}) => {
|
||||
const filterQueryClientMethodCalls = (node, methods) =>
|
||||
utils.isIdentifier(node) && methods.includes(node.name)
|
||||
|
||||
const findQueryClientMethodCalls = (importIdentifiers, methods) => {
|
||||
/**
|
||||
* Here we collect all query client instantiations. We have to make aware of them because some method calls might
|
||||
* be invoked on these instances.
|
||||
*/
|
||||
const queryClientIdentifiers =
|
||||
utils.queryClient.findQueryClientIdentifiers(importIdentifiers)
|
||||
|
||||
return (
|
||||
utils
|
||||
// First, we need to find all method calls.
|
||||
.findAllMethodCalls()
|
||||
// Then we narrow the collection to `QueryClient` methods.
|
||||
.filter((node) =>
|
||||
filterQueryClientMethodCalls(node.value.callee.property, methods),
|
||||
)
|
||||
.filter((node) => {
|
||||
const object = node.value.callee.object
|
||||
|
||||
// If the method is called on a `QueryClient` instance, we keep it in the collection.
|
||||
if (utils.isIdentifier(object)) {
|
||||
return queryClientIdentifiers.includes(object.name)
|
||||
}
|
||||
|
||||
// If the method is called on the return value of `useQueryClient` hook, we keep it in the collection.
|
||||
return utils.isFunctionCallOf(
|
||||
object,
|
||||
utils.getSelectorByImports(importIdentifiers, 'useQueryClient'),
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
const execute = (methods, replacer) => {
|
||||
findQueryClientMethodCalls(
|
||||
utils.locateImports(['QueryClient', 'useQueryClient'], packageName),
|
||||
methods,
|
||||
).replaceWith(replacer)
|
||||
}
|
||||
|
||||
return {
|
||||
execute,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
module.exports = ({
|
||||
jscodeshift,
|
||||
utils,
|
||||
root,
|
||||
packageName = '@tanstack/react-query',
|
||||
}) => {
|
||||
const filterUseQueryLikeHookCalls = (node, importIdentifiers, hooks) => {
|
||||
for (const hook of hooks) {
|
||||
const selector = utils.getSelectorByImports(importIdentifiers, hook)
|
||||
|
||||
if (utils.isFunctionCallOf(node, selector)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const findUseQueryLikeHookCalls = (importIdentifiers, hooks) =>
|
||||
root
|
||||
// First, we need to find all call expressions.
|
||||
.find(jscodeshift.CallExpression, {})
|
||||
// Then we narrow the collection to the `useQuery` like hook calls.
|
||||
.filter((node) =>
|
||||
filterUseQueryLikeHookCalls(node.value, importIdentifiers, hooks),
|
||||
)
|
||||
|
||||
const execute = (hooks, replacer) => {
|
||||
findUseQueryLikeHookCalls(
|
||||
utils.locateImports(hooks, packageName),
|
||||
hooks,
|
||||
).replaceWith(replacer)
|
||||
}
|
||||
|
||||
return {
|
||||
execute,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user