diff --git a/packages/kit/src/module/define.ts b/packages/kit/src/module/define.ts index c1b75c1091..2a2ac6f9fe 100644 --- a/packages/kit/src/module/define.ts +++ b/packages/kit/src/module/define.ts @@ -1,8 +1,9 @@ import { promises as fsp } from 'node:fs' +import { performance } from 'node:perf_hooks' import { defu } from 'defu' import { applyDefaults } from 'untyped' import { dirname } from 'pathe' -import type { Nuxt, NuxtModule, ModuleOptions, ModuleDefinition, NuxtOptions, ResolvedNuxtTemplate } from '@nuxt/schema' +import type { Nuxt, NuxtModule, ModuleOptions, ModuleSetupReturn, ModuleDefinition, NuxtOptions, ResolvedNuxtTemplate } from '@nuxt/schema' import { logger } from '../logger' import { useNuxt, nuxtCtx, tryUseNuxt } from '../context' import { isNuxt2, checkNuxtCompatibility } from '../compatibility' @@ -67,7 +68,27 @@ export function defineNuxtModule (definition: Mo } // Call setup - await definition.setup?.call(null as any, _options, nuxt) + const mark = performance.mark() + const res = await definition.setup?.call(null as any, _options, nuxt) ?? {} + const perf = performance.measure(`nuxt:module:${uniqueKey || (Math.round(Math.random() * 10000))}`, mark.name) + const setupTime = Math.round((perf.duration * 100)) / 100 + + // Measure setup time + if (setupTime > 5000) { + logger.warn(`Slow module \`${uniqueKey || ''}\` took \`${setupTime}ms\` to setup.`) + } else if (nuxt.options.debug) { + logger.info(`Module \`${uniqueKey || ''}\` took \`${setupTime}ms\` to setup.`) + } + + // Check if module is ignored + if (res === false) { return false } + + // Return module install result + return defu(res, { + timings: { + setup: setupTime + } + }) } // Define getters for options and meta diff --git a/packages/kit/src/module/install.ts b/packages/kit/src/module/install.ts index 7aa9fcc05c..d978cfff48 100644 --- a/packages/kit/src/module/install.ts +++ b/packages/kit/src/module/install.ts @@ -9,7 +9,7 @@ export async function installModule (moduleToInstall: string | NuxtModule, _inli const { nuxtModule, inlineOptions } = await normalizeModule(moduleToInstall, _inlineOptions) // Call module - const res = await nuxtModule(inlineOptions, nuxt) + const res = await nuxtModule(inlineOptions, nuxt) ?? {} if (res === false /* setup aborted */) { return } @@ -21,6 +21,7 @@ export async function installModule (moduleToInstall: string | NuxtModule, _inli nuxt.options._installedModules = nuxt.options._installedModules || [] nuxt.options._installedModules.push({ meta: await nuxtModule.getMeta?.(), + timings: res.timings, entryPath: typeof moduleToInstall === 'string' ? resolveAlias(moduleToInstall) : undefined }) } diff --git a/packages/schema/src/types/module.ts b/packages/schema/src/types/module.ts index b14e246cb8..2a105fed98 100644 --- a/packages/schema/src/types/module.ts +++ b/packages/schema/src/types/module.ts @@ -26,19 +26,32 @@ export interface ModuleMeta { /** The options received. */ export type ModuleOptions = Record +/** Optional result for nuxt modules */ +export interface ModuleSetupReturn { + /** + * Timing information for the initial setup + */ + timings?: { + /** Total time took for module setup in ms */ + setup?: number + [key: string]: number | undefined + } +} + +type Awaitable = T | Promise +type _ModuleSetupReturn = Awaitable + /** Input module passed to defineNuxtModule. */ export interface ModuleDefinition { meta?: ModuleMeta defaults?: T | ((nuxt: Nuxt) => T) schema?: T hooks?: Partial - setup?: (this: void, resolvedOptions: T, nuxt: Nuxt) => void | Promise + setup?: (this: void, resolvedOptions: T, nuxt: Nuxt) => _ModuleSetupReturn } -/** Nuxt modules are always a simple function. */ -type Awaitable = T | Promise export interface NuxtModule { - (this: void, inlineOptions: T, nuxt: Nuxt): Awaitable + (this: void, inlineOptions: T, nuxt: Nuxt): _ModuleSetupReturn getOptions?: (inlineOptions?: T, nuxt?: Nuxt) => Promise getMeta?: () => Promise }