diff --git a/src/loader.ts b/src/loader.ts index 319cfc2..74cd87f 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -10,7 +10,7 @@ export interface LoadConfigOptions { name?: string cwd?: string - configFile?: false | string + configFile?: string rcFile?: false | string globalRc?: boolean @@ -50,7 +50,7 @@ export async function loadConfig (opts: LoadConfigOpt } // Load config file - const { config, configPath } = await loadConfigFile(opts) + const { config, configPath } = await loadConfigFile(opts.cwd, opts.configFile) ctx.configPath = configPath // Load rc files @@ -70,24 +70,54 @@ export async function loadConfig (opts: LoadConfigOpt opts.defaults ) as T + // Allow extending + await extendConfig(ctx.config, opts.configFile!, opts.cwd) + ctx.config = defu( + ctx.config, + ...ctx.config._extends.map(e => e.config) + ) as T + // Return resolved context return ctx } +async function extendConfig (config, configFile: string, cwd: string) { + console.log('Extending from', cwd) + config._extends = config._extends || [] + + const extendSources = (Array.isArray(config.extends) ? config.extends : [config.extends]).filter(Boolean) + for (const extendSource of extendSources) { + // TODO: Assuming extendSource is dir + const _cwd = resolve(cwd, extendSource) + const _config = await loadConfigFile(_cwd, configFile) + await extendConfig(_config.config, configFile, _cwd) + delete _config.config._extends + config._extends.push({ + config: _config.config, + meta: { + cwd: _cwd, + configPath: _config.configPath + } + }) + } + + return config +} + const jiti = createJiti(null, { cache: false, interopDefault: true }) -async function loadConfigFile (opts: LoadConfigOptions) { +async function loadConfigFile (cwd: string, configFile: string | false) { const res = { configPath: null, config: null } - if (!opts.configFile) { + if (!configFile) { return res } try { - res.configPath = jiti.resolve(resolve(opts.cwd, opts.configFile), { paths: [opts.cwd] }) + res.configPath = jiti.resolve(resolve(cwd, configFile), { paths: [cwd] }) res.config = jiti(res.configPath) if (typeof res.config === 'function') { res.config = await res.config() diff --git a/test/fixture/base/foo.config.ts b/test/fixture/base/foo.config.ts new file mode 100644 index 0000000..6639d00 --- /dev/null +++ b/test/fixture/base/foo.config.ts @@ -0,0 +1,3 @@ +export default { + baseConfig: true +} diff --git a/test/fixture/foo.config.ts b/test/fixture/foo.config.ts index c141ce9..28691d0 100644 --- a/test/fixture/foo.config.ts +++ b/test/fixture/foo.config.ts @@ -1,4 +1,5 @@ export default { + extends: './base', configFile: true, overriden: false } diff --git a/test/index.test.ts b/test/index.test.ts index ace32c8..3c99fcc 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -1,10 +1,13 @@ import { fileURLToPath } from 'url' +import { resolve } from 'pathe' import { expect, it, describe } from 'vitest' import { loadConfig } from '../src' describe('c12', () => { it('load fixture config', async () => { const fixtureDir = fileURLToPath(new URL('./fixture', import.meta.url)) + const rFixture = (...segments: string[]) => resolve(fixtureDir, ...segments) + const { config } = await loadConfig({ cwd: fixtureDir, dotenv: true, @@ -21,7 +24,17 @@ describe('c12', () => { configFile: true, rcFile: true, defaultConfig: true, - overriden: true + overriden: true, + baseConfig: true, + _extends: [ + { + config: { baseConfig: true }, + meta: { + configPath: rFixture('base/foo.config.ts'), + cwd: rFixture('base') + } + } + ] }) }) })