UNPKG

3.26 kBJavaScriptView Raw
1const React = require('react');
2const { existsSync, readFileSync } = require('fs');
3const {
4 Mixin,
5 strategies: {
6 sync: { override, callable, sequence },
7 },
8} = require('hops-mixin');
9
10const { ApolloProvider, getDataFromTree } = require('react-apollo');
11const { default: ApolloClient } = require('apollo-client');
12const { HttpLink } = require('apollo-link-http');
13const {
14 InMemoryCache,
15 IntrospectionFragmentMatcher,
16 HeuristicFragmentMatcher,
17} = require('apollo-cache-inmemory');
18const fetch = require('cross-fetch');
19
20let introspectionResult = undefined;
21let warned = false;
22
23class GraphQLMixin extends Mixin {
24 constructor(config, element, { graphql: options = {} } = {}) {
25 super(config, element);
26
27 this.options = options;
28
29 if (introspectionResult === undefined) {
30 try {
31 if (existsSync(config.fragmentsFile)) {
32 const fileContent = readFileSync(config.fragmentsFile, 'utf-8');
33 introspectionResult = JSON.parse(fileContent);
34 } else if (!warned) {
35 warned = true;
36 console.warn(
37 'Could not find a graphql introspection query result at %s.',
38 config.fragmentsFile,
39 'You might need to execute `hops graphql introspect`'
40 );
41 }
42 } catch (_) {
43 introspectionResult = null;
44 }
45 }
46 }
47
48 getApolloClient() {
49 if (this.client) {
50 return this.client;
51 }
52 return (this.client = this.createClient(this.options));
53 }
54
55 createClient(options) {
56 return new ApolloClient(this.enhanceClientOptions(options));
57 }
58
59 enhanceClientOptions(options) {
60 return {
61 ...options,
62 link: this.getApolloLink(),
63 cache: this.getApolloCache(),
64 ssrMode: true,
65 };
66 }
67
68 getApolloLink() {
69 return (
70 this.options.link ||
71 new HttpLink({
72 uri: this.config.graphqlUri,
73 fetch,
74 })
75 );
76 }
77
78 getApolloCache() {
79 return (
80 this.options.cache ||
81 new InMemoryCache({ fragmentMatcher: this.createFragmentMatcher() })
82 );
83 }
84
85 createFragmentMatcher() {
86 return !introspectionResult
87 ? new HeuristicFragmentMatcher()
88 : new IntrospectionFragmentMatcher({
89 introspectionQueryResultData: introspectionResult,
90 });
91 }
92
93 fetchData(data = {}, element) {
94 return this.prefetchData(element).then(() => data);
95 }
96
97 prefetchData(element) {
98 const prefetchOnServer = this.canPrefetchOnServer().every(value => value);
99
100 return prefetchOnServer ? getDataFromTree(element) : Promise.resolve();
101 }
102
103 canPrefetchOnServer() {
104 const { shouldPrefetchOnServer } = this.config;
105
106 return shouldPrefetchOnServer !== false;
107 }
108
109 getTemplateData(data) {
110 return {
111 ...data,
112 globals: {
113 ...data.globals,
114 APOLLO_FRAGMENT_TYPES: introspectionResult,
115 APOLLO_STATE: this.getApolloClient().cache.extract(),
116 },
117 };
118 }
119
120 enhanceElement(reactElement) {
121 return React.createElement(
122 ApolloProvider,
123 { client: this.getApolloClient() },
124 reactElement
125 );
126 }
127}
128
129GraphQLMixin.strategies = {
130 getApolloLink: override,
131 getApolloCache: override,
132 createFragmentMatcher: callable,
133 canPrefetchOnServer: sequence,
134};
135
136module.exports = GraphQLMixin;