@typescript/analyze-trace
Version:
Analyze the output of tsc --generatetrace
206 lines • 9.09 kB
JavaScript
;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.reportHighlights = void 0;
const console_1 = require("console");
const fs = require("fs");
const path = require("path");
const parse_trace_file_1 = require("./parse-trace-file");
const analyze_trace_utilities_1 = require("./analyze-trace-utilities");
async function reportHighlights(tracePath, typesPath, thresholdDuration, minDuration, minPercentage, importExpressionThreshold) {
const parseResult = await (0, parse_trace_file_1.parse)(tracePath, minDuration);
const root = (0, analyze_trace_utilities_1.buildHotPathsTree)(parseResult, thresholdDuration, minPercentage);
const result = {};
const unclosedEvents = parseResult.unclosedStack;
unclosedEvents.reverse();
result["unterminatedEvents"] = undefinedIfEmpty(unclosedEvents);
const hotSpots = await getHotSpots(root, importExpressionThreshold, typesPath);
result["hotSpots"] = undefinedIfEmpty(hotSpots);
const duplicatePackages = await getDuplicateNodeModules(parseResult.nodeModulePaths);
result["duplicatePackages"] = undefinedIfEmpty(duplicatePackages);
console.log(JSON.stringify(result, undefined, 2));
return !!hotSpots.length || !!duplicatePackages.length;
}
exports.reportHighlights = reportHighlights;
async function getDuplicateNodeModules(nodeModulePaths) {
const duplicates = [];
for (const [packageName, packagePaths] of nodeModulePaths.entries()) {
if (packagePaths.length < 2)
continue;
const instances = [];
for (const packagePath of packagePaths) {
instances.push({
path: packagePath,
version: await (0, analyze_trace_utilities_1.getPackageVersion)(packagePath),
});
}
duplicates.push({
name: packageName,
instances,
});
}
return duplicates;
}
async function getHotSpots(root, importExpressionThreshold, typesPath) {
const relatedTypes = typesPath ? await (0, analyze_trace_utilities_1.getRelatedTypes)(root, typesPath, /*leafOnly*/ false) : undefined;
const positionMap = await (0, analyze_trace_utilities_1.getNormalizedPositions)(root, relatedTypes);
const types = typesPath ? await (0, analyze_trace_utilities_1.getTypes)(typesPath) : undefined;
return await getHotSpotsWorker(root, /*currentFile*/ undefined, positionMap, relatedTypes, importExpressionThreshold);
}
async function getHotSpotsWorker(curr, currentFile, positionMap, relatedTypes, importExpressionThreshold) {
var _a, _b;
if (((_a = curr.event) === null || _a === void 0 ? void 0 : _a.cat) === "check") {
const path = curr.event.args.path;
if (path) {
currentFile = path;
}
else {
(0, console_1.assert)(((_b = curr.event) === null || _b === void 0 ? void 0 : _b.name) !== "checkSourceFile", "checkSourceFile should have a path");
}
}
const timeMs = Math.round((curr.end - curr.start) / 1000);
const children = [];
if (curr.children.length) {
// Sort slow to fast
const sortedChildren = curr.children.sort((a, b) => (b.end - b.start) - (a.end - a.start));
for (const child of sortedChildren) {
children.push(...await getHotSpotsWorker(child, currentFile, positionMap, relatedTypes, importExpressionThreshold));
}
}
if (curr.event) {
const hotFrame = await makeHotFrame();
if (hotFrame) {
return [hotFrame];
}
}
return children;
async function makeHotFrame() {
const event = curr.event;
switch (event.name) {
// case "findSourceFile":
// TODO (https://github.com/microsoft/typescript-analyze-trace/issues/2)
case "emitDeclarationFileOrBundle":
const dtsPath = event.args.declarationFilePath;
if (!dtsPath || !fs.existsSync(dtsPath)) {
return undefined;
}
try {
const emittedImports = await (0, analyze_trace_utilities_1.getEmittedImports)(dtsPath, importExpressionThreshold);
if (emittedImports.length === 0) {
return undefined;
}
return {
description: `Emit declarations file`,
timeMs,
path: formatPath(dtsPath),
children,
emittedImports,
};
}
catch (_a) {
return undefined;
}
case "checkSourceFile":
return {
description: `Check file ${formatPath(currentFile)}`,
timeMs,
path: formatPath(currentFile),
children,
};
case "structuredTypeRelatedTo":
const args = event.args;
return {
description: `Compare types ${args.sourceId} and ${args.targetId}`,
timeMs,
children,
types: relatedTypes ? [getHotType(args.sourceId), getHotType(args.targetId)] : undefined,
};
case "getVariancesWorker":
return {
description: `Determine variance of type ${event.args.id}`,
timeMs,
children,
types: relatedTypes ? [getHotType(event.args.id)] : undefined,
};
default:
if (event.cat === "check" && event.args && event.args.pos && event.args.end) {
const frame = {
description: (0, analyze_trace_utilities_1.unmangleCamelCase)(event.name),
timeMs,
path: formatPath(currentFile),
children: undefined,
};
if (positionMap.has(currentFile)) {
const updatedPos = positionMap.get(currentFile).get(event.args.pos.toString());
const updatedEnd = positionMap.get(currentFile).get(event.args.end.toString());
frame.startLine = updatedPos[0];
frame.startChar = updatedPos[1];
frame.endLine = updatedEnd[0];
frame.endChar = updatedEnd[1];
}
else {
frame.startOffset = event.args.pos;
frame.endOffset = event.args.end;
}
// Hack to print the children last for readability
delete frame.children;
frame.children = children;
return frame;
}
return undefined;
}
}
function getHotType(id) {
return worker(id, []);
function worker(id, ancestorIds) {
if (typeof id !== "number")
return;
const type = relatedTypes.get(id);
if (!type)
return undefined;
if (type.location) {
const path = type.location.path;
if (positionMap.has(path)) {
const updatedPosition = positionMap.get(path).get((0, analyze_trace_utilities_1.getLineCharMapKey)(type.location.line, type.location.char));
if (updatedPosition) {
[type.location.line, type.location.char] = updatedPosition;
}
type.location.path = formatPath(path);
}
}
const children = [];
// If there's a cycle, suppress the children, but not the type itself
if (ancestorIds.indexOf(id) < 0) {
ancestorIds.push(id);
for (const prop in type) {
if (prop.match(/type/i)) {
if (Array.isArray(type[prop])) {
for (const t of type[prop]) {
const child = worker(t, ancestorIds);
if (child) {
children.push(child);
}
}
}
else {
const child = worker(type[prop], ancestorIds);
if (child) {
children.push(child);
}
}
}
}
ancestorIds.pop();
}
return { type, children };
}
}
}
function formatPath(p) {
return path.normalize(p);
}
function undefinedIfEmpty(arr) {
return arr.length ? arr : undefined;
}
//# sourceMappingURL=print-trace-analysis-json.js.map