1 | "use strict";
|
2 |
|
3 | const os = require('os');
|
4 | const gql = require('./main.js');
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | function expandImports(source, doc) {
|
10 | const lines = source.split(/\r\n|\r|\n/);
|
11 | let outputCode = `
|
12 | var names = {};
|
13 | function unique(defs) {
|
14 | return defs.filter(
|
15 | function(def) {
|
16 | if (def.kind !== 'FragmentDefinition') return true;
|
17 | var name = def.name.value
|
18 | if (names[name]) {
|
19 | return false;
|
20 | } else {
|
21 | names[name] = true;
|
22 | return true;
|
23 | }
|
24 | }
|
25 | )
|
26 | }
|
27 | `;
|
28 |
|
29 | lines.some((line) => {
|
30 | if (line[0] === '#' && line.slice(1).split(' ')[0] === 'import') {
|
31 | const importFile = line.slice(1).split(' ')[1];
|
32 | const parseDocument = `require(${importFile})`;
|
33 | const appendDef = `doc.definitions = doc.definitions.concat(unique(${parseDocument}.definitions));`;
|
34 | outputCode += appendDef + os.EOL;
|
35 | }
|
36 | return (line.length !== 0 && line[0] !== '#');
|
37 | });
|
38 |
|
39 | return outputCode;
|
40 | }
|
41 |
|
42 | module.exports = function(source) {
|
43 | this.cacheable();
|
44 | const doc = gql`${source}`;
|
45 | let headerCode = `
|
46 | var doc = ${JSON.stringify(doc)};
|
47 | doc.loc.source = ${JSON.stringify(doc.loc.source)};
|
48 | `;
|
49 |
|
50 | let outputCode = "";
|
51 |
|
52 |
|
53 |
|
54 |
|
55 | let operationCount = doc.definitions.reduce(function(accum, op) {
|
56 | if (op.kind === "OperationDefinition" || op.kind === "FragmentDefinition") {
|
57 | return accum + 1;
|
58 | }
|
59 |
|
60 | return accum;
|
61 | }, 0);
|
62 |
|
63 | if (operationCount < 1) {
|
64 | outputCode += `
|
65 | module.exports = doc;
|
66 | `
|
67 | } else {
|
68 | outputCode += `
|
69 | // Collect any fragment/type references from a node, adding them to the refs Set
|
70 | function collectFragmentReferences(node, refs) {
|
71 | if (node.kind === "FragmentSpread") {
|
72 | refs.add(node.name.value);
|
73 | } else if (node.kind === "VariableDefinition") {
|
74 | var type = node.type;
|
75 | if (type.kind === "NamedType") {
|
76 | refs.add(type.name.value);
|
77 | }
|
78 | }
|
79 |
|
80 | if (node.selectionSet) {
|
81 | node.selectionSet.selections.forEach(function(selection) {
|
82 | collectFragmentReferences(selection, refs);
|
83 | });
|
84 | }
|
85 |
|
86 | if (node.variableDefinitions) {
|
87 | node.variableDefinitions.forEach(function(def) {
|
88 | collectFragmentReferences(def, refs);
|
89 | });
|
90 | }
|
91 |
|
92 | if (node.definitions) {
|
93 | node.definitions.forEach(function(def) {
|
94 | collectFragmentReferences(def, refs);
|
95 | });
|
96 | }
|
97 | }
|
98 |
|
99 | var definitionRefs = {};
|
100 | (function extractReferences() {
|
101 | doc.definitions.forEach(function(def) {
|
102 | if (def.name) {
|
103 | var refs = new Set();
|
104 | collectFragmentReferences(def, refs);
|
105 | definitionRefs[def.name.value] = refs;
|
106 | }
|
107 | });
|
108 | })();
|
109 |
|
110 | function findOperation(doc, name) {
|
111 | for (var i = 0; i < doc.definitions.length; i++) {
|
112 | var element = doc.definitions[i];
|
113 | if (element.name && element.name.value == name) {
|
114 | return element;
|
115 | }
|
116 | }
|
117 | }
|
118 |
|
119 | function oneQuery(doc, operationName) {
|
120 | // Copy the DocumentNode, but clear out the definitions
|
121 | var newDoc = {
|
122 | kind: doc.kind,
|
123 | definitions: [findOperation(doc, operationName)]
|
124 | };
|
125 | if (doc.hasOwnProperty("loc")) {
|
126 | newDoc.loc = doc.loc;
|
127 | }
|
128 |
|
129 | // Now, for the operation we're running, find any fragments referenced by
|
130 | // it or the fragments it references
|
131 | var opRefs = definitionRefs[operationName] || new Set();
|
132 | var allRefs = new Set();
|
133 | var newRefs = new Set();
|
134 |
|
135 | // IE 11 doesn't support "new Set(iterable)", so we add the members of opRefs to newRefs one by one
|
136 | opRefs.forEach(function(refName) {
|
137 | newRefs.add(refName);
|
138 | });
|
139 |
|
140 | while (newRefs.size > 0) {
|
141 | var prevRefs = newRefs;
|
142 | newRefs = new Set();
|
143 |
|
144 | prevRefs.forEach(function(refName) {
|
145 | if (!allRefs.has(refName)) {
|
146 | allRefs.add(refName);
|
147 | var childRefs = definitionRefs[refName] || new Set();
|
148 | childRefs.forEach(function(childRef) {
|
149 | newRefs.add(childRef);
|
150 | });
|
151 | }
|
152 | });
|
153 | }
|
154 |
|
155 | allRefs.forEach(function(refName) {
|
156 | var op = findOperation(doc, refName);
|
157 | if (op) {
|
158 | newDoc.definitions.push(op);
|
159 | }
|
160 | });
|
161 |
|
162 | return newDoc;
|
163 | }
|
164 |
|
165 | module.exports = doc;
|
166 | `
|
167 |
|
168 | for (const op of doc.definitions) {
|
169 | if (op.kind === "OperationDefinition" || op.kind === "FragmentDefinition") {
|
170 | if (!op.name) {
|
171 | if (operationCount > 1) {
|
172 | throw "Query/mutation names are required for a document with multiple definitions";
|
173 | } else {
|
174 | continue;
|
175 | }
|
176 | }
|
177 |
|
178 | const opName = op.name.value;
|
179 | outputCode += `
|
180 | module.exports["${opName}"] = oneQuery(doc, "${opName}");
|
181 | `
|
182 | }
|
183 | }
|
184 | }
|
185 |
|
186 | const importOutputCode = expandImports(source, doc);
|
187 | const allCode = headerCode + os.EOL + importOutputCode + os.EOL + outputCode + os.EOL;
|
188 |
|
189 | return allCode;
|
190 | };
|