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