├── .gitignore
├── dist
├── icons
│ ├── icon128.png
│ ├── icon16.png
│ ├── icon19.png
│ └── icon48.png
├── _locales
│ └── en
│ │ └── messages.json
├── popup.html
├── manifest.json
└── js
│ ├── background.js
│ └── content.js
├── src
├── app
│ ├── background.ts
│ └── content.ts
├── styles
│ └── popup.css
└── ui
│ └── popup.tsx
├── tsconfig.json
├── package.json
├── webpack.config.js
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | dist/js/*
3 | node_modules/
--------------------------------------------------------------------------------
/dist/icons/icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duo-labs/chrome-extension-boilerplate/HEAD/dist/icons/icon128.png
--------------------------------------------------------------------------------
/dist/icons/icon16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duo-labs/chrome-extension-boilerplate/HEAD/dist/icons/icon16.png
--------------------------------------------------------------------------------
/dist/icons/icon19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duo-labs/chrome-extension-boilerplate/HEAD/dist/icons/icon19.png
--------------------------------------------------------------------------------
/dist/icons/icon48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duo-labs/chrome-extension-boilerplate/HEAD/dist/icons/icon48.png
--------------------------------------------------------------------------------
/dist/_locales/en/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "l10nHello": {
3 | "message": "Hello, React!",
4 | "description": "Hello text for the popup."
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/app/background.ts:
--------------------------------------------------------------------------------
1 | chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
2 | console.log("Background got a message!")
3 | sendResponse({})
4 | })
--------------------------------------------------------------------------------
/dist/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/styles/popup.css:
--------------------------------------------------------------------------------
1 | body {
2 | min-width: 400px;
3 | min-height: 400px;
4 | }
5 |
6 | .popup-padded {
7 | padding-top: 10px;
8 | padding-bottom: 10px;
9 | padding-left: 10px;
10 | padding-right: 10px;
11 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "sourceMap": true,
4 | "target": "es6",
5 | "module": "commonjs",
6 | "jsx": "react"
7 | },
8 | "exclude": [
9 | "node_modules",
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/content.ts:
--------------------------------------------------------------------------------
1 | chrome.runtime.sendMessage({}, (response) => {
2 | var checkReady = setInterval(() => {
3 | if (document.readyState === "complete") {
4 | clearInterval(checkReady)
5 | console.log("We're in the injected content script!")
6 | }
7 | })
8 | })
--------------------------------------------------------------------------------
/src/ui/popup.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as ReactDOM from "react-dom"
3 |
4 | import "../styles/popup.css"
5 |
6 | class Hello extends React.Component {
7 | render() {
8 | return (
9 |
10 |
{ chrome.i18n.getMessage("l10nHello") }
11 |
12 | )
13 | }
14 | }
15 |
16 | // --------------
17 |
18 | ReactDOM.render(
19 | ,
20 | document.getElementById('root')
21 | )
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tsrwpcx",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "BSD-3-Clause",
12 | "devDependencies": {
13 | "@types/chrome": "0.0.75",
14 | "@types/react": "^16.7.8",
15 | "@types/react-dom": "^16.0.11",
16 | "css-loader": "^1.0.1",
17 | "style-loader": "^0.23.1",
18 | "ts-loader": "^5.3.1",
19 | "typescript": "^3.1.6",
20 | "webpack": "^4.26.1",
21 | "webpack-cli": "^3.1.2"
22 | },
23 | "dependencies": {
24 | "react": "^16.6.3",
25 | "react-dom": "^16.6.3"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | mode: "development",
5 | devtool: "inline-source-map",
6 |
7 | entry: {
8 | content: './src/app/content.ts',
9 | background: './src/app/background.ts',
10 | popup: './src/ui/popup.tsx',
11 | },
12 |
13 | output: {
14 | path: path.resolve(__dirname, 'dist/js'),
15 | filename: '[name].js'
16 | },
17 |
18 | resolve: {
19 | extensions: [".ts", ".tsx", ".js"]
20 | },
21 |
22 | module: {
23 | rules: [
24 | { test: /\.tsx?$/, loader: "ts-loader" },
25 | { test: /\.css$/, use: ['style-loader', 'css-loader'] }
26 | ]
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/dist/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "TS/R/WP Chrome Extension",
3 | "version": "0.0.1",
4 | "manifest_version": 2,
5 | "description": "Boilerplate for a Chrome extension with TypeScript, React, and Webpack.",
6 | "homepage_url": "https://duo.com/labs",
7 | "icons": {
8 | "16": "icons/icon16.png",
9 | "48": "icons/icon48.png",
10 | "128": "icons/icon128.png"
11 | },
12 | "browser_action": {
13 | "default_title": "TSRWPCX",
14 | "default_popup": "popup.html"
15 | },
16 | "default_locale": "en",
17 | "background": {
18 | "scripts": [
19 | "js/background.js"
20 | ],
21 | "persistent": true
22 | },
23 | "permissions": [
24 | "https://*/*"
25 | ],
26 | "content_security_policy": "default-src 'self';",
27 | "content_scripts": [
28 | {
29 | "matches": [
30 | "https://*/*"
31 | ],
32 | "js": [
33 | "js/content.js"
34 | ]
35 | }
36 | ]
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2018, Duo Labs
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TypeScript / React / Webpack / Chrome Extension Boilerplate
2 |
3 | You can use this boilerplate code to start developing a Chrome extension using [TypeScript](https://www.typescriptlang.org/)/JS, [React](https://reactjs.org/) for the frontend, and [Webpack](https://webpack.js.org/) as the build system.
4 |
5 | At Duo Labs, we found ourselves creating Chrome extensions with this stack frequently enough that we thought it would be nice to have a consistent starting point. Getting all the individual pieces configured from scratch can be a pain.
6 |
7 | ## Get started
8 |
9 | Clone this repository, and then, in this directory:
10 |
11 | 1. `npm install`
12 | 2. `npx webpack`
13 |
14 | Your unpacked Chrome extension will be compiled into `dist/`. You can load it into Chrome by enabling developer mode on the "Extensions" page, hitting "Load unpacked", and selecting the `dist/` folder. You can pack the extension into a `.crx` by using the "Pack extension" button on the same page.
15 |
16 | Use `npx webpack` to recompile after editing.
17 |
18 | ## Source layout
19 |
20 | The default source layout looks like this:
21 |
22 | ```
23 | src
24 | ├── app
25 | │ ├── background.ts
26 | │ └── content.ts
27 | ├── styles
28 | │ └── popup.css
29 | └── ui
30 | └── popup.tsx
31 | ```
32 |
33 | * `background.ts` will get loaded as the extension background script, and will run persistently in the background
34 | * `content.ts` will be injected into the URLs matched by `dist/manifest.json`'s `matches` entry (see [Match Patterns](https://developer.chrome.com/extensions/match_patterns) documentation)
35 | * `popup.tsx` will become the extension's "browser action" popup
36 | * NOTE: `popup.tsx` compiles into `dist/js/popup.js`. It is loaded into `dist/popup.html` by an explicit `