Duffer Derek
"use strict";
const hmrJSBundle = require("./DeltaBundler/Serializers/hmrJSBundle");
const GraphNotFoundError = require("./IncrementalBundler/GraphNotFoundError");
const RevisionNotFoundError = require("./IncrementalBundler/RevisionNotFoundError");
const debounceAsyncQueue = require("./lib/debounceAsyncQueue");
const formatBundlingError = require("./lib/formatBundlingError");
const getGraphId = require("./lib/getGraphId");
const parseOptionsFromUrl = require("./lib/parseOptionsFromUrl");
const splitBundleOptions = require("./lib/splitBundleOptions");
const transformHelpers = require("./lib/transformHelpers");
const {
Logger: { createActionStartEntry, createActionEndEntry, log },
} = require("metro-core");
const nullthrows = require("nullthrows");
const url = require("url");
function send(sendFns, message) {
const strMessage = JSON.stringify(message);
sendFns.forEach((sendFn) => sendFn(strMessage));
}
class HmrServer {
constructor(bundler, createModuleId, config) {
this._config = config;
this._bundler = bundler;
this._createModuleId = createModuleId;
this._clientGroups = new Map();
}
onClientConnect = async (requestUrl, sendFn) => {
return {
sendFn,
revisionIds: [],
optedIntoHMR: false,
};
};
async _registerEntryPoint(client, requestUrl, sendFn) {
requestUrl = this._config.server.rewriteRequestUrl(requestUrl);
const clientUrl = nullthrows(url.parse(requestUrl, true));
const options = parseOptionsFromUrl(
requestUrl,
new Set(this._config.resolver.platforms)
);
const { entryFile, resolverOptions, transformOptions, graphOptions } =
splitBundleOptions(options);
const resolutionFn = await transformHelpers.getResolveDependencyFn(
this._bundler.getBundler(),
transformOptions.platform,
resolverOptions
);
const resolvedEntryFilePath = resolutionFn(
(this._config.server.unstable_serverRoot ?? this._config.projectRoot) +
"/.",
{
name: entryFile,
data: {
key: entryFile,
asyncType: null,
isESMImport: false,
locs: [],
},
}
).filePath;
const graphId = getGraphId(resolvedEntryFilePath, transformOptions, {
resolverOptions,
shallow: graphOptions.shallow,
lazy: graphOptions.lazy,
unstable_allowRequireContext:
this._config.transformer.unstable_allowRequireContext,
});
const revPromise = this._bundler.getRevisionByGraphId(graphId);
if (!revPromise) {
send([sendFn], {
type: "error",
body: formatBundlingError(new GraphNotFoundError(graphId)),
});
return;
}
const { graph, id } = await revPromise;
client.revisionIds.push(id);
let clientGroup = this._clientGroups.get(id);
if (clientGroup != null) {
clientGroup.clients.add(client);
} else {
clientUrl.protocol = "http";
const {
dev,
minify,
runModule,
bundleEntry: _bundleEntry,
...query
} = clientUrl.query || {};
clientUrl.query = {
...query,
dev: dev || "true",
minify: minify || "false",
modulesOnly: "true",
runModule: runModule || "false",
shallow: "true",
};
clientUrl.search = "";
clientGroup = {
clients: new Set([client]),
clientUrl,
revisionId: id,
graphOptions,
unlisten: () => unlisten(),
};
this._clientGroups.set(id, clientGroup);
let latestEventArgs = [];
const debounceCallHandleFileChange = debounceAsyncQueue(async () => {
await this._handleFileChange(
nullthrows(clientGroup),
{
isInitialUpdate: false,
},
...latestEventArgs
);
}, 50);
const unlisten = this._bundler
.getDeltaBundler()
.listen(graph, async (...args) => {
latestEventArgs = args;
await debounceCallHandleFileChange();
});
}
await this._handleFileChange(clientGroup, {
isInitialUpdate: true,
});
send([sendFn], {
type: "bundle-registered",
});
}
onClientMessage = async (client, message, sendFn) => {
let data;
try {
data = JSON.parse(String(message));
} catch (error) {
send([sendFn], {
type: "error",
body: formatBundlingError(error),
});
return Promise.resolve();
}
if (data && data.type) {
switch (data.type) {
case "register-entrypoints":
return Promise.all(
data.entryPoints.map((entryPoint) =>
this._registerEntryPoint(client, entryPoint, sendFn)
)
);
case "log":
if (this._config.server.forwardClientLogs) {
this._config.reporter.update({
type: "client_log",
level: data.level,
data: data.data,
mode: data.mode,
});
}
break;
case "log-opt-in":
client.optedIntoHMR = true;
break;
default:
break;
}
}
return Promise.resolve();
};
onClientError = (client, e) => {
this._config.reporter.update({
type: "hmr_client_error",
error: e.error,
});
this.onClientDisconnect(client);
};
onClientDisconnect = (client) => {
client.revisionIds.forEach((revisionId) => {
const group = this._clientGroups.get(revisionId);
if (group != null) {
if (group.clients.size === 1) {
this._clientGroups.delete(revisionId);
group.unlisten();
} else {
group.clients.delete(client);
}
}
});
};
async _handleFileChange(group, options, changeEvent) {
const logger = !options.isInitialUpdate ? changeEvent?.logger : null;
if (logger) {
logger.point("fileChange_end");
logger.point("hmrPrepareAndSendMessage_start");
}
const optedIntoHMR = [...group.clients].some(
(client) => client.optedIntoHMR
);
const processingHmrChange = log(
createActionStartEntry({
action_name: optedIntoHMR
? "Processing HMR change"
: "Processing HMR change (no client opt-in)",
})
);
const sendFns = [...group.clients].map((client) => client.sendFn);
send(sendFns, {
type: "update-start",
body: options,
});
const message = await this._prepareMessage(group, options, changeEvent);
send(sendFns, message);
send(sendFns, {
type: "update-done",
});
log({
...createActionEndEntry(processingHmrChange),
outdated_modules:
message.type === "update"
? message.body.added.length + message.body.modified.length
: undefined,
});
if (logger) {
logger.point("hmrPrepareAndSendMessage_end");
logger.end("SUCCESS");
}
}
async _prepareMessage(group, options, changeEvent) {
const logger = !options.isInitialUpdate ? changeEvent?.logger : null;
try {
const revPromise = this._bundler.getRevision(group.revisionId);
if (!revPromise) {
return {
type: "error",
body: formatBundlingError(
new RevisionNotFoundError(group.revisionId)
),
};
}
logger?.point("updateGraph_start");
const { revision, delta } = await this._bundler.updateGraph(
await revPromise,
false
);
logger?.point("updateGraph_end");
this._clientGroups.delete(group.revisionId);
group.revisionId = revision.id;
for (const client of group.clients) {
client.revisionIds = client.revisionIds.filter(
(revisionId) => revisionId !== group.revisionId
);
client.revisionIds.push(revision.id);
}
this._clientGroups.set(group.revisionId, group);
logger?.point("serialize_start");
const hmrUpdate = hmrJSBundle(delta, revision.graph, {
clientUrl: group.clientUrl,
createModuleId: this._createModuleId,
includeAsyncPaths: group.graphOptions.lazy,
projectRoot: this._config.projectRoot,
serverRoot:
this._config.server.unstable_serverRoot ?? this._config.projectRoot,
});
logger?.point("serialize_end");
return {
type: "update",
body: {
revisionId: revision.id,
isInitialUpdate: options.isInitialUpdate,
...hmrUpdate,
},
};
} catch (error) {
const formattedError = formatBundlingError(error);
this._config.reporter.update({
type: "bundling_error",
error,
});
return {
type: "error",
body: formattedError,
};
}
}
}
module.exports = HmrServer;
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists