From 6bad5f5232d5436886162c030af6e31846749d7a Mon Sep 17 00:00:00 2001 From: Dotan Simha Date: Tue, 26 Feb 2019 10:02:54 +0200 Subject: [PATCH] done with basic typescript package fixed flow package --- .../flow/src/flow-variables-to-object.ts | 42 ++++++++++ packages/plugins/flow/src/visitor.ts | 6 +- .../src/typescript-variables-to-object.ts | 46 +++++++++++ packages/plugins/typescript/src/visitor.ts | 20 +++-- .../typescript/tests/typescript.spec.ts | 81 ++++++++++++------- .../visitor-plugin-common/src/base-visitor.ts | 31 +++---- .../visitor-plugin-common/src/utils.ts | 13 --- .../src/variables-to-object.ts | 77 +++++++++++------- 8 files changed, 212 insertions(+), 104 deletions(-) create mode 100644 packages/plugins/flow/src/flow-variables-to-object.ts create mode 100644 packages/plugins/typescript/src/typescript-variables-to-object.ts diff --git a/packages/plugins/flow/src/flow-variables-to-object.ts b/packages/plugins/flow/src/flow-variables-to-object.ts new file mode 100644 index 000000000000..0e92017a6e7b --- /dev/null +++ b/packages/plugins/flow/src/flow-variables-to-object.ts @@ -0,0 +1,42 @@ +import { OperationVariablesToObject, ScalarsMap, ConvertNameFn } from 'graphql-codegen-visitor-plugin-common'; +import { TypeNode, Kind } from 'graphql'; + +export class FlowOperationVariablesToObject extends OperationVariablesToObject { + private clearOptional(str: string): string { + if (str.startsWith('?')) { + return str.replace(/^\?(.*?)$/i, '$1'); + } + + return str; + } + + protected wrapAstTypeWithModifiers(baseType: string, typeNode: TypeNode): string { + if (typeNode.kind === Kind.NON_NULL_TYPE) { + const type = this.wrapAstTypeWithModifiers(baseType, typeNode.type); + + return this.clearOptional(type); + } else if (typeNode.kind === Kind.LIST_TYPE) { + const innerType = this.wrapAstTypeWithModifiers(baseType, typeNode.type); + + return `?Array<${innerType}>`; + } else { + return `?${baseType}`; + } + } + + protected formatFieldString(fieldName: string, isNonNullType: boolean, hasDefaultValue: boolean): string { + if (hasDefaultValue || isNonNullType) { + return fieldName; + } else { + return `${fieldName}?`; + } + } + + protected formatTypeString(fieldType: string, isNonNullType: boolean, hasDefaultValue: boolean): string { + if (hasDefaultValue && !isNonNullType) { + return this.clearOptional(fieldType); + } + + return fieldType; + } +} diff --git a/packages/plugins/flow/src/visitor.ts b/packages/plugins/flow/src/visitor.ts index 1ec673426bc8..4b12b3b82128 100644 --- a/packages/plugins/flow/src/visitor.ts +++ b/packages/plugins/flow/src/visitor.ts @@ -9,7 +9,6 @@ import { NamedTypeNode } from 'graphql'; import { - wrapAstTypeWithModifiers, BaseVisitor, DeclarationBlock, wrapWithSingleQuotes, @@ -18,6 +17,7 @@ import { } from 'graphql-codegen-visitor-plugin-common'; import * as autoBind from 'auto-bind'; import { FlowPluginConfig } from './index'; +import { FlowOperationVariablesToObject } from './flow-variables-to-object'; export interface FlowPluginParsedConfig extends ParsedConfig { useFlowExactObjects: boolean; @@ -32,9 +32,9 @@ export class FlowVisitor extends BaseVisitor$/i, '$1'); + } + + return str; + } + + protected wrapAstTypeWithModifiers(baseType: string, typeNode: TypeNode): string { + if (typeNode.kind === Kind.NON_NULL_TYPE) { + const type = this.wrapAstTypeWithModifiers(baseType, typeNode.type); + + return this.clearOptional(type); + } else if (typeNode.kind === Kind.LIST_TYPE) { + const innerType = this.wrapAstTypeWithModifiers(baseType, typeNode.type); + + return `Maybe>`; + } else { + return `Maybe<${baseType}>`; + } + } + + protected formatFieldString(fieldName: string, isNonNullType: boolean, hasDefaultValue: boolean): string { + if (hasDefaultValue || isNonNullType || this._avoidOptionals) { + return fieldName; + } else { + return `${fieldName}?`; + } + } + + protected formatTypeString(fieldType: string, isNonNullType: boolean, hasDefaultValue: boolean): string { + if (hasDefaultValue) { + return this.clearOptional(fieldType); + } + + return fieldType; + } +} diff --git a/packages/plugins/typescript/src/visitor.ts b/packages/plugins/typescript/src/visitor.ts index 1bcd467256fc..6159c5f7d556 100644 --- a/packages/plugins/typescript/src/visitor.ts +++ b/packages/plugins/typescript/src/visitor.ts @@ -2,7 +2,7 @@ import { indent, BaseVisitor, ParsedConfig } from 'graphql-codegen-visitor-plugi import { TypeScriptPluginConfig } from './index'; import * as autoBind from 'auto-bind'; import { FieldDefinitionNode, NamedTypeNode, ListTypeNode, NonNullTypeNode } from 'graphql'; -import { wrapAstTypeWithModifiers } from '../../visitor-plugin-common/src/utils'; +import { TypeScriptOperationVariablesToObject } from './typescript-variables-to-object'; export interface TypeScriptPluginParsedConfig extends ParsedConfig { avoidOptionals: boolean; @@ -11,15 +11,21 @@ export interface TypeScriptPluginParsedConfig extends ParsedConfig { export class TsVisitor extends BaseVisitor { constructor(pluginConfig: TypeScriptPluginConfig = {}) { - super(pluginConfig, { - avoidOptionals: pluginConfig.avoidOptionals || false, - maybeValue: pluginConfig.maybeValue || 'T | null' - } as TypeScriptPluginParsedConfig); + super( + pluginConfig, + { + avoidOptionals: pluginConfig.avoidOptionals || false, + maybeValue: pluginConfig.maybeValue || 'T | null' + } as TypeScriptPluginParsedConfig, + null + ); autoBind(this); + this.setArgumentsTransformer( + new TypeScriptOperationVariablesToObject(this.scalars, this.convertName, this.config.avoidOptionals) + ); this.setDeclarationBlockConfig({ - enumNameValueSeparator: ' =', - wrapAstTypeWithModifiers: wrapAstTypeWithModifiers('') + enumNameValueSeparator: ' =' }); } diff --git a/packages/plugins/typescript/tests/typescript.spec.ts b/packages/plugins/typescript/tests/typescript.spec.ts index aafa49d0ae4d..059a4010c05a 100644 --- a/packages/plugins/typescript/tests/typescript.spec.ts +++ b/packages/plugins/typescript/tests/typescript.spec.ts @@ -284,41 +284,49 @@ describe('TypeScript', () => { const result = await plugin(schema, [], { namingConvention: 'change-case#lowerCase' }, { outputFile: '' }); expect(result).toBeSimilarStringTo(` - export enum myenumvalues { + export enum myenum { a = 'A', b = 'B', c = 'C' } - + `); + expect(result).toBeSimilarStringTo(` export type mytype = { f?: Maybe, bar?: Maybe, b_a_r?: Maybe, myOtherField?: Maybe, }; - + `); + expect(result).toBeSimilarStringTo(` export type my_type = { linkTest?: Maybe, }; - + `); + expect(result).toBeSimilarStringTo(` export type myunion = my_type | mytype; - + `); + expect(result).toBeSimilarStringTo(` export type some_interface = { id: string, }; - + `); + expect(result).toBeSimilarStringTo(` export type impl1 = some_interface & { id: string, }; - + `); + expect(result).toBeSimilarStringTo(` export type impl_2 = some_interface & { id: string, }; - + `); + expect(result).toBeSimilarStringTo(` export type impl_3 = some_interface & { id: string, }; - + `); + expect(result).toBeSimilarStringTo(` export type query = { something?: Maybe, use_interface?: Maybe, @@ -337,36 +345,44 @@ describe('TypeScript', () => { B = 'B', C = 'C' } - + `); + expect(result).toBeSimilarStringTo(` export type MyType = { f?: Maybe, bar?: Maybe, b_a_r?: Maybe, myOtherField?: Maybe, }; - + `); + expect(result).toBeSimilarStringTo(` export type My_Type = { linkTest?: Maybe, }; - + `); + expect(result).toBeSimilarStringTo(` export type MyUnion = My_Type | MyType; - + `); + expect(result).toBeSimilarStringTo(` export type Some_Interface = { id: string, }; - + `); + expect(result).toBeSimilarStringTo(` export type Impl1 = Some_Interface & { id: string, }; - + `); + expect(result).toBeSimilarStringTo(` export type Impl_2 = Some_Interface & { id: string, }; - + `); + expect(result).toBeSimilarStringTo(` export type Impl_3 = Some_Interface & { id: string, }; - + `); + expect(result).toBeSimilarStringTo(` export type Query = { something?: Maybe, use_interface?: Maybe, @@ -384,37 +400,42 @@ describe('TypeScript', () => { IA = 'A', IB = 'B', IC = 'C' - } - + };`); + + expect(result).toBeSimilarStringTo(` export type IMyType = { f?: Maybe, bar?: Maybe, b_a_r?: Maybe, myOtherField?: Maybe, - }; - + };`); + expect(result).toBeSimilarStringTo(` export type IMy_Type = { linkTest?: Maybe, }; - - export type IMyUnion = IMy_Type | IMyType; - + `); + expect(result).toBeSimilarStringTo(`export type IMyUnion = IMy_Type | IMyType;`); + expect(result).toBeSimilarStringTo(` export type ISome_Interface = { id: string, }; - + `); + expect(result).toBeSimilarStringTo(` export type IImpl1 = ISome_Interface & { id: string, }; - + `); + expect(result).toBeSimilarStringTo(` export type IImpl_2 = ISome_Interface & { id: string, }; - + `); + expect(result).toBeSimilarStringTo(` export type IImpl_3 = ISome_Interface & { id: string, }; - + `); + expect(result).toBeSimilarStringTo(` export type IQuery = { something?: Maybe, use_interface?: Maybe, @@ -497,8 +518,8 @@ describe('TypeScript', () => { }; export type TMutationFooArgs = { - id?: string, - input?: TInput + id?: Maybe, + input?: Maybe }; `); diff --git a/packages/plugins/visitor-plugin-common/src/base-visitor.ts b/packages/plugins/visitor-plugin-common/src/base-visitor.ts index b00bd4c7cde3..cd35ba771832 100644 --- a/packages/plugins/visitor-plugin-common/src/base-visitor.ts +++ b/packages/plugins/visitor-plugin-common/src/base-visitor.ts @@ -1,12 +1,5 @@ import { ScalarsMap, EnumValuesMap } from './types'; -import { - toPascalCase, - DeclarationBlock, - indent, - wrapAstTypeWithModifiers, - wrapWithSingleQuotes, - DeclarationBlockConfig -} from './utils'; +import { toPascalCase, DeclarationBlock, indent, wrapWithSingleQuotes, DeclarationBlockConfig } from './utils'; import { resolveExternalModuleAndFn } from 'graphql-codegen-plugin-helpers'; import * as autoBind from 'auto-bind'; import { @@ -44,13 +37,12 @@ export interface RawConfig { export class BaseVisitor { protected _parsedConfig: TPluginConfig; - protected _declarationBlockConfig: DeclarationBlockConfig = { - wrapAstTypeWithModifiers: wrapAstTypeWithModifiers('') - }; + protected _argumentsTransformer: OperationVariablesToObject; + protected _declarationBlockConfig: DeclarationBlockConfig = {}; constructor(rawConfig: TRawConfig, additionalConfig: TPluginConfig, defaultScalars: ScalarsMap = DEFAULT_SCALARS) { this._parsedConfig = { - scalars: { ...defaultScalars, ...(rawConfig.scalars || {}) }, + scalars: { ...(defaultScalars || DEFAULT_SCALARS), ...(rawConfig.scalars || {}) }, enumValues: rawConfig.enumValues || {}, convert: rawConfig.namingConvention ? resolveExternalModuleAndFn(rawConfig.namingConvention) : toPascalCase, typesPrefix: rawConfig.typesPrefix || '', @@ -58,10 +50,15 @@ export class BaseVisitor field.arguments && field.arguments.length > 0); const fieldsArguments = fieldsWithArguments.map(field => { const name = original.name.value + this.convertName(field.name.value, false) + 'Args'; - const transformedArguments = new OperationVariablesToObject( - this.scalars, - this.convertName, - field.arguments, - this._declarationBlockConfig.wrapAstTypeWithModifiers - ); return new DeclarationBlock(this._declarationBlockConfig) .export() .asKind('type') .withName(this.convertName(name)) - .withBlock(transformedArguments.string).string; + .withBlock(this._argumentsTransformer.transform(field.arguments)).string; }); return [typeDefinition, ...fieldsArguments].filter(f => f).join('\n\n'); diff --git a/packages/plugins/visitor-plugin-common/src/utils.ts b/packages/plugins/visitor-plugin-common/src/utils.ts index ab61183c1e0a..62aef1e2a551 100644 --- a/packages/plugins/visitor-plugin-common/src/utils.ts +++ b/packages/plugins/visitor-plugin-common/src/utils.ts @@ -30,7 +30,6 @@ export function indent(str: string): string { export interface DeclarationBlockConfig { blockWrapper?: string; enumNameValueSeparator?: string; - wrapAstTypeWithModifiers?: Function; } export class DeclarationBlock { @@ -148,18 +147,6 @@ export function toPascalCase(str: string) { .join('_'); } -export const wrapAstTypeWithModifiers = (prefix = '') => (baseType: string, typeNode: TypeNode): string => { - if (typeNode.kind === Kind.NON_NULL_TYPE) { - return wrapAstTypeWithModifiers(prefix)(baseType, typeNode.type).substr(1); - } else if (typeNode.kind === Kind.LIST_TYPE) { - const innerType = wrapAstTypeWithModifiers(prefix)(baseType, typeNode.type); - - return `${prefix}Array<${innerType}>`; - } else { - return `${prefix}${baseType}`; - } -}; - export const wrapTypeWithModifiers = (prefix = '') => ( baseType: string, type: GraphQLObjectType | GraphQLNonNull | GraphQLList diff --git a/packages/plugins/visitor-plugin-common/src/variables-to-object.ts b/packages/plugins/visitor-plugin-common/src/variables-to-object.ts index a1d337df3889..82583784b074 100644 --- a/packages/plugins/visitor-plugin-common/src/variables-to-object.ts +++ b/packages/plugins/visitor-plugin-common/src/variables-to-object.ts @@ -3,24 +3,19 @@ import { indent, getBaseTypeNode } from './utils'; import { ScalarsMap, ConvertNameFn } from './types'; import * as autoBind from 'auto-bind'; -export class OperationVariablesToObject< - DefinitionType extends { - name?: NameNode; - variable?: VariableNode; - type: TypeNode; - defaultValue?: ValueNode; - } -> { - constructor( - private _scalars: ScalarsMap, - private _convertName: ConvertNameFn, - private _variablesNode: ReadonlyArray, - private _wrapAstTypeWithModifiers: Function - ) { +export interface InterfaceOrVariable { + name?: NameNode; + variable?: VariableNode; + type: TypeNode; + defaultValue?: ValueNode; +} + +export class OperationVariablesToObject { + constructor(protected _scalars: ScalarsMap, protected _convertName: ConvertNameFn) { autoBind(this); } - getName(node: DefinitionType): string { + getName(node: TDefinitionType): string { if (node.name) { if (typeof node.name === 'string') { return node.name; @@ -34,28 +29,48 @@ export class OperationVariablesToObject< return null; } - get string(): string { - if (!this._variablesNode || this._variablesNode.length === 0) { + transform(variablesNode: ReadonlyArray): string { + if (!variablesNode || variablesNode.length === 0) { return null; } - return this._variablesNode - .map(variable => { - const baseType = typeof variable.type === 'string' ? variable.type : getBaseTypeNode(variable.type); - const typeName = typeof baseType === 'string' ? baseType : baseType.name.value; - const typeValue = this._scalars[typeName] ? this._scalars[typeName] : this._convertName(typeName, true); + return variablesNode.map(variable => indent(this.transformVariable(variable))).join(',\n'); + } - const fieldName = this.getName(variable); - const fieldType = this._wrapAstTypeWithModifiers(typeValue, variable.type); + protected transformVariable(variable: TDefinitionType): string { + const baseType = typeof variable.type === 'string' ? variable.type : getBaseTypeNode(variable.type); + const typeName = typeof baseType === 'string' ? baseType : baseType.name.value; + const typeValue = this._scalars[typeName] ? this._scalars[typeName] : this._convertName(typeName, true); - const hasDefaultValue = !!variable.defaultValue; - const isNonNullType = variable.type.kind === Kind.NON_NULL_TYPE; + const fieldName = this.getName(variable); + const fieldType = this.wrapAstTypeWithModifiers(typeValue, variable.type); - const formattedFieldString = hasDefaultValue || isNonNullType ? fieldName : `${fieldName}?`; - const formattedTypeString = hasDefaultValue && !isNonNullType ? fieldType.substring(1) : fieldType; + const hasDefaultValue = !!variable.defaultValue; + const isNonNullType = variable.type.kind === Kind.NON_NULL_TYPE; + + const formattedFieldString = this.formatFieldString(fieldName, isNonNullType, hasDefaultValue); + const formattedTypeString = this.formatTypeString(fieldType, isNonNullType, hasDefaultValue); + + return `${formattedFieldString}: ${formattedTypeString}`; + } + + protected wrapAstTypeWithModifiers(baseType: string, typeNode: TypeNode): string { + if (typeNode.kind === Kind.NON_NULL_TYPE) { + return this.wrapAstTypeWithModifiers(baseType, typeNode.type); + } else if (typeNode.kind === Kind.LIST_TYPE) { + const innerType = this.wrapAstTypeWithModifiers(baseType, typeNode.type); + + return `Array<${innerType}>`; + } else { + return baseType; + } + } + + protected formatFieldString(fieldName: string, isNonNullType: boolean, hasDefaultValue: boolean): string { + return fieldName; + } - return indent(`${formattedFieldString}: ${formattedTypeString}`); - }) - .join(',\n'); + protected formatTypeString(fieldType: string, isNonNullType: boolean, hasDefaultValue: boolean): string { + return fieldType; } }