@khanacademy/graphql-flow 3.4.2 → 4.0.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.
@@ -1,4 +1,7 @@
1
- import {describe, it, expect} from "@jest/globals";
1
+ /**
2
+ * @jest-environment node
3
+ */
4
+ import {describe, it, expect, afterEach} from "@jest/globals";
2
5
 
3
6
  import {Config} from "../../types";
4
7
  import {processFiles} from "../parse";
@@ -6,6 +9,32 @@ import {resolveDocuments} from "../resolve";
6
9
 
7
10
  import {print} from "graphql/language/printer";
8
11
 
12
+ jest.mock("../resolveImport", () => ({
13
+ resetImportCache: jest.fn(),
14
+ resolveImportPath: jest
15
+ .fn()
16
+ .mockImplementation((sourceFile: string, importPath: string) => {
17
+ const path = require("path");
18
+ if (importPath === "graphql-tag") {
19
+ return "/repo/node_modules/graphql-tag/index.js";
20
+ }
21
+ if (importPath === "monorepo-package/fragment") {
22
+ return "/repo/node_modules/monorepo-package/fragment.js";
23
+ }
24
+ if (importPath.startsWith(".")) {
25
+ return path.resolve(path.dirname(sourceFile), importPath);
26
+ }
27
+ if (path.isAbsolute(importPath)) {
28
+ return importPath;
29
+ }
30
+ return null;
31
+ }),
32
+ }));
33
+
34
+ afterEach(() => {
35
+ jest.clearAllMocks();
36
+ });
37
+
9
38
  const fixtureFiles: {
10
39
  [key: string]:
11
40
  | string
@@ -14,6 +43,27 @@ const fixtureFiles: {
14
43
  resolvedPath: string;
15
44
  };
16
45
  } = {
46
+ "/repo/node_modules/monorepo-package/fragment.js": `
47
+ import gql from 'graphql-tag';
48
+
49
+ export const sharedFragment = gql\`
50
+ fragment SharedFields on Something {
51
+ id
52
+ }
53
+ \`;
54
+ `,
55
+ "/repo/packages/app/App.js": `
56
+ import gql from 'graphql-tag';
57
+ import {sharedFragment} from 'monorepo-package/fragment';
58
+ export const appQuery = gql\`
59
+ query AppQuery {
60
+ viewer {
61
+ ...SharedFields
62
+ }
63
+ }
64
+ \${sharedFragment}
65
+ \`;
66
+ `,
17
67
  "/firstFile.js": `
18
68
  // Note that you can import graphql-tag as
19
69
  // something other than gql.
@@ -57,6 +107,31 @@ const fixtureFiles: {
57
107
  \`;
58
108
  export {secondFragment};`,
59
109
 
110
+ "/starExportSource.js": `
111
+ import gql from 'graphql-tag';
112
+ export const starFragment = gql\`
113
+ fragment StarFragment on Star {
114
+ id
115
+ }
116
+ \`;
117
+ `,
118
+ "/starExportReexport.js": `
119
+ export * from './starExportSource.js';
120
+ `,
121
+ "/starExportConsumer.js": `
122
+ import gql from 'graphql-tag';
123
+ import {starFragment} from './starExportReexport.js';
124
+
125
+ export const starQuery = gql\`
126
+ query StarQuery {
127
+ stars {
128
+ ...StarFragment
129
+ }
130
+ }
131
+ \${starFragment}
132
+ \`;
133
+ `,
134
+
60
135
  "/thirdFile.js": `
61
136
  import {fromFirstFile, alsoFirst, secondFragment} from './secondFile.js';
62
137
  import gql from 'graphql-tag';
@@ -281,7 +356,7 @@ describe("processing fragments in various ways", () => {
281
356
  expect(files["/invalidThings.js"].errors.map((m: any) => m.message))
282
357
  .toMatchInlineSnapshot(`
283
358
  Array [
284
- "Unable to resolve someExternalFragment",
359
+ "Unable to resolve import someExternalFragment from \\"somewhere\\" at /invalidThings.js:4.",
285
360
  "Unable to resolve someUndefinedFragment",
286
361
  "Template literal interpolation must be an identifier",
287
362
  ]
@@ -338,4 +413,123 @@ describe("processing fragments in various ways", () => {
338
413
  );
339
414
  expect(printed).toMatchInlineSnapshot(`Object {}`);
340
415
  });
416
+
417
+ it("should resolve fragments re-exported via export all", () => {
418
+ // Arrange
419
+ const config: Config = {
420
+ crawl: {
421
+ root: "/here/we/crawl",
422
+ },
423
+ generate: {
424
+ match: [/\.fixture\.js$/],
425
+ exclude: [
426
+ "_test\\.js$",
427
+ "\\bcourse-editor-package\\b",
428
+ "\\.fixture\\.js$",
429
+ "\\b__flowtests__\\b",
430
+ "\\bcourse-editor\\b",
431
+ ],
432
+ readOnlyArray: false,
433
+ regenerateCommand: "make gqlflow",
434
+ scalars: {
435
+ JSONString: "string",
436
+ KALocale: "string",
437
+ NaiveDateTime: "string",
438
+ },
439
+ splitTypes: true,
440
+ generatedDirectory: "__graphql-types__",
441
+ exportAllObjectTypes: true,
442
+ schemaFilePath: "./composed_schema.graphql",
443
+ },
444
+ };
445
+ // Act
446
+ const files = processFiles(
447
+ ["/starExportConsumer.js"],
448
+ config,
449
+ getFileSource,
450
+ );
451
+ const {resolved} = resolveDocuments(files, config);
452
+ const printed: Record<string, any> = {};
453
+ Object.keys(resolved).map(
454
+ (k: any) => (printed[k] = print(resolved[k].document).trim()),
455
+ );
456
+
457
+ // Assert
458
+ expect(printed).toMatchInlineSnapshot(`
459
+ Object {
460
+ "/starExportConsumer.js:5": "query StarQuery {
461
+ stars {
462
+ ...StarFragment
463
+ }
464
+ }
465
+
466
+ fragment StarFragment on Star {
467
+ id
468
+ }",
469
+ "/starExportSource.js:3": "fragment StarFragment on Star {
470
+ id
471
+ }",
472
+ }
473
+ `);
474
+ });
475
+
476
+ it("should resolve fragments imported from monorepo packages", () => {
477
+ // Arrange
478
+ const config: Config = {
479
+ crawl: {
480
+ root: "/here/we/crawl",
481
+ },
482
+ generate: {
483
+ match: [/\.fixture\.js$/],
484
+ exclude: [
485
+ "_test\\.js$",
486
+ "\\bcourse-editor-package\\b",
487
+ "\\.fixture\\.js$",
488
+ "\\b__flowtests__\\b",
489
+ "\\bcourse-editor\\b",
490
+ ],
491
+ readOnlyArray: false,
492
+ regenerateCommand: "make gqlflow",
493
+ scalars: {
494
+ JSONString: "string",
495
+ KALocale: "string",
496
+ NaiveDateTime: "string",
497
+ },
498
+ splitTypes: true,
499
+ generatedDirectory: "__graphql-types__",
500
+ exportAllObjectTypes: true,
501
+ schemaFilePath: "./composed_schema.graphql",
502
+ },
503
+ };
504
+
505
+ // Act
506
+ const files = processFiles(
507
+ ["/repo/packages/app/App.js"],
508
+ config,
509
+ getFileSource,
510
+ );
511
+ const {resolved} = resolveDocuments(files, config);
512
+ const printed: Record<string, any> = {};
513
+ Object.keys(resolved).map(
514
+ (k: any) => (printed[k] = print(resolved[k].document).trim()),
515
+ );
516
+
517
+ // Assert
518
+ expect(printed).toMatchInlineSnapshot(`
519
+ Object {
520
+ "/repo/node_modules/monorepo-package/fragment.js:4": "fragment SharedFields on Something {
521
+ id
522
+ }",
523
+ "/repo/packages/app/App.js:4": "query AppQuery {
524
+ viewer {
525
+ ...SharedFields
526
+ }
527
+ }
528
+
529
+ fragment SharedFields on Something {
530
+ id
531
+ }",
532
+ }
533
+ `);
534
+ });
341
535
  });
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @jest-environment node
3
+ */
4
+ import {afterEach, beforeEach, describe, expect, it} from "@jest/globals";
5
+ import {resolveImportPath, resetImportCache} from "../resolveImport";
6
+ import {ResolverFactory} from "rspack-resolver";
7
+
8
+ const mockSync = jest.fn();
9
+ const mockCreateMatchPath = jest.fn();
10
+ const mockLoadConfig = jest.fn();
11
+
12
+ jest.mock("rspack-resolver", () => ({
13
+ ResolverFactory: jest.fn().mockImplementation(() => ({
14
+ sync: (...args: Array<unknown>) => mockSync(...args),
15
+ })),
16
+ }));
17
+
18
+ jest.mock("tsconfig-paths", () => ({
19
+ createMatchPath: jest
20
+ .fn()
21
+ .mockImplementation((...args: Array<unknown>) =>
22
+ mockCreateMatchPath(...args),
23
+ ),
24
+ loadConfig: jest
25
+ .fn()
26
+ .mockImplementation((...args: Array<unknown>) =>
27
+ mockLoadConfig(...args),
28
+ ),
29
+ }));
30
+
31
+ describe("resolveImportPath", () => {
32
+ beforeEach(() => {
33
+ mockSync.mockReset();
34
+ mockCreateMatchPath.mockReset();
35
+ mockLoadConfig.mockReset();
36
+ (ResolverFactory as unknown as jest.Mock).mockClear();
37
+ resetImportCache();
38
+ });
39
+
40
+ afterEach(() => {
41
+ jest.clearAllMocks();
42
+ });
43
+
44
+ it("returns the resolved path from the resolver", () => {
45
+ // Arrange
46
+ const matchPath = jest.fn().mockReturnValue("/repo/src/alias/thing.ts");
47
+ mockLoadConfig.mockReturnValue({
48
+ resultType: "success",
49
+ absoluteBaseUrl: "/repo",
50
+ paths: {"@/*": ["src/*"]},
51
+ });
52
+ mockCreateMatchPath.mockReturnValue(matchPath);
53
+ mockSync.mockReturnValue({path: "/repo/src/alias/thing.ts"});
54
+
55
+ // Act
56
+ const result = resolveImportPath("/repo/src/file.ts", "@/alias/thing");
57
+
58
+ // Assert
59
+ expect(result).toBe("/repo/src/alias/thing.ts");
60
+ });
61
+
62
+ it("loads tsconfig from the source file directory", () => {
63
+ // Arrange
64
+ const matchPath = jest.fn().mockReturnValue("/repo/src/alias/thing.ts");
65
+ mockLoadConfig.mockReturnValue({
66
+ resultType: "success",
67
+ absoluteBaseUrl: "/repo",
68
+ paths: {"@/*": ["src/*"]},
69
+ });
70
+ mockCreateMatchPath.mockReturnValue(matchPath);
71
+ mockSync.mockReturnValue({path: "/repo/src/alias/thing.ts"});
72
+
73
+ // Act
74
+ resolveImportPath("/repo/src/file.ts", "@/alias/thing");
75
+
76
+ // Assert
77
+ expect(mockLoadConfig).toHaveBeenCalledWith("/repo/src");
78
+ });
79
+
80
+ it("caches tsconfig matchPath per source directory", () => {
81
+ // Arrange
82
+ const matchPath = jest.fn().mockReturnValue(undefined);
83
+ mockLoadConfig.mockReturnValue({
84
+ resultType: "success",
85
+ absoluteBaseUrl: "/repo",
86
+ paths: {},
87
+ });
88
+ mockCreateMatchPath.mockReturnValue(matchPath);
89
+ mockSync.mockReturnValue({path: "/repo/node_modules/pkg/index.js"});
90
+
91
+ // Act
92
+ resolveImportPath("/repo/src/a.ts", "pkg");
93
+ resolveImportPath("/repo/src/b.ts", "pkg");
94
+
95
+ // Assert
96
+ expect(mockLoadConfig).toHaveBeenCalledTimes(1);
97
+ });
98
+ });
@@ -1,41 +1,10 @@
1
+ /**
2
+ * @jest-environment node
3
+ */
1
4
  import fs from "fs";
2
- import {describe, it, expect, jest} from "@jest/globals";
3
- import type {Config} from "../../types";
4
-
5
+ import {describe, it, expect} from "@jest/globals";
5
6
  import {getPathWithExtension} from "../utils";
6
7
 
7
- const generate = {
8
- match: [/\.fixture\.js$/],
9
- exclude: [
10
- "_test\\.js$",
11
- "\\bcourse-editor-package\\b",
12
- "\\.fixture\\.js$",
13
- "\\b__flowtests__\\b",
14
- "\\bcourse-editor\\b",
15
- ],
16
- readOnlyArray: false,
17
- regenerateCommand: "make gqlflow",
18
- scalars: {
19
- JSONString: "string",
20
- KALocale: "string",
21
- NaiveDateTime: "string",
22
- },
23
- splitTypes: true,
24
- generatedDirectory: "__graphql-types__",
25
- exportAllObjectTypes: true,
26
- schemaFilePath: "./composed_schema.graphql",
27
- } as const;
28
-
29
- const config: Config = {
30
- crawl: {
31
- root: "/here/we/crawl",
32
- },
33
- generate: [
34
- {...generate, match: [/^static/], exportAllObjectTypes: false},
35
- generate,
36
- ],
37
- };
38
-
39
8
  describe("getPathWithExtension", () => {
40
9
  it("should handle a basic missing extension", () => {
41
10
  // Arrange
@@ -44,7 +13,7 @@ describe("getPathWithExtension", () => {
44
13
  );
45
14
 
46
15
  // Act
47
- const result = getPathWithExtension("/path/to/file", config);
16
+ const result = getPathWithExtension("/path/to/file");
48
17
 
49
18
  // Assert
50
19
  expect(result).toBe("/path/to/file.js");
@@ -55,26 +24,20 @@ describe("getPathWithExtension", () => {
55
24
  jest.spyOn(fs, "existsSync").mockImplementation((path) => false);
56
25
 
57
26
  // Act
58
- const result = getPathWithExtension("/path/to/file", config);
27
+ const result = getPathWithExtension("/path/to/file");
59
28
 
60
29
  // Assert
61
30
  expect(result).toBe(null);
62
31
  });
63
32
 
64
- it("maps aliases to their correct value", () => {
33
+ it("returns the original path when an extension is already present", () => {
65
34
  // Arrange
66
- jest.spyOn(fs, "existsSync").mockImplementation((path) =>
67
- typeof path === "string" ? path.endsWith(".js") : false,
68
- );
69
- const tmpConfig: Config = {
70
- ...config,
71
- alias: [{find: "~", replacement: "../../some/prefix"}],
72
- };
35
+ const input = "/dir/file.tsx";
73
36
 
74
37
  // Act
75
- const result = getPathWithExtension("~/dir/file", tmpConfig);
38
+ const result = getPathWithExtension(input);
76
39
 
77
40
  // Assert
78
- expect(result).toBe("../../some/prefix/dir/file.js");
41
+ expect(result).toBe(input);
79
42
  });
80
43
  });
@@ -8,10 +8,12 @@ import type {
8
8
 
9
9
  import {parse, ParserPlugin} from "@babel/parser";
10
10
  import traverse from "@babel/traverse";
11
+ import type {NodePath} from "@babel/traverse";
11
12
 
12
13
  import path from "path";
13
14
 
14
- import {fixPathResolution, getPathWithExtension} from "./utils";
15
+ import {resolveImportPath} from "./resolveImport";
16
+ import {getPathWithExtension} from "./utils";
15
17
  import {Config} from "../types";
16
18
 
17
19
  /**
@@ -63,6 +65,8 @@ export type Import = {
63
65
  type: "import";
64
66
  name: string;
65
67
  path: string;
68
+ rawPath?: string;
69
+ resolvedPath?: string | null;
66
70
  loc: Loc;
67
71
  };
68
72
 
@@ -79,6 +83,7 @@ export type FileResult = {
79
83
  exports: {
80
84
  [key: string]: Document | Import;
81
85
  };
86
+ exportAlls: Array<Import>;
82
87
  locals: {
83
88
  [key: string]: Document | Import;
84
89
  };
@@ -86,6 +91,12 @@ export type FileResult = {
86
91
  loc: Loc;
87
92
  message: string;
88
93
  }>;
94
+ unresolvedImports?: {
95
+ [key: string]: {
96
+ source: string;
97
+ loc: Loc;
98
+ };
99
+ };
89
100
  };
90
101
 
91
102
  export type Files = {
@@ -100,15 +111,12 @@ export type Files = {
100
111
  * potentially relevant, and of course any values referenced
101
112
  * from a graphql template are treated as relevant.
102
113
  */
103
- const listExternalReferences = (
104
- file: FileResult,
105
- config: Config,
106
- ): Array<string> => {
114
+ const listExternalReferences = (file: FileResult): Array<string> => {
107
115
  const paths: Record<string, any> = {};
108
116
  const add = (v: Document | Import, followImports: boolean) => {
109
117
  if (v.type === "import") {
110
118
  if (followImports) {
111
- const absPath = getPathWithExtension(v.path, config);
119
+ const absPath = getPathWithExtension(v.path);
112
120
  if (absPath) {
113
121
  paths[absPath] = true;
114
122
  }
@@ -141,6 +149,7 @@ const listExternalReferences = (
141
149
  ),
142
150
  ),
143
151
  );
152
+ file.exportAlls.forEach((expr) => add(expr, true));
144
153
  return Object.keys(paths);
145
154
  };
146
155
 
@@ -154,13 +163,14 @@ export const processFile = (
154
163
  },
155
164
  config: Config,
156
165
  ): FileResult => {
157
- const dir = path.dirname(filePath);
158
166
  const result: FileResult = {
159
167
  path: filePath,
160
168
  operations: [],
161
169
  exports: {},
170
+ exportAlls: [],
162
171
  locals: {},
163
172
  errors: [],
173
+ unresolvedImports: {},
164
174
  };
165
175
  const text = typeof contents === "string" ? contents : contents.text;
166
176
  const plugins: Array<ParserPlugin> = filePath.endsWith("x")
@@ -178,17 +188,42 @@ export const processFile = (
178
188
 
179
189
  ast.program.body.forEach((toplevel) => {
180
190
  if (toplevel.type === "ImportDeclaration") {
181
- const newLocals = getLocals(dir, toplevel, filePath, config);
191
+ const isUnresolvedModule =
192
+ !toplevel.source.value.startsWith(".") &&
193
+ !path.isAbsolute(toplevel.source.value) &&
194
+ toplevel.source.value !== "graphql-tag";
195
+ if (isUnresolvedModule) {
196
+ toplevel.specifiers.forEach((spec) => {
197
+ if (
198
+ spec.type === "ImportSpecifier" ||
199
+ spec.type === "ImportDefaultSpecifier"
200
+ ) {
201
+ result.unresolvedImports![spec.local.name] = {
202
+ source: toplevel.source.value,
203
+ loc: {
204
+ start: spec.start ?? -1,
205
+ end: spec.end ?? -1,
206
+ line: spec.loc?.start.line ?? -1,
207
+ path: filePath,
208
+ },
209
+ };
210
+ }
211
+ });
212
+ }
213
+ const newLocals = getLocals(toplevel, filePath);
182
214
  if (newLocals) {
183
215
  Object.keys(newLocals).forEach((k) => {
184
216
  const local = newLocals[k];
185
- if (local.path.startsWith("/")) {
217
+ const isGraphqlTagImport =
218
+ local.rawPath === "graphql-tag" ||
219
+ (local.resolvedPath?.includes(
220
+ `${path.sep}node_modules${path.sep}graphql-tag`,
221
+ ) ??
222
+ false);
223
+ if (path.isAbsolute(local.path)) {
186
224
  result.locals[k] = local;
187
225
  }
188
- if (
189
- local.path === "graphql-tag" &&
190
- local.name === "default"
191
- ) {
226
+ if (isGraphqlTagImport && local.name === "default") {
192
227
  gqlTagNames.push(k);
193
228
  }
194
229
  });
@@ -197,9 +232,8 @@ export const processFile = (
197
232
  if (toplevel.type === "ExportNamedDeclaration") {
198
233
  if (toplevel.source) {
199
234
  const source = toplevel.source;
200
- const importPath = source.value.startsWith(".")
201
- ? path.resolve(path.join(dir, source.value))
202
- : source.value;
235
+ const resolvedPath = resolveImportPath(filePath, source.value);
236
+ const importPath = resolvedPath ?? source.value;
203
237
  toplevel.specifiers?.forEach((spec) => {
204
238
  if (
205
239
  spec.type === "ExportSpecifier" &&
@@ -229,6 +263,23 @@ export const processFile = (
229
263
  });
230
264
  }
231
265
  }
266
+ if (toplevel.type === "ExportAllDeclaration" && toplevel.source) {
267
+ const source = toplevel.source;
268
+ const importPath = source.value.startsWith(".")
269
+ ? path.resolve(path.join(path.dirname(filePath), source.value))
270
+ : source.value;
271
+ result.exportAlls.push({
272
+ type: "import",
273
+ name: "*",
274
+ path: importPath,
275
+ loc: {
276
+ start: toplevel.start ?? -1,
277
+ end: toplevel.end ?? -1,
278
+ line: toplevel.loc?.start.line ?? -1,
279
+ path: filePath,
280
+ },
281
+ });
282
+ }
232
283
 
233
284
  const processDeclarator = (
234
285
  decl: VariableDeclarator,
@@ -308,8 +359,9 @@ export const processFile = (
308
359
  };
309
360
 
310
361
  traverse(ast as any, {
311
- TaggedTemplateExpression(path) {
312
- visitTpl(path.node as any, (name) => {
362
+ TaggedTemplateExpression(path: NodePath) {
363
+ const node = path.node as TaggedTemplateExpression;
364
+ visitTpl(node, (name) => {
313
365
  const binding = path.scope.getBinding(name);
314
366
  if (!binding) {
315
367
  return null;
@@ -358,10 +410,18 @@ const processTemplate = (
358
410
  const found = getTemplate(expr.name);
359
411
  return found;
360
412
  }
361
- result.errors.push({
362
- loc,
363
- message: `Unable to resolve ${expr.name}`,
364
- });
413
+ const unresolved = result.unresolvedImports?.[expr.name];
414
+ if (unresolved) {
415
+ result.errors.push({
416
+ loc: unresolved.loc,
417
+ message: `Unable to resolve import ${expr.name} from "${unresolved.source}" at ${unresolved.loc.path}:${unresolved.loc.line}.`,
418
+ });
419
+ } else {
420
+ result.errors.push({
421
+ loc,
422
+ message: `Unable to resolve ${expr.name}`,
423
+ });
424
+ }
365
425
  return null;
366
426
  }
367
427
  return result.locals[expr.name];
@@ -384,10 +444,8 @@ const processTemplate = (
384
444
  };
385
445
 
386
446
  const getLocals = (
387
- dir: string,
388
447
  toplevel: ImportDeclaration,
389
448
  myPath: string,
390
- config: Config,
391
449
  ):
392
450
  | {
393
451
  [key: string]: Import;
@@ -397,10 +455,8 @@ const getLocals = (
397
455
  if (toplevel.importKind === "type") {
398
456
  return null;
399
457
  }
400
- const fixedPath = fixPathResolution(toplevel.source.value, config);
401
- const importPath = fixedPath.startsWith(".")
402
- ? path.resolve(path.join(dir, fixedPath))
403
- : fixedPath;
458
+ const resolvedPath = resolveImportPath(myPath, toplevel.source.value);
459
+ const importPath = resolvedPath ?? toplevel.source.value;
404
460
  const locals: Record<string, any> = {};
405
461
  toplevel.specifiers.forEach((spec) => {
406
462
  if (spec.type === "ImportDefaultSpecifier") {
@@ -408,6 +464,8 @@ const getLocals = (
408
464
  type: "import",
409
465
  name: "default",
410
466
  path: importPath,
467
+ rawPath: toplevel.source.value,
468
+ resolvedPath,
411
469
  loc: {start: spec.start, end: spec.end, path: myPath},
412
470
  };
413
471
  } else if (spec.type === "ImportSpecifier") {
@@ -418,6 +476,8 @@ const getLocals = (
418
476
  ? spec.imported.name
419
477
  : spec.imported.value,
420
478
  path: importPath,
479
+ rawPath: toplevel.source.value,
480
+ resolvedPath,
421
481
  loc: {start: spec.start, end: spec.end, path: myPath},
422
482
  };
423
483
  }
@@ -449,7 +509,7 @@ export const processFiles = (
449
509
  }
450
510
  const result = processFile(next, source, config);
451
511
  files[next] = result;
452
- listExternalReferences(result, config).forEach((path) => {
512
+ listExternalReferences(result).forEach((path) => {
453
513
  if (!files[path] && !toProcess.includes(path)) {
454
514
  toProcess.push(path);
455
515
  }