1/*@internal*/ 2namespace ts { 3 // The following definitions provide the minimum compatible support for the Web Performance User Timings API 4 // between browsers and NodeJS: 5 6 export interface PerformanceHooks { 7 /** Indicates whether we should write native performance events */ 8 shouldWriteNativeEvents: boolean; 9 performance: Performance; 10 PerformanceObserver: PerformanceObserverConstructor; 11 } 12 13 export interface Performance { 14 mark(name: string): void; 15 measure(name: string, startMark?: string, endMark?: string): void; 16 clearMeasures(name?: string): void; 17 clearMarks(name?: string): void; 18 now(): number; 19 timeOrigin: number; 20 } 21 22 export interface PerformanceEntry { 23 name: string; 24 entryType: string; 25 startTime: number; 26 duration: number; 27 } 28 29 export interface PerformanceObserverEntryList { 30 getEntries(): PerformanceEntryList; 31 getEntriesByName(name: string, type?: string): PerformanceEntryList; 32 getEntriesByType(type: string): PerformanceEntryList; 33 } 34 35 export interface PerformanceObserver { 36 disconnect(): void; 37 observe(options: { entryTypes: readonly ("mark" | "measure")[] }): void; 38 } 39 40 export type PerformanceObserverConstructor = new (callback: (list: PerformanceObserverEntryList, observer: PerformanceObserver) => void) => PerformanceObserver; 41 export type PerformanceEntryList = PerformanceEntry[]; 42 43 // Browser globals for the Web Performance User Timings API 44 declare const process: any; 45 declare const performance: Performance | undefined; 46 declare const PerformanceObserver: PerformanceObserverConstructor | undefined; 47 48 // eslint-disable-next-line @typescript-eslint/naming-convention 49 function hasRequiredAPI(performance: Performance | undefined, PerformanceObserver: PerformanceObserverConstructor | undefined) { 50 return typeof performance === "object" && 51 typeof performance.timeOrigin === "number" && 52 typeof performance.mark === "function" && 53 typeof performance.measure === "function" && 54 typeof performance.now === "function" && 55 typeof performance.clearMarks === "function" && 56 typeof performance.clearMeasures === "function" && 57 typeof PerformanceObserver === "function"; 58 } 59 60 function tryGetWebPerformanceHooks(): PerformanceHooks | undefined { 61 if (typeof performance === "object" && 62 typeof PerformanceObserver === "function" && 63 hasRequiredAPI(performance, PerformanceObserver)) { 64 return { 65 // For now we always write native performance events when running in the browser. We may 66 // make this conditional in the future if we find that native web performance hooks 67 // in the browser also slow down compilation. 68 shouldWriteNativeEvents: true, 69 performance, 70 PerformanceObserver 71 }; 72 } 73 } 74 75 function tryGetNodePerformanceHooks(): PerformanceHooks | undefined { 76 if (typeof process !== "undefined" && process.nextTick && !process.browser && typeof module === "object" && typeof require === "function") { 77 try { 78 let performance: Performance; 79 const { performance: nodePerformance, PerformanceObserver } = require("perf_hooks") as typeof import("perf_hooks"); 80 if (hasRequiredAPI(nodePerformance as unknown as Performance, PerformanceObserver)) { 81 performance = nodePerformance as unknown as Performance; 82 // There is a bug in Node's performance.measure prior to 12.16.3/13.13.0 that does not 83 // match the Web Performance API specification. Node's implementation did not allow 84 // optional `start` and `end` arguments for `performance.measure`. 85 // See https://github.com/nodejs/node/pull/32651 for more information. 86 const version = new Version(process.versions.node); 87 const range = new VersionRange("<12.16.3 || 13 <13.13"); 88 if (range.test(version)) { 89 performance = { 90 get timeOrigin() { return nodePerformance.timeOrigin; }, 91 now() { return nodePerformance.now(); }, 92 mark(name) { return nodePerformance.mark(name); }, 93 measure(name, start = "nodeStart", end?) { 94 if (end === undefined) { 95 end = "__performance.measure-fix__"; 96 nodePerformance.mark(end); 97 } 98 nodePerformance.measure(name, start, end); 99 if (end === "__performance.measure-fix__") { 100 nodePerformance.clearMarks("__performance.measure-fix__"); 101 } 102 }, 103 clearMarks(name) { return nodePerformance.clearMarks(name); }, 104 clearMeasures(name) { return (nodePerformance as unknown as Performance).clearMeasures(name); }, 105 }; 106 } 107 return { 108 // By default, only write native events when generating a cpu profile or using the v8 profiler. 109 shouldWriteNativeEvents: false, 110 performance, 111 PerformanceObserver 112 }; 113 } 114 } 115 catch { 116 // ignore errors 117 } 118 } 119 } 120 121 // Unlike with the native Map/Set 'tryGet' functions in corePublic.ts, we eagerly evaluate these 122 // since we will need them for `timestamp`, below. 123 const nativePerformanceHooks = tryGetWebPerformanceHooks() || tryGetNodePerformanceHooks(); 124 const nativePerformance = nativePerformanceHooks?.performance; 125 126 export function tryGetNativePerformanceHooks() { 127 return nativePerformanceHooks; 128 } 129 130 /** Gets a timestamp with (at least) ms resolution */ 131 export const timestamp = 132 nativePerformance ? () => nativePerformance.now() : 133 Date.now ? Date.now : 134 () => +(new Date()); 135}