UNPKG

@graphql-mesh/cli

Version:
132 lines (131 loc) 5.65 kB
import { Ajv } from 'ajv'; import { cosmiconfig, defaultLoaders } from 'cosmiconfig'; import { processConfig } from '@graphql-mesh/config'; import { path, process } from '@graphql-mesh/cross-helpers'; import { include } from '@graphql-mesh/include'; import { jsonSchema } from '@graphql-mesh/types'; import { DefaultLogger, loadYaml } from '@graphql-mesh/utils'; export function validateConfig(config, filepath, initialLoggerPrefix, throwOnInvalidConfig = false) { if (jsonSchema) { const ajv = new Ajv({ strict: false, }); jsonSchema.$schema = undefined; const isValid = ajv.validate(jsonSchema, config); if (!isValid) { if (throwOnInvalidConfig) { const aggregateError = new AggregateError(ajv.errors.map(e => { const error = new Error(e.message); error.stack += `\n at ${filepath}:0:0`; return error; }), 'Configuration file is not valid'); throw aggregateError; } const logger = new DefaultLogger(initialLoggerPrefix).child('config'); logger.warn('Configuration file is not valid!'); logger.warn("This is just a warning! It doesn't have any effects on runtime."); ajv.errors.forEach(error => { let errorMessage = ''; if (error.propertyName) { errorMessage += `Property: ${error.propertyName} \n`; } if (error.data) { errorMessage += `Given: ${error.data} \n`; } errorMessage += `Error: ${error.message}`; logger.warn(errorMessage); }); } } } export async function findConfig(options) { const { configName = 'mesh', dir: configDir = '', initialLoggerPrefix = '🕸️ Mesh', importFn, } = options || {}; const dir = path.isAbsolute(configDir) ? configDir : path.join(process.cwd(), configDir); const explorer = cosmiconfig(configName, { searchPlaces: [ 'package.json', `.${configName}rc`, `.${configName}rc.json`, `.${configName}rc.yaml`, `.${configName}rc.yml`, `.${configName}rc.js`, `.${configName}rc.ts`, `.${configName}rc.cjs`, `${configName}.config.js`, `${configName}.config.cjs`, ], loaders: { '.json': customLoader('json', importFn, initialLoggerPrefix), '.yaml': customLoader('yaml', importFn, initialLoggerPrefix), '.yml': customLoader('yaml', importFn, initialLoggerPrefix), '.js': customLoader('js', importFn, initialLoggerPrefix), '.ts': customLoader('js', importFn, initialLoggerPrefix), noExt: customLoader('yaml', importFn, initialLoggerPrefix), }, }); const results = await explorer.search(dir); if (!results) { throw new Error(`No ${configName} config file found in "${dir}"!`); } return results; } export async function findAndParseConfig(options) { const { configName = 'mesh', dir: configDir = '', initialLoggerPrefix = '🕸️ Mesh', importFn, ...restOptions } = options || {}; const dir = path.isAbsolute(configDir) ? configDir : path.join(process.cwd(), configDir); const explorer = cosmiconfig(configName, { searchPlaces: [ 'package.json', `.${configName}rc`, `.${configName}rc.json`, `.${configName}rc.yaml`, `.${configName}rc.yml`, `.${configName}rc.js`, `.${configName}rc.ts`, `.${configName}rc.cjs`, `${configName}.config.js`, `${configName}.config.cjs`, ], loaders: { '.json': customLoader('json', importFn, initialLoggerPrefix), '.yaml': customLoader('yaml', importFn, initialLoggerPrefix), '.yml': customLoader('yaml', importFn, initialLoggerPrefix), '.js': customLoader('js', importFn, initialLoggerPrefix), '.ts': customLoader('js', importFn, initialLoggerPrefix), noExt: customLoader('yaml', importFn, initialLoggerPrefix), }, }); const results = await explorer.search(dir); if (!results) { throw new Error(`No ${configName} config file found in "${dir}"!`); } const config = results.config; validateConfig(config, results.filepath, initialLoggerPrefix, restOptions.throwOnInvalidConfig); return processConfig(config, { dir, initialLoggerPrefix, importFn, ...restOptions }); } function customLoader(ext, importFn = include, initialLoggerPrefix = '🕸️ Mesh') { const logger = new DefaultLogger(initialLoggerPrefix).child('config'); function loader(filepath, content) { if (process.env) { content = content.replace(/\$\{(.*?)\}/g, (_, variable) => { let varName = variable; let defaultValue = ''; if (variable.includes(':')) { const spl = variable.split(':'); varName = spl.shift(); defaultValue = spl.join(':'); } return process.env[varName] || defaultValue; }); } if (ext === 'json') { return defaultLoaders['.json'](filepath, content); } if (ext === 'yaml') { return loadYaml(filepath, content, logger); } if (ext === 'js') { return importFn(filepath); } } return loader; }