@ardatan/relay-compiler
Version:
A compiler tool for building GraphQL-driven applications.
130 lines (113 loc) • 3.56 kB
Flow
/**
* 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.
*
* @flow strict-local
* @format
*/
// flowlint ambiguous-object-type:error
;
const IRTransformer = require('../core/IRTransformer');
const {createUserError} = require('../core/CompilerError');
import type CompilerContext from '../core/CompilerContext';
import type {
Fragment,
FragmentSpread,
InlineDataFragmentSpread,
} from '../core/IR';
const SCHEMA_EXTENSION = `
directive @inline on FRAGMENT_DEFINITION
`;
/**
* A transform that converts fragment spreads where the referenced fragment
* is annotated with @inline to a InlineDataFragmentSpread.
* InlineDataFragmentSpreads have the selections of the referenced fragment inlined.
*/
function inlineDataFragmentTransform(
context: CompilerContext,
): CompilerContext {
return IRTransformer.transform(context, {
// $FlowFixMe[prop-missing] - this visitor intentionally changes node types
// $FlowFixMe[incompatible-call] - this visitor intentionally changes node types
FragmentSpread: visitFragmentSpread,
Fragment: visitFragment,
});
}
function visitFragment(fragment: Fragment): Fragment {
// $FlowFixMe[incompatible-use]
const transformedFragment = this.traverse(fragment);
const inlineDirective = transformedFragment.directives.find(
directive => directive.name === 'inline',
);
if (inlineDirective == null) {
return transformedFragment;
}
return {
...transformedFragment,
directives: transformedFragment.directives.filter(
directive => directive !== inlineDirective,
),
metadata: {
...(transformedFragment.metadata || {}),
inlineData: true,
},
};
}
function visitFragmentSpread(
fragmentSpread: FragmentSpread,
): FragmentSpread | InlineDataFragmentSpread {
// $FlowFixMe[incompatible-use]
const transformedFragmentSpread: FragmentSpread = this.traverse(
fragmentSpread,
);
// $FlowFixMe[incompatible-use]
const context: CompilerContext = this.getContext();
const fragment = context.get(transformedFragmentSpread.name);
if (
!fragment ||
fragment.kind !== 'Fragment' ||
!fragment.directives.some(directive => directive.name === 'inline')
) {
return transformedFragmentSpread;
}
if (
fragment.argumentDefinitions.length > 0 ||
transformedFragmentSpread.args.length > 0
) {
throw createUserError(
'Variables are not yet supported inside @inline fragments.',
[fragment.argumentDefinitions[0].loc],
);
}
if (transformedFragmentSpread.directives.length > 0) {
throw createUserError(
'Directives on fragment spreads for @inline fragments are not yet ' +
'supported',
[transformedFragmentSpread.loc],
);
}
// $FlowFixMe[incompatible-use]
const transformedFragment = (this.visit(fragment): Fragment);
return ({
kind: 'InlineDataFragmentSpread',
loc: transformedFragmentSpread.loc,
metadata: transformedFragmentSpread.metadata,
name: transformedFragmentSpread.name,
selections: [
{
directives: [],
kind: 'InlineFragment',
loc: {kind: 'Derived', source: transformedFragmentSpread.loc},
metadata: null,
selections: transformedFragment.selections,
typeCondition: transformedFragment.type,
},
],
}: InlineDataFragmentSpread);
}
module.exports = {
SCHEMA_EXTENSION,
transform: inlineDataFragmentTransform,
};