@khanacademy/graphql-flow 1.1.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +1 -1
- package/.eslintrc.js +0 -1
- package/.github/workflows/changeset-release.yml +1 -1
- package/CHANGELOG.md +16 -0
- package/dist/cli/config.js +2 -4
- package/dist/cli/run.js +1 -2
- package/dist/enums.js +8 -9
- package/dist/generateResponseType.js +33 -41
- package/dist/generateTypeFiles.js +13 -35
- package/dist/generateVariablesType.js +15 -31
- package/dist/index.js +11 -17
- package/dist/parser/parse.js +10 -8
- package/dist/parser/resolve.js +11 -8
- package/dist/parser/utils.js +36 -0
- package/dist/schemaFromIntrospectionData.js +1 -2
- package/dist/types.js +1 -2
- package/dist/utils.js +43 -3
- package/package.json +8 -7
- package/{src/cli/schema.json → schema.json} +3 -0
- package/{dist/__test__/generateTypeFileContents.test.js → src/__test__/generateTypeFileContents.test.ts} +38 -41
- package/{dist/__test__/graphql-flow.test.js → src/__test__/graphql-flow.test.ts} +232 -235
- package/src/__test__/{processPragmas.test.js → processPragmas.test.ts} +0 -1
- package/{dist/cli/__test__/config.test.js → src/cli/__test__/config.test.ts} +5 -6
- package/{dist/cli/config.js.flow → src/cli/config.ts} +6 -11
- package/src/cli/{run.js → run.ts} +5 -4
- package/src/{enums.js → enums.ts} +20 -22
- package/{dist/generateResponseType.js.flow → src/generateResponseType.ts} +167 -182
- package/src/{generateTypeFiles.js → generateTypeFiles.ts} +24 -40
- package/src/{generateVariablesType.js → generateVariablesType.ts} +34 -44
- package/{dist/index.js.flow → src/index.ts} +33 -24
- package/{dist/parser/__test__/parse.test.js → src/parser/__test__/parse.test.ts} +12 -11
- package/{dist/parser/parse.js.flow → src/parser/parse.ts} +69 -48
- package/{dist/parser/resolve.js.flow → src/parser/resolve.ts} +25 -19
- package/src/parser/utils.ts +24 -0
- package/{dist/schemaFromIntrospectionData.js.flow → src/schemaFromIntrospectionData.ts} +1 -4
- package/src/types.ts +97 -0
- package/src/utils.ts +73 -0
- package/tools/{find-files-with-gql.js → find-files-with-gql.ts} +2 -3
- package/tsconfig.json +110 -0
- package/types/flow-to-ts.d.ts +1 -0
- package/dist/__test__/example-schema.graphql +0 -67
- package/dist/__test__/processPragmas.test.js +0 -76
- package/dist/cli/config.js.map +0 -1
- package/dist/cli/run.js.flow +0 -236
- package/dist/cli/run.js.map +0 -1
- package/dist/cli/schema.json +0 -94
- package/dist/enums.js.flow +0 -98
- package/dist/enums.js.map +0 -1
- package/dist/generateResponseType.js.map +0 -1
- package/dist/generateTypeFiles.js.flow +0 -197
- package/dist/generateTypeFiles.js.map +0 -1
- package/dist/generateVariablesType.js.flow +0 -156
- package/dist/generateVariablesType.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/parser/parse.js.map +0 -1
- package/dist/parser/resolve.js.map +0 -1
- package/dist/schemaFromIntrospectionData.js.map +0 -1
- package/dist/types.js.flow +0 -87
- package/dist/types.js.map +0 -1
- package/dist/utils.js.flow +0 -50
- package/dist/utils.js.map +0 -1
- package/flow-typed/npm/@babel/types_vx.x.x.js +0 -5331
- package/flow-typed/npm/jest_v23.x.x.js +0 -1155
- package/flow-typed/overrides.js +0 -435
- package/src/__test__/generateTypeFileContents.test.js +0 -157
- package/src/__test__/graphql-flow.test.js +0 -639
- package/src/cli/__test__/config.test.js +0 -120
- package/src/cli/config.js +0 -84
- package/src/generateResponseType.js +0 -583
- package/src/index.js +0 -159
- package/src/parser/__test__/parse.test.js +0 -249
- package/src/parser/parse.js +0 -414
- package/src/parser/resolve.js +0 -117
- package/src/schemaFromIntrospectionData.js +0 -68
- package/src/types.js +0 -87
- package/src/utils.js +0 -50
package/src/parser/parse.js
DELETED
|
@@ -1,414 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
import type {
|
|
3
|
-
BabelNodeImportDeclaration,
|
|
4
|
-
BabelNodeVariableDeclarator,
|
|
5
|
-
BabelNodeTaggedTemplateExpression,
|
|
6
|
-
BabelNodeFile,
|
|
7
|
-
} from '@babel/types';
|
|
8
|
-
|
|
9
|
-
import {parse} from '@babel/parser'; // eslint-disable-line flowtype-errors/uncovered
|
|
10
|
-
import traverse from '@babel/traverse'; // eslint-disable-line flowtype-errors/uncovered
|
|
11
|
-
|
|
12
|
-
import path from 'path';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* This file is responsible for finding all gql`-annotated
|
|
16
|
-
* template strings in a set of provided files, and for resolving
|
|
17
|
-
* all fragment references to their eventual sources.
|
|
18
|
-
*
|
|
19
|
-
* Things that are supported:
|
|
20
|
-
* - importing fragments from other files
|
|
21
|
-
* - re-exporting fragments that were imported
|
|
22
|
-
* - using locally-defined fragments, even if they're
|
|
23
|
-
* not at the top level (scope is honored correctly)
|
|
24
|
-
* - importing the gql tag as some other name
|
|
25
|
-
* (e.g. 'import blah from "graphql-tag"')
|
|
26
|
-
* - renaming fragments at the top level
|
|
27
|
-
*
|
|
28
|
-
* Things that are *not* supported:
|
|
29
|
-
* - doing anything other than 'const x = gql`my template`'
|
|
30
|
-
* e.g. const x = someCond ? one fragment literal : another fragment literal
|
|
31
|
-
* or const x = someFragmentPreprocessor(fragment literal)
|
|
32
|
-
* - importing the graphql tag fn from anywhere other than "graphql-tag"
|
|
33
|
-
* - anything else fancy with the graphql tag fn, e.g. 'const blah = gql; blah`xyz`'
|
|
34
|
-
* - including a fragment in an operation by anything other than a bare identifier,
|
|
35
|
-
* e.g. 'const myQuery = gql`query xyz {...} ${cond ? someFrag : otherFrag}`.
|
|
36
|
-
* - getting fragments from e.g. function arguments, or renaming non-toplevel fragments
|
|
37
|
-
*
|
|
38
|
-
* Things that could be supported, but are not yet:
|
|
39
|
-
* - tracking whether a given graphql operation has already been wrapped
|
|
40
|
-
* in `gqlOp<Type>()` or not (to inform an auto-wrapper of the future)
|
|
41
|
-
*/
|
|
42
|
-
|
|
43
|
-
export type Template = {|
|
|
44
|
-
literals: Array<string>,
|
|
45
|
-
expressions: Array<Document | Import>,
|
|
46
|
-
loc: Loc,
|
|
47
|
-
|};
|
|
48
|
-
export type Loc = {start: number, end: number, path: string, line: number};
|
|
49
|
-
|
|
50
|
-
export type Document = {|
|
|
51
|
-
type: 'document',
|
|
52
|
-
source: Template,
|
|
53
|
-
|};
|
|
54
|
-
export type Import = {|
|
|
55
|
-
type: 'import',
|
|
56
|
-
name: string,
|
|
57
|
-
path: string,
|
|
58
|
-
loc: Loc,
|
|
59
|
-
|};
|
|
60
|
-
|
|
61
|
-
export type Operation = {|
|
|
62
|
-
source: Template,
|
|
63
|
-
// TODO: Determine if an operation is already wrapped
|
|
64
|
-
// in `gqlOp` so we can automatically wrap if needed.
|
|
65
|
-
// needsWrapping: boolean,
|
|
66
|
-
|};
|
|
67
|
-
|
|
68
|
-
export type FileResult = {|
|
|
69
|
-
path: string,
|
|
70
|
-
operations: Array<Operation>,
|
|
71
|
-
exports: {[key: string]: Document | Import},
|
|
72
|
-
locals: {[key: string]: Document | Import},
|
|
73
|
-
errors: Array<{loc: Loc, message: string}>,
|
|
74
|
-
|};
|
|
75
|
-
|
|
76
|
-
export type Files = {[path: string]: FileResult};
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Finds all referenced imports that might possibly be relevant
|
|
80
|
-
* graphql fragments.
|
|
81
|
-
*
|
|
82
|
-
* Importantly, any values that are re-exported are treated as
|
|
83
|
-
* potentially relevant, and of course any values referenced
|
|
84
|
-
* from a graphql template are treated as relevant.
|
|
85
|
-
*/
|
|
86
|
-
const listExternalReferences = (file: FileResult): Array<string> => {
|
|
87
|
-
const paths = {};
|
|
88
|
-
const add = (v: Document | Import, followImports: boolean) => {
|
|
89
|
-
if (v.type === 'import') {
|
|
90
|
-
if (followImports) {
|
|
91
|
-
paths[v.path] = true;
|
|
92
|
-
}
|
|
93
|
-
} else {
|
|
94
|
-
v.source.expressions.forEach((expr) => add(expr, true));
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
Object.keys(file.exports).forEach((k) =>
|
|
98
|
-
add(
|
|
99
|
-
file.exports[k],
|
|
100
|
-
// If we're re-exporting something, we need to follow that import.
|
|
101
|
-
true,
|
|
102
|
-
),
|
|
103
|
-
);
|
|
104
|
-
Object.keys(file.locals).forEach((k) =>
|
|
105
|
-
add(
|
|
106
|
-
file.locals[k],
|
|
107
|
-
// If we've imported something but haven't used it or exported it,
|
|
108
|
-
// we don't need to follow the import.
|
|
109
|
-
false,
|
|
110
|
-
),
|
|
111
|
-
);
|
|
112
|
-
file.operations.forEach((op) =>
|
|
113
|
-
op.source.expressions.forEach((expr) =>
|
|
114
|
-
add(
|
|
115
|
-
expr,
|
|
116
|
-
// Imports that are used in graphql expressions definitely need to be followed.
|
|
117
|
-
true,
|
|
118
|
-
),
|
|
119
|
-
),
|
|
120
|
-
);
|
|
121
|
-
return Object.keys(paths);
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
export const processFile = (
|
|
125
|
-
filePath: string,
|
|
126
|
-
contents: string | {text: string, resolvedPath: string},
|
|
127
|
-
): FileResult => {
|
|
128
|
-
const dir = path.dirname(filePath);
|
|
129
|
-
const result: FileResult = {
|
|
130
|
-
path: filePath,
|
|
131
|
-
operations: [],
|
|
132
|
-
exports: {},
|
|
133
|
-
locals: {},
|
|
134
|
-
errors: [],
|
|
135
|
-
};
|
|
136
|
-
const resolved =
|
|
137
|
-
typeof contents === 'string' ? filePath : contents.resolvedPath;
|
|
138
|
-
const text = typeof contents === 'string' ? contents : contents.text;
|
|
139
|
-
const plugins = resolved.match(/\.tsx?$/)
|
|
140
|
-
? ['typescript', filePath.endsWith('x') ? 'jsx' : null].filter(Boolean)
|
|
141
|
-
: [['flow', {enums: true}], 'jsx'];
|
|
142
|
-
/* eslint-disable flowtype-errors/uncovered */
|
|
143
|
-
const ast: BabelNodeFile = parse(text, {
|
|
144
|
-
sourceType: 'module',
|
|
145
|
-
allowImportExportEverywhere: true,
|
|
146
|
-
plugins: plugins,
|
|
147
|
-
});
|
|
148
|
-
/* eslint-enable flowtype-errors/uncovered */
|
|
149
|
-
const gqlTagNames = [];
|
|
150
|
-
const seenTemplates: {[key: number]: Document | false} = {};
|
|
151
|
-
|
|
152
|
-
ast.program.body.forEach((toplevel) => {
|
|
153
|
-
if (toplevel.type === 'ImportDeclaration') {
|
|
154
|
-
const newLocals = getLocals(dir, toplevel, filePath);
|
|
155
|
-
if (newLocals) {
|
|
156
|
-
Object.keys(newLocals).forEach((k) => {
|
|
157
|
-
const local = newLocals[k];
|
|
158
|
-
if (local.path.startsWith('/')) {
|
|
159
|
-
result.locals[k] = local;
|
|
160
|
-
}
|
|
161
|
-
if (
|
|
162
|
-
local.path === 'graphql-tag' &&
|
|
163
|
-
local.name === 'default'
|
|
164
|
-
) {
|
|
165
|
-
gqlTagNames.push(k);
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
if (toplevel.type === 'ExportNamedDeclaration') {
|
|
171
|
-
if (toplevel.source) {
|
|
172
|
-
const source = toplevel.source;
|
|
173
|
-
const importPath = source.value.startsWith('.')
|
|
174
|
-
? path.resolve(path.join(dir, source.value))
|
|
175
|
-
: source.value;
|
|
176
|
-
toplevel.specifiers?.forEach((spec) => {
|
|
177
|
-
if (
|
|
178
|
-
spec.type === 'ExportSpecifier' &&
|
|
179
|
-
spec.exported.type === 'Identifier'
|
|
180
|
-
) {
|
|
181
|
-
result.exports[spec.exported.name] = {
|
|
182
|
-
type: 'import',
|
|
183
|
-
name: spec.local.name,
|
|
184
|
-
path: importPath,
|
|
185
|
-
loc: {
|
|
186
|
-
start: spec.start ?? -1,
|
|
187
|
-
end: spec.end ?? -1,
|
|
188
|
-
line: spec.loc?.start.line ?? -1,
|
|
189
|
-
path: filePath,
|
|
190
|
-
},
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
} else {
|
|
195
|
-
toplevel.specifiers?.forEach((spec) => {
|
|
196
|
-
if (spec.type === 'ExportSpecifier') {
|
|
197
|
-
const local = result.locals[spec.local.name];
|
|
198
|
-
if (local && spec.exported.type === 'Identifier') {
|
|
199
|
-
result.exports[spec.exported.name] = local;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const processDeclarator = (
|
|
207
|
-
decl: BabelNodeVariableDeclarator,
|
|
208
|
-
isExported: boolean,
|
|
209
|
-
) => {
|
|
210
|
-
if (decl.id.type !== 'Identifier' || !decl.init) {
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
const {init} = decl;
|
|
214
|
-
const id = decl.id.name;
|
|
215
|
-
if (
|
|
216
|
-
init.type === 'TaggedTemplateExpression' &&
|
|
217
|
-
init.tag.type === 'Identifier'
|
|
218
|
-
) {
|
|
219
|
-
if (gqlTagNames.includes(init.tag.name)) {
|
|
220
|
-
const tpl = processTemplate(init, result);
|
|
221
|
-
if (tpl) {
|
|
222
|
-
const document = (result.locals[id] = {
|
|
223
|
-
type: 'document',
|
|
224
|
-
source: tpl,
|
|
225
|
-
});
|
|
226
|
-
seenTemplates[init.start ?? -1] = document;
|
|
227
|
-
if (isExported) {
|
|
228
|
-
result.exports[id] = document;
|
|
229
|
-
}
|
|
230
|
-
} else {
|
|
231
|
-
seenTemplates[init.start ?? -1] = false;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
if (init.type === 'Identifier' && result.locals[init.name]) {
|
|
236
|
-
result.locals[id] = result.locals[init.name];
|
|
237
|
-
if (isExported) {
|
|
238
|
-
result.exports[id] = result.locals[init.name];
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
if (toplevel.type === 'VariableDeclaration') {
|
|
244
|
-
toplevel.declarations.forEach((decl) => {
|
|
245
|
-
processDeclarator(decl, false);
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (
|
|
250
|
-
toplevel.type === 'ExportNamedDeclaration' &&
|
|
251
|
-
toplevel.declaration?.type === 'VariableDeclaration'
|
|
252
|
-
) {
|
|
253
|
-
toplevel.declaration.declarations.forEach((decl) => {
|
|
254
|
-
processDeclarator(decl, true);
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
const visitTpl = (
|
|
260
|
-
node: BabelNodeTaggedTemplateExpression,
|
|
261
|
-
getBinding: (name: string) => Document | null,
|
|
262
|
-
) => {
|
|
263
|
-
if (seenTemplates[node.start ?? -1] != null) {
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
if (
|
|
267
|
-
node.tag.type !== 'Identifier' ||
|
|
268
|
-
!gqlTagNames.includes(node.tag.name)
|
|
269
|
-
) {
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
const tpl = processTemplate(node, result, getBinding);
|
|
273
|
-
if (tpl) {
|
|
274
|
-
seenTemplates[node.start ?? -1] = {type: 'document', source: tpl};
|
|
275
|
-
result.operations.push({
|
|
276
|
-
source: tpl,
|
|
277
|
-
});
|
|
278
|
-
} else {
|
|
279
|
-
seenTemplates[node.start ?? -1] = false;
|
|
280
|
-
}
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
/* eslint-disable flowtype-errors/uncovered */
|
|
284
|
-
traverse(ast, {
|
|
285
|
-
TaggedTemplateExpression(path) {
|
|
286
|
-
visitTpl(path.node, (name) => {
|
|
287
|
-
const binding = path.scope.getBinding(name);
|
|
288
|
-
const start = binding.path.node.init
|
|
289
|
-
? binding.path.node.init.start
|
|
290
|
-
: null;
|
|
291
|
-
if (start && seenTemplates[start]) {
|
|
292
|
-
return seenTemplates[start];
|
|
293
|
-
}
|
|
294
|
-
return null;
|
|
295
|
-
});
|
|
296
|
-
},
|
|
297
|
-
});
|
|
298
|
-
/* eslint-enable flowtype-errors/uncovered */
|
|
299
|
-
|
|
300
|
-
return result;
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
const processTemplate = (
|
|
304
|
-
tpl: BabelNodeTaggedTemplateExpression,
|
|
305
|
-
result: FileResult,
|
|
306
|
-
getTemplate?: (name: string) => Document | null,
|
|
307
|
-
// getBinding?: (name: string) => Binding,
|
|
308
|
-
// seenTemplates,
|
|
309
|
-
): ?Template => {
|
|
310
|
-
// 'cooked' is the string as runtime javascript will see it.
|
|
311
|
-
const literals = tpl.quasi.quasis.map((q) => q.value.cooked || '');
|
|
312
|
-
const expressions = tpl.quasi.expressions.map(
|
|
313
|
-
(expr): null | Document | Import => {
|
|
314
|
-
const loc: Loc = {
|
|
315
|
-
start: expr.start ?? -1,
|
|
316
|
-
end: expr.end ?? -1,
|
|
317
|
-
line: expr.loc?.start.line ?? -1,
|
|
318
|
-
path: result.path,
|
|
319
|
-
};
|
|
320
|
-
if (expr.type !== 'Identifier') {
|
|
321
|
-
result.errors.push({
|
|
322
|
-
loc,
|
|
323
|
-
message: `Template literal interpolation must be an identifier`,
|
|
324
|
-
});
|
|
325
|
-
return null;
|
|
326
|
-
}
|
|
327
|
-
if (!result.locals[expr.name]) {
|
|
328
|
-
if (getTemplate) {
|
|
329
|
-
const found = getTemplate(expr.name);
|
|
330
|
-
return found;
|
|
331
|
-
}
|
|
332
|
-
result.errors.push({
|
|
333
|
-
loc,
|
|
334
|
-
message: `Unable to resolve ${expr.name}`,
|
|
335
|
-
});
|
|
336
|
-
return null;
|
|
337
|
-
}
|
|
338
|
-
return result.locals[expr.name];
|
|
339
|
-
},
|
|
340
|
-
);
|
|
341
|
-
if (expressions.includes(null)) {
|
|
342
|
-
// bail, stop processing.
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
return {
|
|
346
|
-
literals,
|
|
347
|
-
expressions: expressions.filter(Boolean),
|
|
348
|
-
loc: {
|
|
349
|
-
line: tpl.loc?.start.line ?? -1,
|
|
350
|
-
start: tpl.start ?? -1,
|
|
351
|
-
end: tpl.end ?? -1,
|
|
352
|
-
path: result.path,
|
|
353
|
-
},
|
|
354
|
-
};
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
const getLocals = (
|
|
358
|
-
dir,
|
|
359
|
-
toplevel: BabelNodeImportDeclaration,
|
|
360
|
-
myPath: string,
|
|
361
|
-
): ?{[key: string]: Import} => {
|
|
362
|
-
if (toplevel.importKind === 'type') {
|
|
363
|
-
return null;
|
|
364
|
-
}
|
|
365
|
-
const importPath = toplevel.source.value.startsWith('.')
|
|
366
|
-
? path.resolve(path.join(dir, toplevel.source.value))
|
|
367
|
-
: toplevel.source.value;
|
|
368
|
-
const locals = {};
|
|
369
|
-
toplevel.specifiers.forEach((spec) => {
|
|
370
|
-
if (spec.type === 'ImportDefaultSpecifier') {
|
|
371
|
-
locals[spec.local.name] = {
|
|
372
|
-
type: 'import',
|
|
373
|
-
name: 'default',
|
|
374
|
-
path: importPath,
|
|
375
|
-
loc: {start: spec.start, end: spec.end, path: myPath},
|
|
376
|
-
};
|
|
377
|
-
} else if (spec.type === 'ImportSpecifier') {
|
|
378
|
-
locals[spec.local.name] = {
|
|
379
|
-
type: 'import',
|
|
380
|
-
name:
|
|
381
|
-
spec.imported.type === 'Identifier'
|
|
382
|
-
? spec.imported.name
|
|
383
|
-
: spec.imported.value,
|
|
384
|
-
path: importPath,
|
|
385
|
-
loc: {start: spec.start, end: spec.end, path: myPath},
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
});
|
|
389
|
-
return locals;
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
export const processFiles = (
|
|
393
|
-
filePaths: Array<string>,
|
|
394
|
-
getFileSource: (
|
|
395
|
-
path: string,
|
|
396
|
-
) => string | {text: string, resolvedPath: string},
|
|
397
|
-
): Files => {
|
|
398
|
-
const files: Files = {};
|
|
399
|
-
const toProcess = filePaths.slice();
|
|
400
|
-
while (toProcess.length) {
|
|
401
|
-
const next = toProcess.shift();
|
|
402
|
-
if (files[next]) {
|
|
403
|
-
continue;
|
|
404
|
-
}
|
|
405
|
-
const result = processFile(next, getFileSource(next));
|
|
406
|
-
files[next] = result;
|
|
407
|
-
listExternalReferences(result).forEach((path) => {
|
|
408
|
-
if (!files[path] && !toProcess.includes(path)) {
|
|
409
|
-
toProcess.push(path);
|
|
410
|
-
}
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
return files;
|
|
414
|
-
};
|
package/src/parser/resolve.js
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
import gql from 'graphql-tag';
|
|
3
|
-
import type {DocumentNode} from 'graphql/language/ast';
|
|
4
|
-
import type {FileResult, Files, Import, Template, Document} from './parse';
|
|
5
|
-
|
|
6
|
-
export type Resolved = {
|
|
7
|
-
[key: string]: {
|
|
8
|
-
document: DocumentNode,
|
|
9
|
-
raw: Template,
|
|
10
|
-
},
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const resolveDocuments = (
|
|
14
|
-
files: Files,
|
|
15
|
-
): {resolved: Resolved, errors: FileResult['errors']} => {
|
|
16
|
-
const resolved: Resolved = {};
|
|
17
|
-
const errors: FileResult['errors'] = [];
|
|
18
|
-
Object.keys(files).forEach((path) => {
|
|
19
|
-
const file = files[path];
|
|
20
|
-
file.operations.forEach((op) => {
|
|
21
|
-
resolveGqlTemplate(op.source, files, errors, resolved, {});
|
|
22
|
-
});
|
|
23
|
-
Object.keys(file.locals).forEach((k) => {
|
|
24
|
-
const local = file.locals[k];
|
|
25
|
-
if (local.type === 'document') {
|
|
26
|
-
resolveGqlTemplate(local.source, files, errors, resolved, {});
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
return {resolved, errors};
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const resolveImport = (
|
|
34
|
-
expr: Import,
|
|
35
|
-
files: Files,
|
|
36
|
-
errors: FileResult['errors'],
|
|
37
|
-
seen: {[key: string]: true},
|
|
38
|
-
): ?Document => {
|
|
39
|
-
if (seen[expr.path]) {
|
|
40
|
-
errors.push({
|
|
41
|
-
loc: expr.loc,
|
|
42
|
-
message: `Circular import ${Object.keys(seen).join(' -> ')} -> ${
|
|
43
|
-
expr.path
|
|
44
|
-
}`,
|
|
45
|
-
});
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
seen[expr.path] = true;
|
|
49
|
-
const res = files[expr.path];
|
|
50
|
-
if (!res) {
|
|
51
|
-
errors.push({loc: expr.loc, message: `No file ${expr.path}`});
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
if (!res.exports[expr.name]) {
|
|
55
|
-
errors.push({
|
|
56
|
-
loc: expr.loc,
|
|
57
|
-
message: `${expr.path} has no valid gql export ${expr.name}`,
|
|
58
|
-
});
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
const value = res.exports[expr.name];
|
|
62
|
-
if (value.type === 'import') {
|
|
63
|
-
return resolveImport(value, files, errors, seen);
|
|
64
|
-
}
|
|
65
|
-
return value;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const resolveGqlTemplate = (
|
|
69
|
-
template: Template,
|
|
70
|
-
files: Files,
|
|
71
|
-
errors: FileResult['errors'],
|
|
72
|
-
resolved: Resolved,
|
|
73
|
-
seen: {[key: string]: Template},
|
|
74
|
-
): ?DocumentNode => {
|
|
75
|
-
const key = template.loc.path + ':' + template.loc.line;
|
|
76
|
-
if (seen[key]) {
|
|
77
|
-
errors.push({
|
|
78
|
-
loc: template.loc,
|
|
79
|
-
message: `Recursive template dependency! ${Object.keys(seen)
|
|
80
|
-
.map(
|
|
81
|
-
(k) =>
|
|
82
|
-
k +
|
|
83
|
-
' ~ ' +
|
|
84
|
-
seen[k].expressions.length +
|
|
85
|
-
',' +
|
|
86
|
-
seen[k].literals.length,
|
|
87
|
-
)
|
|
88
|
-
.join(' -> ')} -> ${key}`,
|
|
89
|
-
});
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
seen[key] = template;
|
|
93
|
-
if (resolved[key]) {
|
|
94
|
-
return resolved[key].document;
|
|
95
|
-
}
|
|
96
|
-
const expressions = template.expressions.map((expr) => {
|
|
97
|
-
if (expr.type === 'import') {
|
|
98
|
-
const document = resolveImport(expr, files, errors, {});
|
|
99
|
-
return document
|
|
100
|
-
? resolveGqlTemplate(document.source, files, errors, resolved, {
|
|
101
|
-
...seen,
|
|
102
|
-
})
|
|
103
|
-
: null;
|
|
104
|
-
}
|
|
105
|
-
return resolveGqlTemplate(expr.source, files, errors, resolved, {
|
|
106
|
-
...seen,
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
if (expressions.includes(null)) {
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
resolved[key] = {
|
|
113
|
-
document: gql(template.literals, ...expressions),
|
|
114
|
-
raw: template,
|
|
115
|
-
};
|
|
116
|
-
return resolved[key].document;
|
|
117
|
-
};
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
/**
|
|
3
|
-
* Takes the introspectionQuery response and parses it into the "Schema"
|
|
4
|
-
* type that we use to look up types, interfaces, etc.
|
|
5
|
-
*/
|
|
6
|
-
import type {IntrospectionQuery} from 'graphql';
|
|
7
|
-
import type {Schema} from './types';
|
|
8
|
-
|
|
9
|
-
export const schemaFromIntrospectionData = (
|
|
10
|
-
schema: IntrospectionQuery,
|
|
11
|
-
): Schema => {
|
|
12
|
-
const result: Schema = {
|
|
13
|
-
interfacesByName: {},
|
|
14
|
-
typesByName: {},
|
|
15
|
-
inputObjectsByName: {},
|
|
16
|
-
unionsByName: {},
|
|
17
|
-
enumsByName: {},
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
schema.__schema.types.forEach((type) => {
|
|
21
|
-
if (type.kind === 'ENUM') {
|
|
22
|
-
result.enumsByName[type.name] = type;
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
if (type.kind === 'UNION') {
|
|
26
|
-
result.unionsByName[type.name] = type;
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
if (type.kind === 'INTERFACE') {
|
|
30
|
-
result.interfacesByName[type.name] = {
|
|
31
|
-
...type,
|
|
32
|
-
possibleTypesByName: {},
|
|
33
|
-
fieldsByName: {},
|
|
34
|
-
};
|
|
35
|
-
type.possibleTypes.forEach(
|
|
36
|
-
(p) =>
|
|
37
|
-
(result.interfacesByName[type.name].possibleTypesByName[
|
|
38
|
-
p.name
|
|
39
|
-
] = true),
|
|
40
|
-
);
|
|
41
|
-
type.fields.forEach((field) => {
|
|
42
|
-
result.interfacesByName[type.name].fieldsByName[field.name] =
|
|
43
|
-
field;
|
|
44
|
-
});
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
if (type.kind === 'INPUT_OBJECT') {
|
|
48
|
-
result.inputObjectsByName[type.name] = type;
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
if (type.kind === 'SCALAR') {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
result.typesByName[type.name] = {
|
|
55
|
-
...type,
|
|
56
|
-
fieldsByName: {},
|
|
57
|
-
};
|
|
58
|
-
if (!type.fields) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
type.fields.forEach((field) => {
|
|
63
|
-
result.typesByName[type.name].fieldsByName[field.name] = field;
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
return result;
|
|
68
|
-
};
|
package/src/types.js
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
|
|
3
|
-
import type {BabelNode} from '@babel/types';
|
|
4
|
-
import type {
|
|
5
|
-
FragmentDefinitionNode,
|
|
6
|
-
IntrospectionEnumType,
|
|
7
|
-
IntrospectionField,
|
|
8
|
-
IntrospectionInputObjectType,
|
|
9
|
-
IntrospectionInterfaceType,
|
|
10
|
-
IntrospectionObjectType,
|
|
11
|
-
IntrospectionUnionType,
|
|
12
|
-
SelectionNode,
|
|
13
|
-
} from 'graphql';
|
|
14
|
-
|
|
15
|
-
export type Selections = $ReadOnlyArray<SelectionNode>;
|
|
16
|
-
|
|
17
|
-
export type GenerateConfig = {|
|
|
18
|
-
schemaFilePath: string,
|
|
19
|
-
match?: Array<RegExp | string>,
|
|
20
|
-
exclude?: Array<RegExp | string>,
|
|
21
|
-
|
|
22
|
-
typeScript?: boolean,
|
|
23
|
-
scalars?: Scalars,
|
|
24
|
-
strictNullability?: boolean,
|
|
25
|
-
/**
|
|
26
|
-
* The command that users should run to regenerate the types files.
|
|
27
|
-
*/
|
|
28
|
-
regenerateCommand?: string,
|
|
29
|
-
readOnlyArray?: boolean,
|
|
30
|
-
splitTypes?: boolean,
|
|
31
|
-
generatedDirectory?: string,
|
|
32
|
-
exportAllObjectTypes?: boolean,
|
|
33
|
-
typeFileName?: string,
|
|
34
|
-
experimentalEnums?: boolean,
|
|
35
|
-
|};
|
|
36
|
-
|
|
37
|
-
export type CrawlConfig = {
|
|
38
|
-
root: string,
|
|
39
|
-
pragma?: string,
|
|
40
|
-
loosePragma?: string,
|
|
41
|
-
ignorePragma?: string,
|
|
42
|
-
dumpOperations?: string,
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export type Config = {
|
|
46
|
-
crawl: CrawlConfig,
|
|
47
|
-
generate: GenerateConfig | Array<GenerateConfig>,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
export type Schema = {
|
|
51
|
-
interfacesByName: {
|
|
52
|
-
[key: string]: IntrospectionInterfaceType & {
|
|
53
|
-
fieldsByName: {[key: string]: IntrospectionField},
|
|
54
|
-
possibleTypesByName: {[key: string]: boolean},
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
inputObjectsByName: {
|
|
58
|
-
[key: string]: IntrospectionInputObjectType,
|
|
59
|
-
},
|
|
60
|
-
typesByName: {
|
|
61
|
-
[key: string]: IntrospectionObjectType & {
|
|
62
|
-
fieldsByName: {[key: string]: IntrospectionField},
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
unionsByName: {
|
|
66
|
-
[key: string]: IntrospectionUnionType,
|
|
67
|
-
},
|
|
68
|
-
enumsByName: {
|
|
69
|
-
[key: string]: IntrospectionEnumType,
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export type Context = {
|
|
74
|
-
path: Array<string>,
|
|
75
|
-
strictNullability: boolean,
|
|
76
|
-
readOnlyArray: boolean,
|
|
77
|
-
fragments: {[key: string]: FragmentDefinitionNode},
|
|
78
|
-
|
|
79
|
-
schema: Schema,
|
|
80
|
-
scalars: Scalars,
|
|
81
|
-
errors: Array<string>,
|
|
82
|
-
allObjectTypes: null | {[key: string]: BabelNode},
|
|
83
|
-
typeScript: boolean,
|
|
84
|
-
|
|
85
|
-
experimentalEnumsMap?: {[key: string]: BabelNode}, // index signature that is populated with declarations
|
|
86
|
-
};
|
|
87
|
-
export type Scalars = {[key: string]: 'string' | 'number' | 'boolean'};
|