- 37개 파일 IP → zioinfo.co.kr 치환 (소스/매뉴얼/설정/하네스) - Manager DrConsole/NetworkConsole/CsapConsole 빌드 + /var/www/manager/ 배포 - 테스트: Manager HTTP 200, ITSM 신규 API 7개 전체 200 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
286 lines
8.6 KiB
JavaScript
286 lines
8.6 KiB
JavaScript
"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,
|
|
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;
|