1 | import { YError } from '../yerror.js';
|
---|
2 | let previouslyVisitedConfigs = [];
|
---|
3 | let shim;
|
---|
4 | export 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 | }
|
---|
36 | function checkForCircularExtends(cfgPath) {
|
---|
37 | if (previouslyVisitedConfigs.indexOf(cfgPath) > -1) {
|
---|
38 | throw new YError(`Circular extended configurations: '${cfgPath}'.`);
|
---|
39 | }
|
---|
40 | }
|
---|
41 | function getPathToDefaultConfig(cwd, pathToExtend) {
|
---|
42 | return shim.path.resolve(cwd, pathToExtend);
|
---|
43 | }
|
---|
44 | function 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 | }
|
---|