-
-
Notifications
You must be signed in to change notification settings - Fork 0
feat: implement queryOptions export pattern and type-safe query key builder #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
a38af84
feat: implement queryOptions export pattern for GET methods
kyleamazza-fq 87d7e2e
docs: Update documentation for queryOptions export pattern
kyleamazza-fq d9fa0dd
Complete Task Group 4: Final verification and release preparation
kyleamazza-fq d300bdf
refactor: preserve full operation names in query options
kyleamazza-fq dec74f9
feat: implement type-safe query key builder generator
kyleamazza-fq 02af6ca
feat: enhance query key builder with improved matchQueryKey support
kyleamazza-fq 33b6acb
refactor: rename query-key-builder-file.ts to query-key-builder.ts
kyleamazza-fq 5ae0718
test: add comprehensive tests for query key builder and update docume…
kyleamazza-fq 015214a
docs: update changelog for v0.2.0 with matchQueryKey feature
kyleamazza-fq 9925b21
Remove plan/ and CLAUDE.md from tracking and add to .gitignore
kyleamazza-fq 79e6135
refactor: use service-specific names instead of generic names
kyleamazza-fq 028af00
fix: keep full method names for infinite query options
kyleamazza-fq c1af9b1
test: add coverage for infinite query options generation
kyleamazza-fq 1c82078
docs: update CHANGELOG for v0.2.0 with complete feature list
kyleamazza-fq cda04f4
style: apply prettier formatting
kyleamazza-fq 5bd1ffb
doc: update basketry URL to doc website
kyleamazza-fq 7e80c01
fix: remove non-null assertion in query key builder
kyleamazza-fq 85e52ac
chore: lint
kyleamazza-fq ebff0d2
0.2.0-alpha.0
kyleamazza 3d9c6f5
feat: re-enable hook name generation for deprecated patterns
kyleamazza-fq 7789c6e
feat: add deprecated hook wrappers for backwards compatibility
kyleamazza-fq a44997f
feat: add tests for deprecated hook generation (Task Group 4)
kyleamazza-fq fcf1046
feat: prepare v0.2.0-alpha.1 release with documentation
kyleamazza-fq 7163b88
feat: add jscodeshift codemod for v0.2 migration
kyleamazza-fq 4dcc25b
docs: add codemod reference to migration guide
kyleamazza-fq c023bf8
docs: add npx jscodeshift command to migration guide
kyleamazza-fq 5d5d830
chore: prepare for v0.2.0-alpha.1 release
kyleamazza-fq e8df9af
fix: revert version to 0.2.0-alpha.0
kyleamazza-fq 215adfd
0.2.0-alpha.1
kyleamazza 035a7c4
fix: handle Get prefix removal for backwards compatibility
kyleamazza-fq 5a225eb
Merge branch 'export-query-options' of personal-github:basketry/react…
kyleamazza-fq 50833b1
style: apply prettier formatting
kyleamazza-fq d949488
0.2.0-alpha.2
kyleamazza 6c5a439
fix: restore v0.1.0 hook patterns with options parameters
kyleamazza-fq c548a49
Merge branch 'export-query-options' of personal-github:basketry/react…
kyleamazza-fq File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -130,3 +130,7 @@ dist | |
| .pnp.* | ||
|
|
||
| lib/ | ||
|
|
||
| # Project-specific files | ||
| plan/ | ||
| CLAUDE.md | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,3 +4,6 @@ | |
| src | ||
| **/snapshot | ||
| /*.* | ||
| !codemod | ||
| codemod/__tests__ | ||
| codemod/__testfixtures__ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| # Changelog | ||
|
|
||
| All notable changes to this project will be documented in this file. | ||
|
|
||
| The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), | ||
| and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||
|
|
||
| ## [0.2.0-alpha.1] - 2025-07-17 | ||
|
|
||
| ### Added | ||
|
|
||
| - Backwards compatibility layer with deprecated hook wrappers | ||
| - All existing `useXxx()` hooks continue to work but are marked as deprecated | ||
| - Hooks include migration instructions in JSDoc comments | ||
| - Mutation hooks maintain automatic query invalidation behavior | ||
| - jscodeshift codemod for automated migration from v0.1.x to v0.2.x | ||
| - Automatically transforms deprecated hooks to new queryOptions pattern | ||
| - Preserves all parameters, options, and TypeScript types | ||
| - Includes dry-run mode for safe preview of changes | ||
|
|
||
| ### Changed | ||
|
|
||
| - Re-added deprecated hooks alongside new queryOptions exports for smoother migration path | ||
|
|
||
| ## [0.2.0-alpha.0] - 2025-07-17 | ||
|
|
||
| ### Changed | ||
|
|
||
| - **BREAKING**: Migrated from wrapper hooks to queryOptions/mutationOptions export pattern | ||
| - Changed from `useWidgets()` to `getWidgetsQueryOptions()` | ||
| - Changed from `useCreateWidget()` to `createWidgetMutationOptions()` | ||
| - Changed from `useInfiniteWidgets()` to `getWidgetsInfiniteQueryOptions()` | ||
| - **BREAKING**: Updated query key structure for better cache management | ||
| - From: `['/widgets', compact({ status: params?.status })].filter(Boolean)` | ||
| - To: `['widget', 'getWidgets', params || {}] as const` | ||
| - Added non-hook service getters in context for use in queryOptions | ||
|
|
||
| ### Added | ||
|
|
||
| - Type-safe `matchQueryKey` function for building query keys with IntelliSense support | ||
| - Supports partial query matching at service, operation, or full parameter levels | ||
| - Provides compile-time type safety and autocomplete for all query operations | ||
| - Enables flexible cache invalidation patterns | ||
| - Test coverage for infinite query options generation | ||
| - Support for direct composition with React Query hooks | ||
| - Better TypeScript inference with queryOptions pattern | ||
|
|
||
| ### Removed | ||
|
|
||
| - Wrapper hook functions (use queryOptions with React Query hooks directly) | ||
| - Complex query key filtering logic | ||
|
|
||
| ## [0.1.x] - Previous versions | ||
|
|
||
| Initial implementation with wrapper hooks pattern. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,203 @@ | ||
| # React Query v0.2 Migration Codemod | ||
|
|
||
| This codemod helps automatically migrate your codebase from `@basketry/react-query` v0.1.x to v0.2.x by transforming deprecated hook patterns to the new queryOptions pattern. | ||
|
|
||
| ## What it does | ||
|
|
||
| The codemod will transform: | ||
|
|
||
| ### Query Hooks | ||
|
|
||
| ```typescript | ||
| // Before | ||
| import { useGetWidgets } from '../hooks/widgets'; | ||
| const { data } = useGetWidgets({ status: 'active' }); | ||
|
|
||
| // After | ||
| import { useQuery } from '@tanstack/react-query'; | ||
| import { getWidgetsQueryOptions } from '../hooks/widgets'; | ||
| const { data } = useQuery(getWidgetsQueryOptions({ status: 'active' })); | ||
| ``` | ||
|
|
||
| ### Mutation Hooks | ||
|
|
||
| ```typescript | ||
| // Before | ||
| import { useCreateWidget } from '../hooks/widgets'; | ||
| const mutation = useCreateWidget({ onSuccess: handleSuccess }); | ||
|
|
||
| // After | ||
| import { useMutation } from '@tanstack/react-query'; | ||
| import { createWidgetMutationOptions } from '../hooks/widgets'; | ||
| const mutation = useMutation( | ||
| createWidgetMutationOptions({ onSuccess: handleSuccess }), | ||
| ); | ||
| ``` | ||
|
|
||
| ### Infinite Query Hooks | ||
|
|
||
| ```typescript | ||
| // Before | ||
| import { useGetWidgetsInfinite } from '../hooks/widgets'; | ||
| const { data, fetchNextPage } = useGetWidgetsInfinite({ limit: 20 }); | ||
|
|
||
| // After | ||
| import { useInfiniteQuery } from '@tanstack/react-query'; | ||
| import { getWidgetsInfiniteQueryOptions } from '../hooks/widgets'; | ||
| const { data, fetchNextPage } = useInfiniteQuery( | ||
| getWidgetsInfiniteQueryOptions({ limit: 20 }), | ||
| ); | ||
| ``` | ||
|
|
||
| ### Suspense Hooks | ||
|
|
||
| ```typescript | ||
| // Before | ||
| import { useSuspenseGetWidgets } from '../hooks/widgets'; | ||
| const { data } = useSuspenseGetWidgets(); | ||
|
|
||
| // After | ||
| import { useSuspenseQuery } from '@tanstack/react-query'; | ||
| import { getWidgetsQueryOptions } from '../hooks/widgets'; | ||
| const { data } = useSuspenseQuery(getWidgetsQueryOptions()); | ||
| ``` | ||
|
|
||
| ## Installation | ||
|
|
||
| ```bash | ||
| # Install jscodeshift globally | ||
| npm install -g jscodeshift | ||
|
|
||
| # Or use npx (no installation needed) | ||
| npx jscodeshift ... | ||
| ``` | ||
|
|
||
| ## Usage | ||
|
|
||
| ### Basic Usage | ||
|
|
||
| ```bash | ||
| # Dry run (preview changes without modifying files) | ||
| jscodeshift -t codemod/react-query-v0.2-migration.js src/ --extensions=ts,tsx --parser=tsx --dry | ||
|
|
||
| # Run the transformation | ||
| jscodeshift -t codemod/react-query-v0.2-migration.js src/ --extensions=ts,tsx --parser=tsx | ||
| ``` | ||
|
|
||
| ### Specific Files or Directories | ||
|
|
||
| ```bash | ||
| # Transform a single file | ||
| jscodeshift -t codemod/react-query-v0.2-migration.js src/components/WidgetList.tsx --parser=tsx | ||
|
|
||
| # Transform a specific directory | ||
| jscodeshift -t codemod/react-query-v0.2-migration.js src/features/widgets/ --extensions=ts,tsx --parser=tsx | ||
| ``` | ||
|
|
||
| ### With Git | ||
|
|
||
| ```bash | ||
| # See what would change | ||
| jscodeshift -t codemod/react-query-v0.2-migration.js src/ --extensions=ts,tsx --parser=tsx --dry | ||
|
|
||
| # Run and see the diff | ||
| jscodeshift -t codemod/react-query-v0.2-migration.js src/ --extensions=ts,tsx --parser=tsx | ||
| git diff | ||
|
|
||
| # If something went wrong, revert | ||
| git checkout -- . | ||
| ``` | ||
|
|
||
| ## Features | ||
|
|
||
| - ✅ Transforms all deprecated hook types (query, mutation, infinite, suspense) | ||
| - ✅ Preserves TypeScript type parameters | ||
| - ✅ Updates imports correctly | ||
| - ✅ Handles multiple hooks from the same module | ||
| - ✅ Adds React Query imports only when needed | ||
| - ✅ Preserves existing React Query imports | ||
| - ✅ Maintains code formatting | ||
| - ✅ Only transforms hooks from generated `hooks/` modules | ||
|
|
||
| ## Limitations | ||
|
|
||
| 1. **Hooks in Dynamic Contexts**: The codemod may not handle hooks called in complex dynamic contexts (e.g., inside conditional logic or loops). | ||
|
|
||
| 2. **Custom Wrappers**: If you've created custom wrappers around the generated hooks, those won't be automatically migrated. | ||
|
|
||
| 3. **Import Aliases**: If you're using import aliases or renamed imports, you may need to update those manually: | ||
|
|
||
| ```typescript | ||
| // This won't be transformed automatically | ||
| import { useGetWidgets as useWidgets } from '../hooks/widgets'; | ||
| ``` | ||
|
|
||
| 4. **Side Effects**: The old mutation hooks automatically invalidated queries on success. The new pattern requires you to handle this in your mutationOptions if needed. | ||
|
|
||
| ## Testing the Codemod | ||
|
|
||
| ### Run Tests | ||
|
|
||
| ```bash | ||
| # Install dependencies | ||
| npm install | ||
|
|
||
| # Run the test suite | ||
| npm test codemod/__tests__/react-query-v0.2-migration.test.js | ||
| ``` | ||
|
|
||
| ### Test on a Single File | ||
|
|
||
| ```bash | ||
| # Create a test file | ||
| echo "import { useGetWidgets } from './hooks/widgets'; | ||
| const Component = () => { | ||
| const { data } = useGetWidgets(); | ||
| return <div>{data?.length}</div>; | ||
| };" > test-migration.tsx | ||
|
|
||
| # Run the codemod | ||
| jscodeshift -t codemod/react-query-v0.2-migration.js test-migration.tsx --parser=tsx --print | ||
| ``` | ||
|
|
||
| ## Manual Review Checklist | ||
|
|
||
| After running the codemod, review: | ||
|
|
||
| 1. **Build**: Run `npm run build` to ensure no TypeScript errors | ||
| 2. **Tests**: Run your test suite to ensure functionality is preserved | ||
| 3. **Mutations**: Check that mutation success handlers still invalidate queries if needed | ||
| 4. **Imports**: Verify all imports are correct and no duplicates exist | ||
| 5. **Runtime**: Test your application to ensure everything works as expected | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### "Cannot find module" errors | ||
|
|
||
| Make sure you're running the codemod from your project root where `node_modules` is located. | ||
|
|
||
| ### Parser errors | ||
|
|
||
| Ensure you're using the `--parser=tsx` flag for TypeScript files. | ||
|
|
||
| ### Nothing is transformed | ||
|
|
||
| Check that your imports match the expected pattern (from `'../hooks/[service]'` modules). | ||
|
|
||
| ### Formatting issues | ||
|
|
||
| The codemod tries to preserve formatting, but you may want to run your formatter after: | ||
|
|
||
| ```bash | ||
| npm run prettier -- --write src/ | ||
| # or | ||
| npm run eslint -- --fix src/ | ||
| ``` | ||
|
|
||
| ## Need Help? | ||
|
|
||
| If you encounter issues: | ||
|
|
||
| 1. Check the [migration guide](../README.md#migration-guide-v01x-to-v02x) in the main README | ||
| 2. Look at the generated hooks to understand the new pattern | ||
| 3. Open an issue with a code sample that isn't working correctly |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.