• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1
2'use strict'
3const fs = require('fs')
4const path = require('path')
5const YError = require('./yerror')
6
7let previouslyVisitedConfigs = []
8
9function checkForCircularExtends (cfgPath) {
10  if (previouslyVisitedConfigs.indexOf(cfgPath) > -1) {
11    throw new YError(`Circular extended configurations: '${cfgPath}'.`)
12  }
13}
14
15function getPathToDefaultConfig (cwd, pathToExtend) {
16  return path.resolve(cwd, pathToExtend)
17}
18
19function mergeDeep (config1, config2) {
20  const target = {}
21  const isObject = obj => obj && typeof obj === 'object' && !Array.isArray(obj)
22  Object.assign(target, config1)
23  for (let key of Object.keys(config2)) {
24    if (isObject(config2[key]) && isObject(target[key])) {
25      target[key] = mergeDeep(config1[key], config2[key])
26    } else {
27      target[key] = config2[key]
28    }
29  }
30  return target
31}
32
33function applyExtends (config, cwd, mergeExtends) {
34  let defaultConfig = {}
35
36  if (Object.prototype.hasOwnProperty.call(config, 'extends')) {
37    if (typeof config.extends !== 'string') return defaultConfig
38    const isPath = /\.json|\..*rc$/.test(config.extends)
39    let pathToDefault = null
40    if (!isPath) {
41      try {
42        pathToDefault = require.resolve(config.extends)
43      } catch (err) {
44        // most likely this simply isn't a module.
45      }
46    } else {
47      pathToDefault = getPathToDefaultConfig(cwd, config.extends)
48    }
49    // maybe the module uses key for some other reason,
50    // err on side of caution.
51    if (!pathToDefault && !isPath) return config
52
53    checkForCircularExtends(pathToDefault)
54
55    previouslyVisitedConfigs.push(pathToDefault)
56
57    defaultConfig = isPath ? JSON.parse(fs.readFileSync(pathToDefault, 'utf8')) : require(config.extends)
58    delete config.extends
59    defaultConfig = applyExtends(defaultConfig, path.dirname(pathToDefault), mergeExtends)
60  }
61
62  previouslyVisitedConfigs = []
63
64  return mergeExtends ? mergeDeep(defaultConfig, config) : Object.assign({}, defaultConfig, config)
65}
66
67module.exports = applyExtends
68