Skip to content

Commit

Permalink
feat: support extension auto-reload in development phase (#14)
Browse files Browse the repository at this point in the history
* feat: support extension auto-reload in development phase

* feat(docs): add README

* fix code styles

---------

Co-authored-by: raojinlin <raojinlin302@gmail.com>
  • Loading branch information
raojinlin and raojinlin authored Sep 18, 2024
1 parent 8d41044 commit a152dc9
Show file tree
Hide file tree
Showing 15 changed files with 318 additions and 173 deletions.
32 changes: 30 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@


## 特性
* 代码实时构建
* 代码实时构建、插件自动加载
* 支持react
* 支持less
* 支持ts
Expand Down Expand Up @@ -42,8 +42,9 @@ npm run build

3. 插件开发

自动编译代码。编译完成后需要重新加载插件
自动编译代码, 编译完成后会自动加载插件

如果没有自动刷新插件,请点击下面的按键刷新。
![Alt text](./screenshot/image.png)

- Linux或Mac
Expand Down Expand Up @@ -149,6 +150,33 @@ consoleLogger.info('message to console');

其中,default配置表示全局配置,在test或者prod环境下可以覆盖default的配置。

#### 配置项

```js
{
// 日志配置
logger: {
// 日志handler: console|http
handler: 'console',
// handler参数
options: {
url: 'https://api.mywebsite.com/api/v1/clientlogs',
method: 'POST',
formatter: 'json',
},
},
// webpack 插件配置
plugins: {
// 自动重载配置
reload: {
// 监听端口
port: 8082,
}
}
},
```


#### 获取配置
```js
import { getConfig } from './lib/config';
Expand Down
2 changes: 1 addition & 1 deletion extension/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<title>Extension Option</title>
</head>
<body>
<div id="root"></div>
<div id="root"></div>
</body>
<script type="text/javascript" src="js/vendors.js"></script>
<script type="text/javascript" src="js/options.js"></script>
Expand Down
2 changes: 1 addition & 1 deletion extension/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<title>Extension Popup</title>
</head>
<body>
<div id="root"></div>
<div id="root"></div>
</body>
<script type="text/javascript" src="js/vendors.js"></script>
<script type="text/javascript" src="js/popup.js"></script>
Expand Down
44 changes: 44 additions & 0 deletions plugins/reload-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const http = require('http');


const pluginName = 'ReloadPlugin';

const defaultHeaders = {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
};

const defaultPort = 8082;


/**
* @typedef {Object} ReloadPluginOptions
* @property {RequestInit.headers|undefined} headers 响应头
* @property {Number|undefined} port 监听端口
*/

class ReloadPlugin {
/**
* @param {ReloadPluginOptions} options
*/
constructor(options={}) {
const server = http.createServer((req, res) => {
res.writeHead(200, {...(options.headers || {}), ...defaultHeaders});
this.sse = res;
});

server.listen(options.port || defaultPort);
}

apply(compiler) {
compiler.hooks.done.tap(pluginName, stats => {
if (!this.sse) return;
this.sse.write(`event: message\ndata: reload\n\n`);
});
}
}


module.exports = ReloadPlugin;
31 changes: 26 additions & 5 deletions src/background.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,46 @@
import { Logger, ConsoleHandler } from "./lib/logger";
import Message from "./lib/message";
import {dispatchMessage} from "./lib/brower";
import { dispatchMessage, sendMessageToTabs } from "./lib/brower";
import { LruCacheWithExpireWithSerialization } from './lib/cache';
import { getConfig } from "./lib/config";

const logger = new Logger('background', new ConsoleHandler());
const message = new Message(logger);
const config = getConfig(process.env.NODE_ENV);
const isDev = process.env.NODE_ENV === 'development';

function main() {
logger.info('hello, background');
// 监听代码更新
if (isDev) {
const eventSource = new EventSource(`http://localhost:${config.plugins.reload.port}/`);
eventSource.onmessage = data => {
if (data.data === 'reload') {
logger.info('reload extension ...')
sendMessageToTabs({currentWindow: true}, {'subject': 'reload'}).then(r => {
chrome.runtime.reload();
}).catch((err) => {
logger.error('send message to tabs error: ', err.message);
});
}
};

eventSource.onerror = error => {
logger.error(error);
};
}
}

addEventListener('active', e => {
console.log(e);
logger.info(e)
})

const cache = new LruCacheWithExpireWithSerialization('service-woker', 100, 3);

console.log(cache);

chrome.runtime.onInstalled.addListener((details) => {
if(details.reason !== "install" && details.reason !== "update") return;
if (details.reason !== "install" && details.reason !== "update") {
return;
}
});

// 监听ping
Expand Down
17 changes: 17 additions & 0 deletions src/components/Content/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from "react";
import Example from "../Example";

import { getURL } from "../../lib/brower";
import styles from './index.less';


export default function Content({ }) {
return (
<div className={styles.content}>
<div className={styles.icon}>
<img src={getURL('images/chrome-icon.png')} style={{width: 50}} />
</div>
<Example />
</div>
);
}
7 changes: 7 additions & 0 deletions src/components/Content/index.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.content {
display: flex;

.icon {
margin-right: 15px;
}
}
7 changes: 4 additions & 3 deletions src/components/Example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ const Example = () => {
const handleClick = React.useCallback(() => {
message.info("hello");
}, []);
return (

return (
<Space wrap>
<Button onClick={handleClick} type="primary">Primary Button</Button>
<Button>Default Button</Button>
<Button onClick={handleClick}>Default Button</Button>
<Button type="dashed">Dashed Button</Button>
<Button type="text">Text Button</Button>
<Button type="link">Link Button</Button>
</Space>
)
};

export default Example;
export default Example;
4 changes: 2 additions & 2 deletions src/components/Options/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from "react";
export default function Options({ }) {
return (
<div>
<a target='_blank' href="https://developer.chrome.com/docs/extensions/mv3/options/">Options page</a>
<a target='_blank' href="https://developer.chrome.com/docs/extensions/mv3/options/">Goto Options page</a>
</div>
);
}
}
23 changes: 15 additions & 8 deletions src/config/config.default.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
export default {
module.exports = {
default: {
logger: {
handler: 'http',
options: {
url: 'https://api.mywebsite.com/api/v1/clientlogs',
method: 'POST',
formatter: 'json',
},
handler: 'console',
options: {
url: 'https://api.mywebsite.com/api/v1/clientlogs',
method: 'POST',
formatter: 'json',
},
},
plugins: {
reload: {
port: 8082,
}
}
}
},
}
41 changes: 23 additions & 18 deletions src/content-script.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,41 @@
import React from 'react';
import ReactDom from 'react-dom/client';
import { getURL } from "./lib/brower";
import Content from './components/Content';
import { dispatchMessage } from "./lib/brower";
import Message from "./lib/message";
import {Logger, ConsoleHandler} from "./lib/logger";
import Example from './components/Example';
import { Logger } from "./lib/logger";
import { getConfig } from './lib/config';

const config = getConfig(process.env.NODE_ENV);
const logger = Logger.createLogger('ContentScript', config.logger.handler, config.logger.options);

function ContentScript({ }) {
return (
<div>
<div>
<img src={getURL('images/chrome-icon.png')} style={{width: 50}} />
</div>
<Example />
</div>
);
}


function main() {
const container = document.createElement('div');

console.log(config);
const message = new Message(logger);
// 发送消息并打印响应
new Message(logger).sendMessage('ping').then(res => {
message.sendMessage('ping').then(res => {
logger.info(res);
});

// 监听 reload 消息, 刷新页面
message.addListener('reload', () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
window.location.reload();
}, 1000);
});
})

dispatchMessage(message);

// 创建React DOM容器
const container = document.createElement('div');
// 追加容器到页面
document.body.prepend(container);
ReactDom.createRoot(container).render(<ContentScript />);
// 挂载React组件
ReactDom.createRoot(container).render(<Content />);
}

main();
Expand Down
10 changes: 8 additions & 2 deletions src/lib/brower.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { Logger } from "./logger";

const logger = Logger.createLogger('browser', 'console');

export const openOptionsPage = () => {
chrome.runtime.openOptionsPage();
Expand All @@ -24,9 +27,12 @@ export const sendMessageToTab = async (tabID, message) => {
}

export const sendMessageToTabs = async (query, message) => {
const tabs = queryTabs(query);
const tabs = await queryTabs(query);

return Promise.all(tabs.map(tab => sendMessageToTab(tab.id, message)));
return Promise.allSettled(tabs.map(tab => {
logger.info('send message to tab: ', tab.id, message)
return sendMessageToTab(tab.id, message);
}));
};

export const dispatchMessage = (message) => {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export class Logger {
}

const logger = new Logger(loggerName, new handlers[handlerName](handlerOptions));
if (handlerOptions.formatter === 'json') {
if (handlerOptions?.formatter === 'json') {
logger.formatter = new JSONFormatter();
}

Expand Down
7 changes: 5 additions & 2 deletions src/lib/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export default class Message {
* @param sendResponse {(function(res: *))}
* @return {Promise<boolean>}
*/
dispatch(request, sender, sendResponse) {
async dispatch(request, sender, sendResponse) {
const { subject, data } = request;
const listeners = this.listeners[subject];
if (!listeners || !listeners.length) {
Expand All @@ -82,7 +82,10 @@ export default class Message {
this.logger.info(`dispatch ${subject} to ${listeners.length} listeners`);
for (let i = 0; i < listeners.length; i++) {
const handler = listeners[i];
handler(request, sender, sendResponse);
const retval = handler(request, sender, sendResponse);
if (retval?.then) {
await retval;
}
}

return true;
Expand Down
Loading

0 comments on commit a152dc9

Please sign in to comment.