Migration Guide: vite-plugin-dts v4 → v5 / unplugin-dts v1

May 6, 2026 · View on GitHub

English | 中文

v5 is a major architectural upgrade. The core logic has been extracted into unplugin-dts, while vite-plugin-dts has become a thin compatibility layer that simply re-exports unplugin-dts/vite. At the same time, the plugin officially supports Rollup, Rolldown, Webpack, Rspack, and Esbuild.

If you are using Vite only, you can continue using vite-plugin-dts. Its public API is highly compatible with v4. However, it is recommended to use unplugin-dts directly for new projects.


Table of Contents


Package Installation Changes

Scenariov4v5
Vite (compatibility)pnpm i -D vite-plugin-dtspnpm i -D vite-plugin-dts
Vite (recommended)pnpm i -D unplugin-dts
Rollup / Rolldown / Webpack / Rspack / Esbuildvite-plugin-dts (limited support)pnpm i -D unplugin-dts
Using bundleTypesBuilt-in API Extractorpnpm i -D @microsoft/api-extractor
Vue projectsBuilt-in Vue language corepnpm i -D @vue/language-core

Note: Both @microsoft/api-extractor and @vue/language-core are peerDependencies (optional: true) in v5. Please install them manually when needed.


Bundler Import Paths

// ========== Vite ==========
// v4
import dts from 'vite-plugin-dts'

// v5 (compatible, still works)
import dts from 'vite-plugin-dts'

// v5 (recommended)
import dts from 'unplugin-dts/vite'

// ========== Rollup ==========
// v4 (only supported via vite-plugin-dts Rollup compatibility mode)
import dts from 'vite-plugin-dts'

// v5
import dts from 'unplugin-dts/rollup'

// ========== Rolldown (new) ==========
import dts from 'unplugin-dts/rolldown'

// ========== Webpack (new) ==========
import dts from 'unplugin-dts/webpack'

// ========== Rspack (new) ==========
import dts from 'unplugin-dts/rspack'

// ========== Esbuild (new) ==========
import dts from 'unplugin-dts/esbuild'

Breaking Changes

rollupTypesbundleTypes

This is the most significant naming change. All API Extractor related configurations have been consolidated under bundleTypes.

import { defineConfig } from 'vite'

// v4
export default defineConfig({
  plugins: [
    dts({
      rollupTypes: true,
      bundledPackages: ['vue'],
      rollupConfig: { /* ... */ },
      rollupOptions: { /* ... */ },
    }),
  ],
})

// v5
export default defineConfig({
  plugins: [
    dts({
      bundleTypes: {
        bundledPackages: ['vue'],
        extractorConfig: { /* ... */ },      // formerly rollupConfig
        invokeOptions: { /* ... */ },        // formerly rollupOptions
        configPath: './api-extractor.json',  // new
      },
    }),
  ],
})

// Boolean shorthand still works
export default defineConfig({
  plugins: [dts({ bundleTypes: true })],
})

Flattening of rollupConfig / rollupOptions / bundledPackages

v4v5
rollupTypesbundleTypes (boolean | object)
bundledPackagesbundleTypes.bundledPackages
rollupConfigbundleTypes.extractorConfig
rollupOptionsbundleTypes.invokeOptions

Type hint: The type of bundleTypes.extractorConfig has also been renamed from RollupConfig to BundleConfig, with the omission of internal fields like extends, projectFolder, mainEntryPointFilePath, and bundledPackages.

outDiroutDirs

v5 renames outDir to outDirs and supports richer output configurations: strings, arrays, OutDirConfig objects, or mixed arrays.

import { defineConfig } from 'vite'

// v4
export default defineConfig({
  plugins: [
    dts({
      outDir: 'dist',
    }),
  ],
})

// v5 (string, equivalent)
export default defineConfig({
  plugins: [
    dts({
      outDirs: 'dist',
    }),
  ],
})

// v5 (multiple directories + module format, new capability)
export default defineConfig({
  plugins: [
    dts({
      outDirs: [
        'dist',                                       // .d.ts
        { dir: 'dist-cjs', moduleFormat: 'cjs' },     // .d.cts
        { dir: 'dist-esm', moduleFormat: 'esm' },     // .d.mts
      ],
    }),
  ],
})

Removal of logLevel

The logLevel option from v4 has been removed. v5 no longer depends on Vite's LogLevel type; log output is managed internally by the plugin. To control logging, please use your bundler's own log level or environment variables (e.g., DEBUG=unplugin-dts).


New Options and Capabilities

Multi-Bundler Support

This is the core upgrade in v5. The same configuration works seamlessly across 6 bundlers:

  • unplugin-dts/vite
  • unplugin-dts/rollup
  • unplugin-dts/rolldown
  • unplugin-dts/webpack
  • unplugin-dts/rspack
  • unplugin-dts/esbuild

The plugin automatically infers root and outDirs from each bundler's entry, output.path, outdir, build.outDir, etc., reducing duplicate configuration.

processor: Explicit TS/Vue Program Processor

v5 introduces the processor option to control how the TypeScript Program is created:

import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    dts({
      processor: 'vue',  // or 'ts' (default)
    }),
  ],
})
  • When not specified, the plugin automatically scans source files for .vue files. If found, it automatically uses the 'vue' processor.
  • For pure TypeScript projects, keep the default 'ts'.
  • Note for Vue projects: v5 requires installing @vue/language-core separately.

aliases: Custom Path Aliases

In addition to automatically resolving aliases from tsconfig.json paths, v5 allows passing custom aliases directly:

import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    dts({
      aliases: [
        { find: /^@\//, replacement: './src/' },
        { find: 'old-pkg', replacement: 'new-pkg' },
      ],
      // Or object form
      // aliases: { '@/*': './src/*', 'old-pkg': 'new-pkg' }
    }),
  ],
})

afterBootstrap Hook

Called after the Runtime (TS Program, resolvers, aliases, etc.) is created, but before type checking and emit:

import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    dts({
      afterBootstrap(runtime) {
        // Access the runtime for advanced customization
        console.info(runtime.getRootFiles())
      },
    }),
  ],
})

bundleTypes.configPath

v5's bundleTypes adds configPath, allowing you to specify a real api-extractor.json config file path:

import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    dts({
      bundleTypes: {
        configPath: './config/api-extractor.json',
      },
    }),
  ],
})

Config merge priority: internal defaultsconfigPath file configextractorConfig manual config.

outDirs Supports moduleFormat

Using the OutDirConfig object, you can specify a module format for each output directory, automatically generating .d.cts or .d.mts:

export interface OutDirConfig {
  dir: string,
  moduleFormat?: 'cjs' | 'esm',  // defaults to .d.ts if unspecified
}

Behavioral Changes

declarationOnly and esbuild

In v4, declarationOnly: true forcefully removes all build artifacts (Vite/Rollup only).

In v5, this behavior remains the same, but esbuild is excluded — enabling declarationOnly in esbuild will not remove original outputs, because esbuild's plugin lifecycle and bundling mechanism differ from Rollup-based tools.

Automatic Vue File Detection

v5 automatically detects whether .vue files exist in your source. If they do, but processor is explicitly set to 'ts', the plugin prints a warning:

Detected .vue files but processor is set to "ts". Vue declaration files may not be generated correctly. Consider using processor: "vue".

It is recommended to explicitly configure processor: 'vue' for Vue projects.

Renamed Trigger Condition for insertTypesEntry / staticImport

In v4, when rollupTypes: true, insertTypesEntry and staticImport were forced to true.

In v5, the trigger condition has been renamed to when bundleTypes is enabled (boolean or object form).

copyDtsFiles Defaults Vary by Bundler

In v4, copyDtsFiles defaulted to false for all bundlers.

In v5, the defaults are:

BundlerDefault
Vitefalse
Rollup / Rolldown / Webpack / Rspack / Esbuildtrue

If you notice .d.ts source files not being copied or being copied unexpectedly after migration, please specify this option explicitly.

declarationMap Support

v5 removes the internal forceDtsEmit behavior, which means when declarationMap: true is set in tsconfig.json, the plugin correctly generates .d.ts.map source map files (which may have been forcibly overridden in v4).


Dependency Changes

Dependencyv4v5
@microsoft/api-extractorDirect dependencypeerDependencies (optional)
@vue/language-coreDirect dependencypeerDependencies (optional)
@volar/typescriptDirect dependencyDirect dependency
unpluginNoneDirect dependency (core)

Migration suggestions:

# If using bundleTypes
pnpm i -D @microsoft/api-extractor

# If using Vue
pnpm i -D @vue/language-core

Types and Exports

// v4
import dts, { type PluginOptions, editSourceMapDir } from 'vite-plugin-dts'

// v5 (vite-plugin-dts compatibility layer, still works)
import dts, { type PluginOptions, editSourceMapDir } from 'vite-plugin-dts'

// v5 (unplugin-dts, recommended)
import dts from 'unplugin-dts/vite'
import { type PluginOptions, editSourceMapDir } from 'unplugin-dts'

export { type PluginOptions, editSourceMapDir }
  • editSourceMapDir signature remains unchanged: (content: string, fromDir: string, toDir: string) => string
  • Resolver type remains unchanged (name, supports, transform)
  • PluginOptions inheritance has been adjusted, but user-facing fields remain largely consistent

Quick Reference Table

Optionv4v5Notes
Import pathvite-plugin-dtsunplugin-dts/<framework>Vite still supports vite-plugin-dts
rollupTypesRenamed to bundleTypes
bundleTypesSupports boolean | object
bundledPackagesTop-levelbundleTypes.bundledPackagesMoved into nested config
rollupConfigTop-levelbundleTypes.extractorConfigMoved into nested config; type renamed to BundleConfig
rollupOptionsTop-levelbundleTypes.invokeOptionsMoved into nested config
outDirRenamed to outDirs
outDirsSupports moduleFormat
logLevelRemoved
processor'ts' | 'vue', auto-detected
aliasesCustom aliases
afterBootstrapHook after Runtime creation
beforeWriteFileNo changes
afterDiagnosticNo changes
afterRollupNo changes
afterBuildNo changes
declarationOnlyNo longer removes original outputs in esbuild
insertTypesEntryTrigger renamed from rollupTypes to bundleTypes
staticImportTrigger renamed from rollupTypes to bundleTypes
strictOutputNo changes
copyDtsFilesNo changes
cleanVueFileNameNo changes
clearPureImportNo changes
pathsToAliasesNo changes
aliasesExcludeNo changes
resolversNo changes
entryRootNo changes
tsconfigPathNo changes
compilerOptionsNo changes
include / excludeNo changes
rootNo changes

Minimal Migration Examples

Vite Project (Renaming Options Only)

// vite.config.ts
import { defineConfig } from 'vite'
import dts from 'vite-plugin-dts' // or 'unplugin-dts/vite'

export default defineConfig({
  plugins: [
    dts({
      // Before (v4)
      // rollupTypes: true,
      // rollupConfig: { ... },
      // rollupOptions: { ... },
      // outDir: 'dist',

      // After (v5)
      bundleTypes: {
        extractorConfig: { /* ... */ },   // formerly rollupConfig
        invokeOptions: { /* ... */ },     // formerly rollupOptions
      },
      outDirs: 'dist',
      // logLevel removed, no need to configure
    }),
  ],
})

Rollup Project (Migrating to unplugin)

// rollup.config.mjs
import typescript from '@rollup/plugin-typescript'
import dts from 'unplugin-dts/rollup'

export default {
  input: './src/index.ts',
  output: { dir: 'dist', format: 'esm' },
  plugins: [
    typescript(),
    dts({
      bundleTypes: true,
      outDirs: 'dist',
    }),
  ],
}

For further questions, please refer to the full configuration in README.md and README.zh-CN.md.