Skip to content
Closed
Show file tree
Hide file tree
Changes from 18 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
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Changelog
Comment thread
kyleamazza marked this conversation as resolved.

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] - TBD

### 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.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@

# 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
Comment thread
kyleamazza marked this conversation as resolved.
- 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

## For contributors:

Expand Down
38 changes: 30 additions & 8 deletions src/context-file.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { camel, pascal } from 'case';
import { ModuleBuilder } from './module-builder';
import { ImportBuilder } from './import-builder';
import { NameFactory } from './name-factory';

export class ContextFile extends ModuleBuilder {
private readonly nameFactory = new NameFactory(this.service, this.options);
private readonly react = new ImportBuilder('react');
private readonly client = new ImportBuilder(
this.options?.reactQuery?.clientModule ?? '../http-client',
Expand All @@ -23,23 +25,43 @@ export class ContextFile extends ModuleBuilder {
const FetchLike = () => this.client.type('FetchLike');
const OptionsType = () => this.client.type(optionsName);

yield `export interface ClientContextProps { fetch: ${FetchLike()}; options: ${OptionsType()}; }`;
yield `const ClientContext = ${createContext()}<ClientContextProps | undefined>( undefined );`;
const contextName = this.nameFactory.buildContextName();
const contextPropsName = pascal(`${contextName}_props`);
const providerName = this.nameFactory.buildProviderName();

yield `export interface ${contextPropsName} { fetch: ${FetchLike()}; options: ${OptionsType()}; }`;
yield `const ${contextName} = ${createContext()}<${contextPropsName} | undefined>( undefined );`;
yield ``;
// Store context for non-hook access
yield `let currentContext: ${contextPropsName} | undefined;`;
yield ``;
yield `export const ClientProvider: ${FC()}<${PropsWithChildren()}<ClientContextProps>> = ({ children, fetch, options }) => {`;
yield `export const ${providerName}: ${FC()}<${PropsWithChildren()}<${contextPropsName}>> = ({ children, fetch, options }) => {`;
yield ` const value = ${useMemo()}(() => ({ fetch, options }), [fetch, options.mapUnhandledException, options.mapValidationError, options.root]);`;
yield ` return <ClientContext.Provider value={value}>{children}</ClientContext.Provider>;`;
yield ` currentContext = value;`;
yield ` return <${contextName}.Provider value={value}>{children}</${contextName}.Provider>;`;
yield `};`;
for (const int of this.service.interfaces) {
const hookName = camel(`use_${int.name.value}_service`);
const localName = camel(`${int.name.value}_service`);
const hookName = this.nameFactory.buildServiceHookName(int);
const localName = this.nameFactory.buildServiceName(int);
const interfaceName = pascal(`${int.name.value}_service`);
const className = pascal(`http_${int.name.value}_service`);

const getterName = camel(`get_${int.name.value}_service`);

yield ``;
yield `export const ${getterName} = () => {`;
yield ` if (!currentContext) { throw new Error('${getterName} called outside of ${providerName}'); }`;
yield ` const ${localName}: ${this.types.type(
interfaceName,
)} = new ${this.client.fn(
className,
)}(currentContext.fetch, currentContext.options);`;
yield ` return ${localName};`;
yield `}`;
yield ``;
yield `export const ${hookName} = () => {`;
yield ` const context = ${useContext()}(ClientContext);`;
yield ` if (!context) { throw new Error('${hookName} must be used within a ClientProvider'); }`;
yield ` const context = ${useContext()}(${contextName});`;
yield ` if (!context) { throw new Error('${hookName} must be used within a ${providerName}'); }`;
yield ` const ${localName}: ${this.types.type(
interfaceName,
)} = new ${this.client.fn(className)}(context.fetch, context.options);`;
Expand Down
Loading