diff --git a/packages/codemods/.eslintrc.cjs b/packages/codemods/.eslintrc.cjs
index 37758a00b1..a3adafe1b6 100644
--- a/packages/codemods/.eslintrc.cjs
+++ b/packages/codemods/.eslintrc.cjs
@@ -11,6 +11,7 @@ const config = {
files: ['**/__testfixtures__/*'],
rules: {
'import/no-unresolved': 'off',
+ 'sort-imports': 'off',
'@typescript-eslint/no-unused-vars': 'off',
},
},
diff --git a/packages/codemods/src/v5/keep-previous-data/README.md b/packages/codemods/src/v5/keep-previous-data/README.md
new file mode 100644
index 0000000000..a85f43416b
--- /dev/null
+++ b/packages/codemods/src/v5/keep-previous-data/README.md
@@ -0,0 +1,32 @@
+### Intro
+
+The prerequisite for this code mod is to migrate your usages to the new syntax, so overloads for hooks and `QueryClient` methods shouldn't be available anymore.
+
+### Affected usages
+
+Please note, this code mod transforms usages only where the first argument is an object expression.
+
+The following usage should be transformed by the code mod:
+
+```ts
+const { data } = useQuery({
+ queryKey: ['posts'],
+ queryFn: queryFn,
+ keepPreviousData: true,
+})
+```
+
+But the following usage won't be transformed by the code mod, because the first argument an identifier:
+
+```ts
+const hookArgument = {
+ queryKey: ['posts'],
+ queryFn: queryFn,
+ keepPreviousData: true,
+}
+const { data } = useQuery(hookArgument)
+```
+
+### Troubleshooting
+
+In case of any errors, feel free to reach us out via Discord or open an issue. If you open an issue, please provide a code snippet as well, because without a snippet we cannot find the bug in the code mod.
diff --git a/packages/codemods/src/v5/keep-previous-data/__testfixtures__/default.input.tsx b/packages/codemods/src/v5/keep-previous-data/__testfixtures__/default.input.tsx
index f84f116d82..9528058885 100644
--- a/packages/codemods/src/v5/keep-previous-data/__testfixtures__/default.input.tsx
+++ b/packages/codemods/src/v5/keep-previous-data/__testfixtures__/default.input.tsx
@@ -1,6 +1,6 @@
import * as React from 'react'
import axios from 'axios'
-import { useQueries, useQuery } from '@tanstack/react-query'
+import { QueryClient, useQueries, useQuery, useQueryClient } from '@tanstack/react-query'
type Post = {
id: number
@@ -53,7 +53,7 @@ export const Example3 = () => {
queryKey: ['posts'],
queryFn: queryFn,
keepPreviousData: true,
- placeholderData: "somePlaceholderData"
+ placeholderData: 'somePlaceholderData'
})
return
{JSON.stringify(data)}
@@ -88,3 +88,197 @@ export const Example5 = () => {
return {JSON.stringify(data)}
}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, so the codemod should transform
+ * this usage.
+ */
+export const Example6 = () => {
+ const queryClient = new QueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true
+ })
+
+ useQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: true
+ })
+
+ const anotherQueryClient = useQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a function.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example7 = () => {
+ const queryClient = new QueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ })
+
+ useQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ })
+
+ const anotherQueryClient = useQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a string.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example8 = () => {
+ const queryClient = new QueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ })
+
+ useQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ })
+
+ const anotherQueryClient = useQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `false`.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example9 = () => {
+ const queryClient = new QueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: false,
+ })
+
+ useQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: false,
+ })
+
+ const anotherQueryClient = useQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: false,
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with which is an identifier.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example10 = () => {
+ const queryClient = new QueryClient()
+ const keepPreviousDataIdentifier = false
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ })
+
+ useQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ })
+
+ const anotherQueryClient = useQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, so the codemod should transform
+ * this usage.
+ */
+export const Example11 = () => {
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: true
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a function.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example12 = () => {
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a string.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example13 = () => {
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `false`.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example14 = () => {
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: false,
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with which is an identifier.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example15 = () => {
+ const keepPreviousDataIdentifier = false
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ }
+ }
+ })
+}
diff --git a/packages/codemods/src/v5/keep-previous-data/__testfixtures__/default.output.tsx b/packages/codemods/src/v5/keep-previous-data/__testfixtures__/default.output.tsx
index 875b6c1b29..cf0a32a747 100644
--- a/packages/codemods/src/v5/keep-previous-data/__testfixtures__/default.output.tsx
+++ b/packages/codemods/src/v5/keep-previous-data/__testfixtures__/default.output.tsx
@@ -1,6 +1,6 @@
import * as React from 'react'
import axios from 'axios'
-import { keepPreviousData, useQueries, useQuery } from '@tanstack/react-query';
+import { keepPreviousData, QueryClient, useQueries, useQuery, useQueryClient } from '@tanstack/react-query';
type Post = {
id: number
@@ -53,7 +53,7 @@ export const Example3 = () => {
queryKey: ['posts'],
queryFn: queryFn,
keepPreviousData: true,
- placeholderData: "somePlaceholderData"
+ placeholderData: 'somePlaceholderData'
})
return {JSON.stringify(data)}
@@ -88,3 +88,197 @@ export const Example5 = () => {
return {JSON.stringify(data)}
}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, so the codemod should transform
+ * this usage.
+ */
+export const Example6 = () => {
+ const queryClient = new QueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ placeholderData: keepPreviousData
+ })
+
+ useQueryClient().setQueryDefaults(['key'], {
+ placeholderData: keepPreviousData
+ })
+
+ const anotherQueryClient = useQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ placeholderData: keepPreviousData
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a function.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example7 = () => {
+ const queryClient = new QueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ })
+
+ useQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ })
+
+ const anotherQueryClient = useQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a string.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example8 = () => {
+ const queryClient = new QueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ })
+
+ useQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ })
+
+ const anotherQueryClient = useQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `false`.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example9 = () => {
+ const queryClient = new QueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: false,
+ })
+
+ useQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: false,
+ })
+
+ const anotherQueryClient = useQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: false,
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with which is an identifier.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example10 = () => {
+ const queryClient = new QueryClient()
+ const keepPreviousDataIdentifier = false
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ })
+
+ useQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ })
+
+ const anotherQueryClient = useQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, so the codemod should transform
+ * this usage.
+ */
+export const Example11 = () => {
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ placeholderData: keepPreviousData
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a function.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example12 = () => {
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a string.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example13 = () => {
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `false`.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example14 = () => {
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: false,
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with which is an identifier.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example15 = () => {
+ const keepPreviousDataIdentifier = false
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ }
+ }
+ })
+}
diff --git a/packages/codemods/src/v5/keep-previous-data/__testfixtures__/named.input.tsx b/packages/codemods/src/v5/keep-previous-data/__testfixtures__/named.input.tsx
index e69de29bb2..bbce30bf10 100644
--- a/packages/codemods/src/v5/keep-previous-data/__testfixtures__/named.input.tsx
+++ b/packages/codemods/src/v5/keep-previous-data/__testfixtures__/named.input.tsx
@@ -0,0 +1,289 @@
+import * as React from 'react'
+import axios from 'axios'
+import {
+ QueryClient as RenamedQueryClient,
+ useQueries as useRenamedUseQueries,
+ useQuery as useRenamedUseQuery,
+ useQueryClient as useRenamedUseQueryClient
+} from '@tanstack/react-query'
+
+type Post = {
+ id: number
+ title: string
+ body: string
+}
+
+const queryFn = async (): Promise> => {
+ const { data } = await axios.get(
+ 'https://jsonplaceholder.typicode.com/posts',
+ )
+ return data
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, so the codemod should transform
+ * this usage.
+ */
+export const Example1 = () => {
+ const { data } = useRenamedUseQuery({
+ queryKey: ['posts'],
+ queryFn: queryFn,
+ keepPreviousData: true,
+ })
+
+ return {JSON.stringify(data)}
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a function.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example2 = () => {
+ const { data } = useRenamedUseQuery({
+ queryKey: ['posts'],
+ queryFn: queryFn,
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ })
+
+ return {JSON.stringify(data)}
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a string.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example3 = () => {
+ const { data } = useRenamedUseQueries({
+ queryKey: ['posts'],
+ queryFn: queryFn,
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ })
+
+ return {JSON.stringify(data)}
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `false`.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example4 = () => {
+ const { data } = useRenamedUseQueries({
+ queryKey: ['posts'],
+ queryFn: queryFn,
+ keepPreviousData: false,
+ })
+
+ return {JSON.stringify(data)}
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with which is an identifier.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example5 = () => {
+ const keepPreviousDataIdentifier = false
+ const { data } = useRenamedUseQueries({
+ queryKey: ['posts'],
+ queryFn: queryFn,
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ })
+
+ return {JSON.stringify(data)}
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, so the codemod should transform
+ * this usage.
+ */
+export const Example6 = () => {
+ const queryClient = new RenamedQueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true
+ })
+
+ useRenamedUseQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: true
+ })
+
+ const anotherQueryClient = useRenamedUseQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a function.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example7 = () => {
+ const queryClient = new RenamedQueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ })
+
+ useRenamedUseQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ })
+
+ const anotherQueryClient = useRenamedUseQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a string.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example8 = () => {
+ const queryClient = new RenamedQueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ })
+
+ useRenamedUseQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ })
+
+ const anotherQueryClient = useRenamedUseQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `false`.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example9 = () => {
+ const queryClient = new RenamedQueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: false,
+ })
+
+ useRenamedUseQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: false,
+ })
+
+ const anotherQueryClient = useRenamedUseQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: false,
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with which is an identifier.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example10 = () => {
+ const queryClient = new RenamedQueryClient()
+ const keepPreviousDataIdentifier = false
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ })
+
+ useRenamedUseQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ })
+
+ const anotherQueryClient = useRenamedUseQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, so the codemod should transform
+ * this usage.
+ */
+export const Example11 = () => {
+ new RenamedQueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: true
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a function.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example12 = () => {
+ new RenamedQueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a string.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example13 = () => {
+ new RenamedQueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `false`.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example14 = () => {
+ new RenamedQueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: false,
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with which is an identifier.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example15 = () => {
+ const keepPreviousDataIdentifier = false
+ new RenamedQueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ }
+ }
+ })
+}
diff --git a/packages/codemods/src/v5/keep-previous-data/__testfixtures__/named.output.tsx b/packages/codemods/src/v5/keep-previous-data/__testfixtures__/named.output.tsx
index e69de29bb2..b8c546f11c 100644
--- a/packages/codemods/src/v5/keep-previous-data/__testfixtures__/named.output.tsx
+++ b/packages/codemods/src/v5/keep-previous-data/__testfixtures__/named.output.tsx
@@ -0,0 +1,290 @@
+import * as React from 'react'
+import axios from 'axios'
+import {
+ keepPreviousData,
+ QueryClient as RenamedQueryClient,
+ useQueries as useRenamedUseQueries,
+ useQuery as useRenamedUseQuery,
+ useQueryClient as useRenamedUseQueryClient,
+} from '@tanstack/react-query';
+
+type Post = {
+ id: number
+ title: string
+ body: string
+}
+
+const queryFn = async (): Promise> => {
+ const { data } = await axios.get(
+ 'https://jsonplaceholder.typicode.com/posts',
+ )
+ return data
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, so the codemod should transform
+ * this usage.
+ */
+export const Example1 = () => {
+ const { data } = useRenamedUseQuery({
+ queryKey: ['posts'],
+ queryFn: queryFn,
+ placeholderData: keepPreviousData
+ })
+
+ return {JSON.stringify(data)}
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a function.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example2 = () => {
+ const { data } = useRenamedUseQuery({
+ queryKey: ['posts'],
+ queryFn: queryFn,
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ })
+
+ return {JSON.stringify(data)}
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a string.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example3 = () => {
+ const { data } = useRenamedUseQueries({
+ queryKey: ['posts'],
+ queryFn: queryFn,
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ })
+
+ return {JSON.stringify(data)}
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `false`.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example4 = () => {
+ const { data } = useRenamedUseQueries({
+ queryKey: ['posts'],
+ queryFn: queryFn,
+ keepPreviousData: false,
+ })
+
+ return {JSON.stringify(data)}
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with which is an identifier.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example5 = () => {
+ const keepPreviousDataIdentifier = false
+ const { data } = useRenamedUseQueries({
+ queryKey: ['posts'],
+ queryFn: queryFn,
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ })
+
+ return {JSON.stringify(data)}
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, so the codemod should transform
+ * this usage.
+ */
+export const Example6 = () => {
+ const queryClient = new RenamedQueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ placeholderData: keepPreviousData
+ })
+
+ useRenamedUseQueryClient().setQueryDefaults(['key'], {
+ placeholderData: keepPreviousData
+ })
+
+ const anotherQueryClient = useRenamedUseQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ placeholderData: keepPreviousData
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a function.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example7 = () => {
+ const queryClient = new RenamedQueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ })
+
+ useRenamedUseQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ })
+
+ const anotherQueryClient = useRenamedUseQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a string.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example8 = () => {
+ const queryClient = new RenamedQueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ })
+
+ useRenamedUseQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ })
+
+ const anotherQueryClient = useRenamedUseQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `false`.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example9 = () => {
+ const queryClient = new RenamedQueryClient()
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: false,
+ })
+
+ useRenamedUseQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: false,
+ })
+
+ const anotherQueryClient = useRenamedUseQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: false,
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with which is an identifier.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example10 = () => {
+ const queryClient = new RenamedQueryClient()
+ const keepPreviousDataIdentifier = false
+
+ queryClient.setQueryDefaults(['key'], {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ })
+
+ useRenamedUseQueryClient().setQueryDefaults(['key'], {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ })
+
+ const anotherQueryClient = useRenamedUseQueryClient()
+
+ anotherQueryClient.setQueryDefaults(['key'], {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, so the codemod should transform
+ * this usage.
+ */
+export const Example11 = () => {
+ new RenamedQueryClient({
+ defaultOptions: {
+ queries: {
+ placeholderData: keepPreviousData
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a function.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example12 = () => {
+ new RenamedQueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: true,
+ placeholderData: () => previousData
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `true`, but the `placeholderData` is a string.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example13 = () => {
+ new RenamedQueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: true,
+ placeholderData: 'somePlaceholderData'
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with value `false`.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example14 = () => {
+ new RenamedQueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: false,
+ }
+ }
+ })
+}
+
+/**
+ * The object expression has a `keepPreviousData` property with which is an identifier.
+ * The codemod shouldn't transform this case, only warn the user about the manual migration.
+ */
+export const Example15 = () => {
+ const keepPreviousDataIdentifier = false
+ new RenamedQueryClient({
+ defaultOptions: {
+ queries: {
+ keepPreviousData: keepPreviousDataIdentifier,
+ placeholderData: () => previousData
+ }
+ }
+ })
+}
diff --git a/packages/codemods/src/v5/keep-previous-data/keep-previous-data.js b/packages/codemods/src/v5/keep-previous-data/keep-previous-data.js
index dc44483ae6..5b2a9553a9 100644
--- a/packages/codemods/src/v5/keep-previous-data/keep-previous-data.js
+++ b/packages/codemods/src/v5/keep-previous-data/keep-previous-data.js
@@ -3,6 +3,8 @@ const createUtilsObject = require('../../utils')
// eslint-disable-next-line @typescript-eslint/no-var-requires
const createUseQueryLikeTransformer = require('../../utils/transformers/use-query-like-transformer')
// eslint-disable-next-line @typescript-eslint/no-var-requires
+const createQueryClientTransformer = require('../../utils/transformers/query-client-transformer')
+// eslint-disable-next-line @typescript-eslint/no-var-requires
const AlreadyHasPlaceholderDataProperty = require('./utils/already-has-placeholder-data-property')
/**
@@ -14,11 +16,11 @@ const AlreadyHasPlaceholderDataProperty = require('./utils/already-has-placehold
*/
const transformUsages = ({ jscodeshift, utils, root, filePath, config }) => {
/**
- * @param {import('jscodeshift').CallExpression} callExpression
+ * @param {import('jscodeshift').CallExpression | import('jscodeshift').ExpressionStatement} node
* @returns {{start: number, end: number}}
*/
- const getCallExpressionLocation = (callExpression) => {
- const location = callExpression.callee.loc
+ const getNodeLocation = (node) => {
+ const location = utils.isCallExpression(node) ? node.callee.loc : node.loc
const start = location.start.line
const end = location.end.line
@@ -87,26 +89,25 @@ const transformUsages = ({ jscodeshift, utils, root, filePath, config }) => {
return objectExpression.properties.find(isKeepPreviousDataObjectProperty)
}
- let isKeepPreviousDataInUse = false
+ let shouldAddKeepPreviousDataImport = false
- const replacer = (path) => {
+ const replacer = (path, resolveTargetArgument, transformNode) => {
const node = path.node
- const functionArguments = []
- const { start, end } = getCallExpressionLocation(node)
+ const { start, end } = getNodeLocation(node)
try {
- const firstArgument = node.arguments[0] ?? null
+ const targetArgument = resolveTargetArgument(node)
- if (firstArgument && utils.isObjectExpression(firstArgument)) {
+ if (targetArgument && utils.isObjectExpression(targetArgument)) {
const isPlaceholderDataPropertyPresent =
- hasPlaceholderDataProperty(firstArgument)
+ hasPlaceholderDataProperty(targetArgument)
- if (hasPlaceholderDataProperty(firstArgument)) {
+ if (hasPlaceholderDataProperty(targetArgument)) {
throw new AlreadyHasPlaceholderDataProperty(node, filePath)
}
const keepPreviousDataProperty =
- getKeepPreviousDataProperty(firstArgument)
+ getKeepPreviousDataProperty(targetArgument)
const keepPreviousDataPropertyHasTrueValue =
isObjectPropertyHasTrueBooleanLiteralValue(keepPreviousDataProperty)
@@ -122,10 +123,10 @@ const transformUsages = ({ jscodeshift, utils, root, filePath, config }) => {
if (keepPreviousDataPropertyHasTrueValue) {
// Removing the `keepPreviousData` property from the object.
const mutableObjectExpressionProperties =
- filterKeepPreviousDataProperty(firstArgument)
+ filterKeepPreviousDataProperty(targetArgument)
if (!isPlaceholderDataPropertyPresent) {
- isKeepPreviousDataInUse = true
+ shouldAddKeepPreviousDataImport = true
// When the `placeholderData` property is not present, the `placeholderData: keepPreviousData` property will be added.
mutableObjectExpressionProperties.push(
@@ -133,14 +134,10 @@ const transformUsages = ({ jscodeshift, utils, root, filePath, config }) => {
)
}
- functionArguments.push(
+ return transformNode(
+ node,
jscodeshift.objectExpression(mutableObjectExpressionProperties),
)
-
- return jscodeshift.callExpression(
- node.original.callee,
- functionArguments,
- )
}
}
@@ -149,8 +146,6 @@ const transformUsages = ({ jscodeshift, utils, root, filePath, config }) => {
)
return node
-
- return node
} catch (error) {
utils.warn(
error.name === AlreadyHasPlaceholderDataProperty.name
@@ -164,10 +159,81 @@ const transformUsages = ({ jscodeshift, utils, root, filePath, config }) => {
createUseQueryLikeTransformer({ jscodeshift, utils, root }).execute(
config.hooks,
- replacer,
+ (path) => {
+ const resolveTargetArgument = (node) => node.arguments[0] ?? null
+ const transformNode = (node, transformedArgument) =>
+ jscodeshift.callExpression(node.original.callee, [transformedArgument])
+
+ return replacer(path, resolveTargetArgument, transformNode)
+ },
+ )
+
+ createQueryClientTransformer({ jscodeshift, utils, root }).execute(
+ config.queryClientMethods,
+ (path) => {
+ const resolveTargetArgument = (node) => node.arguments[1] ?? null
+ const transformNode = (node, transformedArgument) => {
+ return jscodeshift.callExpression(node.original.callee, [
+ node.arguments[0],
+ transformedArgument,
+ ...node.arguments.slice(2, 0),
+ ])
+ }
+
+ return replacer(path, resolveTargetArgument, transformNode)
+ },
+ )
+
+ const importIdentifierOfQueryClient = utils.getSelectorByImports(
+ utils.locateImports(['QueryClient']),
+ 'QueryClient',
)
- return { isKeepPreviousDataInUse }
+ root
+ .find(jscodeshift.ExpressionStatement, {
+ expression: {
+ type: jscodeshift.NewExpression.name,
+ callee: {
+ type: jscodeshift.Identifier.name,
+ name: importIdentifierOfQueryClient,
+ },
+ },
+ })
+ .filter((path) => path.node.expression)
+ .replaceWith((path) => {
+ const resolveTargetArgument = (node) => {
+ const paths = jscodeshift(node)
+ .find(jscodeshift.ObjectProperty, {
+ key: {
+ type: jscodeshift.Identifier.name,
+ name: 'keepPreviousData',
+ },
+ })
+ .paths()
+
+ return paths.length > 0 ? paths[0].parent.node : null
+ }
+ const transformNode = (node, transformedArgument) => {
+ jscodeshift(node.expression)
+ .find(jscodeshift.ObjectProperty, {
+ key: {
+ type: jscodeshift.Identifier.name,
+ name: 'queries',
+ },
+ })
+ .replaceWith(({ node: mutableNode }) => {
+ mutableNode.value.properties = transformedArgument.properties
+
+ return mutableNode
+ })
+
+ return node
+ }
+
+ return replacer(path, resolveTargetArgument, transformNode)
+ })
+
+ return { shouldAddKeepPreviousDataImport }
}
module.exports = (file, api) => {
@@ -178,14 +244,15 @@ module.exports = (file, api) => {
const dependencies = { jscodeshift, utils, root, filePath }
- const { isKeepPreviousDataInUse } = transformUsages({
+ const { shouldAddKeepPreviousDataImport } = transformUsages({
...dependencies,
config: {
hooks: ['useInfiniteQuery', 'useQueries', 'useQuery'],
+ queryClientMethods: ['setQueryDefaults'],
},
})
- if (isKeepPreviousDataInUse) {
+ if (shouldAddKeepPreviousDataImport) {
root
.find(jscodeshift.ImportDeclaration, {
source: {