diff --git a/packages/wxt/e2e/tests/typescript-project.test.ts b/packages/wxt/e2e/tests/typescript-project.test.ts index ce645112..0cd78476 100644 --- a/packages/wxt/e2e/tests/typescript-project.test.ts +++ b/packages/wxt/e2e/tests/typescript-project.test.ts @@ -375,4 +375,9 @@ describe('TypeScript Project', () => { }" `); }); + + // TODO: Once a module has been published, use it here for testing - local files are never added to the .wxt/wxt.d.ts file + it.todo( + 'should add modules from NPM to the TS project if they have a configKey', + ); }); diff --git a/packages/wxt/src/core/utils/building/generate-wxt-dir.ts b/packages/wxt/src/core/utils/building/generate-wxt-dir.ts index a2585e67..b735d19a 100644 --- a/packages/wxt/src/core/utils/building/generate-wxt-dir.ts +++ b/packages/wxt/src/core/utils/building/generate-wxt-dir.ts @@ -204,6 +204,16 @@ async function writeMainDeclarationFile(references: string[]): Promise { (ref) => `/// `, ), + + // Add references to modules installed from NPM to the TS project so + // their type augmentation can update InlineConfig correctly. Local + // modules defined in /modules are already apart of the project, so + // we don't need to add them. + ...wxt.config.modules + .filter( + (module) => module.type === 'node_module' && module.configKey != null, + ) + .map((module) => `/// `), ].join('\n') + '\n', ); return filePath; diff --git a/packages/wxt/src/core/utils/building/resolve-config.ts b/packages/wxt/src/core/utils/building/resolve-config.ts index 88ff23eb..fe3fd68a 100644 --- a/packages/wxt/src/core/utils/building/resolve-config.ts +++ b/packages/wxt/src/core/utils/building/resolve-config.ts @@ -11,6 +11,7 @@ import { Logger, WxtCommand, WxtModule, + WxtModuleWithMetadata, } from '~/types'; import path from 'node:path'; import { createFsCache } from '~/core/utils/cache'; @@ -380,15 +381,21 @@ export async function mergeBuilderConfig( export async function resolveWxtModules( modulesDir: string, modules: string[] = [], -): Promise[]> { - // Resolve NPM packages - const npmModules = await Promise.all( +): Promise[]> { + // Resolve node_modules modules + const npmModules = await Promise.all>( modules.map(async (moduleId) => { - const mod = await import(/* @vite-ignore */ moduleId); + const mod: { default: WxtModule } = await import( + /* @vite-ignore */ moduleId + ); if (mod.default == null) { throw Error('Module missing default export: ' + moduleId); } - return mod.default; + return { + ...mod.default, + type: 'node_module', + id: moduleId, + }; }), ); @@ -397,10 +404,11 @@ export async function resolveWxtModules( cwd: modulesDir, onlyFiles: true, }).catch(() => []); - const localModules = await Promise.all( + const localModules = await Promise.all>( localModulePaths.map(async (file) => { + const absolutePath = normalizePath(path.resolve(modulesDir, file)); const { config } = await loadConfig>({ - configFile: path.resolve(modulesDir, file), + configFile: absolutePath, globalRc: false, rcFile: false, packageJson: false, @@ -413,10 +421,12 @@ export async function resolveWxtModules( ); // Add name based on filename config.name ??= file; - return config; + return { + ...config, + type: 'local', + id: absolutePath, + }; }), ); - - // Execute modules return [...npmModules, ...localModules]; } diff --git a/packages/wxt/src/types/index.ts b/packages/wxt/src/types/index.ts index a996fce7..3bf0063b 100644 --- a/packages/wxt/src/types/index.ts +++ b/packages/wxt/src/types/index.ts @@ -1185,7 +1185,7 @@ export interface ResolvedConfig { reloadCommand: string | false; }; hooks: NestedHooks; - modules: WxtModule[]; + modules: WxtModuleWithMetadata[]; /** * An array of string to import plugins from. These paths should be * resolvable by vite, and they should `export default defineWxtPlugin(...)`. @@ -1325,6 +1325,12 @@ export interface WxtModule { setup?: WxtModuleSetup; } +export interface WxtModuleWithMetadata + extends WxtModule { + type: 'local' | 'node_module'; + id: string; +} + export interface ResolvedPublicFile { /** * The absolute path to the file that will be copied to the output directory.