UNPKG

graphiql-code-exporter

Version:

Export working code snippets from GraphiQL queries

348 lines (294 loc) 8.37 kB
// @flow import capitalizeFirstLetter from '../../utils/capitalizeFirstLetter'; import commentsFactory from '../../utils/jsCommentsFactory.js'; import { distinct, isOperationNamed, collapseExtraNewlines, addLeftWhitespace, } from '../../utils/index.js'; import 'codemirror/mode/jsx/jsx'; import type {Snippet, OperationData} from '../../index.js'; const comments = { setup: `This setup is only needed once per application`, }; function formatVariableName(operationData: OperationData): string { const {name} = operationData; return ( name.charAt(0).toUpperCase() + name .slice(1) .replace(/[A-Z]/g, '_$&') .toUpperCase() ); } function operationVariableName(operation: OperationData): string { const {type} = operation; return formatVariableName(operation) + '_' + type.toUpperCase(); } function operationVariables(operationData: OperationData) { const params = ( operationData.operationDefinition.variableDefinitions || [] ).map(def => def.variable.name.value); const variablesBody = params.map(param => `"${param}": ${param}`).join(', '); const variables = `{${variablesBody}}`; const propsBody = params .map(param => `"${param}": props.${param}`) .join(', '); const props = `{${propsBody}}`; return {params, variables, props}; } function operationComponentName(operationData: OperationData): string { const {type} = operationData; const suffix = type === 'query' ? 'Query' : type === 'mutation' ? 'Mutation' : type === 'subscription' ? 'Subscription' : ''; return suffix.length > 0 ? '' + capitalizeFirstLetter(operationData.name) + suffix : capitalizeFirstLetter(operationData.name); } function mutationComponent( getComment, options, element, operationData, heads, vars, ) { const {params, variables} = operationVariables(operationData); const call = `${operationData.name}(${ params.length === 0 ? '' : `${variables}` })`; const onClick = `() => ${call}`; return `<Mutation mutation={${operationVariableName(operationData)}}${ heads === '{}' ? '' : ` context={{ headers: ${heads} }} ` }> {(${operationData.name}, { loading, error, data }) => { if (loading) return <${element}>Loading</${element}> if (error) return ( <${element}> Error in ${operationVariableName(operationData)} {JSON.stringify(error, null, 2)} </${element}> ); const dataEl = data ? ( <${element}>{JSON.stringify(data, null, 2)}</${element}> ) : null; return ( <div> {dataEl} <button onClick={${onClick}}> Run mutation: ${operationData.name} </button> </div> ); }} </Mutation>`; } const queryComponent = ( getComment, options, element, operationData, heads, vars, ) => { const {params, props} = operationVariables(operationData); return `<Query query={${operationVariableName(operationData)}}${ heads === '{}' ? '' : ` context={{ headers: ${heads} }}` } ${ params.length === 0 ? '' : ` variables={${props}}` }> {({ loading, error, data }) => { if (loading) return <${element}>Loading</${element}> if (error) return ( <${element}> Error in ${operationVariableName(operationData)} {JSON.stringify(error, null, 2)} </${element}> ); if (data) { return ( <${element}>{JSON.stringify(data, null, 2)}</${element}> ) } }} </Query>`; }; const snippet: Snippet = { language: 'JavaScript', codeMirrorMode: 'jsx', name: 'react-apollo', options: [ { id: 'client', label: 'with client setup', initial: true, }, { id: 'imports', label: 'with required imports', initial: true, }, ], generate: opts => { const {headers, options, serverUrl} = opts; const getComment = commentsFactory(true, comments); const operationDataList = opts.operationDataList.map( (operationData, idx) => { if (!isOperationNamed(operationData)) { return { ...operationData, name: `unnamed${capitalizeFirstLetter(operationData.type)}${idx + 1}`.trim(), query: `# Consider giving this ${ operationData.type } a unique, descriptive # name in your application as a best practice ${operationData.type} unnamed${capitalizeFirstLetter(operationData.type)}${idx + 1} ` + operationData.query .trim() .replace(/^(query|mutation|subscription) /i, ''), }; } else { return operationData; } }, ); const element = options.reactNative ? 'View' : 'pre'; const vars = JSON.stringify({}, null, 2); const headersValues = [...Object.keys(headers || [])] .filter(k => headers[k]) .map(k => `"${k}": "${headers[k]}"`) .join(',\n'); const heads = `{${headersValues}}`; const packageDeps = `/* Add these to your \`package.json\`: "apollo-boost": "^0.3.1", "graphql": "^14.2.1", "graphql-tag": "^2.10.0", "react-apollo": "^2.5.5" */ `; const clientSetup = options.client ? `${getComment('setup')}; const apolloClient = new ApolloClient({ cache: new InMemoryCache(), link: new HttpLink({ uri: "${serverUrl}", }), });\n` : ''; const operationTypes = distinct( operationDataList.map(operationData => operationData.type), ); const imports = [ operationTypes.indexOf('query') > -1 ? 'Query' : null, operationTypes.indexOf('mutation') > -1 ? 'Mutation' : null, 'ApolloProvider', ].filter(Boolean); const reactApolloImports = `import { ${imports.join( ', ', )} } from "react-apollo";`; const reactImports = `import React from "react"; import ReactDOM from "react-dom"; import { ${ options.client ? 'ApolloClient, ' : '' }InMemoryCache, HttpLink } from "apollo-boost";`; const gqlImport = 'import gql from "graphql-tag";'; const generalImports = options.imports ? `${gqlImport} ${reactImports} ${reactApolloImports}` : ''; const components = operationDataList .map(operationData => { const componentFn = operationData.type === 'query' ? queryComponent : operationData.type === 'mutation' ? mutationComponent : () => `"We don't support ${ operationData.type } GraphQL operations yet"`; const graphqlOperation = `const ${operationVariableName( operationData, )} = gql\` ${addLeftWhitespace(operationData.query, 2)} \`;`; const component = `${graphqlOperation} const ${operationComponentName(operationData)} = (props) => { return ( ${addLeftWhitespace( componentFn( // $FlowFixMe: Add flow type to utils fn getComment, options, element, operationData, heads, vars, ), 4, )} ) };`; return component; }) .join('\n\n'); const componentInstantiations = operationDataList .map(operationData => { const {params} = operationVariables(operationData); const props = params.map(param => `${param}={${param}}`).join(' '); return `<${operationComponentName(operationData)} ${props} />`; }) .join('\n'); const variableInstantiations = operationDataList .map(operationData => { const variables = Object.entries(operationData.variables || {}).map( ([key, value]) => `const ${key} = ${JSON.stringify(value, null, 2)};`, ); return `${variables.join('\n')}`; }) .join('\n\n'); const containerComponent = `${variableInstantiations} const container = ( <ApolloProvider client={apolloClient}> ${addLeftWhitespace(componentInstantiations, 4)} </ApolloProvider> );`; const snippet = ` /* This is an example snippet - you should consider tailoring it to your service. */ ${packageDeps}${generalImports} ${clientSetup} ${components} ${containerComponent} ReactDOM.render(container, document.getElementById("root"));`; return collapseExtraNewlines(snippet.trim()); }, }; export default snippet;