UNPKG

@oaklean/profiler-core

Version:

Part of the @oaklean suite. It provides all basic functions to work with the `.oak` file format. It allows parsing the `.oak` file format as well as tools for analyzing the measurement values. It also provides all necessary capabilities required for prec

810 lines 85.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SourceFileMetaDataTree = void 0; const fs = __importStar(require("fs")); const glob_to_regexp_1 = __importDefault(require("glob-to-regexp")); const BaseModel_1 = require("./BaseModel"); const ModelMap_1 = require("./ModelMap"); const SourceFileMetaData_1 = require("./SourceFileMetaData"); const SourceNodeMetaData_1 = require("./SourceNodeMetaData"); const NodeModule_1 = require("./NodeModule"); const SensorValues_1 = require("./SensorValues"); const PathIndex_1 = require("./indices/PathIndex"); const GlobalIndex_1 = require("./indices/GlobalIndex"); const env_1 = require("../constants/env"); const UnifiedPath_1 = require("../system/UnifiedPath"); const LoggerHelper_1 = require("../helper/LoggerHelper"); const SetHelper_1 = require("../helper/SetHelper"); const PermissionHelper_1 = require("../helper/PermissionHelper"); // Types const types_1 = require("../types"); class SourceFileMetaDataTree extends BaseModel_1.BaseModel { constructor(type, filePath, index) { super(); this.type = type; this.filePath = filePath; this.index = index; } globalIndex() { switch (this.type) { case types_1.SourceFileMetaDataTreeType.Root: return this .index; case types_1.SourceFileMetaDataTreeType.File: return this .index.moduleIndex.globalIndex; case types_1.SourceFileMetaDataTreeType.Directory: case types_1.SourceFileMetaDataTreeType.Module: return this.index.globalIndex; } throw new Error('SourceFileMetaDataTree.globalIndex: unexpected type'); } static isRootNode(data) { return data.type === types_1.SourceFileMetaDataTreeType.Root; } static isFileNode(data) { return data.type === types_1.SourceFileMetaDataTreeType.File; } static isDirectoryNode(data) { return data.type === types_1.SourceFileMetaDataTreeType.Directory; } static isModuleNode(data) { return data.type === types_1.SourceFileMetaDataTreeType.Module; } isRoot() { return SourceFileMetaDataTree.isRootNode(this); } isFile() { return SourceFileMetaDataTree.isFileNode(this); } isDirectory() { return SourceFileMetaDataTree.isDirectoryNode(this); } isModule() { return SourceFileMetaDataTree.isModuleNode(this); } get headlessSensorValues() { if (this._headlessSensorValues === undefined) { this._headlessSensorValues = new SensorValues_1.SensorValues({}); } return this._headlessSensorValues; } set headlessSensorValues(value) { this._headlessSensorValues = value; } get aggregatedLangInternalSourceNodeMetaData() { if (!this._aggregatedLangInternalSourceNodeMetaData) { this._aggregatedLangInternalSourceNodeMetaData = new SourceFileMetaData_1.AggregatedSourceNodeMetaData(new SourceNodeMetaData_1.SourceNodeMetaData(types_1.SourceNodeMetaDataType.Aggregate, undefined, new SensorValues_1.SensorValues({}), undefined), new SourceNodeMetaData_1.SourceNodeMetaData(types_1.SourceNodeMetaDataType.Aggregate, undefined, new SensorValues_1.SensorValues({}), undefined)); } return this._aggregatedLangInternalSourceNodeMetaData; } get aggregatedInternSourceMetaData() { if (!this._aggregatedInternSourceMetaData) { this._aggregatedInternSourceMetaData = new SourceFileMetaData_1.AggregatedSourceNodeMetaData(new SourceNodeMetaData_1.SourceNodeMetaData(types_1.SourceNodeMetaDataType.Aggregate, undefined, new SensorValues_1.SensorValues({}), undefined), new SourceNodeMetaData_1.SourceNodeMetaData(types_1.SourceNodeMetaDataType.Aggregate, undefined, new SensorValues_1.SensorValues({}), undefined)); } return this._aggregatedInternSourceMetaData; } get aggregatedExternSourceMetaData() { if (!this._aggregatedExternSourceMetaData) { this._aggregatedExternSourceMetaData = new SourceFileMetaData_1.AggregatedSourceNodeMetaData(new SourceNodeMetaData_1.SourceNodeMetaData(types_1.SourceNodeMetaDataType.Aggregate, undefined, new SensorValues_1.SensorValues({}), undefined), new SourceNodeMetaData_1.SourceNodeMetaData(types_1.SourceNodeMetaDataType.Aggregate, undefined, new SensorValues_1.SensorValues({}), undefined)); } return this._aggregatedExternSourceMetaData; } get totalAggregatedSourceMetaData() { return SourceFileMetaData_1.AggregatedSourceNodeMetaData.join(this.aggregatedLangInternalSourceNodeMetaData, this.aggregatedInternSourceMetaData, this.aggregatedExternSourceMetaData); } get langInternalChildren() { if (!this._langInternalChildren) { this._langInternalChildren = new ModelMap_1.ModelMap('string'); } return this._langInternalChildren; } get internChildren() { if (!this._internChildren) { this._internChildren = new ModelMap_1.ModelMap('string'); } return this._internChildren; } get externChildren() { if (!this._externChildren) { this._externChildren = new ModelMap_1.ModelMap('string'); } return this._externChildren; } storeToFile(filePath, kind) { switch (kind !== undefined ? kind : 'json') { case 'pretty-json': PermissionHelper_1.PermissionHelper.writeFileWithUserPermission(filePath, JSON.stringify(this, null, 2)); break; case 'json': PermissionHelper_1.PermissionHelper.writeFileWithUserPermission(filePath, JSON.stringify(this)); break; default: break; } } validate() { var _a, _b, _c, _d; if (this.type === types_1.SourceFileMetaDataTreeType.File) { (_a = this.linkedMetaData) === null || _a === void 0 ? void 0 : _a.sourceFileMetaData.validate(); return; } const totals = []; const maxs = []; for (const sourceFileMetaData of this.internChildren.values()) { totals.push(sourceFileMetaData.aggregatedInternSourceMetaData.total); maxs.push(sourceFileMetaData.aggregatedInternSourceMetaData.max); } const total = SourceNodeMetaData_1.SourceNodeMetaData.sum(...totals); const max = SourceNodeMetaData_1.SourceNodeMetaData.max(...maxs); if (this.type === types_1.SourceFileMetaDataTreeType.Root) { total.sensorValues.addToAggregated(this.headlessSensorValues); } if (total.sensorValues.aggregatedCPUTime > total.sensorValues.selfCPUTime + total.sensorValues.internCPUTime + total.sensorValues.externCPUTime + total.sensorValues.langInternalCPUTime || total.sensorValues.aggregatedCPUEnergyConsumption > total.sensorValues.selfCPUEnergyConsumption + total.sensorValues.internCPUEnergyConsumption + total.sensorValues.externCPUEnergyConsumption + total.sensorValues.langInternalCPUEnergyConsumption || total.sensorValues.aggregatedRAMEnergyConsumption > total.sensorValues.selfRAMEnergyConsumption + total.sensorValues.internRAMEnergyConsumption + total.sensorValues.externRAMEnergyConsumption + total.sensorValues.langInternalRAMEnergyConsumption) { LoggerHelper_1.LoggerHelper.error(total.sensorValues, (_b = this.filePath) === null || _b === void 0 ? void 0 : _b.toString()); throw new Error('SourceFileMetaDataTree.validate: Assertion error aggregatedCPUTime is not correct'); } if (!SourceNodeMetaData_1.SourceNodeMetaData.equals(this.aggregatedInternSourceMetaData.max, max)) { LoggerHelper_1.LoggerHelper.error(max, this.aggregatedInternSourceMetaData.max, (_c = this.filePath) === null || _c === void 0 ? void 0 : _c.toString()); throw new Error('SourceFileMetaDataTree.validate: Assertion error max is not correct ' + ((_d = this.filePath) === null || _d === void 0 ? void 0 : _d.toString())); } } toJSON() { var _a, _b, _c, _d, _e; if (env_1.NODE_ENV === 'test') { this.validate(); } return { headlessSensorValues: (_a = this._headlessSensorValues) === null || _a === void 0 ? void 0 : _a.toJSON(), aggregatedLangInternalSourceNodeMetaData: (_b = this._aggregatedLangInternalSourceNodeMetaData) === null || _b === void 0 ? void 0 : _b.toJSON(), aggregatedInternSourceMetaData: (_c = this._aggregatedInternSourceMetaData) === null || _c === void 0 ? void 0 : _c.toJSON(), aggregatedExternSourceMetaData: (_d = this._aggregatedExternSourceMetaData) === null || _d === void 0 ? void 0 : _d.toJSON(), type: this.type, filePath: (_e = this.filePath) === null || _e === void 0 ? void 0 : _e.toJSON(), langInternalChildren: this.langInternalChildren.toJSON(), internChildren: this.internChildren.toJSON() || {}, externChildren: this.externChildren.toJSON(), linkedMetaData: this.linkedMetaData === undefined ? undefined : { internReportID: this.linkedMetaData.internReportID, sourceFileMetaData: this.linkedMetaData.sourceFileMetaData.toJSON() }, globalIndex: (this.isRoot() ? this.index.toJSON() : undefined), engineModule: (this.isRoot() ? this.index.engineModule.toJSON() : undefined) }; } static fromJSON(json, type, indexFromParent) { var _a, _b, _c, _d; let data; if (typeof json === 'string') { data = JSON.parse(json); } else { data = json; } let index; if (type !== data.type) { throw new Error('SourceFileMetaDataTree.fromJSON: given type and type of input data is not the same'); } if (SourceFileMetaDataTree.isRootNode(data)) { if (data.globalIndex === undefined) { throw new Error('SourceFileMetaDataTree.fromJSON: input does not contain a globalIndex'); } index = GlobalIndex_1.GlobalIndex.fromJSON(data.globalIndex, NodeModule_1.NodeModule.fromJSON(data .engineModule)); } else { if (indexFromParent === undefined) { throw new Error('SourceFileMetaDataTree.fromJSON: indexFromParent must be given for non root nodes'); } index = indexFromParent; } const result = new SourceFileMetaDataTree(data.type, (data.type === types_1.SourceFileMetaDataTreeType.File || data.type === types_1.SourceFileMetaDataTreeType.Directory || data.type === types_1.SourceFileMetaDataTreeType.Module ? new UnifiedPath_1.UnifiedPath(data.filePath) : undefined), index); if (data.headlessSensorValues) { result._headlessSensorValues = SensorValues_1.SensorValues.fromJSON(data.headlessSensorValues); } if (data.aggregatedLangInternalSourceNodeMetaData) { result._aggregatedLangInternalSourceNodeMetaData = SourceFileMetaData_1.AggregatedSourceNodeMetaData.fromJSON(data.aggregatedLangInternalSourceNodeMetaData); } if (data.aggregatedInternSourceMetaData) { result._aggregatedInternSourceMetaData = SourceFileMetaData_1.AggregatedSourceNodeMetaData.fromJSON(data.aggregatedInternSourceMetaData); } if (data.aggregatedExternSourceMetaData) { result._aggregatedExternSourceMetaData = SourceFileMetaData_1.AggregatedSourceNodeMetaData.fromJSON(data.aggregatedExternSourceMetaData); } if (data.linkedMetaData !== undefined) { if (index === undefined) { throw new Error('SourceFileMetaDataTree.fromJSON: pathIndex is missing'); } result.linkedMetaData = { internReportID: data.linkedMetaData.internReportID, sourceFileMetaData: SourceFileMetaData_1.SourceFileMetaData.fromJSON(data.linkedMetaData.sourceFileMetaData, index) }; } if (data.langInternalChildren) { for (const [langInternalPath, subTree] of Object.entries(data.langInternalChildren)) { const indexToPass = type === types_1.SourceFileMetaDataTreeType.Root ? (_a = index .getLangInternalIndex('get')) === null || _a === void 0 ? void 0 : _a.getFilePathIndex('get', subTree.filePath) : (_c = (_b = index === null || index === void 0 ? void 0 : index.globalIndex) === null || _b === void 0 ? void 0 : _b.getLangInternalIndex('get')) === null || _c === void 0 ? void 0 : _c.getFilePathIndex('get', subTree.filePath); if (indexToPass === undefined) { LoggerHelper_1.LoggerHelper.error(index.getModuleIndex('get')); throw new Error('SourceFileMetaDataTree.fromJSON: (langInternal children) could not resolve index for subTree'); } result.langInternalChildren.set(langInternalPath, SourceFileMetaDataTree.fromJSON(subTree, subTree.type, indexToPass)); } } if (data.internChildren) { for (const [filePart, subTree] of Object.entries(data.internChildren)) { let indexToPass; switch (subTree.type) { case types_1.SourceFileMetaDataTreeType.Directory: indexToPass = type === types_1.SourceFileMetaDataTreeType.Root ? index.getModuleIndex('get') : index; break; case types_1.SourceFileMetaDataTreeType.File: indexToPass = type === types_1.SourceFileMetaDataTreeType.Root ? (_d = index .getModuleIndex('get')) === null || _d === void 0 ? void 0 : _d.getFilePathIndex('get', subTree.filePath) : index.getFilePathIndex('get', subTree.filePath); break; default: throw new Error('SourceFileMetaDataTree.fromJSON: unexpected subTree type'); } if (indexToPass === undefined) { LoggerHelper_1.LoggerHelper.error(index.getModuleIndex('get')); throw new Error('SourceFileMetaDataTree.fromJSON: (intern children) could not resolve index for subTree'); } result.internChildren.set(filePart, SourceFileMetaDataTree.fromJSON(subTree, subTree.type, indexToPass)); } } if (data.externChildren) { for (const [moduleIdentifier, subTree] of Object.entries(data.externChildren)) { let indexToPass; switch (type) { case types_1.SourceFileMetaDataTreeType.Root: indexToPass = index.getModuleIndex('get', moduleIdentifier); break; case types_1.SourceFileMetaDataTreeType.Module: indexToPass = index.globalIndex.getModuleIndex('get', moduleIdentifier); break; case types_1.SourceFileMetaDataTreeType.Directory: case types_1.SourceFileMetaDataTreeType.File: default: throw new Error('SourceFileMetaDataTree.fromJSON: unexpected subTree type'); } if (indexToPass === undefined) { throw new Error('SourceFileMetaDataTree.fromJSON: (extern children) could not resolve index for subTree'); } result.externChildren.set(moduleIdentifier, SourceFileMetaDataTree.fromJSON(subTree, subTree.type, indexToPass)); } } return result; } static loadFromFile(filePath) { if (!fs.existsSync(filePath.toPlatformString())) { return undefined; } return SourceFileMetaDataTree.fromJSON(fs.readFileSync(filePath.toPlatformString()).toString(), types_1.SourceFileMetaDataTreeType.Root, undefined); } addToAggregatedLangInternalSourceNodeMetaDataOfTree(aggregatedSourceNodeMetaData) { this.aggregatedLangInternalSourceNodeMetaData.total = SourceNodeMetaData_1.SourceNodeMetaData.sum(this.aggregatedLangInternalSourceNodeMetaData.total, aggregatedSourceNodeMetaData.total); this.aggregatedLangInternalSourceNodeMetaData.max = SourceNodeMetaData_1.SourceNodeMetaData.max(this.aggregatedLangInternalSourceNodeMetaData.max, aggregatedSourceNodeMetaData.max); } addToAggregatedInternSourceNodeMetaDataOfTree(aggregatedSourceNodeMetaData) { this.aggregatedInternSourceMetaData.total = SourceNodeMetaData_1.SourceNodeMetaData.sum(this.aggregatedInternSourceMetaData.total, aggregatedSourceNodeMetaData.total); this.aggregatedInternSourceMetaData.max = SourceNodeMetaData_1.SourceNodeMetaData.max(this.aggregatedInternSourceMetaData.max, aggregatedSourceNodeMetaData.max); } addToAggregatedExternSourceNodeMetaDataOfTree(aggregatedSourceNodeMetaData) { this.aggregatedExternSourceMetaData.total = SourceNodeMetaData_1.SourceNodeMetaData.sum(this.aggregatedExternSourceMetaData.total, aggregatedSourceNodeMetaData.total); this.aggregatedExternSourceMetaData.max = SourceNodeMetaData_1.SourceNodeMetaData.max(this.aggregatedExternSourceMetaData.max, aggregatedSourceNodeMetaData.max); } static fromProjectReport(projectReport) { const tree = new SourceFileMetaDataTree(types_1.SourceFileMetaDataTreeType.Root, undefined, projectReport.globalIndex); tree.addProjectReport(projectReport); tree.headlessSensorValues = SensorValues_1.SensorValues.fromJSON(projectReport.headlessSensorValues.toJSON()); return tree; } insertLangInternalPath(internReportID, langInternalPath, aggregatedSourceNodeMetaData, sourceFileMetaData) { var _a; this.addToAggregatedLangInternalSourceNodeMetaDataOfTree(aggregatedSourceNodeMetaData); let child = this.langInternalChildren.get(langInternalPath); if (!child) { const moduleIndex = (_a = (this.isRoot() ? this.index.getModuleIndex('get') : this.index)) === null || _a === void 0 ? void 0 : _a.globalIndex.getLangInternalIndex('get'); const pathIndex = moduleIndex === null || moduleIndex === void 0 ? void 0 : moduleIndex.getFilePathIndex('get', langInternalPath); if (pathIndex === undefined) { throw new Error('SourceFileMetaDataTree.insertLangInternalPath: could not resolve path index'); } child = new SourceFileMetaDataTree(types_1.SourceFileMetaDataTreeType.File, new UnifiedPath_1.UnifiedPath(langInternalPath), pathIndex); child.linkedMetaData = { internReportID, sourceFileMetaData }; child.addToAggregatedInternSourceNodeMetaDataOfTree(aggregatedSourceNodeMetaData); this.langInternalChildren.set(langInternalPath, child); return child; } else { throw new Error('SourceFileMetaDataTree.insertLangInternalPath: path was already inserted ' + `${langInternalPath}`); } } insertPath(internReportID, filePathParts, aggregatedSourceNodeMetaData, sourceFileMetaData) { this.addToAggregatedInternSourceNodeMetaDataOfTree(aggregatedSourceNodeMetaData); let child = this.internChildren.get(filePathParts[0]); const filePath = this.type === types_1.SourceFileMetaDataTreeType.Module ? new UnifiedPath_1.UnifiedPath('./') : this.filePath === undefined ? new UnifiedPath_1.UnifiedPath('./') : this.filePath; if (filePathParts.length === 1) { if (!child) { const moduleIndex = this.isRoot() ? this.index.getModuleIndex('get') : this.index; const pathIndex = moduleIndex === null || moduleIndex === void 0 ? void 0 : moduleIndex.getFilePathIndex('get', filePath.join(...filePathParts).toString()); if (pathIndex === undefined) { throw new Error('SourceFileMetaDataTree.insertPath: could not resolve path index'); } child = new SourceFileMetaDataTree(types_1.SourceFileMetaDataTreeType.File, filePath.join(filePathParts[0]), pathIndex); child.linkedMetaData = { internReportID, sourceFileMetaData }; child.addToAggregatedInternSourceNodeMetaDataOfTree(aggregatedSourceNodeMetaData); this.internChildren.set(filePathParts[0], child); return child; } else { throw new Error('SourceFileMetaDataTree.insertPath: path was already inserted ' + `${filePath.toString()}/${filePathParts[0]}`); } } if (!child) { const moduleIndex = this.isRoot() ? this.index.getModuleIndex('get') : this.index; if (moduleIndex === undefined) { throw new Error('SourceFileMetaDataTree.insertPath: could not resolve module index'); } child = new SourceFileMetaDataTree(types_1.SourceFileMetaDataTreeType.Directory, filePath.toString() === '' ? new UnifiedPath_1.UnifiedPath(filePathParts[0]) : filePath.join(filePathParts[0]), moduleIndex); this.internChildren.set(filePathParts[0], child); } return child.insertPath(internReportID, filePathParts.slice(1), aggregatedSourceNodeMetaData, sourceFileMetaData); } addExternReport(sourceNodeGraph, moduleReport, index) { const child = new SourceFileMetaDataTree(types_1.SourceFileMetaDataTreeType.Module, new UnifiedPath_1.UnifiedPath('node_modules/' + moduleReport.nodeModule.identifier), index); child.addInternReport(sourceNodeGraph, moduleReport); for (const [moduleID, externModuleReport] of moduleReport.extern.entries()) { const childIndex = index.globalIndex.getModuleIndexByID(moduleID); if (childIndex === undefined) { throw new Error('SourceFileMetaDataTree.addExternReport: could not resolve module index'); } child.addExternReport(sourceNodeGraph, externModuleReport, childIndex); } this.addToAggregatedExternSourceNodeMetaDataOfTree(child.totalAggregatedSourceMetaData); this.externChildren.set(moduleReport.nodeModule.identifier, child); } addInternReport(sourceNodeGraph, report) { for (const [filePathID, sourceFileMetaData] of report.lang_internal.entries()) { const filePathIndex = report.getPathIndexByID(filePathID); if (filePathIndex === undefined) { throw new Error(`SourceFileMetaDataTree.addInternReport: could not resolve path from pathID: ${filePathID}`); } const aggregatedSourceNodeMetaData = new SourceFileMetaData_1.AggregatedSourceNodeMetaData(sourceFileMetaData.totalSourceNodeMetaData(sourceNodeGraph).sum, sourceFileMetaData.maxSourceNodeMetaData()); this.insertLangInternalPath(report.internID, filePathIndex.identifier, aggregatedSourceNodeMetaData, sourceFileMetaData); } for (const [filePathID, sourceFileMetaData] of report.intern.entries()) { const filePathIndex = report.getPathIndexByID(filePathID); if (filePathIndex === undefined) { throw new Error(`SourceFileMetaDataTree.addInternReport: could not resolve path from pathID: ${filePathID}`); } const filePathParts = new UnifiedPath_1.UnifiedPath(filePathIndex.identifier).split(); const aggregatedSourceNodeMetaData = new SourceFileMetaData_1.AggregatedSourceNodeMetaData(sourceFileMetaData.totalSourceNodeMetaData(sourceNodeGraph).sum, sourceFileMetaData.maxSourceNodeMetaData()); this.insertPath(report.internID, filePathParts, aggregatedSourceNodeMetaData, sourceFileMetaData); } } addProjectReport(projectReport) { if (!this.isRoot()) { throw new Error('SourceFileMetaDataTree.addProjectReport: can only be executed on root nodes'); } this.addInternReport(projectReport.asSourceNodeGraph(), projectReport); for (const [moduleID, externModuleReport] of projectReport.extern.entries()) { const childIndex = this.index.getModuleIndexByID(moduleID); if (childIndex === undefined) { throw new Error('SourceFileMetaDataTree.addExternReport: could not resolve module index'); } this.addExternReport(projectReport.asSourceNodeGraph(), externModuleReport, childIndex); } } _mergeReferences(...references) { const result = new ModelMap_1.ModelMap('number'); for (const reference of references) { for (const [pathID, sensorValues] of reference.entries()) { const accumulatedSensorValues = result.get(pathID); if (accumulatedSensorValues !== undefined) { result.set(pathID, SensorValues_1.SensorValues.sum(accumulatedSensorValues, sensorValues)); } else { result.set(pathID, sensorValues); } } } return result; } filter(sourceNodeGraph, includedFilterPathString, excludedFilterPathString) { // eslint-disable-next-line @typescript-eslint/no-this-alias const self = this; const includeCache = new Map(); const excludeCache = new Map(); const pathIndexCache = new Map(); // Normalize filter paths if (includedFilterPathString && !(includedFilterPathString.endsWith('/*') || includedFilterPathString.endsWith('/'))) { includedFilterPathString = includedFilterPathString + '/*'; } else if (excludedFilterPathString && excludedFilterPathString.endsWith('/')) { excludedFilterPathString = excludedFilterPathString + '*'; } const includedFilterPathList = includedFilterPathString ? includedFilterPathString.split(',') : []; const excludedFilterPathList = excludedFilterPathString ? excludedFilterPathString.split(',') : []; // check if the path is included/excluded in the filter function checkGlob(filePath, filterPath) { if (filePath === undefined) { throw new Error('SourceFileMetaDataTree.checkGlob: filePath is undefined'); } const normalizedDirectory = filePath.startsWith('./') ? filePath.substring(2) : filePath; const normalizedFilterPath = filterPath.startsWith('./') ? filterPath.substring(2) : filterPath; const includeRe = (0, glob_to_regexp_1.default)(normalizedFilterPath, { extended: true }); return (includeRe.test(normalizedDirectory) || includeRe.test(normalizedDirectory + '/')); } function pathIndexByID(pathID) { let pathIndex = pathIndexCache.get(pathID); if (pathIndex === undefined) { pathIndex = self.globalIndex().getPathIndexByID(pathID); if (pathIndex === undefined) { throw new Error('SourceFileMetaDataTree.filter: pathIndex is undefined'); } pathIndexCache.set(pathID, pathIndex); } return pathIndex; } // Filter function on pathIndex function filterPaths(pathIndexObj) { const pathIndex = pathIndexObj instanceof PathIndex_1.PathIndex ? pathIndexObj : pathIndexByID(pathIndexObj); if (pathIndex.id === undefined) { throw new Error('SourceFileMetaDataTree.filter: pathIndex.id is undefined'); } let isIncludedNode = includeCache.get(pathIndex.id); let isExcludedNode = excludeCache.get(pathIndex.id); if (isIncludedNode === undefined) { if (includedFilterPathList.length === 0) { // if no include filter is set, all are included isIncludedNode = true; } else if (pathIndex.moduleIndex.identifier === '{node}') { // always include lang_internal functions isIncludedNode = true; } else { let pathToCompare = pathIndex.identifier; if (pathIndex.moduleIndex.identifier !== '{self}') { // add module prefix pathToCompare = new UnifiedPath_1.UnifiedPath('node_modules') .join(pathIndex.moduleIndex.identifier, pathIndex.identifier) .toString(); } // remove ./ prefix if (pathToCompare.startsWith('./')) { pathToCompare = pathToCompare.substring(2); } isIncludedNode = false; for (const includedFilterPath of includedFilterPathList) { if (checkGlob(pathToCompare, includedFilterPath)) { // only needs to be included in one filter to be included isIncludedNode = true; break; } } } includeCache.set(pathIndex.id, isIncludedNode); } if (isExcludedNode === undefined) { if (excludedFilterPathList.length === 0) { // if no exclude filter is set, none are excluded isExcludedNode = false; } else if (pathIndex.moduleIndex.identifier === '{node}') { // never exclude lang_internal functions isExcludedNode = false; } else { let pathToCompare = pathIndex.identifier; if (pathIndex.moduleIndex.identifier !== '{self}') { // add module prefix pathToCompare = new UnifiedPath_1.UnifiedPath('node_modules') .join(pathIndex.moduleIndex.identifier, pathIndex.identifier) .toString(); } // remove ./ prefix if (pathToCompare.startsWith('./')) { pathToCompare = pathToCompare.substring(2); } isExcludedNode = false; for (const excludedFilterPath of excludedFilterPathList) { if (checkGlob(pathToCompare, excludedFilterPath)) { // only needs to be excluded in one filter to be excluded isExcludedNode = true; break; } } } excludeCache.set(pathIndex.id, isExcludedNode); } return isIncludedNode && !isExcludedNode; } function filterReferences(references) { for (const pathID of references.keys()) { if (!filterPaths(pathID)) { references.delete(pathID); } } } // call the filter function return this._filter(sourceNodeGraph, filterPaths, filterReferences); } _filter(sourceNodeGraph, filterPaths, filterReferences) { const allSensorValuesToSum = []; const allInternReferencesToMerge = []; const allExternReferencesToMerge = []; const allLangInternalReferencesToMerge = []; const allContainsFilesToMerge = []; const node = new SourceFileMetaDataTree(this.type, this.filePath, this.index); node.headlessSensorValues = SensorValues_1.SensorValues.fromJSON(this.headlessSensorValues.toJSON()); node.linkedMetaData = this.linkedMetaData; if (SourceFileMetaDataTree.isFileNode(this)) { if (this.linkedMetaData === undefined) { throw new Error('SourceFileMetaDataTree.filter: linkedMetaData is undefined'); } const pathID = this.linkedMetaData.sourceFileMetaData.pathIndex.id; if (pathID === undefined) { throw new Error('SourceFileMetaDataTree.filter: pathID is undefined'); } const max = this.linkedMetaData.sourceFileMetaData.maxSourceNodeMetaData(); const total = this.linkedMetaData.sourceFileMetaData.totalSourceNodeMetaData(sourceNodeGraph); filterReferences(total.intern); filterReferences(total.extern); filterReferences(total.langInternal); allInternReferencesToMerge.push(total.intern); allExternReferencesToMerge.push(total.extern); allLangInternalReferencesToMerge.push(total.langInternal); allContainsFilesToMerge.push(new Set([pathID])); const internSum = SensorValues_1.SensorValues.sum(...total.intern.values()); const externSum = SensorValues_1.SensorValues.sum(...total.extern.values()); const langInternalSum = SensorValues_1.SensorValues.sum(...total.langInternal.values()); // clones the total sensor values but removes the references const ownSensorValues = total.sum.sensorValues.cloneAsIsolated(); allSensorValuesToSum.push(ownSensorValues); node.addToAggregatedInternSourceNodeMetaDataOfTree(new SourceFileMetaData_1.AggregatedSourceNodeMetaData(new SourceNodeMetaData_1.SourceNodeMetaData(types_1.SourceNodeMetaDataType.Aggregate, undefined, ownSensorValues.add({ internSensorValues: internSum, externSensorValues: externSum, langInternalSensorValues: langInternalSum }), undefined), max)); } if (this.langInternalChildren.size > 0) { const sensorValuesToSum = []; const maxSourceNodeMetaDataToMax = []; const internReferencesToMerge = []; const externReferencesToMerge = []; const langInternalReferencesToMerge = []; const containsFilesToMerge = []; for (const [langInternalPath, child] of this.langInternalChildren.entries()) { const { node: filteredChild, sensorValues: filteredChildSensorValues, internReferences: filteredIntern, externReferences: filteredExtern, langInternalReferences: filteredLangInternal, containsFiles: filteredContainsFiles } = child._filter(sourceNodeGraph, filterPaths, filterReferences); if (filteredChild) { node.langInternalChildren.set(langInternalPath, filteredChild); if (filteredChildSensorValues) { sensorValuesToSum.push(filteredChildSensorValues); } internReferencesToMerge.push(filteredIntern); externReferencesToMerge.push(filteredExtern); langInternalReferencesToMerge.push(filteredLangInternal); containsFilesToMerge.push(filteredContainsFiles); maxSourceNodeMetaDataToMax.push(filteredChild.totalAggregatedSourceMetaData.max); } } const internReferences = this._mergeReferences(...internReferencesToMerge); const externReferences = this._mergeReferences(...externReferencesToMerge); const langInternalReferences = this._mergeReferences(...langInternalReferencesToMerge); const containsFilesInChildren = SetHelper_1.SetHelper.union(...containsFilesToMerge); for (const pathID of containsFilesInChildren) { internReferences.delete(pathID); externReferences.delete(pathID); langInternalReferences.delete(pathID); } allInternReferencesToMerge.push(internReferences); allExternReferencesToMerge.push(externReferences); allLangInternalReferencesToMerge.push(langInternalReferences); node.addToAggregatedLangInternalSourceNodeMetaDataOfTree(new SourceFileMetaData_1.AggregatedSourceNodeMetaData(new SourceNodeMetaData_1.SourceNodeMetaData(types_1.SourceNodeMetaDataType.Aggregate, undefined, SensorValues_1.SensorValues.sum(...sensorValuesToSum, ...internReferences.values()), undefined), SourceNodeMetaData_1.SourceNodeMetaData.max(...maxSourceNodeMetaDataToMax))); } if (this.internChildren.size > 0) { const sensorValuesToSum = []; const maxSourceNodeMetaDataToMax = []; const internReferencesToMerge = []; const externReferencesToMerge = []; const langInternalReferencesToMerge = []; const containsFilesToMerge = []; for (const [internPath, child] of this.internChildren.entries()) { const { node: filteredChild, sensorValues: filteredChildSensorValues, internReferences: filteredIntern, externReferences: filteredExtern, langInternalReferences: filteredLangInternal, containsFiles: filteredContainsFiles } = child._filter(sourceNodeGraph, filterPaths, filterReferences); if (filteredChild) { node.internChildren.set(internPath, filteredChild); if (filteredChildSensorValues) { sensorValuesToSum.push(filteredChildSensorValues); } internReferencesToMerge.push(filteredIntern); externReferencesToMerge.push(filteredExtern); langInternalReferencesToMerge.push(filteredLangInternal); containsFilesToMerge.push(filteredContainsFiles); maxSourceNodeMetaDataToMax.push(filteredChild.totalAggregatedSourceMetaData.max); } } const internReferences = this._mergeReferences(...internReferencesToMerge); const externReferences = this._mergeReferences(...externReferencesToMerge); const langInternalReferences = this._mergeReferences(...langInternalReferencesToMerge); const containsFilesInChildren = SetHelper_1.SetHelper.union(...containsFilesToMerge); for (const pathID of containsFilesInChildren) { internReferences.delete(pathID); externReferences.delete(pathID); langInternalReferences.delete(pathID); } const sensorValuesSum = SensorValues_1.SensorValues.sum(...sensorValuesToSum); const internReferencesSum = SensorValues_1.SensorValues.sum(...internReferences.values()); const externReferencesSum = SensorValues_1.SensorValues.sum(...externReferences.values()); const langInternalReferencesSum = SensorValues_1.SensorValues.sum(...langInternalReferences.values()); allInternReferencesToMerge.push(internReferences); allExternReferencesToMerge.push(externReferences); allLangInternalReferencesToMerge.push(langInternalReferences); allContainsFilesToMerge.push(containsFilesInChildren); allSensorValuesToSum.push(sensorValuesSum); node.addToAggregatedInternSourceNodeMetaDataOfTree(new SourceFileMetaData_1.AggregatedSourceNodeMetaData(new SourceNodeMetaData_1.SourceNodeMetaData(types_1.SourceNodeMetaDataType.Aggregate, undefined, sensorValuesSum.add({ internSensorValues: internReferencesSum, externSensorValues: externReferencesSum, langInternalSensorValues: langInternalReferencesSum }), undefined), SourceNodeMetaData_1.SourceNodeMetaData.max(...maxSourceNodeMetaDataToMax))); } if (this.externChildren.size > 0) { const totalSourceNodeMetaDataToMerge = []; for (const [moduleIdentifier, child] of this.externChildren.entries()) { const { node: filteredChild } = child._filter(sourceNodeGraph, filterPaths, filterReferences); if (filteredChild) { node.externChildren.set(moduleIdentifier, filteredChild); totalSourceNodeMetaDataToMerge.push(filteredChild.aggregatedInternSourceMetaData); } } node.addToAggregatedExternSourceNodeMetaDataOfTree(SourceFileMetaData_1.AggregatedSourceNodeMetaData.join(...totalSourceNodeMetaDataToMerge)); } const internReferences = this._mergeReferences(...allInternReferencesToMerge); const externReferences = this._mergeReferences(...allExternReferencesToMerge); const langInternalReferences = this._mergeReferences(...allLangInternalReferencesToMerge); if (SourceFileMetaDataTree.isFileNode(this)) { const survivesFilter = filterPaths(this.index); if (!survivesFilter) { return { node: null, sensorValues: undefined, internReferences, externReferences, langInternalReferences, containsFiles: SetHelper_1.SetHelper.union(...allContainsFilesToMerge) }; } } if (!SourceFileMetaDataTree.isFileNode(this) && node.internChildren.size === 0 && node.langInternalChildren.size === 0 && node.externChildren.size === 0) { return { node: null, sensorValues: undefined, internReferences, externReferences, langInternalReferences, containsFiles: SetHelper_1.SetHelper.union(...allContainsFilesToMerge) }; } return { node, sensorValues: SensorValues_1.SensorValues.sum(...allSensorValuesToSum), internReferences, externReferences, langInternalReferences, containsFiles: SetHelper_1.SetHelper.union(...allContainsFilesToMerge) }; } } exports.SourceFileMetaDataTree = SourceFileMetaDataTree; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU291cmNlRmlsZU1ldGFEYXRhVHJlZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9tb2RlbC9Tb3VyY2VGaWxlTWV0YURhdGFUcmVlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLHVDQUF3QjtBQUV4QixvRUFBeUM7QUFFekMsMkNBQXVDO0FBQ3ZDLHlDQUFxQztBQUNyQyw2REFHNkI7QUFDN0IsNkRBQXlEO0FBS3pELDZDQUF5QztBQUN6QyxpREFBNkM7QUFDN0MsbURBQStDO0FBQy9DLHVEQUFtRDtBQUduRCwwQ0FBMkM7QUFDM0MsdURBQW1EO0FBQ25ELHlEQUFxRDtBQUNyRCxtREFBK0M7QUFDL0MsaUVBQTZEO0FBQzdELFFBQVE7QUFDUixvQ0FhaUI7QUF1QmpCLE1BQWEsc0JBRVgsU0FBUSxxQkFBUztJQTBCbEIsWUFDQyxJQUFPLEVBQ1AsUUFBdUMsRUFDdkMsS0FBc0I7UUFFdEIsS0FBSyxFQUFFLENBQUE7UUFDUCxJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQTtRQUNoQixJQUFJLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQTtRQUN4QixJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQTtJQUNuQixDQUFDO0lBRUQsV0FBVztRQUNWLFFBQVEsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ25CLEtBQUssa0NBQTBCLENBQUMsSUFBSTtnQkFDbkMsT0FBUSxJQUFnRTtxQkFDdEUsS0FBSyxDQUFBO1lBQ1IsS0FBSyxrQ0FBMEIsQ0FBQyxJQUFJO2dCQUNuQyxPQUFRLElBQWdFO3FCQUN0RSxLQUFLLENBQUMsV0FBVyxDQUFDLFdBQVcsQ0FBQTtZQUNoQyxLQUFLLGtDQUEwQixDQUFDLFNBQVMsQ0FBQztZQUMxQyxLQUFLLGtDQUEwQixDQUFDLE1BQU07Z0JBQ3JDLE9BQ0MsSUFDQSxDQUFDLEtBQUssQ0FBQyxXQUFXLENB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