feat(nuxt): support ~/~~/@/@@ aliases within layers (#19986)

This commit is contained in:
Daniel Roe 2023-04-03 14:18:24 +01:00 committed by GitHub
parent ee8d3f6ea6
commit f40b3e2492
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 96 additions and 2 deletions

View File

@ -20,6 +20,7 @@ import { UnctxTransformPlugin } from './plugins/unctx'
import type { TreeShakeComposablesPluginOptions } from './plugins/tree-shake'
import { TreeShakeComposablesPlugin } from './plugins/tree-shake'
import { DevOnlyPlugin } from './plugins/dev-only'
import { LayerAliasingPlugin } from './plugins/layer-aliasing'
import { addModuleTranspiles } from './modules'
import { initNitro } from './nitro'
import schemaModule from './schema'
@ -80,6 +81,21 @@ async function initNuxt (nuxt: Nuxt) {
addVitePlugin(ImportProtectionPlugin.vite(config))
addWebpackPlugin(ImportProtectionPlugin.webpack(config))
if (nuxt.options.experimental.localLayerAliases) {
// Add layer aliasing support for ~, ~~, @ and @@ aliases
addVitePlugin(LayerAliasingPlugin.vite({
sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client,
// skip top-level layer (user's project) as the aliases will already be correctly resolved
layers: nuxt.options._layers.slice(1)
}))
addWebpackPlugin(LayerAliasingPlugin.webpack({
sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client,
// skip top-level layer (user's project) as the aliases will already be correctly resolved
layers: nuxt.options._layers.slice(1),
transform: true
}))
}
nuxt.hook('modules:done', () => {
// Add unctx transform
addVitePlugin(UnctxTransformPlugin(nuxt).vite({ sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))

View File

@ -0,0 +1,68 @@
import { createUnplugin } from 'unplugin'
import type { NuxtConfigLayer } from 'nuxt/schema'
import { resolveAlias } from '@nuxt/kit'
import { normalize } from 'pathe'
import MagicString from 'magic-string'
interface LayerAliasingOptions {
sourcemap?: boolean
transform?: boolean
layers: NuxtConfigLayer[]
}
const ALIAS_RE = /(?<=['"])[~@]{1,2}(?=\/)/g
export const LayerAliasingPlugin = createUnplugin((options: LayerAliasingOptions) => {
const aliases = Object.fromEntries(options.layers.map(l => [l.config.srcDir || l.cwd, {
'~': l.config?.alias?.['~'] || l.config.srcDir || l.cwd,
'@': l.config?.alias?.['@'] || l.config.srcDir || l.cwd,
'~~': l.config?.alias?.['~~'] || l.config.rootDir || l.cwd,
'@@': l.config?.alias?.['@@'] || l.config.rootDir || l.cwd
}]))
const layers = Object.keys(aliases).sort((a, b) => b.length - a.length)
return {
name: 'nuxt:layer-aliasing',
enforce: 'pre',
vite: {
resolveId: {
order: 'pre',
async handler (id, importer) {
if (!importer) { return }
const layer = layers.find(l => importer.startsWith(l))
if (!layer) { return }
const resolvedId = resolveAlias(id, aliases[layer])
if (resolvedId !== id) {
return await this.resolve(resolvedId, importer, { skipSelf: true })
}
}
}
},
// webpack-only transform
transformInclude: (id) => {
if (!options.transform) { return false }
const _id = normalize(id)
return layers.some(dir => _id.startsWith(dir))
},
transform (code, id) {
if (!options.transform) { return }
const _id = normalize(id)
const layer = layers.find(l => _id.startsWith(l))
if (!layer || !code.match(ALIAS_RE)) { return }
const s = new MagicString(code)
s.replace(ALIAS_RE, r => aliases[layer][r as '~'] || r)
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap ? s.generateMap({ source: id, includeContent: true }) : undefined
}
}
}
}
})

View File

@ -162,5 +162,8 @@ export default defineUntypedSchema({
/** Allow disabling Nuxt SSR responses by setting the `x-nuxt-no-ssr` header. */
respectNoSSRHeader: false,
/** Resolve `~`, `~~`, `@` and `@@` aliases located within layers with respect to their layer source and root directories. */
localLayerAliases: true,
}
})

View File

@ -707,6 +707,10 @@ describe('extends support', () => {
})
describe('middlewares', () => {
it('works with layer aliases', async () => {
const html = await $fetch('/foo')
expect(html).toContain('from layer alias')
})
it('extends foo/middleware/foo', async () => {
const html = await $fetch('/foo')
expect(html).toContain('Middleware | foo: Injected by extended middleware from foo')

View File

@ -0,0 +1 @@
export const test = 'from layer alias'

View File

@ -1,4 +1,5 @@
<script setup>
import { test } from '~/alias/test'
definePageMeta({
middleware: 'foo'
})
@ -8,6 +9,7 @@ const foo = useExtendsFoo()
<template>
<div>
<div>{{ test }}</div>
<div>Extended page from foo</div>
<div>Middleware | foo: {{ $route.meta.foo }}</div>
<div>Composable | useExtendsFoo: {{ foo }}</div>