|
1 | | -import { loadTypedefs } from '@graphql-tools/load'; |
2 | | -import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader'; |
3 | | -import { concatAST } from 'graphql'; |
4 | | -import { getOptions } from 'loader-utils'; |
| 1 | +import os from 'os'; |
| 2 | +import { isExecutableDefinitionNode, visit, Kind, DocumentNode } from 'graphql'; |
| 3 | +import { uniqueCode } from '@graphql-tools/webpack-loader-runtime'; |
| 4 | +import { parseDocument } from './parser'; |
5 | 5 |
|
6 | | -export default function (this: any, path: string) { |
7 | | - const callback = this.async(); |
| 6 | +function isSDL(doc: DocumentNode) { |
| 7 | + return !doc.definitions.some(def => isExecutableDefinitionNode(def)); |
| 8 | +} |
8 | 9 |
|
9 | | - this.cacheable(); |
| 10 | +function removeDescriptions(doc: DocumentNode) { |
| 11 | + function transformNode(node: any) { |
| 12 | + if (node.description) { |
| 13 | + node.description = undefined; |
| 14 | + } |
| 15 | + |
| 16 | + return node; |
| 17 | + } |
| 18 | + |
| 19 | + if (isSDL(doc)) { |
| 20 | + return visit(doc, { |
| 21 | + ScalarTypeDefinition: transformNode, |
| 22 | + ObjectTypeDefinition: transformNode, |
| 23 | + InterfaceTypeDefinition: transformNode, |
| 24 | + UnionTypeDefinition: transformNode, |
| 25 | + EnumTypeDefinition: transformNode, |
| 26 | + EnumValueDefinition: transformNode, |
| 27 | + InputObjectTypeDefinition: transformNode, |
| 28 | + InputValueDefinition: transformNode, |
| 29 | + FieldDefinition: transformNode, |
| 30 | + }); |
| 31 | + } |
| 32 | + |
| 33 | + return doc; |
| 34 | +} |
10 | 35 |
|
11 | | - const options = getOptions(this); |
| 36 | +interface Options { |
| 37 | + noDescription?: boolean; |
| 38 | + esModule?: boolean; |
| 39 | + importHelpers?: boolean; |
| 40 | +} |
| 41 | + |
| 42 | +function expandImports(source: string, options: Options) { |
| 43 | + const lines = source.split(/\r\n|\r|\n/); |
| 44 | + let outputCode = options.importHelpers |
| 45 | + ? ` |
| 46 | + const { useUnique } = require('@graphql-tools/webpack-loader-runtime'); |
| 47 | +
|
| 48 | + const unique = useUnique(); |
| 49 | + ` |
| 50 | + : ` |
| 51 | + ${uniqueCode} |
| 52 | + `; |
12 | 53 |
|
13 | | - loadTypedefs(path, { |
14 | | - loaders: [new GraphQLFileLoader()], |
15 | | - noLocation: true, |
16 | | - ...options, |
17 | | - }).then(sources => { |
18 | | - const documents = sources.map(source => source.document); |
19 | | - const mergedDoc = concatAST(documents); |
20 | | - return callback(null, `export default ${JSON.stringify(mergedDoc)}`); |
| 54 | + lines.some(line => { |
| 55 | + if (line[0] === '#' && line.slice(1).split(' ')[0] === 'import') { |
| 56 | + const importFile = line.slice(1).split(' ')[1]; |
| 57 | + const parseDocument = `require(${importFile})`; |
| 58 | + const appendDef = `doc.definitions = doc.definitions.concat(unique(${parseDocument}.definitions));`; |
| 59 | + outputCode += appendDef + os.EOL; |
| 60 | + } |
| 61 | + return line.length !== 0 && line[0] !== '#'; |
21 | 62 | }); |
| 63 | + |
| 64 | + return outputCode; |
| 65 | +} |
| 66 | + |
| 67 | +export default function graphqlLoader(source: string) { |
| 68 | + this.cacheable(); |
| 69 | + const options: Options = this.query || {}; |
| 70 | + let doc = parseDocument(source); |
| 71 | + |
| 72 | + // Removes descriptions from Nodes |
| 73 | + if (options.noDescription) { |
| 74 | + doc = removeDescriptions(doc); |
| 75 | + } |
| 76 | + |
| 77 | + const headerCode = ` |
| 78 | + const doc = ${JSON.stringify(doc)}; |
| 79 | + `; |
| 80 | + |
| 81 | + let outputCode = ''; |
| 82 | + |
| 83 | + // Allow multiple query/mutation definitions in a file. This parses out dependencies |
| 84 | + // at compile time, and then uses those at load time to create minimal query documents |
| 85 | + // We cannot do the latter at compile time due to how the #import code works. |
| 86 | + const operationCount = doc.definitions.reduce<number>((accum, op) => { |
| 87 | + if (op.kind === Kind.OPERATION_DEFINITION) { |
| 88 | + return accum + 1; |
| 89 | + } |
| 90 | + |
| 91 | + return accum; |
| 92 | + }, 0); |
| 93 | + |
| 94 | + function exportDefaultStatement(identifier: string) { |
| 95 | + if (options.esModule) { |
| 96 | + return `export default ${identifier}`; |
| 97 | + } |
| 98 | + |
| 99 | + return `module.exports = ${identifier}`; |
| 100 | + } |
| 101 | + |
| 102 | + if (operationCount > 1) { |
| 103 | + throw new Error('GraphQL Webpack Loader allows only for one GraphQL Operation per file'); |
| 104 | + } |
| 105 | + |
| 106 | + outputCode += ` |
| 107 | + ${exportDefaultStatement('doc')} |
| 108 | + `; |
| 109 | + |
| 110 | + const importOutputCode = expandImports(source, options); |
| 111 | + const allCode = [headerCode, importOutputCode, outputCode, ''].join(os.EOL); |
| 112 | + |
| 113 | + return allCode; |
22 | 114 | } |
0 commit comments