Skip to content

Commit

Permalink
feat(QueryParser): add support for interpolation at document level.
Browse files Browse the repository at this point in the history
apollo client uses interpolation to include fragments definitions
see http://dev.apollodata.com/react/fragments.html
  • Loading branch information
Mayank1791989 committed Feb 16, 2018
1 parent e889937 commit 921dca0
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 28 deletions.
40 changes: 22 additions & 18 deletions src/query/_shared/Parsers/QueryParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,36 @@ import {
import { type Stream, type TokenState } from '../../../shared/types';
import invariant from 'invariant';

function JSInlineFragment() {
type InterpolationState = { count: number, style: string };

function Interpolation(style: string) {
return {
style: '',
style: '', // NOTE: should be empty
match(token) {
return token.value === '${';
},
update(state) {
state.jsInlineFragment = { count: 1 }; // count is number of open curly braces
state.interpolation = { count: 1, style }; // count is number of open curly braces
},
};
}

function eatJSInlineFragment(stream, state) {
const { jsInlineFragment: frag } = state;
invariant(frag, 'missing JSInlineFragment');
function eatInterpolation(stream, state) {
const { interpolation } = state;
invariant(interpolation, 'missing interpolation field in state');
stream.eatWhile((ch) => {
if (frag.count === 0) {
state.jsInlineFragment = null;
if (interpolation.count === 0) {
state.interpolation = null;
return false;
}
if (!ch) {
return false;
} // eol
if (ch === '}') {
frag.count -= 1;
interpolation.count -= 1;
}
if (ch === '{') {
frag.count += 1;
interpolation.count += 1;
}
return true;
});
Expand All @@ -51,9 +53,6 @@ const parserOptions = {
parseRules: {
...ParseRules,

// relay only one definition per Relay.QL
// Document: ['Definition'],

// only query, mutation and fragment possible in Relay.QL
Definition(token) {
switch (token.value) {
Expand All @@ -65,6 +64,8 @@ const parserOptions = {
return 'Subscription';
case 'fragment':
return 'FragmentDefinition';
case '${':
return 'DocumentInterpolation';
default:
return null;
}
Expand All @@ -77,7 +78,8 @@ const parserOptions = {
return ParseRules.Selection(token, stream);
},

JSInlineFragment: [JSInlineFragment()],
JSInlineFragment: [Interpolation('js-frag')],
DocumentInterpolation: [Interpolation('ws-2')],
},
};

Expand All @@ -92,11 +94,13 @@ export default class QueryParser {
return this._parser.startState();
}

token(stream: Stream, state: TokenState) {
if (state.jsInlineFragment) {
eatJSInlineFragment(stream, state);
token(stream: Stream, state: TokenState & { interpolation: ?InterpolationState }) {
if (state.interpolation) {
const { style } = state.interpolation;
// NOTE: eatInterpolation mutate both stream and state
eatInterpolation(stream, state);
stream._start -= 2; // to include '${' in token
return 'js-frag';
return style;
}

return this._parser.token(stream, state);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ Object {
"prevChar": "a",
"start": 78,
"state": Object {
"jsInlineFragment": null,
"interpolation": null,
"kind": "Field",
"level": 0,
"levels": Array [
Expand All @@ -128,7 +128,7 @@ Object {
"needsAdvance": true,
"needsSeperator": false,
"prevState": Object {
"jsInlineFragment": null,
"interpolation": null,
"kind": "Selection",
"level": 0,
"levels": Array [
Expand All @@ -138,7 +138,7 @@ Object {
"needsAdvance": false,
"needsSeperator": false,
"prevState": Object {
"jsInlineFragment": null,
"interpolation": null,
"kind": "SelectionSet",
"level": 0,
"levels": Array [
Expand Down Expand Up @@ -267,7 +267,7 @@ Object {
"prevChar": "a",
"start": 62,
"state": Object {
"jsInlineFragment": null,
"interpolation": null,
"kind": "Field",
"level": 0,
"levels": Array [
Expand All @@ -277,7 +277,7 @@ Object {
"needsAdvance": true,
"needsSeperator": false,
"prevState": Object {
"jsInlineFragment": null,
"interpolation": null,
"kind": "Selection",
"level": 0,
"levels": Array [
Expand All @@ -287,7 +287,7 @@ Object {
"needsAdvance": false,
"needsSeperator": false,
"prevState": Object {
"jsInlineFragment": null,
"interpolation": null,
"kind": "SelectionSet",
"level": 0,
"levels": Array [
Expand Down Expand Up @@ -416,7 +416,7 @@ Object {
"prevChar": "a",
"start": 111,
"state": Object {
"jsInlineFragment": null,
"interpolation": null,
"kind": "Field",
"level": 0,
"levels": Array [
Expand All @@ -426,7 +426,7 @@ Object {
"needsAdvance": true,
"needsSeperator": false,
"prevState": Object {
"jsInlineFragment": null,
"interpolation": null,
"kind": "Selection",
"level": 0,
"levels": Array [
Expand All @@ -436,7 +436,7 @@ Object {
"needsAdvance": false,
"needsSeperator": false,
"prevState": Object {
"jsInlineFragment": null,
"interpolation": null,
"kind": "SelectionSet",
"level": 0,
"levels": Array [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ exports[`add dummy fragment name if missing (relay) and \`on\` in next line 1`]
"
`;

exports[`allow template string interpolation at document level 1`] = `
"
fragment FeedEntry on Entry {
commentCount
...VoteButtons
...RepoInfo
}
"
`;

exports[`extract embedded queries 1`] = `
"
Expand Down
23 changes: 23 additions & 0 deletions src/query/_shared/__tests__/parseQuery_toQueryDocument.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,26 @@ test('replace all irregular whitespace with space', () => {

expect(qd).toMatchSnapshot();
});

test('allow template string interpolation at document level', () => {
// for apollo client which uses interpolation to include child components fragments
// see http://dev.apollodata.com/react/fragments.html
const text = `
gql\`
fragment FeedEntry on Entry {
commentCount
...VoteButtons
...RepoInfo
}
\${VoteButtons.fragments.entry}
\${RepoInfo.fragments.entry}
\`
`;
const qd = toQueryDocument(
new Source(text, 'query.js'),
{ parser: ['EmbeddedQueryParser', { startTag: 'gql`', endTag: '`' }] },
);

expect(qd).toMatchSnapshot();
});

2 changes: 1 addition & 1 deletion src/query/_shared/parseQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function toQueryDocument(source: Source, config: Config): string {
condition: () => stream.getCurrentPosition() < source.body.length,
call: () => {
const style = parser.token(stream, state);
// console.log('current', stream.current(), style);
// console.log('current', `[${stream.current()}]`, style);
if ( // add fragment name is missing
config.isRelay &&
state.kind === 'TypeCondition' &&
Expand Down

0 comments on commit 921dca0

Please sign in to comment.