191 lines
4.6 KiB
JavaScript
191 lines
4.6 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* strict-local
|
|
* @format
|
|
*/
|
|
'use strict';
|
|
|
|
var GraphQLCompilerContext = require("./GraphQLCompilerContext");
|
|
|
|
var GraphQLIRTransformer = require("./GraphQLIRTransformer");
|
|
|
|
var invariant = require("fbjs/lib/invariant");
|
|
|
|
var _require = require("./GraphQLSchemaUtils"),
|
|
assertTypeWithFields = _require.assertTypeWithFields,
|
|
canHaveSelections = _require.canHaveSelections,
|
|
getRawType = _require.getRawType;
|
|
|
|
var _require2 = require("graphql"),
|
|
SchemaMetaFieldDef = _require2.SchemaMetaFieldDef,
|
|
TypeMetaFieldDef = _require2.TypeMetaFieldDef,
|
|
TypeNameMetaFieldDef = _require2.TypeNameMetaFieldDef;
|
|
|
|
/**
|
|
* A transform that removes any selections that are not valid relative to the
|
|
* server schema. The primary use case is for fields added via client
|
|
* `extend type ...` definitions and for inline fragments / fragment spreads
|
|
* whose types are added with client `type ...` type extensions.
|
|
*
|
|
* Given a base schema:
|
|
*
|
|
* ```
|
|
* # Note: full schema definition elided for clarity
|
|
* interface Viewer {
|
|
* name: String
|
|
* }
|
|
* type User implements Viewer {
|
|
* name: String
|
|
* }
|
|
* ```
|
|
*
|
|
* And a fragment:
|
|
*
|
|
* ```
|
|
* fragment on Viewer {
|
|
* name
|
|
* ... on User {
|
|
* clientField # (1)
|
|
* }
|
|
* ... on ClientType { # (2)
|
|
* clientField
|
|
* }
|
|
* }
|
|
* extend type User {
|
|
* clientField: String
|
|
* }
|
|
* type ClientType implements Viewer {
|
|
* name: String
|
|
* clientField: String
|
|
* }
|
|
* ```
|
|
*
|
|
* This transform will output:
|
|
*
|
|
* ```
|
|
* fragment on Viewer {
|
|
* name
|
|
* }
|
|
* ```
|
|
*
|
|
* Note that (1) is removed because this field does not exist on the base `User`
|
|
* type, and (2) is removed because the `ClientType` type does not exist in the
|
|
* base schema.
|
|
*/
|
|
function skipClientFieldTransform(context) {
|
|
return GraphQLIRTransformer.transform(context, {
|
|
FragmentSpread: visitFragmentSpread,
|
|
InlineFragment: visitInlineFragment,
|
|
LinkedField: visitField,
|
|
MatchField: visitField,
|
|
ScalarField: visitField
|
|
}, function (node) {
|
|
return buildState(context, node);
|
|
});
|
|
}
|
|
/**
|
|
* @internal
|
|
*
|
|
* Build the initial state, returning null for fragments whose type is not
|
|
* defined in the server schema.
|
|
*/
|
|
|
|
|
|
function buildState(context, node) {
|
|
var schema = context.serverSchema;
|
|
|
|
switch (node.kind) {
|
|
case 'Fragment':
|
|
return schema.getType(node.type.name);
|
|
|
|
case 'Root':
|
|
switch (node.operation) {
|
|
case 'query':
|
|
return schema.getQueryType();
|
|
|
|
case 'mutation':
|
|
return schema.getMutationType();
|
|
|
|
case 'subscription':
|
|
return schema.getSubscriptionType();
|
|
|
|
default:
|
|
node.operation;
|
|
}
|
|
|
|
break;
|
|
|
|
case 'SplitOperation':
|
|
return schema.getType(node.type.name);
|
|
|
|
default:
|
|
node;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
/**
|
|
* @internal
|
|
*
|
|
* Skip fields that were added via `extend type ...`.
|
|
*/
|
|
|
|
|
|
function visitField(field, parentType) {
|
|
if ( // Field is defined in the original parent type definition:
|
|
canHaveSelections(parentType) && assertTypeWithFields(parentType).getFields()[field.name] || // Allow metadata fields and fields defined on classic "fat" interfaces
|
|
field.name === SchemaMetaFieldDef.name || field.name === TypeMetaFieldDef.name || field.name === TypeNameMetaFieldDef.name || field.directives.some(function (_ref) {
|
|
var name = _ref.name;
|
|
return name === 'fixme_fat_interface';
|
|
})) {
|
|
var rawType = getRawType(field.type);
|
|
var type = this.getContext().serverSchema.getType(rawType.name);
|
|
!type ? process.env.NODE_ENV !== "production" ? invariant(false, 'SkipClientFieldTransform: Expected type `%s` to be defined in ' + 'the server schema.', rawType.name) : invariant(false) : void 0;
|
|
return this.traverse(field, type);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
/**
|
|
* @internal
|
|
*
|
|
* Skip fragment spreads where the referenced fragment is not defined in the
|
|
* original schema.
|
|
*/
|
|
|
|
|
|
function visitFragmentSpread(spread, parentType) {
|
|
var context = this.getContext();
|
|
var fragment = context.getFragment(spread.name);
|
|
|
|
if (context.serverSchema.getType(fragment.type.name)) {
|
|
return this.traverse(spread, parentType);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
/**
|
|
* @internal
|
|
*
|
|
* Skip inline fragments where the type is not in the schema.
|
|
*/
|
|
|
|
|
|
function visitInlineFragment(fragment, parentType) {
|
|
var schema = this.getContext().serverSchema;
|
|
var type = schema.getType(fragment.typeCondition.name);
|
|
|
|
if (type) {
|
|
return this.traverse(fragment, type);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
module.exports = {
|
|
transform: skipClientFieldTransform
|
|
}; |