UNPKG

@hippy/debug-server-next

Version:
219 lines (218 loc) 8.55 kB
"use strict"; /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.onHMRServerConnection = exports.onHMRClientConnection = void 0; const tslib_1 = require("tslib"); const fs_1 = tslib_1.__importDefault(require("fs")); const path_1 = tslib_1.__importDefault(require("path")); const util_1 = require("util"); const enum_1 = require("@debug-server-next/@types/enum"); const url_1 = require("@debug-server-next/utils/url"); const log_1 = require("@debug-server-next/utils/log"); const pub_sub_channel_1 = require("@debug-server-next/utils/pub-sub-channel"); const db_1 = require("@debug-server-next/db"); const config_1 = require("@debug-server-next/config"); const buffer_1 = require("@debug-server-next/utils/buffer"); const cos_1 = require("@debug-server-next/utils/cos"); const report_1 = require("@debug-server-next/utils/report"); const log = new log_1.Logger('hmr-controller', enum_1.WinstonColor.Blue); const hmrCloseEvent = 'HMR_SERVER_CLOSED'; /** * Pub/Sub HMR msg to client side */ const onHMRClientConnection = async (ws, wsUrlParams) => { const { hash } = wsUrlParams; log.info('HMR client connected'); const { Subscriber, DB } = (0, db_1.getDBOperator)(); const bundle = await new DB(config_1.config.redis.bundleTable).get(hash); if (!bundle) { const reason = 'hippy-dev not started, not support HMR!'; ws.close(enum_1.WSCode.InvalidRequestParams, reason); return log.warn(reason); } report_1.report.event({ name: enum_1.ReportEvent.RemoteHMR, ext2: enum_1.HMRReportExt2.Client, }); const subscriber = new Subscriber((0, pub_sub_channel_1.createHMRChannel)(hash)); const hmrHandler = (msg) => { if (msg === hmrCloseEvent) { subscriber.disconnect(); const reason = 'Hippy dev server closed, stop HMR!'; ws.close(enum_1.WSCode.HMRServerClosed, reason); log.warn(reason); } else { log.verbose('receive HMR msg from redis: %s', msg); const msgObj = JSON.parse(msg.toString()); if (msgObj.performance) { msgObj.performance.serverToApp = Date.now(); } ws.send(JSON.stringify(msgObj)); } }; subscriber.subscribe(hmrHandler); ws.on('close', (code, reason) => { log.info('HMR client closed. code: %s, reason: %s', code, reason); subscriber.disconnect(); }); ws.on('error', (e) => log.error('HMR client ws error: %s', e.stack || e)); }; exports.onHMRClientConnection = onHMRClientConnection; /** * Pub/Sub HMR msg to server side */ const onHMRServerConnection = (ws, wsUrlParams) => { const { hash } = wsUrlParams; log.info('HMR server connected'); const { Publisher, DB } = (0, db_1.getDBOperator)(); const model = new DB(config_1.config.redis.bundleTable); model.upsert(hash, { hash }); const publisher = new Publisher((0, pub_sub_channel_1.createHMRChannel)(hash)); report_1.report.event({ name: enum_1.ReportEvent.RemoteHMR, ext2: enum_1.HMRReportExt2.Server, }); // store all synced dist file, and clean when HMR server disconnect const distFiles = new Set(); ws.on('message', async (msg) => { try { const serverReceive = Date.now(); const { emitList, publicPath, ...emitJSON } = (0, buffer_1.decodeHMRData)(msg); if (emitJSON.performance) { const { hadSyncBundleResource } = emitJSON; const { pcToServer } = emitJSON.performance; const hmrSize = `${Math.ceil(msg.length / 1024)}KB`; const reportData = { name: enum_1.ReportEvent.HMRPCToServer, ext1: hmrSize, ext2: '', }; if ('hadSyncBundleResource' in emitJSON) { reportData.ext2 = hadSyncBundleResource ? enum_1.HMRSyncType.Patch : enum_1.HMRSyncType.FirstTime; report_1.report.event({ name: enum_1.ReportEvent.HMR, ext1: hmrSize, ext2: reportData.ext2, }); } report_1.report.time(serverReceive - pcToServer, reportData); } const folder = (0, url_1.getBaseFolderOfPublicPath)(publicPath); emitList.forEach((file) => { distFiles.add(path_1.default.join(folder, file.name)); }); if (emitList === null || emitList === void 0 ? void 0 : emitList.length) { await saveHMRFiles(folder, emitList); } const msgStr = JSON.stringify(emitJSON); log.verbose('receive HMR msg from PC: %s', msgStr); setTimeout(() => { var _a; if ((_a = emitJSON.messages) === null || _a === void 0 ? void 0 : _a.length) publisher.publish(msgStr); }, 1000); } catch (e) { log.error('decodeHMRData failed: ', (e === null || e === void 0 ? void 0 : e.stack) || e); } }); ws.on('close', async (code, reason) => { log.info('HMR server closed. code: %s, reason: %s', code, reason); await publisher.publish(hmrCloseEvent); publisher.disconnect(); model.delete(hash); cleanHMRFiles(Array.from(distFiles)); }); ws.on('error', (e) => log.error('HMR server ws error: %s', e.stack || e)); }; exports.onHMRServerConnection = onHMRServerConnection; async function saveHMRFiles(folder, emitList) { return Promise.all(emitList.map(async ({ name, content }) => { const saveFn = { [enum_1.StaticFileStorage.COS]: saveHMRFileToCOS, [enum_1.StaticFileStorage.Local]: saveHMRFileToLocal, }[config_1.config.staticFileStorage]; return saveFn(folder, name, content); })); } async function saveHMRFileToLocal(folder, name, content) { const fullFname = path_1.default.resolve(config_1.config.hmrStaticPath, folder, name); const cacheFolder = path_1.default.dirname(fullFname); await fs_1.default.promises.mkdir(cacheFolder, { recursive: true }); return fs_1.default.promises.writeFile(fullFname, content); } async function saveHMRFileToCOS(folder, name, content) { if (sensitiveFiles.some((f) => name.indexOf(f) !== -1)) return; const key = path_1.default.join(folder, name); return (0, cos_1.cosUpload)(key, content); } async function cleanHMRFiles(files) { try { if (files.length === 0) return; log.verbose('clean cached static resources! \n %j', files); if (config_1.config.staticFileStorage === enum_1.StaticFileStorage.COS) { await (0, cos_1.deleteObjects)(files); } else { await Promise.all(files.map((file) => { const fullFname = path_1.default.join(config_1.config.hmrStaticPath, file); return (0, util_1.promisify)(fs_1.default.rm)(fullFname, { force: true, recursive: true }); })); } } catch (e) { log.error('clean cached static resources failed! %j', e.stack || e); } } const sensitiveFiles = [ '.DS_Store', '.bashrc', '.kshrc', '.zshrc', '.bash_history', '.bash_profile', '.bash_logout', 'known_hosts', '.mysql_history', 'authorized_keys', '.svn/entries', '.svn/format', '.git/config', '.git/logs/HEAD', 'error.log', 'access.log', 'a.out', 'user.cfg', 'global.cfg', 'config.ini', '.htaccess', 'README.md', 'private.key', '.idea/workspace.xml', 'web.config', '.env', 'package.json', ];