diff --git a/crates/graphql_artifact_generation/src/eager_reader_artifact.rs b/crates/graphql_artifact_generation/src/eager_reader_artifact.rs index 8b080b628..c5eda84ed 100644 --- a/crates/graphql_artifact_generation/src/eager_reader_artifact.rs +++ b/crates/graphql_artifact_generation/src/eager_reader_artifact.rs @@ -165,7 +165,7 @@ pub(crate) fn generate_eager_reader_condition_artifact( {}{reader_output_type}\n\ > = {{\n\ {}kind: \"EagerReaderArtifact\",\n\ - {}resolver: ({{ data }}) => data.__typename === \"{concrete_type}\" ? data.link : null,\n\ + {}resolver: ({{ firstParameter }}) => firstParameter.data.__typename === \"{concrete_type}\" ? firstParameter.data.link : null,\n\ {}readerAst,\n\ }};\n\n\ export default artifact;\n", diff --git a/crates/graphql_artifact_generation/src/iso_overload_file.rs b/crates/graphql_artifact_generation/src/iso_overload_file.rs index 6df8cf6c6..af241dbd5 100644 --- a/crates/graphql_artifact_generation/src/iso_overload_file.rs +++ b/crates/graphql_artifact_generation/src/iso_overload_file.rs @@ -107,7 +107,7 @@ pub(crate) fn build_iso_overload_artifact( // of type TParam. type IdentityWithParam = ( clientField: (param: TParam) => TClientFieldReturn -) => (param: TParam) => TClientFieldReturn; +) => (data: { firstParameter: TParam }) => TClientFieldReturn; // This is the type given it to client fields with @component. // This means that the type of the exported iso literal is exactly @@ -121,7 +121,7 @@ type IdentityWithParamComponent = < TComponentProps = Record, >( clientComponentField: (data: TParam, componentProps: TComponentProps) => TClientFieldReturn -) => (data: TParam, componentProps: TComponentProps) => TClientFieldReturn; +) => (props: { firstParameter: TParam, additionalRuntimeProps: TComponentProps }) => TClientFieldReturn; type WhitespaceCharacter = ' ' | '\\t' | '\\n'; type Whitespace = In extends `${WhitespaceCharacter}${infer In}` @@ -168,24 +168,24 @@ type MatchesWhitespaceAndString< content.push_str(&entrypoint_overload); } - content.push_str( - " -export function iso(_isographLiteralText: string): + content.push_str(match no_babel_transform { + false => { + "export function iso(clientFieldResolver: any): | IdentityWithParam | IdentityWithParamComponent | IsographEntrypoint -{\n", - ); - - content.push_str(match no_babel_transform { - false => { -" throw new Error('iso: Unexpected invocation at runtime. Either the Babel transform ' + - 'was not set up, or it failed to identify this call site. Make sure it ' + - 'is being used verbatim as `iso`. If you cannot use the babel transform, ' + - 'set options.no_babel_transform to true in your Isograph config. ');" +{\n return (props: any) => { + return clientFieldResolver(props.firstParameter, props.additionalRuntimeProps) + };" } true => { - " return (clientFieldResolver: any) => clientFieldResolver;" + "export function iso(_isographLiteralText: string): + | IdentityWithParam + | IdentityWithParamComponent + | IsographEntrypoint +{\n return (clientFieldResolver: any) => (props: any) => { + return clientFieldResolver(props.firstParameter, props.additionalRuntimeProps) + };" } }); diff --git a/demos/github-demo/src/isograph-components/__isograph/User/asUser/resolver_reader.ts b/demos/github-demo/src/isograph-components/__isograph/User/asUser/resolver_reader.ts index 6c7cc03ef..0a0d2237f 100644 --- a/demos/github-demo/src/isograph-components/__isograph/User/asUser/resolver_reader.ts +++ b/demos/github-demo/src/isograph-components/__isograph/User/asUser/resolver_reader.ts @@ -18,7 +18,7 @@ const artifact: EagerReaderArtifact< Link | null > = { kind: "EagerReaderArtifact", - resolver: ({ data }) => data.__typename === "User" ? data.link : null, + resolver: ({ firstParameter }) => firstParameter.data.__typename === "User" ? firstParameter.data.link : null, readerAst, }; diff --git a/demos/github-demo/src/isograph-components/__isograph/iso.ts b/demos/github-demo/src/isograph-components/__isograph/iso.ts index 85346833b..626aefd4a 100644 --- a/demos/github-demo/src/isograph-components/__isograph/iso.ts +++ b/demos/github-demo/src/isograph-components/__isograph/iso.ts @@ -31,7 +31,7 @@ import entrypoint_Query__UserPage from '../__isograph/Query/UserPage/entrypoint' // of type TParam. type IdentityWithParam = ( clientField: (param: TParam) => TClientFieldReturn -) => (param: TParam) => TClientFieldReturn; +) => (data: { firstParameter: TParam }) => TClientFieldReturn; // This is the type given it to client fields with @component. // This means that the type of the exported iso literal is exactly @@ -45,7 +45,7 @@ type IdentityWithParamComponent = < TComponentProps = Record, >( clientComponentField: (data: TParam, componentProps: TComponentProps) => TClientFieldReturn -) => (data: TParam, componentProps: TComponentProps) => TClientFieldReturn; +) => (props: { firstParameter: TParam, additionalRuntimeProps: TComponentProps }) => TClientFieldReturn; type WhitespaceCharacter = ' ' | '\t' | '\n'; type Whitespace = In extends `${WhitespaceCharacter}${infer In}` @@ -172,14 +172,12 @@ export function iso( export function iso( param: T & MatchesWhitespaceAndString<'entrypoint Query.UserPage', T> ): typeof entrypoint_Query__UserPage; - -export function iso(_isographLiteralText: string): +export function iso(clientFieldResolver: any): | IdentityWithParam | IdentityWithParamComponent | IsographEntrypoint { - throw new Error('iso: Unexpected invocation at runtime. Either the Babel transform ' + - 'was not set up, or it failed to identify this call site. Make sure it ' + - 'is being used verbatim as `iso`. If you cannot use the babel transform, ' + - 'set options.no_babel_transform to true in your Isograph config. '); + return (props: any) => { + return clientFieldResolver(props.firstParameter, props.additionalRuntimeProps) + }; } \ No newline at end of file diff --git a/demos/pet-demo/src/components/__isograph/AdItem/asAdItem/resolver_reader.ts b/demos/pet-demo/src/components/__isograph/AdItem/asAdItem/resolver_reader.ts index 7562bd540..f983b74aa 100644 --- a/demos/pet-demo/src/components/__isograph/AdItem/asAdItem/resolver_reader.ts +++ b/demos/pet-demo/src/components/__isograph/AdItem/asAdItem/resolver_reader.ts @@ -18,7 +18,7 @@ const artifact: EagerReaderArtifact< Link | null > = { kind: "EagerReaderArtifact", - resolver: ({ data }) => data.__typename === "AdItem" ? data.link : null, + resolver: ({ firstParameter }) => firstParameter.data.__typename === "AdItem" ? firstParameter.data.link : null, readerAst, }; diff --git a/demos/pet-demo/src/components/__isograph/BlogItem/asBlogItem/resolver_reader.ts b/demos/pet-demo/src/components/__isograph/BlogItem/asBlogItem/resolver_reader.ts index 5bb1d6963..419c1196d 100644 --- a/demos/pet-demo/src/components/__isograph/BlogItem/asBlogItem/resolver_reader.ts +++ b/demos/pet-demo/src/components/__isograph/BlogItem/asBlogItem/resolver_reader.ts @@ -18,7 +18,7 @@ const artifact: EagerReaderArtifact< Link | null > = { kind: "EagerReaderArtifact", - resolver: ({ data }) => data.__typename === "BlogItem" ? data.link : null, + resolver: ({ firstParameter }) => firstParameter.data.__typename === "BlogItem" ? firstParameter.data.link : null, readerAst, }; diff --git a/demos/pet-demo/src/components/__isograph/iso.ts b/demos/pet-demo/src/components/__isograph/iso.ts index 4e32e31d0..7d8bfaf1b 100644 --- a/demos/pet-demo/src/components/__isograph/iso.ts +++ b/demos/pet-demo/src/components/__isograph/iso.ts @@ -44,7 +44,7 @@ import entrypoint_Query__PetFavoritePhrase from '../__isograph/Query/PetFavorite // of type TParam. type IdentityWithParam = ( clientField: (param: TParam) => TClientFieldReturn -) => (param: TParam) => TClientFieldReturn; +) => (data: { firstParameter: TParam }) => TClientFieldReturn; // This is the type given it to client fields with @component. // This means that the type of the exported iso literal is exactly @@ -58,7 +58,7 @@ type IdentityWithParamComponent = < TComponentProps = Record, >( clientComponentField: (data: TParam, componentProps: TComponentProps) => TClientFieldReturn -) => (data: TParam, componentProps: TComponentProps) => TClientFieldReturn; +) => (props: { firstParameter: TParam, additionalRuntimeProps: TComponentProps }) => TClientFieldReturn; type WhitespaceCharacter = ' ' | '\t' | '\n'; type Whitespace = In extends `${WhitespaceCharacter}${infer In}` @@ -237,14 +237,12 @@ export function iso( export function iso( param: T & MatchesWhitespaceAndString<'entrypoint Query.PetFavoritePhrase', T> ): typeof entrypoint_Query__PetFavoritePhrase; - -export function iso(_isographLiteralText: string): +export function iso(clientFieldResolver: any): | IdentityWithParam | IdentityWithParamComponent | IsographEntrypoint { - throw new Error('iso: Unexpected invocation at runtime. Either the Babel transform ' + - 'was not set up, or it failed to identify this call site. Make sure it ' + - 'is being used verbatim as `iso`. If you cannot use the babel transform, ' + - 'set options.no_babel_transform to true in your Isograph config. '); + return (props: any) => { + return clientFieldResolver(props.firstParameter, props.additionalRuntimeProps) + }; } \ No newline at end of file diff --git a/demos/vite-demo/src/components/__isograph/iso.ts b/demos/vite-demo/src/components/__isograph/iso.ts index 4b2850233..49af9d754 100644 --- a/demos/vite-demo/src/components/__isograph/iso.ts +++ b/demos/vite-demo/src/components/__isograph/iso.ts @@ -9,7 +9,7 @@ import entrypoint_Query__HomePage from '../__isograph/Query/HomePage/entrypoint' // of type TParam. type IdentityWithParam = ( clientField: (param: TParam) => TClientFieldReturn -) => (param: TParam) => TClientFieldReturn; +) => (data: { firstParameter: TParam }) => TClientFieldReturn; // This is the type given it to client fields with @component. // This means that the type of the exported iso literal is exactly @@ -23,7 +23,7 @@ type IdentityWithParamComponent = < TComponentProps = Record, >( clientComponentField: (data: TParam, componentProps: TComponentProps) => TClientFieldReturn -) => (data: TParam, componentProps: TComponentProps) => TClientFieldReturn; +) => (props: { firstParameter: TParam, additionalRuntimeProps: TComponentProps }) => TClientFieldReturn; type WhitespaceCharacter = ' ' | '\t' | '\n'; type Whitespace = In extends `${WhitespaceCharacter}${infer In}` @@ -62,14 +62,12 @@ export function iso( export function iso( param: T & MatchesWhitespaceAndString<'entrypoint Query.HomePage', T> ): typeof entrypoint_Query__HomePage; - -export function iso(_isographLiteralText: string): +export function iso(clientFieldResolver: any): | IdentityWithParam | IdentityWithParamComponent | IsographEntrypoint { - throw new Error('iso: Unexpected invocation at runtime. Either the Babel transform ' + - 'was not set up, or it failed to identify this call site. Make sure it ' + - 'is being used verbatim as `iso`. If you cannot use the babel transform, ' + - 'set options.no_babel_transform to true in your Isograph config. '); + return (props: any) => { + return clientFieldResolver(props.firstParameter, props.additionalRuntimeProps) + }; } \ No newline at end of file diff --git a/docs-website/docs/how-isograph-works/babel-plugin.md b/docs-website/docs/how-isograph-works/babel-plugin.md index e178b0078..1f3a07cf5 100644 --- a/docs-website/docs/how-isograph-works/babel-plugin.md +++ b/docs-website/docs/how-isograph-works/babel-plugin.md @@ -16,6 +16,8 @@ It is then used via adding the following to your `.babelrc.js`: } ``` +For HMR to work `@isograph/babel-plugin` must run before `react-refresh/babel`. + ## Behavior The babel plugin will replace calls to `iso` entrypoint with require calls to the appropriate `entrypoint.js` file. @@ -31,7 +33,7 @@ export const foo = iso(`field Query.Foo { whatever }`)(({ data }) => { ```ts // and this is transformed into -export const foo = ({ data }) => doStuff(); +export const foo = iso(({ data }) => doStuff()); ``` ## Requirements diff --git a/libs/isograph-babel-plugin/compileTag.js b/libs/isograph-babel-plugin/compileTag.js index 4f56f1c6a..3f4a4802e 100644 --- a/libs/isograph-babel-plugin/compileTag.js +++ b/libs/isograph-babel-plugin/compileTag.js @@ -17,20 +17,8 @@ function compileTag(t, path, config) { // This throws if the tag is invalid compileImportStatement(t, path, type, field, 'entrypoint', config); } else if (keyword === 'field') { - if (t.isCallExpression(path.parentPath.node)) { - const firstArg = path.parentPath.node.arguments[0]; - if (path.parentPath.node.arguments.length === 1 && firstArg != null) { - path.parentPath.replaceWith(firstArg); - } else { - throw new Error( - 'Invalid iso tag usage. The iso function should be passed at most one argument.', - ); - } - } else { - path.replaceWith( - t.arrowFunctionExpression([t.identifier('x')], t.identifier('x')), - ); - } + // replace `iso("field ...")` with `iso` + path.replaceWith(path.node.callee); } else { throw new Error( "Invalid iso tag usage. Expected 'entrypoint' or 'field'.", diff --git a/libs/isograph-react/src/core/componentCache.ts b/libs/isograph-react/src/core/componentCache.tsx similarity index 86% rename from libs/isograph-react/src/core/componentCache.ts rename to libs/isograph-react/src/core/componentCache.tsx index 19d3b7975..5185b46bd 100644 --- a/libs/isograph-react/src/core/componentCache.ts +++ b/libs/isograph-react/src/core/componentCache.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { useReadAndSubscribe } from '../react/useReadAndSubscribe'; import { stableCopy } from './cache'; import { FragmentReference } from './FragmentReference'; @@ -47,12 +48,14 @@ export function getOrCreateCachedComponent( rootLink: fragmentReference.root, }); - return readerWithRefetchQueries.readerArtifact.resolver( - { - data, - parameters: fragmentReference.variables, - }, - additionalRuntimeProps, + return ( + ); } Component.displayName = `${componentName} (id: ${fragmentReference.root}) @component`; diff --git a/libs/isograph-react/src/core/makeNetworkRequest.ts b/libs/isograph-react/src/core/makeNetworkRequest.ts index d2dd21866..dc0ce6558 100644 --- a/libs/isograph-react/src/core/makeNetworkRequest.ts +++ b/libs/isograph-react/src/core/makeNetworkRequest.ts @@ -267,8 +267,10 @@ function readDataForOnComplete< } case 'EagerReaderArtifact': { return readerArtifact.resolver({ - data: fragmentResult, - parameters: variables, + firstParameter: { + data: fragmentResult, + parameters: variables, + }, }); } default: { diff --git a/libs/isograph-react/src/core/read.ts b/libs/isograph-react/src/core/read.ts index 1e3fb7e58..ebe49c45c 100644 --- a/libs/isograph-react/src/core/read.ts +++ b/libs/isograph-react/src/core/read.ts @@ -262,8 +262,10 @@ function readData( }; } const condition = field.condition.resolver({ - data: data.data, - parameters: {}, + firstParameter: { + data: data.data, + parameters: {}, + }, }); if (condition === true) { link = root; @@ -428,8 +430,9 @@ function readData( data: data.data, parameters: variables, }; - target[field.alias] = - field.readerArtifact.resolver(firstParameter); + target[field.alias] = field.readerArtifact.resolver({ + firstParameter, + }); } break; } diff --git a/libs/isograph-react/src/core/reader.ts b/libs/isograph-react/src/core/reader.ts index 1999fe894..45222999b 100644 --- a/libs/isograph-react/src/core/reader.ts +++ b/libs/isograph-react/src/core/reader.ts @@ -32,9 +32,9 @@ export type EagerReaderArtifact< > = { readonly kind: 'EagerReaderArtifact'; readonly readerAst: ReaderAst; - readonly resolver: ( - data: ResolverFirstParameter, - ) => TClientFieldValue; + readonly resolver: (data: { + firstParameter: ResolverFirstParameter; + }) => TClientFieldValue; }; export type ComponentReaderArtifact< @@ -44,10 +44,10 @@ export type ComponentReaderArtifact< readonly kind: 'ComponentReaderArtifact'; readonly componentName: ComponentOrFieldName; readonly readerAst: ReaderAst; - readonly resolver: ( - data: ResolverFirstParameter, - runtimeProps: TComponentProps, - ) => React.ReactNode; + readonly resolver: (props: { + firstParameter: ResolverFirstParameter; + additionalRuntimeProps: TComponentProps; + }) => React.ReactNode; }; export type ResolverFirstParameter< diff --git a/libs/isograph-react/src/core/util.ts b/libs/isograph-react/src/core/util.ts index dc20deb4f..7df00010b 100644 --- a/libs/isograph-react/src/core/util.ts +++ b/libs/isograph-react/src/core/util.ts @@ -1,5 +1,14 @@ -export type ExtractSecondParam any> = - T extends (arg1: any, arg2: infer P) => any ? P : never; +export type ExtractSecondParam< + T extends (props: { + firstParameter: any; + additionalRuntimeProps: any; + }) => any, +> = T extends (props: { + firstParameter: any; + additionalRuntimeProps: infer P; +}) => any + ? P + : never; export type CombineWithIntrinsicAttributes = T extends Record ? JSX.IntrinsicAttributes diff --git a/libs/isograph-react/src/loadable-hooks/useConnectionSpecPagination.ts b/libs/isograph-react/src/loadable-hooks/useConnectionSpecPagination.ts index 2fea82330..9ebd6fca6 100644 --- a/libs/isograph-react/src/loadable-hooks/useConnectionSpecPagination.ts +++ b/libs/isograph-react/src/loadable-hooks/useConnectionSpecPagination.ts @@ -138,7 +138,9 @@ export function useConnectionSpecPagination< ); } - return readerWithRefetchQueries.readerArtifact.resolver(firstParameter); + return readerWithRefetchQueries.readerArtifact.resolver({ + firstParameter, + }); }); const items = flatten(results.map((result) => result.edges ?? [])); diff --git a/libs/isograph-react/src/loadable-hooks/useSkipLimitPagination.ts b/libs/isograph-react/src/loadable-hooks/useSkipLimitPagination.ts index c61000f00..e40d7ac91 100644 --- a/libs/isograph-react/src/loadable-hooks/useSkipLimitPagination.ts +++ b/libs/isograph-react/src/loadable-hooks/useSkipLimitPagination.ts @@ -132,7 +132,9 @@ export function useSkipLimitPagination< ); } - return readerWithRefetchQueries.readerArtifact.resolver(firstParameter); + return readerWithRefetchQueries.readerArtifact.resolver({ + firstParameter, + }); }); const items = flatten(results); diff --git a/libs/isograph-react/src/react/useResult.ts b/libs/isograph-react/src/react/useResult.ts index 496a0d097..2cedbbdda 100644 --- a/libs/isograph-react/src/react/useResult.ts +++ b/libs/isograph-react/src/react/useResult.ts @@ -52,7 +52,9 @@ export function useResult< data: data, parameters: fragmentReference.variables, }; - return readerWithRefetchQueries.readerArtifact.resolver(param); + return readerWithRefetchQueries.readerArtifact.resolver({ + firstParameter: param, + }); } } } diff --git a/libs/isograph-react/src/tests/__isograph/iso.ts b/libs/isograph-react/src/tests/__isograph/iso.ts index 2945e3361..b7ea8c0d3 100644 --- a/libs/isograph-react/src/tests/__isograph/iso.ts +++ b/libs/isograph-react/src/tests/__isograph/iso.ts @@ -14,7 +14,7 @@ import entrypoint_Query__subquery from '../__isograph/Query/subquery/entrypoint' // of type TParam. type IdentityWithParam = ( clientField: (param: TParam) => TClientFieldReturn -) => (param: TParam) => TClientFieldReturn; +) => (data: { firstParameter: TParam }) => TClientFieldReturn; // This is the type given it to client fields with @component. // This means that the type of the exported iso literal is exactly @@ -28,7 +28,7 @@ type IdentityWithParamComponent = < TComponentProps = Record, >( clientComponentField: (data: TParam, componentProps: TComponentProps) => TClientFieldReturn -) => (data: TParam, componentProps: TComponentProps) => TClientFieldReturn; +) => (props: { firstParameter: TParam, additionalRuntimeProps: TComponentProps }) => TClientFieldReturn; type WhitespaceCharacter = ' ' | '\t' | '\n'; type Whitespace = In extends `${WhitespaceCharacter}${infer In}` @@ -87,14 +87,12 @@ export function iso( export function iso( param: T & MatchesWhitespaceAndString<'entrypoint Query.subquery', T> ): typeof entrypoint_Query__subquery; - -export function iso(_isographLiteralText: string): +export function iso(clientFieldResolver: any): | IdentityWithParam | IdentityWithParamComponent | IsographEntrypoint { - throw new Error('iso: Unexpected invocation at runtime. Either the Babel transform ' + - 'was not set up, or it failed to identify this call site. Make sure it ' + - 'is being used verbatim as `iso`. If you cannot use the babel transform, ' + - 'set options.no_babel_transform to true in your Isograph config. '); + return (props: any) => { + return clientFieldResolver(props.firstParameter, props.additionalRuntimeProps) + }; } \ No newline at end of file