0%

Build Chrome Extension with TypeScript

Intro

This blog is to document how to build a Chrome Extension with Typescript.
In detail, I will use Rollup as the bundler rather than Webpack since Rollup provides a lighter solution for general bundling task.

Context in Chrome Extension

The Chrome Extension is like a regular web app that runs in chrome, but it gets the privilege to hijack websites and inject contents/scripts.
Furthermore, there are two scripts here one is content that will be injected into a website by the browser and background script, which is an offscreen script that acts as a secondary thread.

Apart from scripts, we also need to deal with assets(HTML, image, and manifest) through the development process.

Thus for our bundler, we want it can package the two scripts separately and package assets into one folder.

Project Structure

Above is the structure where src has two folders background and content storing for their scripts and a json file descriping the manifest.

Manifest

In the manifest.json, we need to define the entry point for background and content. Also, we need to mention the permissions we need as well as other basic information.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"manifest_version": 2,
"web_accessible_resources": ["./content/banner/**"],
"name": "***",
"description": "***",
"version": "1.0",

"background": {
"scripts": ["background/index.js"]
},

"content_scripts": [
{
"js": ["content/index.js"]
}
],
"permissions": [
"background",
"downloads"
]
}

Rollup

After the above Chrome Extension setting, we need to deal with bundler setting in this case – Rollup.

Below is the Rollup config file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import typescript from 'rollup-plugin-typescript2';
import multiInput from 'rollup-plugin-multi-input';
import copy from 'rollup-plugin-copy'
import require from "rollup-plugin-commonjs";
import resolve from '@rollup/plugin-node-resolve';

export default {
input: ["src/**/index.ts"],
output: [
{
dir: 'dist/',
format: 'esm'
}
],

plugins: [
require({
include: "node_modules/**"
}),
resolve({
include: "node_modules/**",
browser: true,
preferBuiltins: false
}),
multiInput(),
typescript({
objectHashIgnoreUnknownHack: true
}),
copy({
targets: [
{src: "src/manifest.json", dest: "dist/"}
]
})
]
}

A couple things to mention,

  • multiInput is for providing multi-entry points for bundling.(background script and content script)
  • require & resolve are both for finding codes that need to be bundled.
  • copy is for moving assets file to the final build folder.