refactor: HMR

This commit is contained in:
cha0s 2024-02-06 08:16:53 -06:00
parent 4bed4730b9
commit 98bbaaa98f
6 changed files with 70 additions and 12 deletions

View File

@ -41,14 +41,16 @@ export const hooks = {
}), }),
/** /**
* Invoked when a fleck is HMR'd * Invoked when a module is HMR'd. Throw to abort hot reload and restart application.
* Must be synchronous.
*
* @param {string} path The path of the fleck * @param {string} path The path of the fleck
* @param {Module} updatedFleck The updated fleck module. * @param {Module} updated The updated module.
* @invoke * @invokeSequential
*/ */
'@flecks/core.hmr': (path, updatedFleck) => { '@flecks/core.hmr': (path, updated) => {
if ('my-fleck' === path) { if ('my-fleck' === path) {
updatedFleck.doSomething(); updated.doSomething();
} }
}, },

View File

@ -70,8 +70,16 @@ module.exports = async (config, env, argv, flecks) => {
// Hooks for each fleck. // Hooks for each fleck.
resolvedPaths.forEach((path) => { resolvedPaths.forEach((path) => {
source.push(` module.hot.accept('${path}', async () => {`); source.push(` module.hot.accept('${path}', async () => {`);
source.push(` global.flecks.refresh('${path}', require('${path}'));`); source.push(` const M = require('${path}')`);
source.push(` global.flecks.invoke('@flecks/core.hmr', '${path}');`); source.push(' try {');
source.push(` global.flecks.invokeSequential('@flecks/core.hmr', '${path}', M);`);
source.push(` global.flecks.refresh('${path}', M);`);
source.push(' }');
source.push(' catch (error) {');
// eslint-disable-next-line no-template-curly-in-string
source.push(' console.error(`HMR failed for fleck: ${error.message}`);');
source.push(' module.hot.invalidate();');
source.push(' }');
source.push(' });'); source.push(' });');
}); });
source.push('}'); source.push('}');

View File

@ -85,12 +85,23 @@ class StartServerPlugin {
args, args,
...(inspectPort && {inspectPort}), ...(inspectPort && {inspectPort}),
}); });
this.worker = cluster.fork(env); const setupListeners = (worker) => {
if (killOnExit) { if (killOnExit) {
this.worker.on('exit', () => { worker.on('exit', () => {
process.exit(); process.exit();
});
}
worker.on('message', (message) => {
if ('hmr-restart' === message) {
// eslint-disable-next-line no-console
console.error('[HMR] Restarting application...');
this.worker = cluster.fork(env);
setupListeners(this.worker);
}
}); });
} };
this.worker = cluster.fork(env);
setupListeners(this.worker);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.worker.on('error', reject); this.worker.on('error', reject);
this.worker.on('online', resolve); this.worker.on('online', resolve);

View File

@ -1,3 +1,4 @@
import cluster from 'cluster';
import {mkdir} from 'fs/promises'; import {mkdir} from 'fs/promises';
import {tmpdir} from 'os'; import {tmpdir} from 'os';
import {join} from 'path'; import {join} from 'path';
@ -30,3 +31,14 @@ import {D, Flecks} from '@flecks/core';
console.error(error); console.error(error);
} }
})(); })();
if (module.hot) {
module.hot.accept('./runtime', () => {
if (cluster.isWorker) {
cluster.worker.send('hmr-restart');
const error = new Error('Restart requested!');
error.stack = '';
throw error;
}
});
}

View File

@ -33,6 +33,18 @@ class SocketWrapper {
}); });
} }
async waitForHmr() {
return new Promise((resolve, reject) => {
this.socket.on('error', reject);
this.socket.on('data', (data) => {
const action = JSON.parse(data.toString());
if ('hmr' === action.type) {
resolve(action);
}
});
});
}
} }
export async function listen() { export async function listen() {

View File

@ -5,8 +5,21 @@ const {
} = process.env; } = process.env;
export const hooks = { export const hooks = {
'@flecks/core.hmr': async (path, M, flecks) => {
if (!flecks.socket) {
return;
}
flecks.socket.write(JSON.stringify({
type: 'hmr',
payload: path,
}));
},
'@flecks/server.up': async (flecks) => { '@flecks/server.up': async (flecks) => {
if (!FLECKS_SERVER_TEST_SOCKET) {
return;
}
const socket = createConnection({path: FLECKS_SERVER_TEST_SOCKET}); const socket = createConnection({path: FLECKS_SERVER_TEST_SOCKET});
flecks.socket = socket;
socket.on('connect', () => { socket.on('connect', () => {
socket.on('data', (data) => { socket.on('data', (data) => {
const {meta, payload, type} = JSON.parse(data); const {meta, payload, type} = JSON.parse(data);