1- import { dirname } from 'path'
1+ import { resolve , dirname } from 'path'
2+ import { existsSync , realpathSync } from 'fs'
23import Hookable , { configHooksT } from 'hookable'
34import defu from 'defu'
45import jiti from 'jiti'
56import type { NuxtConfig } from '@nuxt/types'
67
78declare module '@nuxt/types' {
89 interface NuxtConfig {
9- _level ?: number
1010 hooks ?: configHooksT
1111 name ?: string
1212 extends ?: string
1313 alias ?: { [ key : string ] : string }
1414 }
1515}
1616
17- export function resolveConfig ( config : string | NuxtConfig , from : string = process . cwd ( ) , level = 0 ) : NuxtConfig {
18- if ( typeof config === 'string' ) {
19- const _require = jiti ( from )
20- const nuxtConfigFile = _require . resolve ( config )
21- config = _require ( nuxtConfigFile ) as NuxtConfig
22- config . _file = nuxtConfigFile
17+ export function nuxtConfig ( config : NuxtConfig ) : NuxtConfig {
18+ config = resolveConfig ( config , 0 )
2319
24- if ( ! config . rootDir ) {
25- config . rootDir = dirname ( nuxtConfigFile )
26- }
27- }
20+ delete config . _file
21+ delete config . _dir
22+ delete config . name
23+ delete config . extends
24+
25+ return config
26+ }
2827
28+ function resolveConfig ( config : NuxtConfig , level ) {
2929 if ( typeof config === 'function' ) {
3030 throw new TypeError ( 'extending is not possible with nuxt config as a function' )
3131 }
32- if ( ! config . rootDir ) {
33- config . rootDir = from
32+
33+ const dir = config . srcDir || config . rootDir || config . _dir
34+
35+ if ( dir && config . name ) {
36+ config . alias = config . alias || { }
37+ config . alias [ config . name ] = dir
3438 }
3539
36- config . _level = level
40+ if ( dir && config . components === undefined ) {
41+ config . components = [ ]
42+ const componentsDir = resolve ( dir , 'components' )
43+ if ( existsSync ( componentsDir ) ) {
44+ config . components . push ( { path : componentsDir } )
45+ }
46+ const globalComponentsDir = resolve ( dir , 'components/global' )
47+ if ( existsSync ( globalComponentsDir ) ) {
48+ config . components . push ( { path : globalComponentsDir , global : true } )
49+ }
50+ }
3751
3852 if ( config . extends ) {
39- const _resolvedExtends = resolveConfig ( config . extends , config . rootDir , level + 1 )
40- config = extendConfig ( config , _resolvedExtends )
53+ const base = loadConfig ( config . extends , dir )
54+ return mergeConfig ( config , resolveConfig ( base , level + 1 ) , level )
4155 }
4256
43- // delete tempory _file for error DX
44- delete config . _file
45-
4657 return config
4758}
4859
49- export function extendConfig ( target : NuxtConfig , base : NuxtConfig ) : NuxtConfig {
50- // Ensure base has required fields
51- if ( ! base . name ) {
52- throw new Error ( 'Config is missing the `name` property' + ( base . _file ? `in ${ base . _file } ` : '' ) )
53- }
54- if ( ! base . rootDir ) {
55- throw new Error ( 'Config is missing the `rootDir` property' )
56- }
57- if ( ! base . srcDir ) {
58- base . srcDir = base . rootDir
59- }
60+ function loadConfig ( configFile : string , from : string ) : NuxtConfig {
61+ const _require = jiti ( from )
62+ configFile = realpathSync ( _require . resolve ( configFile ) )
6063
61- // Ensure there is no name conflict
62- if ( target . alias && target . alias [ '~' + base . name ] ) {
63- throw new Error ( 'Name conflict: ' + base . name )
64- }
64+ let config = _require ( configFile )
65+ config = ( config . default || config ) as NuxtConfig
66+ config . _file = configFile
67+ config . _dir = dirname ( configFile )
6568
66- // Assign aliases for base
67- base . alias = base . alias || { }
68- base . alias [ '~' + base . name ] = base . srcDir
69- base . alias [ '~~' + base . name ] = base . rootDir
70- base . alias [ '@' + base . name ] = base . srcDir
71- base . alias [ '@@' + base . name ] = base . rootDir
69+ return config
70+ }
7271
72+ function mergeConfig ( target : NuxtConfig , base : NuxtConfig , level ) : NuxtConfig {
7373 // Custom merges
7474 const override : NuxtConfig = { }
7575
7676 // Merge hooks
7777 override . hooks = Hookable . mergeHooks ( base . hooks || { } , target . hooks || { } )
7878
79- // Merge components prop
79+ // Merge components
8080 if ( base . components || target . components ) {
8181 override . components = [
82- ...normalizeComponents ( target . components , { level : target . _level } ) ,
83- ...normalizeComponents ( base . components , { level : base . _level } )
82+ ...normalizeComponents ( target . components ) ,
83+ ...normalizeComponents ( base . components , true )
8484 ]
8585 }
8686
87- // Merge with defu
87+ // Mege with defu
8888 return { ...defu . arrayFn ( target , base ) , ...override }
8989}
9090
91- function normalizeComponents ( components : NuxtConfig [ 'components' ] , defaults = { } ) {
91+ function normalizeComponents ( components : NuxtConfig [ 'components' ] , isBase ?: boolean ) {
9292 if ( typeof components === 'boolean' || ! components ) {
9393 components = [ ]
9494 }
@@ -99,9 +99,12 @@ function normalizeComponents (components: NuxtConfig['components'], defaults = {
9999 }
100100
101101 components = components . map ( dir => ( {
102- ...defaults ,
103102 ...( typeof dir === 'string' ? { path : dir } : dir )
104103 } ) )
105104
105+ for ( const component of components ) {
106+ component . level = ( component . level || 0 ) + ( isBase ? 1 : 0 )
107+ }
108+
106109 return components
107110}
0 commit comments