Chapter 02 Development Environment Setup and Tools
Chapter 2: Development Environment Setup and Tools
- Configure Chrome Extension development environment
- Master developer tools and debugging techniques
- Understand commonly used development tools and frameworks
Knowledge Summary
Development Environment Components
The Chrome Extension development environment mainly consists of the following parts:
- Browser Environment: Chrome browser (dev or stable version)
- Code Editor: VS Code, WebStorm, etc.
- Version Control: Git
- Build Tools: Webpack, Vite, Parcel (optional)
- Development Frameworks: React, Vue, Angular (optional)
Development Toolchain Architecture
Basic Development Environment Setup
Essential Tool Installation
Environment Check Commands:
# Check if tools are installed
google-chrome --version # Chrome browser
node --version # Node.js
npm --version # NPM
git --version # Git
code --version # VS Code
Chrome Developer Mode Settings:
- Open Chrome browser
- Visit
chrome://extensions/ - Enable “Developer mode” toggle in the top right corner
Project Directory Structure:
my-chrome-extension/
├── src/
│ ├── background/ # Background scripts
│ ├── content/ # Content scripts
│ ├── popup/ # Popup pages
│ ├── options/ # Options pages
│ └── assets/
│ ├── icons/ # Icons
│ └── images/ # Image resources
├── dist/ # Build output
├── test/ # Test files
└── docs/ # Documentation
package.json Configuration:
{
"name": "my-chrome-extension",
"version": "1.0.0",
"description": "Chrome Extension project",
"scripts": {
"build": "webpack --mode production",
"dev": "webpack --mode development --watch",
"test": "jest",
"lint": "eslint src/"
},
"devDependencies": {
"webpack": "^5.0.0",
"webpack-cli": "^4.0.0",
"copy-webpack-plugin": "^9.0.0",
"clean-webpack-plugin": "^4.0.0",
"eslint": "^8.0.0",
"jest": "^27.0.0"
}
}
.gitignore Configuration:
# Dependencies
node_modules/
# Build output
dist/
build/
*.zip
# IDE files
.vscode/
.idea/
*.swp
*.swo
# OS files
.DS_Store
Thumbs.db
# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Environment variables
.env
.env.local
# Test coverage
coverage/
VS Code Extension Configuration
Recommended VS Code Extensions (.vscode/extensions.json):
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"christian-kohler.path-intellisense",
"formulahendry.auto-rename-tag",
"ritwickdey.liveserver",
"ms-vscode.vscode-typescript-tslint-plugin",
"octref.vetur",
"dsznajder.es7-react-js-snippets",
"msjsdiag.debugger-for-chrome",
"naumovs.color-highlight"
]
}
Workspace Settings (.vscode/settings.json):
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"editor.tabSize": 2,
"editor.wordWrap": "on",
"files.autoSave": "afterDelay",
"files.autoSaveDelay": 1000,
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"emmet.includeLanguages": {
"javascript": "javascriptreact"
},
"search.exclude": {
"**/node_modules": true,
"**/dist": true,
"**/build": true
}
}
Code Snippets (.vscode/chrome-extension.code-snippets):
{
"Chrome Extension Manifest": {
"prefix": "manifest",
"body": [
"{",
" \"manifest_version\": 3,",
" \"name\": \"${1:Extension Name}\",",
" \"version\": \"${2:1.0.0}\",",
" \"description\": \"${3:Description}\",",
" \"permissions\": [${4}],",
" \"action\": {",
" \"default_popup\": \"popup.html\"",
" }",
"}"
],
"description": "Chrome Extension Manifest V3 template"
},
"Content Script": {
"prefix": "content",
"body": [
"// Content script for Chrome Extension",
"console.log('Content script loaded');",
"",
"// Interact with the page DOM",
"document.addEventListener('DOMContentLoaded', () => {",
" ${1:// Your code here}",
"});"
],
"description": "Content script template"
},
"Chrome Storage Get": {
"prefix": "chromeget",
"body": [
"chrome.storage.${1|local,sync|}.get(['${2:key}'], (result) => {",
" console.log('Value:', result.${2:key});",
" ${3:// Handle the result}",
"});"
],
"description": "Chrome storage get template"
}
}
Debug Launch Configuration (.vscode/launch.json):
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Chrome Extension",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}/src",
"runtimeArgs": [
"--load-extension=${workspaceFolder}/dist"
]
}
]
}
Using Developer Tools
Chrome DevTools Features Explained
DevTools Panel Functions:
| Panel | Function |
|---|---|
| Elements | Inspect and modify DOM/CSS |
| Console | JavaScript console |
| Sources | Debug JavaScript code |
| Network | Monitor network requests |
| Performance | Performance analysis |
| Memory | Memory analysis |
| Application | Storage and cache management |
| Security | Security checks |
Extension Debugging Guide:
Background Script Debugging:
- Open
chrome://extensions/ - Find your extension
- Click the “Service Worker” link
- DevTools will open, showing the background script console
Popup Page Debugging:
- Right-click the extension icon
- Select “Inspect popup”
- DevTools will open, showing the popup page
Content Script Debugging:
- Press F12 on the target web page
- Go to the Sources panel
- Find the Content Scripts section on the left
- Select your extension script
Useful Console Commands:
| Command | Description |
|---|---|
$_ | Returns the value of the most recent expression |
$0-$4 | Returns recently selected DOM elements |
$(selector) | Equivalent to document.querySelector |
$$(selector) | Equivalent to document.querySelectorAll |
clear() | Clear the console |
copy(object) | Copy object to clipboard |
debug(function) | Set breakpoint at function entry |
monitor(function) | Monitor function calls |
monitorEvents(element, events) | Monitor element events |
getEventListeners(element) | Get element’s event listeners |
Extension-Specific Commands:
// Get manifest information
chrome.runtime.getManifest()
// Get extension ID
chrome.runtime.id
// Get extension details
chrome.management.getSelf(console.log)
// View local storage
chrome.storage.local.get(null, console.log)
// View permissions
chrome.permissions.getAll(console.log)
Performance Analysis Tools:
| Tool | Purpose | Key Metrics |
|---|---|---|
| Performance Panel | Record runtime performance | FPS, CPU usage, network activity, memory |
| Memory Panel | Memory analysis and leak detection | Heap size, object count, memory allocation |
| Coverage Panel | Code coverage analysis | JS coverage, CSS coverage |
Debugging Techniques and Best Practices
Breakpoint Strategies Explained:
| Breakpoint Type | Description | How to Use |
|---|---|---|
| Conditional Breakpoint | Pause only under specific conditions (e.g., i === 5) | Right-click line number → Add conditional breakpoint |
| Logpoint | Don’t pause, only output log | Right-click line number → Add logpoint |
| DOM Breakpoint | Triggered on DOM changes (subtree modifications, attribute modifications, node removal) | Elements panel → Right-click element → Break on |
| XHR/Fetch Breakpoint | Triggered on network requests | Sources panel → XHR/fetch Breakpoints |
| Event Listener Breakpoint | Pause when specific events trigger (mouse, keyboard, touch, etc.) | Sources panel → Event Listener Breakpoints |
Error Handling Pattern Examples:
// 1. Global error capture
window.addEventListener('error', (event) => {
console.error('Global error:', event.error);
// Send error report
reportError({
message: event.message,
source: event.filename,
line: event.lineno,
column: event.colno,
error: event.error.stack
});
});
// 2. Promise error capture
window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled promise rejection:', event.reason);
event.preventDefault();
});
// 3. Chrome Extension error handling
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
try {
// Handle message
handleMessage(request, sender, sendResponse);
} catch (error) {
console.error('Message handling error:', error);
sendResponse({ success: false, error: error.message });
}
return true; // Keep message channel open
});
// 4. Async error handling
async function safeAsyncOperation() {
try {
const result = await riskyOperation();
return { success: true, data: result };
} catch (error) {
console.error('Async operation failed:', error);
return { success: false, error: error.message };
}
}
// 5. Development environment debugging helper
const DEBUG = true; // Set to false in production
function debugLog(...args) {
if (DEBUG) {
console.log('[DEBUG]', new Date().toISOString(), ...args);
}
}
// 6. Performance monitoring
function measurePerformance(functionName, fn) {
return function(...args) {
const startTime = performance.now();
const result = fn.apply(this, args);
const endTime = performance.now();
console.log(`${functionName} took ${endTime - startTime}ms`);
return result;
};
}
Remote Debugging Configuration:
Start Chrome Remote Debugging:
chrome --remote-debugging-port=9222
- Purpose: Allow external tools to connect for debugging
- Uses: Automated testing, remote development, CI/CD integration
Connect to Remote Debugging:
- URL:
http://localhost:9222 - Supported tools: Chrome DevTools, VS Code, Puppeteer
- Capabilities: Inspect pages, execute scripts, performance analysis
Extension Auto-Reload:
- Tool: Extension Reloader
- Benefit: Automatically reload extension after code changes
Common Development Tools and Frameworks
Build Tool Configuration
Webpack Configuration Example (webpack.config.js):
// webpack.config.js
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: process.env.NODE_ENV || 'development',
devtool: 'inline-source-map',
entry: {
popup: './src/popup/popup.js',
options: './src/options/options.js',
background: './src/background/background.js',
content: './src/content/content.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name]/[name].js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|jpg|gif|svg)$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/'
}
}
}
]
},
plugins: [
new CleanWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [
{ from: 'src/manifest.json' },
{ from: 'src/assets', to: 'assets' }
]
}),
new HtmlWebpackPlugin({
template: 'src/popup/popup.html',
filename: 'popup/popup.html',
chunks: ['popup']
}),
new HtmlWebpackPlugin({
template: 'src/options/options.html',
filename: 'options/options.html',
chunks: ['options']
})
],
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
priority: 10
}
}
}
}
};
Vite Configuration Example (vite.config.js):
// vite.config.js
import { defineConfig } from 'vite';
import { crx } from '@crxjs/vite-plugin';
import manifest from './src/manifest.json';
export default defineConfig({
plugins: [
crx({ manifest })
],
build: {
rollupOptions: {
input: {
popup: 'src/popup/index.html',
options: 'src/options/index.html'
},
output: {
entryFileNames: '[name]/[name].js',
chunkFileNames: 'chunks/[name]-[hash].js',
assetFileNames: 'assets/[name]-[hash].[ext]'
}
}
},
server: {
port: 3000,
hmr: {
port: 3001
}
}
});
NPM Scripts Configuration:
{
"scripts": {
"dev": "webpack --mode development --watch",
"build": "webpack --mode production",
"build:firefox": "cross-env TARGET=firefox webpack --mode production",
"clean": "rimraf dist",
"test": "jest",
"test:watch": "jest --watch",
"lint": "eslint src/ --ext .js,.jsx",
"lint:fix": "eslint src/ --ext .js,.jsx --fix",
"format": "prettier --write 'src/**/*.{js,jsx,css,html}'",
"analyze": "webpack-bundle-analyzer dist/stats.json",
"zip": "node scripts/zip.js",
"release": "npm run clean && npm run build && npm run zip"
}
}
Framework Integration Examples
React Integration Example:
// React Popup Component Example
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
function PopupApp() {
const [tabs, setTabs] = useState([]);
const [activeTab, setActiveTab] = useState(null);
useEffect(() => {
// Get all tabs
chrome.tabs.query({}, (tabs) => {
setTabs(tabs);
});
// Get current active tab
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
if (tabs[0]) {
setActiveTab(tabs[0]);
}
});
}, []);
const handleTabClick = (tabId) => {
chrome.tabs.update(tabId, { active: true });
};
return (
<div className="popup-container">
<h2>Tab Manager</h2>
<div className="current-tab">
{activeTab && (
<div>
<strong>Current Tab:</strong>
<p>{activeTab.title}</p>
</div>
)}
</div>
<div className="tab-list">
<h3>All Tabs</h3>
{tabs.map(tab => (
<div
key={tab.id}
className="tab-item"
onClick={() => handleTabClick(tab.id)}
>
<img src={tab.favIconUrl} width="16" height="16" />
<span>{tab.title}</span>
</div>
))}
</div>
</div>
);
}
ReactDOM.render(<PopupApp />, document.getElementById('root'));
Vue Integration Example:
<!-- Vue Popup Component Example -->
<template>
<div id="app">
<h2>{{ title }}</h2>
<div class="settings">
<label>
<input type="checkbox" v-model="darkMode" @change="saveSetting">
Enable Dark Mode
</label>
<label>
<input type="checkbox" v-model="notifications" @change="saveSetting">
Enable Notifications
</label>
</div>
<div class="actions">
<button @click="captureTab">Capture Current Page</button>
<button @click="clearData">Clear Data</button>
</div>
</div>
</template>
<script>
export default {
name: 'PopupApp',
data() {
return {
title: 'Extension Settings',
darkMode: false,
notifications: true
}
},
mounted() {
this.loadSettings();
},
methods: {
loadSettings() {
chrome.storage.sync.get(['darkMode', 'notifications'], (result) => {
this.darkMode = result.darkMode || false;
this.notifications = result.notifications !== false;
});
},
saveSetting() {
chrome.storage.sync.set({
darkMode: this.darkMode,
notifications: this.notifications
});
},
captureTab() {
chrome.tabs.captureVisibleTab((dataUrl) => {
// Handle screenshot
console.log('Screenshot captured');
});
},
clearData() {
if (confirm('Are you sure you want to clear all data?')) {
chrome.storage.sync.clear(() => {
console.log('Data cleared');
this.loadSettings();
});
}
}
}
}
</script>
TypeScript Configuration Example (tsconfig.json):
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"jsx": "react",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"types": ["chrome", "node"],
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
TypeScript Type-Safe Message Handling:
// Chrome API type definitions example
interface ExtensionMessage {
type: string;
payload: any;
}
interface TabInfo {
id: number;
url: string;
title: string;
active: boolean;
}
// Type-safe message handling
chrome.runtime.onMessage.addListener(
(message: ExtensionMessage, sender: chrome.runtime.MessageSender,
sendResponse: (response?: any) => void) => {
switch(message.type) {
case 'GET_TAB_INFO':
const tabInfo: TabInfo = {
id: sender.tab?.id || 0,
url: sender.tab?.url || '',
title: sender.tab?.title || '',
active: sender.tab?.active || false
};
sendResponse(tabInfo);
break;
}
return true;
}
);
Development Workflow Optimization
Automated Workflows
Hot Reload Script (hot-reload.js):
// hot-reload.js - Development environment auto-reload script
const filesInDirectory = dir => new Promise(resolve =>
dir.createReader().readEntries(entries =>
Promise.all(entries.filter(e => e.name[0] !== '.').map(e =>
e.isDirectory
? filesInDirectory(e)
: new Promise(resolve => e.file(resolve))
))
.then(files => [].concat(...files))
.then(resolve)
)
);
const timestampForFilesInDirectory = dir =>
filesInDirectory(dir).then(files =>
files.map(f => f.name + f.lastModified).join()
);
const watchChanges = (dir, lastTimestamp) => {
timestampForFilesInDirectory(dir).then(timestamp => {
if (!lastTimestamp || (lastTimestamp === timestamp)) {
setTimeout(() => watchChanges(dir, timestamp), 1000);
} else {
chrome.runtime.reload();
}
});
};
// Only enable in development environment
if (process.env.NODE_ENV === 'development') {
chrome.management.getSelf(self => {
if (self.installType === 'development') {
chrome.runtime.getPackageDirectoryEntry(dir =>
watchChanges(dir)
);
}
});
}
Git Hooks Configuration:
.git/hooks/pre-commit:
#!/bin/sh
# Run lint checks
npm run lint
# Run tests
npm test
# Check for console.log
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|jsx|ts|tsx)$')
if [ -n "$FILES" ]; then
echo "$FILES" | xargs grep -n "console\.log" && {
echo "Found console.log, please remove before committing"
exit 1
}
fi
exit 0
.git/hooks/pre-push:
#!/bin/sh
# Build project
npm run build
# Run full test suite
npm run test:full
exit 0
.git/hooks/commit-msg:
#!/bin/sh
# Validate commit message format
commit_regex='^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,50}'
if ! grep -qE "$commit_regex" "$1"; then
echo "Commit message format error!"
echo "Format: <type>(<scope>): <subject>"
echo "Example: feat(popup): add new feature"
exit 1
fi
CI/CD Pipeline Configuration (.github/workflows/ci.yml):
name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linting
run: npm run lint
- name: Run tests
run: npm test -- --coverage
- name: Build extension
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
name: extension-build
path: dist/
release:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Build for production
run: npm run build:prod
- name: Create release package
run: npm run package
- name: Upload to Chrome Web Store
env:
CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}
run: |
npx chrome-webstore-upload-cli upload \
--source extension.zip \
--extension-id ${{ secrets.EXTENSION_ID }}
- Use Code Snippets: Create common code templates for quick boilerplate generation
- Configure Aliases: Set path aliases to simplify import statements
- Enable Hot Reload: Automatically reload extensions without manual refresh
- Use Debugging Tools: Fully utilize all Chrome DevTools features
- Version Control: Use Git for version management, commit code regularly
Chapter Summary
This chapter provided detailed coverage of Chrome Extension development environment setup and tool usage:
- Basic Environment Setup: Configured essential tools and project structure for development
- VS Code Configuration: Set up an editor environment suitable for extension development
- Debugging Tool Usage: Mastered various debugging techniques in Chrome DevTools
- Build Tool Configuration: Learned configuration methods for Webpack and Vite
- Framework Integration: Understood integration methods for React, Vue, and TypeScript
- Workflow Optimization: Configured automation tools to improve development efficiency
The next chapter will dive into detailed Manifest file configuration.