Skip to content

Commit ef199fc

Browse files
committed
feat: basic extends support (#1)
1 parent 5a63f9a commit ef199fc

File tree

4 files changed

+53
-6
lines changed

4 files changed

+53
-6
lines changed

src/loader.ts

+35-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export interface LoadConfigOptions<T extends ConfigT=ConfigT> {
1010
name?: string
1111
cwd?: string
1212

13-
configFile?: false | string
13+
configFile?: string
1414

1515
rcFile?: false | string
1616
globalRc?: boolean
@@ -50,7 +50,7 @@ export async function loadConfig<T extends ConfigT=ConfigT> (opts: LoadConfigOpt
5050
}
5151

5252
// Load config file
53-
const { config, configPath } = await loadConfigFile(opts)
53+
const { config, configPath } = await loadConfigFile(opts.cwd, opts.configFile)
5454
ctx.configPath = configPath
5555

5656
// Load rc files
@@ -70,24 +70,54 @@ export async function loadConfig<T extends ConfigT=ConfigT> (opts: LoadConfigOpt
7070
opts.defaults
7171
) as T
7272

73+
// Allow extending
74+
await extendConfig(ctx.config, opts.configFile!, opts.cwd)
75+
ctx.config = defu(
76+
ctx.config,
77+
...ctx.config._extends.map(e => e.config)
78+
) as T
79+
7380
// Return resolved context
7481
return ctx
7582
}
7683

84+
async function extendConfig (config, configFile: string, cwd: string) {
85+
console.log('Extending from', cwd)
86+
config._extends = config._extends || []
87+
88+
const extendSources = (Array.isArray(config.extends) ? config.extends : [config.extends]).filter(Boolean)
89+
for (const extendSource of extendSources) {
90+
// TODO: Assuming extendSource is dir
91+
const _cwd = resolve(cwd, extendSource)
92+
const _config = await loadConfigFile(_cwd, configFile)
93+
await extendConfig(_config.config, configFile, _cwd)
94+
delete _config.config._extends
95+
config._extends.push({
96+
config: _config.config,
97+
meta: {
98+
cwd: _cwd,
99+
configPath: _config.configPath
100+
}
101+
})
102+
}
103+
104+
return config
105+
}
106+
77107
const jiti = createJiti(null, { cache: false, interopDefault: true })
78108

79-
async function loadConfigFile (opts: LoadConfigOptions) {
109+
async function loadConfigFile (cwd: string, configFile: string | false) {
80110
const res = {
81111
configPath: null,
82112
config: null
83113
}
84114

85-
if (!opts.configFile) {
115+
if (!configFile) {
86116
return res
87117
}
88118

89119
try {
90-
res.configPath = jiti.resolve(resolve(opts.cwd, opts.configFile), { paths: [opts.cwd] })
120+
res.configPath = jiti.resolve(resolve(cwd, configFile), { paths: [cwd] })
91121
res.config = jiti(res.configPath)
92122
if (typeof res.config === 'function') {
93123
res.config = await res.config()

test/fixture/base/foo.config.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default {
2+
baseConfig: true
3+
}

test/fixture/foo.config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export default {
2+
extends: './base',
23
configFile: true,
34
overriden: false
45
}

test/index.test.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { fileURLToPath } from 'url'
2+
import { resolve } from 'pathe'
23
import { expect, it, describe } from 'vitest'
34
import { loadConfig } from '../src'
45

56
describe('c12', () => {
67
it('load fixture config', async () => {
78
const fixtureDir = fileURLToPath(new URL('./fixture', import.meta.url))
9+
const rFixture = (...segments: string[]) => resolve(fixtureDir, ...segments)
10+
811
const { config } = await loadConfig({
912
cwd: fixtureDir,
1013
dotenv: true,
@@ -21,7 +24,17 @@ describe('c12', () => {
2124
configFile: true,
2225
rcFile: true,
2326
defaultConfig: true,
24-
overriden: true
27+
overriden: true,
28+
baseConfig: true,
29+
_extends: [
30+
{
31+
config: { baseConfig: true },
32+
meta: {
33+
configPath: rFixture('base/foo.config.ts'),
34+
cwd: rFixture('base')
35+
}
36+
}
37+
]
2538
})
2639
})
2740
})

0 commit comments

Comments
 (0)