@khanacademy/graphql-flow 3.0.1 → 3.1.1

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.
Files changed (40) hide show
  1. package/.eslintrc.js +15 -4
  2. package/.github/workflows/changeset-release.yml +3 -5
  3. package/.github/workflows/pr-checks.yml +17 -17
  4. package/.prettierrc +2 -2
  5. package/.vscode/settings.json +6 -0
  6. package/CHANGELOG.md +12 -0
  7. package/dist/cli/config.js +6 -15
  8. package/dist/cli/run.js +26 -49
  9. package/dist/enums.js +9 -9
  10. package/dist/generateResponseType.js +36 -40
  11. package/dist/generateTypeFiles.js +10 -10
  12. package/dist/generateVariablesType.js +12 -13
  13. package/dist/index.js +4 -6
  14. package/dist/parser/parse.js +48 -56
  15. package/dist/parser/resolve.js +20 -16
  16. package/dist/parser/utils.js +29 -16
  17. package/dist/schemaFromIntrospectionData.js +5 -5
  18. package/dist/utils.js +8 -8
  19. package/package.json +12 -13
  20. package/schema.json +18 -2
  21. package/src/__test__/generateTypeFileContents.test.ts +26 -25
  22. package/src/__test__/graphql-flow.test.ts +32 -33
  23. package/src/__test__/processPragmas.test.ts +14 -13
  24. package/src/cli/__test__/config.test.ts +51 -56
  25. package/src/cli/config.ts +23 -20
  26. package/src/cli/run.ts +38 -52
  27. package/src/enums.ts +17 -17
  28. package/src/generateResponseType.ts +120 -91
  29. package/src/generateTypeFiles.ts +24 -22
  30. package/src/generateVariablesType.ts +20 -20
  31. package/src/index.ts +28 -29
  32. package/src/parser/__test__/parse.test.ts +114 -23
  33. package/src/parser/__test__/utils.test.ts +80 -0
  34. package/src/parser/parse.ts +126 -109
  35. package/src/parser/resolve.ts +64 -34
  36. package/src/parser/utils.ts +25 -15
  37. package/src/schemaFromIntrospectionData.ts +10 -8
  38. package/src/types.ts +57 -53
  39. package/src/utils.ts +30 -16
  40. package/tools/find-files-with-gql.ts +7 -11
@@ -1,17 +1,18 @@
1
- import {isTruthy} from '@khanacademy/wonder-stuff-core';
1
+ import {isTruthy} from "@khanacademy/wonder-stuff-core";
2
2
  import type {
3
- BabelNodeImportDeclaration,
4
- BabelNodeVariableDeclarator,
5
- BabelNodeTaggedTemplateExpression,
6
- BabelNodeFile,
7
- } from '@babel/types';
3
+ ImportDeclaration,
4
+ VariableDeclarator,
5
+ TaggedTemplateExpression,
6
+ File,
7
+ } from "@babel/types";
8
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
9
+ import {parse, ParserPlugin} from "@babel/parser";
10
+ import traverse from "@babel/traverse";
11
11
 
12
- import path from 'path';
12
+ import path from "path";
13
13
 
14
- import {getPathWithExtension} from './utils';
14
+ import {fixPathResolution, getPathWithExtension} from "./utils";
15
+ import {Config} from "../types";
15
16
 
16
17
  /**
17
18
  * This file is responsible for finding all gql`-annotated
@@ -43,52 +44,52 @@ import {getPathWithExtension} from './utils';
43
44
  */
44
45
 
45
46
  export type Template = {
46
- literals: Array<string>
47
- expressions: Array<Document | Import>
48
- loc: Loc
47
+ literals: Array<string>;
48
+ expressions: Array<Document | Import>;
49
+ loc: Loc;
49
50
  };
50
51
  export type Loc = {
51
- start: number
52
- end: number
53
- path: string
54
- line: number
52
+ start: number;
53
+ end: number;
54
+ path: string;
55
+ line: number;
55
56
  };
56
57
 
57
58
  export type Document = {
58
- type: 'document'
59
- source: Template
59
+ type: "document";
60
+ source: Template;
60
61
  };
61
62
  export type Import = {
62
- type: 'import'
63
- name: string
64
- path: string
65
- loc: Loc
63
+ type: "import";
64
+ name: string;
65
+ path: string;
66
+ loc: Loc;
66
67
  };
67
68
 
68
69
  export type Operation = {
69
- source: Template
70
+ source: Template;
70
71
  // TODO: Determine if an operation is already wrapped
71
72
  // in `gqlOp` so we can automatically wrap if needed.
72
73
  // needsWrapping: boolean,
73
74
  };
74
75
 
75
76
  export type FileResult = {
76
- path: string
77
- operations: Array<Operation>
77
+ path: string;
78
+ operations: Array<Operation>;
78
79
  exports: {
79
- [key: string]: Document | Import
80
- }
80
+ [key: string]: Document | Import;
81
+ };
81
82
  locals: {
82
- [key: string]: Document | Import
83
- }
83
+ [key: string]: Document | Import;
84
+ };
84
85
  errors: Array<{
85
- loc: Loc
86
- message: string
87
- }>
86
+ loc: Loc;
87
+ message: string;
88
+ }>;
88
89
  };
89
90
 
90
91
  export type Files = {
91
- [path: string]: FileResult
92
+ [path: string]: FileResult;
92
93
  };
93
94
 
94
95
  /**
@@ -99,12 +100,15 @@ export type Files = {
99
100
  * potentially relevant, and of course any values referenced
100
101
  * from a graphql template are treated as relevant.
101
102
  */
102
- const listExternalReferences = (file: FileResult): Array<string> => {
103
+ const listExternalReferences = (
104
+ file: FileResult,
105
+ config: Config,
106
+ ): Array<string> => {
103
107
  const paths: Record<string, any> = {};
104
108
  const add = (v: Document | Import, followImports: boolean) => {
105
- if (v.type === 'import') {
109
+ if (v.type === "import") {
106
110
  if (followImports) {
107
- const absPath = getPathWithExtension(v.path);
111
+ const absPath = getPathWithExtension(v.path, config);
108
112
  if (absPath) {
109
113
  paths[absPath] = true;
110
114
  }
@@ -142,10 +146,13 @@ const listExternalReferences = (file: FileResult): Array<string> => {
142
146
 
143
147
  export const processFile = (
144
148
  filePath: string,
145
- contents: string | {
146
- text: string
147
- resolvedPath: string
148
- },
149
+ contents:
150
+ | string
151
+ | {
152
+ text: string;
153
+ resolvedPath: string;
154
+ },
155
+ config: Config,
149
156
  ): FileResult => {
150
157
  const dir = path.dirname(filePath);
151
158
  const result: FileResult = {
@@ -155,55 +162,51 @@ export const processFile = (
155
162
  locals: {},
156
163
  errors: [],
157
164
  };
158
- const resolved =
159
- typeof contents === 'string' ? filePath : contents.resolvedPath;
160
- const text = typeof contents === 'string' ? contents : contents.text;
161
- const plugins = resolved.match(/\.tsx?$/)
162
- ? ['typescript', filePath.endsWith('x') ? 'jsx' : null].filter(isTruthy)
163
- : [['flow', {enums: true}], 'jsx'];
164
- /* eslint-disable flowtype-errors/uncovered */
165
- const ast: BabelNodeFile = parse(text, {
166
- sourceType: 'module',
165
+ const text = typeof contents === "string" ? contents : contents.text;
166
+ const plugins: Array<ParserPlugin> = filePath.endsWith("x")
167
+ ? ["typescript", "jsx"]
168
+ : ["typescript"];
169
+ const ast: File = parse(text, {
170
+ sourceType: "module",
167
171
  allowImportExportEverywhere: true,
168
172
  plugins: plugins,
169
173
  });
170
- /* eslint-enable flowtype-errors/uncovered */
171
- const gqlTagNames = [];
174
+ const gqlTagNames: Array<string> = [];
172
175
  const seenTemplates: {
173
- [key: number]: Document | false
176
+ [key: number]: Document | false;
174
177
  } = {};
175
178
 
176
179
  ast.program.body.forEach((toplevel) => {
177
- if (toplevel.type === 'ImportDeclaration') {
178
- const newLocals = getLocals(dir, toplevel, filePath);
180
+ if (toplevel.type === "ImportDeclaration") {
181
+ const newLocals = getLocals(dir, toplevel, filePath, config);
179
182
  if (newLocals) {
180
183
  Object.keys(newLocals).forEach((k) => {
181
184
  const local = newLocals[k];
182
- if (local.path.startsWith('/')) {
185
+ if (local.path.startsWith("/")) {
183
186
  result.locals[k] = local;
184
187
  }
185
188
  if (
186
- local.path === 'graphql-tag' &&
187
- local.name === 'default'
189
+ local.path === "graphql-tag" &&
190
+ local.name === "default"
188
191
  ) {
189
192
  gqlTagNames.push(k);
190
193
  }
191
194
  });
192
195
  }
193
196
  }
194
- if (toplevel.type === 'ExportNamedDeclaration') {
197
+ if (toplevel.type === "ExportNamedDeclaration") {
195
198
  if (toplevel.source) {
196
199
  const source = toplevel.source;
197
- const importPath = source.value.startsWith('.')
200
+ const importPath = source.value.startsWith(".")
198
201
  ? path.resolve(path.join(dir, source.value))
199
202
  : source.value;
200
- toplevel.specifiers?.forEach((spec: unknown) => {
203
+ toplevel.specifiers?.forEach((spec) => {
201
204
  if (
202
- spec.type === 'ExportSpecifier' &&
203
- spec.exported.type === 'Identifier'
205
+ spec.type === "ExportSpecifier" &&
206
+ spec.exported.type === "Identifier"
204
207
  ) {
205
208
  result.exports[spec.exported.name] = {
206
- type: 'import',
209
+ type: "import",
207
210
  name: spec.local.name,
208
211
  path: importPath,
209
212
  loc: {
@@ -216,10 +219,10 @@ export const processFile = (
216
219
  }
217
220
  });
218
221
  } else {
219
- toplevel.specifiers?.forEach((spec: unknown) => {
220
- if (spec.type === 'ExportSpecifier') {
222
+ toplevel.specifiers?.forEach((spec) => {
223
+ if (spec.type === "ExportSpecifier") {
221
224
  const local = result.locals[spec.local.name];
222
- if (local && spec.exported.type === 'Identifier') {
225
+ if (local && spec.exported.type === "Identifier") {
223
226
  result.exports[spec.exported.name] = local;
224
227
  }
225
228
  }
@@ -228,23 +231,23 @@ export const processFile = (
228
231
  }
229
232
 
230
233
  const processDeclarator = (
231
- decl: BabelNodeVariableDeclarator,
234
+ decl: VariableDeclarator,
232
235
  isExported: boolean,
233
236
  ) => {
234
- if (decl.id.type !== 'Identifier' || !decl.init) {
237
+ if (decl.id.type !== "Identifier" || !decl.init) {
235
238
  return;
236
239
  }
237
240
  const {init} = decl;
238
241
  const id = decl.id.name;
239
242
  if (
240
- init.type === 'TaggedTemplateExpression' &&
241
- init.tag.type === 'Identifier'
243
+ init.type === "TaggedTemplateExpression" &&
244
+ init.tag.type === "Identifier"
242
245
  ) {
243
246
  if (gqlTagNames.includes(init.tag.name)) {
244
247
  const tpl = processTemplate(init, result);
245
248
  if (tpl) {
246
249
  const document = (result.locals[id] = {
247
- type: 'document',
250
+ type: "document",
248
251
  source: tpl,
249
252
  });
250
253
  seenTemplates[init.start ?? -1] = document;
@@ -256,7 +259,7 @@ export const processFile = (
256
259
  }
257
260
  }
258
261
  }
259
- if (init.type === 'Identifier' && result.locals[init.name]) {
262
+ if (init.type === "Identifier" && result.locals[init.name]) {
260
263
  result.locals[id] = result.locals[init.name];
261
264
  if (isExported) {
262
265
  result.exports[id] = result.locals[init.name];
@@ -264,15 +267,15 @@ export const processFile = (
264
267
  }
265
268
  };
266
269
 
267
- if (toplevel.type === 'VariableDeclaration') {
270
+ if (toplevel.type === "VariableDeclaration") {
268
271
  toplevel.declarations.forEach((decl) => {
269
272
  processDeclarator(decl, false);
270
273
  });
271
274
  }
272
275
 
273
276
  if (
274
- toplevel.type === 'ExportNamedDeclaration' &&
275
- toplevel.declaration?.type === 'VariableDeclaration'
277
+ toplevel.type === "ExportNamedDeclaration" &&
278
+ toplevel.declaration?.type === "VariableDeclaration"
276
279
  ) {
277
280
  toplevel.declaration.declarations.forEach((decl) => {
278
281
  processDeclarator(decl, true);
@@ -281,21 +284,21 @@ export const processFile = (
281
284
  });
282
285
 
283
286
  const visitTpl = (
284
- node: BabelNodeTaggedTemplateExpression,
287
+ node: TaggedTemplateExpression,
285
288
  getBinding: (name: string) => Document | null,
286
289
  ) => {
287
290
  if (seenTemplates[node.start ?? -1] != null) {
288
291
  return;
289
292
  }
290
293
  if (
291
- node.tag.type !== 'Identifier' ||
294
+ node.tag.type !== "Identifier" ||
292
295
  !gqlTagNames.includes(node.tag.name)
293
296
  ) {
294
297
  return;
295
298
  }
296
299
  const tpl = processTemplate(node, result, getBinding);
297
300
  if (tpl) {
298
- seenTemplates[node.start ?? -1] = {type: 'document', source: tpl};
301
+ seenTemplates[node.start ?? -1] = {type: "document", source: tpl};
299
302
  result.operations.push({
300
303
  source: tpl,
301
304
  });
@@ -304,35 +307,37 @@ export const processFile = (
304
307
  }
305
308
  };
306
309
 
307
- /* eslint-disable flowtype-errors/uncovered */
308
310
  traverse(ast, {
309
- TaggedTemplateExpression(path: unknown) {
311
+ TaggedTemplateExpression(path) {
310
312
  visitTpl(path.node, (name) => {
311
313
  const binding = path.scope.getBinding(name);
312
- const start = binding.path.node.init
313
- ? binding.path.node.init.start
314
- : null;
314
+ if (!binding) {
315
+ return null;
316
+ }
317
+ const start =
318
+ "init" in binding.path.node && binding.path.node.init
319
+ ? binding.path.node.init.start
320
+ : null;
315
321
  if (start && seenTemplates[start]) {
316
- return seenTemplates[start];
322
+ return seenTemplates[start] || null;
317
323
  }
318
324
  return null;
319
325
  });
320
326
  },
321
327
  });
322
- /* eslint-enable flowtype-errors/uncovered */
323
328
 
324
329
  return result;
325
330
  };
326
331
 
327
332
  const processTemplate = (
328
- tpl: BabelNodeTaggedTemplateExpression,
333
+ tpl: TaggedTemplateExpression,
329
334
  result: FileResult,
330
335
  // getBinding?: (name: string) => Binding,
331
336
  // seenTemplates,
332
337
  getTemplate?: (name: string) => Document | null,
333
338
  ): Template | null | undefined => {
334
339
  // 'cooked' is the string as runtime javascript will see it.
335
- const literals = tpl.quasi.quasis.map((q) => q.value.cooked || '');
340
+ const literals = tpl.quasi.quasis.map((q) => q.value.cooked || "");
336
341
  const expressions = tpl.quasi.expressions.map(
337
342
  (expr): null | Document | Import => {
338
343
  const loc: Loc = {
@@ -341,7 +346,7 @@ const processTemplate = (
341
346
  line: expr.loc?.start.line ?? -1,
342
347
  path: result.path,
343
348
  };
344
- if (expr.type !== 'Identifier') {
349
+ if (expr.type !== "Identifier") {
345
350
  result.errors.push({
346
351
  loc,
347
352
  message: `Template literal interpolation must be an identifier`,
@@ -378,29 +383,38 @@ const processTemplate = (
378
383
  };
379
384
  };
380
385
 
381
- const getLocals = (dir: unknown, toplevel: BabelNodeImportDeclaration, myPath: string): {
382
- [key: string]: Import
383
- } | null | undefined => {
384
- if (toplevel.importKind === 'type') {
386
+ const getLocals = (
387
+ dir: string,
388
+ toplevel: ImportDeclaration,
389
+ myPath: string,
390
+ config: Config,
391
+ ):
392
+ | {
393
+ [key: string]: Import;
394
+ }
395
+ | null
396
+ | undefined => {
397
+ if (toplevel.importKind === "type") {
385
398
  return null;
386
399
  }
387
- const importPath = toplevel.source.value.startsWith('.')
388
- ? path.resolve(path.join(dir, toplevel.source.value))
389
- : toplevel.source.value;
400
+ const fixedPath = fixPathResolution(toplevel.source.value, config);
401
+ const importPath = fixedPath.startsWith(".")
402
+ ? path.resolve(path.join(dir, fixedPath))
403
+ : fixedPath;
390
404
  const locals: Record<string, any> = {};
391
405
  toplevel.specifiers.forEach((spec) => {
392
- if (spec.type === 'ImportDefaultSpecifier') {
406
+ if (spec.type === "ImportDefaultSpecifier") {
393
407
  locals[spec.local.name] = {
394
- type: 'import',
395
- name: 'default',
408
+ type: "import",
409
+ name: "default",
396
410
  path: importPath,
397
411
  loc: {start: spec.start, end: spec.end, path: myPath},
398
412
  };
399
- } else if (spec.type === 'ImportSpecifier') {
413
+ } else if (spec.type === "ImportSpecifier") {
400
414
  locals[spec.local.name] = {
401
- type: 'import',
415
+ type: "import",
402
416
  name:
403
- spec.imported.type === 'Identifier'
417
+ spec.imported.type === "Identifier"
404
418
  ? spec.imported.name
405
419
  : spec.imported.value,
406
420
  path: importPath,
@@ -413,21 +427,24 @@ const getLocals = (dir: unknown, toplevel: BabelNodeImportDeclaration, myPath: s
413
427
 
414
428
  export const processFiles = (
415
429
  filePaths: Array<string>,
416
- getFileSource: (path: string) => string | {
417
- text: string
418
- resolvedPath: string
419
- },
430
+ config: Config,
431
+ getFileSource: (path: string) =>
432
+ | string
433
+ | {
434
+ text: string;
435
+ resolvedPath: string;
436
+ },
420
437
  ): Files => {
421
438
  const files: Files = {};
422
439
  const toProcess = filePaths.slice();
423
440
  while (toProcess.length) {
424
441
  const next = toProcess.shift();
425
- if (files[next]) {
442
+ if (!next || files[next]) {
426
443
  continue;
427
444
  }
428
- const result = processFile(next, getFileSource(next));
445
+ const result = processFile(next, getFileSource(next), config);
429
446
  files[next] = result;
430
- listExternalReferences(result).forEach((path) => {
447
+ listExternalReferences(result, config).forEach((path) => {
431
448
  if (!files[path] && !toProcess.includes(path)) {
432
449
  toProcess.push(path);
433
450
  }
@@ -1,30 +1,41 @@
1
- import gql from 'graphql-tag';
2
- import {getPathWithExtension} from './utils';
3
- import type {DocumentNode} from 'graphql/language/ast';
4
- import type {FileResult, Files, Import, Template, Document} from './parse';
1
+ import gql from "graphql-tag";
2
+ import {getPathWithExtension} from "./utils";
3
+ import type {DocumentNode} from "graphql/language/ast";
4
+ import type {FileResult, Files, Import, Template, Document} from "./parse";
5
+ import {Config} from "../types";
5
6
 
6
7
  export type Resolved = {
7
8
  [key: string]: {
8
- document: DocumentNode
9
- raw: Template
10
- }
9
+ document: DocumentNode;
10
+ raw: Template;
11
+ };
11
12
  };
12
13
 
13
- export const resolveDocuments = (files: Files): {
14
- resolved: Resolved
15
- errors: FileResult['errors']
14
+ export const resolveDocuments = (
15
+ files: Files,
16
+ config: Config,
17
+ ): {
18
+ resolved: Resolved;
19
+ errors: FileResult["errors"];
16
20
  } => {
17
21
  const resolved: Resolved = {};
18
- const errors: FileResult['errors'] = [];
22
+ const errors: FileResult["errors"] = [];
19
23
  Object.keys(files).forEach((path) => {
20
24
  const file = files[path];
21
25
  file.operations.forEach((op) => {
22
- resolveGqlTemplate(op.source, files, errors, resolved, {});
26
+ resolveGqlTemplate(op.source, files, errors, resolved, {}, config);
23
27
  });
24
28
  Object.keys(file.locals).forEach((k) => {
25
29
  const local = file.locals[k];
26
- if (local.type === 'document') {
27
- resolveGqlTemplate(local.source, files, errors, resolved, {});
30
+ if (local.type === "document") {
31
+ resolveGqlTemplate(
32
+ local.source,
33
+ files,
34
+ errors,
35
+ resolved,
36
+ {},
37
+ config,
38
+ );
28
39
  }
29
40
  });
30
41
  });
@@ -34,17 +45,21 @@ export const resolveDocuments = (files: Files): {
34
45
  const resolveImport = (
35
46
  expr: Import,
36
47
  files: Files,
37
- errors: FileResult['errors'],
48
+ errors: FileResult["errors"],
38
49
  seen: {
39
- [key: string]: true
50
+ [key: string]: true;
40
51
  },
52
+ config: Config,
41
53
  ): Document | null | undefined => {
42
- const absPath: string = getPathWithExtension(expr.path);
54
+ const absPath = getPathWithExtension(expr.path, config);
55
+ if (!absPath) {
56
+ return null;
57
+ }
43
58
  if (seen[absPath]) {
44
59
  errors.push({
45
60
  loc: expr.loc,
46
61
  message: `Circular import ${Object.keys(seen).join(
47
- ' -> ',
62
+ " -> ",
48
63
  )} -> ${absPath}`,
49
64
  });
50
65
  return null;
@@ -63,8 +78,8 @@ const resolveImport = (
63
78
  return null;
64
79
  }
65
80
  const value = res.exports[expr.name];
66
- if (value.type === 'import') {
67
- return resolveImport(value, files, errors, seen);
81
+ if (value.type === "import") {
82
+ return resolveImport(value, files, errors, seen, config);
68
83
  }
69
84
  return value;
70
85
  };
@@ -72,13 +87,14 @@ const resolveImport = (
72
87
  const resolveGqlTemplate = (
73
88
  template: Template,
74
89
  files: Files,
75
- errors: FileResult['errors'],
90
+ errors: FileResult["errors"],
76
91
  resolved: Resolved,
77
92
  seen: {
78
- [key: string]: Template
93
+ [key: string]: Template;
79
94
  },
95
+ config: Config,
80
96
  ): DocumentNode | null | undefined => {
81
- const key = template.loc.path + ':' + template.loc.line;
97
+ const key = template.loc.path + ":" + template.loc.line;
82
98
  if (seen[key]) {
83
99
  errors.push({
84
100
  loc: template.loc,
@@ -86,12 +102,12 @@ const resolveGqlTemplate = (
86
102
  .map(
87
103
  (k) =>
88
104
  k +
89
- ' ~ ' +
105
+ " ~ " +
90
106
  seen[k].expressions.length +
91
- ',' +
107
+ "," +
92
108
  seen[k].literals.length,
93
109
  )
94
- .join(' -> ')} -> ${key}`,
110
+ .join(" -> ")} -> ${key}`,
95
111
  });
96
112
  return null;
97
113
  }
@@ -100,17 +116,31 @@ const resolveGqlTemplate = (
100
116
  return resolved[key].document;
101
117
  }
102
118
  const expressions = template.expressions.map((expr) => {
103
- if (expr.type === 'import') {
104
- const document = resolveImport(expr, files, errors, {});
119
+ if (expr.type === "import") {
120
+ const document = resolveImport(expr, files, errors, {}, config);
105
121
  return document
106
- ? resolveGqlTemplate(document.source, files, errors, resolved, {
107
- ...seen,
108
- })
122
+ ? resolveGqlTemplate(
123
+ document.source,
124
+ files,
125
+ errors,
126
+ resolved,
127
+ {
128
+ ...seen,
129
+ },
130
+ config,
131
+ )
109
132
  : null;
110
133
  }
111
- return resolveGqlTemplate(expr.source, files, errors, resolved, {
112
- ...seen,
113
- });
134
+ return resolveGqlTemplate(
135
+ expr.source,
136
+ files,
137
+ errors,
138
+ resolved,
139
+ {
140
+ ...seen,
141
+ },
142
+ config,
143
+ );
114
144
  });
115
145
  if (expressions.includes(null)) {
116
146
  return null;
@@ -1,6 +1,20 @@
1
- import fs from 'fs';
1
+ import fs from "fs";
2
+ import {Config} from "../types";
2
3
 
3
- export const getPathWithExtension = (pathWithoutExtension: string): string => {
4
+ export const fixPathResolution = (path: string, config: Config) => {
5
+ if (config.alias) {
6
+ for (const {find, replacement} of config.alias) {
7
+ path = path.replace(find, replacement);
8
+ }
9
+ }
10
+ return path;
11
+ };
12
+
13
+ export const getPathWithExtension = (
14
+ pathWithoutExtension: string,
15
+ config: Config,
16
+ ) => {
17
+ pathWithoutExtension = fixPathResolution(pathWithoutExtension, config);
4
18
  if (
5
19
  /\.(less|css|png|gif|jpg|jpeg|js|jsx|ts|tsx|mjs)$/.test(
6
20
  pathWithoutExtension,
@@ -8,21 +22,17 @@ export const getPathWithExtension = (pathWithoutExtension: string): string => {
8
22
  ) {
9
23
  return pathWithoutExtension;
10
24
  }
11
- if (fs.existsSync(pathWithoutExtension + '.js')) {
12
- return pathWithoutExtension + '.js';
25
+ if (fs.existsSync(pathWithoutExtension + ".js")) {
26
+ return pathWithoutExtension + ".js";
13
27
  }
14
- if (fs.existsSync(pathWithoutExtension + '.jsx')) {
15
- return pathWithoutExtension + '.jsx';
28
+ if (fs.existsSync(pathWithoutExtension + ".jsx")) {
29
+ return pathWithoutExtension + ".jsx";
16
30
  }
17
- if (fs.existsSync(pathWithoutExtension + '.tsx')) {
18
- return pathWithoutExtension + '.tsx';
31
+ if (fs.existsSync(pathWithoutExtension + ".tsx")) {
32
+ return pathWithoutExtension + ".tsx";
19
33
  }
20
- if (fs.existsSync(pathWithoutExtension + '.ts')) {
21
- return pathWithoutExtension + '.ts';
34
+ if (fs.existsSync(pathWithoutExtension + ".ts")) {
35
+ return pathWithoutExtension + ".ts";
22
36
  }
23
- // NOTE(john): This is a bit of a hack, but it's necessary for when we
24
- // have a file that doesn't exist. This will happen when we delete all of
25
- // the type files before re-running graphql-flow again. We want to ensure
26
- // that we don't error out in this case.
27
- return "";
37
+ return null;
28
38
  };