DEV Community

Cover image for Building universal JS/TS plugins with unplugin
Jakub Andrzejewski
Jakub Andrzejewski

Posted on

Building universal JS/TS plugins with unplugin

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

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:

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
Enter fullscreen mode Exit fullscreen mode

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:

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']
    }
  }
})
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)