Chrome Extension using Node + Rollup plugin: Stepwise Process

2 min read

Building Chrome extensions is a powerful way to enhance browser functionality through automation. While my previous guide covered creating extensions with Vite, React, and TypeScript, today I’m focusing on a streamlined development workflow using rollup-plugin-chrome-extension.

This setup eliminates the need to constantly rebuild and refresh your extension during development. Let’s dive in!

Setting Up Your Development Environment

Step 1: Install Node.js with NVM

First, we need a proper Node.js environment. Node Version Manager (NVM) lets you manage multiple Node versions effortlessly:

# Install NVM
brew install nvm

# Create NVM's working directory
mkdir ~/.nvm

Add these lines to your shell profile (~/.zshrc or ~/.bash_profile):

export NVM_DIR="$HOME/.nvm"
[ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && . "/opt/homebrew/opt/nvm/nvm.sh"
[ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ] && . "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm"

Apply the changes immediately:

source ~/.zshrc

Verify NVM installation:

nvm --version

Install the latest Node.js:

nvm install node

Step 2: Set Up Package Management

Enable Corepack for modern package management:

corepack enable

Verify with:

corepack --version

Check if Yarn is installed:

yarn --version

If not, install it:

brew install yarn

Step 3: Initialize Your Project

Create and navigate to your project directory:

mkdir chrome-extension-hot-reload
cd chrome-extension-hot-reload
yarn init -y

Step 4: Install Essential Dependencies

Add the rollup-plugin-chrome-extension and supporting packages:

yarn add -D rollup rollup-plugin-chrome-extension @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-typescript

For TypeScript support, add:

yarn add -D typescript @types/chrome

Step 5: Configure Your Project

Create a rollup.config.mjs file:

import { chromeExtension, simpleReloader } from 'rollup-plugin-chrome-extension';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';

export default {
  input: 'src/manifest.json',
  output: {
    dir: 'dist',
    format: 'esm',
  },
  plugins: [
    chromeExtension(),
    simpleReloader(), // Enables hot reloading
    nodeResolve(),
    commonjs(),
    typescript(),
  ],
};

Create a minimal tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": true,
    "outDir": "dist",
    "rootDir": "src"
  }
}

Update your package.json with these scripts:

{
  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c -w"
  },
  "type": "module"
}

Step 6: Create Basic Extension Files

Set up your project structure:

mkdir -p src/background src/popup

Create a basic src/manifest.json:

{
  "manifest_version": 3,
  "name": "My Extension",
  "version": "1.0.0",
  "description": "A Chrome extension with hot reloading",
  "background": {
    "service_worker": "background/index.js"
  },
  "action": {
    "default_popup": "popup/index.html"
  },
  "permissions": ["storage"]
}

Add a simple background script (src/background/index.ts):

console.log('Background script running!');

chrome.runtime.onInstalled.addListener(() => {
  console.log('Extension installed!');
});

Create a popup interface:

src/popup/index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <div id="app">
      <h1>My Extension</h1>
      <p>Count: <span id="counter">0</span></p>
      <button id="increment">Increment</button>
    </div>
    <script src="index.js"></script>
  </body>
</html>

src/popup/styles.css:

body {
  width: 300px;
  padding: 10px;
  font-family: system-ui, sans-serif;
}

button {
  margin-top: 10px;
  padding: 5px 10px;
  background: #4285f4;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

button:hover {
  background: #3367d6;
}

src/popup/index.ts:

let count = 0;
const counterElement = document.getElementById('counter') as HTMLElement;
const incrementButton = document.getElementById('increment') as HTMLButtonElement;

// Load the stored count
chrome.storage.sync.get(['count'], (result) => {
  count = result.count || 0;
  updateCounter();
});

// Update the counter display
function updateCounter() {
  counterElement.textContent = count.toString();
}

// Handle button click
incrementButton.addEventListener('click', () => {
  count += 1;
  chrome.storage.sync.set({ count });
  updateCounter();
});

Running Your Extension

Start the development server with hot reloading:

yarn dev

Load your extension in Chrome:

  1. Open Chrome and go to chrome://extensions/
  2. Enable “Developer mode”
  3. Click “Load unpacked” and select the dist directory

Now, whenever you make changes to your code, rollup will rebuild the extension and the simpleReloader plugin will refresh it automatically in Chrome!

Troubleshooting Common Issues

Module Format Problems

If you see this error:

[!] RollupError: Node tried to load your configuration file as CommonJS even though it is likely an ES module.

Ensure your rollup config file has the .mjs extension and your package.json includes "type": "module".

Missing Plugins

If rollup complains about missing plugins, install them individually:

yarn add -D @rollup/plugin-node-resolve @rollup/plugin-commonjs

Chrome Manifest Validation Errors

Chrome’s strict validation may reject your extension if the manifest has issues. Check Chrome’s error console in the extensions page for details.

Conclusion

This setup gives you a streamlined Chrome extension development workflow with hot reloading. The rollup-plugin-chrome-extension handles all the complexity of rebuilding your extension and keeping it in sync with Chrome.

By eliminating the manual refresh-rebuild cycle, you can focus on building functionality rather than managing your development environment.

What Chrome extension ideas are you planning to build? The possibilities are endless, from productivity tools to content enhancers and beyond!

Leave a Reply

Your email address will not be published. Required fields are marked *