@shoplflow/extension 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/LICENSE +201 -0
- package/README.md +172 -0
- package/dist/assets/css/contentStyle17107484687.chunk.css +1 -0
- package/dist/assets/css/optionsIndex.chunk.css +1 -0
- package/dist/assets/css/popupIndex.chunk.css +1 -0
- package/dist/assets/js/index.a235624f.js +1 -0
- package/dist/assets/js/jsx-runtime.ea866ad2.js +40 -0
- package/dist/assets/js/twind.60e4b726.js +1 -0
- package/dist/icon-128.png +0 -0
- package/dist/icon-34.png +0 -0
- package/dist/manifest.json +45 -0
- package/dist/src/pages/background/index.js +1 -0
- package/dist/src/pages/content/index.js +1 -0
- package/dist/src/pages/options/index.html +15 -0
- package/dist/src/pages/options/index.js +1 -0
- package/dist/src/pages/popup/index.html +16 -0
- package/dist/src/pages/popup/index.js +918 -0
- package/manifest.ts +37 -0
- package/package.json +57 -0
- package/public/icon-128.png +0 -0
- package/public/icon-34.png +0 -0
- package/public/manifest.json +45 -0
- package/src/Components/ElementListCard.tsx +35 -0
- package/src/assets/img/logo.svg +7 -0
- package/src/environment.d.ts +10 -0
- package/src/global.d.ts +37 -0
- package/src/pages/background/index.ts +11 -0
- package/src/pages/content/components/Demo/app.tsx +93 -0
- package/src/pages/content/components/Demo/index.tsx +27 -0
- package/src/pages/content/index.ts +7 -0
- package/src/pages/content/style.scss +7 -0
- package/src/pages/options/Options.css +8 -0
- package/src/pages/options/Options.tsx +8 -0
- package/src/pages/options/index.css +0 -0
- package/src/pages/options/index.html +12 -0
- package/src/pages/options/index.tsx +18 -0
- package/src/pages/panel/Panel.css +7 -0
- package/src/pages/panel/Panel.tsx +65 -0
- package/src/pages/panel/index.css +0 -0
- package/src/pages/panel/index.html +12 -0
- package/src/pages/panel/index.tsx +20 -0
- package/src/pages/popup/Popup.css +44 -0
- package/src/pages/popup/Popup.tsx +123 -0
- package/src/pages/popup/index.css +13 -0
- package/src/pages/popup/index.html +12 -0
- package/src/pages/popup/index.tsx +20 -0
- package/src/shared/hoc/withErrorBoundary.tsx +42 -0
- package/src/shared/hoc/withSuspense.tsx +14 -0
- package/src/shared/hooks/useStorage.tsx +47 -0
- package/src/shared/storages/base.ts +75 -0
- package/src/shared/storages/componentsInfoStorage.ts +46 -0
- package/src/shared/style/twind.ts +13 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +29 -0
- package/twind.config.ts +7 -0
- package/utils/checkShopl.ts +6 -0
- package/utils/log.ts +52 -0
- package/utils/manifest-parser/index.ts +35 -0
- package/utils/plugins/add-hmr.ts +46 -0
- package/utils/plugins/custom-dynamic-import.ts +34 -0
- package/utils/plugins/make-manifest.ts +49 -0
- package/utils/plugins/watch-rebuild.ts +16 -0
- package/utils/reload/constant.ts +5 -0
- package/utils/reload/initReloadClient.ts +52 -0
- package/utils/reload/initReloadServer.js +72 -0
- package/utils/reload/initReloadServer.ts +68 -0
- package/utils/reload/injections/script.js +60 -0
- package/utils/reload/injections/script.ts +12 -0
- package/utils/reload/injections/view.js +74 -0
- package/utils/reload/injections/view.ts +29 -0
- package/utils/reload/interpreter/index.ts +13 -0
- package/utils/reload/interpreter/types.ts +15 -0
- package/utils/reload/rollup.config.ts +28 -0
- package/utils/reload/utils.ts +9 -0
- package/vite.config.ts +89 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PluginOption } from 'vite';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
|
|
4
|
+
const rootDir = resolve(__dirname, '..', '..');
|
|
5
|
+
const manifestFile = resolve(rootDir, 'manifest.ts');
|
|
6
|
+
const viteConfigFile = resolve(rootDir, 'vite.config.ts');
|
|
7
|
+
|
|
8
|
+
export default function watchRebuild(): PluginOption {
|
|
9
|
+
return {
|
|
10
|
+
name: 'watch-rebuild',
|
|
11
|
+
buildStart() {
|
|
12
|
+
this.addWatchFile(manifestFile);
|
|
13
|
+
this.addWatchFile(viteConfigFile);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const LOCAL_RELOAD_SOCKET_PORT = 8081;
|
|
2
|
+
export const LOCAL_RELOAD_SOCKET_URL = `ws://localhost:${LOCAL_RELOAD_SOCKET_PORT}`;
|
|
3
|
+
export const UPDATE_PENDING_MESSAGE = 'wait_update';
|
|
4
|
+
export const UPDATE_REQUEST_MESSAGE = 'do_update';
|
|
5
|
+
export const UPDATE_COMPLETE_MESSAGE = 'done_update';
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LOCAL_RELOAD_SOCKET_URL,
|
|
3
|
+
UPDATE_COMPLETE_MESSAGE,
|
|
4
|
+
UPDATE_PENDING_MESSAGE,
|
|
5
|
+
UPDATE_REQUEST_MESSAGE,
|
|
6
|
+
} from './constant';
|
|
7
|
+
import MessageInterpreter from './interpreter';
|
|
8
|
+
|
|
9
|
+
let needToUpdate = false;
|
|
10
|
+
|
|
11
|
+
export default function initReloadClient({
|
|
12
|
+
watchPath,
|
|
13
|
+
onUpdate,
|
|
14
|
+
}: {
|
|
15
|
+
watchPath: string;
|
|
16
|
+
onUpdate: () => void;
|
|
17
|
+
}): WebSocket {
|
|
18
|
+
const socket = new WebSocket(LOCAL_RELOAD_SOCKET_URL);
|
|
19
|
+
|
|
20
|
+
function sendUpdateCompleteMessage() {
|
|
21
|
+
socket.send(MessageInterpreter.send({ type: UPDATE_COMPLETE_MESSAGE }));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
socket.addEventListener('message', event => {
|
|
25
|
+
const message = MessageInterpreter.receive(String(event.data));
|
|
26
|
+
|
|
27
|
+
switch (message.type) {
|
|
28
|
+
case UPDATE_REQUEST_MESSAGE: {
|
|
29
|
+
if (needToUpdate) {
|
|
30
|
+
sendUpdateCompleteMessage();
|
|
31
|
+
needToUpdate = false;
|
|
32
|
+
onUpdate();
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
case UPDATE_PENDING_MESSAGE: {
|
|
37
|
+
if (!needToUpdate) {
|
|
38
|
+
needToUpdate = message.path.includes(watchPath);
|
|
39
|
+
}
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
socket.onclose = () => {
|
|
46
|
+
console.warn(
|
|
47
|
+
`Reload server disconnected.\nPlease check if the WebSocket server is running properly on ${LOCAL_RELOAD_SOCKET_URL}. This feature detects changes in the code and helps the browser to reload the extension or refresh the current tab.`,
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return socket;
|
|
52
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { WebSocketServer } from 'ws';
|
|
2
|
+
import chokidar from 'chokidar';
|
|
3
|
+
import { clearTimeout } from 'timers';
|
|
4
|
+
|
|
5
|
+
function debounce(callback, delay) {
|
|
6
|
+
let timer;
|
|
7
|
+
return function (...args) {
|
|
8
|
+
clearTimeout(timer);
|
|
9
|
+
timer = setTimeout(() => callback(...args), delay);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const LOCAL_RELOAD_SOCKET_PORT = 8081;
|
|
14
|
+
const LOCAL_RELOAD_SOCKET_URL = `ws://localhost:${LOCAL_RELOAD_SOCKET_PORT}`;
|
|
15
|
+
const UPDATE_PENDING_MESSAGE = 'wait_update';
|
|
16
|
+
const UPDATE_REQUEST_MESSAGE = 'do_update';
|
|
17
|
+
const UPDATE_COMPLETE_MESSAGE = 'done_update';
|
|
18
|
+
|
|
19
|
+
class MessageInterpreter {
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
21
|
+
constructor() { }
|
|
22
|
+
static send(message) {
|
|
23
|
+
return JSON.stringify(message);
|
|
24
|
+
}
|
|
25
|
+
static receive(serializedMessage) {
|
|
26
|
+
return JSON.parse(serializedMessage);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const clientsThatNeedToUpdate = new Set();
|
|
31
|
+
function initReloadServer() {
|
|
32
|
+
const wss = new WebSocketServer({ port: LOCAL_RELOAD_SOCKET_PORT });
|
|
33
|
+
wss.on('listening', () => console.log(`[HRS] Server listening at ${LOCAL_RELOAD_SOCKET_URL}`));
|
|
34
|
+
wss.on('connection', ws => {
|
|
35
|
+
clientsThatNeedToUpdate.add(ws);
|
|
36
|
+
ws.addEventListener('close', () => clientsThatNeedToUpdate.delete(ws));
|
|
37
|
+
ws.addEventListener('message', event => {
|
|
38
|
+
const message = MessageInterpreter.receive(String(event.data));
|
|
39
|
+
if (message.type === UPDATE_COMPLETE_MESSAGE) {
|
|
40
|
+
ws.close();
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/** CHECK:: src file was updated **/
|
|
46
|
+
const debounceSrc = debounce(function (path) {
|
|
47
|
+
// Normalize path on Windows
|
|
48
|
+
const pathConverted = path.replace(/\\/g, '/');
|
|
49
|
+
clientsThatNeedToUpdate.forEach((ws) => ws.send(MessageInterpreter.send({
|
|
50
|
+
type: UPDATE_PENDING_MESSAGE,
|
|
51
|
+
path: pathConverted,
|
|
52
|
+
})));
|
|
53
|
+
// Delay waiting for public assets to be copied
|
|
54
|
+
}, 400);
|
|
55
|
+
chokidar.watch('src').on('all', (event, path) => debounceSrc(path));
|
|
56
|
+
/** CHECK:: build was completed **/
|
|
57
|
+
const debounceDist = debounce(() => {
|
|
58
|
+
clientsThatNeedToUpdate.forEach((ws) => {
|
|
59
|
+
ws.send(MessageInterpreter.send({ type: UPDATE_REQUEST_MESSAGE }));
|
|
60
|
+
});
|
|
61
|
+
}, 100);
|
|
62
|
+
chokidar.watch('dist').on('all', event => {
|
|
63
|
+
// Ignore unlink, unlinkDir and change events
|
|
64
|
+
// that happen in beginning of build:watch and
|
|
65
|
+
// that will cause ws.send() if it takes more than 400ms
|
|
66
|
+
// to build (which it might). This fixes:
|
|
67
|
+
// https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/issues/100
|
|
68
|
+
if (event !== 'add' && event !== 'addDir')
|
|
69
|
+
return;
|
|
70
|
+
debounceDist();
|
|
71
|
+
});
|
|
72
|
+
initReloadServer();
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { WebSocket } from 'ws';
|
|
2
|
+
import { WebSocketServer } from 'ws';
|
|
3
|
+
import chokidar from 'chokidar';
|
|
4
|
+
import { debounce } from './utils';
|
|
5
|
+
import {
|
|
6
|
+
LOCAL_RELOAD_SOCKET_PORT,
|
|
7
|
+
LOCAL_RELOAD_SOCKET_URL,
|
|
8
|
+
UPDATE_COMPLETE_MESSAGE,
|
|
9
|
+
UPDATE_PENDING_MESSAGE,
|
|
10
|
+
UPDATE_REQUEST_MESSAGE,
|
|
11
|
+
} from './constant';
|
|
12
|
+
import MessageInterpreter from './interpreter';
|
|
13
|
+
|
|
14
|
+
const clientsThatNeedToUpdate: Set<WebSocket> = new Set();
|
|
15
|
+
|
|
16
|
+
function initReloadServer() {
|
|
17
|
+
const wss = new WebSocketServer({ port: LOCAL_RELOAD_SOCKET_PORT });
|
|
18
|
+
|
|
19
|
+
wss.on('listening', () => console.info(`[HRS] Server listening at ${LOCAL_RELOAD_SOCKET_URL}`));
|
|
20
|
+
|
|
21
|
+
wss.on('connection', (ws) => {
|
|
22
|
+
clientsThatNeedToUpdate.add(ws);
|
|
23
|
+
|
|
24
|
+
ws.addEventListener('close', () => clientsThatNeedToUpdate.delete(ws));
|
|
25
|
+
ws.addEventListener('message', (event) => {
|
|
26
|
+
const message = MessageInterpreter.receive(String(event.data));
|
|
27
|
+
if (message.type === UPDATE_COMPLETE_MESSAGE) {
|
|
28
|
+
ws.close();
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** CHECK:: src file was updated **/
|
|
35
|
+
const debounceSrc = debounce(function (path: string) {
|
|
36
|
+
// Normalize path on Windows
|
|
37
|
+
const pathConverted = path.replace(/\\/g, '/');
|
|
38
|
+
clientsThatNeedToUpdate.forEach((ws: WebSocket) =>
|
|
39
|
+
ws.send(
|
|
40
|
+
MessageInterpreter.send({
|
|
41
|
+
type: UPDATE_PENDING_MESSAGE,
|
|
42
|
+
path: pathConverted,
|
|
43
|
+
}),
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
// Delay waiting for public assets to be copied
|
|
47
|
+
}, 400);
|
|
48
|
+
chokidar.watch('src').on('all', (event, path) => debounceSrc(path));
|
|
49
|
+
|
|
50
|
+
/** CHECK:: build was completed **/
|
|
51
|
+
const debounceDist = debounce(() => {
|
|
52
|
+
clientsThatNeedToUpdate.forEach((ws: WebSocket) => {
|
|
53
|
+
ws.send(MessageInterpreter.send({ type: UPDATE_REQUEST_MESSAGE }));
|
|
54
|
+
});
|
|
55
|
+
}, 100);
|
|
56
|
+
chokidar.watch('dist').on('all', (event) => {
|
|
57
|
+
// Ignore unlink, unlinkDir and change events
|
|
58
|
+
// that happen in beginning of build:watch and
|
|
59
|
+
// that will cause ws.send() if it takes more than 400ms
|
|
60
|
+
// to build (which it might). This fixes:
|
|
61
|
+
// https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/issues/100
|
|
62
|
+
if (event !== 'add' && event !== 'addDir') {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
debounceDist();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
initReloadServer();
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const LOCAL_RELOAD_SOCKET_PORT = 8081;
|
|
2
|
+
const LOCAL_RELOAD_SOCKET_URL = `ws://localhost:${LOCAL_RELOAD_SOCKET_PORT}`;
|
|
3
|
+
const UPDATE_PENDING_MESSAGE = 'wait_update';
|
|
4
|
+
const UPDATE_REQUEST_MESSAGE = 'do_update';
|
|
5
|
+
const UPDATE_COMPLETE_MESSAGE = 'done_update';
|
|
6
|
+
|
|
7
|
+
class MessageInterpreter {
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
9
|
+
constructor() { }
|
|
10
|
+
static send(message) {
|
|
11
|
+
return JSON.stringify(message);
|
|
12
|
+
}
|
|
13
|
+
static receive(serializedMessage) {
|
|
14
|
+
return JSON.parse(serializedMessage);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let needToUpdate = false;
|
|
19
|
+
function initReloadClient({ watchPath, onUpdate, }) {
|
|
20
|
+
const socket = new WebSocket(LOCAL_RELOAD_SOCKET_URL);
|
|
21
|
+
function sendUpdateCompleteMessage() {
|
|
22
|
+
socket.send(MessageInterpreter.send({ type: UPDATE_COMPLETE_MESSAGE }));
|
|
23
|
+
}
|
|
24
|
+
socket.addEventListener('message', event => {
|
|
25
|
+
const message = MessageInterpreter.receive(String(event.data));
|
|
26
|
+
switch (message.type) {
|
|
27
|
+
case UPDATE_REQUEST_MESSAGE: {
|
|
28
|
+
if (needToUpdate) {
|
|
29
|
+
sendUpdateCompleteMessage();
|
|
30
|
+
needToUpdate = false;
|
|
31
|
+
onUpdate();
|
|
32
|
+
}
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
case UPDATE_PENDING_MESSAGE: {
|
|
36
|
+
if (!needToUpdate) {
|
|
37
|
+
needToUpdate = message.path.includes(watchPath);
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
socket.onclose = () => {
|
|
44
|
+
console.warn(`Reload server disconnected.\nPlease check if the WebSocket server is running properly on ${LOCAL_RELOAD_SOCKET_URL}. This feature detects changes in the code and helps the browser to reload the extension or refresh the current tab.`);
|
|
45
|
+
};
|
|
46
|
+
return socket;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function addHmrIntoScript(watchPath) {
|
|
50
|
+
initReloadClient({
|
|
51
|
+
watchPath,
|
|
52
|
+
onUpdate: () => {
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
54
|
+
// @ts-ignore
|
|
55
|
+
chrome.runtime.reload();
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { addHmrIntoScript as default };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import initReloadClient from '../initReloadClient';
|
|
2
|
+
|
|
3
|
+
export default function addHmrIntoScript(watchPath: string) {
|
|
4
|
+
initReloadClient({
|
|
5
|
+
watchPath,
|
|
6
|
+
onUpdate: () => {
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
chrome.runtime.reload();
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const LOCAL_RELOAD_SOCKET_PORT = 8081;
|
|
2
|
+
const LOCAL_RELOAD_SOCKET_URL = `ws://localhost:${LOCAL_RELOAD_SOCKET_PORT}`;
|
|
3
|
+
const UPDATE_PENDING_MESSAGE = 'wait_update';
|
|
4
|
+
const UPDATE_REQUEST_MESSAGE = 'do_update';
|
|
5
|
+
const UPDATE_COMPLETE_MESSAGE = 'done_update';
|
|
6
|
+
|
|
7
|
+
class MessageInterpreter {
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
9
|
+
constructor() { }
|
|
10
|
+
static send(message) {
|
|
11
|
+
return JSON.stringify(message);
|
|
12
|
+
}
|
|
13
|
+
static receive(serializedMessage) {
|
|
14
|
+
return JSON.parse(serializedMessage);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let needToUpdate = false;
|
|
19
|
+
function initReloadClient({ watchPath, onUpdate, }) {
|
|
20
|
+
const socket = new WebSocket(LOCAL_RELOAD_SOCKET_URL);
|
|
21
|
+
function sendUpdateCompleteMessage() {
|
|
22
|
+
socket.send(MessageInterpreter.send({ type: UPDATE_COMPLETE_MESSAGE }));
|
|
23
|
+
}
|
|
24
|
+
socket.addEventListener('message', event => {
|
|
25
|
+
const message = MessageInterpreter.receive(String(event.data));
|
|
26
|
+
switch (message.type) {
|
|
27
|
+
case UPDATE_REQUEST_MESSAGE: {
|
|
28
|
+
if (needToUpdate) {
|
|
29
|
+
sendUpdateCompleteMessage();
|
|
30
|
+
needToUpdate = false;
|
|
31
|
+
onUpdate();
|
|
32
|
+
}
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
case UPDATE_PENDING_MESSAGE: {
|
|
36
|
+
if (!needToUpdate) {
|
|
37
|
+
needToUpdate = message.path.includes(watchPath);
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
socket.onclose = () => {
|
|
44
|
+
console.warn(`Reload server disconnected.\nPlease check if the WebSocket server is running properly on ${LOCAL_RELOAD_SOCKET_URL}. This feature detects changes in the code and helps the browser to reload the extension or refresh the current tab.`);
|
|
45
|
+
};
|
|
46
|
+
return socket;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function addHmrIntoView(watchPath) {
|
|
50
|
+
let pendingReload = false;
|
|
51
|
+
initReloadClient({
|
|
52
|
+
watchPath,
|
|
53
|
+
onUpdate: () => {
|
|
54
|
+
// disable reload when tab is hidden
|
|
55
|
+
if (document.hidden) {
|
|
56
|
+
pendingReload = true;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
reload();
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
// reload
|
|
63
|
+
function reload() {
|
|
64
|
+
pendingReload = false;
|
|
65
|
+
window.location.reload();
|
|
66
|
+
}
|
|
67
|
+
// reload when tab is visible
|
|
68
|
+
function reloadWhenTabIsVisible() {
|
|
69
|
+
!document.hidden && pendingReload && reload();
|
|
70
|
+
}
|
|
71
|
+
document.addEventListener('visibilitychange', reloadWhenTabIsVisible);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { addHmrIntoView as default };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import initReloadClient from '../initReloadClient';
|
|
2
|
+
|
|
3
|
+
export default function addHmrIntoView(watchPath: string) {
|
|
4
|
+
let pendingReload = false;
|
|
5
|
+
// reload
|
|
6
|
+
function reload(): void {
|
|
7
|
+
pendingReload = false;
|
|
8
|
+
window.location.reload();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// reload when tab is visible
|
|
12
|
+
function reloadWhenTabIsVisible(): void {
|
|
13
|
+
!document.hidden && pendingReload && reload();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
initReloadClient({
|
|
17
|
+
watchPath,
|
|
18
|
+
onUpdate: () => {
|
|
19
|
+
// disable reload when tab is hidden
|
|
20
|
+
if (document.hidden) {
|
|
21
|
+
pendingReload = true;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
reload();
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
document.addEventListener('visibilitychange', reloadWhenTabIsVisible);
|
|
29
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ReloadMessage, SerializedMessage } from './types';
|
|
2
|
+
|
|
3
|
+
export default class MessageInterpreter {
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
5
|
+
private constructor() {}
|
|
6
|
+
|
|
7
|
+
static send(message: ReloadMessage): SerializedMessage {
|
|
8
|
+
return JSON.stringify(message);
|
|
9
|
+
}
|
|
10
|
+
static receive(serializedMessage: SerializedMessage): ReloadMessage {
|
|
11
|
+
return JSON.parse(serializedMessage) as ReloadMessage;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { UPDATE_COMPLETE_MESSAGE, UPDATE_PENDING_MESSAGE, UPDATE_REQUEST_MESSAGE } from '../constant';
|
|
2
|
+
|
|
3
|
+
type UpdatePendingMessage = {
|
|
4
|
+
type: typeof UPDATE_PENDING_MESSAGE;
|
|
5
|
+
path: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
type UpdateRequestMessage = {
|
|
9
|
+
type: typeof UPDATE_REQUEST_MESSAGE;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type UpdateCompleteMessage = { type: typeof UPDATE_COMPLETE_MESSAGE };
|
|
13
|
+
|
|
14
|
+
export type SerializedMessage = string;
|
|
15
|
+
export type ReloadMessage = UpdateCompleteMessage | UpdateRequestMessage | UpdatePendingMessage;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import typescript from '@rollup/plugin-typescript';
|
|
2
|
+
|
|
3
|
+
const plugins = [typescript()];
|
|
4
|
+
|
|
5
|
+
export default [
|
|
6
|
+
{
|
|
7
|
+
plugins,
|
|
8
|
+
input: 'utils/reload/initReloadServer.ts',
|
|
9
|
+
output: {
|
|
10
|
+
file: 'utils/reload/initReloadServer.js',
|
|
11
|
+
},
|
|
12
|
+
external: ['ws', 'chokidar', 'timers'],
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
plugins,
|
|
16
|
+
input: 'utils/reload/injections/script.ts',
|
|
17
|
+
output: {
|
|
18
|
+
file: 'utils/reload/injections/script.js',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
plugins,
|
|
23
|
+
input: 'utils/reload/injections/view.ts',
|
|
24
|
+
output: {
|
|
25
|
+
file: 'utils/reload/injections/view.js',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { clearTimeout } from 'timers';
|
|
2
|
+
|
|
3
|
+
export function debounce<A extends unknown[]>(callback: (...args: A) => void, delay: number) {
|
|
4
|
+
let timer: NodeJS.Timeout;
|
|
5
|
+
return function (...args: A) {
|
|
6
|
+
clearTimeout(timer);
|
|
7
|
+
timer = setTimeout(() => callback(...args), delay);
|
|
8
|
+
};
|
|
9
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
import path, { resolve } from 'path';
|
|
4
|
+
import makeManifest from './utils/plugins/make-manifest';
|
|
5
|
+
import customDynamicImport from './utils/plugins/custom-dynamic-import';
|
|
6
|
+
import addHmr from './utils/plugins/add-hmr';
|
|
7
|
+
import watchRebuild from './utils/plugins/watch-rebuild';
|
|
8
|
+
import manifest from './manifest';
|
|
9
|
+
|
|
10
|
+
const rootDir = resolve(__dirname);
|
|
11
|
+
const srcDir = resolve(rootDir, 'src');
|
|
12
|
+
const pagesDir = resolve(srcDir, 'pages');
|
|
13
|
+
const assetsDir = resolve(srcDir, 'assets');
|
|
14
|
+
const outDir = resolve(rootDir, 'dist');
|
|
15
|
+
const publicDir = resolve(rootDir, 'public');
|
|
16
|
+
|
|
17
|
+
const isDev = process.env.__DEV__ === 'true';
|
|
18
|
+
const isProduction = !isDev;
|
|
19
|
+
|
|
20
|
+
function generateKey(): string {
|
|
21
|
+
return `${(Date.now() / 100).toFixed()}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function firstUpperCase(str: string) {
|
|
25
|
+
const firstAlphabet = new RegExp(/( |^)[a-z]/, 'g');
|
|
26
|
+
return str.toLowerCase().replace(firstAlphabet, (L) => L.toUpperCase());
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let cacheInvalidationKey: string = generateKey();
|
|
30
|
+
function regenerateCacheInvalidationKey() {
|
|
31
|
+
cacheInvalidationKey = generateKey();
|
|
32
|
+
return cacheInvalidationKey;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ENABLE HMR IN BACKGROUND SCRIPT
|
|
36
|
+
const enableHmrInBackgroundScript = true;
|
|
37
|
+
|
|
38
|
+
export default defineConfig({
|
|
39
|
+
resolve: {
|
|
40
|
+
alias: {
|
|
41
|
+
'@root': rootDir,
|
|
42
|
+
'@src': srcDir,
|
|
43
|
+
'@assets': assetsDir,
|
|
44
|
+
'@pages': pagesDir,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
plugins: [
|
|
48
|
+
react(),
|
|
49
|
+
makeManifest(manifest, {
|
|
50
|
+
isDev,
|
|
51
|
+
contentScriptCssKey: regenerateCacheInvalidationKey(),
|
|
52
|
+
}),
|
|
53
|
+
customDynamicImport(),
|
|
54
|
+
addHmr({ background: enableHmrInBackgroundScript, view: true }),
|
|
55
|
+
watchRebuild(),
|
|
56
|
+
],
|
|
57
|
+
publicDir,
|
|
58
|
+
build: {
|
|
59
|
+
outDir,
|
|
60
|
+
/** Can slowDown build speed. */
|
|
61
|
+
// sourcemap: isDev,
|
|
62
|
+
minify: isProduction,
|
|
63
|
+
modulePreload: false,
|
|
64
|
+
reportCompressedSize: isProduction,
|
|
65
|
+
rollupOptions: {
|
|
66
|
+
input: {
|
|
67
|
+
// devtools: resolve(pagesDir, 'devtools', 'index.html'),
|
|
68
|
+
content: resolve(pagesDir, 'content', 'index.ts'),
|
|
69
|
+
background: resolve(pagesDir, 'background', 'index.ts'),
|
|
70
|
+
contentStyle: resolve(pagesDir, 'content', 'style.scss'),
|
|
71
|
+
popup: resolve(pagesDir, 'popup', 'index.html'),
|
|
72
|
+
options: resolve(pagesDir, 'options', 'index.html'),
|
|
73
|
+
},
|
|
74
|
+
output: {
|
|
75
|
+
entryFileNames: 'src/pages/[name]/index.js',
|
|
76
|
+
chunkFileNames: isDev ? 'assets/js/[name].js' : 'assets/js/[name].[hash].js',
|
|
77
|
+
assetFileNames: (assetInfo) => {
|
|
78
|
+
const { dir, name: _name } = path.parse(assetInfo.name);
|
|
79
|
+
const assetFolder = dir.split('/').at(-1);
|
|
80
|
+
const name = assetFolder + firstUpperCase(_name);
|
|
81
|
+
if (name === 'contentStyle') {
|
|
82
|
+
return `assets/css/contentStyle${cacheInvalidationKey}.chunk.css`;
|
|
83
|
+
}
|
|
84
|
+
return `assets/[ext]/${name}.chunk.[ext]`;
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
});
|