feat(kit): add named layer aliases (#30948)

This commit is contained in:
Daniel Roe 2025-02-11 20:58:42 +00:00 committed by GitHub
parent b391f8f03a
commit 5aca9e63c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 83 additions and 13 deletions

View File

@ -18,10 +18,16 @@ One of the core features of Nuxt is the layers and extending support. You can ex
## Usage ## Usage
By default, any layers within your project in the `~~/layers` directory will be automatically registered as layers in your project By default, any layers within your project in the `~~/layers` directory will be automatically registered as layers in your project.
::note ::note
Layer auto-registration was introduced in Nuxt v3.12.0 Layer auto-registration was introduced in Nuxt v3.12.0.
::
In addition, named layer aliases to the `srcDir` of each of these layers will automatically be created. For example, you will be able to access the `~~/layers/test` layer via `#layers/test`.
::note
Named layer aliases were introduced in Nuxt v3.16.0.
:: ::
In addition, you can extend from a layer by adding the [extends](/docs/api/nuxt-config#extends) property to your [`nuxt.config`](/docs/guide/directory-structure/nuxt-config) file. In addition, you can extend from a layer by adding the [extends](/docs/api/nuxt-config#extends) property to your [`nuxt.config`](/docs/guide/directory-structure/nuxt-config) file.

View File

@ -164,9 +164,25 @@ When publishing the layer as a private npm package, you need to make sure you lo
## Tips ## Tips
### Named Layer Aliases
Auto-scanned layers (from your `~~/layers` directory) automatically create aliases. For example, you can access your `~~/layers/test` layer via `#layers/test`.
If you want to create named layer aliases for other layers, you can specify a name in the configuration of the layer.
```ts [nuxt.config.ts]
export default defineNuxtConfig({
$meta: {
name: 'example',
},
})
```
This will produce an alias of `#layers/example` which points to your layer.
### Relative Paths and Aliases ### Relative Paths and Aliases
When importing using aliases (such as `~/` and `@/`) in a layer components and composables, note that aliases are resolved relative to the user's project paths. As a workaround, you can **use relative paths** to import them. We are working on a better solution for named layer aliases. When importing using global aliases (such as `~/` and `@/`) in a layer components and composables, note that these aliases are resolved relative to the user's project paths. As a workaround, you can **use relative paths** to import them, or use named layer aliases.
Also when using relative paths in `nuxt.config` file of a layer, (with exception of nested `extends`) they are resolved relative to user's project instead of the layer. As a workaround, use full resolved paths in `nuxt.config`: Also when using relative paths in `nuxt.config` file of a layer, (with exception of nested `extends`) they are resolved relative to user's project instead of the layer. As a workaround, use full resolved paths in `nuxt.config`:

View File

@ -7,7 +7,7 @@ import { loadConfig } from 'c12'
import type { NuxtConfig, NuxtOptions } from '@nuxt/schema' import type { NuxtConfig, NuxtOptions } from '@nuxt/schema'
import { globby } from 'globby' import { globby } from 'globby'
import defu from 'defu' import defu from 'defu'
import { join } from 'pathe' import { basename, join, relative } from 'pathe'
import { isWindows } from 'std-env' import { isWindows } from 'std-env'
import { tryResolveModule } from '../internal/esm' import { tryResolveModule } from '../internal/esm'
@ -18,14 +18,11 @@ export interface LoadNuxtConfigOptions extends Omit<LoadConfigOptions<NuxtConfig
export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<NuxtOptions> { export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<NuxtOptions> {
// Automatically detect and import layers from `~~/layers/` directory // Automatically detect and import layers from `~~/layers/` directory
opts.overrides = defu(opts.overrides, { const localLayers = await globby('layers/*', { onlyDirectories: true, cwd: opts.cwd || process.cwd() })
_extends: await globby('layers/*', { opts.overrides = defu(opts.overrides, { _extends: localLayers });
onlyDirectories: true,
cwd: opts.cwd || process.cwd(),
}),
});
(globalThis as any).defineNuxtConfig = (c: any) => c (globalThis as any).defineNuxtConfig = (c: any) => c
const result = await loadConfig<NuxtConfig>({ const { configFile, layers = [], cwd, config: nuxtConfig, meta } = await loadConfig<NuxtConfig>({
name: 'nuxt', name: 'nuxt',
configFile: 'nuxt.config', configFile: 'nuxt.config',
rcFile: '.nuxtrc', rcFile: '.nuxtrc',
@ -35,13 +32,17 @@ export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<Nuxt
...opts, ...opts,
}) })
delete (globalThis as any).defineNuxtConfig delete (globalThis as any).defineNuxtConfig
const { configFile, layers = [], cwd } = result
const nuxtConfig = result.config!
// Fill config // Fill config
nuxtConfig.rootDir = nuxtConfig.rootDir || cwd nuxtConfig.rootDir = nuxtConfig.rootDir || cwd
nuxtConfig._nuxtConfigFile = configFile nuxtConfig._nuxtConfigFile = configFile
nuxtConfig._nuxtConfigFiles = [configFile] nuxtConfig._nuxtConfigFiles = [configFile]
nuxtConfig.alias ||= {}
if (meta?.name) {
const alias = `#layers/${meta.name}`
nuxtConfig.alias[alias] ||= nuxtConfig.rootDir
}
const defaultBuildDir = join(nuxtConfig.rootDir!, '.nuxt') const defaultBuildDir = join(nuxtConfig.rootDir!, '.nuxt')
if (!opts.overrides?._prepare && !nuxtConfig.dev && !nuxtConfig.buildDir && existsSync(defaultBuildDir)) { if (!opts.overrides?._prepare && !nuxtConfig.dev && !nuxtConfig.buildDir && existsSync(defaultBuildDir)) {
@ -74,6 +75,18 @@ export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<Nuxt
// Filter layers // Filter layers
if (!layer.configFile || layer.configFile.endsWith('.nuxtrc')) { continue } if (!layer.configFile || layer.configFile.endsWith('.nuxtrc')) { continue }
// Add layer name for local layers
if (layer.cwd && cwd && localLayers.includes(relative(cwd, layer.cwd))) {
layer.meta ||= {}
layer.meta.name ||= basename(layer.cwd)
}
// Add layer alias
if (layer.meta?.name) {
const alias = `#layers/${layer.meta.name}`
nuxtConfig.alias[alias] ||= layer.config.rootDir || layer.cwd
}
_layers.push(layer) _layers.push(layer)
} }

View File

@ -0,0 +1 @@
export const foo = 'bar'

View File

@ -0,0 +1 @@
export default defineNuxtConfig({})

View File

@ -0,0 +1,5 @@
export default defineNuxtConfig({
$meta: {
name: 'layer-fixture',
},
})

View File

@ -0,0 +1,28 @@
import { fileURLToPath } from 'node:url'
import { describe, expect, it } from 'vitest'
import { loadNuxtConfig } from '@nuxt/kit'
describe('loadNuxtConfig', () => {
it('should add named aliases for local layers', async () => {
const cwd = fileURLToPath(new URL('./layer-fixture', import.meta.url))
const config = await loadNuxtConfig({ cwd })
for (const alias in config.alias) {
config.alias[alias] = config.alias[alias]!.replace(cwd, '<rootDir>')
}
expect(config.alias).toMatchInlineSnapshot(`
{
"#build": "<rootDir>/.nuxt",
"#internal/nuxt/paths": "<rootDir>/.nuxt/paths.mjs",
"#layers/layer-fixture": "<rootDir>",
"#layers/test": "<rootDir>/layers/test",
"#shared": "<rootDir>/shared",
"@": "<rootDir>",
"@@": "<rootDir>",
"assets": "<rootDir>/assets",
"public": "<rootDir>/public",
"~": "<rootDir>",
"~~": "<rootDir>",
}
`)
})
})