TypeScript/src/compiler/performanceCore.ts

129 lines
6 KiB
TypeScript

/*@internal*/
namespace ts {
// The following definitions provide the minimum compatible support for the Web Performance User Timings API
// between browsers and NodeJS:
export interface PerformanceHooks {
/** Indicates whether we should write native performance events */
shouldWriteNativeEvents: boolean;
performance: Performance;
PerformanceObserver: PerformanceObserverConstructor;
}
export interface Performance {
mark(name: string): void;
measure(name: string, startMark?: string, endMark?: string): void;
now(): number;
timeOrigin: number;
}
export interface PerformanceEntry {
name: string;
entryType: string;
startTime: number;
duration: number;
}
export interface PerformanceObserverEntryList {
getEntries(): PerformanceEntryList;
getEntriesByName(name: string, type?: string): PerformanceEntryList;
getEntriesByType(type: string): PerformanceEntryList;
}
export interface PerformanceObserver {
disconnect(): void;
observe(options: { entryTypes: readonly ("mark" | "measure")[] }): void;
}
export type PerformanceObserverConstructor = new (callback: (list: PerformanceObserverEntryList, observer: PerformanceObserver) => void) => PerformanceObserver;
export type PerformanceEntryList = PerformanceEntry[];
// Browser globals for the Web Performance User Timings API
declare const process: any;
declare const performance: Performance | undefined;
declare const PerformanceObserver: PerformanceObserverConstructor | undefined;
// eslint-disable-next-line @typescript-eslint/naming-convention
function hasRequiredAPI(performance: Performance | undefined, PerformanceObserver: PerformanceObserverConstructor | undefined) {
return typeof performance === "object" &&
typeof performance.timeOrigin === "number" &&
typeof performance.mark === "function" &&
typeof performance.measure === "function" &&
typeof performance.now === "function" &&
typeof PerformanceObserver === "function";
}
function tryGetWebPerformanceHooks(): PerformanceHooks | undefined {
if (typeof performance === "object" &&
typeof PerformanceObserver === "function" &&
hasRequiredAPI(performance, PerformanceObserver)) {
return {
// For now we always write native performance events when running in the browser. We may
// make this conditional in the future if we find that native web performance hooks
// in the browser also slow down compilation.
shouldWriteNativeEvents: true,
performance,
PerformanceObserver
};
}
}
function tryGetNodePerformanceHooks(): PerformanceHooks | undefined {
if (typeof process !== "undefined" && process.nextTick && !process.browser && typeof module === "object" && typeof require === "function") {
try {
let performance: Performance;
const { performance: nodePerformance, PerformanceObserver } = require("perf_hooks") as typeof import("perf_hooks");
if (hasRequiredAPI(nodePerformance, PerformanceObserver)) {
performance = nodePerformance;
// There is a bug in Node's performance.measure prior to 12.16.3/13.13.0 that does not
// match the Web Performance API specification. Node's implementation did not allow
// optional `start` and `end` arguments for `performance.measure`.
// See https://github.com/nodejs/node/pull/32651 for more information.
const version = new Version(process.versions.node);
const range = new VersionRange("<12.16.3 || 13 <13.13");
if (range.test(version)) {
performance = {
get timeOrigin() { return nodePerformance.timeOrigin; },
now() { return nodePerformance.now(); },
mark(name) { return nodePerformance.mark(name); },
measure(name, start = "nodeStart", end?) {
if (end === undefined) {
end = "__performance.measure-fix__";
nodePerformance.mark(end);
}
nodePerformance.measure(name, start, end);
if (end === "__performance.measure-fix__") {
nodePerformance.clearMarks("__performance.measure-fix__");
}
}
};
}
return {
// By default, only write native events when generating a cpu profile or using the v8 profiler.
shouldWriteNativeEvents: false,
performance,
PerformanceObserver
};
}
}
catch {
// ignore errors
}
}
}
// Unlike with the native Map/Set 'tryGet' functions in corePublic.ts, we eagerly evaluate these
// since we will need them for `timestamp`, below.
const nativePerformanceHooks = tryGetWebPerformanceHooks() || tryGetNodePerformanceHooks();
const nativePerformance = nativePerformanceHooks?.performance;
export function tryGetNativePerformanceHooks() {
return nativePerformanceHooks;
}
/** Gets a timestamp with (at least) ms resolution */
export const timestamp =
nativePerformance ? () => nativePerformance.now() :
Date.now ? Date.now :
() => +(new Date());
}