hops-graphql
Version:
React and GraphQL implementation for Hops
142 lines (123 loc) • 3.29 kB
JavaScript
require('isomorphic-fetch');
const React = require('react');
const { existsSync, readFileSync } = require('fs');
const {
Mixin,
strategies: {
sync: { override, callable },
},
} = require('hops-mixin');
const { ApolloProvider, getDataFromTree } = require('react-apollo');
const { default: ApolloClient } = require('apollo-client');
const { HttpLink } = require('apollo-link-http');
const {
InMemoryCache,
IntrospectionFragmentMatcher,
HeuristicFragmentMatcher,
} = require('apollo-cache-inmemory');
let introspectionResult = undefined;
let warned = false;
class GraphQLMixin extends Mixin {
constructor(config, element, { graphql: options = {} } = {}) {
super(config, element);
this.options = options;
if (introspectionResult === undefined) {
try {
if (existsSync(config.fragmentsFile)) {
const fileContent = readFileSync(config.fragmentsFile, 'utf-8');
introspectionResult = JSON.parse(fileContent);
} else if (!warned) {
warned = true;
console.warn(
'Could not find a graphql introspection query result at %s.',
config.fragmentsFile,
'You might need to execute `hops graphql introspect`'
);
}
} catch (_) {
introspectionResult = null;
}
}
}
bootstrap() {
this.client = this.createClient(this.options);
}
createClient(options) {
return new ApolloClient(this.enhanceClientOptions(options));
}
enhanceClientOptions(options) {
return {
...options,
link: this.getApolloLink(),
cache: this.getApolloCache(),
ssrMode: true,
};
}
getApolloLink() {
return (
this.options.link ||
new HttpLink({
uri: this.config.graphqlUri,
})
);
}
getApolloCache() {
return (
this.options.cache ||
new InMemoryCache({ fragmentMatcher: this.createFragmentMatcher() })
);
}
createFragmentMatcher() {
return !introspectionResult
? new HeuristicFragmentMatcher()
: new IntrospectionFragmentMatcher({
introspectionQueryResultData: introspectionResult,
});
}
fetchData(data = {}, element) {
return this.prefetchData(element).then(() => {
return {
...data,
globals: {
...(data.globals || {}),
APOLLO_FRAGMENT_TYPES: introspectionResult,
APOLLO_STATE: this.client.cache.extract(),
},
};
});
}
prefetchData(element) {
return this.shouldPrefetchOnServer()
? getDataFromTree(element)
: Promise.resolve();
}
shouldPrefetchOnServer() {
const { shouldPrefetchOnServer } = this.config;
return typeof shouldPrefetchOnServer === 'boolean'
? shouldPrefetchOnServer
: true;
}
getTemplateData(data) {
return {
...data,
globals: {
...data.globals,
...(data.fetchedData || {}).globals,
},
};
}
enhanceElement(reactElement) {
return React.createElement(
ApolloProvider,
{ client: this.client },
reactElement
);
}
}
GraphQLMixin.strategies = {
getApolloLink: override,
getApolloCache: override,
createFragmentMatcher: callable,
shouldPrefetchOnServer: override,
};
module.exports = GraphQLMixin;