Skip to content
Closed
Show file tree
Hide file tree
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 Jun 14, 2025
87d7e2e
docs: Update documentation for queryOptions export pattern
kyleamazza-fq Jun 14, 2025
d9fa0dd
Complete Task Group 4: Final verification and release preparation
kyleamazza-fq Jun 14, 2025
d300bdf
refactor: preserve full operation names in query options
kyleamazza-fq Jun 15, 2025
dec74f9
feat: implement type-safe query key builder generator
kyleamazza-fq Jun 15, 2025
02af6ca
feat: enhance query key builder with improved matchQueryKey support
kyleamazza-fq Jun 15, 2025
33b6acb
refactor: rename query-key-builder-file.ts to query-key-builder.ts
kyleamazza-fq Jun 15, 2025
5ae0718
test: add comprehensive tests for query key builder and update docume…
kyleamazza-fq Jun 15, 2025
015214a
docs: update changelog for v0.2.0 with matchQueryKey feature
kyleamazza-fq Jun 15, 2025
9925b21
Remove plan/ and CLAUDE.md from tracking and add to .gitignore
kyleamazza-fq Jun 15, 2025
79e6135
refactor: use service-specific names instead of generic names
kyleamazza-fq Jun 15, 2025
028af00
fix: keep full method names for infinite query options
kyleamazza-fq Jun 15, 2025
c1af9b1
test: add coverage for infinite query options generation
kyleamazza-fq Jun 15, 2025
1c82078
docs: update CHANGELOG for v0.2.0 with complete feature list
kyleamazza-fq Jun 15, 2025
cda04f4
style: apply prettier formatting
kyleamazza-fq Jun 15, 2025
5bd1ffb
doc: update basketry URL to doc website
kyleamazza-fq Jun 15, 2025
7e80c01
fix: remove non-null assertion in query key builder
kyleamazza-fq Jun 15, 2025
85e52ac
chore: lint
kyleamazza-fq Jun 16, 2025
ebff0d2
0.2.0-alpha.0
kyleamazza Jun 27, 2025
3d9c6f5
feat: re-enable hook name generation for deprecated patterns
kyleamazza-fq Jul 17, 2025
7789c6e
feat: add deprecated hook wrappers for backwards compatibility
kyleamazza-fq Jul 17, 2025
a44997f
feat: add tests for deprecated hook generation (Task Group 4)
kyleamazza-fq Jul 17, 2025
fcf1046
feat: prepare v0.2.0-alpha.1 release with documentation
kyleamazza-fq Jul 17, 2025
7163b88
feat: add jscodeshift codemod for v0.2 migration
kyleamazza-fq Jul 17, 2025
4dcc25b
docs: add codemod reference to migration guide
kyleamazza-fq Jul 17, 2025
c023bf8
docs: add npx jscodeshift command to migration guide
kyleamazza-fq Jul 17, 2025
5d5d830
chore: prepare for v0.2.0-alpha.1 release
kyleamazza-fq Jul 17, 2025
e8df9af
fix: revert version to 0.2.0-alpha.0
kyleamazza-fq Jul 17, 2025
215adfd
0.2.0-alpha.1
kyleamazza Jul 17, 2025
035a7c4
fix: handle Get prefix removal for backwards compatibility
kyleamazza-fq Jul 18, 2025
5a225eb
Merge branch 'export-query-options' of personal-github:basketry/react…
kyleamazza-fq Jul 18, 2025
50833b1
style: apply prettier formatting
kyleamazza-fq Jul 18, 2025
d949488
0.2.0-alpha.2
kyleamazza Jul 18, 2025
6c5a439
fix: restore v0.1.0 hook patterns with options parameters
kyleamazza-fq Jul 18, 2025
c548a49
Merge branch 'export-query-options' of personal-github:basketry/react…
kyleamazza-fq Jul 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,7 @@ dist
.pnp.*

lib/

# Project-specific files
plan/
CLAUDE.md
3 changes: 3 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
src
**/snapshot
/*.*
!codemod
codemod/__tests__
codemod/__testfixtures__
55 changes: 55 additions & 0 deletions CHANGELOG.md
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.
101 changes: 97 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,106 @@

# React Query

[Basketry generator](https://github.com/basketry/basketry) for generating React Query hooks. This parser can be coupled with any Basketry parser.
[Basketry generator](https://basketry.io) for generating [React Query](https://tanstack.com/query) (TanStack Query) hooks and query/mutation options. This generator can be coupled with any Basketry parser to automatically generate type-safe React Query integration from your API definitions.

## Quick Start
## Features

// TODO
- Generates type-safe query and mutation options following React Query v5 patterns
- Type-safe query key builder for cache operations with IntelliSense support
- Support for infinite queries with Relay-style pagination
- Full TypeScript support with proper type inference
- Backwards compatibility with deprecated hook wrappers for smooth migration

---
## Migration Guide (v0.1.x to v0.2.x)

Starting with v0.2.0, this generator adopts the React Query v5 queryOptions pattern. The old hook wrappers are deprecated but still available for backwards compatibility.

### Query Hooks

```typescript
// Old pattern (deprecated)
import { useGetWidgets } from './hooks/widgets';
const result = useGetWidgets(params);

// New pattern
import { useQuery } from '@tanstack/react-query';
import { getWidgetsQueryOptions } from './hooks/widgets';
const result = useQuery(getWidgetsQueryOptions(params));
```

### Mutation Hooks

```typescript
// Old pattern (deprecated)
import { useCreateWidget } from './hooks/widgets';
const mutation = useCreateWidget();

// New pattern
import { useMutation } from '@tanstack/react-query';
import { createWidgetMutationOptions } from './hooks/widgets';
const mutation = useMutation(createWidgetMutationOptions());
```

### Infinite Query Hooks

```typescript
// Old pattern (deprecated)
import { useGetWidgetsInfinite } from './hooks/widgets';
const result = useGetWidgetsInfinite(params);

// New pattern
import { useInfiniteQuery } from '@tanstack/react-query';
import { getWidgetsInfiniteQueryOptions } from './hooks/widgets';
const result = useInfiniteQuery(getWidgetsInfiniteQueryOptions(params));
```

### Query Key Builder

The new version includes a type-safe query key builder for cache operations:

```typescript
import { matchQueryKey } from './hooks/query-key-builder';

// Invalidate all queries for a service
queryClient.invalidateQueries({ queryKey: matchQueryKey('widgets') });

// Invalidate specific operation
queryClient.invalidateQueries({
queryKey: matchQueryKey('widgets', 'getWidgets'),
});

// Invalidate with specific parameters
queryClient.invalidateQueries({
queryKey: matchQueryKey('widgets', 'getWidgets', { status: 'active' }),
});
```

### Benefits of the New Pattern

- Better tree-shaking - only import what you use
- More flexible - compose with any React Query hook
- Better TypeScript inference
- Easier testing - options can be tested without React context
- Consistent with React Query v5 best practices

### Automated Migration

We provide a jscodeshift codemod to automatically migrate your codebase:

```bash
# Using the provided script
./node_modules/@basketry/react-query/codemod/run-migration.sh # Preview (dry run)
./node_modules/@basketry/react-query/codemod/run-migration.sh --apply # Apply changes

# Or using jscodeshift directly
npx jscodeshift -t ./node_modules/@basketry/react-query/codemod/react-query-v0.2-migration.js \
src/ --extensions=ts,tsx --parser=tsx --dry # Preview (dry run)

npx jscodeshift -t ./node_modules/@basketry/react-query/codemod/react-query-v0.2-migration.js \
src/ --extensions=ts,tsx --parser=tsx # Apply changes
```

See [codemod documentation](./codemod/README.md) for more details.

## For contributors:

Expand Down
203 changes: 203 additions & 0 deletions codemod/README.md
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
Loading
Loading