538 |
543 |
544 |
545 | {languages.map((lang: string) => (
546 | this.setLanguage(lang)}>
547 | {lang}
548 |
549 | ))}
550 |
551 |
552 | {snippets
553 | .filter(snippet => snippet.language === language)
554 | .map(snippet => (
555 | this.setSnippet(snippet)}>
558 | {snippet.name}
559 |
560 | ))}
561 |
562 |
563 | {snippet.options.length > 0 ? (
564 |
565 |
572 | Options
573 |
574 | {snippet.options.map(option => (
575 |
576 |
583 | this.handleSetOptionValue(
584 | snippet,
585 | option.id,
586 | // $FlowFixMe: Come back for this
587 | !optionValues[option.id],
588 | )
589 | }
590 | />
591 |
594 |
595 | ))}
596 |
597 | ) : (
598 |
599 | )}
600 | {supportsCodesandbox ? (
601 |
602 |
624 | {codesandboxResult ? (
625 |
626 | {codesandboxResult.type === 'loading' ? (
627 | 'Loading...'
628 | ) : codesandboxResult.type === 'error' ? (
629 | `Error: ${codesandboxResult.error}`
630 | ) : (
631 |
635 | Visit CodeSandbox
636 |
637 | )}
638 |
639 | ) : null}
640 |
641 | ) : null}
642 |
643 |
686 |
693 | {codeSnippet ? (
694 |
699 | ) : (
700 |
701 | The query is invalid.
702 |
703 | The generated code will appear here once the errors in the query
704 | editor are resolved.
705 |
706 | )}
707 |
708 |
709 | );
710 | }
711 | }
712 |
713 | class ErrorBoundary extends React.Component<*, {hasError: boolean}> {
714 | state = {hasError: false};
715 |
716 | componentDidCatch(error, info) {
717 | this.setState({hasError: true});
718 | console.error('Error in component', error, info);
719 | }
720 |
721 | render() {
722 | if (this.state.hasError) {
723 | return (
724 | ,
748 | snippet?: Snippet,
749 | codeMirrorTheme?: string,
750 | onSelectSnippet?: (snippet: Snippet) => void,
751 | onSetOptionValue?: (snippet: Snippet, option: string, value: boolean) => void,
752 | optionValues?: OptionValues,
753 | onGenerateCodesandbox?: ?({sandboxId: string}) => void,
754 | schema: ?GraphQLSchema,
755 | };
756 |
757 | // we borrow class names from graphiql's CSS as the visual appearance is the same
758 | // yet we might want to change that at some point in order to have a self-contained standalone
759 | export default function CodeExporterWrapper({
760 | query,
761 | serverUrl,
762 | variables,
763 | context = {},
764 | headers = {},
765 | hideCodeExporter = () => {},
766 | snippets,
767 | snippet,
768 | codeMirrorTheme,
769 | onSelectSnippet,
770 | onSetOptionValue,
771 | optionValues,
772 | onGenerateCodesandbox,
773 | schema,
774 | }: WrapperProps) {
775 | let jsonVariables: Variables = {};
776 |
777 | try {
778 | const parsedVariables = JSON.parse(variables);
779 | if (typeof parsedVariables === 'object') {
780 | jsonVariables = parsedVariables;
781 | }
782 | } catch (e) {}
783 |
784 | return (
785 |
792 |
793 |
Code Exporter
794 |
795 |
796 | {'\u2715'}
797 |
798 |
799 |
800 |
803 | {snippets.length ? (
804 |
805 |
820 |
821 | ) : (
822 |
823 | Please provide a list of snippets
824 |
825 | )}
826 |
827 |
828 | );
829 | }
830 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import CodeExporter, {computeOperationDataList} from './CodeExporter';
4 | import capitalizeFirstLetter from './utils/capitalizeFirstLetter';
5 | import jsCommentsFactory from './utils/jsCommentsFactory';
6 | import snippets from './snippets/index';
7 |
8 | export type {
9 | CodesandboxFile,
10 | CodesandboxFiles,
11 | Snippet,
12 | GenerateOptions,
13 | OperationData,
14 | Options,
15 | OptionValues,
16 | Variables,
17 | } from './CodeExporter';
18 |
19 | export {
20 | computeOperationDataList,
21 | capitalizeFirstLetter,
22 | jsCommentsFactory,
23 | snippets,
24 | };
25 |
26 | export default CodeExporter;
27 |
--------------------------------------------------------------------------------
/src/snippets/__helpers__/getOptionCombinations.js:
--------------------------------------------------------------------------------
1 | export default function getOptionCombinations(options) {
2 | const optionIds = options.map(option => option.id);
3 | const combinationCount = Math.pow(2, optionIds.length);
4 |
5 | return Array.from(Array(combinationCount).keys()).reduce(
6 | (combinations, index) => {
7 | const booleanValues = index
8 | .toString(2)
9 | .padStart(3, '0')
10 | .split('');
11 |
12 | const optionMap = optionIds.reduce((map, name, i) => {
13 | map[name] = Boolean(parseInt(booleanValues[i]));
14 | return map;
15 | }, {});
16 |
17 | combinations.push(optionMap);
18 | return combinations;
19 | },
20 | [],
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/src/snippets/index.js:
--------------------------------------------------------------------------------
1 | // javascript
2 | import jsFetch from './javascript/fetch';
3 | import jsReactApollo from './javascript/reactApollo';
4 |
5 | export default [jsFetch, jsReactApollo];
6 |
--------------------------------------------------------------------------------
/src/snippets/javascript/__tests__/__snapshots__/fetch-test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Generating a JavaScript:fetch snippet should generate the correct mutation snippet 1`] = `
4 | Object {
5 | "options": Object {
6 | "asyncAwait": false,
7 | "comments": false,
8 | "server": false,
9 | },
10 | "snippet": "
11 | const TEST_MUTATION = \`
12 |
13 | mutation testMutation {
14 | addData(id: \\"id\\") {
15 | id
16 | }
17 | }\`
18 |
19 | const serverUrl = \\"https://api.myservice.com/\\"
20 |
21 | fetch(serverUrl, {
22 | method: \\"POST\\",
23 | headers: {},
24 | body: JSON.stringify({
25 | query: TEST_MUTATION,
26 | variables: {}
27 | })
28 | })
29 | .then(res => res.json())
30 | .then(({ data, errors }) => {
31 | if (errors) {
32 | console.error(errors)
33 | }
34 |
35 | console.log(data)
36 | })
37 | .catch(err => {
38 | console.error(err)
39 | })
40 | ",
41 | }
42 | `;
43 |
44 | exports[`Generating a JavaScript:fetch snippet should generate the correct mutation snippet 2`] = `
45 | Object {
46 | "options": Object {
47 | "asyncAwait": true,
48 | "comments": false,
49 | "server": false,
50 | },
51 | "snippet": "
52 | const TEST_MUTATION = \`
53 |
54 | mutation testMutation {
55 | addData(id: \\"id\\") {
56 | id
57 | }
58 | }\`
59 |
60 | const serverUrl = \\"https://api.myservice.com/\\"
61 |
62 | const res = await fetch(serverUrl, {
63 | method: \\"POST\\",
64 | headers: {},
65 | body: JSON.stringify({
66 | query: TEST_MUTATION,
67 | variables: {}
68 | })
69 | })
70 | const { errors, data } = await res.json()
71 |
72 | if (errors) {
73 | console.error(errors)
74 | }
75 |
76 | console.log(data)
77 | ",
78 | }
79 | `;
80 |
81 | exports[`Generating a JavaScript:fetch snippet should generate the correct mutation snippet 3`] = `
82 | Object {
83 | "options": Object {
84 | "asyncAwait": false,
85 | "comments": false,
86 | "server": true,
87 | },
88 | "snippet": "
89 | import fetch from \\"node-fetch\\"
90 |
91 | const TEST_MUTATION = \`
92 |
93 | mutation testMutation {
94 | addData(id: \\"id\\") {
95 | id
96 | }
97 | }\`
98 |
99 | const serverUrl = \\"https://api.myservice.com/\\"
100 |
101 | fetch(serverUrl, {
102 | method: \\"POST\\",
103 | headers: {},
104 | body: JSON.stringify({
105 | query: TEST_MUTATION,
106 | variables: {}
107 | })
108 | })
109 | .then(res => res.json())
110 | .then(({ data, errors }) => {
111 | if (errors) {
112 | console.error(errors)
113 | }
114 |
115 | console.log(data)
116 | })
117 | .catch(err => {
118 | console.error(err)
119 | })
120 | ",
121 | }
122 | `;
123 |
124 | exports[`Generating a JavaScript:fetch snippet should generate the correct mutation snippet 4`] = `
125 | Object {
126 | "options": Object {
127 | "asyncAwait": true,
128 | "comments": false,
129 | "server": true,
130 | },
131 | "snippet": "
132 | import fetch from \\"node-fetch\\"
133 |
134 | const TEST_MUTATION = \`
135 |
136 | mutation testMutation {
137 | addData(id: \\"id\\") {
138 | id
139 | }
140 | }\`
141 |
142 | const serverUrl = \\"https://api.myservice.com/\\"
143 |
144 | const res = await fetch(serverUrl, {
145 | method: \\"POST\\",
146 | headers: {},
147 | body: JSON.stringify({
148 | query: TEST_MUTATION,
149 | variables: {}
150 | })
151 | })
152 | const { errors, data } = await res.json()
153 |
154 | if (errors) {
155 | console.error(errors)
156 | }
157 |
158 | console.log(data)
159 | ",
160 | }
161 | `;
162 |
163 | exports[`Generating a JavaScript:fetch snippet should generate the correct mutation snippet 5`] = `
164 | Object {
165 | "options": Object {
166 | "asyncAwait": false,
167 | "comments": true,
168 | "server": false,
169 | },
170 | "snippet": "
171 | const TEST_MUTATION = \`
172 |
173 | mutation testMutation {
174 | addData(id: \\"id\\") {
175 | id
176 | }
177 | }\`
178 |
179 | const serverUrl = \\"https://api.myservice.com/\\"
180 |
181 | fetch(serverUrl, {
182 | method: \\"POST\\",
183 | headers: {},
184 | body: JSON.stringify({
185 | query: TEST_MUTATION,
186 | variables: {}
187 | })
188 | })
189 | .then(res => res.json())
190 | .then(({ data, errors }) => {
191 | if (errors) {
192 | // handle OneGraph errors
193 | console.error(errors)
194 | }
195 |
196 | // do something with data
197 | console.log(data)
198 | })
199 | .catch(err => {
200 | // handle fetch error
201 | console.error(err)
202 | })
203 | ",
204 | }
205 | `;
206 |
207 | exports[`Generating a JavaScript:fetch snippet should generate the correct mutation snippet 6`] = `
208 | Object {
209 | "options": Object {
210 | "asyncAwait": true,
211 | "comments": true,
212 | "server": false,
213 | },
214 | "snippet": "
215 | const TEST_MUTATION = \`
216 |
217 | mutation testMutation {
218 | addData(id: \\"id\\") {
219 | id
220 | }
221 | }\`
222 |
223 | const serverUrl = \\"https://api.myservice.com/\\"
224 |
225 | const res = await fetch(serverUrl, {
226 | method: \\"POST\\",
227 | headers: {},
228 | body: JSON.stringify({
229 | query: TEST_MUTATION,
230 | variables: {}
231 | })
232 | })
233 | const { errors, data } = await res.json()
234 |
235 | if (errors) {
236 | // handle OneGraph errors
237 | console.error(errors)
238 | }
239 |
240 | // do something with data
241 | console.log(data)
242 | ",
243 | }
244 | `;
245 |
246 | exports[`Generating a JavaScript:fetch snippet should generate the correct mutation snippet 7`] = `
247 | Object {
248 | "options": Object {
249 | "asyncAwait": false,
250 | "comments": true,
251 | "server": true,
252 | },
253 | "snippet": "
254 | // Node doesn't implement fetch so we have to import it
255 | import fetch from \\"node-fetch\\"
256 |
257 | const TEST_MUTATION = \`
258 |
259 | mutation testMutation {
260 | addData(id: \\"id\\") {
261 | id
262 | }
263 | }\`
264 |
265 | const serverUrl = \\"https://api.myservice.com/\\"
266 |
267 | fetch(serverUrl, {
268 | method: \\"POST\\",
269 | headers: {},
270 | body: JSON.stringify({
271 | query: TEST_MUTATION,
272 | variables: {}
273 | })
274 | })
275 | .then(res => res.json())
276 | .then(({ data, errors }) => {
277 | if (errors) {
278 | // handle OneGraph errors
279 | console.error(errors)
280 | }
281 |
282 | // do something with data
283 | console.log(data)
284 | })
285 | .catch(err => {
286 | // handle fetch error
287 | console.error(err)
288 | })
289 | ",
290 | }
291 | `;
292 |
293 | exports[`Generating a JavaScript:fetch snippet should generate the correct mutation snippet 8`] = `
294 | Object {
295 | "options": Object {
296 | "asyncAwait": true,
297 | "comments": true,
298 | "server": true,
299 | },
300 | "snippet": "
301 | // Node doesn't implement fetch so we have to import it
302 | import fetch from \\"node-fetch\\"
303 |
304 | const TEST_MUTATION = \`
305 |
306 | mutation testMutation {
307 | addData(id: \\"id\\") {
308 | id
309 | }
310 | }\`
311 |
312 | const serverUrl = \\"https://api.myservice.com/\\"
313 |
314 | const res = await fetch(serverUrl, {
315 | method: \\"POST\\",
316 | headers: {},
317 | body: JSON.stringify({
318 | query: TEST_MUTATION,
319 | variables: {}
320 | })
321 | })
322 | const { errors, data } = await res.json()
323 |
324 | if (errors) {
325 | // handle OneGraph errors
326 | console.error(errors)
327 | }
328 |
329 | // do something with data
330 | console.log(data)
331 | ",
332 | }
333 | `;
334 |
335 | exports[`Generating a JavaScript:fetch snippet should generate the correct query snippet 1`] = `
336 | Object {
337 | "options": Object {
338 | "asyncAwait": false,
339 | "comments": false,
340 | "server": false,
341 | },
342 | "snippet": "
343 | const TEST_QUERY = \`
344 |
345 | query testQuery {
346 | someData {
347 | id
348 | }
349 | }
350 | \`
351 |
352 | const serverUrl = \\"https://api.myservice.com/\\"
353 |
354 | fetch(serverUrl, {
355 | method: \\"POST\\",
356 | headers: {},
357 | body: JSON.stringify({
358 | query: TEST_QUERY,
359 | variables: {}
360 | })
361 | })
362 | .then(res => res.json())
363 | .then(({ data, errors }) => {
364 | if (errors) {
365 | console.error(errors)
366 | }
367 |
368 | console.log(data)
369 | })
370 | .catch(err => {
371 | console.error(err)
372 | })
373 | ",
374 | }
375 | `;
376 |
377 | exports[`Generating a JavaScript:fetch snippet should generate the correct query snippet 2`] = `
378 | Object {
379 | "options": Object {
380 | "asyncAwait": true,
381 | "comments": false,
382 | "server": false,
383 | },
384 | "snippet": "
385 | const TEST_QUERY = \`
386 |
387 | query testQuery {
388 | someData {
389 | id
390 | }
391 | }
392 | \`
393 |
394 | const serverUrl = \\"https://api.myservice.com/\\"
395 |
396 | const res = await fetch(serverUrl, {
397 | method: \\"POST\\",
398 | headers: {},
399 | body: JSON.stringify({
400 | query: TEST_QUERY,
401 | variables: {}
402 | })
403 | })
404 | const { errors, data } = await res.json()
405 |
406 | if (errors) {
407 | console.error(errors)
408 | }
409 |
410 | console.log(data)
411 | ",
412 | }
413 | `;
414 |
415 | exports[`Generating a JavaScript:fetch snippet should generate the correct query snippet 3`] = `
416 | Object {
417 | "options": Object {
418 | "asyncAwait": false,
419 | "comments": false,
420 | "server": true,
421 | },
422 | "snippet": "
423 | import fetch from \\"node-fetch\\"
424 |
425 | const TEST_QUERY = \`
426 |
427 | query testQuery {
428 | someData {
429 | id
430 | }
431 | }
432 | \`
433 |
434 | const serverUrl = \\"https://api.myservice.com/\\"
435 |
436 | fetch(serverUrl, {
437 | method: \\"POST\\",
438 | headers: {},
439 | body: JSON.stringify({
440 | query: TEST_QUERY,
441 | variables: {}
442 | })
443 | })
444 | .then(res => res.json())
445 | .then(({ data, errors }) => {
446 | if (errors) {
447 | console.error(errors)
448 | }
449 |
450 | console.log(data)
451 | })
452 | .catch(err => {
453 | console.error(err)
454 | })
455 | ",
456 | }
457 | `;
458 |
459 | exports[`Generating a JavaScript:fetch snippet should generate the correct query snippet 4`] = `
460 | Object {
461 | "options": Object {
462 | "asyncAwait": true,
463 | "comments": false,
464 | "server": true,
465 | },
466 | "snippet": "
467 | import fetch from \\"node-fetch\\"
468 |
469 | const TEST_QUERY = \`
470 |
471 | query testQuery {
472 | someData {
473 | id
474 | }
475 | }
476 | \`
477 |
478 | const serverUrl = \\"https://api.myservice.com/\\"
479 |
480 | const res = await fetch(serverUrl, {
481 | method: \\"POST\\",
482 | headers: {},
483 | body: JSON.stringify({
484 | query: TEST_QUERY,
485 | variables: {}
486 | })
487 | })
488 | const { errors, data } = await res.json()
489 |
490 | if (errors) {
491 | console.error(errors)
492 | }
493 |
494 | console.log(data)
495 | ",
496 | }
497 | `;
498 |
499 | exports[`Generating a JavaScript:fetch snippet should generate the correct query snippet 5`] = `
500 | Object {
501 | "options": Object {
502 | "asyncAwait": false,
503 | "comments": true,
504 | "server": false,
505 | },
506 | "snippet": "
507 | const TEST_QUERY = \`
508 |
509 | query testQuery {
510 | someData {
511 | id
512 | }
513 | }
514 | \`
515 |
516 | const serverUrl = \\"https://api.myservice.com/\\"
517 |
518 | fetch(serverUrl, {
519 | method: \\"POST\\",
520 | headers: {},
521 | body: JSON.stringify({
522 | query: TEST_QUERY,
523 | variables: {}
524 | })
525 | })
526 | .then(res => res.json())
527 | .then(({ data, errors }) => {
528 | if (errors) {
529 | // handle OneGraph errors
530 | console.error(errors)
531 | }
532 |
533 | // do something with data
534 | console.log(data)
535 | })
536 | .catch(err => {
537 | // handle fetch error
538 | console.error(err)
539 | })
540 | ",
541 | }
542 | `;
543 |
544 | exports[`Generating a JavaScript:fetch snippet should generate the correct query snippet 6`] = `
545 | Object {
546 | "options": Object {
547 | "asyncAwait": true,
548 | "comments": true,
549 | "server": false,
550 | },
551 | "snippet": "
552 | const TEST_QUERY = \`
553 |
554 | query testQuery {
555 | someData {
556 | id
557 | }
558 | }
559 | \`
560 |
561 | const serverUrl = \\"https://api.myservice.com/\\"
562 |
563 | const res = await fetch(serverUrl, {
564 | method: \\"POST\\",
565 | headers: {},
566 | body: JSON.stringify({
567 | query: TEST_QUERY,
568 | variables: {}
569 | })
570 | })
571 | const { errors, data } = await res.json()
572 |
573 | if (errors) {
574 | // handle OneGraph errors
575 | console.error(errors)
576 | }
577 |
578 | // do something with data
579 | console.log(data)
580 | ",
581 | }
582 | `;
583 |
584 | exports[`Generating a JavaScript:fetch snippet should generate the correct query snippet 7`] = `
585 | Object {
586 | "options": Object {
587 | "asyncAwait": false,
588 | "comments": true,
589 | "server": true,
590 | },
591 | "snippet": "
592 | // Node doesn't implement fetch so we have to import it
593 | import fetch from \\"node-fetch\\"
594 |
595 | const TEST_QUERY = \`
596 |
597 | query testQuery {
598 | someData {
599 | id
600 | }
601 | }
602 | \`
603 |
604 | const serverUrl = \\"https://api.myservice.com/\\"
605 |
606 | fetch(serverUrl, {
607 | method: \\"POST\\",
608 | headers: {},
609 | body: JSON.stringify({
610 | query: TEST_QUERY,
611 | variables: {}
612 | })
613 | })
614 | .then(res => res.json())
615 | .then(({ data, errors }) => {
616 | if (errors) {
617 | // handle OneGraph errors
618 | console.error(errors)
619 | }
620 |
621 | // do something with data
622 | console.log(data)
623 | })
624 | .catch(err => {
625 | // handle fetch error
626 | console.error(err)
627 | })
628 | ",
629 | }
630 | `;
631 |
632 | exports[`Generating a JavaScript:fetch snippet should generate the correct query snippet 8`] = `
633 | Object {
634 | "options": Object {
635 | "asyncAwait": true,
636 | "comments": true,
637 | "server": true,
638 | },
639 | "snippet": "
640 | // Node doesn't implement fetch so we have to import it
641 | import fetch from \\"node-fetch\\"
642 |
643 | const TEST_QUERY = \`
644 |
645 | query testQuery {
646 | someData {
647 | id
648 | }
649 | }
650 | \`
651 |
652 | const serverUrl = \\"https://api.myservice.com/\\"
653 |
654 | const res = await fetch(serverUrl, {
655 | method: \\"POST\\",
656 | headers: {},
657 | body: JSON.stringify({
658 | query: TEST_QUERY,
659 | variables: {}
660 | })
661 | })
662 | const { errors, data } = await res.json()
663 |
664 | if (errors) {
665 | // handle OneGraph errors
666 | console.error(errors)
667 | }
668 |
669 | // do something with data
670 | console.log(data)
671 | ",
672 | }
673 | `;
674 |
--------------------------------------------------------------------------------
/src/snippets/javascript/__tests__/__snapshots__/reactApollo-test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Generating a JavaScript:react-apollo snippet should generate the correct mutation snippet 1`] = `
4 | Object {
5 | "options": Object {
6 | "client": false,
7 | "reactNative": false,
8 | },
9 | "snippet": "
10 | import gql from \\"graphql-tag\\"
11 | import { Mutation } from \\"react-apollo\\"
12 |
13 | const TEST_MUTATION = gql\`
14 |
15 | mutation testMutation {
16 | addData(id: \\"id\\") {
17 | id
18 | }
19 | }\`
20 |
21 | function TestMutation() {
22 | return (
23 |
27 | {(testMutation, { loading, error, data }) => {
28 | if (loading) return Loading
29 | if (error) return Error
30 |
31 | // call testMutation() to run the mutation
32 | return (
33 |
40 | )
41 | }}
42 |
43 | )
44 | }
45 | ",
46 | }
47 | `;
48 |
49 | exports[`Generating a JavaScript:react-apollo snippet should generate the correct mutation snippet 2`] = `
50 | Object {
51 | "options": Object {
52 | "client": false,
53 | "reactNative": false,
54 | },
55 | "snippet": "
56 | import gql from \\"graphql-tag\\"
57 | import { Mutation } from \\"react-apollo\\"
58 |
59 | const TEST_MUTATION = gql\`
60 |
61 | mutation testMutation {
62 | addData(id: \\"id\\") {
63 | id
64 | }
65 | }\`
66 |
67 | function TestMutation() {
68 | return (
69 |
73 | {(testMutation, { loading, error, data }) => {
74 | if (loading) return Loading
75 | if (error) return Error
76 |
77 | // call testMutation() to run the mutation
78 | return (
79 |
86 | )
87 | }}
88 |
89 | )
90 | }
91 | ",
92 | }
93 | `;
94 |
95 | exports[`Generating a JavaScript:react-apollo snippet should generate the correct mutation snippet 3`] = `
96 | Object {
97 | "options": Object {
98 | "client": false,
99 | "reactNative": true,
100 | },
101 | "snippet": "
102 | import gql from \\"graphql-tag\\"
103 | import { Mutation } from \\"react-apollo\\"
104 | import { View } from \\"react-native\\"
105 |
106 | const TEST_MUTATION = gql\`
107 |
108 | mutation testMutation {
109 | addData(id: \\"id\\") {
110 | id
111 | }
112 | }\`
113 |
114 | function TestMutation() {
115 | return (
116 |
120 | {(testMutation, { loading, error, data }) => {
121 | if (loading) return Loading
122 | if (error) return Error
123 |
124 | // call testMutation() to run the mutation
125 | return (
126 |
133 | )
134 | }}
135 |
136 | )
137 | }
138 | ",
139 | }
140 | `;
141 |
142 | exports[`Generating a JavaScript:react-apollo snippet should generate the correct mutation snippet 4`] = `
143 | Object {
144 | "options": Object {
145 | "client": false,
146 | "reactNative": true,
147 | },
148 | "snippet": "
149 | import gql from \\"graphql-tag\\"
150 | import { Mutation } from \\"react-apollo\\"
151 | import { View } from \\"react-native\\"
152 |
153 | const TEST_MUTATION = gql\`
154 |
155 | mutation testMutation {
156 | addData(id: \\"id\\") {
157 | id
158 | }
159 | }\`
160 |
161 | function TestMutation() {
162 | return (
163 |
167 | {(testMutation, { loading, error, data }) => {
168 | if (loading) return Loading
169 | if (error) return Error
170 |
171 | // call testMutation() to run the mutation
172 | return (
173 |
180 | )
181 | }}
182 |
183 | )
184 | }
185 | ",
186 | }
187 | `;
188 |
189 | exports[`Generating a JavaScript:react-apollo snippet should generate the correct query snippet 1`] = `
190 | Object {
191 | "options": Object {
192 | "client": false,
193 | "reactNative": false,
194 | },
195 | "snippet": "
196 | import gql from \\"graphql-tag\\"
197 | import { Query } from \\"react-apollo\\"
198 |
199 | const TEST_QUERY = gql\`
200 |
201 | query testQuery {
202 | someData {
203 | id
204 | }
205 | }
206 | \`
207 |
208 | function TestQuery() {
209 | return (
210 |
215 | {({ loading, error, data }) => {
216 | if (loading) return Loading
217 | if (error) return Error
218 |
219 | if (data) {
220 | return (
221 | {JSON.stringify(data, null, 2)}
222 | )
223 | }
224 | }}
225 |
226 | )
227 | }
228 | ",
229 | }
230 | `;
231 |
232 | exports[`Generating a JavaScript:react-apollo snippet should generate the correct query snippet 2`] = `
233 | Object {
234 | "options": Object {
235 | "client": false,
236 | "reactNative": false,
237 | },
238 | "snippet": "
239 | import gql from \\"graphql-tag\\"
240 | import { Query } from \\"react-apollo\\"
241 |
242 | const TEST_QUERY = gql\`
243 |
244 | query testQuery {
245 | someData {
246 | id
247 | }
248 | }
249 | \`
250 |
251 | function TestQuery() {
252 | return (
253 |
258 | {({ loading, error, data }) => {
259 | if (loading) return Loading
260 | if (error) return Error
261 |
262 | if (data) {
263 | return (
264 | {JSON.stringify(data, null, 2)}
265 | )
266 | }
267 | }}
268 |
269 | )
270 | }
271 | ",
272 | }
273 | `;
274 |
275 | exports[`Generating a JavaScript:react-apollo snippet should generate the correct query snippet 3`] = `
276 | Object {
277 | "options": Object {
278 | "client": false,
279 | "reactNative": true,
280 | },
281 | "snippet": "
282 | import gql from \\"graphql-tag\\"
283 | import { Query } from \\"react-apollo\\"
284 | import { View } from \\"react-native\\"
285 |
286 | const TEST_QUERY = gql\`
287 |
288 | query testQuery {
289 | someData {
290 | id
291 | }
292 | }
293 | \`
294 |
295 | function TestQuery() {
296 | return (
297 |
302 | {({ loading, error, data }) => {
303 | if (loading) return Loading
304 | if (error) return Error
305 |
306 | if (data) {
307 | return (
308 |
309 | {JSON.stringify(data, null, 2)}
310 |
311 | )
312 | }
313 | }}
314 |
315 | )
316 | }
317 | ",
318 | }
319 | `;
320 |
321 | exports[`Generating a JavaScript:react-apollo snippet should generate the correct query snippet 4`] = `
322 | Object {
323 | "options": Object {
324 | "client": false,
325 | "reactNative": true,
326 | },
327 | "snippet": "
328 | import gql from \\"graphql-tag\\"
329 | import { Query } from \\"react-apollo\\"
330 | import { View } from \\"react-native\\"
331 |
332 | const TEST_QUERY = gql\`
333 |
334 | query testQuery {
335 | someData {
336 | id
337 | }
338 | }
339 | \`
340 |
341 | function TestQuery() {
342 | return (
343 |
348 | {({ loading, error, data }) => {
349 | if (loading) return Loading
350 | if (error) return Error
351 |
352 | if (data) {
353 | return (
354 |
355 | {JSON.stringify(data, null, 2)}
356 |
357 | )
358 | }
359 | }}
360 |
361 | )
362 | }
363 | ",
364 | }
365 | `;
366 |
--------------------------------------------------------------------------------
/src/snippets/javascript/__tests__/__snapshots__/reactHooks-test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Generating a JavaScript:react-hooks snippet should generate the correct mutation snippet 1`] = `
4 | Object {
5 | "options": Object {
6 | "comments": false,
7 | "reactNative": false,
8 | },
9 | "snippet": "
10 | import React, { useState, useEffect } from \\"react\\"
11 |
12 | const TEST_MUTATION = \`
13 |
14 | mutation testMutation {
15 | addData(id: \\"id\\") {
16 | id
17 | }
18 | }\`
19 |
20 | const serverUrl = \\"https://api.myservice.com/\\"
21 |
22 | function TestMutation() {
23 | const [loading, setLoading] = useState(false)
24 | const [errors, setErrors] = useState([])
25 | const [data, setData] = useState(null)
26 |
27 | useEffect(() => {
28 | setLoading(true)
29 |
30 | fetch(serverUrl, {
31 | method: \\"POST\\",
32 | headers: {},
33 | body: JSON.stringify({
34 | query: TEST_MUTATION,
35 | variables: {}
36 | })
37 | })
38 | .then(res => res.json())
39 | .then(({ data, errors }) => {
40 | if (errors) {
41 | setErrors(errors)
42 | }
43 |
44 | setData(data)
45 | })
46 | .catch(err => setErrors([err]))
47 | .finally(() => setLoading(false))
48 | }, [])
49 |
50 | if (loading) return Loading
51 | if (errors.length > 0)
52 | return {JSON.stringify(errors)}
53 |
54 | return {JSON.stringify(data, null, 2)}
55 | }
56 | ",
57 | }
58 | `;
59 |
60 | exports[`Generating a JavaScript:react-hooks snippet should generate the correct mutation snippet 2`] = `
61 | Object {
62 | "options": Object {
63 | "comments": false,
64 | "reactNative": false,
65 | },
66 | "snippet": "
67 | import React, { useState, useEffect } from \\"react\\"
68 |
69 | const TEST_MUTATION = \`
70 |
71 | mutation testMutation {
72 | addData(id: \\"id\\") {
73 | id
74 | }
75 | }\`
76 |
77 | const serverUrl = \\"https://api.myservice.com/\\"
78 |
79 | function TestMutation() {
80 | const [loading, setLoading] = useState(false)
81 | const [errors, setErrors] = useState([])
82 | const [data, setData] = useState(null)
83 |
84 | useEffect(() => {
85 | setLoading(true)
86 |
87 | fetch(serverUrl, {
88 | method: \\"POST\\",
89 | headers: {},
90 | body: JSON.stringify({
91 | query: TEST_MUTATION,
92 | variables: {}
93 | })
94 | })
95 | .then(res => res.json())
96 | .then(({ data, errors }) => {
97 | if (errors) {
98 | setErrors(errors)
99 | }
100 |
101 | setData(data)
102 | })
103 | .catch(err => setErrors([err]))
104 | .finally(() => setLoading(false))
105 | }, [])
106 |
107 | if (loading) return Loading
108 | if (errors.length > 0)
109 | return {JSON.stringify(errors)}
110 |
111 | return {JSON.stringify(data, null, 2)}
112 | }
113 | ",
114 | }
115 | `;
116 |
117 | exports[`Generating a JavaScript:react-hooks snippet should generate the correct mutation snippet 3`] = `
118 | Object {
119 | "options": Object {
120 | "comments": false,
121 | "reactNative": true,
122 | },
123 | "snippet": "
124 | import React, { useState, useEffect } from \\"react\\"
125 | import { View } from \\"react-native\\"
126 |
127 | const TEST_MUTATION = \`
128 |
129 | mutation testMutation {
130 | addData(id: \\"id\\") {
131 | id
132 | }
133 | }\`
134 |
135 | const serverUrl = \\"https://api.myservice.com/\\"
136 |
137 | function TestMutation() {
138 | const [loading, setLoading] = useState(false)
139 | const [errors, setErrors] = useState([])
140 | const [data, setData] = useState(null)
141 |
142 | useEffect(() => {
143 | setLoading(true)
144 |
145 | fetch(serverUrl, {
146 | method: \\"POST\\",
147 | headers: {},
148 | body: JSON.stringify({
149 | query: TEST_MUTATION,
150 | variables: {}
151 | })
152 | })
153 | .then(res => res.json())
154 | .then(({ data, errors }) => {
155 | if (errors) {
156 | setErrors(errors)
157 | }
158 |
159 | setData(data)
160 | })
161 | .catch(err => setErrors([err]))
162 | .finally(() => setLoading(false))
163 | }, [])
164 |
165 | if (loading) return Loading
166 | if (errors.length > 0)
167 | return {JSON.stringify(errors)}
168 |
169 | return {JSON.stringify(data, null, 2)}
170 | }
171 | ",
172 | }
173 | `;
174 |
175 | exports[`Generating a JavaScript:react-hooks snippet should generate the correct mutation snippet 4`] = `
176 | Object {
177 | "options": Object {
178 | "comments": false,
179 | "reactNative": true,
180 | },
181 | "snippet": "
182 | import React, { useState, useEffect } from \\"react\\"
183 | import { View } from \\"react-native\\"
184 |
185 | const TEST_MUTATION = \`
186 |
187 | mutation testMutation {
188 | addData(id: \\"id\\") {
189 | id
190 | }
191 | }\`
192 |
193 | const serverUrl = \\"https://api.myservice.com/\\"
194 |
195 | function TestMutation() {
196 | const [loading, setLoading] = useState(false)
197 | const [errors, setErrors] = useState([])
198 | const [data, setData] = useState(null)
199 |
200 | useEffect(() => {
201 | setLoading(true)
202 |
203 | fetch(serverUrl, {
204 | method: \\"POST\\",
205 | headers: {},
206 | body: JSON.stringify({
207 | query: TEST_MUTATION,
208 | variables: {}
209 | })
210 | })
211 | .then(res => res.json())
212 | .then(({ data, errors }) => {
213 | if (errors) {
214 | setErrors(errors)
215 | }
216 |
217 | setData(data)
218 | })
219 | .catch(err => setErrors([err]))
220 | .finally(() => setLoading(false))
221 | }, [])
222 |
223 | if (loading) return Loading
224 | if (errors.length > 0)
225 | return {JSON.stringify(errors)}
226 |
227 | return {JSON.stringify(data, null, 2)}
228 | }
229 | ",
230 | }
231 | `;
232 |
233 | exports[`Generating a JavaScript:react-hooks snippet should generate the correct query snippet 1`] = `
234 | Object {
235 | "options": Object {
236 | "comments": false,
237 | "reactNative": false,
238 | },
239 | "snippet": "
240 | import React, { useState, useEffect } from \\"react\\"
241 |
242 | const TEST_QUERY = \`
243 |
244 | query testQuery {
245 | someData {
246 | id
247 | }
248 | }
249 | \`
250 |
251 | const serverUrl = \\"https://api.myservice.com/\\"
252 |
253 | function TestQuery() {
254 | const [loading, setLoading] = useState(false)
255 | const [errors, setErrors] = useState([])
256 | const [data, setData] = useState(null)
257 |
258 | useEffect(() => {
259 | setLoading(true)
260 |
261 | fetch(serverUrl, {
262 | method: \\"POST\\",
263 | headers: {},
264 | body: JSON.stringify({
265 | query: TEST_QUERY,
266 | variables: {}
267 | })
268 | })
269 | .then(res => res.json())
270 | .then(({ data, errors }) => {
271 | if (errors) {
272 | setErrors(errors)
273 | }
274 |
275 | setData(data)
276 | })
277 | .catch(err => setErrors([err]))
278 | .finally(() => setLoading(false))
279 | }, [])
280 |
281 | if (loading) return Loading
282 | if (errors.length > 0)
283 | return {JSON.stringify(errors)}
284 |
285 | return {JSON.stringify(data, null, 2)}
286 | }
287 | ",
288 | }
289 | `;
290 |
291 | exports[`Generating a JavaScript:react-hooks snippet should generate the correct query snippet 2`] = `
292 | Object {
293 | "options": Object {
294 | "comments": false,
295 | "reactNative": false,
296 | },
297 | "snippet": "
298 | import React, { useState, useEffect } from \\"react\\"
299 |
300 | const TEST_QUERY = \`
301 |
302 | query testQuery {
303 | someData {
304 | id
305 | }
306 | }
307 | \`
308 |
309 | const serverUrl = \\"https://api.myservice.com/\\"
310 |
311 | function TestQuery() {
312 | const [loading, setLoading] = useState(false)
313 | const [errors, setErrors] = useState([])
314 | const [data, setData] = useState(null)
315 |
316 | useEffect(() => {
317 | setLoading(true)
318 |
319 | fetch(serverUrl, {
320 | method: \\"POST\\",
321 | headers: {},
322 | body: JSON.stringify({
323 | query: TEST_QUERY,
324 | variables: {}
325 | })
326 | })
327 | .then(res => res.json())
328 | .then(({ data, errors }) => {
329 | if (errors) {
330 | setErrors(errors)
331 | }
332 |
333 | setData(data)
334 | })
335 | .catch(err => setErrors([err]))
336 | .finally(() => setLoading(false))
337 | }, [])
338 |
339 | if (loading) return Loading
340 | if (errors.length > 0)
341 | return {JSON.stringify(errors)}
342 |
343 | return {JSON.stringify(data, null, 2)}
344 | }
345 | ",
346 | }
347 | `;
348 |
349 | exports[`Generating a JavaScript:react-hooks snippet should generate the correct query snippet 3`] = `
350 | Object {
351 | "options": Object {
352 | "comments": false,
353 | "reactNative": true,
354 | },
355 | "snippet": "
356 | import React, { useState, useEffect } from \\"react\\"
357 | import { View } from \\"react-native\\"
358 |
359 | const TEST_QUERY = \`
360 |
361 | query testQuery {
362 | someData {
363 | id
364 | }
365 | }
366 | \`
367 |
368 | const serverUrl = \\"https://api.myservice.com/\\"
369 |
370 | function TestQuery() {
371 | const [loading, setLoading] = useState(false)
372 | const [errors, setErrors] = useState([])
373 | const [data, setData] = useState(null)
374 |
375 | useEffect(() => {
376 | setLoading(true)
377 |
378 | fetch(serverUrl, {
379 | method: \\"POST\\",
380 | headers: {},
381 | body: JSON.stringify({
382 | query: TEST_QUERY,
383 | variables: {}
384 | })
385 | })
386 | .then(res => res.json())
387 | .then(({ data, errors }) => {
388 | if (errors) {
389 | setErrors(errors)
390 | }
391 |
392 | setData(data)
393 | })
394 | .catch(err => setErrors([err]))
395 | .finally(() => setLoading(false))
396 | }, [])
397 |
398 | if (loading) return Loading
399 | if (errors.length > 0)
400 | return {JSON.stringify(errors)}
401 |
402 | return {JSON.stringify(data, null, 2)}
403 | }
404 | ",
405 | }
406 | `;
407 |
408 | exports[`Generating a JavaScript:react-hooks snippet should generate the correct query snippet 4`] = `
409 | Object {
410 | "options": Object {
411 | "comments": false,
412 | "reactNative": true,
413 | },
414 | "snippet": "
415 | import React, { useState, useEffect } from \\"react\\"
416 | import { View } from \\"react-native\\"
417 |
418 | const TEST_QUERY = \`
419 |
420 | query testQuery {
421 | someData {
422 | id
423 | }
424 | }
425 | \`
426 |
427 | const serverUrl = \\"https://api.myservice.com/\\"
428 |
429 | function TestQuery() {
430 | const [loading, setLoading] = useState(false)
431 | const [errors, setErrors] = useState([])
432 | const [data, setData] = useState(null)
433 |
434 | useEffect(() => {
435 | setLoading(true)
436 |
437 | fetch(serverUrl, {
438 | method: \\"POST\\",
439 | headers: {},
440 | body: JSON.stringify({
441 | query: TEST_QUERY,
442 | variables: {}
443 | })
444 | })
445 | .then(res => res.json())
446 | .then(({ data, errors }) => {
447 | if (errors) {
448 | setErrors(errors)
449 | }
450 |
451 | setData(data)
452 | })
453 | .catch(err => setErrors([err]))
454 | .finally(() => setLoading(false))
455 | }, [])
456 |
457 | if (loading) return Loading
458 | if (errors.length > 0)
459 | return {JSON.stringify(errors)}
460 |
461 | return {JSON.stringify(data, null, 2)}
462 | }
463 | ",
464 | }
465 | `;
466 |
--------------------------------------------------------------------------------
/src/snippets/javascript/__tests__/fetch-test.js:
--------------------------------------------------------------------------------
1 | import snippetObject from '../fetch';
2 |
3 | import getOptionCombinations from '../../__helpers__/getOptionCombinations';
4 |
5 | const {options, generate} = snippetObject;
6 | const optionCombinations = getOptionCombinations(options);
7 |
8 | const testQuery = `
9 | query testQuery {
10 | someData {
11 | id
12 | }
13 | }
14 | `;
15 |
16 | const testMutation = `
17 | mutation testMutation {
18 | addData(id: "id") {
19 | id
20 | }
21 | }`;
22 |
23 | describe('Generating a JavaScript:fetch snippet', () => {
24 | it('should generate the correct query snippet', () => {
25 | optionCombinations.forEach(options => {
26 | const snippet = generate({
27 | headers: {},
28 | variables: {},
29 | serverUrl: 'https://api.myservice.com/',
30 | operation: testQuery,
31 | operationType: 'query',
32 | variableName: 'TEST_QUERY',
33 | operationName: 'testQuery',
34 | options,
35 | });
36 |
37 | expect({
38 | options,
39 | snippet: '\n' + snippet,
40 | }).toMatchSnapshot();
41 | });
42 | });
43 |
44 | it('should generate the correct mutation snippet', () => {
45 | optionCombinations.forEach(options => {
46 | const snippet = generate({
47 | headers: {},
48 | variables: {},
49 | serverUrl: 'https://api.myservice.com/',
50 | operation: testMutation,
51 | operationType: 'mutation',
52 | variableName: 'TEST_MUTATION',
53 | operationName: 'testMutation',
54 | options,
55 | });
56 |
57 | expect({
58 | options,
59 | snippet: '\n' + snippet,
60 | }).toMatchSnapshot();
61 | });
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/src/snippets/javascript/__tests__/reactApollo-test.js:
--------------------------------------------------------------------------------
1 | import snippetObject from '../reactApollo';
2 |
3 | import getOptionCombinations from '../../__helpers__/getOptionCombinations';
4 |
5 | const {options, generate} = snippetObject;
6 | const optionCombinations = getOptionCombinations(options);
7 |
8 | const testQuery = `
9 | query testQuery {
10 | someData {
11 | id
12 | }
13 | }
14 | `;
15 |
16 | const testMutation = `
17 | mutation testMutation {
18 | addData(id: "id") {
19 | id
20 | }
21 | }`;
22 |
23 | describe('Generating a JavaScript:react-apollo snippet', () => {
24 | it('should generate the correct query snippet', () => {
25 | optionCombinations.forEach(options => {
26 | const snippet = generate({
27 | headers: {},
28 | variables: {},
29 | serverUrl: 'https://api.myservice.com/',
30 | operation: testQuery,
31 | operationType: 'query',
32 | variableName: 'TEST_QUERY',
33 | operationName: 'testQuery',
34 | options,
35 | });
36 |
37 | expect({
38 | options,
39 | snippet: '\n' + snippet,
40 | }).toMatchSnapshot();
41 | });
42 | });
43 |
44 | it('should generate the correct mutation snippet', () => {
45 | optionCombinations.forEach(options => {
46 | const snippet = generate({
47 | headers: {},
48 | variables: {},
49 | serverUrl: 'https://api.myservice.com/',
50 | operation: testMutation,
51 | operationType: 'mutation',
52 | variableName: 'TEST_MUTATION',
53 | operationName: 'testMutation',
54 | options,
55 | });
56 |
57 | expect({
58 | options,
59 | snippet: '\n' + snippet,
60 | }).toMatchSnapshot();
61 | });
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/src/snippets/javascript/__tests__/reactHooks-test.js:
--------------------------------------------------------------------------------
1 | import snippetObject from '../reactHooks';
2 |
3 | import getOptionCombinations from '../../__helpers__/getOptionCombinations';
4 |
5 | const {options, generate} = snippetObject;
6 | const optionCombinations = getOptionCombinations(options);
7 |
8 | const testQuery = `
9 | query testQuery {
10 | someData {
11 | id
12 | }
13 | }
14 | `;
15 |
16 | const testMutation = `
17 | mutation testMutation {
18 | addData(id: "id") {
19 | id
20 | }
21 | }`;
22 |
23 | describe('Generating a JavaScript:react-hooks snippet', () => {
24 | it('should generate the correct query snippet', () => {
25 | optionCombinations.forEach(options => {
26 | const snippet = generate({
27 | headers: {},
28 | variables: {},
29 | serverUrl: 'https://api.myservice.com/',
30 | operation: testQuery,
31 | operationType: 'query',
32 | variableName: 'TEST_QUERY',
33 | operationName: 'testQuery',
34 | options,
35 | });
36 |
37 | expect({
38 | options,
39 | snippet: '\n' + snippet,
40 | }).toMatchSnapshot();
41 | });
42 | });
43 |
44 | it('should generate the correct mutation snippet', () => {
45 | optionCombinations.forEach(options => {
46 | const snippet = generate({
47 | headers: {},
48 | variables: {},
49 | serverUrl: 'https://api.myservice.com/',
50 | operation: testMutation,
51 | operationType: 'mutation',
52 | variableName: 'TEST_MUTATION',
53 | operationName: 'testMutation',
54 | options,
55 | });
56 |
57 | expect({
58 | options,
59 | snippet: '\n' + snippet,
60 | }).toMatchSnapshot();
61 | });
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/src/snippets/javascript/fetch.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import capitalizeFirstLetter from '../../utils/capitalizeFirstLetter';
4 | import commentsFactory from '../../utils/jsCommentsFactory.js';
5 | import {
6 | isOperationNamed,
7 | collapseExtraNewlines,
8 | addLeftWhitespace,
9 | } from '../../utils';
10 |
11 | import 'codemirror/mode/javascript/javascript';
12 |
13 | import type {Snippet, OperationData} from '../../index.js';
14 |
15 | const snippetOptions = [
16 | {
17 | id: 'server',
18 | label: 'server-side usage',
19 | initial: false,
20 | },
21 | {
22 | id: 'asyncAwait',
23 | label: 'async/await',
24 | initial: true,
25 | },
26 | ];
27 |
28 | const comments = {
29 | setup: `This setup is only needed once per application`,
30 | nodeFetch: `Node doesn't implement fetch so we have to import it`,
31 | graphqlError: `handle those errors like a pro`,
32 | graphqlData: `do something great with this precious data`,
33 | fetchError: `handle errors from fetch itself`,
34 | };
35 |
36 | function generateDocumentQuery(
37 | operationDataList: Array,
38 | ): string {
39 | const body = operationDataList
40 | .map(operationData => operationData.query)
41 | .join('\n\n')
42 | .trim();
43 |
44 | return `const operationsDoc = \`
45 | ${addLeftWhitespace(body, 2)}
46 | \`;`;
47 | }
48 |
49 | const fetcherName = 'fetchGraphQL';
50 |
51 | function operationFunctionName(operationData: OperationData) {
52 | const {type} = operationData;
53 |
54 | const prefix =
55 | type === 'query'
56 | ? 'fetch'
57 | : type === 'mutation'
58 | ? 'execute'
59 | : type === 'subscription'
60 | ? 'subscribeTo'
61 | : '';
62 |
63 | const fnName =
64 | prefix +
65 | (prefix.length > 0
66 | ? capitalizeFirstLetter(operationData.name)
67 | : operationData.name);
68 |
69 | return fnName;
70 | }
71 |
72 | // Promise-based functions
73 | function promiseFetcher(serverUrl: string, headers: string): string {
74 | return `function ${fetcherName}(operationsDoc, operationName, variables) {
75 | return fetch(
76 | "${serverUrl}",
77 | {
78 | method: "POST",${
79 | headers
80 | ? `
81 | headers: {
82 | ${addLeftWhitespace(headers, 8)}
83 | },`
84 | : ''
85 | }
86 | body: JSON.stringify({
87 | query: operationsDoc,
88 | variables: variables,
89 | operationName: operationName
90 | })
91 | }
92 | ).then((result) => result.json());
93 | }`;
94 | }
95 |
96 | function fetcherFunctions(operationDataList: Array): string {
97 | return operationDataList
98 | .map(operationData => {
99 | const fnName = operationFunctionName(operationData);
100 | const params = (
101 | operationData.operationDefinition.variableDefinitions || []
102 | ).map(def => def.variable.name.value);
103 | const variablesBody = params
104 | .map(param => `"${param}": ${param}`)
105 | .join(', ');
106 | const variables = `{${variablesBody}}`;
107 | return `function ${fnName}(${params.join(', ')}) {
108 | return ${fetcherName}(
109 | operationsDoc,
110 | "${operationData.name}",
111 | ${variables}
112 | );
113 | }`;
114 | })
115 | .join('\n\n');
116 | }
117 |
118 | function promiseFetcherInvocation(
119 | getComment,
120 | operationDataList: Array,
121 | vars,
122 | ): string {
123 | return operationDataList
124 | .map(namedOperationData => {
125 | const params = (
126 | namedOperationData.operationDefinition.variableDefinitions || []
127 | ).map(def => def.variable.name.value);
128 | const variables = Object.entries(namedOperationData.variables || {}).map(
129 | ([key, value]) => `const ${key} = ${JSON.stringify(value, null, 2)};`,
130 | );
131 | return `${variables.join('\n')}
132 |
133 | ${operationFunctionName(namedOperationData)}(${params.join(', ')})
134 | .then(({ data, errors }) => {
135 | if (errors) {
136 | ${getComment('graphqlError')}
137 | console.error(errors);
138 | }
139 | ${getComment('graphqlData')}
140 | console.log(data);
141 | })
142 | .catch((error) => {
143 | ${getComment('fetchError')}
144 | console.error(error);
145 | });`;
146 | })
147 | .join('\n\n');
148 | }
149 |
150 | // Async-await-based functions
151 | function asyncFetcher(serverUrl: string, headers: string): string {
152 | return `async function ${fetcherName}(operationsDoc, operationName, variables) {
153 | const result = await fetch(
154 | "${serverUrl}",
155 | {
156 | method: "POST",${
157 | headers
158 | ? `
159 | headers: {
160 | ${addLeftWhitespace(headers, 8)}
161 | },`
162 | : ''
163 | }
164 | body: JSON.stringify({
165 | query: operationsDoc,
166 | variables: variables,
167 | operationName: operationName
168 | })
169 | }
170 | );
171 |
172 | return await result.json();
173 | }`;
174 | }
175 |
176 | function asyncFetcherInvocation(
177 | getComment,
178 | operationDataList: Array,
179 | vars,
180 | ): string {
181 | return operationDataList
182 | .map(namedOperationData => {
183 | const params = (
184 | namedOperationData.operationDefinition.variableDefinitions || []
185 | ).map(def => def.variable.name.value);
186 | const variables = Object.entries(namedOperationData.variables || {}).map(
187 | ([key, value]) => `const ${key} = ${JSON.stringify(value, null, 2)};`,
188 | );
189 | return `async function start${capitalizeFirstLetter(
190 | operationFunctionName(namedOperationData),
191 | )}(${params.join(', ')}) {
192 | const { errors, data } = await ${operationFunctionName(
193 | namedOperationData,
194 | )}(${params.join(', ')});
195 |
196 | if (errors) {
197 | ${getComment('graphqlError')}
198 | console.error(errors);
199 | }
200 |
201 | ${getComment('graphqlData')}
202 | console.log(data);
203 | }
204 |
205 | ${variables.join('\n')}
206 |
207 | start${capitalizeFirstLetter(
208 | operationFunctionName(namedOperationData),
209 | )}(${params.join(', ')});`;
210 | })
211 | .join('\n\n');
212 | }
213 |
214 | // Snippet generation!
215 | const snippet: Snippet = {
216 | language: 'JavaScript',
217 | codeMirrorMode: 'javascript',
218 | name: 'fetch',
219 | options: snippetOptions,
220 | generate: opts => {
221 | const {serverUrl, headers, options} = opts;
222 |
223 | const operationDataList = opts.operationDataList.map(
224 | (operationData, idx) => {
225 | if (!isOperationNamed(operationData)) {
226 | return {
227 | ...operationData,
228 | name: `unnamed${capitalizeFirstLetter(operationData.type)}${idx +
229 | 1}`.trim(),
230 | query:
231 | `# Consider giving this ${
232 | operationData.type
233 | } a unique, descriptive
234 | # name in your application as a best practice
235 | ${operationData.type} unnamed${capitalizeFirstLetter(operationData.type)}${idx +
236 | 1} ` +
237 | operationData.query
238 | .trim()
239 | .replace(/^(query|mutation|subscription) /i, ''),
240 | };
241 | } else {
242 | return operationData;
243 | }
244 | },
245 | );
246 |
247 | const getComment = commentsFactory(true, comments);
248 |
249 | const serverComment = options.server ? getComment('nodeFetch') : '';
250 | const serverImport = options.server
251 | ? `import fetch from "node-fetch";\n`
252 | : '';
253 |
254 | const graphqlQuery = generateDocumentQuery(operationDataList);
255 | const vars = JSON.stringify({}, null, 2);
256 | const headersValues = [];
257 | for (const header of Object.keys(headers)) {
258 | if (header && headers[header]) {
259 | headersValues.push(`"${header}": "${headers[header]}"`);
260 | }
261 | }
262 | const heads = headersValues.length ? `${headersValues.join(',\n')}` : '';
263 |
264 | const requiredDeps = [
265 | options.server ? '"node-fetch": "^2.5.0"' : null,
266 | ].filter(Boolean);
267 |
268 | const packageDeps =
269 | requiredDeps.length > 0
270 | ? `/*
271 | Add these to your \`package.json\`:
272 | ${addLeftWhitespace(requiredDeps.join(',\n'), 2)}
273 | */
274 | `
275 | : '';
276 |
277 | const fetcher = options.asyncAwait
278 | ? asyncFetcher(serverUrl, heads)
279 | : promiseFetcher(serverUrl, heads);
280 |
281 | const fetcherFunctionsDefs = fetcherFunctions(operationDataList);
282 |
283 | const fetcherInvocation = options.asyncAwait
284 | ? asyncFetcherInvocation(getComment, operationDataList, vars)
285 | : promiseFetcherInvocation(getComment, operationDataList, vars);
286 |
287 | const snippet = `
288 | /*
289 | This is an example snippet - you should consider tailoring it
290 | to your service.
291 | */
292 | ${packageDeps}
293 | ${serverComment}
294 | ${serverImport}
295 |
296 | ${fetcher}
297 |
298 | ${graphqlQuery}
299 |
300 | ${fetcherFunctionsDefs}
301 |
302 | ${fetcherInvocation}`;
303 |
304 | return collapseExtraNewlines(snippet.trim());
305 | },
306 | };
307 |
308 | export default snippet;
309 |
--------------------------------------------------------------------------------
/src/snippets/javascript/reactApollo.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import capitalizeFirstLetter from '../../utils/capitalizeFirstLetter';
4 | import commentsFactory from '../../utils/jsCommentsFactory.js';
5 | import {
6 | distinct,
7 | isOperationNamed,
8 | collapseExtraNewlines,
9 | addLeftWhitespace,
10 | } from '../../utils/index.js';
11 |
12 | import 'codemirror/mode/jsx/jsx';
13 |
14 | import type {Snippet, OperationData} from '../../index.js';
15 |
16 | const comments = {
17 | setup: `This setup is only needed once per application`,
18 | };
19 |
20 | function formatVariableName(operationData: OperationData): string {
21 | const {name} = operationData;
22 | return (
23 | name.charAt(0).toUpperCase() +
24 | name
25 | .slice(1)
26 | .replace(/[A-Z]/g, '_$&')
27 | .toUpperCase()
28 | );
29 | }
30 |
31 | function operationVariableName(operation: OperationData): string {
32 | const {type} = operation;
33 | return formatVariableName(operation) + '_' + type.toUpperCase();
34 | }
35 |
36 | function operationVariables(operationData: OperationData) {
37 | const params = (
38 | operationData.operationDefinition.variableDefinitions || []
39 | ).map(def => def.variable.name.value);
40 | const variablesBody = params.map(param => `"${param}": ${param}`).join(', ');
41 | const variables = `{${variablesBody}}`;
42 |
43 | const propsBody = params
44 | .map(param => `"${param}": props.${param}`)
45 | .join(', ');
46 | const props = `{${propsBody}}`;
47 |
48 | return {params, variables, props};
49 | }
50 |
51 | function operationComponentName(operationData: OperationData): string {
52 | const {type} = operationData;
53 |
54 | const suffix =
55 | type === 'query'
56 | ? 'Query'
57 | : type === 'mutation'
58 | ? 'Mutation'
59 | : type === 'subscription'
60 | ? 'Subscription'
61 | : '';
62 |
63 | return suffix.length > 0
64 | ? '' + capitalizeFirstLetter(operationData.name) + suffix
65 | : capitalizeFirstLetter(operationData.name);
66 | }
67 |
68 | function mutationComponent(
69 | getComment,
70 | options,
71 | element,
72 | operationData,
73 | heads,
74 | vars,
75 | ) {
76 | const {params, variables} = operationVariables(operationData);
77 |
78 | const call = `${operationData.name}(${
79 | params.length === 0 ? '' : `${variables}`
80 | })`;
81 |
82 | const onClick = `() => ${call}`;
83 |
84 | return `
92 | {(${operationData.name}, { loading, error, data }) => {
93 | if (loading) return <${element}>Loading${element}>
94 |
95 | if (error)
96 | return (
97 | <${element}>
98 | Error in ${operationVariableName(operationData)}
99 | {JSON.stringify(error, null, 2)}
100 | ${element}>
101 | );
102 |
103 | const dataEl = data ? (
104 | <${element}>{JSON.stringify(data, null, 2)}${element}>
105 | ) : null;
106 |
107 | return (
108 |
109 | {dataEl}
110 |
111 |
114 |
115 | );
116 | }}
117 | `;
118 | }
119 |
120 | const queryComponent = (
121 | getComment,
122 | options,
123 | element,
124 | operationData,
125 | heads,
126 | vars,
127 | ) => {
128 | const {params, props} = operationVariables(operationData);
129 | return `
141 | {({ loading, error, data }) => {
142 | if (loading) return <${element}>Loading${element}>
143 | if (error)
144 | return (
145 | <${element}>
146 | Error in ${operationVariableName(operationData)}
147 | {JSON.stringify(error, null, 2)}
148 | ${element}>
149 | );
150 |
151 | if (data) {
152 | return (
153 | <${element}>{JSON.stringify(data, null, 2)}${element}>
154 | )
155 | }
156 | }}
157 | `;
158 | };
159 |
160 | const snippet: Snippet = {
161 | language: 'JavaScript',
162 | codeMirrorMode: 'jsx',
163 | name: 'react-apollo',
164 | options: [
165 | {
166 | id: 'client',
167 | label: 'with client setup',
168 | initial: true,
169 | },
170 | {
171 | id: 'imports',
172 | label: 'with required imports',
173 | initial: true,
174 | },
175 | ],
176 | generate: opts => {
177 | const {headers, options, serverUrl} = opts;
178 |
179 | const getComment = commentsFactory(true, comments);
180 |
181 | const operationDataList = opts.operationDataList.map(
182 | (operationData, idx) => {
183 | if (!isOperationNamed(operationData)) {
184 | return {
185 | ...operationData,
186 | name: `unnamed${capitalizeFirstLetter(operationData.type)}${idx +
187 | 1}`.trim(),
188 | query:
189 | `# Consider giving this ${
190 | operationData.type
191 | } a unique, descriptive
192 | # name in your application as a best practice
193 | ${operationData.type} unnamed${capitalizeFirstLetter(operationData.type)}${idx +
194 | 1} ` +
195 | operationData.query
196 | .trim()
197 | .replace(/^(query|mutation|subscription) /i, ''),
198 | };
199 | } else {
200 | return operationData;
201 | }
202 | },
203 | );
204 |
205 | const element = options.reactNative ? 'View' : 'pre';
206 | const vars = JSON.stringify({}, null, 2);
207 | const headersValues = [...Object.keys(headers || [])]
208 | .filter(k => headers[k])
209 | .map(k => `"${k}": "${headers[k]}"`)
210 | .join(',\n');
211 |
212 | const heads = `{${headersValues}}`;
213 |
214 | const packageDeps = `/*
215 | Add these to your \`package.json\`:
216 | "apollo-boost": "^0.3.1",
217 | "graphql": "^14.2.1",
218 | "graphql-tag": "^2.10.0",
219 | "react-apollo": "^2.5.5"
220 | */
221 |
222 | `;
223 |
224 | const clientSetup = options.client
225 | ? `${getComment('setup')};
226 | const apolloClient = new ApolloClient({
227 | cache: new InMemoryCache(),
228 | link: new HttpLink({
229 | uri: "${serverUrl}",
230 | }),
231 | });\n`
232 | : '';
233 |
234 | const operationTypes = distinct(
235 | operationDataList.map(operationData => operationData.type),
236 | );
237 |
238 | const imports = [
239 | operationTypes.indexOf('query') > -1 ? 'Query' : null,
240 | operationTypes.indexOf('mutation') > -1 ? 'Mutation' : null,
241 | 'ApolloProvider',
242 | ].filter(Boolean);
243 |
244 | const reactApolloImports = `import { ${imports.join(
245 | ', ',
246 | )} } from "react-apollo";`;
247 | const reactImports = `import React from "react";
248 | import ReactDOM from "react-dom";
249 | import { ${
250 | options.client ? 'ApolloClient, ' : ''
251 | }InMemoryCache, HttpLink } from "apollo-boost";`;
252 |
253 | const gqlImport = 'import gql from "graphql-tag";';
254 |
255 | const generalImports = options.imports
256 | ? `${gqlImport}
257 | ${reactImports}
258 | ${reactApolloImports}`
259 | : '';
260 |
261 | const components = operationDataList
262 | .map(operationData => {
263 | const componentFn =
264 | operationData.type === 'query'
265 | ? queryComponent
266 | : operationData.type === 'mutation'
267 | ? mutationComponent
268 | : () =>
269 | `"We don't support ${
270 | operationData.type
271 | } GraphQL operations yet"`;
272 |
273 | const graphqlOperation = `const ${operationVariableName(
274 | operationData,
275 | )} = gql\`
276 | ${addLeftWhitespace(operationData.query, 2)}
277 | \`;`;
278 |
279 | const component = `${graphqlOperation}
280 |
281 | const ${operationComponentName(operationData)} = (props) => {
282 | return (
283 | ${addLeftWhitespace(
284 | componentFn(
285 | // $FlowFixMe: Add flow type to utils fn
286 | getComment,
287 | options,
288 | element,
289 | operationData,
290 | heads,
291 | vars,
292 | ),
293 | 4,
294 | )}
295 | )
296 | };`;
297 |
298 | return component;
299 | })
300 | .join('\n\n');
301 |
302 | const componentInstantiations = operationDataList
303 | .map(operationData => {
304 | const {params} = operationVariables(operationData);
305 |
306 | const props = params.map(param => `${param}={${param}}`).join(' ');
307 |
308 | return `<${operationComponentName(operationData)} ${props} />`;
309 | })
310 | .join('\n');
311 |
312 | const variableInstantiations = operationDataList
313 | .map(operationData => {
314 | const variables = Object.entries(operationData.variables || {}).map(
315 | ([key, value]) => `const ${key} = ${JSON.stringify(value, null, 2)};`,
316 | );
317 |
318 | return `${variables.join('\n')}`;
319 | })
320 | .join('\n\n');
321 |
322 | const containerComponent = `${variableInstantiations}
323 |
324 | const container = (
325 |
326 | ${addLeftWhitespace(componentInstantiations, 4)}
327 |
328 | );`;
329 |
330 | const snippet = `
331 | /* This is an example snippet - you should consider tailoring it
332 | to your service.
333 | */
334 | ${packageDeps}${generalImports}
335 |
336 | ${clientSetup}
337 |
338 | ${components}
339 |
340 | ${containerComponent}
341 |
342 | ReactDOM.render(container, document.getElementById("root"));`;
343 | return collapseExtraNewlines(snippet.trim());
344 | },
345 | };
346 |
347 | export default snippet;
348 |
--------------------------------------------------------------------------------
/src/toposort.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type {FragmentDefinitionNode, OperationDefinitionNode} from 'graphql';
3 | import type {OperationData} from './CodeExporter.js';
4 |
5 | type stringBoolMap = {[string]: boolean};
6 |
7 | const operationDataByName = (
8 | graph: Array,
9 | name: string,
10 | ): ?OperationData => {
11 | return graph.find(operationData => operationData.name === name);
12 | };
13 |
14 | function topologicalSortHelper(
15 | node: OperationData,
16 | visited: stringBoolMap,
17 | temp: stringBoolMap,
18 | graph: Array,
19 | result,
20 | ) {
21 | temp[node.name] = true;
22 | var neighbors = node.fragmentDependencies;
23 | for (var i = 0; i < neighbors.length; i += 1) {
24 | var fragmentDependency = neighbors[i];
25 | var fragmentOperationData = operationDataByName(
26 | graph,
27 | fragmentDependency.name.value,
28 | );
29 |
30 | if (!fragmentOperationData) {
31 | continue;
32 | }
33 |
34 | if (temp[fragmentOperationData.name]) {
35 | console.error('The operation graph has a cycle');
36 | continue;
37 | }
38 | if (!visited[fragmentOperationData.name]) {
39 | topologicalSortHelper(
40 | fragmentOperationData,
41 | visited,
42 | temp,
43 | graph,
44 | result,
45 | );
46 | }
47 | }
48 | temp[node.name] = false;
49 | visited[node.name] = true;
50 | result.push(node);
51 | }
52 |
53 | export default function toposort(graph: Array) {
54 | var result: Array = [];
55 | var visited = {};
56 | var temp = {};
57 | for (var node of graph) {
58 | if (!visited[node.name] && !temp[node.name]) {
59 | topologicalSortHelper(node, visited, temp, graph, result);
60 | }
61 | }
62 | return result;
63 | }
64 |
--------------------------------------------------------------------------------
/src/utils/capitalizeFirstLetter.js:
--------------------------------------------------------------------------------
1 | export default function capitalizeFirstLetter(string) {
2 | return string.charAt(0).toUpperCase() + string.slice(1);
3 | }
4 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | function distinct(array: Array): Array {
2 | return [...new Set(array)];
3 | }
4 |
5 | const unnamedSymbols = ['query', 'mutation', 'subscription'];
6 |
7 | function isOperationNamed(operationData: OperationData): boolean {
8 | return unnamedSymbols.indexOf(operationData.name.trim()) === -1;
9 | }
10 |
11 | const findFirstNamedOperation = (
12 | operations: Array,
13 | ): ?OperationData => operations.find(isOperationNamed);
14 |
15 | function addLeftWhitespace(s: string, padding: number): string {
16 | const pad = [...new Array(padding + 1)].join(' ');
17 | return s
18 | .split('\n')
19 | .map(x => `${pad}${x}`)
20 | .join('\n');
21 | }
22 |
23 | function collapseExtraNewlines(s: string): string {
24 | return s.replace(/\n{2,}/g, '\n\n');
25 | }
26 |
27 | export {
28 | distinct,
29 | findFirstNamedOperation,
30 | isOperationNamed,
31 | addLeftWhitespace,
32 | collapseExtraNewlines,
33 | };
34 |
--------------------------------------------------------------------------------
/src/utils/jsCommentsFactory.js:
--------------------------------------------------------------------------------
1 | export default function commentFactory(commentsEnabled, comments) {
2 | return id => (commentsEnabled ? '// ' + comments[id] : '');
3 | }
4 |
--------------------------------------------------------------------------------