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