Skip to content
This repository was archived by the owner on Apr 6, 2023. It is now read-only.

feat(nuxt): nested layout files #5842

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/nuxt/src/core/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { Nuxt, NuxtApp, NuxtPlugin, NuxtTemplate, ResolvedNuxtTemplate } fr
import { findPath, resolveFiles, normalizePlugin, normalizeTemplate, compileTemplate, templateUtils, tryResolveModule, resolvePath, resolveAlias } from '@nuxt/kit'

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> = {}): NuxtApp {
return defu(options, {
Expand Down Expand Up @@ -85,9 +85,9 @@ 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(',')}}`)
const layoutFiles = await resolveFiles(config.srcDir, `${config.dir?.layouts || 'layouts'}/**/*{${nuxt.options.extensions.join(',')}}`)
for (const file of layoutFiles) {
const name = getNameFromPath(file)
const name = getNameFromPathLocal(file, config.srcDir + '/layouts')
app.layouts[name] = app.layouts[name] || { name, file }
}
}
Expand Down
16 changes: 15 additions & 1 deletion packages/nuxt/src/core/utils.ts
Original file line number Diff line number Diff line change
@@ -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 <T, K extends keyof T> (arr: T[], key: K) {
const res: T[] = []
const seen = new Set<T[K]>()
Expand Down
19 changes: 19 additions & 0 deletions test/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,25 @@ describe('layouts', () => {
expect(html).toContain('some prop was passed')
await expectNoClientErrors('/layouts/with-props')
})
it('should apply desktop layout', async () => {
const html = await $fetch('/with-desktop-layout')

// Snapshot
// expect(html).toMatchInlineSnapshot()

expect(html).toContain('with-desktop-layout.vue')
expect(html).toContain('Desktop Layout:')
})

it('should apply mobile layout', async () => {
const html = await $fetch('/with-mobile-layout')

// Snapshot
// expect(html).toMatchInlineSnapshot()

expect(html).toContain('with-mobile-layout.vue')
expect(html).toContain('Mobile Layout:')
})
})

describe('reactivity transform', () => {
Expand Down
6 changes: 6 additions & 0 deletions test/fixtures/basic/layouts/desktop/default.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<template>
<div>
<h1>Default desktop layout</h1>
<slot />
</div>
</template>
6 changes: 6 additions & 0 deletions test/fixtures/basic/layouts/mobile/default.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<template>
<div>
<h1>Default mobile layout</h1>
<slot />
</div>
</template>
11 changes: 11 additions & 0 deletions test/fixtures/basic/pages/with-desktop-layout.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script setup>
definePageMeta({
layout: 'desktop/default'
})
</script>

<template>
<div>
<div>with-desktop-layout.vue</div>
</div>
</template>
11 changes: 11 additions & 0 deletions test/fixtures/basic/pages/with-mobile-layout.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script setup>
definePageMeta({
layout: 'mobile/default'
})
</script>

<template>
<div>
<div>with-mobile-layout.vue</div>
</div>
</template>