apply-extends.js 1.9 KB

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