diff --git a/docs/2.guide/3.going-further/3.modules.md b/docs/2.guide/3.going-further/3.modules.md index a6cfbf7625..889c5b0f8b 100644 --- a/docs/2.guide/3.going-further/3.modules.md +++ b/docs/2.guide/3.going-further/3.modules.md @@ -443,22 +443,40 @@ export default defineNuxtModule({ :: -#### Augmenting Types +#### Adding Templates/Virtual Files -If your module should augment types handled by Nuxt, you can use the `prepare:types` hook to perform this operation. +If you need to add a virtual file that can be imported into the user's app, you can use the `addTemplate` utility. -```js -import { defineNuxtModule, addTemplate, createResolver } from '@nuxt/kit' +```ts +import { defineNuxtModule, addTemplate } from '@nuxt/kit' export default defineNuxtModule({ setup (options, nuxt) { - const { resolve } = createResolver(import.meta.url) - - // Generating types to be injected + // The file is added to Nuxt's internal virtual file system and can be imported from '#build/my-module-feature.mjs' addTemplate({ - filename: 'my-module.d.ts', - getContents: () => { - return `// Generated by my-module + filename: 'my-module-feature.mjs', + getContents: () => 'export const myModuleFeature = () => "hello world !"' + }) + } +}) +``` + +#### Adding Type Declarations + +You might also want to add a type declaration to the user's project (for example, to augment an Nuxt interface +or provide a global type of your own). For this, Nuxt provides the `addTypeTemplate` utility that both +writes a template to disk and adds a reference to it in the generated `nuxt.d.ts` file that. + +If your module should augment types handled by Nuxt, you can use `addTypeTemplate` to perform this operation: + +```js +import { defineNuxtModule, addTemplate, addTypeTemplate } from '@nuxt/kit' + +export default defineNuxtModule({ + setup (options, nuxt) { + addTypeTemplate({ + filename: 'types/my-module.d.ts', + getContents: () => `// Generated by my-module interface MyModuleNitroRules { myModule?: { foo: 'bar' } } @@ -467,13 +485,29 @@ export default defineNuxtModule({ interface NitroRouteConfig extends MyModuleNitroRules {} } export {}` - }, }) + } +}) +``` - // Injecting previously generated types - nuxt.hooks.hook('prepare:types', ({ references }) => { - references.push({ path: resolve(nuxt.options.buildDir, 'my-module.d.ts') }) - }) +If you need more granular control, you can use the `prepare:types` hook to register a callback that will inject your types. + +```ts +const template = addTemplate({ /* template options */ }) +nuxt.hook('prepare:types', ({ references }) => { + references.push({ path: template.dst }) +}) +``` + +##### Updating Templates + +If you need to update your templates/virtual files, you can leverage the `updateTemplates` utility like this : + +```ts +nuxt.hook('builder:watch', async (event, path) => { + if (path.includes('my-module-feature.config')) { + // This will reload the template that you registered + updateTemplates({ filter: t => t.filename === 'my-module-feature.mjs' }) } }) ``` diff --git a/docs/3.api/4.advanced/2.kit.md b/docs/3.api/4.advanced/2.kit.md index 781f4aac28..62d147b051 100644 --- a/docs/3.api/4.advanced/2.kit.md +++ b/docs/3.api/4.advanced/2.kit.md @@ -76,6 +76,7 @@ description: Nuxt Kit provides composable utilities to help interacting with Nux [source code](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/template.ts) - `addTemplate(templateOptions)` +- `addTypeTemplate(templateOptions)` - `updateTemplates({ filter?: ResolvedNuxtTemplate => boolean })` ### Nitro diff --git a/packages/kit/src/template.ts b/packages/kit/src/template.ts index 719d870830..bce72cfd79 100644 --- a/packages/kit/src/template.ts +++ b/packages/kit/src/template.ts @@ -23,6 +23,27 @@ export function addTemplate (_template: NuxtTemplate | string) { return template } +/** + * Renders given types using lodash template during build into the project buildDir + * and register them as types. + */ +export function addTypeTemplate (_template: NuxtTemplate) { + const nuxt = useNuxt() + + const template = addTemplate(_template) + + if (!template.filename.endsWith('.d.ts')) { + throw new Error(`Invalid type template. Filename must end with .d.ts : "${template.filename}"`) + } + + // Add template to types reference + nuxt.hook('prepare:types', ({ references }) => { + references.push({ path: template.dst }) + }) + + return template +} + /** * Normalize a nuxt template object */ diff --git a/test/fixtures/basic-types/nuxt.config.ts b/test/fixtures/basic-types/nuxt.config.ts index 164fdd0fcc..19d3853d88 100644 --- a/test/fixtures/basic-types/nuxt.config.ts +++ b/test/fixtures/basic-types/nuxt.config.ts @@ -1,3 +1,5 @@ +import { addTypeTemplate } from 'nuxt/kit' + export default defineNuxtConfig({ experimental: { typedPages: true @@ -33,6 +35,12 @@ export default defineNuxtConfig({ } }, modules: [ + function () { + addTypeTemplate({ + filename: 'test.d.ts', + getContents: () => 'declare type Fromage = "cheese"' + }) + }, './modules/test', [ '~/modules/example', diff --git a/test/fixtures/basic-types/types.ts b/test/fixtures/basic-types/types.ts index 8714207a83..5daadb201a 100644 --- a/test/fixtures/basic-types/types.ts +++ b/test/fixtures/basic-types/types.ts @@ -401,3 +401,12 @@ describe('composables inference', () => { expectTypeOf().toEqualTypeOf>() }) }) + +describe('kit utilities', () => { + it('addTypeTemplate', () => { + // @ts-expect-error Fromage is 'cheese' + const _fake: Fromage = 'babybel' + + const _fromage: Fromage = 'cheese' + }) +})