@graphql-tools/module-loader
Version:
A set of utils for faster development of GraphQL tools
121 lines (120 loc) • 3.95 kB
JavaScript
import { existsSync, promises as fsPromises } from 'fs';
import { isSchema } from 'graphql';
const { access } = fsPromises;
const InvalidError = new Error(`Imported object was not a string, DocumentNode or GraphQLSchema`);
const createLoadError = (error) => new Error('Unable to load schema from module: ' + `${error.message || /* istanbul ignore next */ error}`);
// module:node/module#export
function extractData(pointer) {
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 {
isExpressionValid(pointer) {
return typeof pointer === 'string' && pointer.toLowerCase().startsWith('module:');
}
async canLoad(pointer) {
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) {
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) {
try {
const result = this.parse(pointer, await this.importModule(pointer));
if (result) {
return [result];
}
throw InvalidError;
}
catch (error) {
throw createLoadError(error);
}
}
loadSync(pointer) {
try {
const result = this.parse(pointer, this.importModuleSync(pointer));
if (result) {
return [result];
}
throw InvalidError;
}
catch (error) {
throw createLoadError(error);
}
}
parse(pointer, importedModule) {
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,
};
}
}
extractFromModule(mod, modulePath, identifier) {
const thing = identifier ? mod[identifier] : mod;
if (!thing) {
throw new Error('Unable to import an object from module: ' + modulePath);
}
return thing;
}
// Sync and Async
async importModule(pointer) {
const { modulePath, exportName } = extractData(pointer);
const imported = await import(modulePath);
return this.extractFromModule(imported, modulePath, exportName || 'default');
}
importModuleSync(pointer) {
const { modulePath, exportName } = extractData(pointer);
// eslint-disable-next-line @typescript-eslint/no-require-imports
const imported = require(modulePath);
return this.extractFromModule(imported, modulePath, exportName);
}
}