feat(nuxt): auto-register modules in ~/modules (#19394)

This commit is contained in:
Daniel Roe 2023-03-03 17:52:55 +00:00 committed by GitHub
parent a420369c90
commit 3681bddfd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 115 additions and 10 deletions

View File

@ -0,0 +1,48 @@
---
navigation.icon: IconDirectory
title: 'modules'
head.title: 'modules/'
description: Use the modules/ directory to automatically register local modules within your application.
---
# Modules Directory
Nuxt scans the `modules/` directory and loads them before starting. It is a good place to place any local modules you develop while building your application.
The auto-registered files patterns are:
- `modules/*/index.ts`
- `modules/*.ts`
You don't need to add those local modules to your [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt.config) separately.
::code-group
```ts [modules/hello/index.ts]
// `nuxt/kit` is a helper subpath import you can use when defining local modules
// that means you do not need to add `@nuxt/kit` to your project's dependencies
import { createResolver, defineNuxtModule, addServerHandler } from 'nuxt/kit'
export default defineNuxtModule({
meta: {
name: 'hello'
},
setup () {
const { resolve } = createResolver(import.meta.url)
// Add an API route
addServerHandler({
route: '/api/hello',
handler: resolve('./runtime/api-route')
})
}
})
```
```ts [modules/hello/runtime/api-route.ts]
export default defineEventHandler(() => {
return { hello: 'world' }
}
```
::
When starting Nuxt, the `hello` module will be registered and the `/api/hello` route will be available.
:ReadMore{link="/docs/guide/going-further/modules"}

View File

@ -1,7 +1,7 @@
import { join, normalize, resolve } from 'pathe' import { join, normalize, relative, resolve } from 'pathe'
import { createHooks, createDebugger } from 'hookable' import { createHooks, createDebugger } from 'hookable'
import type { LoadNuxtOptions } from '@nuxt/kit' import type { LoadNuxtOptions } from '@nuxt/kit'
import { loadNuxtConfig, nuxtCtx, installModule, addComponent, addVitePlugin, addWebpackPlugin, tryResolveModule, addPlugin } from '@nuxt/kit' import { resolvePath, resolveAlias, resolveFiles, loadNuxtConfig, nuxtCtx, installModule, addComponent, addVitePlugin, addWebpackPlugin, tryResolveModule, addPlugin } from '@nuxt/kit'
import escapeRE from 'escape-string-regexp' import escapeRE from 'escape-string-regexp'
import fse from 'fs-extra' import fse from 'fs-extra'
@ -122,10 +122,38 @@ async function initNuxt (nuxt: Nuxt) {
// Init user modules // Init user modules
await nuxt.callHook('modules:before') await nuxt.callHook('modules:before')
const modulesToInstall = [ const modulesToInstall = []
...nuxt.options.modules,
...nuxt.options._modules const watchedPaths = new Set<string>()
] const specifiedModules = new Set<string>()
for (const _mod of nuxt.options.modules) {
const mod = Array.isArray(_mod) ? _mod[0] : _mod
if (typeof mod !== 'string') { continue }
const modPath = await resolvePath(resolveAlias(mod))
specifiedModules.add(modPath)
}
// Automatically register user modules
for (const config of nuxt.options._layers.map(layer => layer.config).reverse()) {
const layerModules = await resolveFiles(config.srcDir, [
`${config.dir?.modules || 'modules'}/*{${nuxt.options.extensions.join(',')}}`,
`${config.dir?.modules || 'modules'}/*/index{${nuxt.options.extensions.join(',')}}`
])
for (const mod of layerModules) {
watchedPaths.add(relative(config.srcDir, mod))
if (specifiedModules.has(mod)) { continue }
specifiedModules.add(mod)
modulesToInstall.push(mod)
}
}
// Register user and then ad-hoc modules
modulesToInstall.push(...nuxt.options.modules, ...nuxt.options._modules)
nuxt.hooks.hookOnce('builder:watch', (event, path) => {
if (watchedPaths.has(path)) { nuxt.callHook('restart', { hard: true }) }
})
// Add <NuxtWelcome> // Add <NuxtWelcome>
addComponent({ addComponent({

View File

@ -219,6 +219,11 @@ export default defineUntypedSchema({
*/ */
middleware: 'middleware', middleware: 'middleware',
/**
* The modules directory, each file in which will be auto-registered as a Nuxt module.
*/
modules: 'modules',
/** /**
* The directory which will be processed to auto-generate your application page routes. * The directory which will be processed to auto-generate your application page routes.
*/ */

View File

@ -46,6 +46,13 @@ describe('route rules', () => {
}) })
}) })
describe('modules', () => {
it('should auto-register modules in ~/modules', async () => {
const result = await $fetch('/auto-registered-module')
expect(result).toEqual('handler added by auto-registered module')
})
})
describe('pages', () => { describe('pages', () => {
it('render index', async () => { it('render index', async () => {
const html = await $fetch('/') const html = await $fetch('/')

View File

@ -0,0 +1,15 @@
import { createResolver, defineNuxtModule, addServerHandler } from 'nuxt/kit'
export default defineNuxtModule({
meta: {
name: 'auto-registered-module'
},
setup () {
const resolver = createResolver(import.meta.url)
addServerHandler({
handler: resolver.resolve('./runtime/handler'),
route: '/auto-registered-module'
})
}
})

View File

@ -0,0 +1 @@
export default defineEventHandler(() => 'handler added by auto-registered module')

View File

@ -1,5 +1,4 @@
import { fileURLToPath } from 'node:url' import { defineNuxtModule, createResolver, addPlugin, useNuxt } from 'nuxt/kit'
import { defineNuxtModule, addPlugin, useNuxt } from '@nuxt/kit'
export default defineNuxtModule({ export default defineNuxtModule({
defaults: { defaults: {
@ -11,11 +10,13 @@ export default defineNuxtModule({
configKey: 'sampleModule' configKey: 'sampleModule'
}, },
setup () { setup () {
addPlugin(fileURLToPath(new URL('./runtime/plugin', import.meta.url))) const resolver = createResolver(import.meta.url)
addPlugin(resolver.resolve('./runtime/plugin'))
useNuxt().hook('app:resolve', (app) => { useNuxt().hook('app:resolve', (app) => {
app.middleware.push({ app.middleware.push({
name: 'unctx-test', name: 'unctx-test',
path: fileURLToPath(new URL('./runtime/middleware', import.meta.url)), path: resolver.resolve('./runtime/middleware'),
global: true global: true
}) })
}) })