UNPKG

read-config-ng

Version:
173 lines 5.79 kB
import * as path from 'path'; import { ReadConfigError } from '../read-config-error.js'; import { mergeConfigs } from './merge-configs.js'; import { load, loadSync } from './parse/index.js'; import { resolvePath, resolvePathSync } from './resolve-path.js'; /** * Merge a configuration file with its parent hierarchy asynchronously */ export async function mergeParents(configPath, opts = {}) { const basedirs = Array.isArray(opts.basedir) ? opts.basedir : [opts.basedir || '.']; const configResolvedPath = await resolvePath(configPath, basedirs); if (!configResolvedPath) { if (isOptional(configPath, opts)) { return {}; } throw configNotFound(configPath); } return mergeParentsRecursive(configResolvedPath, opts); } /** * Merge a configuration file with its parent hierarchy synchronously */ export function mergeParentsSync(configPath, opts = {}) { const basedirs = Array.isArray(opts.basedir) ? opts.basedir : [opts.basedir || '.']; const configResolvedPath = resolvePathSync(configPath, basedirs); if (!configResolvedPath) { if (isOptional(configPath, opts)) { return {}; } throw configNotFound(configPath); } return mergeParentsSyncRecursive(configResolvedPath, opts); } /** * Recursively merge parent configurations asynchronously */ async function mergeParentsRecursive(configPath, opts) { const config = await loadConfig(configPath, opts); const parentField = typeof opts.parentField === 'string' ? opts.parentField : '__parent'; if (!opts.parentField || !config[parentField]) { return config; } const parentPathValue = config[parentField]; const basedirs = generateBasedirs(configPath, opts); const parentPath = await resolvePath(parentPathValue, basedirs); let parentConfig = {}; if (!parentPath) { if (!isOptional(parentPathValue, opts)) { throw parentConfigNotFound(parentPathValue, configPath); } } else { parentConfig = await mergeParentsRecursive(parentPath, opts); } const result = mergeConfigs([parentConfig, config]); delete result[parentField]; return result; } /** * Recursively merge parent configurations synchronously */ function mergeParentsSyncRecursive(configPath, opts) { const config = loadConfigSync(configPath, opts); const parentField = typeof opts.parentField === 'string' ? opts.parentField : '__parent'; if (!opts.parentField || !config[parentField]) { return config; } const parentPathValue = config[parentField]; const basedirs = generateBasedirs(configPath, opts); const parentPath = resolvePathSync(parentPathValue, basedirs); let parentConfig = {}; if (!parentPath) { if (!isOptional(parentPathValue, opts)) { throw parentConfigNotFound(parentPathValue, configPath); } } else { parentConfig = mergeParentsSyncRecursive(parentPath, opts); } const result = mergeConfigs([parentConfig, config]); delete result[parentField]; return result; } /** * Load a configuration file asynchronously */ async function loadConfig(configPath, opts) { const basedirs = Array.isArray(opts.basedir) ? opts.basedir : [opts.basedir || '.']; const configResolvedPath = await resolvePath(configPath, basedirs); if (!configResolvedPath) { if (isOptional(configPath, opts)) { return {}; } throw configNotFound(configPath); } return await load(configResolvedPath); } /** * Load a configuration file synchronously */ function loadConfigSync(configPath, opts) { const basedirs = Array.isArray(opts.basedir) ? opts.basedir : [opts.basedir || '.']; const configResolvedPath = resolvePathSync(configPath, basedirs); if (!configResolvedPath) { if (isOptional(configPath, opts)) { return {}; } throw configNotFound(configPath); } return loadSync(configResolvedPath); } /** * Check if a configuration path is optional */ function isOptional(configPath, opts) { let optional = opts.optional; if (!optional) { return false; } const optionalPaths = Array.isArray(optional) ? optional : [optional]; if (optionalPaths.indexOf(configPath) >= 0) { return true; } return optionalPaths.some(optPath => { if (optPath.indexOf('*') >= 0) { // Convert glob pattern to regex let pattern = escapeRegExp(optPath); pattern = pattern.replace(/\\\*\\\*/g, '.*').replace(/\\\*/g, '[^/]*'); return new RegExp(pattern).test(configPath); } return false; }); } /** * Escape special regex characters */ function escapeRegExp(str) { return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); } /** * Generate base directories for path resolution */ function generateBasedirs(configPath, opts) { const dirs = [ path.dirname(configPath), process.cwd() ]; if (opts.basedir) { if (Array.isArray(opts.basedir)) { dirs.push(...opts.basedir); } else { dirs.push(opts.basedir); } } return dirs.filter(Boolean); } /** * Create config not found error */ function configNotFound(configPath) { return ReadConfigError.fileNotFound(configPath); } /** * Create parent config not found error */ function parentConfigNotFound(parentPath, configPath) { return new ReadConfigError(`Parent config file not found '${parentPath}' for ${configPath}`, 'PARENT_NOT_FOUND', configPath); } // Legacy exports for backward compatibility export const async = mergeParents; export const sync = mergeParentsSync; //# sourceMappingURL=merge-parents.js.map