apollo-codegen
Advanced tools
Comparing version 0.17.0-alpha.4 to 0.17.0-alpha.5
@@ -58,3 +58,3 @@ "use strict"; | ||
transformSelectionSetToLegacyIR(selectionSet) { | ||
const typeCase = new typeCase_1.TypeCase(selectionSet, this.options.mergeInFieldsFromFragmentSpreads); | ||
const typeCase = typeCase_1.typeCaseForSelectionSet(selectionSet, this.options.mergeInFieldsFromFragmentSpreads); | ||
const fields = this.transformFieldsToLegacyIR(collectAndMergeFields_1.collectAndMergeFields(typeCase.default, false)); | ||
@@ -61,0 +61,0 @@ const inlineFragments = typeCase.variants.flatMap(variant => { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
function collectAndMergeFields(selectionSet, mergeInFragmentSpreads = true) { | ||
const fieldMap = new Map(); | ||
const groupedFields = new Map(); | ||
function visitSelectionSet(selections, possibleTypes, conditions = []) { | ||
@@ -11,25 +11,13 @@ if (possibleTypes.length < 1) | ||
case 'Field': | ||
const field = selection; | ||
const existingField = fieldMap.get(field.responseKey); | ||
if (existingField) { | ||
existingField.isConditional = existingField.isConditional && conditions.length > 0; | ||
if (existingField.conditions && conditions.length > 0) { | ||
existingField.conditions = [...existingField.conditions, ...conditions]; | ||
} | ||
if (field.selectionSet && existingField.selectionSet) { | ||
existingField.selectionSet.selections.push(...wrapInBooleanConditionsIfNeeded(field.selectionSet.selections, conditions)); | ||
} | ||
let groupForResponseKey = groupedFields.get(selection.responseKey); | ||
if (!groupForResponseKey) { | ||
groupForResponseKey = []; | ||
groupedFields.set(selection.responseKey, groupForResponseKey); | ||
} | ||
else { | ||
const clonedField = Object.assign({}, field, { selectionSet: field.selectionSet | ||
? { | ||
possibleTypes: field.selectionSet.possibleTypes, | ||
selections: [ | ||
...wrapInBooleanConditionsIfNeeded(field.selectionSet.selections, conditions) | ||
] | ||
} | ||
: undefined }); | ||
clonedField.isConditional = conditions.length > 0; | ||
fieldMap.set(field.responseKey, Object.assign({}, clonedField, { conditions })); | ||
} | ||
groupForResponseKey.push(Object.assign({}, selection, { isConditional: conditions.length > 0, conditions, selectionSet: selection.selectionSet | ||
? { | ||
possibleTypes: selection.selectionSet.possibleTypes, | ||
selections: [...selection.selectionSet.selections] | ||
} | ||
: undefined })); | ||
break; | ||
@@ -51,13 +39,36 @@ case 'FragmentSpread': | ||
visitSelectionSet(selectionSet.selections, selectionSet.possibleTypes); | ||
const fields = Array.from(groupedFields.values()).map(fields => { | ||
const isFieldIncludedUnconditionally = fields.some(field => !field.isConditional); | ||
return fields | ||
.map(field => { | ||
if (isFieldIncludedUnconditionally && field.isConditional && field.selectionSet) { | ||
field.selectionSet.selections = wrapInBooleanConditionsIfNeeded(field.selectionSet.selections, field.conditions); | ||
} | ||
return field; | ||
}) | ||
.reduce((field, otherField) => { | ||
field.isConditional = field.isConditional && otherField.isConditional; | ||
if (field.conditions && otherField.conditions) { | ||
field.conditions = [...field.conditions, ...otherField.conditions]; | ||
} | ||
else { | ||
field.conditions = undefined; | ||
} | ||
if (field.selectionSet && otherField.selectionSet) { | ||
field.selectionSet.selections.push(...otherField.selectionSet.selections); | ||
} | ||
return field; | ||
}); | ||
}); | ||
if (selectionSet.possibleTypes.length == 1) { | ||
const type = selectionSet.possibleTypes[0]; | ||
const fieldDefMap = type.getFields(); | ||
for (const [responseKey, field] of fieldMap) { | ||
for (const field of fields) { | ||
const fieldDef = fieldDefMap[field.name]; | ||
if (fieldDef && fieldDef.description) { | ||
fieldMap.set(responseKey, Object.assign({}, field, { description: fieldDef.description })); | ||
field.description = fieldDef.description; | ||
} | ||
} | ||
} | ||
return Array.from(fieldMap.values()); | ||
return fields; | ||
} | ||
@@ -64,0 +75,0 @@ exports.collectAndMergeFields = collectAndMergeFields; |
@@ -16,12 +16,65 @@ "use strict"; | ||
exports.Variant = Variant; | ||
function typeCaseForSelectionSet(selectionSet, mergeInFragmentSpreads = true) { | ||
const typeCase = new TypeCase(selectionSet.possibleTypes); | ||
for (const selection of selectionSet.selections) { | ||
switch (selection.kind) { | ||
case 'Field': | ||
for (const variant of typeCase.disjointVariantsFor(selectionSet.possibleTypes)) { | ||
variant.selections.push(selection); | ||
} | ||
break; | ||
case 'FragmentSpread': | ||
for (const variant of typeCase.disjointVariantsFor(selectionSet.possibleTypes)) { | ||
variant.fragmentSpreads.push(selection); | ||
if (!mergeInFragmentSpreads) { | ||
variant.selections.push(selection); | ||
} | ||
} | ||
if (mergeInFragmentSpreads) { | ||
for (const { possibleTypes, selections } of typeCaseForSelectionSet({ | ||
possibleTypes: selectionSet.possibleTypes.filter(type => selection.selectionSet.possibleTypes.includes(type)), | ||
selections: selection.selectionSet.selections | ||
}).defaultAndVariants) { | ||
if (selections.length < 1) | ||
continue; | ||
for (const variant of typeCase.disjointVariantsFor(possibleTypes)) { | ||
variant.selections.push(...selections); | ||
} | ||
} | ||
} | ||
break; | ||
case 'TypeCondition': | ||
for (const { possibleTypes, selections } of typeCaseForSelectionSet({ | ||
possibleTypes: selectionSet.possibleTypes.filter(type => selection.selectionSet.possibleTypes.includes(type)), | ||
selections: selection.selectionSet.selections | ||
}).defaultAndVariants) { | ||
if (selections.length < 1) | ||
continue; | ||
for (const variant of typeCase.disjointVariantsFor(possibleTypes)) { | ||
variant.selections.push(...selections); | ||
} | ||
} | ||
break; | ||
case 'BooleanCondition': | ||
for (const { possibleTypes, selections } of typeCaseForSelectionSet(selection.selectionSet) | ||
.defaultAndVariants) { | ||
for (const variant of typeCase.disjointVariantsFor(possibleTypes)) { | ||
if (selections.length < 1) | ||
continue; | ||
variant.selections.push(Object.assign({}, selection, { selectionSet: { possibleTypes: variant.possibleTypes, selections } })); | ||
} | ||
} | ||
break; | ||
} | ||
} | ||
return typeCase; | ||
} | ||
exports.typeCaseForSelectionSet = typeCaseForSelectionSet; | ||
class TypeCase { | ||
constructor(selectionSet, mergeInFragmentSpreads = true) { | ||
this.mergeInFragmentSpreads = mergeInFragmentSpreads; | ||
this.default = new Variant(selectionSet.possibleTypes); | ||
this.variantsByType = new Map(); | ||
this.visitSelectionSet(selectionSet.selections, selectionSet.possibleTypes); | ||
} | ||
get variants() { | ||
return Array.from(new Set(this.variantsByType.values())); | ||
} | ||
get defaultAndVariants() { | ||
return [this.default, ...this.variants]; | ||
} | ||
get remainder() { | ||
@@ -38,3 +91,3 @@ if (this.default.possibleTypes.some(type => !this.variantsByType.has(type))) { | ||
if (remainder) { | ||
return [...this.variants, remainder]; | ||
return [remainder, ...this.variants]; | ||
} | ||
@@ -45,33 +98,7 @@ else { | ||
} | ||
visitSelectionSet(selections, possibleTypes, conditions = []) { | ||
if (possibleTypes.length < 1) | ||
return; | ||
for (const selection of selections) { | ||
switch (selection.kind) { | ||
case 'Field': | ||
for (const variant of this.variantsFor(possibleTypes)) { | ||
variant.selections.push(...collectAndMergeFields_1.wrapInBooleanConditionsIfNeeded([selection], conditions)); | ||
} | ||
break; | ||
case 'FragmentSpread': | ||
for (const variant of this.variantsFor(possibleTypes)) { | ||
variant.fragmentSpreads.push(selection); | ||
if (!this.mergeInFragmentSpreads) { | ||
variant.selections.push(...collectAndMergeFields_1.wrapInBooleanConditionsIfNeeded([selection], conditions)); | ||
} | ||
} | ||
if (this.mergeInFragmentSpreads) { | ||
this.visitSelectionSet(selection.selectionSet.selections, possibleTypes.filter(type => selection.selectionSet.possibleTypes.includes(type)), conditions); | ||
} | ||
break; | ||
case 'TypeCondition': | ||
this.visitSelectionSet(selection.selectionSet.selections, possibleTypes.filter(type => selection.selectionSet.possibleTypes.includes(type)), conditions); | ||
break; | ||
case 'BooleanCondition': | ||
this.visitSelectionSet(selection.selectionSet.selections, possibleTypes, [...conditions, selection]); | ||
break; | ||
} | ||
} | ||
constructor(possibleTypes) { | ||
this.default = new Variant(possibleTypes); | ||
this.variantsByType = new Map(); | ||
} | ||
variantsFor(possibleTypes) { | ||
disjointVariantsFor(possibleTypes) { | ||
const variants = []; | ||
@@ -78,0 +105,0 @@ const matchesDefault = this.default.possibleTypes.every(type => possibleTypes.includes(type)); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const graphql_1 = require("graphql"); | ||
const typeCase_1 = require("../compiler/visitors/typeCase"); | ||
const printing_1 = require("../utilities/printing"); | ||
@@ -9,2 +8,3 @@ const language_1 = require("./language"); | ||
const graphql_2 = require("../utilities/graphql"); | ||
const typeCase_1 = require("../compiler/visitors/typeCase"); | ||
const collectFragmentsReferenced_1 = require("../compiler/visitors/collectFragmentsReferenced"); | ||
@@ -124,3 +124,3 @@ const generateOperationId_1 = require("../compiler/visitors/generateOperationId"); | ||
structDeclarationForSelectionSet({ structName, adoptedProtocols = ['GraphQLSelectionSet'], selectionSet }, beforeClosure) { | ||
const typeCase = new typeCase_1.TypeCase(selectionSet, this.context.options.mergeInFieldsFromFragmentSpreads); | ||
const typeCase = typeCase_1.typeCaseForSelectionSet(selectionSet, this.context.options.mergeInFieldsFromFragmentSpreads); | ||
this.structDeclaration({ structName, adoptedProtocols }, () => { | ||
@@ -127,0 +127,0 @@ if (beforeClosure) { |
{ | ||
"name": "apollo-codegen", | ||
"version": "0.17.0-alpha.4", | ||
"version": "0.17.0-alpha.5", | ||
"description": "Generate API code or type annotations based on a GraphQL schema and query documents", | ||
@@ -5,0 +5,0 @@ "main": "./lib/index.js", |
@@ -7,3 +7,3 @@ import { GraphQLSchema, GraphQLType, GraphQLObjectType, GraphQLCompositeType, DocumentNode } from 'graphql'; | ||
import { generateOperationId } from './visitors/generateOperationId'; | ||
import { TypeCase } from './visitors/typeCase'; | ||
import { typeCaseForSelectionSet } from './visitors/typeCase'; | ||
import { collectAndMergeFields } from './visitors/collectAndMergeFields'; | ||
@@ -158,3 +158,3 @@ | ||
transformSelectionSetToLegacyIR(selectionSet: SelectionSet) { | ||
const typeCase = new TypeCase(selectionSet, this.options.mergeInFieldsFromFragmentSpreads); | ||
const typeCase = typeCaseForSelectionSet(selectionSet, this.options.mergeInFieldsFromFragmentSpreads); | ||
@@ -161,0 +161,0 @@ const fields: LegacyField[] = this.transformFieldsToLegacyIR(collectAndMergeFields(typeCase.default, false)); |
@@ -16,3 +16,3 @@ import { SelectionSet, Selection, Field, BooleanCondition } from '../'; | ||
): Field[] { | ||
const fieldMap: Map<string, Field> = new Map(); | ||
const groupedFields: Map<string, Field[]> = new Map(); | ||
@@ -29,38 +29,20 @@ function visitSelectionSet( | ||
case 'Field': | ||
const field = selection; | ||
const existingField = fieldMap.get(field.responseKey); | ||
if (existingField) { | ||
existingField.isConditional = existingField.isConditional && conditions.length > 0; | ||
// FIXME: This is strictly taken incorrect, because the conditions should be ORed | ||
// These conditions are only used in Android target however, | ||
// and there is now way to express this in the legacy IR. | ||
if (existingField.conditions && conditions.length > 0) { | ||
existingField.conditions = [...existingField.conditions, ...conditions]; | ||
} | ||
if (field.selectionSet && existingField.selectionSet) { | ||
existingField.selectionSet.selections.push( | ||
...wrapInBooleanConditionsIfNeeded(field.selectionSet.selections, conditions) | ||
); | ||
} | ||
} else { | ||
// Make sure to deep clone selections to avoid modifying the original field | ||
// TODO: Should we use an object freezing / immutability solution? | ||
const clonedField = { | ||
...field, | ||
selectionSet: field.selectionSet | ||
? { | ||
possibleTypes: field.selectionSet.possibleTypes, | ||
selections: [ | ||
...wrapInBooleanConditionsIfNeeded(field.selectionSet.selections, conditions) | ||
] | ||
} | ||
: undefined | ||
}; | ||
clonedField.isConditional = conditions.length > 0; | ||
fieldMap.set(field.responseKey, { ...clonedField, conditions }); | ||
let groupForResponseKey = groupedFields.get(selection.responseKey); | ||
if (!groupForResponseKey) { | ||
groupForResponseKey = []; | ||
groupedFields.set(selection.responseKey, groupForResponseKey); | ||
} | ||
// Make sure to deep clone selections to avoid modifying the original field | ||
// TODO: Should we use an object freezing / immutability solution? | ||
groupForResponseKey.push({ | ||
...selection, | ||
isConditional: conditions.length > 0, | ||
conditions, | ||
selectionSet: selection.selectionSet | ||
? { | ||
possibleTypes: selection.selectionSet.possibleTypes, | ||
selections: [...selection.selectionSet.selections] | ||
} | ||
: undefined | ||
}); | ||
break; | ||
@@ -74,7 +56,3 @@ case 'FragmentSpread': | ||
visitSelectionSet( | ||
selection.selectionSet.selections, | ||
possibleTypes, | ||
conditions | ||
); | ||
visitSelectionSet(selection.selectionSet.selections, possibleTypes, conditions); | ||
break; | ||
@@ -90,2 +68,37 @@ case 'BooleanCondition': | ||
// Merge selection sets | ||
const fields = Array.from(groupedFields.values()).map(fields => { | ||
const isFieldIncludedUnconditionally = fields.some(field => !field.isConditional); | ||
return fields | ||
.map(field => { | ||
if (isFieldIncludedUnconditionally && field.isConditional && field.selectionSet) { | ||
field.selectionSet.selections = wrapInBooleanConditionsIfNeeded( | ||
field.selectionSet.selections, | ||
field.conditions | ||
); | ||
} | ||
return field; | ||
}) | ||
.reduce((field, otherField) => { | ||
field.isConditional = field.isConditional && otherField.isConditional; | ||
// FIXME: This is strictly taken incorrect, because the conditions should be ORed | ||
// These conditions are only used in Android target however, | ||
// and there is now way to express this in the legacy IR. | ||
if (field.conditions && otherField.conditions) { | ||
field.conditions = [...field.conditions, ...otherField.conditions]; | ||
} else { | ||
field.conditions = undefined; | ||
} | ||
if (field.selectionSet && otherField.selectionSet) { | ||
field.selectionSet.selections.push(...otherField.selectionSet.selections); | ||
} | ||
return field; | ||
}); | ||
}); | ||
// Replace field descriptions with type-specific descriptions if possible | ||
@@ -96,7 +109,7 @@ if (selectionSet.possibleTypes.length == 1) { | ||
for (const [responseKey, field] of fieldMap) { | ||
for (const field of fields) { | ||
const fieldDef = fieldDefMap[field.name]; | ||
if (fieldDef && fieldDef.description) { | ||
fieldMap.set(responseKey, { ...field, description: fieldDef.description }); | ||
field.description = fieldDef.description; | ||
} | ||
@@ -106,3 +119,3 @@ } | ||
return Array.from(fieldMap.values()); | ||
return fields; | ||
} | ||
@@ -109,0 +122,0 @@ |
@@ -5,4 +5,4 @@ import { inspect } from 'util'; | ||
import { SelectionSet, Selection, FragmentSpread, BooleanCondition } from '../'; | ||
import { collectAndMergeFields, wrapInBooleanConditionsIfNeeded } from './collectAndMergeFields'; | ||
import { SelectionSet, Selection, FragmentSpread } from '../'; | ||
import { collectAndMergeFields } from './collectAndMergeFields'; | ||
@@ -23,2 +23,67 @@ export class Variant implements SelectionSet { | ||
export function typeCaseForSelectionSet( | ||
selectionSet: SelectionSet, | ||
mergeInFragmentSpreads: boolean = true | ||
): TypeCase { | ||
const typeCase = new TypeCase(selectionSet.possibleTypes); | ||
for (const selection of selectionSet.selections) { | ||
switch (selection.kind) { | ||
case 'Field': | ||
for (const variant of typeCase.disjointVariantsFor(selectionSet.possibleTypes)) { | ||
variant.selections.push(selection); | ||
} | ||
break; | ||
case 'FragmentSpread': | ||
for (const variant of typeCase.disjointVariantsFor(selectionSet.possibleTypes)) { | ||
variant.fragmentSpreads.push(selection); | ||
if (!mergeInFragmentSpreads) { | ||
variant.selections.push(selection); | ||
} | ||
} | ||
if (mergeInFragmentSpreads) { | ||
for (const { possibleTypes, selections } of typeCaseForSelectionSet({ | ||
possibleTypes: selectionSet.possibleTypes.filter(type => | ||
selection.selectionSet.possibleTypes.includes(type) | ||
), | ||
selections: selection.selectionSet.selections | ||
}).defaultAndVariants) { | ||
if (selections.length < 1) continue; | ||
for (const variant of typeCase.disjointVariantsFor(possibleTypes)) { | ||
variant.selections.push(...selections); | ||
} | ||
} | ||
} | ||
break; | ||
case 'TypeCondition': | ||
for (const { possibleTypes, selections } of typeCaseForSelectionSet({ | ||
possibleTypes: selectionSet.possibleTypes.filter(type => | ||
selection.selectionSet.possibleTypes.includes(type) | ||
), | ||
selections: selection.selectionSet.selections | ||
}).defaultAndVariants) { | ||
if (selections.length < 1) continue; | ||
for (const variant of typeCase.disjointVariantsFor(possibleTypes)) { | ||
variant.selections.push(...selections); | ||
} | ||
} | ||
break; | ||
case 'BooleanCondition': | ||
for (const { possibleTypes, selections } of typeCaseForSelectionSet(selection.selectionSet) | ||
.defaultAndVariants) { | ||
for (const variant of typeCase.disjointVariantsFor(possibleTypes)) { | ||
if (selections.length < 1) continue; | ||
variant.selections.push({ | ||
...selection, | ||
selectionSet: { possibleTypes: variant.possibleTypes, selections } | ||
}); | ||
} | ||
} | ||
break; | ||
} | ||
} | ||
return typeCase; | ||
} | ||
export class TypeCase { | ||
@@ -33,2 +98,6 @@ default: Variant; | ||
get defaultAndVariants(): Variant[] { | ||
return [this.default, ...this.variants]; | ||
} | ||
get remainder(): Variant | undefined { | ||
@@ -49,3 +118,3 @@ if (this.default.possibleTypes.some(type => !this.variantsByType.has(type))) { | ||
if (remainder) { | ||
return [...this.variants, remainder]; | ||
return [remainder, ...this.variants]; | ||
} else { | ||
@@ -56,61 +125,13 @@ return this.variants; | ||
constructor(selectionSet: SelectionSet, private mergeInFragmentSpreads: boolean = true) { | ||
constructor(possibleTypes: GraphQLObjectType[]) { | ||
// We start out with a single default variant that represents all possible types of the selection set. | ||
this.default = new Variant(selectionSet.possibleTypes); | ||
this.default = new Variant(possibleTypes); | ||
this.variantsByType = new Map(); | ||
this.visitSelectionSet(selectionSet.selections, selectionSet.possibleTypes); | ||
} | ||
private visitSelectionSet( | ||
selections: Selection[], | ||
possibleTypes: GraphQLObjectType[], | ||
conditions: BooleanCondition[] = [] | ||
) { | ||
if (possibleTypes.length < 1) return; | ||
for (const selection of selections) { | ||
switch (selection.kind) { | ||
case 'Field': | ||
// Add the field to all variants for the currently possible types. | ||
for (const variant of this.variantsFor(possibleTypes)) { | ||
variant.selections.push(...wrapInBooleanConditionsIfNeeded([selection], conditions)); | ||
} | ||
break; | ||
case 'FragmentSpread': | ||
// Add the fragment spread to all variants variants for the currently possible types. | ||
for (const variant of this.variantsFor(possibleTypes)) { | ||
variant.fragmentSpreads.push(selection); | ||
if (!this.mergeInFragmentSpreads) { | ||
variant.selections.push(...wrapInBooleanConditionsIfNeeded([selection], conditions)); | ||
} | ||
} | ||
if (this.mergeInFragmentSpreads) { | ||
this.visitSelectionSet( | ||
selection.selectionSet.selections, | ||
possibleTypes.filter(type => selection.selectionSet.possibleTypes.includes(type)), | ||
conditions | ||
); | ||
} | ||
break; | ||
case 'TypeCondition': | ||
this.visitSelectionSet( | ||
selection.selectionSet.selections, | ||
possibleTypes.filter(type => selection.selectionSet.possibleTypes.includes(type)), | ||
conditions | ||
); | ||
break; | ||
case 'BooleanCondition': | ||
this.visitSelectionSet(selection.selectionSet.selections, possibleTypes, [...conditions, selection]); | ||
break; | ||
} | ||
} | ||
} | ||
// Returns records representing a set of possible types, making sure they are disjoint with other possible types. | ||
// That may involve refining the existing partition (https://en.wikipedia.org/wiki/Partition_refinement) | ||
// with the passed in set of possible types. | ||
private variantsFor(possibleTypes: GraphQLObjectType[]): Variant[] { | ||
disjointVariantsFor(possibleTypes: GraphQLObjectType[]): Variant[] { | ||
const variants: Variant[] = []; | ||
@@ -117,0 +138,0 @@ |
@@ -14,4 +14,2 @@ import { | ||
import { TypeCase } from '../compiler/visitors/typeCase'; | ||
import { join, wrap } from '../utilities/printing'; | ||
@@ -23,2 +21,3 @@ | ||
import { typeCaseForSelectionSet, TypeCase } from '../compiler/visitors/typeCase'; | ||
import { collectFragmentsReferenced } from '../compiler/visitors/collectFragmentsReferenced'; | ||
@@ -198,3 +197,3 @@ import { generateOperationId } from '../compiler/visitors/generateOperationId'; | ||
) { | ||
const typeCase = new TypeCase(selectionSet, this.context.options.mergeInFieldsFromFragmentSpreads); | ||
const typeCase = typeCaseForSelectionSet(selectionSet, this.context.options.mergeInFieldsFromFragmentSpreads); | ||
@@ -201,0 +200,0 @@ this.structDeclaration({ structName, adoptedProtocols }, () => { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
440874
7719