UNPKG

@typescript/analyze-trace

Version:
222 lines 8.82 kB
"use strict"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. Object.defineProperty(exports, "__esModule", { value: true }); exports.getRelatedTypes = exports.getEmittedImports = exports.getTypes = exports.unmangleCamelCase = exports.getPackageVersion = exports.getLineCharMapKey = exports.getNormalizedPositions = exports.buildHotPathsTree = void 0; const fs = require("fs"); const path = require("path"); const countImportExpressions = require("./count-import-expressions"); const normalizePositions = require("./normalize-positions"); const simplify = require("./simplify-type"); const jsonstream = require("jsonstream-next"); function buildHotPathsTree(parseResult, thresholdDuration, minPercentage) { const { minTime, maxTime, spans, unclosedStack } = parseResult; for (let i = unclosedStack.length - 1; i >= 0; i--) { const event = unclosedStack[i]; spans.push({ event, start: +event.ts, end: maxTime, children: [] }); } spans.sort((a, b) => a.start - b.start); const root = { start: minTime, end: maxTime, children: [] }; const stack = [root]; for (const span of spans) { let i = stack.length - 1; for (; i > 0; i--) { // No need to check root at stack[0] const curr = stack[i]; if (curr.end > span.start) { // Pop down to parent stack.length = i + 1; break; } } const parent = stack[i]; const duration = span.end - span.start; if (duration >= thresholdDuration || duration >= minPercentage * (parent.end - parent.start)) { parent.children.push(span); stack.push(span); } } return root; } exports.buildHotPathsTree = buildHotPathsTree; async function getNormalizedPositions(root, relatedTypes) { const positionMap = new Map(); recordPositions(root, /*currentFile*/ undefined); if (relatedTypes) { for (const type of relatedTypes.values()) { const location = type.location; if (location) { recordPosition(location.path, [location.line, location.char]); } } } const map = new Map(); // NB: can't use LineChar as map key for (const entry of Array.from(positionMap.entries())) { try { const path = entry[0]; const sourceStream = fs.createReadStream(path, { encoding: "utf-8" }); const rawPositions = entry[1]; const normalizedPositions = await normalizePositions(sourceStream, rawPositions); const pathMap = new Map(); for (let i = 0; i < rawPositions.length; i++) { const rawPosition = rawPositions[i]; const key = typeof rawPosition === "number" ? Math.abs(rawPosition).toString() : getLineCharMapKey(...rawPosition); pathMap.set(key, normalizedPositions[i]); } map.set(path, pathMap); } catch (_a) { // Not finding a file is expected if this isn't the box on which the trace was recorded. } } return map; function recordPositions(span, currentFile) { var _a, _b, _c; if (((_a = span.event) === null || _a === void 0 ? void 0 : _a.name) === "checkSourceFile") { currentFile = span.event.args.path; } else if (((_b = span.event) === null || _b === void 0 ? void 0 : _b.cat) === "check") { const args = span.event.args; currentFile = (_c = args === null || args === void 0 ? void 0 : args.path) !== null && _c !== void 0 ? _c : currentFile; if (currentFile) { if (args === null || args === void 0 ? void 0 : args.pos) { recordPosition(currentFile, args.pos); } if (args === null || args === void 0 ? void 0 : args.end) { recordPosition(currentFile, -args.end); // Negative since end should not be moved past trivia } } } for (const child of span.children) { recordPositions(child, currentFile); } } function recordPosition(path, position) { if (!positionMap.has(path)) { positionMap.set(path, []); } positionMap.get(path).push(position); } } exports.getNormalizedPositions = getNormalizedPositions; function getLineCharMapKey(line, char) { return `${line},${char}`; } exports.getLineCharMapKey = getLineCharMapKey; async function getPackageVersion(packagePath) { try { const jsonPath = path.join(packagePath, "package.json"); const jsonString = await fs.promises.readFile(jsonPath, { encoding: "utf-8" }); const jsonObj = JSON.parse(jsonString); return jsonObj.version; } catch (_a) { } return undefined; } exports.getPackageVersion = getPackageVersion; function unmangleCamelCase(name) { let result = ""; for (const char of [...name]) { if (!result.length) { result += char.toLocaleUpperCase(); continue; } const lower = char.toLocaleLowerCase(); if (char !== lower) { result += " "; } result += lower; } return result; } exports.unmangleCamelCase = unmangleCamelCase; let typesCache; async function getTypes(typesPath) { if (!typesCache) { return new Promise((resolve, _reject) => { typesCache = []; const readStream = fs.createReadStream(typesPath, { encoding: "utf-8" }); readStream.on("end", () => { resolve(typesCache); }); readStream.on("error", onError); // expects types file to be {object[]} const parser = jsonstream.parse("*"); parser.on("data", (data) => { typesCache.push(data); }); parser.on("error", onError); readStream.pipe(parser); function onError(e) { console.error(`Error reading types file: ${e.message}`); resolve(typesCache); } }); } return typesCache; } exports.getTypes = getTypes; async function getEmittedImports(dtsPath, importExpressionThreshold) { const sourceStream = fs.createReadStream(dtsPath, { encoding: "utf-8" }); const frequency = await countImportExpressions(sourceStream); const sorted = Array.from(frequency.entries()) .sort(([import1, count1], [import2, count2]) => count2 - count1 || import1.localeCompare(import2)) .filter(([_, count]) => count >= importExpressionThreshold) .map(([name, count]) => ({ name, count })); return sorted; } exports.getEmittedImports = getEmittedImports; async function getRelatedTypes(root, typesPath, leafOnly) { var _a, _b; const relatedTypes = new Map(); const stack = []; stack.push(root); while (stack.length) { const curr = stack.pop(); if (!leafOnly || curr.children.length === 0) { if (((_a = curr.event) === null || _a === void 0 ? void 0 : _a.name) === "structuredTypeRelatedTo") { const types = await getTypes(typesPath); if (types.length) { addRelatedTypes(types, curr.event.args.sourceId, relatedTypes); addRelatedTypes(types, curr.event.args.targetId, relatedTypes); } } else if (((_b = curr.event) === null || _b === void 0 ? void 0 : _b.name) === "getVariancesWorker") { const types = await getTypes(typesPath); if (types.length) { addRelatedTypes(types, curr.event.args.id, relatedTypes); } } } stack.push(...curr.children); // Order doesn't matter during this traversal } return relatedTypes; } exports.getRelatedTypes = getRelatedTypes; function addRelatedTypes(types, id, relatedTypes) { worker(id); function worker(id) { if (typeof id !== "number") return; const type = types[id - 1]; if (!type) return; // If there's a cycle, suppress the children, but not the type itself if (!relatedTypes.has(id)) { relatedTypes.set(id, simplify(type)); for (const prop in type) { if (prop.match(/type/i)) { if (Array.isArray(type[prop])) { for (const t of type[prop]) { worker(t); } } else { worker(type[prop]); } } } } } } //# sourceMappingURL=analyze-trace-utilities.js.map