1/* 2 * Copyright (c) 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import * as fs from 'fs'; 17import * as ts from 'typescript'; 18 19export interface CompileEvent { 20 createSubEvent?: (eventName: string) => CompileEvent; 21 createEvent: (eventName: string) => CompileEvent; 22 startAsyncEvent?: () => void; 23 start: () => void; 24 stopAsyncEvent?: () => void; 25 stop: () => void; 26 setTotalTime: (time: number) => void; 27} 28 29interface Event { 30 // id: Unique identifier of the event. 31 id: string; 32 // name: The name of the event. Duplicates exist. 33 name: string; 34 startTime: number; 35 endTime: number; 36 parentEvent: string; 37 parentId: string; 38 duration: number; 39} 40 41export enum AnalyzeMode { 42 DEFAULT, 43 VERBOSE, 44 FALSE, 45 TRACE 46} 47 48export enum ExternalEventType { 49 TSC, 50 ES2ABC 51} 52 53let asyncEvents: Map<string, CompileEvent> = new Map(); 54 55export function isNeedPerformanceDotting(projectConfig: Object): boolean { 56 return projectConfig.perf === AnalyzeMode.TRACE || projectConfig.perf === AnalyzeMode.VERBOSE; 57} 58 59export function getHookEventFactory(share: Object, pluginName: string, hookName: string): CompileEvent | undefined { 60 if (typeof share.getHookEventFactory === 'function' && isNeedPerformanceDotting(share.projectConfig)) { 61 return share.getHookEventFactory(pluginName, hookName); 62 } else { 63 return undefined; 64 } 65} 66 67export function createAndStartEvent(eventOrEventFactory: CompileEvent | undefined, eventName: string, 68 isAsyncEvent = false): CompileEvent | undefined { 69 if (eventOrEventFactory === undefined || eventOrEventFactory === null) { 70 return undefined; 71 } 72 let event: CompileEvent; 73 if (isSubEvent(eventOrEventFactory)) { 74 event = eventOrEventFactory.createSubEvent(eventName); 75 } else { 76 event = eventOrEventFactory.createEvent(eventName); 77 } 78 if (isAsyncEvent) { 79 event.startAsyncEvent(); 80 } else { 81 event.start(); 82 } 83 return event; 84} 85 86function isSubEvent(event: CompileEvent): boolean { 87 return typeof event.createSubEvent === 'function'; 88} 89 90export function stopEvent(event: CompileEvent | undefined, isAsyncEvent: boolean = false): void { 91 if (event === undefined || event === null) { 92 return; 93 } 94 if (isAsyncEvent) { 95 event.stopAsyncEvent(); 96 } else { 97 event.stop(); 98 } 99} 100 101export function processExternalEvents(projectConfig: Object, eventType: number, externalEventsInfo: Object): void { 102 if (!isNeedPerformanceDotting(projectConfig)) { 103 return; 104 } 105 const isTsc: boolean = isTscEvents(eventType); 106 const { parentEvent, filePath } = externalEventsInfo; 107 let events: Event[] = isTsc ? ts.PerformanceDotting.getEventData() : 108 (fs.existsSync(filePath) ? JSON.parse(fs.readFileSync(filePath, 'utf-8')) : []); 109 if (events && events.length) { 110 events = events.sort((a, b) => a.startTime - b.startTime); 111 asyncEvents.set(isTsc ? events[0].parentId : events[0].parentEvent, parentEvent); 112 for (const event of events) { 113 setTotalTime(event, isTsc); 114 } 115 } 116 asyncEvents.clear(); 117} 118 119function isTscEvents(eventType: number): boolean { 120 return eventType === ExternalEventType.TSC; 121} 122 123function setTotalTime(event: Event, isTsc: boolean): void { 124 const parentEvent: CompileEvent | undefined = getCurrentAsyncEvent(isTsc ? event.parentId : event.parentEvent); 125 if (parentEvent) { 126 const subEvent: CompileEvent = parentEvent.createSubEvent(event.name); 127 subEvent.start(); 128 subEvent.stop(); 129 subEvent.setTotalTime(event.duration); 130 asyncEvents.set(isTsc ? event.id : event.name, subEvent); 131 } 132} 133 134function getCurrentAsyncEvent(eventName): CompileEvent | undefined { 135 if (asyncEvents && asyncEvents.has(eventName)) { 136 return asyncEvents.get(eventName); 137 } 138 return undefined; 139} 140 141export function cleanUpAsyncEvents(): void { 142 asyncEvents.clear(); 143}