1 | import { isEnumType, isNonNullType, concatAST, Kind, visit } from 'graphql';
|
2 | import { BaseSelectionSetProcessor, indent, BaseDocumentsVisitor, getConfigValue, wrapTypeWithModifiers, PreResolveTypesProcessor, SelectionSetToObject, optimizeOperations } from '@graphql-codegen/visitor-plugin-common';
|
3 | import { FlowOperationVariablesToObject } from '@graphql-codegen/flow';
|
4 | import autoBind from 'auto-bind';
|
5 |
|
6 | class FlowWithPickSelectionSetProcessor extends BaseSelectionSetProcessor {
|
7 | transformAliasesPrimitiveFields(schemaType, fields) {
|
8 | if (fields.length === 0) {
|
9 | return [];
|
10 | }
|
11 | const useFlowExactObject = this.config.useFlowExactObjects;
|
12 | const useFlowReadOnlyTypes = this.config.useFlowReadOnlyTypes;
|
13 | const parentName = (this.config.namespacedImportName ? `${this.config.namespacedImportName}.` : '') +
|
14 | this.config.convertName(schemaType.name, {
|
15 | useTypesPrefix: true,
|
16 | });
|
17 | return [
|
18 | `{${useFlowExactObject ? '|' : ''} ${fields
|
19 | .map(aliasedField => `${useFlowReadOnlyTypes ? '+' : ''}${aliasedField.alias}: $ElementType<${parentName}, '${aliasedField.fieldName}'>`)
|
20 | .join(', ')} ${useFlowExactObject ? '|' : ''}}`,
|
21 | ];
|
22 | }
|
23 | buildFieldsIntoObject(allObjectsMerged) {
|
24 | return `...{ ${allObjectsMerged.join(', ')} }`;
|
25 | }
|
26 | buildSelectionSetFromStrings(pieces) {
|
27 | if (pieces.length === 0) {
|
28 | return null;
|
29 | }
|
30 | else if (pieces.length === 1) {
|
31 | return pieces[0];
|
32 | }
|
33 | else {
|
34 | return `({\n ${pieces.map(t => indent(`...${t}`)).join(`,\n`)}\n})`;
|
35 | }
|
36 | }
|
37 | transformLinkFields(fields) {
|
38 | if (fields.length === 0) {
|
39 | return [];
|
40 | }
|
41 | const useFlowExactObject = this.config.useFlowExactObjects;
|
42 | const useFlowReadOnlyTypes = this.config.useFlowReadOnlyTypes;
|
43 | return [
|
44 | `{${useFlowExactObject ? '|' : ''} ${fields
|
45 | .map(field => `${useFlowReadOnlyTypes ? '+' : ''}${field.alias || field.name}: ${field.selectionSet}`)
|
46 | .join(', ')} ${useFlowExactObject ? '|' : ''}}`,
|
47 | ];
|
48 | }
|
49 | transformPrimitiveFields(schemaType, fields) {
|
50 | if (fields.length === 0) {
|
51 | return [];
|
52 | }
|
53 | const useFlowExactObject = this.config.useFlowExactObjects;
|
54 | const useFlowReadOnlyTypes = this.config.useFlowReadOnlyTypes;
|
55 | const formatNamedField = this.config.formatNamedField;
|
56 | const parentName = (this.config.namespacedImportName ? `${this.config.namespacedImportName}.` : '') +
|
57 | this.config.convertName(schemaType.name, {
|
58 | useTypesPrefix: true,
|
59 | });
|
60 | const fieldObj = schemaType.getFields();
|
61 | return [
|
62 | `$Pick<${parentName}, {${useFlowExactObject ? '|' : ''} ${fields
|
63 | .map(fieldName => `${useFlowReadOnlyTypes ? '+' : ''}${formatNamedField(fieldName, fieldObj[fieldName].type)}: *`)
|
64 | .join(', ')} ${useFlowExactObject ? '|' : ''}}>`,
|
65 | ];
|
66 | }
|
67 | transformTypenameField(type, name) {
|
68 | return [`{ ${name}: ${type} }`];
|
69 | }
|
70 | }
|
71 |
|
72 | class FlowDocumentsVisitor extends BaseDocumentsVisitor {
|
73 | constructor(schema, config, allFragments) {
|
74 | super(config, {
|
75 | useFlowExactObjects: getConfigValue(config.useFlowExactObjects, true),
|
76 | useFlowReadOnlyTypes: getConfigValue(config.useFlowReadOnlyTypes, false),
|
77 | }, schema);
|
78 | autoBind(this);
|
79 | const wrapArray = (type) => `Array<${type}>`;
|
80 | const wrapOptional = (type) => `?${type}`;
|
81 | const formatNamedField = (name, type) => {
|
82 | const optional = !!type && !isNonNullType(type);
|
83 | return `${name}${optional ? '?' : ''}`;
|
84 | };
|
85 | const processorConfig = {
|
86 | namespacedImportName: this.config.namespacedImportName,
|
87 | convertName: this.convertName.bind(this),
|
88 | enumPrefix: this.config.enumPrefix,
|
89 | scalars: this.scalars,
|
90 | formatNamedField,
|
91 | wrapTypeWithModifiers(baseType, type) {
|
92 | return wrapTypeWithModifiers(baseType, type, { wrapOptional, wrapArray });
|
93 | },
|
94 | };
|
95 | const processor = config.preResolveTypes
|
96 | ? new PreResolveTypesProcessor(processorConfig)
|
97 | : new FlowWithPickSelectionSetProcessor({
|
98 | ...processorConfig,
|
99 | useFlowExactObjects: this.config.useFlowExactObjects,
|
100 | useFlowReadOnlyTypes: this.config.useFlowReadOnlyTypes,
|
101 | });
|
102 | const enumsNames = Object.keys(schema.getTypeMap()).filter(typeName => isEnumType(schema.getType(typeName)));
|
103 | this.setSelectionSetHandler(new SelectionSetToObject(processor, this.scalars, this.schema, this.convertName.bind(this), this.getFragmentSuffix.bind(this), allFragments, this.config));
|
104 | this.setVariablesTransformer(new FlowOperationVariablesToObject(this.scalars, this.convertName.bind(this), this.config.namespacedImportName, enumsNames, this.config.enumPrefix));
|
105 | }
|
106 | getPunctuation(declarationKind) {
|
107 | return declarationKind === 'type' ? ',' : ';';
|
108 | }
|
109 | }
|
110 |
|
111 | const plugin = (schema, rawDocuments, config) => {
|
112 | const documents = config.flattenGeneratedTypes
|
113 | ? optimizeOperations(schema, rawDocuments, { includeFragments: true })
|
114 | : rawDocuments;
|
115 | const prefix = config.preResolveTypes
|
116 | ? ''
|
117 | : `type $Pick<Origin: Object, Keys: Object> = $ObjMapi<Keys, <Key>(k: Key) => $ElementType<Origin, Key>>;\n`;
|
118 | const allAst = concatAST(documents.map(v => v.document));
|
119 | const includedFragments = allAst.definitions.filter(d => d.kind === Kind.FRAGMENT_DEFINITION);
|
120 | const allFragments = [
|
121 | ...includedFragments.map(fragmentDef => ({
|
122 | node: fragmentDef,
|
123 | name: fragmentDef.name.value,
|
124 | onType: fragmentDef.typeCondition.name.value,
|
125 | isExternal: false,
|
126 | })),
|
127 | ...(config.externalFragments || []),
|
128 | ];
|
129 | const visitorResult = visit(allAst, {
|
130 | leave: new FlowDocumentsVisitor(schema, config, allFragments),
|
131 | });
|
132 | return {
|
133 | prepend: ['// @flow \n'],
|
134 | content: [prefix, ...visitorResult.definitions].join('\n'),
|
135 | };
|
136 | };
|
137 |
|
138 | export { plugin };
|
139 |
|