diff --git a/packages/kit/src/template.ts b/packages/kit/src/template.ts index d0f85b731a..b635702a80 100644 --- a/packages/kit/src/template.ts +++ b/packages/kit/src/template.ts @@ -48,8 +48,12 @@ export function addServerTemplate (template: NuxtServerTemplate) { /** * Renders given types during build to disk in the project `buildDir` * and register them as types. + * + * You can pass a second context object to specify in which context the type should be added. + * + * If no context object is passed, then it will only be added to the nuxt context. */ -export function addTypeTemplate (_template: NuxtTypeTemplate) { +export function addTypeTemplate (_template: NuxtTypeTemplate, context?: { nitro?: boolean, nuxt?: boolean }) { const nuxt = useNuxt() const template = addTemplate(_template) @@ -59,9 +63,16 @@ export function addTypeTemplate (_template: NuxtTypeTemplate) { } // Add template to types reference - nuxt.hook('prepare:types', ({ references }) => { - references.push({ path: template.dst }) - }) + if (!context || context.nuxt) { + nuxt.hook('prepare:types', ({ references }) => { + references.push({ path: template.dst }) + }) + } + if (context?.nitro) { + nuxt.hook('nitro:prepare:types', ({ references }) => { + references.push({ path: template.dst }) + }) + } return template } diff --git a/packages/nuxt/src/core/templates.ts b/packages/nuxt/src/core/templates.ts index 197204a14d..a53e4e1b18 100644 --- a/packages/nuxt/src/core/templates.ts +++ b/packages/nuxt/src/core/templates.ts @@ -1,13 +1,13 @@ import { existsSync } from 'node:fs' import { genArrayFromRaw, genDynamicImport, genExport, genImport, genObjectFromRawEntries, genSafeVariableName, genString } from 'knitwork' -import { join, relative, resolve } from 'pathe' +import { isAbsolute, join, relative, resolve } from 'pathe' import type { JSValue } from 'untyped' import { generateTypes, resolveSchema } from 'untyped' import escapeRE from 'escape-string-regexp' import { hash } from 'ohash' import { camelCase } from 'scule' import { filename } from 'pathe/utils' -import type { NuxtOptions, NuxtTemplate } from 'nuxt/schema' +import type { NuxtOptions, NuxtTemplate, TSReference } from 'nuxt/schema' import type { Nitro } from 'nitro/types' import { annotatePlugins, checkForCircularDependencies } from './app' @@ -317,10 +317,38 @@ export const middlewareTemplate: NuxtTemplate = { }, } +function renderAttr (key: string, value?: string) { + return value ? `${key}="${value}"` : '' +} + +function renderAttrs (obj: Record) { + const attrs: string[] = [] + for (const key in obj) { + attrs.push(renderAttr(key, obj[key])) + } + return attrs.join(' ') +} + export const nitroSchemaTemplate: NuxtTemplate = { filename: 'types/nitro-nuxt.d.ts', - getContents () { + async getContents ({ nuxt }) { + const references = [] as TSReference[] + const declarations = [] as string[] + await nuxt.callHook('nitro:prepare:types', { references, declarations }) + + const sourceDir = join(nuxt.options.buildDir, 'types') + const lines = [ + ...references.map((ref) => { + if ('path' in ref && isAbsolute(ref.path)) { + ref.path = relative(sourceDir, ref.path) + } + return `/// ` + }), + ...declarations, + ] + return /* typescript */` +${lines.join('\n')} /// import type { RuntimeConfig } from 'nuxt/schema' diff --git a/packages/nuxt/src/pages/module.ts b/packages/nuxt/src/pages/module.ts index 6a27df6bb9..841ab91e6d 100644 --- a/packages/nuxt/src/pages/module.ts +++ b/packages/nuxt/src/pages/module.ts @@ -163,7 +163,7 @@ export default defineNuxtModule({ '}', 'export {}', ].join('\n'), - }) + }, { nuxt: true, nitro: true }) addComponent({ name: 'NuxtPage', priority: 10, // built-in that we do not expect the user to override @@ -580,6 +580,16 @@ export default defineNuxtModule({ ' middleware?: MiddlewareKey | NavigationGuard | Array', ' }', '}', + ].join('\n') + }, + }) + + addTypeTemplate({ + filename: 'types/nitro-middleware.d.ts', + getContents: ({ app }) => { + const namedMiddleware = app.middleware.filter(mw => !mw.global) + return [ + `export type MiddlewareKey = ${namedMiddleware.map(mw => genString(mw.name)).join(' | ') || 'never'}`, 'declare module \'nitropack/types\' {', ' interface NitroRouteConfig {', ' appMiddleware?: MiddlewareKey | MiddlewareKey[] | Record', @@ -592,7 +602,7 @@ export default defineNuxtModule({ '}', ].join('\n') }, - }) + }, { nuxt: true, nitro: true }) addTypeTemplate({ filename: 'types/layouts.d.ts', diff --git a/packages/schema/src/types/hooks.ts b/packages/schema/src/types/hooks.ts index 8ae22441c9..afffd90eb7 100644 --- a/packages/schema/src/types/hooks.ts +++ b/packages/schema/src/types/hooks.ts @@ -257,6 +257,12 @@ export interface NuxtHooks { 'components:extend': (components: Component[]) => HookResult // Nitropack + /** + * Called before Nitro writes `.nuxt/tsconfig.server.json`, allowing addition of custom references and declarations. + * @param options Objects containing `references`, `declarations` + * @returns Promise + */ + 'nitro:prepare:types': (options: { references: TSReference[], declarations: string[] }) => HookResult /** * Called before initializing Nitro, allowing customization of Nitro's configuration. * @param nitroConfig The nitro config to be extended diff --git a/test/fixtures/basic-types/nuxt.config.ts b/test/fixtures/basic-types/nuxt.config.ts index 4c1416ce87..820fbcdc2a 100644 --- a/test/fixtures/basic-types/nuxt.config.ts +++ b/test/fixtures/basic-types/nuxt.config.ts @@ -10,7 +10,7 @@ export default defineNuxtConfig({ addTypeTemplate({ filename: 'test.d.ts', getContents: () => 'declare type Fromage = "cheese"', - }) + }, { nuxt: true, nitro: true }) function _test () { installModule('~/modules/example', { typeTest (val) { diff --git a/test/fixtures/basic-types/server/type-context.ts b/test/fixtures/basic-types/server/type-context.ts new file mode 100644 index 0000000000..8994c5a431 --- /dev/null +++ b/test/fixtures/basic-types/server/type-context.ts @@ -0,0 +1,4 @@ +// @ts-expect-error Fromage is 'cheese' +const _fake: Fromage = 'babybel' + +const _fromage: Fromage = 'cheese'