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

enhance: Modify to let the plugin do the build completion detection not file system. #265

Merged
merged 10 commits into from
Nov 11, 2023
Empty file modified .husky/commit-msg
100644 → 100755
Empty file.
Empty file modified .husky/pre-commit
100644 → 100755
Empty file.
15 changes: 3 additions & 12 deletions utils/plugins/make-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ import type { PluginOption } from 'vite';
const { resolve } = path;

const distDir = resolve(__dirname, '..', '..', 'dist');
const publicDir = resolve(__dirname, '..', '..', 'public');

export default function makeManifest(
manifest: chrome.runtime.ManifestV3,
config: { isDev: boolean; contentScriptCssKey?: string },
config: { contentScriptCssKey?: string },
): PluginOption {
function makeManifest(to: string) {
if (!fs.existsSync(to)) {
Expand All @@ -33,16 +32,8 @@ export default function makeManifest(

return {
name: 'make-manifest',
buildStart() {
if (config.isDev) {
makeManifest(distDir);
}
},
buildEnd() {
if (config.isDev) {
return;
}
makeManifest(publicDir);
writeBundle() {
makeManifest(distDir);
},
};
}
13 changes: 12 additions & 1 deletion utils/plugins/watch-rebuild.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
import type { PluginOption } from 'vite';
import { resolve } from 'path';
import { WebSocket } from 'ws';
import MessageInterpreter from '../reload/interpreter';
import { LOCAL_RELOAD_SOCKET_URL } from '../reload/constant';

const rootDir = resolve(__dirname, '..', '..');
const manifestFile = resolve(rootDir, 'manifest.ts');
const viteConfigFile = resolve(rootDir, 'vite.config.ts');

export default function watchRebuild(): PluginOption {
const ws = new WebSocket(LOCAL_RELOAD_SOCKET_URL);
return {
name: 'watch-rebuild',
async buildStart() {
buildStart() {
this.addWatchFile(manifestFile);
this.addWatchFile(viteConfigFile);
},
writeBundle() {
/**
* When the build is complete, send a message to the reload server.
* The reload server will send a message to the client to reload or refresh the extension.
*/
ws.send(MessageInterpreter.send({ type: 'build_complete' }));
},
};
}
3 changes: 0 additions & 3 deletions utils/reload/constant.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
export const LOCAL_RELOAD_SOCKET_PORT = 8081;
export const LOCAL_RELOAD_SOCKET_URL = `ws://localhost:${LOCAL_RELOAD_SOCKET_PORT}`;
export const UPDATE_PENDING_MESSAGE = 'wait_update';
export const UPDATE_REQUEST_MESSAGE = 'do_update';
export const UPDATE_COMPLETE_MESSAGE = 'done_update';
18 changes: 8 additions & 10 deletions utils/reload/initReloadClient.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
LOCAL_RELOAD_SOCKET_URL,
UPDATE_COMPLETE_MESSAGE,
UPDATE_PENDING_MESSAGE,
UPDATE_REQUEST_MESSAGE,
} from './constant';
import { LOCAL_RELOAD_SOCKET_URL } from './constant';
import MessageInterpreter from './interpreter';

let needToUpdate = false;
Expand All @@ -18,22 +13,22 @@ export default function initReloadClient({
const socket = new WebSocket(LOCAL_RELOAD_SOCKET_URL);

function sendUpdateCompleteMessage() {
socket.send(MessageInterpreter.send({ type: UPDATE_COMPLETE_MESSAGE }));
socket.send(MessageInterpreter.send({ type: 'done_update' }));
}

socket.addEventListener('message', event => {
const message = MessageInterpreter.receive(String(event.data));

switch (message.type) {
case UPDATE_REQUEST_MESSAGE: {
case 'do_update': {
if (needToUpdate) {
sendUpdateCompleteMessage();
needToUpdate = false;
onUpdate();
}
return;
}
case UPDATE_PENDING_MESSAGE: {
case 'wait_update': {
if (!needToUpdate) {
needToUpdate = message.path.includes(watchPath);
}
Expand All @@ -43,9 +38,12 @@ export default function initReloadClient({
});

socket.onclose = () => {
console.warn(
console.log(
`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.`,
);
setTimeout(() => {
initReloadClient({ watchPath, onUpdate });
}, 1000);
};

return socket;
Expand Down
46 changes: 12 additions & 34 deletions utils/reload/initReloadServer.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import { WebSocket, WebSocketServer } from 'ws';
import chokidar from 'chokidar';
import { debounce } from './utils';
import {
LOCAL_RELOAD_SOCKET_PORT,
LOCAL_RELOAD_SOCKET_URL,
UPDATE_COMPLETE_MESSAGE,
UPDATE_PENDING_MESSAGE,
UPDATE_REQUEST_MESSAGE,
} from './constant';
import { LOCAL_RELOAD_SOCKET_PORT, LOCAL_RELOAD_SOCKET_URL } from './constant';
import MessageInterpreter from './interpreter';
import { debounce } from './utils';

const clientsThatNeedToUpdate: Set<WebSocket> = new Set();

Expand All @@ -22,10 +16,16 @@ function initReloadServer() {

ws.addEventListener('close', () => clientsThatNeedToUpdate.delete(ws));
ws.addEventListener('message', event => {
const message = MessageInterpreter.receive(String(event.data));
if (message.type === UPDATE_COMPLETE_MESSAGE) {
if (typeof event.data !== 'string') return;

const message = MessageInterpreter.receive(event.data);

if (message.type === 'done_update') {
ws.close();
}
if (message.type === 'build_complete') {
clientsThatNeedToUpdate.forEach((ws: WebSocket) => ws.send(MessageInterpreter.send({ type: 'do_update' })));
}
});
});
}
Expand All @@ -35,31 +35,9 @@ const debounceSrc = debounce(function (path: string) {
// Normalize path on Windows
const pathConverted = path.replace(/\\/g, '/');
clientsThatNeedToUpdate.forEach((ws: WebSocket) =>
ws.send(
MessageInterpreter.send({
type: UPDATE_PENDING_MESSAGE,
path: pathConverted,
}),
),
ws.send(MessageInterpreter.send({ type: 'wait_update', path: pathConverted })),
);
// Delay waiting for public assets to be copied
}, 400);
chokidar.watch('src', { ignorePermissionErrors: true }).on('all', (_, path) => debounceSrc(path));

/** CHECK:: build was completed **/
const debounceDist = debounce(() => {
clientsThatNeedToUpdate.forEach((ws: WebSocket) => {
ws.send(MessageInterpreter.send({ type: UPDATE_REQUEST_MESSAGE }));
});
}, 100);
chokidar.watch('dist', { ignorePermissionErrors: true }).on('all', event => {
// Ignore unlink, unlinkDir and change events
// that happen in beginning of build:watch and
// that will cause ws.send() if it takes more than 400ms
// to build (which it might). This fixes:
// https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/issues/100
if (event !== 'add' && event !== 'addDir') return;
debounceDist();
});
chokidar.watch('src', { ignorePermissionErrors: true }).on('all', (_, path) => debounceSrc(path));

initReloadServer();
2 changes: 0 additions & 2 deletions utils/reload/injections/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ export default function addHmrIntoScript(watchPath: string) {
initReloadClient({
watchPath,
onUpdate: () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
chrome.runtime.reload();
},
});
Expand Down
6 changes: 3 additions & 3 deletions utils/reload/interpreter/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { ReloadMessage, SerializedMessage } from './types';
import type { WebSocketMessage, SerializedMessage } from './types';

export default class MessageInterpreter {
// eslint-disable-next-line @typescript-eslint/no-empty-function
private constructor() {}

static send(message: ReloadMessage): SerializedMessage {
static send(message: WebSocketMessage): SerializedMessage {
return JSON.stringify(message);
}
static receive(serializedMessage: SerializedMessage): ReloadMessage {
static receive(serializedMessage: SerializedMessage): WebSocketMessage {
return JSON.parse(serializedMessage);
}
}
17 changes: 9 additions & 8 deletions utils/reload/interpreter/types.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { UPDATE_COMPLETE_MESSAGE, UPDATE_PENDING_MESSAGE, UPDATE_REQUEST_MESSAGE } from '../constant';

type UpdatePendingMessage = {
type: typeof UPDATE_PENDING_MESSAGE;
type: 'wait_update';
path: string;
};

type UpdateRequestMessage = {
type: typeof UPDATE_REQUEST_MESSAGE;
type: 'do_update';
};

type UpdateCompleteMessage = { type: typeof UPDATE_COMPLETE_MESSAGE };
type UpdateCompleteMessage = { type: 'done_update' };
type BuildCompletionMessage = { type: 'build_complete' };

export type SerializedMessage = string;
export type ReloadMessage = UpdateCompleteMessage | UpdateRequestMessage | UpdatePendingMessage;
export type WebSocketMessage =
| UpdateCompleteMessage
| UpdateRequestMessage
| UpdatePendingMessage
| BuildCompletionMessage;
4 changes: 2 additions & 2 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,11 @@ export default defineConfig({
plugins: [
react(),
makeManifest(manifest, {
isDev,
contentScriptCssKey: regenerateCacheInvalidationKey(),
}),
customDynamicImport(),
addHmr({ background: enableHmrInBackgroundScript, view: true }),
watchRebuild(),
isDev && watchRebuild(),
],
publicDir,
build: {
Expand All @@ -47,6 +46,7 @@ export default defineConfig({
minify: isProduction,
modulePreload: false,
reportCompressedSize: isProduction,
emptyOutDir: false,
rollupOptions: {
input: {
devtools: resolve(pagesDir, 'devtools', 'index.html'),
Expand Down