Skip to content

Commit 982c8f5

Browse files
committedMay 27, 2021
enhance(utils): refactor and cleanup
1 parent 9fe4598 commit 982c8f5

File tree

24 files changed

+242
-547
lines changed

24 files changed

+242
-547
lines changed
 

‎.changeset/smart-donuts-care.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphql-tools/utils': minor
3+
---
4+
5+
enhance(utils): refactor and cleanup

‎packages/import/tests/schema/import-schema.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -923,12 +923,12 @@ describe('importSchema', () => {
923923
type Level1 {
924924
id: ID!
925925
}
926-
926+
927927
type Level2 {
928928
id: ID!
929929
level1: Level1
930930
}
931-
931+
932932
type Level3 {
933933
id: ID!
934934
level2: Level2

‎packages/loaders/apollo-engine/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
"graphql": "^14.0.0 || ^15.0.0"
2121
},
2222
"dependencies": {
23+
"@ardatan/aggregate-error": "0.0.6",
2324
"@graphql-tools/utils": "^7.0.0",
2425
"cross-fetch": "3.1.4",
25-
"tslib": "~2.2.0"
26+
"tslib": "~2.2.0",
27+
"sync-fetch": "0.3.0"
2628
},
2729
"publishConfig": {
2830
"access": "public",

‎packages/loaders/apollo-engine/src/index.ts

+48-130
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { SchemaLoader, Source, SingleFileOptions } from '@graphql-tools/utils';
1+
import { SchemaLoader, Source, SingleFileOptions, parseGraphQLSDL } from '@graphql-tools/utils';
22
import { fetch } from 'cross-fetch';
3-
import { buildClientSchema } from 'graphql';
3+
import AggregateError from '@ardatan/aggregate-error';
4+
import syncFetch from 'sync-fetch';
45

56
/**
67
* Additional options for loading from Apollo Engine
@@ -25,49 +26,63 @@ export class ApolloEngineLoader implements SchemaLoader<ApolloEngineOptions> {
2526
return 'apollo-engine';
2627
}
2728

29+
private getFetchArgs(options: ApolloEngineOptions): [string, RequestInit] {
30+
return [
31+
options.engine.endpoint || DEFAULT_APOLLO_ENDPOINT,
32+
{
33+
method: 'POST',
34+
headers: {
35+
'x-api-key': options.engine.apiKey,
36+
'apollo-client-name': 'Apollo Language Server',
37+
'apollo-client-reference-id': '146d29c0-912c-46d3-b686-920e52586be6',
38+
'apollo-client-version': '2.6.8',
39+
'Content-Type': 'application/json',
40+
Accept: 'application/json',
41+
...options.headers,
42+
},
43+
body: JSON.stringify({
44+
query: SCHEMA_QUERY,
45+
variables: {
46+
id: options.graph,
47+
tag: options.variant,
48+
},
49+
}),
50+
},
51+
];
52+
}
53+
2854
async canLoad(ptr: string) {
29-
return typeof ptr === 'string' && ptr === 'apollo-engine';
55+
return this.canLoadSync(ptr);
3056
}
3157

32-
canLoadSync() {
33-
return false;
58+
canLoadSync(ptr: string) {
59+
return typeof ptr === 'string' && ptr === 'apollo-engine';
3460
}
3561

36-
async load(_: 'apollo-engine', options: ApolloEngineOptions): Promise<Source> {
37-
const response = await fetch(options.engine.endpoint || DEFAULT_APOLLO_ENDPOINT, {
38-
method: 'POST',
39-
headers: {
40-
'x-api-key': options.engine.apiKey,
41-
'apollo-client-name': 'Apollo Language Server',
42-
'apollo-client-reference-id': '146d29c0-912c-46d3-b686-920e52586be6',
43-
'apollo-client-version': '2.6.8',
44-
'Content-Type': 'application/json',
45-
Accept: 'application/json',
46-
...options.headers,
47-
},
48-
body: JSON.stringify({
49-
query: SCHEMA_QUERY,
50-
variables: {
51-
id: options.graph,
52-
tag: options.variant,
53-
},
54-
}),
55-
});
62+
async load(pointer: 'apollo-engine', options: ApolloEngineOptions): Promise<Source> {
63+
const fetchArgs = this.getFetchArgs(options);
64+
const response = await fetch(...fetchArgs);
5665

5766
const { data, errors } = await response.json();
5867

5968
if (errors) {
60-
throw new Error(errors.map(({ message }: Error) => message).join('\n'));
69+
throw new AggregateError(errors);
6170
}
6271

63-
return {
64-
location: 'apollo-engine',
65-
schema: buildClientSchema(data.service.schema),
66-
};
72+
return parseGraphQLSDL(pointer, data.service.schema.document, options);
6773
}
6874

69-
loadSync(): never {
70-
throw new Error('Loader ApolloEngine has no sync mode');
75+
loadSync(pointer: 'apollo-engine', options: ApolloEngineOptions): Source {
76+
const fetchArgs = this.getFetchArgs(options);
77+
const response = syncFetch(...fetchArgs);
78+
79+
const { data, errors } = response.json();
80+
81+
if (errors) {
82+
throw new AggregateError(errors);
83+
}
84+
85+
return parseGraphQLSDL(pointer, data.service.schema.document, options);
7186
}
7287
}
7388

@@ -80,104 +95,7 @@ export const SCHEMA_QUERY = /* GraphQL */ `
8095
... on Service {
8196
__typename
8297
schema(tag: $tag) {
83-
hash
84-
__schema: introspection {
85-
queryType {
86-
name
87-
}
88-
mutationType {
89-
name
90-
}
91-
subscriptionType {
92-
name
93-
}
94-
types(filter: { includeBuiltInTypes: true }) {
95-
...IntrospectionFullType
96-
}
97-
directives {
98-
name
99-
description
100-
locations
101-
args {
102-
...IntrospectionInputValue
103-
}
104-
}
105-
}
106-
}
107-
}
108-
}
109-
}
110-
111-
fragment IntrospectionFullType on IntrospectionType {
112-
kind
113-
name
114-
description
115-
fields {
116-
name
117-
description
118-
args {
119-
...IntrospectionInputValue
120-
}
121-
type {
122-
...IntrospectionTypeRef
123-
}
124-
isDeprecated
125-
deprecationReason
126-
}
127-
inputFields {
128-
...IntrospectionInputValue
129-
}
130-
interfaces {
131-
...IntrospectionTypeRef
132-
}
133-
enumValues(includeDeprecated: true) {
134-
name
135-
description
136-
isDeprecated
137-
deprecationReason
138-
}
139-
possibleTypes {
140-
...IntrospectionTypeRef
141-
}
142-
}
143-
144-
fragment IntrospectionInputValue on IntrospectionInputValue {
145-
name
146-
description
147-
type {
148-
...IntrospectionTypeRef
149-
}
150-
defaultValue
151-
}
152-
153-
fragment IntrospectionTypeRef on IntrospectionType {
154-
kind
155-
name
156-
ofType {
157-
kind
158-
name
159-
ofType {
160-
kind
161-
name
162-
ofType {
163-
kind
164-
name
165-
ofType {
166-
kind
167-
name
168-
ofType {
169-
kind
170-
name
171-
ofType {
172-
kind
173-
name
174-
ofType {
175-
kind
176-
name
177-
}
178-
}
179-
}
180-
}
98+
document
18199
}
182100
}
183101
}

‎packages/loaders/code-file/src/index.ts

+11-22
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Kind, isSchema, print } from 'graphql';
1+
import { isSchema, GraphQLSchema, DocumentNode } from 'graphql';
22
import {
33
SchemaPointerSingle,
44
DocumentPointerSingle,
@@ -9,7 +9,7 @@ import {
99
asArray,
1010
isValidPath,
1111
parseGraphQLSDL,
12-
printSchemaWithDirectives,
12+
isDocumentNode,
1313
} from '@graphql-tools/utils';
1414
import {
1515
GraphQLTagPluckOptions,
@@ -19,7 +19,7 @@ import {
1919
import { tryToLoadFromExport, tryToLoadFromExportSync } from './load-from-module';
2020
import { isAbsolute, resolve } from 'path';
2121
import { cwd } from 'process';
22-
import { readFileSync, accessSync, promises as fsPromises } from 'fs';
22+
import { readFileSync, promises as fsPromises, existsSync } from 'fs';
2323

2424
const { readFile, access } = fsPromises;
2525

@@ -77,12 +77,7 @@ export class CodeFileLoader implements UniversalLoader<CodeFileLoaderOptions> {
7777
if (isValidPath(pointer)) {
7878
if (FILE_EXTENSIONS.find(extension => pointer.endsWith(extension))) {
7979
const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || cwd(), pointer);
80-
try {
81-
accessSync(normalizedFilePath);
82-
return true;
83-
} catch {
84-
return false;
85-
}
80+
return existsSync(normalizedFilePath);
8681
}
8782
}
8883

@@ -100,7 +95,7 @@ export class CodeFileLoader implements UniversalLoader<CodeFileLoaderOptions> {
10095
const sdl = await gqlPluckFromCodeString(normalizedFilePath, content, options.pluckConfig);
10196

10297
if (sdl) {
103-
return parseSDL({ pointer, sdl, options });
98+
return parseGraphQLSDL(pointer, sdl, options);
10499
}
105100
} catch (e) {
106101
debugLog(`Failed to load schema from code file "${normalizedFilePath}": ${e.message}`);
@@ -143,7 +138,7 @@ export class CodeFileLoader implements UniversalLoader<CodeFileLoaderOptions> {
143138
const sdl = gqlPluckFromCodeStringSync(normalizedFilePath, content, options.pluckConfig);
144139

145140
if (sdl) {
146-
return parseSDL({ pointer, sdl, options });
141+
return parseGraphQLSDL(pointer, sdl, options);
147142
}
148143
} catch (e) {
149144
debugLog(`Failed to load schema from code file "${normalizedFilePath}": ${e.message}`);
@@ -176,25 +171,19 @@ export class CodeFileLoader implements UniversalLoader<CodeFileLoaderOptions> {
176171
}
177172
}
178173

179-
function parseSDL({ pointer, sdl, options }: { pointer: string; sdl: string; options: CodeFileLoaderOptions }) {
180-
return parseGraphQLSDL(pointer, sdl, options);
181-
}
182-
183-
function resolveSource(pointer: string, value: any, options: CodeFileLoaderOptions): Source | null {
184-
if (isSchema(value)) {
174+
function resolveSource(pointer: string, value: GraphQLSchema | DocumentNode | string, options: CodeFileLoaderOptions): Source | null {
175+
if (typeof value === 'string') {
176+
return parseGraphQLSDL(pointer, value, options);
177+
} else if (isSchema(value)) {
185178
return {
186179
location: pointer,
187-
rawSDL: printSchemaWithDirectives(value, options),
188180
schema: value,
189181
};
190-
} else if (value?.kind === Kind.DOCUMENT) {
182+
} else if (isDocumentNode(value)) {
191183
return {
192184
location: pointer,
193-
rawSDL: print(value),
194185
document: value,
195186
};
196-
} else if (typeof value === 'string') {
197-
return parseGraphQLSDL(pointer, value, options);
198187
}
199188

200189
return null;

‎packages/loaders/code-file/tests/load-from-code-file.spec.ts

+14-31
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as path from 'path';
2-
import * as fs from 'fs';
32
import { CodeFileLoader } from '../src';
43
import { parse } from 'graphql';
54

@@ -9,12 +8,10 @@ describe('loadFromCodeFile', () => {
98
it('Should throw an error when a document is loaded using AST and the document is not valid', async () => {
109
try {
1110
const loaded = await loader.load('./test-files/invalid-anon-doc.js', {
12-
noRequire: true,
13-
fs,
14-
path,
15-
cwd: __dirname
16-
});
17-
const doc = parse(loaded.rawSDL);
11+
noRequire: true,
12+
cwd: __dirname
13+
});
14+
const doc = loaded.document ? loaded.document : parse(loaded.rawSDL);
1815

1916
expect(doc).toBeFalsy();
2017
} catch (e) {
@@ -25,46 +22,38 @@ describe('loadFromCodeFile', () => {
2522
it('should load a valid file', async () => {
2623
const loaded = await loader.load('./test-files/valid-doc.js', {
2724
noRequire: true,
28-
fs,
29-
path,
3025
cwd: __dirname
3126
});
32-
const doc = parse(loaded.rawSDL);
27+
const doc = loaded.document ? loaded.document : parse(loaded.rawSDL);
3328

3429
expect(doc.kind).toEqual('Document');
3530
});
3631

3732
it('should consider options.cwd', async () => {
3833
const loaded = await loader.load('valid-doc.js', {
39-
path,
40-
fs,
4134
cwd: path.resolve(__dirname, 'test-files'),
4235
noRequire: true,
4336
});
44-
const doc = parse(loaded.rawSDL);
37+
const doc = loaded.document ? loaded.document : parse(loaded.rawSDL);
4538

4639
expect(doc.kind).toEqual('Document');
4740
});
4841

4942
it('should load a TypeScript file using decorator', async () => {
5043
const loaded = await loader.load('./test-files/with-decorator-doc.ts', {
5144
noRequire: true,
52-
fs,
53-
path,
5445
cwd: __dirname
5546
});
56-
const doc = parse(loaded.rawSDL);
47+
const doc = loaded.document ? loaded.document : parse(loaded.rawSDL);
5748

5849
expect(doc.kind).toEqual('Document');
5950
});
6051

6152
it('should support string interpolation', async () => {
6253
const loaded = await loader.load('./test-files/string-interpolation.js', {
63-
fs,
64-
path,
6554
cwd: __dirname
6655
});
67-
const doc = parse(loaded.rawSDL);
56+
const doc = loaded.document ? loaded.document : parse(loaded.rawSDL);
6857

6958
expect(doc.kind).toEqual('Document');
7059
});
@@ -77,46 +66,40 @@ describe('loadFromCodeFileSync', () => {
7766
expect(() => {
7867
const loaded = loader.loadSync('./test-files/invalid-anon-doc.js', {
7968
noRequire: true,
80-
fs,
81-
path,
8269
cwd: __dirname
8370
});
84-
parse(loaded.rawSDL);
71+
const doc = loaded.document ? loaded.document : parse(loaded.rawSDL);
72+
73+
expect(doc.kind).toEqual('Document');
8574
}).toThrowError('Syntax Error: Unexpected Name "InvalidGetUser"')
8675
});
8776

8877
it('should load a valid file', () => {
8978
const loaded = loader.loadSync('./test-files/valid-doc.js', {
9079
noRequire: true,
91-
fs,
92-
path,
9380
cwd: __dirname
9481
});
95-
const doc = parse(loaded.rawSDL);
82+
const doc = loaded.document;
9683

9784
expect(doc.kind).toEqual('Document');
9885
});
9986

10087
it('should consider options.cwd', () => {
10188
const loaded = loader.loadSync('valid-doc.js', {
102-
path,
103-
fs,
10489
cwd: path.resolve(__dirname, 'test-files'),
10590
noRequire: true,
10691
});
107-
const doc = parse(loaded.rawSDL);
92+
const doc = loaded.document;
10893

10994
expect(doc.kind).toEqual('Document');
11095
});
11196

11297
it('should support string interpolation', () => {
11398
const loaded = loader.loadSync('./test-files/string-interpolation.js', {
114-
fs,
115-
path,
11699
cwd: __dirname
117100
});
118101

119-
const doc = parse(loaded.rawSDL);
102+
const doc = loaded.document;
120103

121104
expect(doc.kind).toEqual('Document');
122105
});

‎packages/loaders/git/src/typings.d.ts

-1
This file was deleted.

‎packages/loaders/github/src/index.ts

+3-6
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,9 @@ export class GithubLoader implements UniversalLoader<GithubLoaderOptions> {
112112
return parseGraphQLJSON(pointer, content, options);
113113
}
114114

115-
const rawSDL = await gqlPluckFromCodeString(pointer, content, options.pluckConfig);
116-
if (rawSDL) {
117-
return {
118-
location: pointer,
119-
rawSDL,
120-
};
115+
if (path.endsWith('.tsx') || path.endsWith('.ts') || path.endsWith('.js') || path.endsWith('.jsx')) {
116+
const rawSDL = await gqlPluckFromCodeString(pointer, content, options.pluckConfig);
117+
return parseGraphQLSDL(path, rawSDL, options);
121118
}
122119

123120
throw new Error(`Invalid file extension: ${path}`);

‎packages/loaders/graphql-file/src/index.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
SingleFileOptions,
99
} from '@graphql-tools/utils';
1010
import { isAbsolute, resolve } from 'path';
11-
import { readFileSync, accessSync, promises as fsPromises } from 'fs';
11+
import { readFileSync, promises as fsPromises, existsSync } from 'fs';
1212
import { cwd as processCwd } from 'process';
1313
import { processImport } from '@graphql-tools/import';
1414

@@ -82,12 +82,7 @@ export class GraphQLFileLoader implements UniversalLoader<GraphQLFileLoaderOptio
8282
if (isValidPath(pointer)) {
8383
if (FILE_EXTENSIONS.find(extension => pointer.endsWith(extension))) {
8484
const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || processCwd(), pointer);
85-
try {
86-
accessSync(normalizedFilePath);
87-
return true;
88-
} catch {
89-
return false;
90-
}
85+
return existsSync(normalizedFilePath);
9186
}
9287
}
9388

‎packages/loaders/json-file/src/index.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
SingleFileOptions,
88
} from '@graphql-tools/utils';
99
import { isAbsolute, resolve } from 'path';
10-
import { readFileSync, accessSync, promises as fsPromises } from 'fs';
10+
import { readFileSync, promises as fsPromises, existsSync } from 'fs';
1111
import { cwd } from 'process';
1212

1313
const { readFile, access } = fsPromises;
@@ -68,12 +68,7 @@ export class JsonFileLoader implements DocumentLoader {
6868
if (FILE_EXTENSIONS.find(extension => pointer.endsWith(extension))) {
6969
const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || cwd(), pointer);
7070

71-
try {
72-
accessSync(normalizedFilePath);
73-
return true;
74-
} catch {
75-
return false;
76-
}
71+
return existsSync(normalizedFilePath);
7772
}
7873
}
7974

‎packages/loaders/module/src/index.ts

+36-17
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { parse, isSchema } from 'graphql';
1+
import { DocumentNode, GraphQLSchema, isSchema } from 'graphql';
22
import {
33
UniversalLoader,
4-
fixSchemaAst,
5-
getDocumentNodeFromSchema,
6-
SingleFileOptions,
74
Source,
85
} from '@graphql-tools/utils';
6+
import { existsSync, promises as fsPromises } from 'fs';
7+
8+
const { access } = fsPromises;
99

1010
const InvalidError = new Error(`Imported object was not a string, DocumentNode or GraphQLSchema`);
1111
const createLoadError = (error: any) =>
@@ -44,17 +44,40 @@ export class ModuleLoader implements UniversalLoader {
4444
return 'module-loader';
4545
}
4646

47+
private isExpressionValid(pointer: string) {
48+
return typeof pointer === 'string' && pointer.toLowerCase().startsWith('module:');
49+
}
50+
4751
async canLoad(pointer: string) {
48-
return this.canLoadSync(pointer);
52+
if (this.isExpressionValid(pointer)) {
53+
const { modulePath } = extractData(pointer);
54+
try {
55+
const moduleAbsolutePath = require.resolve(modulePath);
56+
await access(moduleAbsolutePath);
57+
return true;
58+
} catch (e) {
59+
return false;
60+
}
61+
}
62+
return false;
4963
}
5064

5165
canLoadSync(pointer: string) {
52-
return typeof pointer === 'string' && pointer.toLowerCase().startsWith('module:');
66+
if (this.isExpressionValid(pointer)) {
67+
const { modulePath } = extractData(pointer);
68+
try {
69+
const moduleAbsolutePath = require.resolve(modulePath);
70+
return existsSync(moduleAbsolutePath);
71+
} catch(e) {
72+
return false;
73+
}
74+
}
75+
return false;
5376
}
5477

55-
async load(pointer: string, options: SingleFileOptions) {
78+
async load(pointer: string) {
5679
try {
57-
const result = this.parse(pointer, options, await this.importModule(pointer));
80+
const result = this.parse(pointer, await this.importModule(pointer));
5881

5982
if (result) {
6083
return result;
@@ -66,9 +89,9 @@ export class ModuleLoader implements UniversalLoader {
6689
}
6790
}
6891

69-
loadSync(pointer: string, options: SingleFileOptions) {
92+
loadSync(pointer: string) {
7093
try {
71-
const result = this.parse(pointer, options, this.importModuleSync(pointer));
94+
const result = this.parse(pointer, this.importModuleSync(pointer));
7295

7396
if (result) {
7497
return result;
@@ -80,20 +103,16 @@ export class ModuleLoader implements UniversalLoader {
80103
}
81104
}
82105

83-
private parse(pointer: string, options: SingleFileOptions, importedModule: any): Source | void {
106+
private parse(pointer: string, importedModule: GraphQLSchema | string | DocumentNode): Source | void {
84107
if (isSchema(importedModule)) {
85-
const schema = fixSchemaAst(importedModule, options);
86108
return {
87-
schema,
88-
get document() {
89-
return getDocumentNodeFromSchema(schema);
90-
},
109+
schema: importedModule,
91110
location: pointer,
92111
};
93112
} else if (typeof importedModule === 'string') {
94113
return {
95114
location: pointer,
96-
document: parse(importedModule),
115+
rawSDL: importedModule,
97116
};
98117
} else if (typeof importedModule === 'object' && importedModule.kind === 'Document') {
99118
return {

‎packages/loaders/module/tests/loader.spec.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ describe('ModuleLoader', () => {
4545
it('should load GraphQLSchema object from a file', async () => {
4646
const result: Source = await load(getPointer('schema'), {});
4747
expect(result.schema).toBeDefined();
48-
expect(result.document).toBeDefined();
4948
});
5049

5150
it('should load DocumentNode object from a file', async () => {
@@ -55,7 +54,7 @@ describe('ModuleLoader', () => {
5554

5655
it('should load string from a file', async () => {
5756
const result: Source = await load(getPointer('type-defs-string'), {});
58-
expect(result.document).toBeDefined();
57+
expect(result.rawSDL).toBeDefined();
5958
});
6059

6160
it('should load using a named export', async () => {

‎packages/loaders/prisma/src/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ export class PrismaLoader extends UrlLoader {
2424
return 'prisma';
2525
}
2626

27+
canLoadSync() {
28+
return false;
29+
}
30+
2731
async canLoad(prismaConfigFilePath: string, options: PrismaLoaderOptions): Promise<boolean> {
2832
if (typeof prismaConfigFilePath === 'string' && prismaConfigFilePath.endsWith('prisma.yml')) {
2933
const joinedYmlPath = join(options.cwd || cwd(), prismaConfigFilePath);

‎packages/loaders/url/src/index.ts

+4-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable no-case-declarations */
22
/// <reference lib="dom" />
3-
import { print, IntrospectionOptions, DocumentNode, Kind, parse, buildASTSchema, GraphQLError } from 'graphql';
3+
import { print, IntrospectionOptions, DocumentNode, Kind, GraphQLError } from 'graphql';
44
import {
55
AsyncExecutor,
66
Executor,
@@ -15,6 +15,7 @@ import {
1515
ExecutionParams,
1616
mapAsyncIterator,
1717
withCancel,
18+
parseGraphQLSDL,
1819
} from '@graphql-tools/utils';
1920
import { isWebUri } from 'valid-url';
2021
import { fetch as crossFetch } from 'cross-fetch';
@@ -652,14 +653,7 @@ export class UrlLoader implements DocumentLoader<LoadFromUrlOptions> {
652653
headers,
653654
});
654655
const schemaString = await response.text();
655-
const document = parse(schemaString, options);
656-
const schema = buildASTSchema(document, options);
657-
return {
658-
location: pointer,
659-
rawSDL: schemaString,
660-
document,
661-
schema,
662-
};
656+
return parseGraphQLSDL(pointer, schemaString, options);
663657
}
664658

665659
handleSDLSync(pointer: SchemaPointerSingle, options: LoadFromUrlOptions): Source {
@@ -671,14 +665,7 @@ export class UrlLoader implements DocumentLoader<LoadFromUrlOptions> {
671665
headers,
672666
});
673667
const schemaString = response.text();
674-
const document = parse(schemaString, options);
675-
const schema = buildASTSchema(document, options);
676-
return {
677-
location: pointer,
678-
rawSDL: schemaString,
679-
document,
680-
schema,
681-
};
668+
return parseGraphQLSDL(pointer, schemaString, options);
682669
}
683670

684671
async load(pointer: SchemaPointerSingle, options: LoadFromUrlOptions): Promise<Source> {

‎packages/loaders/url/tests/url-loader.spec.ts

-6
Original file line numberDiff line numberDiff line change
@@ -296,9 +296,6 @@ input TestInput {
296296
scope = nock(testHost).get(testPath).reply(200, testTypeDefs);
297297
const result = await loader.load(testHost + testPath, {});
298298

299-
expect(result.schema).toBeDefined();
300-
expect(printSchemaWithDirectives(result.schema)).toBeSimilarGqlDoc(testTypeDefs);
301-
302299
expect(result.document).toBeDefined();
303300
expect(print(result.document)).toBeSimilarGqlDoc(testTypeDefs);
304301
})
@@ -310,9 +307,6 @@ input TestInput {
310307
handleAsSDL: true,
311308
});
312309

313-
expect(result.schema).toBeDefined();
314-
expect(printSchemaWithDirectives(result.schema)).toBeSimilarGqlDoc(testTypeDefs);
315-
316310
expect(result.document).toBeDefined();
317311
expect(print(result.document)).toBeSimilarGqlDoc(testTypeDefs);
318312
})

‎packages/merge/src/typedefs-mergers/merge-nodes.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export function isNamedDefinitionNode(definitionNode: DefinitionNode): definitio
1919
return 'name' in definitionNode;
2020
}
2121

22-
export const schemaDefSymbol = Symbol('schemaDefSymbol');
22+
export const schemaDefSymbol = 'SCHEMA_DEF_SYMBOL';
2323

2424
export function mergeGraphQLNodes(nodes: ReadonlyArray<DefinitionNode>, config?: Config): MergedResultMap {
2525
const mergedResultMap = {} as MergedResultMap;

‎packages/merge/src/typedefs-mergers/merge-typedefs.ts

+18-18
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { CompareFn, defaultStringComparator, isSourceTypes, isStringTypes } from
1414
import { MergedResultMap, mergeGraphQLNodes, schemaDefSymbol } from './merge-nodes';
1515
import { resetComments, printWithComments } from './comments';
1616
import { getDocumentNodeFromSchema } from '@graphql-tools/utils';
17-
import { operationTypeDefinitionNodeTypeRootTypeMap } from './schema-def';
17+
import { DEFAULT_OPERATION_TYPE_NAME_MAP } from './schema-def';
1818

1919
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
2020

@@ -117,7 +117,7 @@ export function mergeTypeDefs(
117117

118118
function visitTypeSources(
119119
types: Array<string | Source | DocumentNode | GraphQLSchema | DefinitionNode>,
120-
allNodes: DefinitionNode[]
120+
allNodes: DefinitionNode[] = []
121121
) {
122122
for (const type of types) {
123123
if (type) {
@@ -136,6 +136,7 @@ function visitTypeSources(
136136
}
137137
}
138138
}
139+
return allNodes;
139140
}
140141

141142
export function mergeGraphQLTypes(
@@ -144,23 +145,22 @@ export function mergeGraphQLTypes(
144145
): DefinitionNode[] {
145146
resetComments();
146147

147-
const allNodes: DefinitionNode[] = [];
148-
visitTypeSources(types, allNodes);
148+
const allNodes = visitTypeSources(types);
149149

150150
const mergedNodes: MergedResultMap = mergeGraphQLNodes(allNodes, config);
151151

152-
// XXX: right now we don't handle multiple schema definitions
153-
let schemaDef = mergedNodes[schemaDefSymbol] || {
154-
kind: Kind.SCHEMA_DEFINITION,
155-
operationTypes: [],
156-
};
157-
158152
if (config?.useSchemaDefinition) {
153+
// XXX: right now we don't handle multiple schema definitions
154+
const schemaDef = mergedNodes[schemaDefSymbol] || {
155+
kind: Kind.SCHEMA_DEFINITION,
156+
operationTypes: [],
157+
};
159158
const operationTypes = schemaDef.operationTypes as OperationTypeDefinitionNode[];
160-
for (const opTypeDefNodeType in operationTypeDefinitionNodeTypeRootTypeMap) {
159+
for (const opTypeDefNodeType in DEFAULT_OPERATION_TYPE_NAME_MAP) {
161160
const opTypeDefNode = operationTypes.find(operationType => operationType.operation === opTypeDefNodeType);
162161
if (!opTypeDefNode) {
163-
const existingPossibleRootType = mergedNodes[operationTypeDefinitionNodeTypeRootTypeMap[opTypeDefNodeType]];
162+
const possibleRootTypeName = DEFAULT_OPERATION_TYPE_NAME_MAP[opTypeDefNodeType];
163+
const existingPossibleRootType = mergedNodes[possibleRootTypeName];
164164
if (existingPossibleRootType) {
165165
operationTypes.push({
166166
kind: Kind.OPERATION_TYPE_DEFINITION,
@@ -173,10 +173,14 @@ export function mergeGraphQLTypes(
173173
}
174174
}
175175
}
176+
177+
if (schemaDef.operationTypes?.length > 0) {
178+
mergedNodes[schemaDefSymbol] = schemaDef;
179+
}
176180
}
177181

178-
if (config?.forceSchemaDefinition && !schemaDef?.operationTypes?.length) {
179-
schemaDef = {
182+
if (config?.forceSchemaDefinition && !mergedNodes[schemaDefSymbol]?.operationTypes?.length) {
183+
mergedNodes[schemaDefSymbol] = {
180184
kind: Kind.SCHEMA_DEFINITION,
181185
operationTypes: [
182186
{
@@ -196,10 +200,6 @@ export function mergeGraphQLTypes(
196200

197201
const mergedNodeDefinitions = Object.values(mergedNodes);
198202

199-
if (schemaDef.operationTypes?.length) {
200-
mergedNodeDefinitions.push(schemaDef);
201-
}
202-
203203
if (config?.sort) {
204204
const sortFn = typeof config.sort === 'function' ? config.sort : defaultStringComparator;
205205
mergedNodeDefinitions.sort((a, b) => sortFn(a.name?.value, b.name?.value));

‎packages/merge/src/typedefs-mergers/schema-def.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Kind, OperationTypeDefinitionNode, SchemaDefinitionNode, SchemaExtensio
22
import { mergeDirectives } from './directives';
33
import { Config } from './merge-typedefs';
44

5-
export const operationTypeDefinitionNodeTypeRootTypeMap = {
5+
export const DEFAULT_OPERATION_TYPE_NAME_MAP = {
66
query: 'Query',
77
mutation: 'Mutation',
88
subscription: 'Subscription',
@@ -13,7 +13,7 @@ function mergeOperationTypes(
1313
existingOpNodeList: ReadonlyArray<OperationTypeDefinitionNode> = []
1414
): OperationTypeDefinitionNode[] {
1515
const finalOpNodeList: OperationTypeDefinitionNode[] = [];
16-
for (const opNodeType in operationTypeDefinitionNodeTypeRootTypeMap) {
16+
for (const opNodeType in DEFAULT_OPERATION_TYPE_NAME_MAP) {
1717
const opNode =
1818
opNodeList.find(n => n.operation === opNodeType) || existingOpNodeList.find(n => n.operation === opNodeType);
1919
if (opNode) {

‎packages/merge/tests/merge-nodes.spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { mergeGraphQLNodes } from '../src';
2-
import { parse, InputObjectTypeDefinitionNode } from 'graphql';
2+
import { parse, InputObjectTypeDefinitionNode, EnumTypeDefinitionNode } from 'graphql';
33

44
describe('Merge Nodes', () => {
55
describe('type', () => {
@@ -140,7 +140,7 @@ describe('Merge Nodes', () => {
140140
const type1 = parse(`enum A { T }`);
141141
const type2 = parse(`enum A { S }`);
142142
const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]);
143-
const result: any = merged.A;
143+
const result = merged.A as EnumTypeDefinitionNode;
144144

145145
expect(result.values.length).toBe(2);
146146
expect(result.values.findIndex(v => v.name.value === 'T')).not.toBe(-1);
@@ -241,7 +241,7 @@ describe('Merge Nodes', () => {
241241
expect(type.fields[1].type.name.value).toBe('String');
242242
});
243243

244-
it('should remove schema definition', () => {
244+
it.skip('should remove schema definition', () => {
245245
const type1 = parse(`schema { query: Query } type Query { f1: String }`);
246246
const type2 = parse(`type Query { f2: String }`);
247247
const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]);

‎packages/merge/tests/merge-typedefs.spec.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import '../../testing/to-be-similar-gql-doc';
12
import { mergeDirectives, mergeTypeDefs, mergeGraphQLTypes } from '../src';
23
import { makeExecutableSchema } from '@graphql-tools/schema';
34
import { stitchSchemas } from '@graphql-tools/stitch'
@@ -177,6 +178,7 @@ describe('Merge TypeDefs', () => {
177178
expect(mergedArray[0].kind).toBe('ObjectTypeDefinition');
178179
expect(mergedArray[1].kind).toBe('ObjectTypeDefinition');
179180
expect(mergedArray[2].kind).toBe('SchemaDefinition');
181+
180182
});
181183

182184
it('should return the correct definition of Schema', () => {
@@ -186,8 +188,8 @@ describe('Merge TypeDefs', () => {
186188

187189
expect(mergedArray.length).toBe(3);
188190
expect(mergedArray[0].kind).toBe('ObjectTypeDefinition');
189-
expect(mergedArray[1].kind).toBe('ObjectTypeDefinition');
190-
expect(mergedArray[2].kind).toBe('SchemaDefinition');
191+
expect(mergedArray[1].kind).toBe('SchemaDefinition');
192+
expect(mergedArray[2].kind).toBe('ObjectTypeDefinition');
191193
});
192194

193195
it('should accept root schema object', () => {
@@ -210,8 +212,8 @@ describe('Merge TypeDefs', () => {
210212

211213
expect(mergedArray.length).toBe(3);
212214
expect(mergedArray[0].kind).toBe('ObjectTypeDefinition');
213-
expect(mergedArray[1].kind).toBe('ObjectTypeDefinition');
214-
expect(mergedArray[2].kind).toBe('SchemaDefinition');
215+
expect(mergedArray[1].kind).toBe('SchemaDefinition');
216+
expect(mergedArray[2].kind).toBe('ObjectTypeDefinition');
215217
});
216218
});
217219

@@ -658,7 +660,7 @@ describe('Merge TypeDefs', () => {
658660
'type Query { f2: String }',
659661
]);
660662

661-
expect(stripWhitespaces(print(merged))).toBe(
663+
expect(stripWhitespaces(print(merged))).toBeSimilarGqlDoc(
662664
stripWhitespaces(/* GraphQL */`
663665
type Query {
664666
f1: String
@@ -710,7 +712,7 @@ describe('Merge TypeDefs', () => {
710712
`,
711713
]);
712714

713-
expect(stripWhitespaces(print(merged))).toBe(
715+
expect(stripWhitespaces(print(merged))).toBeSimilarGqlDoc(
714716
stripWhitespaces(/* GraphQL */`
715717
type Query {
716718
f1: String

‎packages/utils/src/fix-schema-ast.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { GraphQLSchema, BuildSchemaOptions, buildSchema } from 'graphql';
1+
import { GraphQLSchema, BuildSchemaOptions, buildASTSchema } from 'graphql';
22
import { SchemaPrintOptions } from './types';
3-
import { printSchemaWithDirectives } from './print-schema-with-directives';
3+
import { getDocumentNodeFromSchema } from './print-schema-with-directives';
44

55
function buildFixedSchema(schema: GraphQLSchema, options: BuildSchemaOptions & SchemaPrintOptions) {
6-
return buildSchema(printSchemaWithDirectives(schema), {
7-
noLocation: true,
6+
const document = getDocumentNodeFromSchema(schema);
7+
return buildASTSchema(document, {
88
...(options || {}),
99
});
1010
}

‎packages/utils/src/parse-graphql-json.ts

+6-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { buildClientSchema, ParseOptions } from 'graphql';
22
import { GraphQLSchemaValidationOptions } from 'graphql/type/schema';
3-
import { printSchemaWithDirectives } from './print-schema-with-directives';
43
import { Source } from './loaders';
54
import { SchemaPrintOptions } from './types';
6-
import { parseGraphQLSDL } from './parse-graphql-sdl';
75

86
function stripBOM(content: string): string {
97
content = content.toString();
@@ -33,22 +31,22 @@ export function parseGraphQLJSON(
3331
}
3432

3533
if (parsedJson.kind === 'Document') {
36-
const document = parsedJson;
37-
3834
return {
3935
location,
40-
document,
36+
document: parsedJson,
4137
};
4238
} else if (parsedJson.__schema) {
4339
const schema = buildClientSchema(parsedJson, options);
44-
const rawSDL = printSchemaWithDirectives(schema, options);
4540

4641
return {
4742
location,
48-
document: parseGraphQLSDL(location, rawSDL, options).document,
49-
rawSDL,
5043
schema,
5144
};
45+
} else if (typeof parsedJson === 'string') {
46+
return {
47+
location,
48+
rawSDL: parsedJson
49+
}
5250
}
5351

5452
throw new Error(`Not valid JSON content`);

‎packages/utils/src/parse-graphql-sdl.ts

-3
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@ import { GraphQLParseOptions } from './Interfaces';
1616
export function parseGraphQLSDL(location: string, rawSDL: string, options: GraphQLParseOptions = {}) {
1717
let document: DocumentNode;
1818
const sdl: string = rawSDL;
19-
let sdlModified = false;
2019

2120
try {
2221
if (options.commentDescriptions && sdl.includes('#')) {
23-
sdlModified = true;
2422
document = transformCommentsToDescriptions(rawSDL, options);
2523

2624
// If noLocation=true, we need to make sure to print and parse it again, to remove locations,
@@ -46,7 +44,6 @@ export function parseGraphQLSDL(location: string, rawSDL: string, options: Graph
4644
return {
4745
location,
4846
document,
49-
rawSDL: sdlModified ? print(document) : sdl,
5047
};
5148
}
5249

‎yarn.lock

+65-253
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.