1import { YError } from '../yerror.js'; 2let previouslyVisitedConfigs = []; 3let shim; 4export function applyExtends(config, cwd, mergeExtends, _shim) { 5 shim = _shim; 6 let defaultConfig = {}; 7 if (Object.prototype.hasOwnProperty.call(config, 'extends')) { 8 if (typeof config.extends !== 'string') 9 return defaultConfig; 10 const isPath = /\.json|\..*rc$/.test(config.extends); 11 let pathToDefault = null; 12 if (!isPath) { 13 try { 14 pathToDefault = require.resolve(config.extends); 15 } 16 catch (_err) { 17 return config; 18 } 19 } 20 else { 21 pathToDefault = getPathToDefaultConfig(cwd, config.extends); 22 } 23 checkForCircularExtends(pathToDefault); 24 previouslyVisitedConfigs.push(pathToDefault); 25 defaultConfig = isPath 26 ? JSON.parse(shim.readFileSync(pathToDefault, 'utf8')) 27 : require(config.extends); 28 delete config.extends; 29 defaultConfig = applyExtends(defaultConfig, shim.path.dirname(pathToDefault), mergeExtends, shim); 30 } 31 previouslyVisitedConfigs = []; 32 return mergeExtends 33 ? mergeDeep(defaultConfig, config) 34 : Object.assign({}, defaultConfig, config); 35} 36function checkForCircularExtends(cfgPath) { 37 if (previouslyVisitedConfigs.indexOf(cfgPath) > -1) { 38 throw new YError(`Circular extended configurations: '${cfgPath}'.`); 39 } 40} 41function getPathToDefaultConfig(cwd, pathToExtend) { 42 return shim.path.resolve(cwd, pathToExtend); 43} 44function mergeDeep(config1, config2) { 45 const target = {}; 46 function isObject(obj) { 47 return obj && typeof obj === 'object' && !Array.isArray(obj); 48 } 49 Object.assign(target, config1); 50 for (const key of Object.keys(config2)) { 51 if (isObject(config2[key]) && isObject(target[key])) { 52 target[key] = mergeDeep(config1[key], config2[key]); 53 } 54 else { 55 target[key] = config2[key]; 56 } 57 } 58 return target; 59} 60