diff --git a/packages/nuxt/src/app/components/layout.ts b/packages/nuxt/src/app/components/layout.ts index 13fbeb923b..f82dcf8dc4 100644 --- a/packages/nuxt/src/app/components/layout.ts +++ b/packages/nuxt/src/app/components/layout.ts @@ -55,7 +55,46 @@ export default defineComponent({ // Need to ensure (if we are not a child of ``) that we use synchronous route (not deferred) const injectedRoute = inject('_route') as RouteLocationNormalizedLoaded const route = injectedRoute === useRoute() ? useVueRouterRoute() : injectedRoute - const layout = computed(() => unref(props.name) ?? route.meta.layout as string ?? 'default') + const layout = computed(() => { + const layoutName = unref(props.name) ?? route.meta.layout as string ?? 'default' + + if (typeof layoutName === 'string') { + const layoutPath = layoutName.replace(/-/g, '/') + + // Check if layout exists for example `desktop-default` will translate to `layouts/desktop/default` + if (layoutPath in layouts) { + return layoutPath + } + + // Check if layout exists for example `desktop` or `desktop-index` will translate to ` `layouts/desktop/index` + if ((layoutPath + '/index') in layouts) { + return layoutPath + '/index' + } + + // If the directory inside layouts has has a dash in the name such as `desktop-base` we need to check for that + if (layoutName.includes('-')) { + const layoutPath = layoutName.replace(/-/g, '/') + + // Check if layout exists for example `desktop-base` will translate to `layouts/desktop/base` + if (layoutPath in layouts) { + return layoutPath + } + + // Check if layout exists for example `desktop-base` will translate to `layouts/desktop-base/base` + const layoutPathLast = layoutName.split('-').pop() + if (layoutName + '/' + layoutPathLast in layouts) { + return layoutName + '/' + layoutPathLast + } + + // Check if layout exists for example `desktop-base` will translate to `layouts/desktop-base/index` + if (layoutName + '/index' in layouts) { + return layoutName + '/index' + } + } + } + + return layoutName + }) let vnode: VNode let _layout: string | false diff --git a/packages/nuxt/src/core/app.ts b/packages/nuxt/src/core/app.ts index e20b5da32f..0e9b458c13 100644 --- a/packages/nuxt/src/core/app.ts +++ b/packages/nuxt/src/core/app.ts @@ -5,7 +5,7 @@ import { compileTemplate, findPath, normalizePlugin, normalizeTemplate, resolveA import type { Nuxt, NuxtApp, NuxtPlugin, NuxtTemplate, ResolvedNuxtTemplate } from 'nuxt/schema' import * as defaultTemplates from './templates' -import { getNameFromPath, hasSuffix, uniqueBy } from './utils' +import { getNameFromPath, getNameFromPathLocal, hasSuffix, uniqueBy } from './utils' export function createApp (nuxt: Nuxt, options: Partial = {}): NuxtApp { return defu(options, { @@ -85,10 +85,10 @@ export async function resolveApp (nuxt: Nuxt, app: NuxtApp) { // Resolve layouts/ from all config layers app.layouts = {} 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) - app.layouts[name] = app.layouts[name] || { name, file } + const layoutFiles = await resolveFiles(config.srcDir, `${config.dir?.layouts || 'layouts'}/**/*{${nuxt.options.extensions.join(',')}}`) + for (const file of layoutFiles) { + const name = getNameFromPathLocal(file, config.srcDir + '/layouts') + app.layouts[name] = app.layouts[name] || { name, file } } } diff --git a/packages/nuxt/src/core/utils.ts b/packages/nuxt/src/core/utils.ts index 46757ec20b..3dfe496ae4 100644 --- a/packages/nuxt/src/core/utils.ts +++ b/packages/nuxt/src/core/utils.ts @@ -1,10 +1,24 @@ import { basename, extname } from 'pathe' -import { kebabCase, pascalCase } from 'scule' +import { kebabCase, pascalCase, snakeCase } from 'scule' export function getNameFromPath (path: string) { return kebabCase(basename(path).replace(extname(path), '')).replace(/["']/g, '') } +export function getNameFromPathLocal (path: string, src: string) { + const sourcePath = path + .replace(src + '/', '') + .split('/') + .slice(0, -1) + // .map(e => snakeCase(e)) + .join('/') + return ( + sourcePath + + (sourcePath ? '/' : '') + + kebabCase(basename(path).replace(extname(path), '')).replace(/["']/g, '') + ) +} + export function uniqueBy (arr: T[], key: K) { const res: T[] = [] const seen = new Set()