Compile outputs fun

Lerna to manage multiple modules - Site killer extension for Chrome and Firefox - Part 3

Published 2 years agoTypescript

This is continued from Lerna to manage multiple modules - Site killer extension for Chrome and Firefox - Part 2 .

4. Firefox browser add-on

4.1 Setup

Create the directory ./packages/firefox and create package.json in it with these contents:

{
  "name": "site-killer-firefox",
  "scripts": {
    "build": "webpack && bestzip ./out/site-killer-source.zip package.json package-lock.json tsconfig.json webpack.config.js src/*",
    "dev": "webpack --watch --devtool inline-source-map"
  }
}

The build command is a bit different from the build command for Chrome module. It make a ZIP file for the source code also. The source code is needed when you want to publish your extension on Firefix website because we are using Webpack to compile our source code.

Next we will install the necessary dependencies for it by running this command:

npm i -D webpack webpack-cli ts-loader typescript copy-webpack-plugin zip-webpack-plugin bestzip web-ext-types
npm i ..\common

We have installed the common module as the dependency here. We also installed these additional dependencies:

Dependency Name Description
bestzip Make a zip file from source codes so we can publish it to the Firefox add-ons site
web-ext-types Type definition file for Firefox extension development

Then we create webpack.config.js to tell Webpack how to compile our module:

const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');
const ZipPlugin = require('zip-webpack-plugin');

module.exports = {
    entry: {
        background: './src/background.ts',
        popup: './src/popup.ts'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: 'ts-loader',
                exclude: /node_modules/
            }
        ]
    },
    resolve: {
        extensions: ['.tsx', '.ts', '.js']
    },
    plugins: [
        new CopyPlugin({
            patterns: [
                { from: './node_modules/site-killer-common/assets' }
            ]
        }),
        new ZipPlugin({
            path: path.resolve(__dirname, 'out'),
            filename: 'site-killer.zip'
        })
    ],
    watchOptions: {
        ignored: /\/firefox\/(dist|out)\//
    }
};

It's exactly the same as the one in the Chrome module. Next we create the tsconfig.json to tell the Typescript compiler how to compile our module:

{
    "compilerOptions": {
        "sourceMap": true
    }
}

Once again, it's exactly the same as the one in the Chrome module.

3.3 Code the logics

We will create ./src/background.ts file to record the visited list and kill the tab if the URL is in the blocked list. Put these contents in it:

/// <reference path="../node_modules/web-ext-types/global/index.d.ts" />

import { blockedList, visitedList } from "site-killer-common";

browser.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
    const url = new URL(tab.url);
    if (blockedList.contains(url.hostname)) {
        await browser.tabs.remove(tabId);
        await browser.notifications.create({
            message: `Killed ${url}`,
            title: 'Site Killer',
            iconUrl: 'icon.png',
            type: 'basic'
        });
    } else {
        visitedList.add(url);
    }
});

It's similar to the one in Chrome module except for these:

  1. It needs a reference to the web-ext-types type definition file.
  2. It uses API from browser instead of chrome namespace.
  3. The API returns Promise so we don't have to wrap them.

Now we will create ./src/popup.ts that will show the popup page. Put these contents in it:

import { blockedList, globalEvents, popupComponent } from 'site-killer-common';

globalEvents.rerun.register(async () => {
    const tabs = await browser.tabs.query({});

    let killed = 0;
    for (const tab of tabs) {
        const url = new URL(tab.url);
        if (blockedList.contains(url.hostname)) {
            await browser.tabs.remove(tab.id);
            killed++;
        }
    }

    await browser.notifications.create({
        message: `Killed ${killed} tabs`,
        title: 'Site Killer',
        iconUrl: 'icon.png',
        type: 'basic'
    });
});

document.body.appendChild(popupComponent());

It is similar to the one in Chrome module except the API returns Promise so we don't have to wrap it here.

4.3 Manifest file

We have setup our Webpack to copy the manifest files from the common module, so no work is neede here :)

4.4 Test

Now you can run this command in the root directory to build it:

npm run build

To test it in the Firefox browser, you will need to go to the browser debugging page first. Then you click on the Load Temporary Add-on button. Locate the ./dist/manifest.json file in the Firefox module.

Then you should see your extension loaded in the page.

The icon of your extension should appear in the top right corner now.

That's it. We are done here. The full source code is available at https://github.com/chimin/site-killer