UNPKG

@hippy/debug-server-next

Version:
191 lines (190 loc) 9.37 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.onAppConnection = exports.onDevtoolsConnection = void 0; const report_1 = require("@debug-server-next/utils/report"); const enum_1 = require("@debug-server-next/@types/enum"); const db_1 = require("@debug-server-next/db"); const client_1 = require("@debug-server-next/client"); const pub_sub_manager_1 = require("@debug-server-next/controller/pub-sub-manager"); const log_1 = require("@debug-server-next/utils/log"); const pub_sub_channel_1 = require("@debug-server-next/utils/pub-sub-channel"); const reload_adapter_1 = require("@debug-server-next/utils/reload-adapter"); const history_event_protocol_1 = require("@debug-server-next/utils/history-event-protocol"); const debug_targets_1 = require("@debug-server-next/controller/debug-targets"); const debug_target_1 = require("@debug-server-next/utils/debug-target"); const log = new log_1.Logger('chrome-devtools', enum_1.WinstonColor.Cyan); /** * pipe devtools ws message to redis pub/sub */ const onDevtoolsConnection = (ws, wsUrlParams) => { const { Subscriber, Publisher } = (0, db_1.getDBOperator)(); const { extensionName, clientId, hash } = wsUrlParams; const downwardChannelId = (0, pub_sub_channel_1.createDownwardChannel)(clientId, extensionName); const upwardChannelId = (0, pub_sub_channel_1.createUpwardChannel)(clientId, extensionName); const internalChannelId = (0, pub_sub_channel_1.createInternalChannel)(clientId, ''); report_1.report.event({ name: enum_1.ReportEvent.ConnectFrontend, ext1: clientId, }); const connectTime = Date.now(); log.info('devtools connected'); log.verbose('subscribe channel: %s, publish channel: %s', downwardChannelId, upwardChannelId); const downwardSubscriber = new Subscriber(downwardChannelId); // internal channel used to listen message between nodes, such as when app ws closed, notify devtools ws close const internalSubscriber = new Subscriber(internalChannelId); const internalPublisher = new Publisher(internalChannelId); const publisher = new Publisher(upwardChannelId); const downwardHandler = (msg) => { ws.send(msg); try { const msgStr = msg; const msgObj = JSON.parse(msgStr); const { ts: start } = msgObj; if (start) { report_1.report.time(Date.now() - start, { name: enum_1.ReportEvent.PubSub, ext1: `${Math.ceil(msgStr.length / 1024)}KB`, ext2: msgObj.method, }); } // evaluate response if (msgObj.id === reload_adapter_1.runtimeEvaluateCommandId) { try { const { result } = msgObj.result; const isVue = result.value.split('+')[0] !== 'undefined'; const isReact = result.value.split('+')[1] !== 'undefined'; log.info('receive evaluate response, welcome to use Hippy-%s', isVue ? 'Vue' : isReact ? 'React' : 'Other'); // get debug target const { clientId } = wsUrlParams; try { (0, debug_target_1.updateDebugTarget)(clientId, { driverType: isVue ? enum_1.AppDriverType.Vue : isReact ? enum_1.AppDriverType.React : enum_1.AppDriverType.Other, }).then((debugTarget) => { log.info('debug target updated for %s', debugTarget.title); // 数据上报 report_1.report.event({ name: enum_1.ReportEvent.ConnectFrontend2, ext1: debugTarget.title, ext2: debugTarget.driverType, ext3: debugTarget.platform, bundleId: debugTarget.bundleId, hostVersion: debugTarget.hostVersion, sdkVersion: debugTarget.sdkVersion, }); }); } catch (e) { log.error('update DebugTarget contextName fail', e.stack); } } catch (e) { log.error('evaluate response parse fail', e.stack); } } } catch (e) { log.error('%s channel message are invalid JSON, %s', downwardChannelId, msg); } }; const internalHandler = (msg) => { if (msg === enum_1.InternalChannelEvent.AppWSClose) { log.verbose('close devtools ws connection'); ws.close(enum_1.WSCode.ClosePage, 'the target page is closed'); internalSubscriber.disconnect(); internalPublisher.disconnect(); } }; downwardSubscriber.subscribe(downwardHandler); internalSubscriber.subscribe(internalHandler); internalPublisher.publish(enum_1.InternalChannelEvent.DevtoolsConnected); // for iOS, must invoke Debugger.disable before devtools frontend connected, otherwise couldn't // receive Debugger.scriptParsed event. // for Android, both way is okay reload_adapter_1.resumeCommands.map(publisher.publish.bind(publisher)); ws.on('message', (msg) => { publisher.publish(msg.toString()); }); ws.on('close', async (code, reason) => { log.info('devtools closed. code: %s, reason: %s', code, reason); await Promise.all(reload_adapter_1.resumeCommands.map(publisher.publish.bind(publisher))); // wait for publish finished publisher.disconnect(); downwardSubscriber.disconnect(); internalSubscriber.disconnect(); internalPublisher.disconnect(); report_1.report.event({ name: enum_1.ReportEvent.DisConnectFrontend, ext1: String(code), ext2: String(Date.now() - connectTime), }); }); ws.on('error', (e) => log.error('devtools ws client error: %j', e)); // send evaluate command to get driver type debug_targets_1.DebugTargetManager.findDebugTarget(clientId, hash).then((debugTarget) => { if (debugTarget.driverType === undefined) { setTimeout(() => { log.info('send evaluate command to get driver type...'); (0, reload_adapter_1.publishEvaluateCommand)(clientId, "typeof __VUE_ROOT_INSTANCES__ + '+' + typeof __REACT_DEVTOOLS_GLOBAL_HOOK__"); }, 2000); } else { log.info('reopen frontend, debugTarget.driverType is %s', debugTarget.driverType); } }); }; exports.onDevtoolsConnection = onDevtoolsConnection; const onAppConnection = async (ws, wsUrlParams, debugTarget) => { const { clientId, clientRole, contextName } = wsUrlParams; log.info('app connected. %s', contextName); const platform = { [enum_1.ClientRole.Android]: enum_1.DevicePlatform.Android, [enum_1.ClientRole.IOS]: enum_1.DevicePlatform.IOS, }[clientRole]; const useWS = client_1.appClientManager.shouldUseAppClientType(platform, "WSAppClient" /* AppClientType.WS */); if (!useWS) return log.verbose('current env is %s, ignore ws connection', global.debugAppArgv.env); (0, pub_sub_manager_1.subscribeCommand)(debugTarget, ws); report_1.report.event({ name: enum_1.ReportEvent.RemoteDebug, ext1: contextName, ext2: platform, }); // when reload, iOS will create a new JSContext, so the debug protocol should resend if (debugTarget.platform === enum_1.DevicePlatform.IOS) (0, reload_adapter_1.publishReloadCommand)(debugTarget); const connectTime = Date.now(); ws.on('close', (code, reason) => { log.info('app closed. code: %j, reason: %s, contextName: %s', code, reason, contextName); (0, history_event_protocol_1.clearHistoryProtocol)(clientId); // when reload page, keep frontend open to debug lifecycle of created const closeDevtools = code === enum_1.WSCode.ClosePage; (0, pub_sub_manager_1.cleanDebugTarget)(clientId, closeDevtools); report_1.report.event({ name: enum_1.ReportEvent.DisRemoteDebug, ext1: String(code), ext2: String(Date.now() - connectTime), }); }); ws.on('error', (e) => log.error('WSAppClient error %j', e)); // log.info(`debug target is: ${JSON.stringify(debugTarget)}`); }; exports.onAppConnection = onAppConnection;