├── .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 `