Unified plugin system for build tools.
Currently supports:
unplugin
extends the excellent Rollup plugin API as the unified plugin interface and provides a compatible layer base on the build tools used with.
Hook | Rollup | Vite | Webpack 4 | Webpack 5 | esbuild | Rspack |
---|---|---|---|---|---|---|
enforce |
❌ 1 | ✅ | ✅ | ✅ | ❌ 1 | ❌ |
buildStart |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
resolveId |
✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
loadInclude 2 |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
load |
✅ | ✅ | ✅ | ✅ | ✅ 3 | ✅ |
transformInclude 2 |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
transform |
✅ | ✅ | ✅ | ✅ | ✅ 3 | ✅ |
watchChange |
✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
buildEnd |
✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
writeBundle 4 |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
- Rollup and esbuild do not support using
enforce
to control the order of plugins. Users need to maintain the order manually. - Webpack's id filter is outside of loader logic; an additional hook is needed for better perf on Webpack. In Rollup and Vite, this hook has been polyfilled to match the behaviors. See for following usage examples.
- Although esbuild can handle both JavaScript and CSS and many other file formats, you can only return JavaScript in
load
andtransform
results. - Currently,
writeBundle
is only serves as a hook for the timing. It doesn't pass any arguments.
Warning: The Rspack support is experimental. Future changes to Rspack integrations might not follow semver, please pin
unplugin
in your dependency when using. It's not recommended to use in production.
Hook | Rollup | Vite | Webpack 4 | Webpack 5 | esbuild | Rspack |
---|---|---|---|---|---|---|
this.parse |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
this.addWatchFile |
✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
this.emitFile 5 |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
this.getWatchFiles |
✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
this.warn |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
this.error |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
- Currently,
this.emitFile
only supports theEmittedAsset
variant.
import { createUnplugin } from 'unplugin'
export const unplugin = createUnplugin((options: UserOptions) => {
return {
name: 'unplugin-prefixed-name',
// webpack's id filter is outside of loader logic,
// an additional hook is needed for better perf on webpack
transformInclude(id) {
return id.endsWith('.vue')
},
// just like rollup transform
transform(code) {
return code.replace(/<template>/, '<template><div>Injected</div>')
},
// more hooks coming
}
})
export const vitePlugin = unplugin.vite
export const rollupPlugin = unplugin.rollup
export const webpackPlugin = unplugin.webpack
export const rspackPlugin = unplugin.rspack
export const esbuildPlugin = unplugin.esbuild
Since v0.10.0
, unplugin supports constructing multiple nested plugins to behave like a single one. For example:
Rollup | Vite | Webpack 4 | Webpack 5 | Rspack | esbuild |
---|---|---|---|---|---|
✅ >=3.1 6 |
✅ | ✅ | ✅ | ✅ |
- Rollup supports nested plugins since v3.1.0. Plugin aurthor should ask users to a have a Rollup version of
>=3.1.0
when using nested plugins. For singe plugin format, unplugin works for any versions of Rollup. - Since esbuild does not have a built-in transform phase, the
transform
hook of nested plugin will not work on esbuild yet. Other hooks likeload
orresolveId
work fine. We will try to find a way to support it in the future.
import { createUnplugin } from 'unplugin'
export const unplugin = createUnplugin((options: UserOptions) => {
return [
{
name: 'plugin-a',
transform(code) {
// ...
},
},
{
name: 'plugin-b',
resolveId(id) {
// ...
},
},
]
})
// vite.config.ts
import UnpluginFeature from './unplugin-feature'
export default {
plugins: [
UnpluginFeature.vite({ /* options */ }),
],
}
// rollup.config.js
import UnpluginFeature from './unplugin-feature'
export default {
plugins: [
UnpluginFeature.rollup({ /* options */ }),
],
}
// webpack.config.js
module.exports = {
plugins: [
require('./unplugin-feature').webpack({ /* options */ }),
],
}
// esbuild.config.js
import { build } from 'esbuild'
build({
plugins: [
require('./unplugin-feature').esbuild({ /* options */ }),
],
})
// rspack.config.js
module.exports = {
plugins: [
require('./unplugin-feature').rspack({ /* options */ }),
],
}
While unplugin
provides compatible layers for some hooks, the functionality of it is limited to the common subset of the build's plugins capability. For more advanced framework-specific usages, unplugin
provides an escape hatch for that.
export const unplugin = createUnplugin((options: UserOptions, meta) => {
console.log(meta.framework) // 'vite' | 'rollup' | 'webpack' | 'rspack' | 'esbuild'
return {
// common unplugin hooks
name: 'unplugin-prefixed-name',
transformInclude(id) { /* ... */ },
transform(code) { /* ... */ },
// framework specific hooks
vite: {
// Vite plugin
configureServer(server) {
// configure Vite server
},
// ...
},
rollup: {
// Rollup plugin
},
webpack(compiler) {
// configure Webpack compiler
},
rspack(compiler) {
// configure Rspack compiler
},
esbuild: {
// change the filter of onResolve and onLoad
onResolveFilter?: RegExp,
onLoadFilter?: RegExp,
// or you can completely replace the setup logic
setup?: EsbuildPlugin.setup,
},
}
})
-
Plugins powered by unplugin should have a clear name with
unplugin-
prefix. -
Include
unplugin
keyword inpackage.json
. -
To provide better DX, packages could export 2 kinds of entry points:
-
Default export: the returned value of
createUnplugin
functionimport UnpluginFeature from 'unplugin-feature'
-
Subpath exports: properties of the returned value of
createUnplugin
function for each framework usersimport VitePlugin from 'unplugin-feature/vite'
-
Refer to unplugin-starter for more details about this setup.
-
- unplugin-auto-import
- unplugin-vue2-script-setup
- unplugin-icons
- unplugin-vue-components
- unplugin-upload-cdn
- unplugin-web-ext
- unplugin-properties
- unplugin-moment-to-dayjs
- unplugin-object-3d
- unplugin-parcel-css
- unplugin-vue
- unplugin-vue-macros
- unplugin-vue-define-options
- unplugin-jsx-string
- unplugin-ast
- unplugin-element-plus
- unplugin-glob
- unplugin-sentry
- unplugin-imagemin
- unplugin-typedotenv
- unplugin-fonts
- sentry-javascript-bundler-plugins
- unplugin-svg-component
MIT License © 2021-PRESENT Nuxt Contrib