* loose start for bun plugin * implement bun * support emitFile() * fix Bun cases in integration test * add bun to other test files * remove bun-types-no-globals from github * restore bun-types-no-globals from npm @^1.2 * bun does not yet support .onEnd, so for now we shouldn't fake it with brittle workarounds * add Bun in the documentation * some missing bun references in docs * support multiple plugins * use Bun namespace instead of importing module that won't necessarily exist * Bun is a cute pink color! * fix the transform hook * fix for virtual modules * tidy up * setup bun in ci * revert unplugin require path * ignore bun in test-out folders * update tests * support onEnd * remove * implement guessLoader(), bun also now supports onEnd() * don't eat errors/warnings * we dont need to outdir for bun in this test * bun writebundle test * Update to bun@1.2.22 (supports onEnd and onResolve) * use onStart() * define onStart() in mocks * onStart * ci: run vitest in Bun so we can run bun's tests * Bun error message if building outside of Bun * skip bun specific tests when not running in bun * refactor * allow only * ci: fix typecheck --------- Co-authored-by: Kevin Deng <sxzz@sxzz.moe>
14 KiB
| outline | lastUpdated |
|---|---|
| deep | false |
Getting Started
Overview
Unplugin is a library that offers a unified plugin system for various build tools. It extends the excellent Rollup plugin API to serve as the standard plugin interface, and provides a compatibility layer based on the build tools employed.
Unplugin currently supports:
Trying It Online
You can try Unplugin in your browser directly.
Creating an Unplugin package
Templates
Check repositories above for more details.
Plugin Installation
Pre-requisites
- Node.js 18.12.0 or later.
- webpack 5 or later, if you are using webpack.
Install package
::: code-group
npm install unplugin-starter --save-dev
yarn add unplugin-starter -D
pnpm add unplugin-starter -D
bun add unplugin-starter -D
:::
Bundler & Framework Integration
::: code-group
// vite.config.ts
import Starter from 'unplugin-starter/vite'
export default defineConfig({
plugins: [
Starter({
/* options */
}),
],
})
// rollup.config.js
import Starter from 'unplugin-starter/rollup'
export default {
plugins: [
Starter({
/* options */
}),
],
}
// rolldown.config.js
import Starter from 'unplugin-starter/rolldown'
export default {
plugins: [
Starter({
/* options */
}),
],
}
// webpack.config.js
module.exports = {
/* ... */
plugins: [
require('unplugin-starter/webpack')({
/* options */
}),
],
}
// rspack.config.js
module.exports = {
/* ... */
plugins: [
require('unplugin-starter/rspack')({
/* options */
}),
],
}
// esbuild.config.js
import { build } from 'esbuild'
import Starter from 'unplugin-starter/esbuild'
build({
plugins: [Starter()],
})
// farm.config.ts
import Starter from 'unplugin-starter/farm'
export default defineConfig({
plugins: [
Starter({
/* options */
}),
],
})
// bun.config.ts
import Starter from 'unplugin-starter/bun'
await Bun.build({
entrypoints: ['./src/index.ts'],
outdir: './dist',
plugins: [
Starter({
/* options */
}),
],
})
// vue.config.js
module.exports = {
configureWebpack: {
plugins: [
require('unplugin-starter/webpack')({
/* options */
}),
],
},
}
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
[
'unplugin-starter/nuxt',
{
/* options */
},
],
],
})
// astro.config.mjs
import { defineConfig } from 'astro/config'
import Starter from 'unplugin-turbo-console/astro'
// https://astro.build/config
export default defineConfig({
integrations: [Starter()],
})
Supported Hooks
| Hook | Rollup | Vite | webpack | esbuild | Rspack | Farm | Rolldown | Bun |
|---|---|---|---|---|---|---|---|---|
enforce |
❌ 1 | ✅ | ✅ | ❌ 1 | ✅ | ✅ | ✅ | ❌ |
buildStart |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
resolveId |
✅ | ✅ | ✅ | ✅ | ✅ 5 | ✅ | ✅ | ✅ |
loadInclude |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
load |
✅ | ✅ | ✅ | ✅ 3 | ✅ | ✅ | ✅ | ✅ |
transformInclude |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
transform |
✅ | ✅ | ✅ | ✅ 3 | ✅ | ✅ | ✅ | ✅ |
watchChange |
✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ |
buildEnd |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ 6 |
writeBundle4 |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ 6 |
::: details Notice
- Rollup and esbuild do not support using
enforceto 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 performance on Webpack and Rolldown.
However, it is now deprecated. Please use
transform/load/resolveId.filterinstead. In Rollup, this hook has been polyfilled to match the behaviors. See the following usage examples for reference. - Although esbuild can handle both JavaScript and CSS and many other file formats, you can only return JavaScript in
loadandtransformresults. - Currently,
writeBundleis only serves as a hook for the timing. It doesn't pass any arguments. - Rspack supports
resolveIdwith a minimum required version of v1.0.0-alpha.1. - Bun's plugin API doesn't have an
onEndhook yet, sobuildEndandwriteBundleare not supported.
:::
Usage
import type { UnpluginFactory } from 'unplugin'
import { createUnplugin } from 'unplugin'
export interface Options {
// define your plugin options here
}
export const unpluginFactory: UnpluginFactory<Options | undefined> = options => ({
name: 'unplugin-starter',
transform: {
// an additional hook is needed for better perf on webpack and rolldown
filter: {
id: /main\.ts$/
},
handler(code) {
return code.replace(/<template>/, '<template><div>Injected</div>')
},
},
// more hooks coming
})
export const unplugin = /* #__PURE__ */ createUnplugin(unpluginFactory)
export default unplugin
export const vitePlugin = unplugin.vite
export const rollupPlugin = unplugin.rollup
export const rolldownPlugin = unplugin.rolldown
export const webpackPlugin = unplugin.webpack
export const rspackPlugin = unplugin.rspack
export const esbuildPlugin = unplugin.esbuild
export const farmPlugin = unplugin.farm
export const bunPlugin = unplugin.bun
Filters
To optimize performance in native bundlers, leverage the filter option in resolveId, transform, and load
hooks to exclude files that don’t require processing.
import { createUnplugin } from 'unplugin'
type FilterPattern = string | RegExp | Array<string | RegExp>
const plugin = createUnplugin(() => ({
name: 'unplugin-starter',
transform: {
filter: {
id: {
include: [/\.js$/, '**/*.ts'],
exclude: /node_modules/,
},
code: {
include: 'foo',
exclude: 'bar',
},
},
handler(code) {
// ...
},
}
}))
More details can be found in the Rolldown's documentation.
Supported Context
| Context | Rollup | Vite | webpack | esbuild | Rspack | Farm | Rolldown | Bun |
|---|---|---|---|---|---|---|---|---|
this.parse |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
this.addWatchFile |
✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
this.emitFile1 |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
this.getWatchFiles |
✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
this.warn |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
this.error |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
::: info Notice
- Currently,
this.emitFileonly supports theEmittedAssetvariant. :::
Nested Plugins
Unplugin supports constructing multiple nested plugins to behave like a single one.
Bundler Supported
| Rollup | Vite | webpack | Rspack | esbuild | Farm | Rolldown | Bun |
|---|---|---|---|---|---|---|---|
✅ >=3.11 |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
::: details Notice
- Rollup supports nested plugins since v3.1.0. Plugin author should ask users to have a Rollup version of
>=3.1.0when using nested plugins. For single plugin format, Unplugin works for any version of Rollup. :::
Usage
import type { UnpluginFactory } from 'unplugin'
import { createUnplugin } from 'unplugin'
export interface Options {
// define your plugin options here
}
export const unpluginFactory: UnpluginFactory<Options | undefined> = options => [
{
name: 'plugin-a',
transform(code) {
return code.replace(/<template>/, '<template><div>Injected</div>')
},
},
{
name: 'plugin-b',
resolveId(id) {
return id
},
},
]
export const unplugin = /* #__PURE__ */ createUnplugin(unpluginFactory)
export default unplugin
Bundler-Specific Logic
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 bundler-specific usages, Unplugin provides an escape hatch for that.
Hooks
import type { UnpluginFactory } from 'unplugin'
import { createUnplugin } from 'unplugin'
export interface Options {
// define your plugin options here
}
export const unpluginFactory: UnpluginFactory<Options | undefined> = (
options,
meta,
) => {
console.log(meta.framework) // vite rollup webpack esbuild rspack...
return {
name: 'unplugin-starter',
transform: {
// an additional hook is needed for better perf on webpack and rolldown
filter: {
id: /main\.ts$/
},
handler(code) {
return code.replace(/<template>/, '<template><div>Injected</div>')
},
},
vite: {
// Vite plugin
configureServer(server) {
// configure Vite server
},
},
rollup: {
// Rollup plugin
},
rolldown: {
// Rolldown plugin
},
webpack(compiler) {
// Configure webpack compiler
},
rspack(compiler) {
// Configure Rspack compiler
},
esbuild: {
// Change the filter of onResolve and onLoad
// onResolveFilter?: RegExp,
// onLoadFilter?: RegExp,
// Tell esbuild how to interpret the contents. By default Unplugin tries to guess the loader
// from file extension (eg: .js -> "js", .jsx -> 'jsx')
// loader?: (Loader | (code: string, id: string) => Loader)
// Or you can completely replace the setup logic
// setup?: EsbuildPlugin.setup,
},
farm: {
// Farm plugin
},
bun: {
// Bun plugin
},
}
}
export const unplugin = /* #__PURE__ */ createUnplugin(unpluginFactory)
export default unplugin
Plugins
The package exports a set of functions in place of createUnplugin that allow for the creation of plugins for specific bundlers.
Each of the function takes the same generic factory argument as createUnplugin.
import {
createBunPlugin,
createEsbuildPlugin,
createFarmPlugin,
createRolldownPlugin,
createRollupPlugin,
createRspackPlugin,
createVitePlugin,
createWebpackPlugin,
} from 'unplugin'
const vitePlugin = createVitePlugin(/* factory */)
const rollupPlugin = createRollupPlugin(/* factory */)
const rolldownPlugin = createRolldownPlugin(/* factory */)
const esbuildPlugin = createEsbuildPlugin(/* factory */)
const webpackPlugin = createWebpackPlugin(/* factory */)
const rspackPlugin = createRspackPlugin(/* factory */)
const farmPlugin = createFarmPlugin(/* factory */)
const bunPlugin = createBunPlugin(/* factory */)