Skip to content

Commit e721fed

Browse files
committed
fixes
1 parent a160a44 commit e721fed

9 files changed

Lines changed: 162 additions & 177 deletions

File tree

.changeset/tall-coins-build.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@envelop/fragment-arguments': patch
3+
---
4+
5+
NEW PLUGIN!

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ We provide a few built-in plugins within the `@envelop/core`, and many more plug
133133
| usePersistedOperations | [`@envelop/persisted-operations`](./packages/plugins/persisted-operations) | Simple implementation of persisted operations/queries, based on custom store. |
134134
| useNewRelic | [`@envelop/newrelic`](./packages/plugins/newrelic) | Instrument your GraphQL application with New Relic reporting. |
135135
| useLiveQuery | [`@envelop/live-query`](./packages/plugins/live-query) | The easiest way of adding live queries to your GraphQL server! |
136+
| useFragmentArguments | [`@envelop/fragment-arguments`](./packages/plugins/fragment-arguments) | Adds support for using arguments on fragments |
136137

137138
## Sharing / Composing `envelop`s
138139

packages/plugins/fragment-arguments/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@
3333
},
3434
"devDependencies": {
3535
"@types/common-tags": "1.8.0",
36+
"@graphql-tools/utils": "7.10.0",
3637
"@graphql-tools/schema": "7.1.5",
37-
"bob-the-bundler": "1.2.1",
38-
"graphql": "15.5.0",
39-
"typescript": "4.3.2",
38+
"bob-the-bundler": "1.4.1",
39+
"graphql": "15.5.1",
40+
"typescript": "4.3.4",
4041
"oneline": "1.0.3",
4142
"common-tags": "1.8.0"
4243
},
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { Parser } from 'graphql/language/parser';
2+
import { Lexer } from 'graphql/language/lexer';
3+
import { TokenKind, Kind, Source, DocumentNode, TokenKindEnum, Token } from 'graphql';
4+
5+
declare module 'graphql/language/parser' {
6+
export class Parser {
7+
constructor(source: string | Source, options?: ParseOptions);
8+
_lexer: Lexer;
9+
expectOptionalKeyword(word: string): boolean;
10+
expectToken(token: TokenKindEnum): void;
11+
peek(token: TokenKindEnum): boolean;
12+
parseFragmentName(): string;
13+
parseArguments(flag: boolean): any;
14+
parseDirectives(flag: boolean): any;
15+
loc(start: Token): any;
16+
parseNamedType(): any;
17+
parseSelectionSet(): any;
18+
expectKeyword(keyword: string): void;
19+
parseVariableDefinitions(): void;
20+
parseDocument(): DocumentNode;
21+
}
22+
}
23+
24+
export class FragmentArgumentCompatibleParser extends Parser {
25+
parseFragment() {
26+
const start = this._lexer.token;
27+
this.expectToken(TokenKind.SPREAD);
28+
const hasTypeCondition = this.expectOptionalKeyword('on');
29+
30+
if (!hasTypeCondition && this.peek(TokenKind.NAME)) {
31+
const name = this.parseFragmentName();
32+
33+
if (this.peek(TokenKind.PAREN_L)) {
34+
return {
35+
kind: Kind.FRAGMENT_SPREAD,
36+
name,
37+
arguments: this.parseArguments(false),
38+
directives: this.parseDirectives(false),
39+
loc: this.loc(start),
40+
};
41+
}
42+
43+
return {
44+
kind: Kind.FRAGMENT_SPREAD,
45+
name: this.parseFragmentName(),
46+
directives: this.parseDirectives(false),
47+
loc: this.loc(start),
48+
};
49+
}
50+
51+
return {
52+
kind: Kind.INLINE_FRAGMENT,
53+
typeCondition: hasTypeCondition ? this.parseNamedType() : undefined,
54+
directives: this.parseDirectives(false),
55+
selectionSet: this.parseSelectionSet(),
56+
loc: this.loc(start),
57+
};
58+
}
59+
60+
parseFragmentDefinition() {
61+
const start = this._lexer.token;
62+
this.expectKeyword('fragment');
63+
const name = this.parseFragmentName();
64+
65+
if (this.peek(TokenKind.PAREN_L)) {
66+
return {
67+
kind: Kind.FRAGMENT_DEFINITION,
68+
name,
69+
variableDefinitions: this.parseVariableDefinitions(),
70+
typeCondition: (this.expectKeyword('on'), this.parseNamedType()),
71+
directives: this.parseDirectives(false),
72+
selectionSet: this.parseSelectionSet(),
73+
loc: this.loc(start),
74+
};
75+
}
76+
77+
return {
78+
kind: Kind.FRAGMENT_DEFINITION,
79+
name,
80+
typeCondition: (this.expectKeyword('on'), this.parseNamedType()),
81+
directives: this.parseDirectives(false),
82+
selectionSet: this.parseSelectionSet(),
83+
loc: this.loc(start),
84+
};
85+
}
86+
}
Lines changed: 7 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,19 @@
11
import { Plugin } from '@envelop/types';
2-
import { Parser, ParseOptions } from 'graphql/language/parser';
3-
import { Lexer } from 'graphql/language/lexer';
2+
import { ParseOptions } from 'graphql/language/parser';
3+
import { Source, DocumentNode } from 'graphql';
4+
import { FragmentArgumentCompatibleParser } from './extended-parser';
5+
import { applySelectionSetFragmentArguments } from './utils';
46

5-
import {
6-
TokenKind,
7-
Kind,
8-
Source,
9-
DocumentNode,
10-
visit,
11-
FragmentDefinitionNode,
12-
InlineFragmentNode,
13-
ArgumentNode,
14-
TokenKindEnum,
15-
Token,
16-
} from 'graphql';
7+
export function parseWithFragmentArguments(source: string | Source, options?: ParseOptions): DocumentNode {
8+
const parser = new FragmentArgumentCompatibleParser(source, options);
179

18-
declare module 'graphql/language/parser' {
19-
export class Parser {
20-
constructor(source: string | Source, options?: ParseOptions);
21-
_lexer: Lexer;
22-
expectOptionalKeyword(word: string): boolean;
23-
expectToken(token: TokenKindEnum): void;
24-
peek(token: TokenKindEnum): boolean;
25-
parseFragmentName(): string;
26-
parseArguments(flag: boolean): any;
27-
parseDirectives(flag: boolean): any;
28-
loc(start: Token): any;
29-
parseNamedType(): any;
30-
parseSelectionSet(): any;
31-
expectKeyword(keyword: string): void;
32-
parseVariableDefinitions(): void;
33-
parseDocument(): DocumentNode;
34-
}
35-
}
36-
37-
class FragmentArgumentCompatible extends Parser {
38-
parseFragment() {
39-
const start = this._lexer.token;
40-
this.expectToken(TokenKind.SPREAD);
41-
const hasTypeCondition = this.expectOptionalKeyword('on');
42-
if (!hasTypeCondition && this.peek(TokenKind.NAME)) {
43-
const name = this.parseFragmentName();
44-
if (this.peek(TokenKind.PAREN_L)) {
45-
return {
46-
kind: Kind.FRAGMENT_SPREAD,
47-
name,
48-
arguments: this.parseArguments(false),
49-
directives: this.parseDirectives(false),
50-
loc: this.loc(start),
51-
};
52-
}
53-
return {
54-
kind: Kind.FRAGMENT_SPREAD,
55-
name: this.parseFragmentName(),
56-
directives: this.parseDirectives(false),
57-
loc: this.loc(start),
58-
};
59-
}
60-
return {
61-
kind: Kind.INLINE_FRAGMENT,
62-
typeCondition: hasTypeCondition ? this.parseNamedType() : undefined,
63-
directives: this.parseDirectives(false),
64-
selectionSet: this.parseSelectionSet(),
65-
loc: this.loc(start),
66-
};
67-
}
68-
69-
parseFragmentDefinition() {
70-
const start = this._lexer.token;
71-
this.expectKeyword('fragment');
72-
const name = this.parseFragmentName();
73-
if (this.peek(TokenKind.PAREN_L)) {
74-
return {
75-
kind: Kind.FRAGMENT_DEFINITION,
76-
name,
77-
variableDefinitions: this.parseVariableDefinitions(),
78-
typeCondition: (this.expectKeyword('on'), this.parseNamedType()),
79-
directives: this.parseDirectives(false),
80-
selectionSet: this.parseSelectionSet(),
81-
loc: this.loc(start),
82-
};
83-
}
84-
85-
return {
86-
kind: Kind.FRAGMENT_DEFINITION,
87-
name,
88-
typeCondition: (this.expectKeyword('on'), this.parseNamedType()),
89-
directives: this.parseDirectives(false),
90-
selectionSet: this.parseSelectionSet(),
91-
loc: this.loc(start),
92-
};
93-
}
94-
}
95-
96-
function pimpedParse(source: string | Source, options?: ParseOptions): DocumentNode {
97-
const parser = new FragmentArgumentCompatible(source, options);
9810
return parser.parseDocument();
9911
}
10012

10113
export const useFragmentArguments = (): Plugin => {
10214
return {
10315
onParse({ setParseFn }) {
104-
setParseFn(pimpedParse);
16+
setParseFn(parseWithFragmentArguments);
10517

10618
return ({ result, replaceParseResult }) => {
10719
if (result && 'kind' in result) {
@@ -111,53 +23,3 @@ export const useFragmentArguments = (): Plugin => {
11123
},
11224
};
11325
};
114-
115-
function applySelectionSetFragmentArguments(document: DocumentNode): DocumentNode | Error {
116-
const fragmentList = new Map<string, FragmentDefinitionNode>();
117-
for (const def of document.definitions) {
118-
if (def.kind !== 'FragmentDefinition') {
119-
continue;
120-
}
121-
fragmentList.set(def.name.value, def);
122-
}
123-
124-
return visit(document, {
125-
FragmentSpread(fragmentNode) {
126-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
127-
// @ts-ignore
128-
if (fragmentNode.arguments != null && fragmentNode.arguments.length) {
129-
const fragmentDef = fragmentList.get(fragmentNode.name.value);
130-
if (!fragmentDef) {
131-
return;
132-
}
133-
134-
const fragmentArguments = new Map<string, ArgumentNode>();
135-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
136-
// @ts-ignore
137-
for (const arg of fragmentNode.arguments) {
138-
fragmentArguments.set(arg.name.value, arg);
139-
}
140-
141-
const selectionSet = visit(fragmentDef.selectionSet, {
142-
Variable(variableNode) {
143-
const fragArg = fragmentArguments.get(variableNode.name.value);
144-
if (fragArg) {
145-
return fragArg.value;
146-
}
147-
148-
return variableNode;
149-
},
150-
});
151-
152-
const inlineFragment: InlineFragmentNode = {
153-
kind: 'InlineFragment',
154-
typeCondition: fragmentDef.typeCondition,
155-
selectionSet,
156-
};
157-
158-
return inlineFragment;
159-
}
160-
return fragmentNode;
161-
},
162-
});
163-
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { InlineFragmentNode, ArgumentNode, DocumentNode, FragmentDefinitionNode, visit } from 'graphql';
2+
3+
export function applySelectionSetFragmentArguments(document: DocumentNode): DocumentNode | Error {
4+
const fragmentList = new Map<string, FragmentDefinitionNode>();
5+
for (const def of document.definitions) {
6+
if (def.kind !== 'FragmentDefinition') {
7+
continue;
8+
}
9+
fragmentList.set(def.name.value, def);
10+
}
11+
12+
return visit(document, {
13+
FragmentSpread(fragmentNode) {
14+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
15+
// @ts-ignore
16+
if (fragmentNode.arguments != null && fragmentNode.arguments.length) {
17+
const fragmentDef = fragmentList.get(fragmentNode.name.value);
18+
if (!fragmentDef) {
19+
return;
20+
}
21+
22+
const fragmentArguments = new Map<string, ArgumentNode>();
23+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
24+
// @ts-ignore
25+
for (const arg of fragmentNode.arguments) {
26+
fragmentArguments.set(arg.name.value, arg);
27+
}
28+
29+
const selectionSet = visit(fragmentDef.selectionSet, {
30+
Variable(variableNode) {
31+
const fragArg = fragmentArguments.get(variableNode.name.value);
32+
if (fragArg) {
33+
return fragArg.value;
34+
}
35+
36+
return variableNode;
37+
},
38+
});
39+
40+
const inlineFragment: InlineFragmentNode = {
41+
kind: 'InlineFragment',
42+
typeCondition: fragmentDef.typeCondition,
43+
selectionSet,
44+
};
45+
46+
return inlineFragment;
47+
}
48+
return fragmentNode;
49+
},
50+
});
51+
}

packages/plugins/fragment-arguments/test/use-fragment-arguments.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ describe('useFragmentArguments', () => {
6666
}
6767
`);
6868
test('can inline fragment with argument', () => {
69-
const { parse } = envelop({ plugins: [useFragmentArguments(), useSchema(schema)] })();
69+
const { parse } = envelop({ plugins: [useFragmentArguments(), useSchema(schema)] })({});
7070
const result = parse(/* GraphQL */ `
7171
fragment TestFragment($c: String) on Query {
7272
a(b: $c)

website/src/lib/plugins.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,11 @@ export const pluginsArr: RawPlugin[] = [
204204
iconUrl: '/assets/logos/graphql.png',
205205
tags: ['utilities'],
206206
},
207+
{
208+
identifier: 'use-fragment-arguments',
209+
title: 'useFragmentArguments',
210+
npmPackage: '@envelop/fragment-arguments',
211+
iconUrl: '/assets/logos/graphql.png',
212+
tags: ['utilities'],
213+
},
207214
];

yarn.lock

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3631,29 +3631,6 @@ bn.js@^5.0.0, bn.js@^5.1.1:
36313631
resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002"
36323632
integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==
36333633

3634-
3635-
version "1.2.1"
3636-
resolved "https://registry.npmjs.org/bob-the-bundler/-/bob-the-bundler-1.2.1.tgz#ed759de2ac59f5bda71cb625d17f1cd787984e4e"
3637-
integrity sha512-mrmiqX10ghyMB6v4T/VXjmhQie8xrqoOCWZ0b3N8JmTqnzYpOZbSw9UaAygxGzg4V2hjwTz0Wl7H1GlXQnawVA==
3638-
dependencies:
3639-
"@rollup/plugin-node-resolve" "7.1.1"
3640-
builtins "4.0.0"
3641-
consola "2.11.3"
3642-
cosmiconfig "6.0.0"
3643-
cross-spawn "7.0.1"
3644-
fs-extra "9.0.0"
3645-
globby "11.0.0"
3646-
lodash.get "4.4.2"
3647-
minimatch "3.0.4"
3648-
mkdirp "1.0.4"
3649-
p-limit "2.2.2"
3650-
param-case "3.0.4"
3651-
rollup "2.3.2"
3652-
rollup-plugin-generate-package-json "3.2.0"
3653-
rollup-plugin-typescript2 "0.27.0"
3654-
tslib "^1.11.1"
3655-
yargs "15.3.1"
3656-
36573634
36583635
version "1.4.1"
36593636
resolved "https://registry.yarnpkg.com/bob-the-bundler/-/bob-the-bundler-1.4.1.tgz#794382f60f0d2a68c57d85a41d06caf4a45cb1b4"
@@ -6229,11 +6206,6 @@ graphql-ws@^4.4.2:
62296206
resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-4.4.2.tgz#f2d83f1863ba3069117199311d664fd28f4aaa8e"
62306207
integrity sha512-Cz+t1w+8+tiHIKzcz45tMwrxJpPSQ7KNjQrfN8ADAELECkkBB7aSvAgpShWz0Tne8teH/UxzJsULObLVq5eMvQ==
62316208

6232-
6233-
version "15.5.0"
6234-
resolved "https://registry.npmjs.org/graphql/-/graphql-15.5.0.tgz#39d19494dbe69d1ea719915b578bf920344a69d5"
6235-
integrity sha512-OmaM7y0kaK31NKG31q4YbD2beNYa6jBBKtMFT6gLYJljHLJr42IqJ8KX08u3Li/0ifzTU5HjmoOOrwa5BRLeDA==
6236-
62376209
62386210
version "15.5.1"
62396211
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.1.tgz#f2f84415d8985e7b84731e7f3536f8bb9d383aad"

0 commit comments

Comments
 (0)