UNPKG

@graphql-tools/load

Version:

A set of utils for faster development of GraphQL tools

181 lines (180 loc) • 5.88 kB
import { existsSync, readFileSync } from 'fs'; import { createRequire } from 'module'; import { dirname, isAbsolute, join as joinPaths } from 'path'; import { pathToFileURL } from 'url'; function extractLoaderFromModule(loaderModule) { if (loaderModule == null) { return undefined; } if (typeof loaderModule.default === 'function') { return loaderModule.default; } if (typeof loaderModule === 'function') { return loaderModule; } } function createLoaderRequire(cwd) { return createRequire(joinPaths(cwd, 'noop.js')); } function isBarePackageSpecifier(specifier) { return !isAbsolute(specifier) && !specifier.startsWith('.') && !specifier.startsWith('file:'); } function findPackageDir(packageName, cwd) { let dir = cwd; while (true) { const candidate = joinPaths(dir, 'node_modules', ...packageName.split('/')); if (existsSync(joinPaths(candidate, 'package.json'))) { return candidate; } const parent = dirname(dir); if (parent === dir) { return undefined; } dir = parent; } } function stringFromConditionalExport(entry) { if (typeof entry === 'string') { return entry; } if (entry == null || typeof entry !== 'object') { return undefined; } const record = entry; const importEntry = record['import']; if (typeof importEntry === 'string') { return importEntry; } if (importEntry != null && typeof importEntry === 'object') { const nested = importEntry['default']; if (typeof nested === 'string') { return nested; } } const defaultEntry = record['default']; if (typeof defaultEntry === 'string') { return defaultEntry; } return undefined; } function packageImportEntry(exportsField) { if (typeof exportsField === 'string') { return exportsField; } if (exportsField == null || typeof exportsField !== 'object') { return undefined; } const record = exportsField; if (Object.prototype.hasOwnProperty.call(record, '.')) { return stringFromConditionalExport(record['.']); } return stringFromConditionalExport(record); } function resolvePackageImportPath(packageDir) { const pkg = JSON.parse(readFileSync(joinPaths(packageDir, 'package.json'), 'utf8')); const entry = packageImportEntry(pkg.exports) ?? pkg.module ?? pkg.main; if (entry == null) { return undefined; } return joinPaths(packageDir, entry); } export function resolveLoaderModuleUrl(path, cwd, requireFn) { try { return pathToFileURL(requireFn.resolve(path)).href; } catch { if (isBarePackageSpecifier(path)) { const packageDir = findPackageDir(path, cwd); const entry = packageDir != null ? resolvePackageImportPath(packageDir) : undefined; if (entry != null) { return pathToFileURL(entry).href; } } const absolutePath = isAbsolute(path) ? path : joinPaths(cwd, path); return pathToFileURL(absolutePath).href; } } function toError(error) { return error instanceof Error ? error : new Error(String(error)); } function missingFunctionExportError(path) { return new Error(`custom loader module did not export a function: ${path}`); } function combineLoadErrors(first, second) { if (first != null && first.message !== second.message) { return new Error(`${first}\n${second}`); } return first ?? second; } function failedToLoadCustomLoader(loaderPointer, error) { return new Error(`Failed to load custom loader: ${loaderPointer}\n${error}`); } async function loadCustomLoaderByPath(path, cwd) { const requireFn = createLoaderRequire(cwd); let requireError; try { const loader = extractLoaderFromModule(requireFn(path)); if (typeof loader === 'function') { return loader; } } catch (error) { // createRequire cannot load ESM; fall through to import(). requireError = toError(error); } try { const href = resolveLoaderModuleUrl(path, cwd, requireFn); const importedModule = await import(href); const loader = extractLoaderFromModule(importedModule); if (typeof loader === 'function') { return loader; } throw missingFunctionExportError(path); } catch (error) { throw combineLoadErrors(requireError, toError(error)); } } function loadCustomLoaderByPathSync(path, cwd) { const loader = extractLoaderFromModule(createLoaderRequire(cwd)(path)); if (typeof loader === 'function') { return loader; } throw missingFunctionExportError(path); } export async function getCustomLoaderByPath(path, cwd) { try { return await loadCustomLoaderByPath(path, cwd); } catch { return null; } } export async function useCustomLoader(loaderPointer, cwd) { if (typeof loaderPointer === 'function') { return loaderPointer; } if (typeof loaderPointer === 'string') { try { return await loadCustomLoaderByPath(loaderPointer, cwd); } catch (error) { throw failedToLoadCustomLoader(loaderPointer, toError(error)); } } throw new Error(`Failed to load custom loader: ${loaderPointer}`); } export function useCustomLoaderSync(loaderPointer, cwd) { if (typeof loaderPointer === 'function') { return loaderPointer; } if (typeof loaderPointer === 'string') { try { return loadCustomLoaderByPathSync(loaderPointer, cwd); } catch (error) { throw failedToLoadCustomLoader(loaderPointer, toError(error)); } } throw new Error(`Failed to load custom loader: ${loaderPointer}`); }