From 85ce6db9a8f53210403ffbeb8b59deb1df102098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rivo=20T=C3=BCksammel?= <122151164+rivo420@users.noreply.github.com> Date: Sat, 7 Jan 2023 03:50:08 -0800 Subject: [PATCH 1/2] chore(env): cross platform preinstall CI check. (#4775) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6bf590c152..af536bf89d 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "repository": "https://github.com/tanstack/query.git", "scripts": { "clean": "pnpm --filter \"./packages/**\" --parallel --no-bail run clean", - "preinstall": "if [ \"$CI\" = \"true\" ]; then echo \"Skipping preinstall...\"; else npx -y only-allow pnpm; fi", + "preinstall": "node -e \"if(process.env.CI == 'true') {console.log('Skipping preinstall...'); process.exit(1)}\" || npx -y only-allow pnpm", "install:csb": "pnpm install --frozen-lockfile", "test": "pnpm run test:ci", "test:ci": "pnpm run test:format && pnpm run test:eslint && pnpm run test:jest --collectCoverage false && pnpm run typecheck", From 474bc43a1f080d6dd6a8ed826fd0038910ca678b Mon Sep 17 00:00:00 2001 From: "Mahmoud M. Anwar" Date: Sat, 7 Jan 2023 13:50:54 +0200 Subject: [PATCH 2/2] feat(prefer-query-object-syntax): remove prefer-query-object-syntax rule (#4773) the rule is not needed anymore BREAKING CHANGE: prefer-query-object-syntax eslint rule is removed --- docs/config.json | 4 - docs/react/eslint/eslint-plugin-query.md | 1 - .../eslint/prefer-query-object-syntax.md | 62 --- .../guides/migrating-to-react-query-5.md | 5 +- examples/solid/simple/.eslintrc | 1 - .../src/configs/index.test.ts | 1 - .../eslint-plugin-query/src/rules/index.ts | 2 - .../prefer-query-object-syntax.test.ts | 363 ------------------ .../prefer-query-object-syntax.ts | 227 ----------- 9 files changed, 4 insertions(+), 662 deletions(-) delete mode 100644 docs/react/eslint/prefer-query-object-syntax.md delete mode 100644 packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.test.ts delete mode 100644 packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.ts diff --git a/docs/config.json b/docs/config.json index 96244a63b2..e335efc20f 100644 --- a/docs/config.json +++ b/docs/config.json @@ -294,10 +294,6 @@ { "label": "Exhaustive Deps", "to": "react/eslint/exhaustive-deps" - }, - { - "label": "Prefer object syntax", - "to": "react/eslint/prefer-query-object-syntax" } ] }, diff --git a/docs/react/eslint/eslint-plugin-query.md b/docs/react/eslint/eslint-plugin-query.md index c228b1bd92..2df1535310 100644 --- a/docs/react/eslint/eslint-plugin-query.md +++ b/docs/react/eslint/eslint-plugin-query.md @@ -33,7 +33,6 @@ Then configure the rules you want to use under the rules section: { "rules": { "@tanstack/query/exhaustive-deps": "error", - "@tanstack/query/prefer-query-object-syntax": "error" } } ``` diff --git a/docs/react/eslint/prefer-query-object-syntax.md b/docs/react/eslint/prefer-query-object-syntax.md deleted file mode 100644 index 8a53cdbb0a..0000000000 --- a/docs/react/eslint/prefer-query-object-syntax.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -id: prefer-query-object-syntax -title: Prefer object syntax for useQuery ---- - -You can use [`useQuery`](https://tanstack.com/query/v4/docs/reference/useQuery) in two different ways. - -Standard - -```tsx -useQuery(queryKey, queryFn?, options?) - -// or - -useQuery(options) -``` - -This rule prefers the second option, as it is more consistent with other React Query hooks, like `useQueries`. It will also be the only available option in a future major version. - -## Rule Details - -Examples of **incorrect** code for this rule: - -```js -/* eslint "@tanstack/query/prefer-query-object-syntax": "error" */ - -import { useQuery } from '@tanstack/react-query'; - -useQuery(queryKey, queryFn, { - onSuccess, -}); - -useQuery(queryKey, { - queryFn, - onSuccess, -}); -``` - -Examples of **correct** code for this rule: - -```js -import { useQuery } from '@tanstack/react-query'; - -useQuery({ - queryKey, - queryFn, - onSuccess, -}); -``` - -## When Not To Use It - -If you don't care about useQuery consistency, then you will not need this rule. - -## Attributes - -- [x] ✅ Recommended -- [x] 🔧 Fixable - -## Credits - -This rule was initially developed by [KubaJastrz](https://github.com/KubaJastrz) in [eslint-plugin-react-query](https://github.com/KubaJastrz/eslint-plugin-react-query). diff --git a/docs/react/guides/migrating-to-react-query-5.md b/docs/react/guides/migrating-to-react-query-5.md index c068c1cd9a..ef873362e2 100644 --- a/docs/react/guides/migrating-to-react-query-5.md +++ b/docs/react/guides/migrating-to-react-query-5.md @@ -9,7 +9,7 @@ v5 is a major version, so there are some breaking changes to be aware of: ### Supports a single signature, one object -useQuery and friends used to have many overloads in TypeScript - different ways how the function can be invoked. Not only this was tough to maintain, type wise, it also required a runtime check to see which type the first and the second parameter, to correctly create options. +useQuery and friends used to have many overloads in TypeScript - different ways how the function can be invoked. Not only this was tough to maintain, type wise, it also required a runtime check to see which type the first and the second parameter, to correctly create options. now we only support the object format. @@ -135,3 +135,6 @@ You can achieve the same functionality by passing a function to `structuralShari To make the `useErrorBoundary` prop more framework-agnostic and avoid confusion with the established React function prefix "`use`" for hooks and the "ErrorBoundary" component name, it has been renamed to `throwErrors` to more accurately reflect its functionality. +### eslint `prefer-query-object-syntax` rule is removed + +Since the only supported syntax now is the object syntax, this rule is no longer needed \ No newline at end of file diff --git a/examples/solid/simple/.eslintrc b/examples/solid/simple/.eslintrc index d8c452e40c..ea632a1553 100644 --- a/examples/solid/simple/.eslintrc +++ b/examples/solid/simple/.eslintrc @@ -7,6 +7,5 @@ "rules": { "react/react-in-jsx-scope": "off", "jsx-a11y/accessible-emoji": "off", - "@tanstack/query/prefer-query-object-syntax": "off" } } diff --git a/packages/eslint-plugin-query/src/configs/index.test.ts b/packages/eslint-plugin-query/src/configs/index.test.ts index 1a7c7aa974..a11be28fe5 100644 --- a/packages/eslint-plugin-query/src/configs/index.test.ts +++ b/packages/eslint-plugin-query/src/configs/index.test.ts @@ -10,7 +10,6 @@ describe('configs', () => { ], "rules": { "@tanstack/query/exhaustive-deps": "error", - "@tanstack/query/prefer-query-object-syntax": "error", }, }, } diff --git a/packages/eslint-plugin-query/src/rules/index.ts b/packages/eslint-plugin-query/src/rules/index.ts index bba8422ffb..eb0acbae7f 100644 --- a/packages/eslint-plugin-query/src/rules/index.ts +++ b/packages/eslint-plugin-query/src/rules/index.ts @@ -1,7 +1,5 @@ import * as exhaustiveDeps from './exhaustive-deps/exhaustive-deps.rule' -import * as preferObjectSyntax from './prefer-query-object-syntax/prefer-query-object-syntax' export const rules = { [exhaustiveDeps.name]: exhaustiveDeps.rule, - [preferObjectSyntax.name]: preferObjectSyntax.rule, } diff --git a/packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.test.ts b/packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.test.ts deleted file mode 100644 index 19962c7fe6..0000000000 --- a/packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.test.ts +++ /dev/null @@ -1,363 +0,0 @@ -import { rule, name } from './prefer-query-object-syntax' -import { normalizeIndent } from '../../utils/test-utils' -import { ESLintUtils } from '@typescript-eslint/utils' - -const ruleTester = new ESLintUtils.RuleTester({ - parser: '@typescript-eslint/parser', - settings: {}, -}) - -ruleTester.run(name, rule, { - valid: [ - { - code: normalizeIndent` - useQuery() - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey, queryFn, enabled }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const result = useQuery({ queryKey, queryFn, enabled }); - `, - }, - { - code: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - const result = useQuery({ queryKey, queryFn, enabled }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "somewhere-else"; - useQuery(queryKey, queryFn, { enabled }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getPosts = async () => Promise.resolve([]); - const postsQuery = { queryKey: ["posts"], queryFn: () => getPosts() }; - const usePosts = () => useQuery(postsQuery); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = () => ({ queryKey: ['foo'], queryFn: () => Promise.resolve(5) }) - useQuery(getQuery()) - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = () => { - return { queryKey: ['foo'], queryFn: () => Promise.resolve(5) }; - } - useQuery(getQuery()) - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = () => { - const queryKey = () => ['foo']; - const queryFn = () => { - return Promise.resolve(5); - } - return { queryKey, queryFn }; - } - useQuery(getQuery()) - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = () => { - try { - return { queryKey: ['foo'], queryFn: () => Promise.resolve(5) }; - } finally { - return { queryKey: ['foo'], queryFn: () => Promise.resolve(5) }; - } - } - useQuery(getQuery()) - `, - }, - ], - - invalid: [ - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(['data']); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey: ['data'] }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(queryKey); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(queryKey, queryFn); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - // no autofix - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(['data'], () => fetchData()); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey: ['data'], queryFn: () => fetchData() }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(queryKey, queryFn, { enabled }); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey, queryFn, enabled }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(['data'], () => fetchData(), { enabled: false }); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey: ['data'], queryFn: () => fetchData(), enabled: false }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(queryKey, { queryFn, enabled }); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey, queryFn, enabled }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = () => "foo"; - useQuery(getQuery()); - `, - errors: [ - { - messageId: 'returnTypeAreNotObjectSyntax', - data: { returnType: '"foo"' }, - }, - ], - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = (x) => { - return x - ? { queryKey: "foo", queryFn: () => Promise.resolve(1) } - : null; - }; - useQuery(getQuery(x)); - `, - errors: [ - { - messageId: 'returnTypeAreNotObjectSyntax', - data: { - returnType: `x\n ? { queryKey: "foo", queryFn: () => Promise.resolve(1) }\n : null`, - }, - }, - ], - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = (x) => { - try { - return { queryKey: "foo", queryFn: () => Promise.resolve(1) }; - } catch (e) { - if (x > 1) { - return { queryKey: "bar", queryFn: () => Promise.resolve(2) }; - } else { - return null; - } - } - }; - useQuery(getQuery(x)); - `, - errors: [ - { - messageId: 'returnTypeAreNotObjectSyntax', - data: { returnType: 'null' }, - }, - ], - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = (x) => { - switch (x) { - case 1: - return { queryKey: "foo", queryFn: () => Promise.resolve(1) }; - default: - return null; - } - }; - useQuery(getQuery(x)); - `, - errors: [ - { - messageId: 'returnTypeAreNotObjectSyntax', - data: { returnType: 'null' }, - }, - ], - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = (x, y) => { - if (x) { - return { queryKey: "foo", queryFn: () => Promise.resolve(1) }; - } else { - if (y) { - return { queryKey: "bar", queryFn: () => Promise.resolve(2) }; - } else { - return () => Promise.resolve(3); - } - } - }; - useQuery(getQuery(x)); - `, - errors: [ - { - messageId: 'returnTypeAreNotObjectSyntax', - data: { returnType: '() => Promise.resolve(3)' }, - }, - ], - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(['data'], { queryFn: () => fetchData(), enabled: false }); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey: ['data'], queryFn: () => fetchData(), enabled: false }); - `, - }, - { - code: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery(['data'], { queryFn: () => fetchData(), enabled: false }); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery({ queryKey: ['data'], queryFn: () => fetchData(), enabled: false }); - `, - }, - { - code: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery( - ['key'], - () => Promise.resolve('data') - ); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery({ queryKey: ['key'], queryFn: () => Promise.resolve('data') }); - `, - }, - { - code: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery( - ['key'], () => Promise.resolve('data') - ); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery({ queryKey: ['key'], queryFn: () => Promise.resolve('data') }); - `, - }, - { - code: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery(['key'], () => Promise.resolve('data')); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery({ queryKey: ['key'], queryFn: () => Promise.resolve('data') }); - `, - }, - { - code: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery< - A, - B - >(['key'], () => Promise.resolve('data')); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery< - A, - B - >({ queryKey: ['key'], queryFn: () => Promise.resolve('data') }); - `, - }, - { - code: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - const queryKeys = { userById: (userId: string) => ["users", {userId}] } - createQuery(queryKeys.userById(userId), async () => await fetchUserById(userId)); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - const queryKeys = { userById: (userId: string) => ["users", {userId}] } - createQuery({ queryKey: queryKeys.userById(userId), queryFn: async () => await fetchUserById(userId) }); - `, - }, - ], -}) diff --git a/packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.ts b/packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.ts deleted file mode 100644 index 4e31aba47c..0000000000 --- a/packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.ts +++ /dev/null @@ -1,227 +0,0 @@ -import type { TSESLint, TSESTree } from '@typescript-eslint/utils' -import { AST_NODE_TYPES } from '@typescript-eslint/utils' -import { createRule } from '../../utils/create-rule' -import { ASTUtils } from '../../utils/ast-utils' - -const QUERY_CALLS = ['useQuery', 'createQuery'] - -const messages = { - preferObjectSyntax: `Objects syntax for query is preferred`, - returnTypeAreNotObjectSyntax: `Return type of query should be object syntax. Got {{returnType}} instead`, -} - -type MessageKey = keyof typeof messages - -export const name = 'prefer-query-object-syntax' - -export const rule: TSESLint.RuleModule = - createRule({ - name, - meta: { - type: 'problem', - docs: { - description: 'Prefer object syntax for useQuery', - recommended: 'error', - }, - messages: messages, - fixable: 'code', - schema: [], - }, - defaultOptions: [], - - create(context, _, helpers) { - return { - CallExpression(node) { - const isTanstackQueryCall = - ASTUtils.isIdentifierWithOneOfNames(node.callee, QUERY_CALLS) && - helpers.isTanstackQueryImport(node.callee) - - if (!isTanstackQueryCall) { - return - } - - const firstArgument = node.arguments[0] - - if (!firstArgument) { - return - } - - if ( - node.arguments.length === 1 && - firstArgument.type === AST_NODE_TYPES.CallExpression - ) { - const referencedCallExpression = - ASTUtils.getReferencedExpressionByIdentifier({ - context, - node: firstArgument.callee, - }) - - if ( - referencedCallExpression === null || - !ASTUtils.isNodeOfOneOf(referencedCallExpression, [ - AST_NODE_TYPES.ArrowFunctionExpression, - AST_NODE_TYPES.FunctionExpression, - ]) - ) { - return - } - - if ( - !ASTUtils.isNodeOfOneOf(referencedCallExpression.body, [ - AST_NODE_TYPES.BlockStatement, - AST_NODE_TYPES.ObjectExpression, - ]) - ) { - return context.report({ - node, - messageId: 'returnTypeAreNotObjectSyntax', - data: { - returnType: context - .getSourceCode() - .getText(referencedCallExpression.body), - }, - }) - } - - const returnStmts = ASTUtils.getNestedReturnStatements( - referencedCallExpression, - ) - - for (const stmt of returnStmts) { - if (stmt.argument === null) { - return context.report({ - node, - messageId: 'returnTypeAreNotObjectSyntax', - data: { - returnType: 'void', - }, - }) - } - - runCheckOnNode({ - context: context, - callNode: node, - expression: stmt.argument, - messageId: 'returnTypeAreNotObjectSyntax', - }) - } - - return - } - - if (firstArgument.type === AST_NODE_TYPES.Identifier) { - const referencedNode = ASTUtils.getReferencedExpressionByIdentifier( - { - context, - node: firstArgument, - }, - ) - - if (referencedNode?.type === AST_NODE_TYPES.ObjectExpression) { - return runCheckOnNode({ - context: context, - callNode: node, - expression: referencedNode, - messageId: 'preferObjectSyntax', - }) - } - } - - runCheckOnNode({ - context: context, - callNode: node, - expression: firstArgument, - messageId: 'preferObjectSyntax', - }) - }, - } - }, - }) - -function runCheckOnNode(params: { - context: Readonly> - callNode: TSESTree.CallExpression - expression: TSESTree.Node - messageId: MessageKey -}) { - const { context, expression, messageId, callNode } = params - const sourceCode = context.getSourceCode() - - if (expression.type === AST_NODE_TYPES.ObjectExpression) { - return - } - - const secondArgument = callNode.arguments[1] - const thirdArgument = callNode.arguments[2] - - const optionsObject = - secondArgument?.type === AST_NODE_TYPES.ObjectExpression - ? secondArgument - : thirdArgument?.type === AST_NODE_TYPES.ObjectExpression - ? thirdArgument - : undefined - - if ( - secondArgument && - !thirdArgument && - secondArgument !== optionsObject && - secondArgument.type === AST_NODE_TYPES.Identifier - ) { - // Unable to determine if the secondArgument identifier is the options object or query fn. - // User has to fix the code manually. - context.report({ node: callNode, messageId: messageId }) - return - } - - if (messageId === 'returnTypeAreNotObjectSyntax') { - context.report({ - node: callNode, - messageId: 'returnTypeAreNotObjectSyntax', - data: { - returnType: sourceCode.getText(expression), - }, - }) - return - } - - context.report({ - node: callNode, - messageId: 'preferObjectSyntax', - fix(fixer) { - const optionsObjectProperties: string[] = [] - - // queryKey - const firstArgument = callNode.arguments[0] - const queryKey = sourceCode.getText(firstArgument) - const queryKeyProperty = - queryKey === 'queryKey' ? 'queryKey' : `queryKey: ${queryKey}` - - optionsObjectProperties.push(queryKeyProperty) - - // queryFn - if (secondArgument && secondArgument !== optionsObject) { - const queryFn = sourceCode.getText(secondArgument) - const queryFnProperty = - queryFn === 'queryFn' ? 'queryFn' : `queryFn: ${queryFn}` - - optionsObjectProperties.push(queryFnProperty) - } - - // options - if (optionsObject) { - const existingObjectProperties = optionsObject.properties.map( - (objectLiteral) => { - return sourceCode.getText(objectLiteral) - }, - ) - - optionsObjectProperties.push(...existingObjectProperties) - } - - const calleeText = sourceCode.getText(callNode).split('(')[0] - const argsText = `{ ${optionsObjectProperties.join(', ')} }` - - return fixer.replaceText(callNode, `${calleeText}(${argsText})`) - }, - }) -}