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: {