UNPKG

@graphql-tools/load-files

Version:

A set of utils for faster development of GraphQL tools

210 lines (209 loc) • 8.65 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.matchFilesSync = exports.matchFiles = void 0; exports.loadFilesSync = loadFilesSync; exports.loadFiles = loadFiles; const tslib_1 = require("tslib"); const fs_1 = require("fs"); const module_1 = require("module"); const path_1 = require("path"); const process_1 = require("process"); const graphql_1 = require("graphql"); const unixify_1 = tslib_1.__importDefault(require("unixify")); const utils_1 = require("@graphql-tools/utils"); const matchFiles_js_1 = require("./matchFiles.js"); var matchFiles_js_2 = require("./matchFiles.js"); Object.defineProperty(exports, "matchFiles", { enumerable: true, get: function () { return matchFiles_js_2.matchFiles; } }); Object.defineProperty(exports, "matchFilesSync", { enumerable: true, get: function () { return matchFiles_js_2.matchFilesSync; } }); const { readFile, stat } = fs_1.promises; const DEFAULT_IGNORED_EXTENSIONS = ['spec', 'test', 'd', 'map']; const DEFAULT_EXTENSIONS = ['gql', 'graphql', 'graphqls', 'ts', 'js']; const DEFAULT_EXPORT_NAMES = ['schema', 'typeDef', 'typeDefs', 'resolver', 'resolvers']; const DEFAULT_EXTRACT_EXPORTS_FACTORY = (exportNames) => (fileExport) => { if (!fileExport) { return null; } if (fileExport.default) { for (const exportName of exportNames) { if (fileExport.default[exportName]) { return fileExport.default[exportName]; } } return fileExport.default; } for (const exportName of exportNames) { if (fileExport[exportName]) { return fileExport[exportName]; } } return fileExport; }; function resolveAgainstCwd(path, baseCwd) { if ((0, path_1.isAbsolute)(path)) { return path; } return (0, path_1.join)(baseCwd || (0, process_1.cwd)(), path); } function isDirectorySync(path, baseCwd) { try { const pathStat = (0, fs_1.statSync)(resolveAgainstCwd(path, baseCwd)); return pathStat.isDirectory(); } catch (e) { return false; } } async function isDirectory(path, baseCwd) { try { const pathStat = await stat(resolveAgainstCwd(path, baseCwd)); return pathStat.isDirectory(); } catch (e) { return false; } } function scanForFilesSync(globStr, globOptions = {}) { return (0, matchFiles_js_1.matchFilesSync)(globStr, { absolute: true, ...globOptions }); } function formatExtension(extension) { return extension.charAt(0) === '.' ? extension : `.${extension}`; } function buildGlob(basePath, extensions = [], ignoredExtensions = [], recursive) { const ignored = ignoredExtensions.length > 0 ? `!(${ignoredExtensions.map(e => `*${formatExtension(e)}`).join('|')})` : '*'; const ext = extensions.map(e => `*${formatExtension(e)}`).join('|'); return `${basePath}${recursive ? '/**' : ''}/${ignored}+(${ext})`; } const LoadFilesDefaultOptions = { ignoredExtensions: DEFAULT_IGNORED_EXTENSIONS, extensions: DEFAULT_EXTENSIONS, useRequire: false, requireMethod: null, globOptions: { absolute: true, cwd: (0, process_1.cwd)(), }, exportNames: DEFAULT_EXPORT_NAMES, recursive: true, ignoreIndex: false, }; /** * Synchronously loads files using the provided glob pattern. * @param pattern Glob pattern or patterns to use when loading files * @param options Additional options */ function loadFilesSync(pattern, options = LoadFilesDefaultOptions) { const execOptions = { ...LoadFilesDefaultOptions, ...options }; const baseCwd = options.globOptions?.cwd || execOptions.globOptions?.cwd || (0, process_1.cwd)(); const relevantPaths = scanForFilesSync((0, utils_1.asArray)(pattern).map(path => isDirectorySync(path, baseCwd) ? buildGlob((0, unixify_1.default)(path), execOptions.extensions, execOptions.ignoredExtensions, execOptions.recursive) : (0, unixify_1.default)(path)), options.globOptions); const extractExports = execOptions.extractExports || DEFAULT_EXTRACT_EXPORTS_FACTORY(execOptions.exportNames ?? []); const requireMethod = execOptions.requireMethod || (0, module_1.createRequire)((0, path_1.join)(options?.globOptions?.cwd || (0, process_1.cwd)(), 'noop.js')); return relevantPaths .map(path => { if (!checkExtension(path, options)) { return null; } if (isIndex(path, execOptions.extensions) && options.ignoreIndex) { return false; } const extension = (0, path_1.extname)(path); if (extension === formatExtension('js') || extension === formatExtension('ts') || execOptions.useRequire) { const fileExports = requireMethod(path); const extractedExport = extractExports(fileExports); return extractedExport; } else { const maybeSDL = (0, fs_1.readFileSync)(path, { encoding: 'utf-8' }); return tryToParse(maybeSDL); } }) .filter(v => v); } async function scanForFiles(globStr, globOptions = {}) { return (0, matchFiles_js_1.matchFiles)(globStr, { absolute: true, ...globOptions }); } const checkExtension = (path, { extensions, ignoredExtensions }) => { if (ignoredExtensions) { for (const ignoredExtension of ignoredExtensions) { if (path.endsWith(formatExtension(ignoredExtension))) { return false; } } } if (!extensions) { return true; } for (const extension of extensions) { const formattedExtension = formatExtension(extension); if (path.endsWith(formattedExtension)) { if (ignoredExtensions) { for (const ignoredExtension of ignoredExtensions) { const formattedIgnoredExtension = formatExtension(ignoredExtension); if (path.endsWith(formattedIgnoredExtension + formattedExtension)) { return false; } } } return true; } } return false; }; /** * Asynchronously loads files using the provided glob pattern. * @param pattern Glob pattern or patterns to use when loading files * @param options Additional options */ async function loadFiles(pattern, options = LoadFilesDefaultOptions) { const execOptions = { ...LoadFilesDefaultOptions, ...options }; const baseCwd = options.globOptions?.cwd || execOptions.globOptions?.cwd || (0, process_1.cwd)(); const relevantPaths = await scanForFiles(await Promise.all((0, utils_1.asArray)(pattern).map(async (path) => (await isDirectory(path, baseCwd)) ? buildGlob((0, unixify_1.default)(path), execOptions.extensions, execOptions.ignoredExtensions, execOptions.recursive) : (0, unixify_1.default)(path))), options.globOptions); const extractExports = execOptions.extractExports || DEFAULT_EXTRACT_EXPORTS_FACTORY(execOptions.exportNames ?? []); const defaultRequireMethod = (path) => Promise.resolve(`${path}`).then(s => tslib_1.__importStar(require(s))).catch(importError => { const cwdRequire = (0, module_1.createRequire)((0, path_1.join)(options?.globOptions?.cwd || (0, process_1.cwd)(), 'noop.js')); try { return cwdRequire(path); } catch (e) { throw importError; } }) .then(m => m.default || m); const requireMethod = execOptions.requireMethod || defaultRequireMethod; return Promise.all(relevantPaths .filter(path => checkExtension(path, options) && !(isIndex(path, execOptions.extensions) && options.ignoreIndex)) .map(async (path) => { const extension = (0, path_1.extname)(path); if (extension === formatExtension('js') || extension === formatExtension('ts') || execOptions.useRequire) { const fileExports = await requireMethod(path); const extractedExport = extractExports(fileExports); return extractedExport; } else { const maybeSDL = await readFile(path, { encoding: 'utf-8' }); return tryToParse(maybeSDL); } })); } function isIndex(path, extensions = []) { const IS_INDEX = /(\/|\\)index\.[^\/\\]+$/i; // (/ or \) AND `index.` AND (everything except \ and /)(end of line) return IS_INDEX.test(path) && extensions.some(ext => path.endsWith(formatExtension(ext))); } function tryToParse(maybeSDL) { try { return (0, graphql_1.parse)(maybeSDL); } catch (e) { return maybeSDL; } }