diff --git a/examples/module-extend-pages/modules/pages/index.ts b/examples/module-extend-pages/modules/pages/index.ts new file mode 100644 index 0000000000..1052078d0c --- /dev/null +++ b/examples/module-extend-pages/modules/pages/index.ts @@ -0,0 +1,15 @@ +import { defineNuxtModule, extendPages } from '@nuxt/kit' +import { resolve } from 'pathe' + +export default defineNuxtModule({ + setup () { + extendPages((pages) => { + // Add /test page + pages.push({ + name: 'Test', + path: '/test', + file: resolve(__dirname, './pages/test.vue') + }) + }) + } +}) diff --git a/examples/module-extend-pages/modules/pages/pages/test.vue b/examples/module-extend-pages/modules/pages/pages/test.vue new file mode 100644 index 0000000000..d1399b0d95 --- /dev/null +++ b/examples/module-extend-pages/modules/pages/pages/test.vue @@ -0,0 +1,5 @@ + diff --git a/examples/module-extend-pages/nuxt.config.ts b/examples/module-extend-pages/nuxt.config.ts new file mode 100644 index 0000000000..69cd9ba207 --- /dev/null +++ b/examples/module-extend-pages/nuxt.config.ts @@ -0,0 +1,7 @@ +import { defineNuxtConfig } from 'nuxt3' + +export default defineNuxtConfig({ + buildModules: [ + '~/modules/pages' + ] +}) diff --git a/examples/module-extend-pages/package.json b/examples/module-extend-pages/package.json new file mode 100644 index 0000000000..ba37e74838 --- /dev/null +++ b/examples/module-extend-pages/package.json @@ -0,0 +1,12 @@ +{ + "name": "example-module-extend-pages", + "private": true, + "devDependencies": { + "nuxt3": "latest" + }, + "scripts": { + "dev": "nuxi dev", + "build": "nuxi build", + "start": "node .output/server/index.mjs" + } +} diff --git a/examples/module-extend-pages/pages/index.vue b/examples/module-extend-pages/pages/index.vue new file mode 100644 index 0000000000..709ee2486f --- /dev/null +++ b/examples/module-extend-pages/pages/index.vue @@ -0,0 +1,7 @@ + diff --git a/examples/module-extend-pages/tsconfig.json b/examples/module-extend-pages/tsconfig.json new file mode 100644 index 0000000000..4b34df1571 --- /dev/null +++ b/examples/module-extend-pages/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./.nuxt/tsconfig.json" +} diff --git a/packages/kit/src/module/container.ts b/packages/kit/src/module/container.ts index b6ae21ff2e..6c444318ab 100644 --- a/packages/kit/src/module/container.ts +++ b/packages/kit/src/module/container.ts @@ -2,7 +2,8 @@ import { parse, relative } from 'pathe' import consola from 'consola' import type { Nuxt, NuxtPluginTemplate, NuxtTemplate } from '../types/nuxt' import { chainFn } from '../utils/task' -import { addTemplate, addPluginTemplate, addServerMiddleware } from './utils' +import { resolveAlias } from '../utils/resolve' +import { addTemplate, addPluginTemplate, addServerMiddleware, extendPages } from './utils' import { installModule } from './install' /** Legacy ModuleContainer for backwards compatibility with existing Nuxt 2 modules. */ @@ -103,7 +104,7 @@ export function createModuleContainer (nuxt: Nuxt) { /** Allows extending routes by chaining `options.build.extendRoutes` function. */ extendRoutes (fn) { - nuxt.options.router.extendRoutes = chainFn(nuxt.options.router.extendRoutes, fn) + extendPages(routes => fn(routes, resolveAlias)) }, /** `requireModule` is a shortcut for `addModule` */ diff --git a/packages/kit/src/module/utils.ts b/packages/kit/src/module/utils.ts index d216b53c67..1388ab6c22 100644 --- a/packages/kit/src/module/utils.ts +++ b/packages/kit/src/module/utils.ts @@ -11,6 +11,7 @@ import { Nuxt } from '../types/nuxt' import { useNuxt } from '../nuxt' import type { NuxtTemplate, NuxtPlugin, NuxtPluginTemplate } from '../types/nuxt' import type { ComponentsDir, Component } from '../types/components' +import type { NuxtHooks } from '../types/hooks' /** * Renders given template using lodash template during build into the project buildDir @@ -351,6 +352,11 @@ export function addComponent (opts: AddComponentOptions) { }) } +export function extendPages (cb: NuxtHooks['pages:extend']) { + const nuxt = useNuxt() + nuxt.hook('pages:extend', cb) +} + const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"{(.+)}"/g, '$1') const importName = (src: string) => `${camelCase(basename(src, extname(src))).replace(/[^a-zA-Z?\d\s:]/g, '')}_${hash(src)}` diff --git a/packages/kit/src/types/hooks.ts b/packages/kit/src/types/hooks.ts index faa4332980..019c6c864f 100644 --- a/packages/kit/src/types/hooks.ts +++ b/packages/kit/src/types/hooks.ts @@ -31,12 +31,20 @@ type RenderResult = { // https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html export type TSReference = { types: string } | { path: string } +export type NuxtPage = { + name?: string, + path: string, + file: string, + children?: NuxtPage[] +} + export interface NuxtHooks { // nuxt3 'app:resolve': (app: NuxtApp) => HookResult 'app:templates': (app: NuxtApp) => HookResult 'app:templatesGenerated': (app: NuxtApp) => HookResult 'builder:generateApp': () => HookResult + 'pages:extend': (pages: NuxtPage[]) => HookResult // Auto imports 'autoImports:sources': (autoImportSources: AutoImportSource[]) => HookResult diff --git a/packages/nuxt3/src/pages/module.ts b/packages/nuxt3/src/pages/module.ts index 56d0bf3a25..5d2bf7a29f 100644 --- a/packages/nuxt3/src/pages/module.ts +++ b/packages/nuxt3/src/pages/module.ts @@ -44,8 +44,9 @@ export default defineNuxtModule({ addTemplate({ filename: 'routes.mjs', async getContents () { - const routes = await resolvePagesRoutes(nuxt) - const serializedRoutes = addComponentToRoutes(routes) + const pages = await resolvePagesRoutes(nuxt) + await nuxt.callHook('pages:extend', pages) + const serializedRoutes = addComponentToRoutes(pages) return `export default ${JSON.stringify(serializedRoutes, null, 2).replace(/"{(.+)}"/g, '$1')}` } }) diff --git a/packages/nuxt3/src/pages/utils.ts b/packages/nuxt3/src/pages/utils.ts index ddd8e5d474..833a3380d7 100644 --- a/packages/nuxt3/src/pages/utils.ts +++ b/packages/nuxt3/src/pages/utils.ts @@ -1,15 +1,8 @@ import { basename, extname, relative, resolve } from 'pathe' import { encodePath } from 'ufo' -import { Nuxt, resolveFiles } from '@nuxt/kit' +import { Nuxt, resolveFiles, NuxtRoute } from '@nuxt/kit' import { kebabCase } from 'scule' -export interface NuxtRoute { - name?: string - path: string - file: string - children: NuxtRoute[] -} - enum SegmentParserState { initial, static, @@ -234,7 +227,7 @@ export async function resolveLayouts (nuxt: Nuxt) { export function addComponentToRoutes (routes: NuxtRoute[]) { return routes.map(route => ({ ...route, - children: addComponentToRoutes(route.children), + children: route.children ? addComponentToRoutes(route.children) : [], component: `{() => import('${route.file}')}` })) } diff --git a/yarn.lock b/yarn.lock index 98087bc798..afc40d19f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9661,6 +9661,14 @@ __metadata: languageName: unknown linkType: soft +"example-module-extend-pages@workspace:examples/module-extend-pages": + version: 0.0.0-use.local + resolution: "example-module-extend-pages@workspace:examples/module-extend-pages" + dependencies: + nuxt3: latest + languageName: unknown + linkType: soft + "example-use-async-data@workspace:examples/use-async-data": version: 0.0.0-use.local resolution: "example-use-async-data@workspace:examples/use-async-data"