`ResolvedConfig` interface
Description
ResolvedConfig is currently defined as a type, but it should be an interface.
Unlike with UserConfig, this means declaration merging cannot be taken advantage of by plugin developers. As a TypeScript developer, this makes typing resolved plugin options unnecessarily difficult. Sharing those options with other plugin developers is not possible without declaration merging, or alternatively, cumbersome and possibly out-of-date patches.
Suggested solution
// packages/vite/src/node/config.ts
interface ResolvedConfig extends Readonly<
Omit<
UserConfig,
| 'assetsInclude'
| 'build'
| 'css'
| 'optimizeDeps'
| 'plugins'
| 'worker'
> & PluginHookUtils
> {
readonly appType: AppType
readonly assetsInclude(file: string): boolean
readonly base: string
readonly build: ResolvedBuildOptions
readonly cacheDir: string
readonly command: 'build' | 'serve'
readonly configFile: string | undefined
readonly configFileDependencies: string[]
readonly createResolver(
options?: Partial<InternalResolveOptions>
): ResolveFn
readonly css: ResolvedCSSOptions
readonly env: Record<string, any>
readonly envDir: string
readonly esbuild: ESBuildOptions | false
readonly experimental: ExperimentalOptions
readonly inlineConfig: InlineConfig
readonly isProduction: boolean
readonly isWorker: boolean
readonly logger: Logger
readonly mainConfig: ResolvedConfig | null
readonly mode: string
readonly optimizeDeps: DepOptimizationOptions
readonly packageCache: PackageCache
readonly plugins: readonly Plugin[]
readonly preview: ResolvedPreviewOptions
readonly publicDir: string
readonly rawBase: string
readonly resolve: Required<ResolveOptions> & { alias: Alias[] }
readonly root: string
readonly server: ResolvedServerOptions
readonly ssr: ResolvedSSROptions
readonly worker: ResolvedWorkerOptions
}
Alternative
No response
Additional context
The documentation gives the impression ResolvedConfig is an interface. Given the docs, I wasn't sure if this should be a feature request or bug report :sweat_smile:
Validations
- [X] Follow our Code of Conduct
- [X] Read the Contributing Guidelines.
- [X] Read the docs.
- [X] Check that there isn't already an issue that request the same feature to avoid creating a duplicate.
still looking for this fix.
While your suggestion seems reasonable, can you elaborate your use case? What I'm wondering is that, since extending UserConfig would also extends ResolveConfig = Readonly<Omit<UserConfig, ..., I suppose you'd like to not extend UserConfig but only ResolveConfig?
As an example, Vitest extends UserConfig to allow defineConfig({ test: ... }) and it also affects ResolvedConfig. https://github.com/vitest-dev/vitest/blob/e03725afddfa9516d2ace1bbe160b6516fc4893c/packages/vitest/src/node/types/vite.ts#L10
If there's no breaking changes to switching to an interface, I think it'll be nice to do so. I personally tend to prefer interface but it looks like the source code now has a mix of interface and type, so it's not very consistent to side towards a pattern.
Make ResolvedConfig an interface to allow extending it.
// assume vite custom plugin interface
type CustomContext = Record<string, any>;
type ResolvedCustomConfig = Record<string, any>;
declare module "vite" {
export interface UserConfig {
__customPluginContext?:
| {
customPluginConfig?: Partial<ResolvedCustomConfig>;
}
| Partial<CustomContext>;
}
export interface ResolvedConfig {
__customPluginContext: CustomContext;
}
}
export {};
Why not use an interface? https://github.com/vitejs/vite/blob/v6.0.7/packages/vite/src/node/config.ts#L535
export interface ResolvedConfig extends Readonly<
Omit<
UserConfig,
| 'plugins'
| 'css'
| 'json'
| 'assetsInclude'
| 'optimizeDeps'
| 'worker'
| 'build'
| 'dev'
| 'environments'
| 'server'
| 'preview'
> & {
configFile: string | undefined
configFileDependencies: string[]
inlineConfig: InlineConfig
root: string
base: string
/** @internal */
decodedBase: string
/** @internal */
rawBase: string
publicDir: string
cacheDir: string
command: 'build' | 'serve'
mode: string
isWorker: boolean
// in nested worker bundle to find the main config
/** @internal */
mainConfig: ResolvedConfig | null
/** @internal list of bundle entry id. used to detect recursive worker bundle. */
bundleChain: string[]
isProduction: boolean
envDir: string
env: Record<string, any>
resolve: Required<ResolveOptions> & {
alias: Alias[]
}
plugins: readonly Plugin[]
css: ResolvedCSSOptions
json: Required<JsonOptions>
esbuild: ESBuildOptions | false
server: ResolvedServerOptions
dev: ResolvedDevEnvironmentOptions
/** @experimental */
builder: ResolvedBuilderOptions | undefined
build: ResolvedBuildOptions
preview: ResolvedPreviewOptions
ssr: ResolvedSSROptions
assetsInclude: (file: string) => boolean
logger: Logger
createResolver: (options?: Partial<InternalResolveOptions>) => ResolveFn
optimizeDeps: DepOptimizationOptions
/** @internal */
packageCache: PackageCache
worker: ResolvedWorkerOptions
appType: AppType
experimental: ExperimentalOptions
environments: Record<string, ResolvedEnvironmentOptions>
/** @internal */
fsDenyGlob: AnymatchFn
/** @internal */
safeModulePaths: Set<string>
} & PluginHookUtils
> {}
https://www.diffchecker.com/YFxRGZx8/