diff --git a/examples/config-extends/app.vue b/examples/config-extends/app.vue index 18f3c2b3f4..bea4876876 100644 --- a/examples/config-extends/app.vue +++ b/examples/config-extends/app.vue @@ -10,6 +10,7 @@ const bar = getBar()
{{ JSON.stringify(themeConfig, null, 2) }}
Base Button Fancy Button + UI Button
{{ foo }} {{ bar }}
diff --git a/examples/config-extends/nuxt.config.ts b/examples/config-extends/nuxt.config.ts index a08add69b9..27351ed2a6 100644 --- a/examples/config-extends/nuxt.config.ts +++ b/examples/config-extends/nuxt.config.ts @@ -1,7 +1,10 @@ import { defineNuxtConfig } from 'nuxt3' export default defineNuxtConfig({ - extends: './base', + extends: [ + './ui', + './base' + ], publicRuntimeConfig: { theme: { primaryColor: 'user_primary' diff --git a/examples/config-extends/ui/components/Button.vue b/examples/config-extends/ui/components/Button.vue new file mode 100644 index 0000000000..8508b7cd00 --- /dev/null +++ b/examples/config-extends/ui/components/Button.vue @@ -0,0 +1,14 @@ + + + diff --git a/examples/config-extends/ui/nuxt.config.ts b/examples/config-extends/ui/nuxt.config.ts new file mode 100644 index 0000000000..06d8bba06b --- /dev/null +++ b/examples/config-extends/ui/nuxt.config.ts @@ -0,0 +1,7 @@ +import { defineNuxtConfig } from 'nuxt3' + +export default defineNuxtConfig({ + components: [ + { path: './components', prefix: 'UI' } + ] +}) diff --git a/packages/bridge/src/module.ts b/packages/bridge/src/module.ts index a3e74a7052..01470a74ff 100644 --- a/packages/bridge/src/module.ts +++ b/packages/bridge/src/module.ts @@ -41,8 +41,12 @@ export default defineNuxtModule({ nuxtCtx.set(nuxt) } - // Mock _extends - nuxt.options._extends = nuxt.options._extends || [] + // Mock _layers + nuxt.options._layers = nuxt.options._layers || [{ + config: nuxt.options, + cwd: nuxt.options.rootDir, + configFile: nuxt.options._nuxtConfigFile + }] if (opts.nitro) { await setupNitroBridge() diff --git a/packages/kit/package.json b/packages/kit/package.json index 7e46ec8850..17750c39de 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@nuxt/schema": "^3.0.0", - "c12": "^0.1.4", + "c12": "^0.2.0", "consola": "^2.15.3", "defu": "^5.0.1", "globby": "^13.1.1", diff --git a/packages/kit/src/loader/config.ts b/packages/kit/src/loader/config.ts index 909e30d4e3..a783b7465a 100644 --- a/packages/kit/src/loader/config.ts +++ b/packages/kit/src/loader/config.ts @@ -44,7 +44,7 @@ export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise layer.configFile && !layer.configFile.endsWith('.nuxtrc')) // Resolve and apply defaults return applyDefaults(NuxtConfigSchema, nuxtConfig) as NuxtOptions diff --git a/packages/nitro/src/build.ts b/packages/nitro/src/build.ts index 343a6c658f..4db4b99080 100644 --- a/packages/nitro/src/build.ts +++ b/packages/nitro/src/build.ts @@ -93,10 +93,7 @@ export async function writeTypes (nitroContext: NitroContext) { } async function _build (nitroContext: NitroContext) { - const serverDirs = [ - ...nitroContext._extends.map(layer => layer.serverDir), - nitroContext._nuxt.serverDir - ] + const serverDirs = nitroContext._layers.map(layer => layer.serverDir) nitroContext.scannedMiddleware = ( await Promise.all(serverDirs.map(async dir => await scanMiddleware(dir))) @@ -181,7 +178,7 @@ async function _watch (nitroContext: NitroContext) { let watcher = startRollupWatcher(nitroContext) const serverDirs = [ - ...nitroContext._extends.map(layer => layer.serverDir), + ...nitroContext._layers.map(layer => layer.serverDir), nitroContext._nuxt.serverDir ] diff --git a/packages/nitro/src/context.ts b/packages/nitro/src/context.ts index ea8b7da227..ddb5bc9e46 100644 --- a/packages/nitro/src/context.ts +++ b/packages/nitro/src/context.ts @@ -84,7 +84,7 @@ export interface NitroContext { runtimeDir: string hooks: Hookable }, - _extends: Array<{ + _layers: Array<{ serverDir: string }> } @@ -158,7 +158,7 @@ export function getNitroContext (nuxtOptions: NuxtOptions, input: NitroInput): N runtimeDir, hooks: createHooks() }, - _extends: nuxtOptions._extends.map(layer => ({ + _layers: nuxtOptions._layers.map(layer => ({ serverDir: resolve(layer.config.srcDir, (layer.config.dir as any)?.server || 'server') })) } diff --git a/packages/nuxt3/src/auto-imports/module.ts b/packages/nuxt3/src/auto-imports/module.ts index b580bba570..ab2e9d2c83 100644 --- a/packages/nuxt3/src/auto-imports/module.ts +++ b/packages/nuxt3/src/auto-imports/module.ts @@ -40,14 +40,9 @@ export default defineNuxtModule>({ imports: options.imports }) - // composables/ dirs - let composablesDirs = [ - join(nuxt.options.srcDir, 'composables'), - ...options.dirs - ] - - // Extend with layers - for (const layer of nuxt.options._extends) { + // composables/ dirs from all layers + let composablesDirs = [] + for (const layer of nuxt.options._layers) { composablesDirs.push(resolve(layer.config.srcDir, 'composables')) for (const dir of (layer.config.autoImports?.dirs ?? [])) { composablesDirs.push(resolve(layer.config.srcDir, dir)) diff --git a/packages/nuxt3/src/components/module.ts b/packages/nuxt3/src/components/module.ts index 7a01452cee..99d3d358ec 100644 --- a/packages/nuxt3/src/components/module.ts +++ b/packages/nuxt3/src/components/module.ts @@ -39,26 +39,25 @@ export default defineNuxtModule({ })) } } - if (dir && typeof dir === 'object') { - return { - ...dir, - path: resolve(cwd, resolveAlias(dir.path, { - ...nuxt.options.alias, - '~': cwd - })) - } + if (!dir) { + return [] } - return [] + const dirs = (dir.dirs || [dir]).filter(_dir => _dir.path) + return dirs.map(_dir => ({ + ..._dir, + path: resolve(cwd, resolveAlias(_dir.path, { + ...nuxt.options.alias, + '~': cwd + })) + })) } // Resolve dirs nuxt.hook('app:resolve', async () => { - const allDirs = [ - ...normalizeDirs(componentOptions.dirs, nuxt.options.srcDir), - ...nuxt.options._extends - .map(layer => normalizeDirs(layer.config.components, layer.cwd)) - .flat() - ] + // components/ dirs from all layers + const allDirs = nuxt.options._layers + .map(layer => normalizeDirs(layer.config.components, layer.cwd)) + .flat() await nuxt.callHook('components:dirs', allDirs) diff --git a/packages/nuxt3/src/core/app.ts b/packages/nuxt3/src/core/app.ts index 7726229f59..3d878a8df9 100644 --- a/packages/nuxt3/src/core/app.ts +++ b/packages/nuxt3/src/core/app.ts @@ -71,9 +71,9 @@ export async function resolveApp (nuxt: Nuxt, app: NuxtApp) { app.errorComponent = (await findPath(['~/error'])) || resolve(nuxt.options.appDir, 'components/nuxt-error-page.vue') } - // Resolve layouts + // Resolve layouts/ from all config layers app.layouts = {} - for (const config of [nuxt.options, ...nuxt.options._extends.map(layer => layer.config)]) { + for (const config of nuxt.options._layers.map(layer => layer.config)) { const layoutFiles = await resolveFiles(config.srcDir, `${config.dir?.layouts || 'layouts'}/*{${nuxt.options.extensions.join(',')}}`) for (const file of layoutFiles) { const name = getNameFromPath(file) @@ -82,16 +82,19 @@ export async function resolveApp (nuxt: Nuxt, app: NuxtApp) { } // Resolve plugins - app.plugins = [] - for (const config of [...nuxt.options._extends.map(layer => layer.config), nuxt.options]) { + app.plugins = [ + ...nuxt.options.plugins.map(normalizePlugin) + ] + for (const config of nuxt.options._layers.map(layer => layer.config)) { app.plugins.push(...[ - ...config.plugins ?? [], + ...(config.plugins || []), ...await resolveFiles(config.srcDir, [ 'plugins/*.{ts,js,mjs,cjs,mts,cts}', 'plugins/*/index.*{ts,js,mjs,cjs,mts,cts}' ]) ].map(plugin => normalizePlugin(plugin as NuxtPlugin))) } + app.plugins = uniqueBy(app.plugins, 'src') // Extend app await nuxt.callHook('app:resolve', app) @@ -100,3 +103,15 @@ export async function resolveApp (nuxt: Nuxt, app: NuxtApp) { function getNameFromPath (path: string) { return kebabCase(basename(path).replace(extname(path), '')).replace(/["']/g, '') } + +function uniqueBy (arr: any[], uniqueKey: string) { + const seen = new Set() + const res = [] + for (const i of arr) { + const key = i[uniqueKey] + if (seen.has(key)) { continue } + res.push(i) + seen.add(key) + } + return res +} diff --git a/packages/nuxt3/src/core/nitro-legacy.ts b/packages/nuxt3/src/core/nitro-legacy.ts index ca00a10424..3f5233cc0b 100644 --- a/packages/nuxt3/src/core/nitro-legacy.ts +++ b/packages/nuxt3/src/core/nitro-legacy.ts @@ -77,10 +77,7 @@ export function initNitro (nuxt: Nuxt) { }) nuxt.hook('build:before', async () => { - const serverDirs = [ - ...nitroDevContext._extends.map(layer => layer.serverDir), - nitroDevContext._nuxt.serverDir - ] + const serverDirs = nitroDevContext._layers.map(layer => layer.serverDir) nitroDevContext.scannedMiddleware = ( await Promise.all(serverDirs.map(async dir => await scanMiddleware(dir))) diff --git a/packages/schema/package.json b/packages/schema/package.json index cd2cd5139a..500ac8b498 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -19,7 +19,7 @@ "unbuild": "latest" }, "dependencies": { - "c12": "^0.1.4", + "c12": "^0.2.0", "create-require": "^1.1.1", "defu": "^5.0.1", "jiti": "^1.13.0", diff --git a/packages/schema/src/types/config.ts b/packages/schema/src/types/config.ts index ac8765132c..85f0528729 100644 --- a/packages/schema/src/types/config.ts +++ b/packages/schema/src/types/config.ts @@ -10,7 +10,7 @@ export interface NuxtConfig extends DeepPartial { /** Normalized Nuxt options available as `nuxt.options.*` */ export interface NuxtOptions extends ConfigSchema { - _extends: ResolvedConfig[] + _layers: ResolvedConfig[] } export interface PublicRuntimeConfig extends Record { } diff --git a/yarn.lock b/yarn.lock index f60fa4eea9..a72645de19 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2914,7 +2914,7 @@ __metadata: "@nuxt/schema": ^3.0.0 "@types/lodash.template": ^4 "@types/semver": ^7 - c12: ^0.1.4 + c12: ^0.2.0 consola: ^2.15.3 defu: ^5.0.1 globby: ^13.1.1 @@ -3084,7 +3084,7 @@ __metadata: dependencies: "@types/lodash.template": ^4 "@types/semver": ^7 - c12: ^0.1.4 + c12: ^0.2.0 create-require: ^1.1.1 defu: ^5.0.1 jiti: ^1.13.0 @@ -7061,6 +7061,21 @@ __metadata: languageName: node linkType: hard +"c12@npm:^0.2.0": + version: 0.2.0 + resolution: "c12@npm:0.2.0" + dependencies: + defu: ^5.0.1 + dotenv: ^14.3.2 + gittar: ^0.1.1 + jiti: ^1.12.14 + mlly: ^0.4.1 + pathe: ^0.2.0 + rc9: ^1.2.0 + checksum: 1f69ed861368c8dc057f235a4925c4863b0423683a98578d3fa3c208dbcb43aadb907a5c855bf95969157210f632bfeb2bdf539f15a6c2d731d68f20273c00df + languageName: node + linkType: hard + "cac@npm:^6.7.12": version: 6.7.12 resolution: "cac@npm:6.7.12"