mirror of
https://github.com/nuxt/nuxt.git
synced 2025-02-25 10:08:29 +00:00
feat(nuxt): allow specifying glob patterns for scanning pages/
(#31090)
This commit is contained in:
parent
35df23f41f
commit
74ffa8db80
@ -4,7 +4,7 @@ import { addBuildPlugin, addComponent, addPlugin, addTemplate, addTypeTemplate,
|
||||
import { dirname, join, relative, resolve } from 'pathe'
|
||||
import { genImport, genObjectFromRawEntries, genString } from 'knitwork'
|
||||
import { joinURL } from 'ufo'
|
||||
import type { NuxtPage } from 'nuxt/schema'
|
||||
import type { Nuxt, NuxtPage } from 'nuxt/schema'
|
||||
import { createRoutesContext } from 'unplugin-vue-router'
|
||||
import { resolveOptions } from 'unplugin-vue-router/options'
|
||||
import type { EditableTreeNode, Options as TypedRouterOptions } from 'unplugin-vue-router'
|
||||
@ -22,14 +22,40 @@ import { RouteInjectionPlugin } from './plugins/route-injection'
|
||||
|
||||
const OPTIONAL_PARAM_RE = /^\/?:.*(?:\?|\(\.\*\)\*)$/
|
||||
|
||||
const runtimeDir = resolve(distDir, 'pages/runtime')
|
||||
|
||||
async function resolveRouterOptions (nuxt: Nuxt, builtInRouterOptions: string) {
|
||||
const context = {
|
||||
files: [] as Array<{ path: string, optional?: boolean }>,
|
||||
}
|
||||
|
||||
for (const layer of nuxt.options._layers) {
|
||||
const path = await findPath(resolve(layer.config.srcDir, layer.config.dir?.app || 'app', 'router.options'))
|
||||
if (path) { context.files.unshift({ path }) }
|
||||
}
|
||||
|
||||
// Add default options at beginning
|
||||
context.files.unshift({ path: builtInRouterOptions, optional: true })
|
||||
|
||||
await nuxt.callHook('pages:routerOptions', context)
|
||||
return context.files
|
||||
}
|
||||
|
||||
export default defineNuxtModule({
|
||||
meta: {
|
||||
name: 'nuxt:pages',
|
||||
configKey: 'pages',
|
||||
},
|
||||
defaults: nuxt => ({
|
||||
enabled: undefined as undefined | boolean,
|
||||
pattern: `**/*{${nuxt.options.extensions.join(',')}}` as string | string[],
|
||||
}),
|
||||
async setup (_options, nuxt) {
|
||||
const options = typeof _options === 'boolean' ? { enabled: _options, pattern: `**/*{${nuxt.options.extensions.join(',')}}` } : _options
|
||||
|
||||
const useExperimentalTypedPages = nuxt.options.experimental.typedPages
|
||||
const runtimeDir = resolve(distDir, 'pages/runtime')
|
||||
const builtInRouterOptions = await findPath(resolve(runtimeDir, 'router.options')) || resolve(runtimeDir, 'router.options')
|
||||
|
||||
const pagesDirs = nuxt.options._layers.map(
|
||||
layer => resolve(layer.config.srcDir, (layer.config.rootDir === nuxt.options.rootDir ? nuxt.options : layer.config).dir?.pages || 'pages'),
|
||||
)
|
||||
@ -43,32 +69,14 @@ export default defineNuxtModule({
|
||||
delete tsConfig.compilerOptions.paths['#vue-router/*']
|
||||
})
|
||||
|
||||
const builtInRouterOptions = await findPath(resolve(runtimeDir, 'router.options')) || resolve(runtimeDir, 'router.options')
|
||||
async function resolveRouterOptions () {
|
||||
const context = {
|
||||
files: [] as Array<{ path: string, optional?: boolean }>,
|
||||
}
|
||||
|
||||
for (const layer of nuxt.options._layers) {
|
||||
const path = await findPath(resolve(layer.config.srcDir, layer.config.dir?.app || 'app', 'router.options'))
|
||||
if (path) { context.files.unshift({ path }) }
|
||||
}
|
||||
|
||||
// Add default options at beginning
|
||||
context.files.unshift({ path: builtInRouterOptions, optional: true })
|
||||
|
||||
await nuxt.callHook('pages:routerOptions', context)
|
||||
return context.files
|
||||
}
|
||||
|
||||
// Disable module (and use universal router) if pages dir do not exists or user has disabled it
|
||||
const isNonEmptyDir = (dir: string) => existsSync(dir) && readdirSync(dir).length
|
||||
const userPreference = nuxt.options.pages
|
||||
const userPreference = options.enabled
|
||||
const isPagesEnabled = async () => {
|
||||
if (typeof userPreference === 'boolean') {
|
||||
return userPreference
|
||||
}
|
||||
const routerOptionsFiles = await resolveRouterOptions()
|
||||
const routerOptionsFiles = await resolveRouterOptions(nuxt, builtInRouterOptions)
|
||||
if (routerOptionsFiles.filter(p => !p.optional).length > 0) {
|
||||
return true
|
||||
}
|
||||
@ -76,7 +84,7 @@ export default defineNuxtModule({
|
||||
return true
|
||||
}
|
||||
|
||||
const pages = await resolvePagesRoutes(nuxt)
|
||||
const pages = await resolvePagesRoutes(options.pattern, nuxt)
|
||||
if (pages.length) {
|
||||
if (nuxt.apps.default) {
|
||||
nuxt.apps.default.pages = pages
|
||||
@ -86,9 +94,9 @@ export default defineNuxtModule({
|
||||
|
||||
return false
|
||||
}
|
||||
nuxt.options.pages = await isPagesEnabled()
|
||||
options.enabled = await isPagesEnabled()
|
||||
|
||||
if (nuxt.options.dev && nuxt.options.pages) {
|
||||
if (nuxt.options.dev && options.enabled) {
|
||||
// Add plugin to check if pages are enabled without NuxtPage being instantiated
|
||||
addPlugin(resolve(runtimeDir, 'plugins/check-if-page-unused'))
|
||||
}
|
||||
@ -112,14 +120,14 @@ export default defineNuxtModule({
|
||||
const path = resolve(nuxt.options.srcDir, relativePath)
|
||||
if (restartPaths.some(p => p === path || path.startsWith(p + '/'))) {
|
||||
const newSetting = await isPagesEnabled()
|
||||
if (nuxt.options.pages !== newSetting) {
|
||||
if (options.enabled !== newSetting) {
|
||||
logger.info('Pages', newSetting ? 'enabled' : 'disabled')
|
||||
return nuxt.callHook('restart')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (!nuxt.options.pages) {
|
||||
if (!options.enabled) {
|
||||
addPlugin(resolve(distDir, 'app/plugins/router'))
|
||||
addTemplate({
|
||||
filename: 'pages.mjs',
|
||||
@ -171,13 +179,13 @@ export default defineNuxtModule({
|
||||
if (useExperimentalTypedPages) {
|
||||
const declarationFile = './types/typed-router.d.ts'
|
||||
|
||||
const options: TypedRouterOptions = {
|
||||
const typedRouterOptions: TypedRouterOptions = {
|
||||
routesFolder: [],
|
||||
dts: resolve(nuxt.options.buildDir, declarationFile),
|
||||
logs: nuxt.options.debug && nuxt.options.debug.router,
|
||||
async beforeWriteFiles (rootPage) {
|
||||
rootPage.children.forEach(child => child.delete())
|
||||
const pages = nuxt.apps.default?.pages || await resolvePagesRoutes(nuxt)
|
||||
const pages = nuxt.apps.default?.pages || await resolvePagesRoutes(options.pattern, nuxt)
|
||||
if (nuxt.apps.default) {
|
||||
nuxt.apps.default.pages = pages
|
||||
}
|
||||
@ -218,7 +226,7 @@ export default defineNuxtModule({
|
||||
references.push({ types: 'unplugin-vue-router/client' })
|
||||
})
|
||||
|
||||
const context = createRoutesContext(resolveOptions(options))
|
||||
const context = createRoutesContext(resolveOptions(typedRouterOptions))
|
||||
const dtsFile = resolve(nuxt.options.buildDir, declarationFile)
|
||||
await mkdir(dirname(dtsFile), { recursive: true })
|
||||
await context.scanPages(false)
|
||||
@ -269,7 +277,7 @@ export default defineNuxtModule({
|
||||
}
|
||||
|
||||
nuxt.hooks.hookOnce('app:templates', async (app) => {
|
||||
app.pages ||= await resolvePagesRoutes(nuxt)
|
||||
app.pages ||= await resolvePagesRoutes(options.pattern, nuxt)
|
||||
})
|
||||
|
||||
nuxt.hook('builder:watch', async (event, relativePath) => {
|
||||
@ -279,7 +287,7 @@ export default defineNuxtModule({
|
||||
if (event === 'change' && !shouldAlwaysRegenerate) { return }
|
||||
|
||||
if (shouldAlwaysRegenerate || updateTemplatePaths.some(dir => path.startsWith(dir))) {
|
||||
nuxt.apps.default!.pages = await resolvePagesRoutes(nuxt)
|
||||
nuxt.apps.default!.pages = await resolvePagesRoutes(options.pattern, nuxt)
|
||||
}
|
||||
})
|
||||
|
||||
@ -538,9 +546,9 @@ export default defineNuxtModule({
|
||||
// Add router options template
|
||||
addTemplate({
|
||||
filename: 'router.options.mjs',
|
||||
getContents: async () => {
|
||||
getContents: async ({ nuxt }) => {
|
||||
// Scan and register app/router.options files
|
||||
const routerOptionsFiles = await resolveRouterOptions()
|
||||
const routerOptionsFiles = await resolveRouterOptions(nuxt, builtInRouterOptions)
|
||||
|
||||
const configRouterOptions = genObjectFromRawEntries(Object.entries(nuxt.options.router.options)
|
||||
.map(([key, value]) => [key, genString(value as string)]))
|
||||
|
@ -42,14 +42,14 @@ interface ScannedFile {
|
||||
absolutePath: string
|
||||
}
|
||||
|
||||
export async function resolvePagesRoutes (nuxt = useNuxt()): Promise<NuxtPage[]> {
|
||||
export async function resolvePagesRoutes (pattern: string | string[], nuxt = useNuxt()): Promise<NuxtPage[]> {
|
||||
const pagesDirs = nuxt.options._layers.map(
|
||||
layer => resolve(layer.config.srcDir, (layer.config.rootDir === nuxt.options.rootDir ? nuxt.options : layer.config).dir?.pages || 'pages'),
|
||||
)
|
||||
|
||||
const scannedFiles: ScannedFile[] = []
|
||||
for (const dir of pagesDirs) {
|
||||
const files = await resolveFiles(dir, `**/*{${nuxt.options.extensions.join(',')}}`)
|
||||
const files = await resolveFiles(dir, pattern)
|
||||
scannedFiles.push(...files.map(file => ({ relativePath: relative(dir, file), absolutePath: file })))
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ export default defineResolvers({
|
||||
/**
|
||||
* Whether to use the vue-router integration in Nuxt 3. If you do not provide a value it will be
|
||||
* enabled if you have a `pages/` directory in your source folder.
|
||||
* @type {boolean}
|
||||
* @type {boolean | { enabled?: boolean, pattern?: string | string[] }}
|
||||
*/
|
||||
pages: undefined,
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user