2022-04-15 15:19:05 +00:00
|
|
|
import { existsSync } from 'node:fs'
|
2022-03-15 16:57:41 +00:00
|
|
|
import { defineNuxtModule, addTemplate, addPlugin, addVitePlugin, addWebpackPlugin, findPath } from '@nuxt/kit'
|
2021-09-27 12:49:36 +00:00
|
|
|
import { resolve } from 'pathe'
|
2022-06-27 12:10:29 +00:00
|
|
|
import { genString, genImport, genObjectFromRawEntries } from 'knitwork'
|
2022-01-27 11:13:32 +00:00
|
|
|
import escapeRE from 'escape-string-regexp'
|
2022-07-07 15:07:37 +00:00
|
|
|
import type { NuxtApp, NuxtPage } from '@nuxt/schema'
|
|
|
|
import { joinURL } from 'ufo'
|
2021-08-11 21:26:47 +00:00
|
|
|
import { distDir } from '../dirs'
|
2022-06-27 12:10:29 +00:00
|
|
|
import { resolvePagesRoutes, normalizeRoutes } from './utils'
|
2022-01-17 18:27:23 +00:00
|
|
|
import { TransformMacroPlugin, TransformMacroPluginOptions } from './macros'
|
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
|
|
|
},
|
2021-05-20 11:42:41 +00:00
|
|
|
setup (_options, nuxt) {
|
2022-03-22 18:12:54 +00:00
|
|
|
const pagesDirs = nuxt.options._layers.map(
|
|
|
|
layer => resolve(layer.config.srcDir, layer.config.dir?.pages || 'pages')
|
|
|
|
)
|
2021-07-28 11:35:24 +00:00
|
|
|
|
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
|
|
|
|
if (nuxt.options.pages === false || (nuxt.options.pages !== true && !pagesDirs.some(dir => existsSync(dir)))) {
|
2022-02-21 13:03:42 +00:00
|
|
|
addPlugin(resolve(distDir, 'app/plugins/router'))
|
2021-07-28 11:35:24 +00:00
|
|
|
return
|
|
|
|
}
|
2021-05-20 11:42:41 +00:00
|
|
|
|
2022-03-22 18:12:54 +00:00
|
|
|
const runtimeDir = resolve(distDir, 'pages/runtime')
|
|
|
|
|
2021-11-02 09:39:42 +00:00
|
|
|
// Add $router types
|
|
|
|
nuxt.hook('prepare:types', ({ references }) => {
|
|
|
|
references.push({ types: 'vue-router' })
|
|
|
|
})
|
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
// Regenerate templates when adding or removing pages
|
2021-05-20 11:42:41 +00:00
|
|
|
nuxt.hook('builder:watch', async (event, path) => {
|
2022-01-27 11:13:32 +00:00
|
|
|
const dirs = [
|
|
|
|
nuxt.options.dir.pages,
|
2022-06-27 12:10:29 +00:00
|
|
|
nuxt.options.dir.layouts,
|
2022-01-27 11:13:32 +00:00
|
|
|
nuxt.options.dir.middleware
|
|
|
|
].filter(Boolean)
|
|
|
|
|
2022-06-10 14:50:47 +00:00
|
|
|
const pathPattern = new RegExp(`(^|\\/)(${dirs.map(escapeRE).join('|')})/`)
|
2021-06-30 16:32:22 +00:00
|
|
|
if (event !== 'change' && path.match(pathPattern)) {
|
2021-05-20 11:42:41 +00:00
|
|
|
await nuxt.callHook('builder:generateApp')
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
nuxt.hook('app:resolve', (app) => {
|
2021-10-12 12:51:41 +00:00
|
|
|
// Add default layout for pages
|
2022-03-11 08:22:16 +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-07-07 15:07:37 +00:00
|
|
|
// Prerender all non-dynamic page routes when generating app
|
|
|
|
if (!nuxt.options.dev && nuxt.options._generate) {
|
|
|
|
const routes = new Set<string>()
|
|
|
|
nuxt.hook('modules:done', () => {
|
|
|
|
nuxt.hook('pages:extend', (pages) => {
|
|
|
|
routes.clear()
|
|
|
|
for (const path of nuxt.options.nitro.prerender?.routes || []) {
|
|
|
|
routes.add(path)
|
|
|
|
}
|
|
|
|
const processPages = (pages: NuxtPage[], currentPath = '/') => {
|
|
|
|
for (const page of pages) {
|
|
|
|
// Skip dynamic paths
|
|
|
|
if (page.path.includes(':')) { continue }
|
|
|
|
|
|
|
|
const path = joinURL(currentPath, page.path)
|
|
|
|
routes.add(path)
|
|
|
|
if (page.children) { processPages(page.children, path) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
processPages(pages)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
nuxt.hook('nitro:build:before', (nitro) => {
|
|
|
|
nitro.options.prerender.routes = [...routes]
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-12-17 09:15:03 +00:00
|
|
|
nuxt.hook('autoImports:extend', (autoImports) => {
|
2022-02-21 13:03:42 +00:00
|
|
|
autoImports.push({ name: 'definePageMeta', as: 'definePageMeta', from: resolve(runtimeDir, 'composables') })
|
2021-12-17 09:15:03 +00:00
|
|
|
})
|
|
|
|
|
2022-01-17 18:27:23 +00:00
|
|
|
// Extract macros from pages
|
|
|
|
const macroOptions: TransformMacroPluginOptions = {
|
2022-01-19 18:07:54 +00:00
|
|
|
dev: nuxt.options.dev,
|
2022-04-22 15:35:42 +00:00
|
|
|
sourcemap: nuxt.options.sourcemap,
|
2022-01-17 18:27:23 +00:00
|
|
|
macros: {
|
|
|
|
definePageMeta: 'meta'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
addVitePlugin(TransformMacroPlugin.vite(macroOptions))
|
|
|
|
addWebpackPlugin(TransformMacroPlugin.webpack(macroOptions))
|
|
|
|
|
2021-11-17 11:28:36 +00:00
|
|
|
// Add router plugin
|
2021-07-28 11:35:24 +00:00
|
|
|
addPlugin(resolve(runtimeDir, 'router'))
|
2021-06-30 16:32:22 +00:00
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
// Add routes template
|
|
|
|
addTemplate({
|
|
|
|
filename: 'routes.mjs',
|
|
|
|
async getContents () {
|
2022-03-22 18:12:54 +00:00
|
|
|
const pages = await resolvePagesRoutes()
|
2021-11-09 10:16:23 +00:00
|
|
|
await nuxt.callHook('pages:extend', pages)
|
2022-02-07 13:45:47 +00:00
|
|
|
const { routes, imports } = normalizeRoutes(pages)
|
|
|
|
return [...imports, `export default ${routes}`].join('\n')
|
2021-07-28 11:35:24 +00:00
|
|
|
}
|
|
|
|
})
|
2021-06-30 16:32:22 +00:00
|
|
|
|
2022-03-15 16:57:41 +00:00
|
|
|
// Add router options template
|
|
|
|
addTemplate({
|
|
|
|
filename: 'router.options.mjs',
|
|
|
|
getContents: async () => {
|
|
|
|
// Check for router options
|
2022-04-04 08:23:11 +00:00
|
|
|
const routerOptionsFiles = (await Promise.all(nuxt.options._layers.map(
|
|
|
|
async layer => await findPath(resolve(layer.config.srcDir, 'app/router.options'))
|
|
|
|
))).filter(Boolean)
|
|
|
|
|
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 [
|
2022-04-04 08:23:11 +00:00
|
|
|
...routerOptionsFiles.map((file, index) => genImport(file, `routerOptions${index}`)),
|
2022-03-15 16:57:41 +00:00
|
|
|
`const configRouterOptions = ${configRouterOptions}`,
|
|
|
|
'export default {',
|
|
|
|
'...configRouterOptions,',
|
2022-04-04 08:23:11 +00:00
|
|
|
// We need to reverse spreading order to respect layers priority
|
|
|
|
...routerOptionsFiles.map((_, index) => `...routerOptions${index},`).reverse(),
|
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',
|
2022-06-27 12:10:29 +00:00
|
|
|
getContents: ({ app }: { app: NuxtApp }) => {
|
2022-01-25 12:29:11 +00:00
|
|
|
const composablesFile = 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',
|
2022-06-27 12:10:29 +00:00
|
|
|
getContents: ({ app }: { app: NuxtApp }) => {
|
2022-01-26 11:56:24 +00:00
|
|
|
const composablesFile = resolve(runtimeDir, 'composables')
|
|
|
|
return [
|
|
|
|
'import { ComputedRef, Ref } 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 {',
|
|
|
|
' layout?: false | LayoutKey | Ref<LayoutKey> | ComputedRef<LayoutKey>',
|
|
|
|
' }',
|
|
|
|
'}'
|
|
|
|
].join('\n')
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
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') })
|
2022-01-27 11:13:32 +00:00
|
|
|
})
|
2021-05-20 11:42:41 +00:00
|
|
|
}
|
|
|
|
})
|