UNPKG

vite-uni-dev-tool

Version:

vite-uni-dev-tool, debug, uni-app, 一处编写,到处调试

746 lines (677 loc) 18.7 kB
import { backup } from '../core'; import { DevEvent } from '../devEvent'; import type { DevTool } from '../type'; import { escapeHTML, getCurrentDate, getCurrentPagePath, parseValue, } from '../utils/index'; import { transformValueToView } from '../utils/language'; /** * 拦截器 * * @export * @class DevIntercept */ export class DevIntercept { private event: DevEvent; initPinia = false; cache$on = new Map(); cache$once = new Map(); cache$emit = new Map(); cache$off = new Map(); constructor(options: DevTool.DevInterceptOptions) { this.event = options.event; this.init(options); } init(options: DevTool.DevInterceptOptions) { this.interceptAppError(); this.interceptAppConsole(); this.interceptSetStorage(); this.interceptRemoveStorage(); this.interceptClearStorage(); this.interceptUploadFile(); this.interceptRequest(); this.interceptSocket(); this.interceptSwitchTab(); this.interceptNavigateTo(); this.interceptUniEvent(); options.enableInterceptPromiseReject && this.interceptPromiseReject(); } /** * app 中拦截 console * app * @memberof DevIntercept */ interceptAppConsole() { if (!(uni as any).__log__) return; const that = this; (uni as any).__log__ = function ( type: DevTool.ConsoleType, line: string, ...args: any ) { const path = getCurrentPagePath(); backup['__log__'](type, line, ...args); that.event.updateConsoleList([ { type, position: path, time: getCurrentDate(), args: args.map((item: any) => { // 处理 item 循环引用的问题 return { type: transformValueToView(item), value: parseValue(item), }; }), stack: line, }, ]); }; } /** * 拦截 vue3 信息 * * @memberof DevIntercept */ interceptVue3(vue3instance: any) { if (vue3instance) { // 微信小程序触发了该事件 vue3instance.appContext.config.errorHandler = ( error: Error, vm: any, info: string, ) => { if (this.event.getDevToolDestroy()) { return; } this.interceptErrorVue3(error); }; vue3instance.appContext.config.warnHandler = ( msg: string, vm: any, trace: string, ) => { if (this.event.getDevToolDestroy()) { return; } this.interceptWarnVue3(msg + '\n' + trace); }; } } /** * app 中捕获全局错误 * * @memberof DevIntercept */ interceptAppError() { uni.onError((error) => { if (this.event.getDevToolDestroy()) { return; } const info = error.toString(); const stack = (error as unknown as Error)?.stack?.split('\n')?.[1] ?? ''; this.event.updateConsoleList([ { type: 'error', args: [ { type: 'string', value: info, }, ], position: getCurrentPagePath(), time: getCurrentDate(), stack, }, ]); }); } /** * 拦截 app 错误 * * @param {*} error * @memberof DevIntercept */ interceptErrorVue3(error: Error) { const stackList = error?.stack?.split('\n'); const stack = stackList?.[1]; console.error(error); this.event.updateConsoleList([ { type: 'error', args: [ { type: 'string', value: error.toString(), }, ], position: getCurrentPagePath(), time: getCurrentDate(), stack, }, ]); } preWarn: any; /** * 拦截 app 警告 * * @param {*} warn * @return {*} * @memberof DevIntercept */ interceptWarnVue3(warn: string) { if (this.preWarn === warn) return; this.preWarn = warn; const path = getCurrentPagePath(); const stackList = new Error()?.stack?.split('\n'); const stack = stackList?.slice(2)?.[0]; console.warn(warn); const args = warn .split('\n') ?.map((item) => escapeHTML(item)) .join('\n'); this.event.updateConsoleList([ { type: 'warn', args: [ { type: 'string', value: args, }, ], position: path, time: getCurrentDate(), stack, }, ]); } /** * 拦截 promise reject * * @memberof DevIntercept */ interceptPromiseReject() { Object.defineProperty(Promise, 'reject', { value: function (reason: any) { this.interceptErrorApp(reason); return backup.reject.call(Promise, reason); }, }); } /** * 拦截 nav 跳转 * * @memberof DevIntercept */ interceptSwitchTab() { const that = this; uni.addInterceptor('switchTab', { invoke(args: UniNamespace.NavigateToOptions) { const complete = args.complete; args.complete = function (e: any) { complete && complete(e); const url = args.url?.slice(1)?.split('?')?.[0] || '/'; that.event.updateCurrentPagePath(url); }; }, }); } /** * 拦截页面跳转 * * @memberof DevIntercept */ interceptNavigateTo() { const that = this; uni.addInterceptor('navigateTo', { invoke(args: UniNamespace.NavigateToOptions) { const complete = args.complete; args.complete = function (e: any) { complete && complete(e); const url = args.url?.slice(1)?.split('?')?.[0] || '/'; that.event.updateCurrentPagePath(url); }; }, }); } /** * 拦截网络请求 * * @memberof DevIntercept */ interceptRequest() { const that = this; uni.addInterceptor('request', { invoke(args: UniNamespace.RequestOptions) { const preIndex = that.event.getRequestIndex(); const index = that.event.setRequestIndex(preIndex + 1); const url = args.url; const noParams = url?.split('?')?.[0]; const lastG = noParams?.lastIndexOf('/'); const name = noParams?.slice(lastG + 1) ?? ''; const addInterceptorRequestData: DevTool.NetworkItem = { index, url: args.url, name, startTime: Date.now(), endTime: 0, time: '-', headers: { requestHeader: Object.entries(args.header ?? {}).map( ([key, value]) => ({ key, value, }), ), responseHeader: [], }, method: args.method || 'GET', status: 'pending', payload: args?.data ? JSON.stringify(args.data) : '', response: '', size: '', }; that.event.updateNetworkList([addInterceptorRequestData]); let _complete = args.complete; args.complete = function (e: any) { _complete && _complete(e); addInterceptorRequestData.status = e.statusCode ?? 'error'; addInterceptorRequestData.endTime = Date.now(); const diff = Date.now() - addInterceptorRequestData.startTime; addInterceptorRequestData.time = diff < 1000 ? diff + 'ms' : diff / 1000 + 's'; const len = e?.header?.['Content-Length'] || e?.header?.['content-length'] || 0; addInterceptorRequestData.size = len > 1024 ? (len / 1024).toFixed(2) + 'k' : len + 'b'; addInterceptorRequestData.response = e; addInterceptorRequestData.headers.responseHeader = Object.entries( e.header ?? {}, ).map(([key, value]) => ({ key, value, })); that.event.updateNetworkList([addInterceptorRequestData], index); }; }, }); } /** * 拦截 websocket * * @memberof DevIntercept */ interceptSocket() { const that = this; const connectSocket = function ( connectOption: UniNamespace.ConnectSocketOption, ) { const url = connectOption.url; const headers = Object.entries(connectOption.header ?? {}).map( ([key, value]) => { return { key, value } as { key: string; value: string }; }, ); const method = (connectOption.method ?? 'GET').toLowerCase(); const protocols = connectOption.protocols ?? []; that.event.updateWsList({ url, headers, method, protocols, readyState: 'connection', message: [], }); const socketTask = backup.connectSocket({ ...connectOption, success: (res) => { connectOption.success?.(res); that.event.updateWsList({ url, readyState: 'open', headers, protocols, message: [ { data: '连接成功', type: 'success', time: Date.now(), }, ], }); }, fail: (error) => { connectOption.fail?.(error); that.event.updateWsList({ url, readyState: 'error', headers, protocols, message: [ { data: '连接失败', type: 'error', time: Date.now(), }, ], }); }, complete: (res) => { connectOption?.complete?.(res); }, }); const send = socketTask.send; socketTask.send = (options: UniApp.SendSocketMessageOptions) => { send.call(socketTask, { data: options.data, fail: (error) => { options?.fail?.(error); that.event.updateWsList({ url, readyState: 'error', headers, protocols, message: [ { data: JSON.stringify(error), type: 'error', time: Date.now(), }, ], }); }, success: (res) => { options?.success?.(res); that.event.updateWsList({ readyState: 'open', url, headers, protocols, message: [ { type: 'success', time: Date.now(), data: options.data?.toString(), }, ], }); }, complete: (res) => { options?.complete?.(res); }, }); }; const close = socketTask.close; socketTask.close = (options: UniApp.CloseSocketOptions) => { that.event.updateWsList({ url, readyState: 'closing', headers, protocols, message: [], }); close.call(socketTask, { ...options, fail: (error) => { options?.fail?.(error); that.event.updateWsList({ url, headers, readyState: 'open', protocols, message: [], }); }, success: (res) => { options?.success?.(res); that.event.updateWsList({ url, headers, readyState: 'closed', protocols, message: [], }); }, complete: (res) => { options?.complete?.(res); }, }); }; socketTask.onMessage((res) => { that.event.updateWsList({ url, headers, protocols, message: [ { type: 'success', time: Date.now(), data: JSON.parse(res.data), }, ], }); }); return socketTask; }; uni.connectSocket = connectSocket; } /** * 拦截 uni.setStorageSync 和 uni.setStorage * * @memberof DevIntercept */ interceptSetStorage() { uni.setStorageSync = (key, value) => { backup.setStorageSync(key.toString(), value); this.event.updateStoreList([ { key: key.toString(), _oldKey: key.toString(), value: value, }, ]); }; uni.setStorage = (options: UniNamespace.SetStorageOptions) => { backup .setStorage({ ...options, key: options.key?.toString(), }) .then(() => { this.event.updateStoreList([ { key: options.key?.toString(), _oldKey: options.key?.toString(), value: options.data, }, ]); }); }; } /** * 拦截 uni.clearStorageSync 和 uni.clearStorage * * @memberof DevIntercept */ interceptClearStorage() { uni.clearStorageSync = () => { backup.clearStorageSync(); this.event.clearStorage(); }; uni.clearStorage = () => { backup.clearStorage(); this.event.clearStorage(); }; } /** * 拦截 uni.removeStorageSync 和 uni.removeStorage * * @memberof DevIntercept */ interceptRemoveStorage() { uni.removeStorageSync = (key) => { backup.removeStorageSync(key); // remove(key); this.event.removeStorage(key); }; uni.removeStorage = (options: UniNamespace.RemoveStorageOptions) => { backup.removeStorage(options).then(() => { this.event.removeStorage(options.key); }); }; } /** 拦截vuex */ interceptVuexStorage(store: any) { this.event.setVuexList(store.state); store?.subscribe?.((_: any, state: any) => { this.event.setVuexList(state); }); } /** 拦截pinia */ interceptPiniaStore(context: any) { if (!this.initPinia) { this.initPinia = true; this.event.setPiniaStore(context.pinia); } this.event.setPiniaList(context.pinia.state.value); context?.store?.$subscribe(() => { this.event.setPiniaList({ [context.store.$id]: context.store.$state, }); }); return context.pinia; } /** * 拦截 uni.uploadFile * * @memberof DevIntercept */ interceptUploadFile() { const that = this; const uploadFile = (uploadOption: UniNamespace.UploadFileOption) => { const preIndex = that.event.getUploadIndex(); const index = that.event.setUploadIndex(preIndex + 1); that.event.updateUploadList( [ { index, name: uploadOption.name, url: uploadOption.url, filePath: uploadOption.filePath, fileType: uploadOption.fileType, headers: { requestHeader: Object.entries(uploadOption.header || {}).map( ([key, value]) => ({ key, value }), ), responseHeader: [], }, formData: uploadOption.formData, status: 'pending', progress: 0, totalBytesSent: 0, totalBytesExpectedToSend: 0, startTime: Date.now(), }, ], index, ); const uploadTask = backup.uploadFile({ ...uploadOption, success: (res) => { uploadOption?.success?.(res); that.event.updateUploadList( [ { index, status: 'success', endTime: Date.now(), response: { ...res, data: JSON.parse(res.data), }, }, ], index, ); that.event.removeUploadTask(index); }, fail: (error) => { uploadOption?.fail?.(error); that.event.updateUploadList( [ { index, status: 'error', response: error, endTime: Date.now(), }, ], index, ); that.event.removeUploadTask(index); }, }); uploadTask.onProgressUpdate((res) => { that.event.updateUploadList( [ { index, progress: res.progress, totalBytesSent: res.totalBytesSent, totalBytesExpectedToSend: res.totalBytesExpectedToSend, status: 'uploading', }, ], index, ); }); that.event.addUploadTask(index, uploadTask); return uploadTask; }; uni.uploadFile = uploadFile.bind(uni); } interceptUniEventFactory(type: DevTool.EventCountKey) { const key = `$${type}` as '$on' | '$emit' | '$once' | '$off'; uni[`$${type}`] = (eventName: string, callback: (result: any) => void) => { const stockList = new Error()?.stack?.split('\n'); const stack = stockList?.[2]; backup?.[key]?.(eventName, callback); this.event.updateUniEventList([ { eventName, timer: getCurrentDate(), stack, type, }, ]); this.event.updateUniEventCount(type); }; } interceptUniEvent() { // 判断是否是相同的位置注册,相同的位置注册只算一次 this.interceptUniEventFactory('on'); this.interceptUniEventFactory('once'); this.interceptUniEventFactory('emit'); this.interceptUniEventFactory('off'); } /** * 监听截屏 * * @memberof DevIntercept */ interceptCaptureScreen() { uni.onUserCaptureScreen(() => { if (!this.event.getDevToolDestroy()) { this.event.updateCaptureScreenList([ { position: getCurrentPagePath(), timer: getCurrentDate(), }, ]); } }); } }