Skip to content
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

Support new React Apollo API #2182

Merged
merged 3 commits into from
Aug 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
100 changes: 51 additions & 49 deletions dev-test/githunt/types.reactApollo.customSuffix.tsx

Large diffs are not rendered by default.

130 changes: 66 additions & 64 deletions dev-test/githunt/types.reactApollo.hooks.tsx

Large diffs are not rendered by default.

100 changes: 51 additions & 49 deletions dev-test/githunt/types.reactApollo.preResolveTypes.tsx

Large diffs are not rendered by default.

100 changes: 51 additions & 49 deletions dev-test/githunt/types.reactApollo.tsx

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
"@types/mkdirp": "0.5.2",
"@types/node": "10.14.14",
"@types/request": "2.48.2",
"@apollo/react-common": "3.0.0",
"@apollo/react-components": "3.0.0",
"@apollo/react-hoc": "3.0.0",
"@apollo/react-hooks": "3.0.0",
"apollo-link": "1.2.12",
"apollo-server": "2.8.1",
"graphql": "14.4.2",
Expand All @@ -51,8 +55,6 @@
"jest-junit": "7.0.0",
"lerna": "3.16.4",
"lint-staged": "9.2.1",
"react-apollo": "2.5.8",
"react-apollo-hooks": "0.5.0",
"urql": "1.3.0",
"rimraf": "2.6.3",
"ts-jest": "24.0.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/graphql-codegen-cli/src/utils/debugging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function debugLog(message: string, ...meta: any[]) {
if (!process.env.GQL_CODEGEN_NODEBUG && process.env.DEBUG !== undefined) {
queue.push({
message,
meta
meta,
});
}
}
Expand Down
1 change: 0 additions & 1 deletion packages/plugins/typescript/document-nodes/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,3 @@ export const validate: PluginValidateFn<any> = async (schema: GraphQLSchema, doc
throw new Error(`Plugin "typescript-document-nodes" requires extension to be ".ts"!`);
}
};

2 changes: 1 addition & 1 deletion packages/plugins/typescript/react-apollo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"license": "MIT",
"scripts": {
"build": "tsc -m esnext --outDir dist/esnext && tsc -m commonjs --outDir dist/commonjs",
"test": "jest --config ../../../../jest.config.js"
"test": "jest --config ../../../../jest.config.js --forceExit"
},
"peerDependencies": {
"@types/graphql": "*",
Expand Down
48 changes: 27 additions & 21 deletions packages/plugins/typescript/react-apollo/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { extname } from 'path';

export interface ReactApolloRawPluginConfig extends RawClientSideBasePluginConfig {
/**
* @name withHOC
* @name withComponent
* @type boolean
* @description Customized the output by enabling/disabling the HOC.
* @description Customized the output by enabling/disabling the generated Component.
* @default true
*
* @example
Expand All @@ -20,14 +20,14 @@ export interface ReactApolloRawPluginConfig extends RawClientSideBasePluginConfi
* - typescript-operations
* - typescript-react-apollo
* config:
* withHOC: false
* withComponent: false
* ```
*/
withHOC?: boolean;
withComponent?: boolean;
/**
* @name withComponent
* @name withHOC
* @type boolean
* @description Customized the output by enabling/disabling the generated Component.
* @description Customized the output by enabling/disabling the HOC.
* @default true
*
* @example
Expand All @@ -39,10 +39,10 @@ export interface ReactApolloRawPluginConfig extends RawClientSideBasePluginConfi
* - typescript-operations
* - typescript-react-apollo
* config:
* withComponent: false
* withHOC: false
* ```
*/
withComponent?: boolean;
withHOC?: boolean;
/**
* @name withHooks
* @type boolean
Expand Down Expand Up @@ -84,23 +84,29 @@ export interface ReactApolloRawPluginConfig extends RawClientSideBasePluginConfi
withMutationFn?: boolean;

/**
* @name hooksImportFrom
* @name apolloReactCommonImportFrom
* @type string
* @default @apollo/react-common
*/
apolloReactCommonImportFrom?: string;
/**
* @name apolloReactComponentsImportFrom
* @type string
* @default @apollo/react-components
*/
apolloReactComponentsImportFrom?: string;
/**
* @name apolloReactHocImportFrom
* @type string
* @description You can specify alternative module that is exports `useQuery` `useMutation` and `useSubscription`.
* This is useful for further abstraction of some common tasks (eg. error handling).
* Filepath relative to generated file can be also specified.
* @default react-apollo-hooks
* @default @apollo/react-hoc
*/
hooksImportFrom?: string;
apolloReactHocImportFrom?: string;
/**
* @name reactApolloImportFrom
* @name apolloReactHooksImportFrom
* @type string
* @description You can specify module that exports components `Query`, `Mutation`, `Subscription` and HOCs
* This is useful for further abstraction of some common tasks (eg. error handling).
* Filepath relative to generated file can be also specified.
* @default react-apollo
* @default @apollo/react-hooks
*/
reactApolloImportFrom?: string;
apolloReactHooksImportFrom?: string;
/**
* @name componentSuffix
* @type string
Expand Down Expand Up @@ -179,7 +185,7 @@ export const plugin: PluginFunction<ReactApolloRawPluginConfig> = (schema: Graph
...(config.externalFragments || []),
];

const visitor = new ReactApolloVisitor(allFragments, config, documents) as any;
const visitor = new ReactApolloVisitor(allFragments, config, documents);
const visitorResult = visit(allAst, { leave: visitor });

return {
Expand Down
129 changes: 75 additions & 54 deletions packages/plugins/typescript/react-apollo/src/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,38 @@ import { toPascalCase, Types } from '@graphql-codegen/plugin-helpers';
import { titleCase } from 'change-case';

export interface ReactApolloPluginConfig extends ClientSideBasePluginConfig {
withHOC: boolean;
withComponent: boolean;
withHOC: boolean;
withHooks: boolean;
withMutationFn: boolean;
hooksImportFrom: string;
reactApolloImportFrom: string;
apolloReactCommonImportFrom: string;
apolloReactComponentsImportFrom: string;
apolloReactHocImportFrom: string;
apolloReactHooksImportFrom: string;
componentSuffix: string;
reactApolloVersion: 2 | 3;
withResultType: boolean;
withMutationOptionsType: boolean;
}

export class ReactApolloVisitor extends ClientSideBaseVisitor<ReactApolloRawPluginConfig, ReactApolloPluginConfig> {
constructor(fragments: LoadedFragment[], rawConfig: ReactApolloRawPluginConfig, documents?: Types.DocumentFile[]) {
super(
fragments,
rawConfig,
{
componentSuffix: getConfigValue(rawConfig.componentSuffix, 'Component'),
withHOC: getConfigValue(rawConfig.withHOC, true),
withComponent: getConfigValue(rawConfig.withComponent, true),
withHooks: getConfigValue(rawConfig.withHooks, false),
withMutationFn: getConfigValue(rawConfig.withMutationFn, true),
hooksImportFrom: getConfigValue(rawConfig.hooksImportFrom, 'react-apollo-hooks'),
reactApolloImportFrom: getConfigValue(rawConfig.reactApolloImportFrom, 'react-apollo'),
reactApolloVersion: getConfigValue(rawConfig.reactApolloVersion, 2),
withResultType: getConfigValue(rawConfig.withResultType, true),
withMutationOptionsType: getConfigValue(rawConfig.withMutationOptionsType, true),
} as any,
documents
);
constructor(fragments: LoadedFragment[], rawConfig: ReactApolloRawPluginConfig, documents: Types.DocumentFile[]) {
super(fragments, rawConfig, {
componentSuffix: getConfigValue(rawConfig.componentSuffix, 'Component'),
withHOC: getConfigValue(rawConfig.withHOC, true),
withComponent: getConfigValue(rawConfig.withComponent, true),
withHooks: getConfigValue(rawConfig.withHooks, false),
withMutationFn: getConfigValue(rawConfig.withMutationFn, true),
apolloReactCommonImportFrom: getConfigValue(rawConfig.apolloReactCommonImportFrom, '@apollo/react-common'),
apolloReactComponentsImportFrom: getConfigValue(rawConfig.apolloReactComponentsImportFrom, '@apollo/react-components'),
apolloReactHocImportFrom: getConfigValue(rawConfig.apolloReactHocImportFrom, '@apollo/react-hoc'),
apolloReactHooksImportFrom: getConfigValue(rawConfig.apolloReactHooksImportFrom, '@apollo/react-hooks'),
reactApolloVersion: getConfigValue(rawConfig.reactApolloVersion, 2),
withResultType: getConfigValue(rawConfig.withResultType, true),
withMutationOptionsType: getConfigValue(rawConfig.withMutationOptionsType, true),
});

this._documents = documents;

autoBind(this);
}
Expand All @@ -47,12 +48,20 @@ export class ReactApolloVisitor extends ClientSideBaseVisitor<ReactApolloRawPlug
return `import * as React from 'react';`;
}

private getReactApolloImport(): string {
return `import * as ReactApollo from '${typeof this.config.reactApolloImportFrom === 'string' ? this.config.reactApolloImportFrom : 'react-apollo'}';`;
private getApolloReactCommonImport(): string {
return `import * as ApolloReactCommon from '${this.config.apolloReactCommonImportFrom}';`;
}

private getApolloReactComponentsImport(): string {
return `import * as ApolloReactComponents from '${this.config.apolloReactComponentsImportFrom}';`;
}

private getReactApolloHooksImport(): string {
return `import * as ReactApolloHooks from '${typeof this.config.hooksImportFrom === 'string' ? this.config.hooksImportFrom : 'react-apollo-hooks'}';`;
private getApolloReactHocImport(): string {
return `import * as ApolloReactHoc from '${this.config.apolloReactHocImportFrom}';`;
}

private getApolloReactHooksImport(): string {
return `import * as ApolloReactHooks from '${this.config.apolloReactHooksImportFrom}';`;
}

private getOmitDeclaration(): string {
Expand All @@ -75,31 +84,34 @@ export class ReactApolloVisitor extends ClientSideBaseVisitor<ReactApolloRawPlug
const variablesVarName = this.convertName(operationName + toPascalCase(operationType) + 'Variables');
const argType = operationType === 'mutation' ? 'MutateProps' : 'DataProps';

this.imports.add(this.getReactApolloImport());
this.imports.add(this.getApolloReactCommonImport());
this.imports.add(this.getApolloReactHocImport());

return `ReactApollo.${argType}<${typeVariableName}, ${variablesVarName}>`;
return `ApolloReactHoc.${argType}<${typeVariableName}, ${variablesVarName}>`;
}

private _buildMutationFn(node: OperationDefinitionNode, operationResultType: string, operationVariablesTypes: string): string {
if (node.operation === 'mutation') {
this.imports.add(this.getReactApolloImport());
return `export type ${this.convertName(node.name.value + 'MutationFn')} = ReactApollo.${MutationFnNameForVersionMap[this.config.reactApolloVersion]}<${operationResultType}, ${operationVariablesTypes}>;`;
this.imports.add(this.getApolloReactCommonImport());
return `export type ${this.convertName(node.name.value + 'MutationFn')} = ApolloReactCommon.MutationFunction<${operationResultType}, ${operationVariablesTypes}>;`;
}
return null;
}

private _buildOperationHoc(node: OperationDefinitionNode, documentVariableName: string, operationResultType: string, operationVariablesTypes: string): string {
this.imports.add(this.getApolloReactCommonImport());
this.imports.add(this.getApolloReactHocImport());
const operationName: string = this.convertName(node.name.value, { useTypesPrefix: false });
const propsTypeName: string = this.convertName(node.name.value, { suffix: 'Props' });

const propsVar = `export type ${propsTypeName}<TChildProps = {}> = ${this._buildHocProps(node.name.value, node.operation)} & TChildProps;`;

const hocString = `export function with${operationName}<TProps, TChildProps = {}>(operationOptions?: ReactApollo.OperationOption<
const hocString = `export function with${operationName}<TProps, TChildProps = {}>(operationOptions?: ApolloReactHoc.OperationOption<
TProps,
${operationResultType},
${operationVariablesTypes},
${propsTypeName}<TChildProps>>) {
return ReactApollo.with${titleCase(node.operation)}<TProps, ${operationResultType}, ${operationVariablesTypes}, ${propsTypeName}<TChildProps>>(${documentVariableName}, {
return ApolloReactHoc.with${titleCase(node.operation)}<TProps, ${operationResultType}, ${operationVariablesTypes}, ${propsTypeName}<TChildProps>>(${documentVariableName}, {
alias: 'with${operationName}',
...operationOptions
});
Expand All @@ -109,16 +121,23 @@ export class ReactApolloVisitor extends ClientSideBaseVisitor<ReactApolloRawPlug
}

private _buildComponent(node: OperationDefinitionNode, documentVariableName: string, operationType: string, operationResultType: string, operationVariablesTypes: string): string {
const componentPropsName: string = this.convertName(node.name.value, { suffix: this.config.componentSuffix + 'Props', useTypesPrefix: false });
const componentName: string = this.convertName(node.name.value, { suffix: this.config.componentSuffix, useTypesPrefix: false });
const componentPropsName: string = this.convertName(node.name.value, {
suffix: this.config.componentSuffix + 'Props',
useTypesPrefix: false,
});
const componentName: string = this.convertName(node.name.value, {
suffix: this.config.componentSuffix,
useTypesPrefix: false,
});

const isVariablesRequired = operationType === 'Query' && node.variableDefinitions.some(variableDef => variableDef.type.kind === Kind.NON_NULL_TYPE);

this.imports.add(this.getReactImport());
this.imports.add(this.getReactApolloImport());
this.imports.add(this.getApolloReactCommonImport());
this.imports.add(this.getApolloReactComponentsImport());
this.imports.add(this.getOmitDeclaration());

const propsType = `Omit<ReactApollo.${operationType}Props<${operationResultType}, ${operationVariablesTypes}>, '${operationType.toLowerCase()}'>`;
const propsType = `Omit<ApolloReactComponents.${operationType}ComponentOptions<${operationResultType}, ${operationVariablesTypes}>, '${operationType.toLowerCase()}'>`;
let componentProps = '';
if (isVariablesRequired) {
componentProps = `export type ${componentPropsName} = ${propsType} & ({ variables: ${operationVariablesTypes}; skip?: boolean; } | { skip: boolean; });`;
Expand All @@ -128,20 +147,24 @@ export class ReactApolloVisitor extends ClientSideBaseVisitor<ReactApolloRawPlug

const component = `
export const ${componentName} = (props: ${componentPropsName}) => (
<ReactApollo.${operationType}<${operationResultType}, ${operationVariablesTypes}> ${node.operation}={${this.config.documentMode === DocumentMode.external ? `Operations.${node.name.value}` : documentVariableName}} {...props} />
<ApolloReactComponents.${operationType}<${operationResultType}, ${operationVariablesTypes}> ${node.operation}={${this.config.documentMode === DocumentMode.external ? `Operations.${node.name.value}` : documentVariableName}} {...props} />
);
`;
return [componentProps, component].join('\n');
}

private _buildHooks(node: OperationDefinitionNode, operationType: string, documentVariableName: string, operationResultType: string, operationVariablesTypes: string): string {
const operationName: string = this.convertName(node.name.value, { suffix: titleCase(operationType), useTypesPrefix: false });
const operationName: string = this.convertName(node.name.value, {
suffix: titleCase(operationType),
useTypesPrefix: false,
});

this.imports.add(this.getReactApolloHooksImport());
this.imports.add(this.getApolloReactCommonImport());
this.imports.add(this.getApolloReactHooksImport());

const hookFn = `
export function use${operationName}(baseOptions?: ReactApolloHooks.${operationType}HookOptions<${this.config.hooksImportFrom === '@apollo/react-hooks' || node.operation !== 'query' ? `${operationResultType}, ` : ''}${operationVariablesTypes}>) {
return ReactApolloHooks.use${operationType}<${operationResultType}, ${operationVariablesTypes}>(${documentVariableName}, baseOptions);
export function use${operationName}(baseOptions?: ApolloReactHooks.${operationType}HookOptions<${operationResultType}, ${operationVariablesTypes}>) {
return ApolloReactHooks.use${operationType}<${operationResultType}, ${operationVariablesTypes}>(${documentVariableName}, baseOptions);
};`;

const hookResult = `export type ${operationName}HookResult = ReturnType<typeof use${operationName}>;`;
Expand All @@ -150,18 +173,21 @@ export class ReactApolloVisitor extends ClientSideBaseVisitor<ReactApolloRawPlug
}

private _buildResultType(node: OperationDefinitionNode, operationType: string, operationResultType: string, operationVariablesTypes: string): string {
const componentResultType = this.convertName(node.name.value, { suffix: `${operationType}Result`, useTypesPrefix: false });
const componentResultType = this.convertName(node.name.value, {
suffix: `${operationType}Result`,
useTypesPrefix: false,
});

switch (node.operation) {
case 'query':
this.imports.add(this.getReactApolloImport());
return `export type ${componentResultType} = ReactApollo.QueryResult<${operationResultType}, ${operationVariablesTypes}>;`;
this.imports.add(this.getApolloReactCommonImport());
return `export type ${componentResultType} = ApolloReactCommon.QueryResult<${operationResultType}, ${operationVariablesTypes}>;`;
case 'mutation':
this.imports.add(this.getReactApolloImport());
return `export type ${componentResultType} = ReactApollo.MutationResult<${operationResultType}>;`;
this.imports.add(this.getApolloReactCommonImport());
return `export type ${componentResultType} = ApolloReactCommon.MutationResult<${operationResultType}>;`;
case 'subscription':
this.imports.add(this.getReactApolloImport());
return `export type ${componentResultType} = ReactApollo.SubscriptionResult<${operationResultType}>;`;
this.imports.add(this.getApolloReactCommonImport());
return `export type ${componentResultType} = ApolloReactCommon.SubscriptionResult<${operationResultType}>;`;
default:
return '';
}
Expand All @@ -172,11 +198,11 @@ export class ReactApolloVisitor extends ClientSideBaseVisitor<ReactApolloRawPlug
return '';
}

this.imports.add(this.getReactApolloImport());
this.imports.add(this.getApolloReactCommonImport());

const mutationOptionsType = this.convertName(node.name.value, { suffix: 'MutationOptions', useTypesPrefix: false });

return `export type ${mutationOptionsType} = ReactApollo.MutationOptions<${operationResultType}, ${operationVariablesTypes}>;`;
return `export type ${mutationOptionsType} = ApolloReactCommon.BaseMutationOptions<${operationResultType}, ${operationVariablesTypes}>;`;
}

protected buildOperation(node: OperationDefinitionNode, documentVariableName: string, operationType: string, operationResultType: string, operationVariablesTypes: string): string {
Expand All @@ -190,8 +216,3 @@ export class ReactApolloVisitor extends ClientSideBaseVisitor<ReactApolloRawPlug
return [mutationFn, component, hoc, hooks, resultType, mutationOptionsType].filter(a => a).join('\n');
}
}

const MutationFnNameForVersionMap = {
2: 'MutationFn',
3: 'MutationFunction',
};