Developing Chrome DevTools Extensions and Supporting Technologies in San DevTools
The article expands the San DevTools technical analysis by detailing how to build Chrome DevTools extensions—defining a devtools page, creating panels, and debugging them—while also covering supporting technologies such as Yarn‑workspace monorepos, TypeScript path aliases, a lightweight middleware server, message‑batching bridges, and custom SVG icon components.
This article continues the "San DevTools Technical Analysis" series and introduces two main topics: DevTools extension development and several other valuable techniques used in the San DevTools project.
DevTools Extension Development
The article explains that the term "Chrome Extension" refers to a web‑technology based add‑on that enhances browser functionality. A DevTools extension is a special kind of Chrome extension that adds new panels or sidebars to Chrome DevTools and can interact with the inspected page. It can access DevTools APIs such as devtools.inspectedWindow , devtools.network , and devtools.panels .
Implementation consists of three simple steps:
Define devtools_page in manifest.json to point to an HTML file.
Create an HTML file that only loads a JavaScript entry script.
Use chrome.devtools.panels.create to create a custom panel (multiple panels are allowed).
Example manifest:
{
"devtools_page": "devtools.html"
}Example HTML skeleton:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>DevTools</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
</head>
<body>
<script src="/js/devtools.js"></script>
</body>
</html>Creating a panel:
chrome.devtools.panels.create(
'San',
'/icons/logo128.png',
'panel.html'
);The article also lists debugging methods for the different parts of an extension: Content Script (F12 or right‑click → Inspect), DevTools Panel (right‑click inside the panel → Inspect), and Background page (chrome://extensions → Inspect background page).
Other Technologies
Monorepo Project Management
San DevTools uses a monorepo layout managed by Yarn workspaces. The repository contains a packages folder with sub‑packages such as shared and backend . A simplified tree view:
san-devtools
├── packages
│ ├─ shared
│ │ ├─ src
│ │ └─ package.json
│ └─ backend
│ ├─ src
│ └─ package.json
├── tsconfig.json
├── .eslintrc
├── node_modules
└── package.jsonRelevant package.json snippets:
{
"scripts": {
"start": "yarn workspace san-devtools start",
"build:standalone": "yarn workspace san-devtools build",
"start:extensions": "yarn workspace extensions start",
"build:extensions": "yarn workspace extensions build"
},
"workspaces": ["packages/*"],
"files": ["packages"]
}Module Resolution
Path aliases such as @shared and @backend are configured in tsconfig.json and a webpack‑like config:
{
"paths": {
"@backend/*": ["packages/backend/src/*"],
"@frontend/*": ["packages/frontend/src/*"],
"@shared/*": ["packages/shared/src/*"]
}
} const baseConfig = {
resolve: {
alias: {
'@backend': resolve('backend/src/'),
'@shared': resolve('shared/src/'),
'@frontend': resolve('frontend/src/')
}
}
};Simple Middleware Server
A lightweight server class demonstrates how to register middleware functions and handle requests:
class Server {
constructor(options) {
this._middlewares = [];
this.use(...);
this.createServer();
}
createServer() {
this._server = http.createServer((req, res) => {
this._requestHandler(req, res, err => { /* ... */ });
});
}
_requestHandler(req, res, errorHandler) {
let idx = 0;
const middlewares = this._middlewares;
const firstHandler = middlewares[idx];
run(firstHandler);
function next() {
idx++;
if (idx < middlewares.length) run(middlewares[idx]);
}
function run(fn) { fn(req, res, next); }
}
use(fn) { this._middlewares.push(fn); }
};Message Batching
The Bridge class batches events using requestAnimationFrame to reduce send frequency. Key methods include send , _emit , _nextSend , and internal queues for batching and receiving.
const BATCH_DURATION = 100;
export default class Bridge extends EventEmitter {
constructor(wall) { super(); this.wall = wall; /* ... */ }
send(event, payload) { /* push to _batchingQueue, schedule flush */ }
_emit(message) { /* handle string, chunk, or normal messages */ }
_nextSend() { /* send via wall, then requestAnimationFrame */ }
}Iconfont Integration
A custom san-custom-icon component renders SVG icons defined in icons.ts . The component uses a type prop to select the appropriate path data.
<template>
<svg xmlns="http://www.w3.org/2000/svg" class="{{['Icon', className]}}" width="24" height="24" viewBox="0 0 1024 1024">
<template s-for="item in paths">
<path fill="currentColor" d="{{item}}" />
</template>
</svg>
</template>
<script>
import icons from './icon.ts';
export default {
initData() { return { className: '' }; },
computed: {
paths() { return icons[this.data.get('type')] || null; }
}
};
</script>
<style lang="less">
.Icon { width: 1em; height: 1em; fill: currentColor; }
</style>Usage example:
<san-custom-icon type="start-record" />The article concludes with a brief overview of the San ecosystem and a list of reference links.
Baidu App Technology
Official Baidu App Tech Account
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.