Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support extension auto-reload in development phase #14

Merged
merged 3 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading