Skip to content

Commit bd9997b

Browse files
committed
feat: support custom resolver
1 parent a76db4d commit bd9997b

File tree

2 files changed

+59
-47
lines changed

2 files changed

+59
-47
lines changed

src/loader.ts

+48-46
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ export interface InputConfig extends Record<string, any> {}
1010

1111
export interface ResolvedConfig<T extends InputConfig=InputConfig> {
1212
config: T
13-
cwd: string
14-
configFile: string
15-
layers: ResolvedConfig<T>[]
13+
cwd?: string
14+
configFile?: string
15+
layers?: ResolvedConfig<T>[]
1616
}
1717

18-
export interface ExtendConfigOptions {
19-
extendKey?: string
18+
export interface ResolveConfigOptions {
19+
cwd: string
2020
}
2121

2222
export interface LoadConfigOptions<T extends InputConfig=InputConfig> {
@@ -33,7 +33,11 @@ export interface LoadConfigOptions<T extends InputConfig=InputConfig> {
3333
defaults?: T
3434
overrides?: T
3535

36-
extend?: false | ExtendConfigOptions
36+
resolve?: (id: string, opts: LoadConfigOptions) => null | ResolvedConfig | Promise<ResolvedConfig | null>
37+
38+
extend?: false | {
39+
extendKey?: string
40+
}
3741
}
3842

3943
export async function loadConfig<T extends InputConfig=InputConfig> (opts: LoadConfigOptions<T>): Promise<ResolvedConfig<T>> {
@@ -66,7 +70,7 @@ export async function loadConfig<T extends InputConfig=InputConfig> (opts: LoadC
6670
}
6771

6872
// Load config file
69-
const { config, configFile } = await loadConfigFile(opts.cwd, opts.configFile)
73+
const { config, configFile } = await resolveConfig('.', opts)
7074
if (configFile) {
7175
r.configFile = configFile
7276
}
@@ -90,7 +94,7 @@ export async function loadConfig<T extends InputConfig=InputConfig> (opts: LoadC
9094

9195
// Allow extending
9296
if (opts.extend) {
93-
await extendConfig(r.config, opts.configFile!, opts.cwd, opts.extend)
97+
await extendConfig(r.config, opts)
9498
r.layers = r.config._layers
9599
delete r.config._layers
96100
r.config = defu(
@@ -103,57 +107,56 @@ export async function loadConfig<T extends InputConfig=InputConfig> (opts: LoadC
103107
return r
104108
}
105109

106-
const GIT_PREFIXES = ['github:', 'gitlab:', 'bitbucket:', 'https://']
107-
108-
async function extendConfig (config, configFile: string, cwd: string, opts: ExtendConfigOptions) {
110+
async function extendConfig (config, opts: LoadConfigOptions) {
109111
config._layers = config._layers || []
110-
111-
const extendSources = (Array.isArray(config[opts.extendKey]) ? config[opts.extendKey] : [config[opts.extendKey]]).filter(Boolean)
112-
delete config[opts.extendKey]
113-
for (let extendSource of extendSources) {
114-
if (GIT_PREFIXES.some(prefix => extendSource.startsWith(prefix))) {
115-
const url = new URL(extendSource)
116-
const subPath = url.pathname.split('/').slice(2).join('/')
117-
const gitRepo = url.protocol + url.pathname.split('/').slice(0, 2).join('/')
118-
const tmpdir = resolve(os.tmpdir(), 'c12/', gitRepo.replace(/[#:@/\\]/g, '_'))
119-
await fsp.rm(tmpdir, { recursive: true }).catch(() => {})
120-
const gittar = await import('gittar').then(r => r.default || r)
121-
const tarFile = await gittar.fetch(gitRepo)
122-
await gittar.extract(tarFile, tmpdir)
123-
extendSource = resolve(tmpdir, subPath)
112+
if (!opts.extend) {
113+
return
114+
}
115+
const key = opts.extend.extendKey
116+
const extendSources = (Array.isArray(config[key]) ? config[key] : [config[key]]).filter(Boolean)
117+
delete config[key]
118+
for (const extendSource of extendSources) {
119+
const _config = await resolveConfig(extendSource, opts)
120+
if (!_config.config) {
121+
continue
124122
}
125-
126-
const isDir = !extname(extendSource)
127-
const _cwd = resolve(cwd, isDir ? extendSource : dirname(extendSource))
128-
const _config = await loadConfigFile(_cwd, isDir ? configFile : extendSource)
129-
if (!_config.config) { continue }
130-
await extendConfig(_config.config, configFile, _cwd, opts)
131-
config._layers.push({
132-
config: _config.config,
133-
cwd: _cwd,
134-
configFile: _config.configFile
135-
})
123+
await extendConfig(_config.config, { ...opts, cwd: _config.cwd })
124+
config._layers.push(_config)
136125
if (_config.config._layers) {
137126
config._layers.push(..._config.config._layers)
138127
delete _config.config._layers
139128
}
140129
}
141130
}
142131

132+
const GIT_PREFIXES = ['github:', 'gitlab:', 'bitbucket:', 'https://']
133+
143134
const jiti = createJiti(null, { cache: false, interopDefault: true })
144135

145-
async function loadConfigFile (cwd: string, configFile: string | false) {
146-
const res = {
147-
configFile: null,
148-
config: null
136+
async function resolveConfig (source: string, opts: LoadConfigOptions): Promise<ResolvedConfig> {
137+
if (opts.resolve) {
138+
const res = await opts.resolve(source, opts)
139+
if (res) {
140+
return res
141+
}
149142
}
150-
151-
if (!configFile) {
152-
return res
143+
if (GIT_PREFIXES.some(prefix => source.startsWith(prefix))) {
144+
const url = new URL(source)
145+
const subPath = url.pathname.split('/').slice(2).join('/')
146+
const gitRepo = url.protocol + url.pathname.split('/').slice(0, 2).join('/')
147+
const tmpdir = resolve(os.tmpdir(), 'c12/', gitRepo.replace(/[#:@/\\]/g, '_'))
148+
await fsp.rm(tmpdir, { recursive: true }).catch(() => {})
149+
const gittar = await import('gittar').then(r => r.default || r)
150+
const tarFile = await gittar.fetch(gitRepo)
151+
await gittar.extract(tarFile, tmpdir)
152+
source = resolve(tmpdir, subPath)
153153
}
154-
154+
const isDir = !extname(source)
155+
const cwd = resolve(opts.cwd, isDir ? source : dirname(source))
156+
if (isDir) { source = opts.configFile }
157+
const res: ResolvedConfig = { config: {}, cwd }
155158
try {
156-
res.configFile = jiti.resolve(resolve(cwd, configFile), { paths: [cwd] })
159+
res.configFile = jiti.resolve(resolve(cwd, source), { paths: [cwd] })
157160
res.config = jiti(res.configFile)
158161
if (typeof res.config === 'function') {
159162
res.config = await res.config()
@@ -163,6 +166,5 @@ async function loadConfigFile (cwd: string, configFile: string | false) {
163166
throw err
164167
}
165168
}
166-
167169
return res
168170
}

test/index.test.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@ describe('c12', () => {
99
const { config, layers } = await loadConfig({
1010
cwd: r('./fixture'),
1111
dotenv: true,
12+
resolve: (id) => {
13+
if (id === 'virtual') {
14+
return { config: { virtual: true } }
15+
}
16+
},
1217
overrides: {
13-
overriden: true
18+
overriden: true,
19+
extends: ['virtual']
1420
},
1521
defaults: {
1622
defaultConfig: true
@@ -24,6 +30,7 @@ describe('c12', () => {
2430
overriden: true,
2531
baseConfig: true,
2632
devConfig: true,
33+
virtual: true,
2734
colors: {
2835
primary: 'user_primary',
2936
secondary: 'theme_secondary',
@@ -57,6 +64,9 @@ describe('c12', () => {
5764
config: { devConfig: true },
5865
configFile: r('./fixture/config.dev.ts'),
5966
cwd: r('./fixture')
67+
},
68+
{
69+
config: { virtual: true }
6070
}
6171
])
6272
})

0 commit comments

Comments
 (0)