From 0d84300218875c33d733b9b03021a7fe1a6f9ff4 Mon Sep 17 00:00:00 2001 From: Inesh Bose Date: Mon, 10 Jun 2024 09:54:40 +0100 Subject: [PATCH] feat(kit): support typed module options in `installModule` (#26744) --- packages/kit/src/module/install.ts | 15 ++++++++++++--- packages/nuxt/src/core/templates.ts | 20 ++++++++++++++------ test/fixtures/basic-types/nuxt.config.ts | 11 ++++++++++- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/packages/kit/src/module/install.ts b/packages/kit/src/module/install.ts index b17cee47ca..cc047aa58a 100644 --- a/packages/kit/src/module/install.ts +++ b/packages/kit/src/module/install.ts @@ -1,5 +1,5 @@ import { existsSync, promises as fsp, lstatSync } from 'node:fs' -import type { ModuleMeta, Nuxt, NuxtModule } from '@nuxt/schema' +import type { ModuleMeta, Nuxt, NuxtConfig, NuxtModule } from '@nuxt/schema' import { dirname, isAbsolute, join, resolve } from 'pathe' import { defu } from 'defu' import { isNuxt2 } from '../compatibility' @@ -12,7 +12,10 @@ import { logger } from '../logger' const NODE_MODULES_RE = /[/\\]node_modules[/\\]/ /** Installs a module on a Nuxt instance. */ -export async function installModule (moduleToInstall: string | NuxtModule, inlineOptions?: any, nuxt: Nuxt = useNuxt()) { +export async function installModule< + T extends string | NuxtModule, + Config extends Extract[number], [T, any]>, +> (moduleToInstall: T, inlineOptions?: [Config] extends [never] ? any : Config[1], nuxt: Nuxt = useNuxt()) { const { nuxtModule, buildTimeModuleMeta } = await loadNuxtModuleInstance(moduleToInstall, nuxt) const localLayerModuleDirs = new Set() @@ -43,10 +46,16 @@ export async function installModule (moduleToInstall: string | NuxtModule, inlin } nuxt.options._installedModules = nuxt.options._installedModules || [] + const entryPath = typeof moduleToInstall === 'string' ? resolveAlias(moduleToInstall) : undefined + + if (typeof moduleToInstall === 'string' && entryPath !== moduleToInstall) { + buildTimeModuleMeta.rawPath = moduleToInstall + } + nuxt.options._installedModules.push({ meta: defu(await nuxtModule.getMeta?.(), buildTimeModuleMeta), timings: res.timings, - entryPath: typeof moduleToInstall === 'string' ? resolveAlias(moduleToInstall) : undefined, + entryPath, }) } diff --git a/packages/nuxt/src/core/templates.ts b/packages/nuxt/src/core/templates.ts index 49a89f8943..0395ff2cbb 100644 --- a/packages/nuxt/src/core/templates.ts +++ b/packages/nuxt/src/core/templates.ts @@ -156,21 +156,29 @@ export const schemaTemplate: NuxtTemplate = { const relativeRoot = relative(resolve(nuxt.options.buildDir, 'types'), nuxt.options.rootDir) const getImportName = (name: string) => (name[0] === '.' ? './' + join(relativeRoot, name) : name).replace(/\.\w+$/, '') - const modules = moduleInfo.map(meta => [genString(meta.configKey), getImportName(meta.importName)]) + const modules = moduleInfo.map(meta => [genString(meta.configKey), getImportName(meta.importName), meta]) const privateRuntimeConfig = Object.create(null) for (const key in nuxt.options.runtimeConfig) { if (key !== 'public') { privateRuntimeConfig[key] = nuxt.options.runtimeConfig[key] } } - return [ - 'import { NuxtModule, RuntimeConfig } from \'nuxt/schema\'', - 'declare module \'nuxt/schema\' {', - ' interface NuxtConfig {', + const moduleOptionsInterface = [ ...modules.map(([configKey, importName]) => ` [${configKey}]?: typeof ${genDynamicImport(importName, { wrapper: false })}.default extends NuxtModule ? Partial : Record`, ), - modules.length > 0 ? ` modules?: (undefined | null | false | NuxtModule | string | [NuxtModule | string, Record] | ${modules.map(([configKey, importName]) => `[${genString(importName)}, Exclude]`).join(' | ')})[],` : '', + modules.length > 0 ? ` modules?: (undefined | null | false | NuxtModule | string | [NuxtModule | string, Record] | ${modules.map(([configKey, importName, meta]) => `[${genString(meta?.rawPath || importName)}, Exclude]`).join(' | ')})[],` : '', + ] + return [ + 'import { NuxtModule, RuntimeConfig } from \'@nuxt/schema\'', + 'declare module \'@nuxt/schema\' {', + ' interface NuxtConfig {', + moduleOptionsInterface, + ' }', + '}', + 'declare module \'nuxt/schema\' {', + ' interface NuxtConfig {', + moduleOptionsInterface, ' }', generateTypes(await resolveSchema(privateRuntimeConfig as Record), { diff --git a/test/fixtures/basic-types/nuxt.config.ts b/test/fixtures/basic-types/nuxt.config.ts index 983039bcdf..3278402fa3 100644 --- a/test/fixtures/basic-types/nuxt.config.ts +++ b/test/fixtures/basic-types/nuxt.config.ts @@ -1,4 +1,4 @@ -import { addTypeTemplate } from 'nuxt/kit' +import { addTypeTemplate, installModule } from 'nuxt/kit' export default defineNuxtConfig({ experimental: { @@ -54,6 +54,15 @@ export default defineNuxtConfig({ filename: 'test.d.ts', getContents: () => 'declare type Fromage = "cheese"', }) + function _test () { + installModule('~/modules/example', { + typeTest (val) { + // @ts-expect-error module type defines val as boolean + const b: string = val + return !!b + }, + }) + } }, './modules/test', [