@graphql-tools/import 6.4.2 → 6.5.3

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 (5) hide show
  1. package/README.md +1 -1
  2. package/index.d.ts +26 -0
  3. package/index.js +224 -195
  4. package/index.mjs +222 -196
  5. package/package.json +2 -1
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  Check API Reference for more information about this package;
2
- https://www.graphql-tools.com/docs/api/modules/import
2
+ https://www.graphql-tools.com/docs/api/modules/import_src
3
3
 
4
4
  You can also learn more about GraphQL Import in this chapter;
5
5
  https://www.graphql-tools.com/docs/schema-loading#using-import-expression
package/index.d.ts CHANGED
@@ -1,6 +1,32 @@
1
1
  import { DefinitionNode, DocumentNode } from 'graphql';
2
2
  export declare type VisitedFilesMap = Map<string, Map<string, Set<DefinitionNode>>>;
3
+ /**
4
+ * Loads the GraphQL document and recursively resolves all the imports
5
+ * and copies them into the final document.
6
+ */
3
7
  export declare function processImport(filePath: string, cwd?: string, predefinedImports?: Record<string, string>, visitedFiles?: VisitedFilesMap): DocumentNode;
8
+ export declare function extractDependencies(filePath: string, fileContents: string): {
9
+ definitionsByName: Map<string, Set<DefinitionNode>>;
10
+ dependenciesByDefinitionName: Map<string, Set<string>>;
11
+ };
12
+ export declare function processImports(importLines: string[], filePath: string, visitedFiles: VisitedFilesMap, predefinedImports: Record<string, string>): {
13
+ allImportedDefinitionsMap: Map<string, Set<DefinitionNode>>;
14
+ potentialTransitiveDefinitionsMap: Map<string, Set<DefinitionNode>>;
15
+ };
16
+ /**
17
+ * Splits the contents of a GraphQL file into lines that are imports
18
+ * and other lines which define the actual GraphQL document.
19
+ */
20
+ export declare function extractImportLines(fileContent: string): {
21
+ importLines: string[];
22
+ otherLines: string;
23
+ };
24
+ /**
25
+ * Parses an import line, returning a list of entities imported and the file
26
+ * from which they are imported.
27
+ *
28
+ * Throws if the import line does not have a correct format.
29
+ */
4
30
  export declare function parseImportLine(importLine: string): {
5
31
  imports: string[];
6
32
  from: string;
package/index.js CHANGED
@@ -9,6 +9,7 @@ const fs = require('fs');
9
9
  const path = require('path');
10
10
  const resolveFrom = _interopDefault(require('resolve-from'));
11
11
  const process = require('process');
12
+ const utils = require('@graphql-tools/utils');
12
13
 
13
14
  /* eslint-disable no-unused-expressions */
14
15
  const builtinTypes = ['String', 'Float', 'Int', 'Boolean', 'ID', 'Upload'];
@@ -27,18 +28,20 @@ const builtinDirectives = [
27
28
  ];
28
29
  const IMPORT_FROM_REGEX = /^import\s+(\*|(.*))\s+from\s+('|")(.*)('|");?$/;
29
30
  const IMPORT_DEFAULT_REGEX = /^import\s+('|")(.*)('|");?$/;
31
+ /**
32
+ * Loads the GraphQL document and recursively resolves all the imports
33
+ * and copies them into the final document.
34
+ */
30
35
  function processImport(filePath, cwd = process.cwd(), predefinedImports = {}, visitedFiles = new Map()) {
31
36
  const set = visitFile(filePath, path.join(cwd + '/root.graphql'), visitedFiles, predefinedImports);
32
37
  const definitionStrSet = new Set();
33
38
  let definitionsStr = '';
34
- if (set != null) {
35
- for (const defs of set.values()) {
36
- for (const def of defs) {
37
- const defStr = graphql.print(def);
38
- if (!definitionStrSet.has(defStr)) {
39
- definitionStrSet.add(defStr);
40
- definitionsStr += defStr + '\n';
41
- }
39
+ for (const defs of set.values()) {
40
+ for (const def of defs) {
41
+ const defStr = graphql.print(def);
42
+ if (!definitionStrSet.has(defStr)) {
43
+ definitionStrSet.add(defStr);
44
+ definitionsStr += defStr + '\n';
42
45
  }
43
46
  }
44
47
  }
@@ -50,192 +53,17 @@ function processImport(filePath, cwd = process.cwd(), predefinedImports = {}, vi
50
53
  };
51
54
  }
52
55
  function visitFile(filePath, cwd, visitedFiles, predefinedImports) {
53
- var _a;
54
56
  if (!path.isAbsolute(filePath) && !(filePath in predefinedImports)) {
55
57
  filePath = resolveFilePath(cwd, filePath);
56
58
  }
57
59
  if (!visitedFiles.has(filePath)) {
58
60
  const fileContent = filePath in predefinedImports ? predefinedImports[filePath] : fs.readFileSync(filePath, 'utf8');
59
- const importLines = [];
60
- let otherLines = '';
61
- for (const line of fileContent.split('\n')) {
62
- const trimmedLine = line.trim();
63
- if (trimmedLine.startsWith('#import ') || trimmedLine.startsWith('# import ')) {
64
- importLines.push(trimmedLine);
65
- }
66
- else if (trimmedLine) {
67
- otherLines += line + '\n';
68
- }
69
- }
70
- const definitionsByName = new Map();
71
- const dependenciesByDefinitionName = new Map();
72
- if (otherLines) {
73
- const fileDefinitionMap = new Map();
74
- // To prevent circular dependency
75
- visitedFiles.set(filePath, fileDefinitionMap);
76
- const document = graphql.parse(new graphql.Source(otherLines, filePath), {
77
- noLocation: true,
78
- });
79
- for (const definition of document.definitions) {
80
- if ('name' in definition || definition.kind === graphql.Kind.SCHEMA_DEFINITION) {
81
- const definitionName = 'name' in definition && definition.name ? definition.name.value : 'schema';
82
- if (!definitionsByName.has(definitionName)) {
83
- definitionsByName.set(definitionName, new Set());
84
- }
85
- const definitionsSet = definitionsByName.get(definitionName);
86
- definitionsSet === null || definitionsSet === void 0 ? void 0 : definitionsSet.add(definition);
87
- let dependencySet = dependenciesByDefinitionName.get(definitionName);
88
- if (!dependencySet) {
89
- dependencySet = new Set();
90
- dependenciesByDefinitionName.set(definitionName, dependencySet);
91
- }
92
- switch (definition.kind) {
93
- case graphql.Kind.OPERATION_DEFINITION:
94
- visitOperationDefinitionNode(definition, dependencySet);
95
- break;
96
- case graphql.Kind.FRAGMENT_DEFINITION:
97
- visitFragmentDefinitionNode(definition, dependencySet);
98
- break;
99
- case graphql.Kind.OBJECT_TYPE_DEFINITION:
100
- visitObjectTypeDefinitionNode(definition, dependencySet, dependenciesByDefinitionName);
101
- break;
102
- case graphql.Kind.INTERFACE_TYPE_DEFINITION:
103
- visitInterfaceTypeDefinitionNode(definition, dependencySet, dependenciesByDefinitionName);
104
- break;
105
- case graphql.Kind.UNION_TYPE_DEFINITION:
106
- visitUnionTypeDefinitionNode(definition, dependencySet);
107
- break;
108
- case graphql.Kind.ENUM_TYPE_DEFINITION:
109
- visitEnumTypeDefinitionNode(definition, dependencySet);
110
- break;
111
- case graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION:
112
- visitInputObjectTypeDefinitionNode(definition, dependencySet);
113
- break;
114
- case graphql.Kind.DIRECTIVE_DEFINITION:
115
- visitDirectiveDefinitionNode(definition, dependencySet);
116
- break;
117
- case graphql.Kind.SCALAR_TYPE_DEFINITION:
118
- visitScalarDefinitionNode(definition, dependencySet);
119
- break;
120
- case graphql.Kind.SCHEMA_DEFINITION:
121
- visitSchemaDefinitionNode(definition, dependencySet);
122
- break;
123
- case graphql.Kind.OBJECT_TYPE_EXTENSION:
124
- visitObjectTypeExtensionNode(definition, dependencySet, dependenciesByDefinitionName);
125
- break;
126
- case graphql.Kind.INTERFACE_TYPE_EXTENSION:
127
- visitInterfaceTypeExtensionNode(definition, dependencySet, dependenciesByDefinitionName);
128
- break;
129
- case graphql.Kind.UNION_TYPE_EXTENSION:
130
- visitUnionTypeExtensionNode(definition, dependencySet);
131
- break;
132
- case graphql.Kind.ENUM_TYPE_EXTENSION:
133
- visitEnumTypeExtensionNode(definition, dependencySet);
134
- break;
135
- case graphql.Kind.INPUT_OBJECT_TYPE_EXTENSION:
136
- visitInputObjectTypeExtensionNode(definition, dependencySet);
137
- break;
138
- case graphql.Kind.SCALAR_TYPE_EXTENSION:
139
- visitScalarExtensionNode(definition, dependencySet);
140
- break;
141
- }
142
- if ('fields' in definition && definition.fields) {
143
- for (const field of definition.fields) {
144
- const definitionName = definition.name.value + '.' + field.name.value;
145
- if (!definitionsByName.has(definitionName)) {
146
- definitionsByName.set(definitionName, new Set());
147
- }
148
- (_a = definitionsByName.get(definitionName)) === null || _a === void 0 ? void 0 : _a.add({
149
- ...definition,
150
- fields: [field],
151
- });
152
- let dependencySet = dependenciesByDefinitionName.get(definitionName);
153
- if (!dependencySet) {
154
- dependencySet = new Set();
155
- dependenciesByDefinitionName.set(definitionName, dependencySet);
156
- }
157
- switch (field.kind) {
158
- case graphql.Kind.FIELD_DEFINITION:
159
- visitFieldDefinitionNode(field, dependencySet);
160
- break;
161
- case graphql.Kind.INPUT_VALUE_DEFINITION:
162
- visitInputValueDefinitionNode(field, dependencySet);
163
- break;
164
- }
165
- }
166
- }
167
- }
168
- }
169
- for (const [definitionName, definitions] of definitionsByName) {
170
- let definitionsWithDependencies = fileDefinitionMap.get(definitionName);
171
- if (definitionsWithDependencies == null) {
172
- definitionsWithDependencies = new Set();
173
- fileDefinitionMap.set(definitionName, definitionsWithDependencies);
174
- }
175
- for (const definition of definitions) {
176
- definitionsWithDependencies.add(definition);
177
- }
178
- const dependenciesOfDefinition = dependenciesByDefinitionName.get(definitionName);
179
- if (dependenciesOfDefinition) {
180
- for (const dependencyName of dependenciesOfDefinition) {
181
- const dependencyDefinitions = definitionsByName.get(dependencyName);
182
- if (dependencyDefinitions != null) {
183
- for (const dependencyDefinition of dependencyDefinitions) {
184
- definitionsWithDependencies.add(dependencyDefinition);
185
- }
186
- }
187
- }
188
- }
189
- }
190
- }
191
- const potentialTransitiveDefinitionsMap = new Map();
192
- const allImportedDefinitionsMap = new Map();
193
- for (const line of importLines) {
194
- const { imports, from } = parseImportLine(line.replace('#', '').trim());
195
- const importFileDefinitionMap = visitFile(from, filePath, visitedFiles, predefinedImports);
196
- if (importFileDefinitionMap != null) {
197
- const buildFullDefinitionMap = (dependenciesMap) => {
198
- for (const [importedDefinitionName, importedDefinitions] of importFileDefinitionMap) {
199
- const [importedDefinitionTypeName] = importedDefinitionName.split('.');
200
- if (!dependenciesMap.has(importedDefinitionTypeName)) {
201
- dependenciesMap.set(importedDefinitionTypeName, new Set());
202
- }
203
- const allImportedDefinitions = dependenciesMap.get(importedDefinitionTypeName);
204
- if (allImportedDefinitions) {
205
- for (const importedDefinition of importedDefinitions) {
206
- allImportedDefinitions.add(importedDefinition);
207
- }
208
- }
209
- }
210
- };
211
- buildFullDefinitionMap(potentialTransitiveDefinitionsMap);
212
- if (imports.includes('*')) {
213
- buildFullDefinitionMap(allImportedDefinitionsMap);
214
- }
215
- else {
216
- for (let importedDefinitionName of imports) {
217
- if (importedDefinitionName.endsWith('.*')) {
218
- // Adding whole type means the same thing with adding every single field
219
- importedDefinitionName = importedDefinitionName.replace('.*', '');
220
- }
221
- const [importedDefinitionTypeName] = importedDefinitionName.split('.');
222
- if (!allImportedDefinitionsMap.has(importedDefinitionTypeName)) {
223
- allImportedDefinitionsMap.set(importedDefinitionTypeName, new Set());
224
- }
225
- const allImportedDefinitions = allImportedDefinitionsMap.get(importedDefinitionTypeName);
226
- const importedDefinitions = importFileDefinitionMap.get(importedDefinitionName);
227
- if (!importedDefinitions) {
228
- throw new Error(`${importedDefinitionName} is not exported by ${from} imported by ${filePath}`);
229
- }
230
- if (allImportedDefinitions != null) {
231
- for (const importedDefinition of importedDefinitions) {
232
- allImportedDefinitions.add(importedDefinition);
233
- }
234
- }
235
- }
236
- }
237
- }
238
- }
61
+ const { importLines, otherLines } = extractImportLines(fileContent);
62
+ const { definitionsByName, dependenciesByDefinitionName } = extractDependencies(filePath, otherLines);
63
+ const fileDefinitionMap = getFileDefinitionMap(definitionsByName, dependenciesByDefinitionName);
64
+ // To prevent circular dependency
65
+ visitedFiles.set(filePath, fileDefinitionMap);
66
+ const { allImportedDefinitionsMap, potentialTransitiveDefinitionsMap } = processImports(importLines, filePath, visitedFiles, predefinedImports);
239
67
  const addDefinition = (definition, definitionName, definitionSet) => {
240
68
  const fileDefinitionMap = visitedFiles.get(filePath);
241
69
  if (fileDefinitionMap && !definitionSet.has(definition)) {
@@ -278,12 +106,6 @@ function visitFile(filePath, cwd, visitedFiles, predefinedImports) {
278
106
  };
279
107
  if (!otherLines) {
280
108
  visitedFiles.set(filePath, allImportedDefinitionsMap);
281
- const definitionSet = new Set();
282
- allImportedDefinitionsMap === null || allImportedDefinitionsMap === void 0 ? void 0 : allImportedDefinitionsMap.forEach((importedDefinitions, importedDefinitionName) => {
283
- importedDefinitions === null || importedDefinitions === void 0 ? void 0 : importedDefinitions.forEach(importedDefinition => {
284
- addDefinition(importedDefinition, importedDefinitionName, definitionSet);
285
- });
286
- });
287
109
  }
288
110
  else {
289
111
  const fileDefinitionMap = visitedFiles.get(filePath);
@@ -315,6 +137,210 @@ function visitFile(filePath, cwd, visitedFiles, predefinedImports) {
315
137
  }
316
138
  return visitedFiles.get(filePath);
317
139
  }
140
+ function extractDependencies(filePath, fileContents) {
141
+ const definitionsByName = new Map();
142
+ const dependenciesByDefinitionName = new Map();
143
+ const { document } = utils.parseGraphQLSDL(filePath, fileContents, {
144
+ noLocation: true,
145
+ });
146
+ for (const definition of document.definitions) {
147
+ visitDefinition(definition, definitionsByName, dependenciesByDefinitionName);
148
+ }
149
+ return {
150
+ definitionsByName,
151
+ dependenciesByDefinitionName,
152
+ };
153
+ }
154
+ function visitDefinition(definition, definitionsByName, dependenciesByDefinitionName) {
155
+ var _a;
156
+ // TODO: handle queries without names
157
+ if ('name' in definition || definition.kind === graphql.Kind.SCHEMA_DEFINITION) {
158
+ const definitionName = 'name' in definition && definition.name ? definition.name.value : 'schema';
159
+ if (!definitionsByName.has(definitionName)) {
160
+ definitionsByName.set(definitionName, new Set());
161
+ }
162
+ const definitionsSet = definitionsByName.get(definitionName);
163
+ definitionsSet.add(definition);
164
+ let dependencySet = dependenciesByDefinitionName.get(definitionName);
165
+ if (!dependencySet) {
166
+ dependencySet = new Set();
167
+ dependenciesByDefinitionName.set(definitionName, dependencySet);
168
+ }
169
+ switch (definition.kind) {
170
+ case graphql.Kind.OPERATION_DEFINITION:
171
+ visitOperationDefinitionNode(definition, dependencySet);
172
+ break;
173
+ case graphql.Kind.FRAGMENT_DEFINITION:
174
+ visitFragmentDefinitionNode(definition, dependencySet);
175
+ break;
176
+ case graphql.Kind.OBJECT_TYPE_DEFINITION:
177
+ visitObjectTypeDefinitionNode(definition, dependencySet, dependenciesByDefinitionName);
178
+ break;
179
+ case graphql.Kind.INTERFACE_TYPE_DEFINITION:
180
+ visitInterfaceTypeDefinitionNode(definition, dependencySet, dependenciesByDefinitionName);
181
+ break;
182
+ case graphql.Kind.UNION_TYPE_DEFINITION:
183
+ visitUnionTypeDefinitionNode(definition, dependencySet);
184
+ break;
185
+ case graphql.Kind.ENUM_TYPE_DEFINITION:
186
+ visitEnumTypeDefinitionNode(definition, dependencySet);
187
+ break;
188
+ case graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION:
189
+ visitInputObjectTypeDefinitionNode(definition, dependencySet);
190
+ break;
191
+ case graphql.Kind.DIRECTIVE_DEFINITION:
192
+ visitDirectiveDefinitionNode(definition, dependencySet);
193
+ break;
194
+ case graphql.Kind.SCALAR_TYPE_DEFINITION:
195
+ visitScalarDefinitionNode(definition, dependencySet);
196
+ break;
197
+ case graphql.Kind.SCHEMA_DEFINITION:
198
+ visitSchemaDefinitionNode(definition, dependencySet);
199
+ break;
200
+ case graphql.Kind.OBJECT_TYPE_EXTENSION:
201
+ visitObjectTypeExtensionNode(definition, dependencySet, dependenciesByDefinitionName);
202
+ break;
203
+ case graphql.Kind.INTERFACE_TYPE_EXTENSION:
204
+ visitInterfaceTypeExtensionNode(definition, dependencySet, dependenciesByDefinitionName);
205
+ break;
206
+ case graphql.Kind.UNION_TYPE_EXTENSION:
207
+ visitUnionTypeExtensionNode(definition, dependencySet);
208
+ break;
209
+ case graphql.Kind.ENUM_TYPE_EXTENSION:
210
+ visitEnumTypeExtensionNode(definition, dependencySet);
211
+ break;
212
+ case graphql.Kind.INPUT_OBJECT_TYPE_EXTENSION:
213
+ visitInputObjectTypeExtensionNode(definition, dependencySet);
214
+ break;
215
+ case graphql.Kind.SCALAR_TYPE_EXTENSION:
216
+ visitScalarExtensionNode(definition, dependencySet);
217
+ break;
218
+ }
219
+ if ('fields' in definition && definition.fields) {
220
+ for (const field of definition.fields) {
221
+ const definitionName = definition.name.value + '.' + field.name.value;
222
+ if (!definitionsByName.has(definitionName)) {
223
+ definitionsByName.set(definitionName, new Set());
224
+ }
225
+ (_a = definitionsByName.get(definitionName)) === null || _a === void 0 ? void 0 : _a.add({
226
+ ...definition,
227
+ fields: [field],
228
+ });
229
+ let dependencySet = dependenciesByDefinitionName.get(definitionName);
230
+ if (!dependencySet) {
231
+ dependencySet = new Set();
232
+ dependenciesByDefinitionName.set(definitionName, dependencySet);
233
+ }
234
+ switch (field.kind) {
235
+ case graphql.Kind.FIELD_DEFINITION:
236
+ visitFieldDefinitionNode(field, dependencySet);
237
+ break;
238
+ case graphql.Kind.INPUT_VALUE_DEFINITION:
239
+ visitInputValueDefinitionNode(field, dependencySet);
240
+ break;
241
+ }
242
+ }
243
+ }
244
+ }
245
+ }
246
+ function getFileDefinitionMap(definitionsByName, dependenciesByDefinitionName) {
247
+ const fileDefinitionMap = new Map();
248
+ for (const [definitionName, definitions] of definitionsByName) {
249
+ let definitionsWithDependencies = fileDefinitionMap.get(definitionName);
250
+ if (definitionsWithDependencies == null) {
251
+ definitionsWithDependencies = new Set();
252
+ fileDefinitionMap.set(definitionName, definitionsWithDependencies);
253
+ }
254
+ for (const definition of definitions) {
255
+ definitionsWithDependencies.add(definition);
256
+ }
257
+ const dependenciesOfDefinition = dependenciesByDefinitionName.get(definitionName);
258
+ if (dependenciesOfDefinition) {
259
+ for (const dependencyName of dependenciesOfDefinition) {
260
+ const dependencyDefinitions = definitionsByName.get(dependencyName);
261
+ if (dependencyDefinitions != null) {
262
+ for (const dependencyDefinition of dependencyDefinitions) {
263
+ definitionsWithDependencies.add(dependencyDefinition);
264
+ }
265
+ }
266
+ }
267
+ }
268
+ }
269
+ return fileDefinitionMap;
270
+ }
271
+ function processImports(importLines, filePath, visitedFiles, predefinedImports) {
272
+ const potentialTransitiveDefinitionsMap = new Map();
273
+ const allImportedDefinitionsMap = new Map();
274
+ for (const line of importLines) {
275
+ const { imports, from } = parseImportLine(line.replace('#', '').trim());
276
+ const importFileDefinitionMap = visitFile(from, filePath, visitedFiles, predefinedImports);
277
+ const buildFullDefinitionMap = (dependenciesMap) => {
278
+ for (const [importedDefinitionName, importedDefinitions] of importFileDefinitionMap) {
279
+ const [importedDefinitionTypeName] = importedDefinitionName.split('.');
280
+ if (!dependenciesMap.has(importedDefinitionTypeName)) {
281
+ dependenciesMap.set(importedDefinitionTypeName, new Set());
282
+ }
283
+ const allImportedDefinitions = dependenciesMap.get(importedDefinitionTypeName);
284
+ if (allImportedDefinitions) {
285
+ for (const importedDefinition of importedDefinitions) {
286
+ allImportedDefinitions.add(importedDefinition);
287
+ }
288
+ }
289
+ }
290
+ };
291
+ buildFullDefinitionMap(potentialTransitiveDefinitionsMap);
292
+ if (imports.includes('*')) {
293
+ buildFullDefinitionMap(allImportedDefinitionsMap);
294
+ }
295
+ else {
296
+ for (let importedDefinitionName of imports) {
297
+ if (importedDefinitionName.endsWith('.*')) {
298
+ // Adding whole type means the same thing with adding every single field
299
+ importedDefinitionName = importedDefinitionName.replace('.*', '');
300
+ }
301
+ const [importedDefinitionTypeName] = importedDefinitionName.split('.');
302
+ if (!allImportedDefinitionsMap.has(importedDefinitionTypeName)) {
303
+ allImportedDefinitionsMap.set(importedDefinitionTypeName, new Set());
304
+ }
305
+ const allImportedDefinitions = allImportedDefinitionsMap.get(importedDefinitionTypeName);
306
+ const importedDefinitions = importFileDefinitionMap.get(importedDefinitionName);
307
+ if (!importedDefinitions) {
308
+ throw new Error(`${importedDefinitionName} is not exported by ${from} imported by ${filePath}`);
309
+ }
310
+ if (allImportedDefinitions != null) {
311
+ for (const importedDefinition of importedDefinitions) {
312
+ allImportedDefinitions.add(importedDefinition);
313
+ }
314
+ }
315
+ }
316
+ }
317
+ }
318
+ return { allImportedDefinitionsMap, potentialTransitiveDefinitionsMap };
319
+ }
320
+ /**
321
+ * Splits the contents of a GraphQL file into lines that are imports
322
+ * and other lines which define the actual GraphQL document.
323
+ */
324
+ function extractImportLines(fileContent) {
325
+ const importLines = [];
326
+ let otherLines = '';
327
+ for (const line of fileContent.split('\n')) {
328
+ const trimmedLine = line.trim();
329
+ if (trimmedLine.startsWith('#import ') || trimmedLine.startsWith('# import ')) {
330
+ importLines.push(trimmedLine);
331
+ }
332
+ else if (trimmedLine) {
333
+ otherLines += line + '\n';
334
+ }
335
+ }
336
+ return { importLines, otherLines };
337
+ }
338
+ /**
339
+ * Parses an import line, returning a list of entities imported and the file
340
+ * from which they are imported.
341
+ *
342
+ * Throws if the import line does not have a correct format.
343
+ */
318
344
  function parseImportLine(importLine) {
319
345
  let regexMatch = importLine.match(IMPORT_FROM_REGEX);
320
346
  if (regexMatch != null) {
@@ -563,5 +589,8 @@ function visitOperationTypeDefinitionNode(node, dependencySet) {
563
589
  visitNamedTypeNode(node.type, dependencySet);
564
590
  }
565
591
 
592
+ exports.extractDependencies = extractDependencies;
593
+ exports.extractImportLines = extractImportLines;
566
594
  exports.parseImportLine = parseImportLine;
567
595
  exports.processImport = processImport;
596
+ exports.processImports = processImports;
package/index.mjs CHANGED
@@ -3,6 +3,7 @@ import { readFileSync, realpathSync } from 'fs';
3
3
  import { join, isAbsolute, dirname } from 'path';
4
4
  import resolveFrom from 'resolve-from';
5
5
  import { cwd } from 'process';
6
+ import { parseGraphQLSDL } from '@graphql-tools/utils';
6
7
 
7
8
  /* eslint-disable no-unused-expressions */
8
9
  const builtinTypes = ['String', 'Float', 'Int', 'Boolean', 'ID', 'Upload'];
@@ -21,18 +22,20 @@ const builtinDirectives = [
21
22
  ];
22
23
  const IMPORT_FROM_REGEX = /^import\s+(\*|(.*))\s+from\s+('|")(.*)('|");?$/;
23
24
  const IMPORT_DEFAULT_REGEX = /^import\s+('|")(.*)('|");?$/;
25
+ /**
26
+ * Loads the GraphQL document and recursively resolves all the imports
27
+ * and copies them into the final document.
28
+ */
24
29
  function processImport(filePath, cwd$1 = cwd(), predefinedImports = {}, visitedFiles = new Map()) {
25
30
  const set = visitFile(filePath, join(cwd$1 + '/root.graphql'), visitedFiles, predefinedImports);
26
31
  const definitionStrSet = new Set();
27
32
  let definitionsStr = '';
28
- if (set != null) {
29
- for (const defs of set.values()) {
30
- for (const def of defs) {
31
- const defStr = print(def);
32
- if (!definitionStrSet.has(defStr)) {
33
- definitionStrSet.add(defStr);
34
- definitionsStr += defStr + '\n';
35
- }
33
+ for (const defs of set.values()) {
34
+ for (const def of defs) {
35
+ const defStr = print(def);
36
+ if (!definitionStrSet.has(defStr)) {
37
+ definitionStrSet.add(defStr);
38
+ definitionsStr += defStr + '\n';
36
39
  }
37
40
  }
38
41
  }
@@ -44,192 +47,17 @@ function processImport(filePath, cwd$1 = cwd(), predefinedImports = {}, visitedF
44
47
  };
45
48
  }
46
49
  function visitFile(filePath, cwd, visitedFiles, predefinedImports) {
47
- var _a;
48
50
  if (!isAbsolute(filePath) && !(filePath in predefinedImports)) {
49
51
  filePath = resolveFilePath(cwd, filePath);
50
52
  }
51
53
  if (!visitedFiles.has(filePath)) {
52
54
  const fileContent = filePath in predefinedImports ? predefinedImports[filePath] : readFileSync(filePath, 'utf8');
53
- const importLines = [];
54
- let otherLines = '';
55
- for (const line of fileContent.split('\n')) {
56
- const trimmedLine = line.trim();
57
- if (trimmedLine.startsWith('#import ') || trimmedLine.startsWith('# import ')) {
58
- importLines.push(trimmedLine);
59
- }
60
- else if (trimmedLine) {
61
- otherLines += line + '\n';
62
- }
63
- }
64
- const definitionsByName = new Map();
65
- const dependenciesByDefinitionName = new Map();
66
- if (otherLines) {
67
- const fileDefinitionMap = new Map();
68
- // To prevent circular dependency
69
- visitedFiles.set(filePath, fileDefinitionMap);
70
- const document = parse(new Source(otherLines, filePath), {
71
- noLocation: true,
72
- });
73
- for (const definition of document.definitions) {
74
- if ('name' in definition || definition.kind === Kind.SCHEMA_DEFINITION) {
75
- const definitionName = 'name' in definition && definition.name ? definition.name.value : 'schema';
76
- if (!definitionsByName.has(definitionName)) {
77
- definitionsByName.set(definitionName, new Set());
78
- }
79
- const definitionsSet = definitionsByName.get(definitionName);
80
- definitionsSet === null || definitionsSet === void 0 ? void 0 : definitionsSet.add(definition);
81
- let dependencySet = dependenciesByDefinitionName.get(definitionName);
82
- if (!dependencySet) {
83
- dependencySet = new Set();
84
- dependenciesByDefinitionName.set(definitionName, dependencySet);
85
- }
86
- switch (definition.kind) {
87
- case Kind.OPERATION_DEFINITION:
88
- visitOperationDefinitionNode(definition, dependencySet);
89
- break;
90
- case Kind.FRAGMENT_DEFINITION:
91
- visitFragmentDefinitionNode(definition, dependencySet);
92
- break;
93
- case Kind.OBJECT_TYPE_DEFINITION:
94
- visitObjectTypeDefinitionNode(definition, dependencySet, dependenciesByDefinitionName);
95
- break;
96
- case Kind.INTERFACE_TYPE_DEFINITION:
97
- visitInterfaceTypeDefinitionNode(definition, dependencySet, dependenciesByDefinitionName);
98
- break;
99
- case Kind.UNION_TYPE_DEFINITION:
100
- visitUnionTypeDefinitionNode(definition, dependencySet);
101
- break;
102
- case Kind.ENUM_TYPE_DEFINITION:
103
- visitEnumTypeDefinitionNode(definition, dependencySet);
104
- break;
105
- case Kind.INPUT_OBJECT_TYPE_DEFINITION:
106
- visitInputObjectTypeDefinitionNode(definition, dependencySet);
107
- break;
108
- case Kind.DIRECTIVE_DEFINITION:
109
- visitDirectiveDefinitionNode(definition, dependencySet);
110
- break;
111
- case Kind.SCALAR_TYPE_DEFINITION:
112
- visitScalarDefinitionNode(definition, dependencySet);
113
- break;
114
- case Kind.SCHEMA_DEFINITION:
115
- visitSchemaDefinitionNode(definition, dependencySet);
116
- break;
117
- case Kind.OBJECT_TYPE_EXTENSION:
118
- visitObjectTypeExtensionNode(definition, dependencySet, dependenciesByDefinitionName);
119
- break;
120
- case Kind.INTERFACE_TYPE_EXTENSION:
121
- visitInterfaceTypeExtensionNode(definition, dependencySet, dependenciesByDefinitionName);
122
- break;
123
- case Kind.UNION_TYPE_EXTENSION:
124
- visitUnionTypeExtensionNode(definition, dependencySet);
125
- break;
126
- case Kind.ENUM_TYPE_EXTENSION:
127
- visitEnumTypeExtensionNode(definition, dependencySet);
128
- break;
129
- case Kind.INPUT_OBJECT_TYPE_EXTENSION:
130
- visitInputObjectTypeExtensionNode(definition, dependencySet);
131
- break;
132
- case Kind.SCALAR_TYPE_EXTENSION:
133
- visitScalarExtensionNode(definition, dependencySet);
134
- break;
135
- }
136
- if ('fields' in definition && definition.fields) {
137
- for (const field of definition.fields) {
138
- const definitionName = definition.name.value + '.' + field.name.value;
139
- if (!definitionsByName.has(definitionName)) {
140
- definitionsByName.set(definitionName, new Set());
141
- }
142
- (_a = definitionsByName.get(definitionName)) === null || _a === void 0 ? void 0 : _a.add({
143
- ...definition,
144
- fields: [field],
145
- });
146
- let dependencySet = dependenciesByDefinitionName.get(definitionName);
147
- if (!dependencySet) {
148
- dependencySet = new Set();
149
- dependenciesByDefinitionName.set(definitionName, dependencySet);
150
- }
151
- switch (field.kind) {
152
- case Kind.FIELD_DEFINITION:
153
- visitFieldDefinitionNode(field, dependencySet);
154
- break;
155
- case Kind.INPUT_VALUE_DEFINITION:
156
- visitInputValueDefinitionNode(field, dependencySet);
157
- break;
158
- }
159
- }
160
- }
161
- }
162
- }
163
- for (const [definitionName, definitions] of definitionsByName) {
164
- let definitionsWithDependencies = fileDefinitionMap.get(definitionName);
165
- if (definitionsWithDependencies == null) {
166
- definitionsWithDependencies = new Set();
167
- fileDefinitionMap.set(definitionName, definitionsWithDependencies);
168
- }
169
- for (const definition of definitions) {
170
- definitionsWithDependencies.add(definition);
171
- }
172
- const dependenciesOfDefinition = dependenciesByDefinitionName.get(definitionName);
173
- if (dependenciesOfDefinition) {
174
- for (const dependencyName of dependenciesOfDefinition) {
175
- const dependencyDefinitions = definitionsByName.get(dependencyName);
176
- if (dependencyDefinitions != null) {
177
- for (const dependencyDefinition of dependencyDefinitions) {
178
- definitionsWithDependencies.add(dependencyDefinition);
179
- }
180
- }
181
- }
182
- }
183
- }
184
- }
185
- const potentialTransitiveDefinitionsMap = new Map();
186
- const allImportedDefinitionsMap = new Map();
187
- for (const line of importLines) {
188
- const { imports, from } = parseImportLine(line.replace('#', '').trim());
189
- const importFileDefinitionMap = visitFile(from, filePath, visitedFiles, predefinedImports);
190
- if (importFileDefinitionMap != null) {
191
- const buildFullDefinitionMap = (dependenciesMap) => {
192
- for (const [importedDefinitionName, importedDefinitions] of importFileDefinitionMap) {
193
- const [importedDefinitionTypeName] = importedDefinitionName.split('.');
194
- if (!dependenciesMap.has(importedDefinitionTypeName)) {
195
- dependenciesMap.set(importedDefinitionTypeName, new Set());
196
- }
197
- const allImportedDefinitions = dependenciesMap.get(importedDefinitionTypeName);
198
- if (allImportedDefinitions) {
199
- for (const importedDefinition of importedDefinitions) {
200
- allImportedDefinitions.add(importedDefinition);
201
- }
202
- }
203
- }
204
- };
205
- buildFullDefinitionMap(potentialTransitiveDefinitionsMap);
206
- if (imports.includes('*')) {
207
- buildFullDefinitionMap(allImportedDefinitionsMap);
208
- }
209
- else {
210
- for (let importedDefinitionName of imports) {
211
- if (importedDefinitionName.endsWith('.*')) {
212
- // Adding whole type means the same thing with adding every single field
213
- importedDefinitionName = importedDefinitionName.replace('.*', '');
214
- }
215
- const [importedDefinitionTypeName] = importedDefinitionName.split('.');
216
- if (!allImportedDefinitionsMap.has(importedDefinitionTypeName)) {
217
- allImportedDefinitionsMap.set(importedDefinitionTypeName, new Set());
218
- }
219
- const allImportedDefinitions = allImportedDefinitionsMap.get(importedDefinitionTypeName);
220
- const importedDefinitions = importFileDefinitionMap.get(importedDefinitionName);
221
- if (!importedDefinitions) {
222
- throw new Error(`${importedDefinitionName} is not exported by ${from} imported by ${filePath}`);
223
- }
224
- if (allImportedDefinitions != null) {
225
- for (const importedDefinition of importedDefinitions) {
226
- allImportedDefinitions.add(importedDefinition);
227
- }
228
- }
229
- }
230
- }
231
- }
232
- }
55
+ const { importLines, otherLines } = extractImportLines(fileContent);
56
+ const { definitionsByName, dependenciesByDefinitionName } = extractDependencies(filePath, otherLines);
57
+ const fileDefinitionMap = getFileDefinitionMap(definitionsByName, dependenciesByDefinitionName);
58
+ // To prevent circular dependency
59
+ visitedFiles.set(filePath, fileDefinitionMap);
60
+ const { allImportedDefinitionsMap, potentialTransitiveDefinitionsMap } = processImports(importLines, filePath, visitedFiles, predefinedImports);
233
61
  const addDefinition = (definition, definitionName, definitionSet) => {
234
62
  const fileDefinitionMap = visitedFiles.get(filePath);
235
63
  if (fileDefinitionMap && !definitionSet.has(definition)) {
@@ -272,12 +100,6 @@ function visitFile(filePath, cwd, visitedFiles, predefinedImports) {
272
100
  };
273
101
  if (!otherLines) {
274
102
  visitedFiles.set(filePath, allImportedDefinitionsMap);
275
- const definitionSet = new Set();
276
- allImportedDefinitionsMap === null || allImportedDefinitionsMap === void 0 ? void 0 : allImportedDefinitionsMap.forEach((importedDefinitions, importedDefinitionName) => {
277
- importedDefinitions === null || importedDefinitions === void 0 ? void 0 : importedDefinitions.forEach(importedDefinition => {
278
- addDefinition(importedDefinition, importedDefinitionName, definitionSet);
279
- });
280
- });
281
103
  }
282
104
  else {
283
105
  const fileDefinitionMap = visitedFiles.get(filePath);
@@ -309,6 +131,210 @@ function visitFile(filePath, cwd, visitedFiles, predefinedImports) {
309
131
  }
310
132
  return visitedFiles.get(filePath);
311
133
  }
134
+ function extractDependencies(filePath, fileContents) {
135
+ const definitionsByName = new Map();
136
+ const dependenciesByDefinitionName = new Map();
137
+ const { document } = parseGraphQLSDL(filePath, fileContents, {
138
+ noLocation: true,
139
+ });
140
+ for (const definition of document.definitions) {
141
+ visitDefinition(definition, definitionsByName, dependenciesByDefinitionName);
142
+ }
143
+ return {
144
+ definitionsByName,
145
+ dependenciesByDefinitionName,
146
+ };
147
+ }
148
+ function visitDefinition(definition, definitionsByName, dependenciesByDefinitionName) {
149
+ var _a;
150
+ // TODO: handle queries without names
151
+ if ('name' in definition || definition.kind === Kind.SCHEMA_DEFINITION) {
152
+ const definitionName = 'name' in definition && definition.name ? definition.name.value : 'schema';
153
+ if (!definitionsByName.has(definitionName)) {
154
+ definitionsByName.set(definitionName, new Set());
155
+ }
156
+ const definitionsSet = definitionsByName.get(definitionName);
157
+ definitionsSet.add(definition);
158
+ let dependencySet = dependenciesByDefinitionName.get(definitionName);
159
+ if (!dependencySet) {
160
+ dependencySet = new Set();
161
+ dependenciesByDefinitionName.set(definitionName, dependencySet);
162
+ }
163
+ switch (definition.kind) {
164
+ case Kind.OPERATION_DEFINITION:
165
+ visitOperationDefinitionNode(definition, dependencySet);
166
+ break;
167
+ case Kind.FRAGMENT_DEFINITION:
168
+ visitFragmentDefinitionNode(definition, dependencySet);
169
+ break;
170
+ case Kind.OBJECT_TYPE_DEFINITION:
171
+ visitObjectTypeDefinitionNode(definition, dependencySet, dependenciesByDefinitionName);
172
+ break;
173
+ case Kind.INTERFACE_TYPE_DEFINITION:
174
+ visitInterfaceTypeDefinitionNode(definition, dependencySet, dependenciesByDefinitionName);
175
+ break;
176
+ case Kind.UNION_TYPE_DEFINITION:
177
+ visitUnionTypeDefinitionNode(definition, dependencySet);
178
+ break;
179
+ case Kind.ENUM_TYPE_DEFINITION:
180
+ visitEnumTypeDefinitionNode(definition, dependencySet);
181
+ break;
182
+ case Kind.INPUT_OBJECT_TYPE_DEFINITION:
183
+ visitInputObjectTypeDefinitionNode(definition, dependencySet);
184
+ break;
185
+ case Kind.DIRECTIVE_DEFINITION:
186
+ visitDirectiveDefinitionNode(definition, dependencySet);
187
+ break;
188
+ case Kind.SCALAR_TYPE_DEFINITION:
189
+ visitScalarDefinitionNode(definition, dependencySet);
190
+ break;
191
+ case Kind.SCHEMA_DEFINITION:
192
+ visitSchemaDefinitionNode(definition, dependencySet);
193
+ break;
194
+ case Kind.OBJECT_TYPE_EXTENSION:
195
+ visitObjectTypeExtensionNode(definition, dependencySet, dependenciesByDefinitionName);
196
+ break;
197
+ case Kind.INTERFACE_TYPE_EXTENSION:
198
+ visitInterfaceTypeExtensionNode(definition, dependencySet, dependenciesByDefinitionName);
199
+ break;
200
+ case Kind.UNION_TYPE_EXTENSION:
201
+ visitUnionTypeExtensionNode(definition, dependencySet);
202
+ break;
203
+ case Kind.ENUM_TYPE_EXTENSION:
204
+ visitEnumTypeExtensionNode(definition, dependencySet);
205
+ break;
206
+ case Kind.INPUT_OBJECT_TYPE_EXTENSION:
207
+ visitInputObjectTypeExtensionNode(definition, dependencySet);
208
+ break;
209
+ case Kind.SCALAR_TYPE_EXTENSION:
210
+ visitScalarExtensionNode(definition, dependencySet);
211
+ break;
212
+ }
213
+ if ('fields' in definition && definition.fields) {
214
+ for (const field of definition.fields) {
215
+ const definitionName = definition.name.value + '.' + field.name.value;
216
+ if (!definitionsByName.has(definitionName)) {
217
+ definitionsByName.set(definitionName, new Set());
218
+ }
219
+ (_a = definitionsByName.get(definitionName)) === null || _a === void 0 ? void 0 : _a.add({
220
+ ...definition,
221
+ fields: [field],
222
+ });
223
+ let dependencySet = dependenciesByDefinitionName.get(definitionName);
224
+ if (!dependencySet) {
225
+ dependencySet = new Set();
226
+ dependenciesByDefinitionName.set(definitionName, dependencySet);
227
+ }
228
+ switch (field.kind) {
229
+ case Kind.FIELD_DEFINITION:
230
+ visitFieldDefinitionNode(field, dependencySet);
231
+ break;
232
+ case Kind.INPUT_VALUE_DEFINITION:
233
+ visitInputValueDefinitionNode(field, dependencySet);
234
+ break;
235
+ }
236
+ }
237
+ }
238
+ }
239
+ }
240
+ function getFileDefinitionMap(definitionsByName, dependenciesByDefinitionName) {
241
+ const fileDefinitionMap = new Map();
242
+ for (const [definitionName, definitions] of definitionsByName) {
243
+ let definitionsWithDependencies = fileDefinitionMap.get(definitionName);
244
+ if (definitionsWithDependencies == null) {
245
+ definitionsWithDependencies = new Set();
246
+ fileDefinitionMap.set(definitionName, definitionsWithDependencies);
247
+ }
248
+ for (const definition of definitions) {
249
+ definitionsWithDependencies.add(definition);
250
+ }
251
+ const dependenciesOfDefinition = dependenciesByDefinitionName.get(definitionName);
252
+ if (dependenciesOfDefinition) {
253
+ for (const dependencyName of dependenciesOfDefinition) {
254
+ const dependencyDefinitions = definitionsByName.get(dependencyName);
255
+ if (dependencyDefinitions != null) {
256
+ for (const dependencyDefinition of dependencyDefinitions) {
257
+ definitionsWithDependencies.add(dependencyDefinition);
258
+ }
259
+ }
260
+ }
261
+ }
262
+ }
263
+ return fileDefinitionMap;
264
+ }
265
+ function processImports(importLines, filePath, visitedFiles, predefinedImports) {
266
+ const potentialTransitiveDefinitionsMap = new Map();
267
+ const allImportedDefinitionsMap = new Map();
268
+ for (const line of importLines) {
269
+ const { imports, from } = parseImportLine(line.replace('#', '').trim());
270
+ const importFileDefinitionMap = visitFile(from, filePath, visitedFiles, predefinedImports);
271
+ const buildFullDefinitionMap = (dependenciesMap) => {
272
+ for (const [importedDefinitionName, importedDefinitions] of importFileDefinitionMap) {
273
+ const [importedDefinitionTypeName] = importedDefinitionName.split('.');
274
+ if (!dependenciesMap.has(importedDefinitionTypeName)) {
275
+ dependenciesMap.set(importedDefinitionTypeName, new Set());
276
+ }
277
+ const allImportedDefinitions = dependenciesMap.get(importedDefinitionTypeName);
278
+ if (allImportedDefinitions) {
279
+ for (const importedDefinition of importedDefinitions) {
280
+ allImportedDefinitions.add(importedDefinition);
281
+ }
282
+ }
283
+ }
284
+ };
285
+ buildFullDefinitionMap(potentialTransitiveDefinitionsMap);
286
+ if (imports.includes('*')) {
287
+ buildFullDefinitionMap(allImportedDefinitionsMap);
288
+ }
289
+ else {
290
+ for (let importedDefinitionName of imports) {
291
+ if (importedDefinitionName.endsWith('.*')) {
292
+ // Adding whole type means the same thing with adding every single field
293
+ importedDefinitionName = importedDefinitionName.replace('.*', '');
294
+ }
295
+ const [importedDefinitionTypeName] = importedDefinitionName.split('.');
296
+ if (!allImportedDefinitionsMap.has(importedDefinitionTypeName)) {
297
+ allImportedDefinitionsMap.set(importedDefinitionTypeName, new Set());
298
+ }
299
+ const allImportedDefinitions = allImportedDefinitionsMap.get(importedDefinitionTypeName);
300
+ const importedDefinitions = importFileDefinitionMap.get(importedDefinitionName);
301
+ if (!importedDefinitions) {
302
+ throw new Error(`${importedDefinitionName} is not exported by ${from} imported by ${filePath}`);
303
+ }
304
+ if (allImportedDefinitions != null) {
305
+ for (const importedDefinition of importedDefinitions) {
306
+ allImportedDefinitions.add(importedDefinition);
307
+ }
308
+ }
309
+ }
310
+ }
311
+ }
312
+ return { allImportedDefinitionsMap, potentialTransitiveDefinitionsMap };
313
+ }
314
+ /**
315
+ * Splits the contents of a GraphQL file into lines that are imports
316
+ * and other lines which define the actual GraphQL document.
317
+ */
318
+ function extractImportLines(fileContent) {
319
+ const importLines = [];
320
+ let otherLines = '';
321
+ for (const line of fileContent.split('\n')) {
322
+ const trimmedLine = line.trim();
323
+ if (trimmedLine.startsWith('#import ') || trimmedLine.startsWith('# import ')) {
324
+ importLines.push(trimmedLine);
325
+ }
326
+ else if (trimmedLine) {
327
+ otherLines += line + '\n';
328
+ }
329
+ }
330
+ return { importLines, otherLines };
331
+ }
332
+ /**
333
+ * Parses an import line, returning a list of entities imported and the file
334
+ * from which they are imported.
335
+ *
336
+ * Throws if the import line does not have a correct format.
337
+ */
312
338
  function parseImportLine(importLine) {
313
339
  let regexMatch = importLine.match(IMPORT_FROM_REGEX);
314
340
  if (regexMatch != null) {
@@ -557,4 +583,4 @@ function visitOperationTypeDefinitionNode(node, dependencySet) {
557
583
  visitNamedTypeNode(node.type, dependencySet);
558
584
  }
559
585
 
560
- export { parseImportLine, processImport };
586
+ export { extractDependencies, extractImportLines, parseImportLine, processImport, processImports };
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@graphql-tools/import",
3
- "version": "6.4.2",
3
+ "version": "6.5.3",
4
4
  "description": "A set of utils for faster development of GraphQL tools",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
7
7
  "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
8
8
  },
9
9
  "dependencies": {
10
+ "@graphql-tools/utils": "8.2.5",
10
11
  "resolve-from": "5.0.0",
11
12
  "tslib": "~2.3.0"
12
13
  },