Building universal JS/TS plugins with unplugin
Tags: javascript, tutorial, programming, webdev
Published: 2022-12-12 08:52:06

I started building my open source projects for the community some time ago already. It always gave me a lot of joy when I noticed that the amount of GitHub Stars was growing each day. First, I was building project with Nest.js, next with Nuxt.js and only recently, I have found that developing tools with Vite is even better because you can reach even bigger audience. And just few days, when I started working on my new tool `contentine` (that I will publish quite soon btw) I found out the project called `unplugin`.

Make sure to check it out [here](https://github.com/unjs/unplugin)

`unplugin` is a short from Unified plugin system for build tools. In simple words, it means that you can develop a plugin that has an unified interface that will work with `webpack`, `rollup`, `vite`, and `esbuild`. Vite was a game changer because it allowed me to to build a tool that was working for React, Vue, and Svelte, and now, with `unplugin`, you can target almost all audiences with a single plugin tool.

## Usage

It is fairly simple and explained quite well in the README.md but I will copy it here anyway:

```ts
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 esbuildPlugin = unplugin.esbuild
```

As you can see above, it is only about returning an object that has a name and certain hooks that will include files to transform and do some transformations over it. And that's it. You write a code like this and it will work in almost all modern tools.

## Advanced

Of course, you cannot make everything agnostic but don't be afraid. The package authors thought about that and in case of these advanced scenarions, you still have a configuration for each of the build tools so that you can customize it even more. Take a look at the following code:

```ts
export const unplugin = createUnplugin((options: UserOptions, meta) => {

  console.log(meta.framework) // 'vite' | 'rollup' | 'webpack' | '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
    },
    esbuild: {
      // change the filter of onResolve and onLoad
      onResolveFilter?: RegExp
      onLoadFilter?: RegExp
      // or you can completely replace the setup logic
      setup?: EsbuildPlugin['setup']
    }
  }
})
```

This is a great example of framework (and even build) agnostic tools!

## Summary

And that's it! Unbuild allows you to build universal plugins with one code base. Make sure to check out the starter template -> https://github.com/antfu/unplugin-starter as well as community plugins https://github.com/unjs/unplugin#community-showcases