Don't require WXT modules to define their own module augmentation
I attempted to set this up, but ran into an error as soon as I tried building a WXT module that adds types to the WXT config.
$ pnpm tsdown ℹ tsdown v0.10.0 powered by rolldown v1.0.0-beta.8-commit.151352b tsdown 6:04:57 PM [tsdown 6:04:57 PM] ℹ Using tsdown config: /Users/aklinker1/Development/github.com/wxt-dev/wxt/packages/analytics/tsdown.config.ts [tsdown 6:04:57 PM] ℹ entry: modules/analytics/index.ts, modules/analytics/client.ts, modules/analytics/background-plugin.ts, modules/analytics/types.ts, modules/analytics/providers/google-analytics-4.ts, modules/analytics/providers/umami.ts ℹ Using tsconfig: tsconfig.json tsdown 6:04:57 PM ℹ [ESM] Build start tsdown 6:04:57 PM FATAL Build failed with 2 errors: tsdown 6:04:58 PM [UNRESOLVED_IMPORT] Error: Could not resolve '../pkg' in ../../node_modules/.pnpm/[email protected]/node_modules/lightningcss/node/index.js ╭─[ ../../node_modules/.pnpm/[email protected]/node_modules/lightningcss/node/index.js:17:28 ] │ 17 │ module.exports = require(../pkg); │ ────┬─── │ ╰───── Module not found. ────╯ [UNLOADABLE_DEPENDENCY] Error: Could not load ../../node_modules/.pnpm/[email protected]/node_modules/fsevents/fsevents.node ╭─[ ../../node_modules/.pnpm/[email protected]/node_modules/fsevents/fsevents.js:13:24 ] │ 13 │ const Native = require("./fsevents.node"); │ ────────┬──────── │ ╰────────── stream did not contain valid UTF-8 ────╯ at normalizeErrors (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected]/node_modules/rolldown/dist/shared/src-DvIQg0VF.mjs:935:18) at handleOutputErrors (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected]/node_modules/rolldown/dist/shared/src-DvIQg0VF.mjs:1758:34) at transformToRollupOutput (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected]/node_modules/rolldown/dist/shared/src-DvIQg0VF.mjs:1752:2) at RolldownBuild.write (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected]/node_modules/rolldown/dist/shared/src-DvIQg0VF.mjs:4168:11) at async build (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected]/node_modules/rolldown/dist/shared/src-DvIQg0VF.mjs:4210:22) at async /Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/tsdown/dist/index.js:536:5 at async Promise.all (index 0) at async rebuild (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/tsdown/dist/index.js:527:3) at async buildSingle (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/tsdown/dist/index.js:519:2) at async Promise.all (index 0) at async build (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/tsdown/dist/index.js:491:19) at async CAC.<anonymous> (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/tsdown/dist/run.js:19:2) at async runCLI (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/tsdown/dist/run.js:37:3)This is caused by the weird circular dependency between WXT and the module agumentation :/
The solution is simple: don't do module augmentation. instead, when a config key is listed in the module options, WXT should generate the module augmentation instead of requiring the module author to include it in the module code.
import { defineWxtModule } from 'wxt/modules'; - import 'wxt'; - export interface MyModuleOptions { + export interface ModuleOptions { // Add your build-time options here... } - declare module 'wxt' { - export interface InlineConfig { - // Add types for the "myModule" key in wxt.config.ts - myModule: MyModuleOptions; - } - } export default defineWxtModule<AnalyticModuleOptions>({ configKey: 'myModule', // Build time config is available via the second argument of setup setup(wxt, options) { console.log(options); }, });Then in the project's
.wxtdirectory, we generate something like the following:// .wxt/module-build-options.d.ts import 'wxt'; declare module 'wxt' { export interface InlineConfig { myModule: import("my-module").ModuleConfig } }Good news, this shouldn't be a breaking change! Regardless of if we migrate to
tsdown, this is something that should be done.
Originally posted by @aklinker1 in #1628
We also need to support runtime config. Should be easy to add to the generated type declaration.
// .wxt/module-runtime-options.d.ts
import 'wxt/utils/define-app-config';
declare module 'wxt/utils/define-app-config' {
export interface WxtAppConfig {
myModule: import("my-module").ModuleRuntimeOptions;
}
}
Main problem will be detecting if ModuleOptions or ModuleRuntimeOptions types are included in a module... Ideally, we don't have to detect anything. Just check if a configKey or runtimeConfigKey exists.
Other thing to consider is how to make options optional. Maybe type the keys as configKey?: boolean | { name: string, optional: boolean }... How does nuxt do it?
Nuxt recommends to generate type(or any file) via addTypeTemplate, or via prepare:types hook.
If we have to support runtime config, How about adding addTypeTemplate and change docs?
Nuxt recommends to generate type(or any file) via
addTypeTemplate, or viaprepare:typeshook.If we have to support runtime config, How about adding
addTypeTemplateand change docs?
That's a good idea. For runtime module options, it's probably better for the module author to generate a module augmentation in the .wxt directory.
I was struggling with how to automatically generate the runtime augmentation, do we use a specific export name? But if the type is exported from the module, it could cause node types to leak into runtime code... But this will solve all those issues!
That said, I'll hold off on adding a full template system like Nuxt for now. It's possible to do this in the prepare:types hook, so that should be good enough for now.