@graphql-tools/load
Version:
A set of utils for faster development of GraphQL tools
181 lines (180 loc) • 5.88 kB
JavaScript
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}`);
}