/
index.ts
143 lines (118 loc) · 3.66 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { DocumentNode, GraphQLSchema, isSchema } from 'graphql';
import { Loader, Source } from '@graphql-tools/utils';
import { existsSync, promises as fsPromises } from 'fs';
const { access } = fsPromises;
const InvalidError = new Error(`Imported object was not a string, DocumentNode or GraphQLSchema`);
const createLoadError = (error: any) =>
new Error('Unable to load schema from module: ' + `${error.message || /* istanbul ignore next */ error}`);
// module:node/module#export
function extractData(pointer: string): {
modulePath: string;
exportName?: string;
} {
const parts = pointer.replace(/^module\:/i, '').split('#');
if (!parts || parts.length > 2) {
throw new Error('Schema pointer should match "module:path/to/module#export"');
}
return {
modulePath: parts[0],
exportName: parts[1],
};
}
/**
* * This loader loads documents and type definitions from a Node module
*
* ```js
* const schema = await loadSchema('module:someModuleName#someNamedExport', {
* loaders: [new ModuleLoader()],
* })
* ```
*/
export class ModuleLoader implements Loader {
private isExpressionValid(pointer: string) {
return typeof pointer === 'string' && pointer.toLowerCase().startsWith('module:');
}
async canLoad(pointer: string) {
if (this.isExpressionValid(pointer)) {
const { modulePath } = extractData(pointer);
try {
const moduleAbsolutePath = require.resolve(modulePath);
await access(moduleAbsolutePath);
return true;
} catch (e) {
return false;
}
}
return false;
}
canLoadSync(pointer: string) {
if (this.isExpressionValid(pointer)) {
const { modulePath } = extractData(pointer);
try {
const moduleAbsolutePath = require.resolve(modulePath);
return existsSync(moduleAbsolutePath);
} catch (e) {
return false;
}
}
return false;
}
async load(pointer: string) {
try {
const result = this.parse(pointer, await this.importModule(pointer));
if (result) {
return [result];
}
throw InvalidError;
} catch (error) {
throw createLoadError(error);
}
}
loadSync(pointer: string) {
try {
const result = this.parse(pointer, this.importModuleSync(pointer));
if (result) {
return [result];
}
throw InvalidError;
} catch (error) {
throw createLoadError(error);
}
}
private parse(pointer: string, importedModule: GraphQLSchema | string | DocumentNode): Source | void {
if (isSchema(importedModule)) {
return {
schema: importedModule,
location: pointer,
};
} else if (typeof importedModule === 'string') {
return {
location: pointer,
rawSDL: importedModule,
};
} else if (typeof importedModule === 'object' && importedModule.kind === 'Document') {
return {
location: pointer,
document: importedModule,
};
}
}
private extractFromModule(mod: any, modulePath: string, identifier?: string) {
const thing = identifier ? mod[identifier] : mod;
if (!thing) {
throw new Error('Unable to import an object from module: ' + modulePath);
}
return thing;
}
// Sync and Async
private async importModule(pointer: string) {
const { modulePath, exportName } = extractData(pointer);
const imported = await import(modulePath);
return this.extractFromModule(imported, modulePath, exportName || 'default');
}
private importModuleSync(pointer: string) {
const { modulePath, exportName } = extractData(pointer);
const imported = require(modulePath);
return this.extractFromModule(imported, modulePath, exportName);
}
}