diff --git a/packages/kit/src/module/define.ts b/packages/kit/src/module/define.ts index 061362370f..ff2a56d2d4 100644 --- a/packages/kit/src/module/define.ts +++ b/packages/kit/src/module/define.ts @@ -3,7 +3,7 @@ import { performance } from 'node:perf_hooks' import { defu } from 'defu' import { applyDefaults } from 'untyped' import { dirname } from 'pathe' -import type { ModuleDefinition, ModuleOptions, ModuleSetupInstallResult, ModuleSetupReturn, Nuxt, NuxtModule, NuxtOptions, ResolvedModuleOptions, ResolvedNuxtTemplate } from '@nuxt/schema' +import type { ModuleDefinition, ModuleOptions, ModuleSetupReturn, Nuxt, NuxtModule, NuxtOptions, ResolvedNuxtTemplate } from '@nuxt/schema' import { logger } from '../logger' import { nuxtCtx, tryUseNuxt, useNuxt } from '../context' import { checkNuxtCompatibility, isNuxt2 } from '../compatibility' @@ -13,53 +13,28 @@ import { compileTemplate, templateUtils } from '../internal/template' * Define a Nuxt module, automatically merging defaults with user provided options, installing * any hooks that are provided, and calling an optional setup function for full control. */ -export function defineNuxtModule (definition: ModuleDefinition | NuxtModule): NuxtModule - -export function defineNuxtModule (): { - with: > ( - definition: ModuleDefinition | NuxtModule - ) => NuxtModule -} - -export function defineNuxtModule (definition?: ModuleDefinition | NuxtModule) { - if (definition) { - return _defineNuxtModule(definition) - } - - return { - with: >( - definition: ModuleDefinition | NuxtModule, - ) => _defineNuxtModule(definition), - } -} - -function _defineNuxtModule> (definition: ModuleDefinition | NuxtModule): NuxtModule { - if (typeof definition === 'function') { return _defineNuxtModule({ setup: definition }) } +export function defineNuxtModule (definition: ModuleDefinition | NuxtModule): NuxtModule { + if (typeof definition === 'function') { return defineNuxtModule({ setup: definition }) } // Normalize definition and meta - const module: ModuleDefinition & Required, 'meta'>> = defu(definition, { meta: {} }) - - module.meta.configKey ||= module.meta.name + const module: ModuleDefinition & Required, 'meta'>> = defu(definition, { meta: {} }) + if (module.meta.configKey === undefined) { + module.meta.configKey = module.meta.name + } // Resolves module options from inline options, [configKey] in nuxt.config, defaults and schema - async function getOptions (inlineOptions?: Partial, nuxt: Nuxt = useNuxt()): Promise> { - const nuxtConfigOptionsKey = module.meta.configKey || module.meta.name - - const nuxtConfigOptions: Partial = nuxtConfigOptionsKey && nuxtConfigOptionsKey in nuxt.options ? nuxt.options[ nuxtConfigOptionsKey] : {} - - const optionsDefaults: TOptionsDefaults = module.defaults instanceof Function ? module.defaults(nuxt) : module.defaults ?? {} - - let options: ResolvedModuleOptions = defu(inlineOptions, nuxtConfigOptions, optionsDefaults) - + async function getOptions (inlineOptions?: OptionsT, nuxt: Nuxt = useNuxt()) { + const configKey = module.meta.configKey || module.meta.name! + const _defaults = module.defaults instanceof Function ? module.defaults(nuxt) : module.defaults + let _options = defu(inlineOptions, nuxt.options[configKey as keyof NuxtOptions], _defaults) as OptionsT if (module.schema) { - options = await applyDefaults(module.schema, options) as any + _options = await applyDefaults(module.schema, _options) as OptionsT } - - return Promise.resolve(options) + return Promise.resolve(_options) } // Module format is always a simple function - async function normalizedModule (this: any, inlineOptions: Partial, nuxt: Nuxt): Promise { + async function normalizedModule (this: any, inlineOptions: OptionsT, nuxt: Nuxt) { if (!nuxt) { nuxt = tryUseNuxt() || this.nuxt /* invoked by nuxt 2 */ } @@ -112,7 +87,7 @@ function _defineNuxtModule { + return defu(res, { timings: { setup: setupTime, }, @@ -123,7 +98,7 @@ function _defineNuxtModule Promise.resolve(module.meta) normalizedModule.getOptions = getOptions - return > normalizedModule + return normalizedModule as NuxtModule } // -- Nuxt 2 compatibility shims -- diff --git a/packages/kit/src/module/install.ts b/packages/kit/src/module/install.ts index 153aab0bd8..beecd62aef 100644 --- a/packages/kit/src/module/install.ts +++ b/packages/kit/src/module/install.ts @@ -31,7 +31,7 @@ export async function installModule< isNuxt2() // @ts-expect-error Nuxt 2 `moduleContainer` is not typed ? await nuxtModule.call(nuxt.moduleContainer, inlineOptions, nuxt) - : await nuxtModule(inlineOptions || {}, nuxt) + : await nuxtModule(inlineOptions, nuxt) ) ?? {} if (res === false /* setup aborted */) { return diff --git a/packages/schema/src/types/module.ts b/packages/schema/src/types/module.ts index 13b42365aa..9b92d6a93e 100644 --- a/packages/schema/src/types/module.ts +++ b/packages/schema/src/types/module.ts @@ -1,4 +1,3 @@ -import type { Defu } from 'defu' import type { NuxtHooks } from './hooks' import type { Nuxt } from './nuxt' import type { NuxtCompatibility } from './compatibility' @@ -27,7 +26,8 @@ export interface ModuleMeta { /** The options received. */ export type ModuleOptions = Record -export type ModuleSetupInstallResult = { +/** Optional result for nuxt modules */ +export interface ModuleSetupReturn { /** * Timing information for the initial setup */ @@ -39,37 +39,19 @@ export type ModuleSetupInstallResult = { } type Awaitable = T | Promise +type _ModuleSetupReturn = Awaitable -type Prettify = { - [K in keyof T]: T[K]; -} & {} - -export type ModuleSetupReturn = Awaitable - -export type ResolvedModuleOptions> = Prettify< - Defu< - Partial, - [Partial, TOptionsDefaults] - > -> - -/** Module definition passed to 'defineNuxtModule(...)' or 'defineNuxtModule().with(...)'. */ -export interface ModuleDefinition< - TOptions extends ModuleOptions, - TOptionsDefaults extends Partial = Partial, -> { +/** Input module passed to defineNuxtModule. */ +export interface ModuleDefinition { meta?: ModuleMeta - defaults?: TOptionsDefaults | ((nuxt: Nuxt) => TOptionsDefaults) - schema?: TOptions + defaults?: T | ((nuxt: Nuxt) => T) + schema?: T hooks?: Partial - setup?: (this: void, resolvedOptions: ResolvedModuleOptions, nuxt: Nuxt) => ModuleSetupReturn + setup?: (this: void, resolvedOptions: T, nuxt: Nuxt) => _ModuleSetupReturn } -export interface NuxtModule< - TOptions extends ModuleOptions = ModuleOptions, - TOptionsDefaults extends Partial = Partial, -> { - (this: void, resolvedOptions: ResolvedModuleOptions, nuxt: Nuxt): ModuleSetupReturn - getOptions?: (inlineOptions?: Partial, nuxt?: Nuxt) => Promise> +export interface NuxtModule { + (this: void, inlineOptions: T, nuxt: Nuxt): _ModuleSetupReturn + getOptions?: (inlineOptions?: T, nuxt?: Nuxt) => Promise getMeta?: () => Promise } diff --git a/test/fixtures/basic-types/types.ts b/test/fixtures/basic-types/types.ts index 343ecb1a88..dad52b4f6a 100644 --- a/test/fixtures/basic-types/types.ts +++ b/test/fixtures/basic-types/types.ts @@ -5,7 +5,6 @@ import type { NavigationFailure, RouteLocationNormalized, RouteLocationRaw, Rout import type { AppConfig, RuntimeValue, UpperSnakeCase } from 'nuxt/schema' import { defineNuxtConfig } from 'nuxt/config' -import { defineNuxtModule } from 'nuxt/kit' import { callWithNuxt, isVue3 } from '#app' import type { NuxtError } from '#app' import type { NavigateToOptions } from '#app/composables/router' @@ -243,23 +242,6 @@ describe('modules', () => { // @ts-expect-error we want to ensure we throw type error on invalid key defineNuxtConfig({ undeclaredKey: { other: false } }) }) - - it('correctly typed resolved options in defineNuxtModule setup using `.with()`', () => { - defineNuxtModule<{ - foo?: string - baz: number - }>().with({ - defaults: { - foo: 'bar', - }, - setup: (resolvedOptions) => { - expectTypeOf(resolvedOptions).toEqualTypeOf<{ - foo: string - baz?: number | undefined - }>() - }, - }) - }) }) describe('nuxtApp', () => {