128 lines
4.9 KiB
JavaScript
128 lines
4.9 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "TurbopackHmr", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return TurbopackHmr;
|
|
}
|
|
});
|
|
// How long to wait before reporting the HMR start, used to suppress irrelevant
|
|
// `BUILDING` events. Does not impact reported latency.
|
|
const TURBOPACK_HMR_START_DELAY_MS = 100;
|
|
class TurbopackHmr {
|
|
#updatedModules;
|
|
#startMsSinceEpoch;
|
|
#lastUpdateMsSinceEpoch;
|
|
#deferredReportHmrStartId;
|
|
#reportedHmrStart;
|
|
constructor(){
|
|
this.#updatedModules = new Set();
|
|
this.#reportedHmrStart = false;
|
|
}
|
|
// HACK: Turbopack tends to generate a lot of irrelevant "BUILDING" actions,
|
|
// as it reports *any* compilation, including fully no-op/cached compilations
|
|
// and those unrelated to HMR. Fixing this would require significant
|
|
// architectural changes.
|
|
//
|
|
// Work around this by deferring any "rebuilding" message by 100ms. If we get
|
|
// a BUILT event within that threshold and nothing has changed, just suppress
|
|
// the message entirely.
|
|
#runDeferredReportHmrStart() {
|
|
if (this.#deferredReportHmrStartId != null) {
|
|
console.log('[Fast Refresh] rebuilding');
|
|
this.#reportedHmrStart = true;
|
|
this.#cancelDeferredReportHmrStart();
|
|
}
|
|
}
|
|
#cancelDeferredReportHmrStart() {
|
|
clearTimeout(this.#deferredReportHmrStartId);
|
|
this.#deferredReportHmrStartId = undefined;
|
|
}
|
|
onBuilding() {
|
|
this.#lastUpdateMsSinceEpoch = undefined;
|
|
this.#cancelDeferredReportHmrStart();
|
|
this.#startMsSinceEpoch = Date.now();
|
|
// report the HMR start after a short delay
|
|
this.#deferredReportHmrStartId = setTimeout(()=>this.#runDeferredReportHmrStart(), // debugging feature: don't defer/suppress noisy no-op HMR update messages
|
|
self.__NEXT_HMR_TURBOPACK_REPORT_NOISY_NOOP_EVENTS ? 0 : TURBOPACK_HMR_START_DELAY_MS);
|
|
}
|
|
/** Helper for other `onEvent` methods. */ #onUpdate() {
|
|
this.#runDeferredReportHmrStart();
|
|
this.#lastUpdateMsSinceEpoch = Date.now();
|
|
}
|
|
onTurbopackMessage(msg) {
|
|
this.#onUpdate();
|
|
const updatedModules = extractModulesFromTurbopackMessage(msg.data);
|
|
for (const module of updatedModules){
|
|
this.#updatedModules.add(module);
|
|
}
|
|
}
|
|
onServerComponentChanges() {
|
|
this.#onUpdate();
|
|
}
|
|
onReloadPage() {
|
|
this.#onUpdate();
|
|
}
|
|
onPageAddRemove() {
|
|
this.#onUpdate();
|
|
}
|
|
/**
|
|
* @returns `null` if the caller should ignore the update entirely. Returns an
|
|
* object with `hasUpdates: false` if the caller should report the end of
|
|
* the HMR in the browser console, but the HMR was a no-op.
|
|
*/ onBuilt() {
|
|
// Check that we got *any* `TurbopackMessage`, even if
|
|
// `updatedModules` is empty (not everything gets recorded there).
|
|
//
|
|
// There's also a case where `onBuilt` gets called before `onBuilding`,
|
|
// which can happen during initial page load. Ignore that too!
|
|
const hasUpdates = this.#lastUpdateMsSinceEpoch != null && this.#startMsSinceEpoch != null;
|
|
if (!hasUpdates && !this.#reportedHmrStart) {
|
|
// suppress the update entirely
|
|
this.#cancelDeferredReportHmrStart();
|
|
return null;
|
|
}
|
|
this.#runDeferredReportHmrStart();
|
|
const result = {
|
|
hasUpdates,
|
|
updatedModules: this.#updatedModules,
|
|
startMsSinceEpoch: this.#startMsSinceEpoch,
|
|
endMsSinceEpoch: this.#lastUpdateMsSinceEpoch ?? Date.now()
|
|
};
|
|
this.#updatedModules = new Set();
|
|
this.#reportedHmrStart = false;
|
|
return result;
|
|
}
|
|
}
|
|
function extractModulesFromTurbopackMessage(data) {
|
|
const updatedModules = new Set();
|
|
const updates = Array.isArray(data) ? data : [
|
|
data
|
|
];
|
|
for (const update of updates){
|
|
// TODO this won't capture changes to CSS since they don't result in a "merged" update
|
|
if (update.type !== 'partial' || update.instruction.type !== 'ChunkListUpdate' || update.instruction.merged === undefined) {
|
|
continue;
|
|
}
|
|
for (const mergedUpdate of update.instruction.merged){
|
|
for (const name of Object.keys(mergedUpdate.entries)){
|
|
const res = /(.*)\s+[([].*/.exec(name);
|
|
if (res === null) {
|
|
continue;
|
|
}
|
|
updatedModules.add(res[1]);
|
|
}
|
|
}
|
|
}
|
|
return updatedModules;
|
|
}
|
|
|
|
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
|
|
Object.defineProperty(exports.default, '__esModule', { value: true });
|
|
Object.assign(exports.default, exports);
|
|
module.exports = exports.default;
|
|
}
|
|
|
|
//# sourceMappingURL=turbopack-hot-reloader-common.js.map
|