Rollup Plugins for Absolute Beginners
26 July 2021I've recently been getting into the new generation of development tools like wmr, Vite, esbuild and Snowpack. These are causing quite a bit of hype in the community.
So I thought I'd start to get closer to the metal with these tools and how to configure them. Something interesting I found out is that both Vite and wmr have opted to support rollup plugins!
The rollup plugin API is a hell of a lot simpler than writing webpack loaders so this is good news. This also means that understanding rollup plugins and how to write them will continue to be an important skill in the JavaScript world, so let's get into it:
Default Export Function #
A rollup plugin should be a JavaScript module exporting a single function by default. It's also an important convention to start the name with "rollup-plugin-"
At the moment node.js still isn't using esmodules by default, so let's use the ".mjs" extension. Something like this:
// rollup-plugin-mydopeplugin.mjs
export default function myDopePlugin(){}
This function should return an object with some specific keys which rollup is going to look for when running through your code. In the rollup docs these are referred to as "hooks" because they are places you can hook into the build - makes sense!
Name Property #
The most basic and important property to add is the name of your plugin. This will be used by rollup for any error messages or warnings. So let's name our plugin!
// rollup-plugin-mydopeplugin.mjs
export default function myDopePlugin(){
return {
name:"my-dope-plugin"
}
}
Hey this really doesn't seem so bad does it? But we probably want our plugin to do something. At the moment it will do absolutely nothing, but hey, it's bug free!
Transform hook #
This hook is going to let us change our code. It's a function that takes two arguments:
- Your code (a string)
- The ID of the code (another string)
The string representation of the code is simple enough. But what do I mean by ID? We're JavaScript developers and probably used to IDs being something we use to grab a user or a post when we're processing data. In this case, the ID refers to the string path that was used to import the module. It's the string in the JavaScript import statement after "from" .
So if our source code looked like:
// index.js
import {uppercaseMyString} from './utilities.js'
Then the ID when rollup plugs the utilities import into a transform hook would be "./utilities.js".
What should our transform hook return? There are a few options:
- We can return null. This will tell rollup we don't want to transform the code at all and leave it as is.
- We can return a string. This will be the code after transformation.
- We can return an object. This can give rollup back the transformed code, a sourcemap, something called an abstract syntax tree. It can also provide more information about whether the module has side effects, whether you want to use tree shaking, and specify some special behaviour if another module tries to import a non-existent export.
Let's write our plugin #
Let's write a plugin which will insert a console.log at the start of every JavaScript file that gets imported in our project. We'll start by adding a transform function to our previous code:
// rollup-plugin-mydopeplugin.mjs
export default function myDopePlugin(){
return {
name:"my-dope-plugin",
transform(source,id){
}
}
}
Now we'll take a look at the id and make sure we're looking at a JavaScript file. If it doesn't have the ".js" file extension, we'll return null to tell rollup we don't want to do anything with it. So our transform function becomes:
transform(source,id){
if (id.slice(-3) !== ".js") return null;
}
And finally if it is a JavaScript file, let's concatenate a console.log to the front of our code and return the string.
// rollup-plugin-mydopeplugin.mjs
export default function myDopePlugin(options = {}) {
return {
name: "my-dope-plugin",
transform(source, id) {
if (id.slice(-3) !== ".js") return null;
return "console.log('hello from rollup');" + source;
},
};
}
And that's our plugin finished!
Now let's use it in a rollup build. Let's npm install rollup and create a "rollup.config.js" file. This file needs a default export which is an object with input and output keys that hold strings to where we want to input and output our source. It also has a key that holds an array of plugins.
We can import our plugin to the rollup config file, then call its function within the plugins array in the rollup config:
// rollup.config.js
import myDopePlugin from './rollup-plugin-mydopeplugin.mjs';
export default {
input: 'index.js',
output: {
file: 'bundle.js',
},
plugins: [ myDopePlugin() ]
};
Then when we run our rollup build we just need to tell it to use our config file by adding a --config
argument in the command line. If you do a default npm install it'd look something like this:
./node_modules/.bin/rollup --config
And that's it! Hopefully this has made getting your hands dirty in the build step slightly less scary by keeping things super simple.
As mentioned before, you could potentially use this plugin in the build step of a Vite project or a wmr project - not that this particular plugin has any use at all. But this is super empowering that we have some reusability in build systems now.
There's quite a few more rollup hooks to look into if you want to get some more power building these plugins. I'd also recommend checking out This talk about build plugins with Jake Archibald and Jason Miller.
I might try add another blogpost sometime soon getting deeper into rollup plugins as they're so dang useful.
Back to blog