2022-10-27 15:50:24 +00:00
import { existsSync , readdirSync } from 'node:fs'
2023-05-09 17:08:07 +00:00
import { mkdir , readFile } from 'node:fs/promises'
2024-01-29 12:23:51 +00:00
import { addBuildPlugin , addComponent , addPlugin , addTemplate , addTypeTemplate , addVitePlugin , addWebpackPlugin , defineNuxtModule , findPath , logger , updateTemplates , useNitro } from '@nuxt/kit'
2023-05-09 17:08:07 +00:00
import { dirname , join , relative , resolve } from 'pathe'
2023-04-07 16:02:47 +00:00
import { genImport , genObjectFromRawEntries , genString } from 'knitwork'
2022-07-07 15:07:37 +00:00
import { joinURL } from 'ufo'
2023-08-14 17:07:17 +00:00
import type { Nuxt , NuxtApp , NuxtPage } from 'nuxt/schema'
2023-05-09 17:08:07 +00:00
import { createRoutesContext } from 'unplugin-vue-router'
import { resolveOptions } from 'unplugin-vue-router/options'
import type { EditableTreeNode , Options as TypedRouterOptions } from 'unplugin-vue-router'
2023-03-11 21:16:01 +00:00
2023-08-23 20:38:17 +00:00
import type { NitroRouteConfig } from 'nitropack'
import { defu } from 'defu'
2021-08-11 21:26:47 +00:00
import { distDir } from '../dirs'
2023-04-07 16:02:47 +00:00
import { normalizeRoutes , resolvePagesRoutes } from './utils'
2023-08-23 20:38:17 +00:00
import { extractRouteRules , getMappedPages } from './route-rules'
import type { PageMetaPluginOptions } from './plugins/page-meta'
import { PageMetaPlugin } from './plugins/page-meta'
import { RouteInjectionPlugin } from './plugins/route-injection'
2021-05-20 11:42:41 +00:00
2023-05-22 20:25:42 +00:00
const OPTIONAL_PARAM_RE = /^\/?:.*(\?|\(\.\*\)\*)$/
2021-05-20 11:42:41 +00:00
export default defineNuxtModule ( {
2022-01-05 18:09:53 +00:00
meta : {
2022-04-13 17:18:51 +00:00
name : 'pages'
2022-01-05 18:09:53 +00:00
} ,
2023-05-09 17:08:07 +00:00
async setup ( _options , nuxt ) {
const useExperimentalTypedPages = nuxt . options . experimental . typedPages
2024-01-18 16:06:00 +00:00
const runtimeDir = resolve ( distDir , 'pages/runtime' )
2022-03-22 18:12:54 +00:00
const pagesDirs = nuxt . options . _layers . map (
2023-09-12 14:27:28 +00:00
layer = > resolve ( layer . config . srcDir , ( layer . config . rootDir === nuxt . options . rootDir ? nuxt.options : layer.config ) . dir ? . pages || 'pages' )
2022-03-22 18:12:54 +00:00
)
2021-07-28 11:35:24 +00:00
2024-01-18 16:06:00 +00:00
async function resolveRouterOptions ( ) {
const context = {
files : [ ] as Array < { path : string , optional? : boolean } >
}
2024-01-23 17:44:14 +00:00
for ( const layer of nuxt . options . _layers ) {
2024-01-18 16:06:00 +00:00
const path = await findPath ( resolve ( layer . config . srcDir , 'app/router.options' ) )
2024-01-30 13:55:18 +00:00
if ( path ) { context . files . unshift ( { path } ) }
2024-01-23 17:44:14 +00:00
}
2024-01-18 16:06:00 +00:00
2024-01-30 13:55:18 +00:00
// Add default options at beginning
context . files . unshift ( { path : resolve ( runtimeDir , 'router.options' ) , optional : true } )
2024-01-18 16:06:00 +00:00
await nuxt . callHook ( 'pages:routerOptions' , context )
return context . files
}
2022-04-13 17:18:51 +00:00
// Disable module (and use universal router) if pages dir do not exists or user has disabled it
2022-10-27 15:50:24 +00:00
const isNonEmptyDir = ( dir : string ) = > existsSync ( dir ) && readdirSync ( dir ) . length
2023-03-09 11:46:08 +00:00
const userPreference = nuxt . options . pages
2023-05-15 12:47:30 +00:00
const isPagesEnabled = async ( ) = > {
2023-03-09 11:46:08 +00:00
if ( typeof userPreference === 'boolean' ) {
return userPreference
2022-10-27 15:50:24 +00:00
}
2024-01-18 16:06:00 +00:00
const routerOptionsFiles = await resolveRouterOptions ( )
if ( routerOptionsFiles . filter ( p = > ! p . optional ) . length > 0 ) {
2022-10-27 15:50:24 +00:00
return true
}
if ( pagesDirs . some ( dir = > isNonEmptyDir ( dir ) ) ) {
return true
}
2023-05-15 12:47:30 +00:00
const pages = await resolvePagesRoutes ( )
await nuxt . callHook ( 'pages:extend' , pages )
if ( pages . length ) { return true }
2022-10-27 15:50:24 +00:00
return false
}
2023-05-15 12:47:30 +00:00
nuxt . options . pages = await isPagesEnabled ( )
2022-10-27 15:50:24 +00:00
2024-01-29 16:52:03 +00:00
if ( nuxt . options . dev && nuxt . options . pages ) {
// Add plugin to check if pages are enabled without NuxtPage being instantiated
addPlugin ( resolve ( runtimeDir , 'plugins/check-if-page-unused' ) )
}
2023-10-30 16:55:40 +00:00
nuxt . hook ( 'app:templates' , async ( app ) = > {
app . pages = await resolvePagesRoutes ( )
await nuxt . callHook ( 'pages:extend' , app . pages )
2024-03-13 14:39:35 +00:00
if ( ! nuxt . options . ssr && app . pages . some ( p = > p . mode === 'server' ) ) {
logger . warn ( 'Using server pages with `ssr: false` is not supported with auto-detected component islands. Set `experimental.componentIslands` to `true`.' )
}
2023-10-30 16:55:40 +00:00
} )
2023-03-09 11:46:08 +00:00
// Restart Nuxt when pages dir is added or removed
2023-09-12 14:27:28 +00:00
const restartPaths = nuxt . options . _layers . flatMap ( ( layer ) = > {
const pagesDir = ( layer . config . rootDir === nuxt . options . rootDir ? nuxt.options : layer.config ) . dir ? . pages || 'pages'
return [
join ( layer . config . srcDir || layer . cwd , 'app/router.options.ts' ) ,
join ( layer . config . srcDir || layer . cwd , pagesDir )
]
} )
2023-07-26 09:16:01 +00:00
nuxt . hooks . hook ( 'builder:watch' , async ( event , relativePath ) = > {
const path = resolve ( nuxt . options . srcDir , relativePath )
if ( restartPaths . some ( p = > p === path || path . startsWith ( p + '/' ) ) ) {
2023-05-15 12:47:30 +00:00
const newSetting = await isPagesEnabled ( )
2023-03-09 11:46:08 +00:00
if ( nuxt . options . pages !== newSetting ) {
2023-09-19 21:26:15 +00:00
logger . info ( 'Pages' , newSetting ? 'enabled' : 'disabled' )
2023-03-09 11:46:08 +00:00
return nuxt . callHook ( 'restart' )
}
}
} )
2023-05-22 13:48:08 +00:00
// adds support for #vue-router alias (used for types) with and without pages integration
addTemplate ( {
2023-09-28 07:37:14 +00:00
filename : 'vue-router-stub.d.ts' ,
2023-05-22 13:48:08 +00:00
getContents : ( ) = > ` export * from ' ${ useExperimentalTypedPages ? 'vue-router/auto' : 'vue-router' } ' `
} )
2023-09-28 07:37:14 +00:00
nuxt . options . alias [ '#vue-router' ] = join ( nuxt . options . buildDir , 'vue-router-stub' )
2023-05-22 13:48:08 +00:00
2022-10-27 15:50:24 +00:00
if ( ! nuxt . options . pages ) {
2022-02-21 13:03:42 +00:00
addPlugin ( resolve ( distDir , 'app/plugins/router' ) )
2022-10-24 08:36:49 +00:00
addTemplate ( {
filename : 'pages.mjs' ,
2024-01-02 10:00:47 +00:00
getContents : ( ) = > [
'export { useRoute } from \'#app/composables/router\'' ,
'export const START_LOCATION = Symbol(\'router:start-location\')'
] . join ( '\n' )
2022-10-24 08:36:49 +00:00
} )
2022-10-27 15:50:24 +00:00
addComponent ( {
name : 'NuxtPage' ,
2023-03-06 11:33:40 +00:00
priority : 10 , // built-in that we do not expect the user to override
2022-10-27 15:50:24 +00:00
filePath : resolve ( distDir , 'pages/runtime/page-placeholder' )
} )
2021-07-28 11:35:24 +00:00
return
}
2021-05-20 11:42:41 +00:00
2023-05-22 13:48:08 +00:00
addTemplate ( {
2023-09-28 07:37:14 +00:00
filename : 'vue-router-stub.mjs' ,
2023-05-22 13:48:08 +00:00
// TODO: use `vue-router/auto` when we have support for page metadata
getContents : ( ) = > 'export * from \'vue-router\';'
} )
2023-05-09 17:08:07 +00:00
if ( useExperimentalTypedPages ) {
const declarationFile = './types/typed-router.d.ts'
const options : TypedRouterOptions = {
routesFolder : [ ] ,
dts : resolve ( nuxt . options . buildDir , declarationFile ) ,
logs : nuxt.options.debug ,
async beforeWriteFiles ( rootPage ) {
rootPage . children . forEach ( child = > child . delete ( ) )
2023-10-30 16:55:40 +00:00
let pages = nuxt . apps . default ? . pages
if ( ! pages ) {
pages = await resolvePagesRoutes ( )
await nuxt . callHook ( 'pages:extend' , pages )
}
2023-05-09 17:08:07 +00:00
function addPage ( parent : EditableTreeNode , page : NuxtPage ) {
// @ts-expect-error TODO: either fix types upstream or figure out another
// way to add a route without a file, which must be possible
const route = parent . insert ( page . path , page . file )
if ( page . meta ) {
route . addToMeta ( page . meta )
}
if ( page . alias ) {
route . addAlias ( page . alias )
}
if ( page . name ) {
route . name = page . name
}
// TODO: implement redirect support
// if (page.redirect) {}
if ( page . children ) {
page . children . forEach ( child = > addPage ( route , child ) )
}
}
for ( const page of pages ) {
addPage ( rootPage , page )
}
}
}
nuxt . hook ( 'prepare:types' , ( { references } ) = > {
// This file will be generated by unplugin-vue-router
references . push ( { path : declarationFile } )
} )
const context = createRoutesContext ( resolveOptions ( options ) )
const dtsFile = resolve ( nuxt . options . buildDir , declarationFile )
await mkdir ( dirname ( dtsFile ) , { recursive : true } )
await context . scanPages ( false )
2024-02-03 23:16:42 +00:00
if ( nuxt . options . _prepare || ! nuxt . options . dev ) {
2023-05-09 17:08:07 +00:00
// TODO: could we generate this from context instead?
const dts = await readFile ( dtsFile , 'utf-8' )
addTemplate ( {
filename : 'types/typed-router.d.ts' ,
getContents : ( ) = > dts
} )
}
// Regenerate types/typed-router.d.ts when adding or removing pages
2024-03-12 13:28:54 +00:00
nuxt . hook ( 'app:templatesGenerated' , async ( _app , _templates , options ) = > {
2023-05-09 17:08:07 +00:00
if ( ! options ? . filter || options . filter ( { filename : 'routes.mjs' } as any ) ) {
await context . scanPages ( )
}
} )
}
2021-11-02 09:39:42 +00:00
// Add $router types
nuxt . hook ( 'prepare:types' , ( { references } ) = > {
2023-05-09 17:08:07 +00:00
references . push ( { types : useExperimentalTypedPages ? 'vue-router/auto' : 'vue-router' } )
2021-11-02 09:39:42 +00:00
} )
2022-11-10 13:52:04 +00:00
// Add vue-router route guard imports
nuxt . hook ( 'imports:sources' , ( sources ) = > {
2023-10-30 21:05:02 +00:00
const routerImports = sources . find ( s = > s . from === '#app/composables/router' && s . imports . includes ( 'onBeforeRouteLeave' ) )
2022-11-10 13:52:04 +00:00
if ( routerImports ) {
2023-05-09 17:08:07 +00:00
routerImports . from = '#vue-router'
2022-11-10 13:52:04 +00:00
}
} )
2021-07-28 11:35:24 +00:00
// Regenerate templates when adding or removing pages
2023-09-12 14:27:28 +00:00
const updateTemplatePaths = nuxt . options . _layers . flatMap ( ( l ) = > {
const dir = ( l . config . rootDir === nuxt . options . rootDir ? nuxt.options : l.config ) . dir
return [
join ( l . config . srcDir || l . cwd , dir ? . pages || 'pages' ) + '/' ,
join ( l . config . srcDir || l . cwd , dir ? . layouts || 'layouts' ) + '/' ,
join ( l . config . srcDir || l . cwd , dir ? . middleware || 'middleware' ) + '/'
]
} )
2023-07-26 09:16:01 +00:00
2024-01-29 16:44:54 +00:00
function isPage ( file : string , pages = nuxt . apps . default . pages ) : boolean {
if ( ! pages ) { return false }
return pages . some ( page = > page . file === file ) || pages . some ( page = > page . children && isPage ( file , page . children ) )
}
2023-07-26 09:16:01 +00:00
nuxt . hook ( 'builder:watch' , async ( event , relativePath ) = > {
const path = resolve ( nuxt . options . srcDir , relativePath )
2024-01-29 16:44:54 +00:00
const shouldAlwaysRegenerate = nuxt . options . experimental . scanPageMeta && isPage ( path )
if ( event === 'change' && ! shouldAlwaysRegenerate ) { return }
if ( shouldAlwaysRegenerate || updateTemplatePaths . some ( dir = > path . startsWith ( dir ) ) ) {
2022-11-02 10:28:41 +00:00
await updateTemplates ( {
filter : template = > template . filename === 'routes.mjs'
} )
2021-05-20 11:42:41 +00:00
}
} )
nuxt . hook ( 'app:resolve' , ( app ) = > {
2021-10-12 12:51:41 +00:00
// Add default layout for pages
2022-08-12 17:47:58 +00:00
if ( app . mainComponent ! . includes ( '@nuxt/ui-templates' ) ) {
2021-10-12 12:51:41 +00:00
app . mainComponent = resolve ( runtimeDir , 'app.vue' )
2021-05-20 11:42:41 +00:00
}
2022-10-10 10:18:20 +00:00
app . middleware . unshift ( {
name : 'validate' ,
path : resolve ( runtimeDir , 'validate' ) ,
global : true
} )
2021-05-20 11:42:41 +00:00
} )
2023-06-29 09:14:35 +00:00
nuxt . hook ( 'nitro:init' , ( nitro ) = > {
2023-11-29 00:35:46 +00:00
if ( nuxt . options . dev || ! nitro . options . static || nuxt . options . router . options . hashMode ) { return }
2023-06-29 09:14:35 +00:00
// Prerender all non-dynamic page routes when generating app
2022-07-12 10:56:40 +00:00
const prerenderRoutes = new Set < string > ( )
2023-07-12 07:26:52 +00:00
nuxt . hook ( 'pages:extend' , ( pages ) = > {
prerenderRoutes . clear ( )
const processPages = ( pages : NuxtPage [ ] , currentPath = '/' ) = > {
for ( const page of pages ) {
// Add root of optional dynamic paths and catchalls
if ( OPTIONAL_PARAM_RE . test ( page . path ) && ! page . children ? . length ) { prerenderRoutes . add ( currentPath ) }
// Skip dynamic paths
if ( page . path . includes ( ':' ) ) { continue }
const route = joinURL ( currentPath , page . path )
prerenderRoutes . add ( route )
if ( page . children ) { processPages ( page . children , route ) }
2022-07-07 15:07:37 +00:00
}
2023-07-12 07:26:52 +00:00
}
processPages ( pages )
2022-07-07 15:07:37 +00:00
} )
nuxt . hook ( 'nitro:build:before' , ( nitro ) = > {
2024-02-26 16:08:45 +00:00
if ( nitro . options . prerender . routes . length ) {
for ( const route of nitro . options . prerender . routes ) {
// Skip default route value as we only generate it if it is already
// in the detected routes from `~/pages`.
if ( route === '/' ) { continue }
prerenderRoutes . add ( route )
}
nitro . options . prerender . routes = Array . from ( prerenderRoutes )
2022-07-12 10:56:40 +00:00
}
2022-07-07 15:07:37 +00:00
} )
2023-06-29 09:14:35 +00:00
} )
2022-07-07 15:07:37 +00:00
2022-08-23 14:22:11 +00:00
nuxt . hook ( 'imports:extend' , ( imports ) = > {
imports . push (
2022-07-07 17:28:23 +00:00
{ name : 'definePageMeta' , as : 'definePageMeta' , from : resolve ( runtimeDir , 'composables' ) } ,
2023-05-09 17:08:07 +00:00
{ name : 'useLink' , as : 'useLink' , from : '#vue-router' }
2022-07-07 17:28:23 +00:00
)
2023-08-23 20:38:17 +00:00
if ( nuxt . options . experimental . inlineRouteRules ) {
imports . push ( { name : 'defineRouteRules' , as : 'defineRouteRules' , from : resolve ( runtimeDir , 'composables' ) } )
}
2021-12-17 09:15:03 +00:00
} )
2023-08-23 20:38:17 +00:00
if ( nuxt . options . experimental . inlineRouteRules ) {
// Track mappings of absolute files to globs
let pageToGlobMap = { } as { [ absolutePath : string ] : string | null }
nuxt . hook ( 'pages:extend' , ( pages ) = > { pageToGlobMap = getMappedPages ( pages ) } )
// Extracted route rules defined inline in pages
const inlineRules = { } as { [ glob : string ] : NitroRouteConfig }
// Allow telling Nitro to reload route rules
let updateRouteConfig : ( ) = > void | Promise < void >
nuxt . hook ( 'nitro:init' , ( nitro ) = > {
updateRouteConfig = ( ) = > nitro . updateConfig ( { routeRules : defu ( inlineRules , nitro . options . _config . routeRules ) } )
} )
2023-11-09 17:01:13 +00:00
const updatePage = async function updatePage ( path : string ) {
2023-08-23 20:38:17 +00:00
const glob = pageToGlobMap [ path ]
const code = path in nuxt . vfs ? nuxt . vfs [ path ] : await readFile ( path ! , 'utf-8' )
try {
const extractedRule = await extractRouteRules ( code )
if ( extractedRule ) {
if ( ! glob ) {
const relativePath = relative ( nuxt . options . srcDir , path )
2023-09-19 21:26:15 +00:00
logger . error ( ` Could not set inline route rules in \` ~/ ${ relativePath } \` as it could not be mapped to a Nitro route. ` )
2023-08-23 20:38:17 +00:00
return
}
inlineRules [ glob ] = extractedRule
} else if ( glob ) {
delete inlineRules [ glob ]
}
} catch ( e : any ) {
if ( e . toString ( ) . includes ( 'Error parsing route rules' ) ) {
const relativePath = relative ( nuxt . options . srcDir , path )
2023-09-19 21:26:15 +00:00
logger . error ( ` Error parsing route rules within \` ~/ ${ relativePath } \` . They should be JSON-serializable. ` )
2023-08-23 20:38:17 +00:00
} else {
2023-09-19 21:26:15 +00:00
logger . error ( e )
2023-08-23 20:38:17 +00:00
}
}
}
nuxt . hook ( 'builder:watch' , async ( event , relativePath ) = > {
const path = join ( nuxt . options . srcDir , relativePath )
if ( ! ( path in pageToGlobMap ) ) { return }
if ( event === 'unlink' ) {
delete inlineRules [ path ]
delete pageToGlobMap [ path ]
} else {
await updatePage ( path )
}
await updateRouteConfig ? . ( )
} )
nuxt . hooks . hookOnce ( 'pages:extend' , async ( ) = > {
for ( const page in pageToGlobMap ) { await updatePage ( page ) }
await updateRouteConfig ? . ( )
} )
}
2024-01-29 11:07:52 +00:00
if ( nuxt . options . experimental . appManifest ) {
// Add all redirect paths as valid routes to router; we will handle these in a client-side middleware
// when the app manifest is enabled.
2024-03-09 06:48:15 +00:00
nuxt . hook ( 'pages:extend' , ( routes ) = > {
2024-01-29 11:07:52 +00:00
const nitro = useNitro ( )
for ( const path in nitro . options . routeRules ) {
const rule = nitro . options . routeRules [ path ]
if ( ! rule . redirect ) { continue }
routes . push ( {
2024-03-16 00:07:38 +00:00
_sync : true ,
2024-01-29 11:07:52 +00:00
path : path.replace ( /\/[^/]*\*\*/ , '/:pathMatch(.*)' ) ,
2024-03-09 06:48:15 +00:00
file : resolve ( runtimeDir , 'component-stub' )
2024-01-29 11:07:52 +00:00
} )
}
} )
}
2022-01-17 18:27:23 +00:00
// Extract macros from pages
2022-11-02 10:28:41 +00:00
const pageMetaOptions : PageMetaPluginOptions = {
2022-01-19 18:07:54 +00:00
dev : nuxt.options.dev ,
2023-08-24 12:06:44 +00:00
sourcemap : ! ! nuxt . options . sourcemap . server || ! ! nuxt . options . sourcemap . client
2022-01-17 18:27:23 +00:00
}
2023-04-10 21:57:13 +00:00
nuxt . hook ( 'modules:done' , ( ) = > {
2023-05-02 11:17:41 +00:00
addVitePlugin ( ( ) = > PageMetaPlugin . vite ( pageMetaOptions ) )
addWebpackPlugin ( ( ) = > PageMetaPlugin . webpack ( pageMetaOptions ) )
2023-04-10 21:57:13 +00:00
} )
2022-01-17 18:27:23 +00:00
2023-01-19 13:01:21 +00:00
// Add prefetching support for middleware & layouts
addPlugin ( resolve ( runtimeDir , 'plugins/prefetch.client' ) )
2023-08-07 13:19:48 +00:00
// Add build plugin to ensure template $route is kept in sync with `<NuxtPage>`
if ( nuxt . options . experimental . templateRouteInjection ) {
addBuildPlugin ( RouteInjectionPlugin ( nuxt ) , { server : false } )
}
2021-11-17 11:28:36 +00:00
// Add router plugin
2023-01-19 13:01:21 +00:00
addPlugin ( resolve ( runtimeDir , 'plugins/router' ) )
2021-06-30 16:32:22 +00:00
2023-03-03 14:07:42 +00:00
const getSources = ( pages : NuxtPage [ ] ) : string [ ] = > pages
. filter ( p = > Boolean ( p . file ) )
. flatMap ( p = >
2024-02-26 16:08:45 +00:00
[ relative ( nuxt . options . srcDir , p . file as string ) , . . . ( p . children ? . length ? getSources ( p . children ) : [ ] ) ]
2023-03-03 14:07:42 +00:00
)
2022-08-16 11:19:39 +00:00
// Do not prefetch page chunks
2023-10-30 16:55:40 +00:00
nuxt . hook ( 'build:manifest' , ( manifest ) = > {
2023-02-27 19:36:27 +00:00
if ( nuxt . options . dev ) { return }
2024-02-26 16:08:45 +00:00
const sourceFiles = nuxt . apps . default ? . pages ? . length ? getSources ( nuxt . apps . default . pages ) : [ ]
2023-05-09 17:08:07 +00:00
2022-08-16 11:19:39 +00:00
for ( const key in manifest ) {
2024-03-09 06:48:15 +00:00
if ( manifest [ key ] . src && Object . values ( nuxt . apps ) . some ( app = > app . pages ? . some ( page = > page . mode === 'server' && page . file === join ( nuxt . options . srcDir , manifest [ key ] . src ! ) ) ) ) {
2024-02-26 17:39:26 +00:00
delete manifest [ key ]
continue
}
2022-08-16 11:19:39 +00:00
if ( manifest [ key ] . isEntry ) {
manifest [ key ] . dynamicImports =
manifest [ key ] . dynamicImports ? . filter ( i = > ! sourceFiles . includes ( i ) )
}
}
} )
2021-07-28 11:35:24 +00:00
// Add routes template
addTemplate ( {
filename : 'routes.mjs' ,
2023-10-30 16:55:40 +00:00
getContents ( { app } ) {
2024-03-09 06:48:15 +00:00
if ( ! app . pages ) { return 'export default []' }
2024-01-29 16:44:54 +00:00
const { routes , imports } = normalizeRoutes ( app . pages , new Set ( ) , nuxt . options . experimental . scanPageMeta )
2022-02-07 13:45:47 +00:00
return [ . . . imports , ` export default ${ routes } ` ] . join ( '\n' )
2021-07-28 11:35:24 +00:00
}
} )
2021-06-30 16:32:22 +00:00
2022-10-24 08:36:49 +00:00
// Add vue-router import for `<NuxtLayout>` integration
addTemplate ( {
filename : 'pages.mjs' ,
2024-01-02 10:00:47 +00:00
getContents : ( ) = > 'export { START_LOCATION, useRoute } from \'vue-router\''
2022-10-24 08:36:49 +00:00
} )
2022-11-02 08:53:23 +00:00
// Optimize vue-router to ensure we share the same injection symbol
nuxt . options . vite . optimizeDeps = nuxt . options . vite . optimizeDeps || { }
nuxt . options . vite . optimizeDeps . include = nuxt . options . vite . optimizeDeps . include || [ ]
nuxt . options . vite . optimizeDeps . include . push ( 'vue-router' )
2023-01-30 18:22:15 +00:00
nuxt . options . vite . resolve = nuxt . options . vite . resolve || { }
nuxt . options . vite . resolve . dedupe = nuxt . options . vite . resolve . dedupe || [ ]
nuxt . options . vite . resolve . dedupe . push ( 'vue-router' )
2022-03-15 16:57:41 +00:00
// Add router options template
addTemplate ( {
filename : 'router.options.mjs' ,
getContents : async ( ) = > {
2022-10-19 12:43:03 +00:00
// Scan and register app/router.options files
2024-01-18 16:06:00 +00:00
const routerOptionsFiles = await resolveRouterOptions ( )
2022-10-19 12:43:03 +00:00
2022-03-15 16:57:41 +00:00
const configRouterOptions = genObjectFromRawEntries ( Object . entries ( nuxt . options . router . options )
. map ( ( [ key , value ] ) = > [ key , genString ( value as string ) ] ) )
2022-04-04 08:23:11 +00:00
2022-03-15 16:57:41 +00:00
return [
2024-01-18 16:06:00 +00:00
. . . routerOptionsFiles . map ( ( file , index ) = > genImport ( file . path , ` routerOptions ${ index } ` ) ) ,
2022-03-15 16:57:41 +00:00
` const configRouterOptions = ${ configRouterOptions } ` ,
'export default {' ,
'...configRouterOptions,' ,
2024-01-30 13:55:18 +00:00
. . . routerOptionsFiles . map ( ( _ , index ) = > ` ...routerOptions ${ index } , ` ) ,
2022-03-15 16:57:41 +00:00
'}'
] . join ( '\n' )
}
} )
2022-01-25 12:29:11 +00:00
addTemplate ( {
2022-02-07 10:20:01 +00:00
filename : 'types/middleware.d.ts' ,
2023-08-14 17:07:17 +00:00
getContents : ( { nuxt , app } : { nuxt : Nuxt , app : NuxtApp } ) = > {
const composablesFile = relative ( join ( nuxt . options . buildDir , 'types' ) , resolve ( runtimeDir , 'composables' ) )
2022-06-27 12:10:29 +00:00
const namedMiddleware = app . middleware . filter ( mw = > ! mw . global )
2022-01-25 12:29:11 +00:00
return [
'import type { NavigationGuard } from \'vue-router\'' ,
2022-02-07 13:45:47 +00:00
` export type MiddlewareKey = ${ namedMiddleware . map ( mw = > genString ( mw . name ) ) . join ( ' | ' ) || 'string' } ` ,
` declare module ${ genString ( composablesFile ) } { ` ,
2022-01-25 12:29:11 +00:00
' interface PageMeta {' ,
' middleware?: MiddlewareKey | NavigationGuard | Array<MiddlewareKey | NavigationGuard>' ,
' }' ,
'}'
] . join ( '\n' )
}
} )
2022-01-26 11:56:24 +00:00
addTemplate ( {
2022-02-07 10:20:01 +00:00
filename : 'types/layouts.d.ts' ,
2023-08-14 17:07:17 +00:00
getContents : ( { nuxt , app } : { nuxt : Nuxt , app : NuxtApp } ) = > {
const composablesFile = relative ( join ( nuxt . options . buildDir , 'types' ) , resolve ( runtimeDir , 'composables' ) )
2022-01-26 11:56:24 +00:00
return [
2024-03-06 12:44:33 +00:00
'import type { ComputedRef, MaybeRef } from \'vue\'' ,
2022-03-14 10:47:24 +00:00
` export type LayoutKey = ${ Object . keys ( app . layouts ) . map ( name = > genString ( name ) ) . join ( ' | ' ) || 'string' } ` ,
2022-02-07 13:45:47 +00:00
` declare module ${ genString ( composablesFile ) } { ` ,
2022-01-26 11:56:24 +00:00
' interface PageMeta {' ,
2023-07-31 08:50:55 +00:00
' layout?: MaybeRef<LayoutKey | false> | ComputedRef<LayoutKey | false>' ,
2022-01-26 11:56:24 +00:00
' }' ,
'}'
] . join ( '\n' )
}
} )
2024-01-29 12:23:51 +00:00
// add page meta types if enabled
if ( nuxt . options . experimental . viewTransition ) {
addTypeTemplate ( {
filename : 'types/view-transitions.d.ts' ,
getContents : ( { nuxt } ) = > {
const runtimeDir = resolve ( distDir , 'pages/runtime' )
const composablesFile = relative ( join ( nuxt . options . buildDir , 'types' ) , resolve ( runtimeDir , 'composables' ) )
return [
2024-03-06 12:44:33 +00:00
'import type { ComputedRef, MaybeRef } from \'vue\'' ,
2024-01-29 12:23:51 +00:00
` declare module ${ genString ( composablesFile ) } { ` ,
' interface PageMeta {' ,
2024-03-09 06:48:15 +00:00
' viewTransition?: boolean | \'always\'' ,
2024-01-29 12:23:51 +00:00
' }' ,
2024-03-09 06:48:15 +00:00
'}'
2024-01-29 12:23:51 +00:00
] . join ( '\n' )
}
} )
}
2022-10-14 08:36:52 +00:00
// Add <NuxtPage>
addComponent ( {
name : 'NuxtPage' ,
2023-03-06 11:33:40 +00:00
priority : 10 , // built-in that we do not expect the user to override
2022-10-14 08:36:52 +00:00
filePath : resolve ( distDir , 'pages/runtime/page' )
} )
2022-03-14 10:47:24 +00:00
// Add declarations for middleware keys
2022-01-27 11:13:32 +00:00
nuxt . hook ( 'prepare:types' , ( { references } ) = > {
2022-02-07 10:20:01 +00:00
references . push ( { path : resolve ( nuxt . options . buildDir , 'types/middleware.d.ts' ) } )
references . push ( { path : resolve ( nuxt . options . buildDir , 'types/layouts.d.ts' ) } )
2023-09-28 07:37:14 +00:00
references . push ( { path : resolve ( nuxt . options . buildDir , 'vue-router-stub.d.ts' ) } )
2022-01-27 11:13:32 +00:00
} )
2021-05-20 11:42:41 +00:00
}
} )