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

Commit

Permalink
feat(nuxt): support ~/~~/@/@@ aliases within layers (#19986)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielroe committed Apr 3, 2023
1 parent ee8d3f6 commit f40b3e2
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 2 deletions.
20 changes: 18 additions & 2 deletions packages/nuxt/src/core/nuxt.ts
Expand Up @@ -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'
Expand Down Expand Up @@ -80,21 +81,36 @@ 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 }))
addWebpackPlugin(UnctxTransformPlugin(nuxt).webpack({ sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))

// Add composable tree-shaking optimisations
const serverTreeShakeOptions : TreeShakeComposablesPluginOptions = {
const serverTreeShakeOptions: TreeShakeComposablesPluginOptions = {
sourcemap: nuxt.options.sourcemap.server,
composables: nuxt.options.optimization.treeShake.composables.server
}
if (Object.keys(serverTreeShakeOptions.composables).length) {
addVitePlugin(TreeShakeComposablesPlugin.vite(serverTreeShakeOptions), { client: false })
addWebpackPlugin(TreeShakeComposablesPlugin.webpack(serverTreeShakeOptions), { client: false })
}
const clientTreeShakeOptions : TreeShakeComposablesPluginOptions = {
const clientTreeShakeOptions: TreeShakeComposablesPluginOptions = {
sourcemap: nuxt.options.sourcemap.client,
composables: nuxt.options.optimization.treeShake.composables.client
}
Expand Down
68 changes: 68 additions & 0 deletions packages/nuxt/src/core/plugins/layer-aliasing.ts
@@ -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
}
}
}
}
})
3 changes: 3 additions & 0 deletions packages/schema/src/config/experimental.ts
Expand Up @@ -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,
}
})
4 changes: 4 additions & 0 deletions test/basic.test.ts
Expand Up @@ -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')
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/basic/extends/node_modules/foo/alias/test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions test/fixtures/basic/extends/node_modules/foo/pages/foo.vue

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f40b3e2

Please sign in to comment.