mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
118 lines
3.1 KiB
JavaScript
118 lines
3.1 KiB
JavaScript
const {
|
|
comparePrelinkResult
|
|
} = require('./changesets');
|
|
|
|
// Meteor can load the plugin multiple times
|
|
// when it is a local package
|
|
// Any state that should be preserved is stored in
|
|
// this object
|
|
const sharedState = global.__hotState || {
|
|
initialized: false,
|
|
wsServer: null,
|
|
wsMessageHandler: null,
|
|
prelinkResultHandler: null,
|
|
previousPrelinkResults: {},
|
|
};
|
|
global.__hotState = sharedState;
|
|
|
|
function findLast(array, compare) {
|
|
for (let i = array.length - 1; i > 0; i--) {
|
|
if (compare(array[i])) {
|
|
return array[i]
|
|
}
|
|
}
|
|
}
|
|
|
|
function prelinkResultHandler(prelinkResult) {
|
|
const {
|
|
isApp,
|
|
name,
|
|
bundleArch
|
|
} = prelinkResult;
|
|
|
|
// TODO: we should limit the number of change sets that are saved
|
|
// to avoid a memory leak
|
|
sharedState.previousPrelinkResults[bundleArch] =
|
|
sharedState.previousPrelinkResults[bundleArch] || [];
|
|
|
|
// TODO: support HMR in packages
|
|
// TODO: support HMR in legacy bundle
|
|
if (!isApp || bundleArch !== 'web.browser') {
|
|
// Require a full reload whenever a package is modified
|
|
sharedState.previousPrelinkResults[bundleArch].push({
|
|
name,
|
|
reloadable: false,
|
|
linkedAt: Date.now()
|
|
})
|
|
return;
|
|
}
|
|
|
|
// TODO: Meteor should cache some of this data with the linker cache
|
|
// so we have something to compare with when linking the first time
|
|
// after meteor is started
|
|
const changeSets = sharedState.previousPrelinkResults[bundleArch];
|
|
let previousChangeset = findLast(changeSets, (changeSet) => {
|
|
return changeSet.name === name;
|
|
});
|
|
|
|
const changeset = comparePrelinkResult(previousChangeset, prelinkResult);
|
|
sharedState.previousPrelinkResults[bundleArch].push(changeset);
|
|
}
|
|
|
|
function wsMessageHandler(conn, _message) {
|
|
const message = JSON.parse(_message);
|
|
|
|
switch(message.type) {
|
|
case 'request-changes': {
|
|
const {
|
|
after,
|
|
arch
|
|
} = message;
|
|
|
|
const changesets = sharedState.previousPrelinkResults[arch] || [];
|
|
const newChanges = changesets.filter(({ linkedAt }) => {
|
|
return linkedAt > after;
|
|
});
|
|
|
|
conn.send(JSON.stringify({
|
|
type: 'changes',
|
|
changeSets: newChanges
|
|
}));
|
|
break;
|
|
}
|
|
|
|
default:
|
|
console.warn('Unknown HMR message:', message.type);
|
|
}
|
|
}
|
|
|
|
// Update handlers so event listeners added during initialization can
|
|
// use the latest handlers if this package was modified and rebuilt
|
|
sharedState.prelinkResultHandler = prelinkResultHandler;
|
|
sharedState.wsMessageHandler = wsMessageHandler;
|
|
|
|
function init() {
|
|
sharedState.initialized = true;
|
|
|
|
// TODO: port should be based on port app is using
|
|
// TODO: look into using sockjs instead
|
|
const WebSocket = require('ws');
|
|
sharedState.wsServer = new WebSocket.Server({ port: 3124 });
|
|
|
|
// TODO: should require connections to send a secret before
|
|
// being able to receive changes
|
|
sharedState.wsServer.on('connection', function (ws) {
|
|
ws.on('message', (message) => {
|
|
sharedState.wsMessageHandler(ws, message);
|
|
});
|
|
});
|
|
|
|
Plugin._onPreLinked(function (prelinkResult) {
|
|
sharedState.prelinkResultHandler(prelinkResult);
|
|
});
|
|
}
|
|
|
|
if (!sharedState.initialized) {
|
|
init();
|
|
}
|