• 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        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}