mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 13:45:18 +00:00
feat(nuxt3): middleware type generation improvements (#2945)
This commit is contained in:
parent
6e06d2b4c9
commit
965f2abaee
@ -33,6 +33,7 @@
|
|||||||
"cookie-es": "^0.5.0",
|
"cookie-es": "^0.5.0",
|
||||||
"defu": "^5.0.1",
|
"defu": "^5.0.1",
|
||||||
"destr": "^1.1.0",
|
"destr": "^1.1.0",
|
||||||
|
"escape-string-regexp": "^5.0.0",
|
||||||
"globby": "^13.1.0",
|
"globby": "^13.1.0",
|
||||||
"h3": "^0.3.9",
|
"h3": "^0.3.9",
|
||||||
"hash-sum": "^2.0.0",
|
"hash-sum": "^2.0.0",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import type { AutoImport } from '@nuxt/schema'
|
import type { AutoImport } from '@nuxt/schema'
|
||||||
|
import escapeRE from 'escape-string-regexp'
|
||||||
|
|
||||||
export interface AutoImportContext {
|
export interface AutoImportContext {
|
||||||
autoImports: AutoImport[]
|
autoImports: AutoImport[]
|
||||||
@ -36,7 +37,7 @@ export function updateAutoImportContext (ctx: AutoImportContext) {
|
|||||||
ctx.autoImports = ctx.autoImports.filter(i => i.disabled !== true)
|
ctx.autoImports = ctx.autoImports.filter(i => i.disabled !== true)
|
||||||
|
|
||||||
// Create regex
|
// Create regex
|
||||||
ctx.matchRE = new RegExp(`\\b(${ctx.autoImports.map(i => i.as).join('|')})\\b`, 'g')
|
ctx.matchRE = new RegExp(`\\b(${ctx.autoImports.map(i => escapeRE(i.as)).join('|')})\\b`, 'g')
|
||||||
|
|
||||||
// Create map
|
// Create map
|
||||||
ctx.map.clear()
|
ctx.map.clear()
|
||||||
|
@ -3,6 +3,7 @@ import { createUnplugin } from 'unplugin'
|
|||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
import { isAbsolute, relative, resolve } from 'pathe'
|
import { isAbsolute, relative, resolve } from 'pathe'
|
||||||
import type { Nuxt } from '@nuxt/schema'
|
import type { Nuxt } from '@nuxt/schema'
|
||||||
|
import escapeRE from 'escape-string-regexp'
|
||||||
|
|
||||||
const _require = createRequire(import.meta.url)
|
const _require = createRequire(import.meta.url)
|
||||||
|
|
||||||
@ -11,8 +12,6 @@ interface ImportProtectionOptions {
|
|||||||
patterns: [importPattern: string | RegExp, warning?: string][]
|
patterns: [importPattern: string | RegExp, warning?: string][]
|
||||||
}
|
}
|
||||||
|
|
||||||
const escapeRE = (str: string) => str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
|
|
||||||
|
|
||||||
export const vueAppPatterns = (nuxt: Nuxt) => [
|
export const vueAppPatterns = (nuxt: Nuxt) => [
|
||||||
[/^(nuxt3|nuxt)/, '`nuxt3`/`nuxt` cannot be imported directly. Instead, import runtime Nuxt composables from `#app` or `#imports`.'],
|
[/^(nuxt3|nuxt)/, '`nuxt3`/`nuxt` cannot be imported directly. Instead, import runtime Nuxt composables from `#app` or `#imports`.'],
|
||||||
[/nuxt\.config/, 'Importing directly from a `nuxt.config` file is not allowed. Instead, use runtime config or a module.'],
|
[/nuxt\.config/, 'Importing directly from a `nuxt.config` file is not allowed. Instead, use runtime config or a module.'],
|
||||||
|
@ -2,6 +2,7 @@ import { templateUtils } from '@nuxt/kit'
|
|||||||
import type { Nuxt, NuxtApp } from '@nuxt/schema'
|
import type { Nuxt, NuxtApp } from '@nuxt/schema'
|
||||||
|
|
||||||
import { relative } from 'pathe'
|
import { relative } from 'pathe'
|
||||||
|
import escapeRE from 'escape-string-regexp'
|
||||||
|
|
||||||
type TemplateContext = {
|
type TemplateContext = {
|
||||||
nuxt: Nuxt;
|
nuxt: Nuxt;
|
||||||
@ -94,7 +95,7 @@ export const pluginsDeclaration = {
|
|||||||
filename: 'plugins.d.ts',
|
filename: 'plugins.d.ts',
|
||||||
write: true,
|
write: true,
|
||||||
getContents: (ctx: TemplateContext) => {
|
getContents: (ctx: TemplateContext) => {
|
||||||
const EXTENSION_RE = new RegExp(`(?<=\\w)(${ctx.nuxt.options.extensions.map(e => `\\${e}`).join('|')})$`, 'g')
|
const EXTENSION_RE = new RegExp(`(?<=\\w)(${ctx.nuxt.options.extensions.map(e => `\\${escapeRE(e)}`).join('|')})$`, 'g')
|
||||||
const tsImports = ctx.app.plugins.map(p => relative(ctx.nuxt.options.buildDir, p.src).replace(EXTENSION_RE, ''))
|
const tsImports = ctx.app.plugins.map(p => relative(ctx.nuxt.options.buildDir, p.src).replace(EXTENSION_RE, ''))
|
||||||
|
|
||||||
return `// Generated by Nuxt3'
|
return `// Generated by Nuxt3'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { existsSync } from 'fs'
|
import { existsSync } from 'fs'
|
||||||
import { defineNuxtModule, addTemplate, addPlugin, templateUtils, addVitePlugin, addWebpackPlugin } from '@nuxt/kit'
|
import { defineNuxtModule, addTemplate, addPlugin, templateUtils, addVitePlugin, addWebpackPlugin } from '@nuxt/kit'
|
||||||
import { resolve } from 'pathe'
|
import { resolve } from 'pathe'
|
||||||
|
import escapeRE from 'escape-string-regexp'
|
||||||
import { distDir } from '../dirs'
|
import { distDir } from '../dirs'
|
||||||
import { resolveLayouts, resolvePagesRoutes, normalizeRoutes, resolveMiddleware, getImportName } from './utils'
|
import { resolveLayouts, resolvePagesRoutes, normalizeRoutes, resolveMiddleware, getImportName } from './utils'
|
||||||
import { TransformMacroPlugin, TransformMacroPluginOptions } from './macros'
|
import { TransformMacroPlugin, TransformMacroPluginOptions } from './macros'
|
||||||
@ -25,7 +26,13 @@ export default defineNuxtModule({
|
|||||||
|
|
||||||
// Regenerate templates when adding or removing pages
|
// Regenerate templates when adding or removing pages
|
||||||
nuxt.hook('builder:watch', async (event, path) => {
|
nuxt.hook('builder:watch', async (event, path) => {
|
||||||
const pathPattern = new RegExp(`^(${nuxt.options.dir.pages}|${nuxt.options.dir.layouts})/`)
|
const dirs = [
|
||||||
|
nuxt.options.dir.pages,
|
||||||
|
nuxt.options.dir.layouts,
|
||||||
|
nuxt.options.dir.middleware
|
||||||
|
].filter(Boolean)
|
||||||
|
|
||||||
|
const pathPattern = new RegExp(`^(${dirs.map(escapeRE).join('|')})/`)
|
||||||
if (event !== 'change' && path.match(pathPattern)) {
|
if (event !== 'change' && path.match(pathPattern)) {
|
||||||
await nuxt.callHook('builder:generateApp')
|
await nuxt.callHook('builder:generateApp')
|
||||||
}
|
}
|
||||||
@ -83,13 +90,13 @@ export default defineNuxtModule({
|
|||||||
filename: 'middleware.mjs',
|
filename: 'middleware.mjs',
|
||||||
async getContents () {
|
async getContents () {
|
||||||
const middleware = await resolveMiddleware()
|
const middleware = await resolveMiddleware()
|
||||||
await nuxt.callHook('pages:middleware:extend', middleware)
|
|
||||||
const middlewareObject = Object.fromEntries(middleware.map(mw => [mw.name, `{() => import('${mw.path}')}`]))
|
|
||||||
const globalMiddleware = middleware.filter(mw => mw.global)
|
const globalMiddleware = middleware.filter(mw => mw.global)
|
||||||
|
const namedMiddleware = middleware.filter(mw => !mw.global)
|
||||||
|
const namedMiddlewareObject = Object.fromEntries(namedMiddleware.map(mw => [mw.name, `{() => import('${mw.path}')}`]))
|
||||||
return [
|
return [
|
||||||
...globalMiddleware.map(mw => `import ${getImportName(mw.name)} from '${mw.path}'`),
|
...globalMiddleware.map(mw => `import ${getImportName(mw.name)} from '${mw.path}'`),
|
||||||
`export const globalMiddleware = [${globalMiddleware.map(mw => getImportName(mw.name)).join(', ')}]`,
|
`export const globalMiddleware = [${globalMiddleware.map(mw => getImportName(mw.name)).join(', ')}]`,
|
||||||
`export const namedMiddleware = ${templateUtils.serialize(middlewareObject)}`
|
`export const namedMiddleware = ${templateUtils.serialize(namedMiddlewareObject)}`
|
||||||
].join('\n')
|
].join('\n')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -100,9 +107,10 @@ export default defineNuxtModule({
|
|||||||
getContents: async () => {
|
getContents: async () => {
|
||||||
const composablesFile = resolve(runtimeDir, 'composables')
|
const composablesFile = resolve(runtimeDir, 'composables')
|
||||||
const middleware = await resolveMiddleware()
|
const middleware = await resolveMiddleware()
|
||||||
|
const namedMiddleware = middleware.filter(mw => !mw.global)
|
||||||
return [
|
return [
|
||||||
'import type { NavigationGuard } from \'vue-router\'',
|
'import type { NavigationGuard } from \'vue-router\'',
|
||||||
`export type MiddlewareKey = ${middleware.map(mw => `"${mw.name}"`).join(' | ') || 'string'}`,
|
`export type MiddlewareKey = ${namedMiddleware.map(mw => `"${mw.name}"`).join(' | ') || 'string'}`,
|
||||||
`declare module '${composablesFile}' {`,
|
`declare module '${composablesFile}' {`,
|
||||||
' interface PageMeta {',
|
' interface PageMeta {',
|
||||||
' middleware?: MiddlewareKey | NavigationGuard | Array<MiddlewareKey | NavigationGuard>',
|
' middleware?: MiddlewareKey | NavigationGuard | Array<MiddlewareKey | NavigationGuard>',
|
||||||
@ -130,11 +138,6 @@ export default defineNuxtModule({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
nuxt.hook('prepare:types', ({ references }) => {
|
|
||||||
references.push({ path: resolve(nuxt.options.buildDir, 'middleware.d.ts') })
|
|
||||||
references.push({ path: resolve(nuxt.options.buildDir, 'layouts.d.ts') })
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add layouts template
|
// Add layouts template
|
||||||
addTemplate({
|
addTemplate({
|
||||||
filename: 'layouts.mjs',
|
filename: 'layouts.mjs',
|
||||||
@ -149,5 +152,11 @@ export default defineNuxtModule({
|
|||||||
].join('\n')
|
].join('\n')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Add declarations for middleware and layout keys
|
||||||
|
nuxt.hook('prepare:types', ({ references }) => {
|
||||||
|
references.push({ path: resolve(nuxt.options.buildDir, 'middleware.d.ts') })
|
||||||
|
references.push({ path: resolve(nuxt.options.buildDir, 'layouts.d.ts') })
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -3,6 +3,7 @@ import { encodePath } from 'ufo'
|
|||||||
import type { Nuxt, NuxtMiddleware, NuxtPage } from '@nuxt/schema'
|
import type { Nuxt, NuxtMiddleware, NuxtPage } from '@nuxt/schema'
|
||||||
import { resolveFiles, useNuxt } from '@nuxt/kit'
|
import { resolveFiles, useNuxt } from '@nuxt/kit'
|
||||||
import { kebabCase, pascalCase } from 'scule'
|
import { kebabCase, pascalCase } from 'scule'
|
||||||
|
import escapeRE from 'escape-string-regexp'
|
||||||
|
|
||||||
enum SegmentParserState {
|
enum SegmentParserState {
|
||||||
initial,
|
initial,
|
||||||
@ -37,7 +38,7 @@ export function generateRoutesFromFiles (files: string[], pagesDir: string): Nux
|
|||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const segments = relative(pagesDir, file)
|
const segments = relative(pagesDir, file)
|
||||||
.replace(new RegExp(`${extname(file)}$`), '')
|
.replace(new RegExp(`${escapeRE(extname(file))}$`), '')
|
||||||
.split('/')
|
.split('/')
|
||||||
|
|
||||||
const route: NuxtPage = {
|
const route: NuxtPage = {
|
||||||
@ -219,7 +220,9 @@ export async function resolveLayouts (nuxt: Nuxt) {
|
|||||||
const layoutDir = resolve(nuxt.options.srcDir, nuxt.options.dir.layouts)
|
const layoutDir = resolve(nuxt.options.srcDir, nuxt.options.dir.layouts)
|
||||||
const files = await resolveFiles(layoutDir, `*{${nuxt.options.extensions.join(',')}}`)
|
const files = await resolveFiles(layoutDir, `*{${nuxt.options.extensions.join(',')}}`)
|
||||||
|
|
||||||
return files.map(file => ({ name: getNameFromPath(file), file }))
|
const layouts = files.map(file => ({ name: getNameFromPath(file), file }))
|
||||||
|
await nuxt.callHook('pages:layouts:extend', layouts)
|
||||||
|
return layouts
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeRoutes (routes: NuxtPage[], metaImports: Set<string> = new Set()): { imports: Set<string>, routes: NuxtPage[]} {
|
export function normalizeRoutes (routes: NuxtPage[], metaImports: Set<string> = new Set()): { imports: Set<string>, routes: NuxtPage[]} {
|
||||||
@ -243,7 +246,9 @@ export async function resolveMiddleware (): Promise<NuxtMiddleware[]> {
|
|||||||
const nuxt = useNuxt()
|
const nuxt = useNuxt()
|
||||||
const middlewareDir = resolve(nuxt.options.srcDir, nuxt.options.dir.middleware)
|
const middlewareDir = resolve(nuxt.options.srcDir, nuxt.options.dir.middleware)
|
||||||
const files = await resolveFiles(middlewareDir, `*{${nuxt.options.extensions.join(',')}}`)
|
const files = await resolveFiles(middlewareDir, `*{${nuxt.options.extensions.join(',')}}`)
|
||||||
return files.map(path => ({ name: getNameFromPath(path), path, global: hasSuffix(path, '.global') }))
|
const middleware = files.map(path => ({ name: getNameFromPath(path), path, global: hasSuffix(path, '.global') }))
|
||||||
|
await nuxt.callHook('pages:middleware:extend', middleware)
|
||||||
|
return middleware
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNameFromPath (path: string) {
|
function getNameFromPath (path: string) {
|
||||||
|
@ -433,6 +433,7 @@ export default {
|
|||||||
layouts: 'layouts',
|
layouts: 'layouts',
|
||||||
/**
|
/**
|
||||||
* The middleware directory, each file of which will be auto-registered as a Nuxt middleware.
|
* The middleware directory, each file of which will be auto-registered as a Nuxt middleware.
|
||||||
|
* @version 3
|
||||||
* @version 2
|
* @version 2
|
||||||
*/
|
*/
|
||||||
middleware: 'middleware',
|
middleware: 'middleware',
|
||||||
|
@ -47,6 +47,11 @@ export type NuxtMiddleware = {
|
|||||||
global?: boolean
|
global?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NuxtLayout = {
|
||||||
|
name: string
|
||||||
|
file: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface NuxtHooks {
|
export interface NuxtHooks {
|
||||||
// Kit
|
// Kit
|
||||||
'kit:compatibility': (compatibility: NuxtCompatibility, issues: NuxtCompatibilityIssues) => HookResult
|
'kit:compatibility': (compatibility: NuxtCompatibility, issues: NuxtCompatibilityIssues) => HookResult
|
||||||
@ -58,6 +63,7 @@ export interface NuxtHooks {
|
|||||||
'builder:generateApp': () => HookResult
|
'builder:generateApp': () => HookResult
|
||||||
'pages:extend': (pages: NuxtPage[]) => HookResult
|
'pages:extend': (pages: NuxtPage[]) => HookResult
|
||||||
'pages:middleware:extend': (middleware: NuxtMiddleware[]) => HookResult
|
'pages:middleware:extend': (middleware: NuxtMiddleware[]) => HookResult
|
||||||
|
'pages:layouts:extend': (layouts: NuxtLayout[]) => HookResult
|
||||||
|
|
||||||
// Auto imports
|
// Auto imports
|
||||||
'autoImports:sources': (autoImportSources: AutoImportSource[]) => HookResult
|
'autoImports:sources': (autoImportSources: AutoImportSource[]) => HookResult
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
"consola": "^2.15.3",
|
"consola": "^2.15.3",
|
||||||
"defu": "^5.0.1",
|
"defu": "^5.0.1",
|
||||||
"esbuild": "^0.14.14",
|
"esbuild": "^0.14.14",
|
||||||
|
"escape-string-regexp": "^5.0.0",
|
||||||
"externality": "^0.1.6",
|
"externality": "^0.1.6",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"magic-string": "^0.25.7",
|
"magic-string": "^0.25.7",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { createUnplugin } from 'unplugin'
|
import { createUnplugin } from 'unplugin'
|
||||||
|
import escapeRE from 'escape-string-regexp'
|
||||||
import type { Plugin } from 'vite'
|
import type { Plugin } from 'vite'
|
||||||
|
|
||||||
interface DynamicBasePluginOptions {
|
interface DynamicBasePluginOptions {
|
||||||
@ -7,8 +8,6 @@ interface DynamicBasePluginOptions {
|
|||||||
globalPublicPath?: string
|
globalPublicPath?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const escapeRE = (str: string) => str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
|
|
||||||
|
|
||||||
export const RelativeAssetPlugin = function (): Plugin {
|
export const RelativeAssetPlugin = function (): Plugin {
|
||||||
return {
|
return {
|
||||||
name: 'nuxt:vite-relative-asset',
|
name: 'nuxt:vite-relative-asset',
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
"css-minimizer-webpack-plugin": "^3.4.1",
|
||||||
"cssnano": "^5.0.16",
|
"cssnano": "^5.0.16",
|
||||||
"esbuild-loader": "^2.18.0",
|
"esbuild-loader": "^2.18.0",
|
||||||
|
"escape-string-regexp": "^5.0.0",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"glob": "^7.2.0",
|
"glob": "^7.2.0",
|
||||||
|
@ -4,7 +4,7 @@ import WebpackBar from 'webpackbar'
|
|||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
import webpack from 'webpack'
|
import webpack from 'webpack'
|
||||||
import FriendlyErrorsWebpackPlugin from '@nuxt/friendly-errors-webpack-plugin'
|
import FriendlyErrorsWebpackPlugin from '@nuxt/friendly-errors-webpack-plugin'
|
||||||
import { escapeRegExp } from 'lodash-es'
|
import escapeRegExp from 'escape-string-regexp'
|
||||||
import { joinURL } from 'ufo'
|
import { joinURL } from 'ufo'
|
||||||
import WarningIgnorePlugin from '../plugins/warning-ignore'
|
import WarningIgnorePlugin from '../plugins/warning-ignore'
|
||||||
import { WebpackConfigContext, applyPresets, fileName } from '../utils/config'
|
import { WebpackConfigContext, applyPresets, fileName } from '../utils/config'
|
||||||
|
10
yarn.lock
10
yarn.lock
@ -3182,6 +3182,7 @@ __metadata:
|
|||||||
consola: ^2.15.3
|
consola: ^2.15.3
|
||||||
defu: ^5.0.1
|
defu: ^5.0.1
|
||||||
esbuild: ^0.14.14
|
esbuild: ^0.14.14
|
||||||
|
escape-string-regexp: ^5.0.0
|
||||||
externality: ^0.1.6
|
externality: ^0.1.6
|
||||||
fs-extra: ^10.0.0
|
fs-extra: ^10.0.0
|
||||||
magic-string: ^0.25.7
|
magic-string: ^0.25.7
|
||||||
@ -3298,6 +3299,7 @@ __metadata:
|
|||||||
css-minimizer-webpack-plugin: ^3.4.1
|
css-minimizer-webpack-plugin: ^3.4.1
|
||||||
cssnano: ^5.0.16
|
cssnano: ^5.0.16
|
||||||
esbuild-loader: ^2.18.0
|
esbuild-loader: ^2.18.0
|
||||||
|
escape-string-regexp: ^5.0.0
|
||||||
file-loader: ^6.2.0
|
file-loader: ^6.2.0
|
||||||
fs-extra: ^10.0.0
|
fs-extra: ^10.0.0
|
||||||
glob: ^7.2.0
|
glob: ^7.2.0
|
||||||
@ -9556,6 +9558,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"escape-string-regexp@npm:^5.0.0":
|
||||||
|
version: 5.0.0
|
||||||
|
resolution: "escape-string-regexp@npm:5.0.0"
|
||||||
|
checksum: 20daabe197f3cb198ec28546deebcf24b3dbb1a5a269184381b3116d12f0532e06007f4bc8da25669d6a7f8efb68db0758df4cd981f57bc5b57f521a3e12c59e
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"eslint-config-standard@npm:^16.0.3":
|
"eslint-config-standard@npm:^16.0.3":
|
||||||
version: 16.0.3
|
version: 16.0.3
|
||||||
resolution: "eslint-config-standard@npm:16.0.3"
|
resolution: "eslint-config-standard@npm:16.0.3"
|
||||||
@ -14841,6 +14850,7 @@ __metadata:
|
|||||||
cookie-es: ^0.5.0
|
cookie-es: ^0.5.0
|
||||||
defu: ^5.0.1
|
defu: ^5.0.1
|
||||||
destr: ^1.1.0
|
destr: ^1.1.0
|
||||||
|
escape-string-regexp: ^5.0.0
|
||||||
globby: ^13.1.0
|
globby: ^13.1.0
|
||||||
h3: ^0.3.9
|
h3: ^0.3.9
|
||||||
hash-sum: ^2.0.0
|
hash-sum: ^2.0.0
|
||||||
|
Loading…
Reference in New Issue
Block a user