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