UNPKG

@teambit/workspace

Version:
251 lines (246 loc) • 11.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GraphIdsFromFsBuilder = void 0; exports.lifecycleToDepType = lifecycleToDepType; function _pMapSeries() { const data = _interopRequireDefault(require("p-map-series")); _pMapSeries = function () { return data; }; return data; } function _graph() { const data = require("@teambit/graph.cleargraph"); _graph = function () { return data; }; return data; } function _lodash() { const data = require("lodash"); _lodash = function () { return data; }; return data; } function _legacy() { const data = require("@teambit/legacy.consumer-component"); _legacy = function () { return data; }; return data; } function _componentId() { const data = require("@teambit/component-id"); _componentId = function () { return data; }; return data; } function _legacy2() { const data = require("@teambit/legacy.scope"); _legacy2 = function () { return data; }; return data; } function _scope() { const data = require("@teambit/scope"); _scope = function () { return data; }; return data; } function _lodash2() { const data = _interopRequireDefault(require("lodash.compact")); _lodash2 = function () { return data; }; return data; } function _bitError() { const data = require("@teambit/bit-error"); _bitError = function () { return data; }; return data; } function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function lifecycleToDepType(compDep) { if (compDep.isExtension) return 'ext'; switch (compDep.lifecycle) { case 'dev': return 'dev'; case 'runtime': return 'prod'; case 'peer': return 'peer'; default: throw new Error(`lifecycle ${compDep.lifecycle} is not support`); } } class GraphIdsFromFsBuilder { // for now it has the same value as shouldThrowOnMissingDep. change if needed constructor(workspace, logger, dependencyResolver, shouldThrowOnMissingDep = true) { this.workspace = workspace; this.logger = logger; this.dependencyResolver = dependencyResolver; this.shouldThrowOnMissingDep = shouldThrowOnMissingDep; _defineProperty(this, "graph", new (_graph().Graph)()); _defineProperty(this, "completed", []); _defineProperty(this, "depth", 1); _defineProperty(this, "consumer", void 0); _defineProperty(this, "loadedComponents", {}); _defineProperty(this, "importedIds", []); _defineProperty(this, "shouldThrowOnInvalidDeps", true); this.consumer = this.workspace.consumer; this.shouldThrowOnInvalidDeps = this.shouldThrowOnMissingDep; } /** * create a graph with all dependencies and flattened dependencies of the given components. * the nodes are component-ids and the edges has a label of the dependency type. * to get some info about this the graph build take a look into build-graph-from-fs.buildGraph() docs. */ async buildGraph(ids) { this.logger.debug(`GraphIdsFromFsBuilder, buildGraph with ${ids.length} seeders`); const start = Date.now(); const components = await this.loadManyComponents(ids); await this.processManyComponents(components); this.logger.debug(`GraphIdsFromFsBuilder, buildGraph with ${ids.length} seeders completed (${(Date.now() - start) / 1000} sec)`); return this.graph; } async processManyComponents(components) { this.logger.debug(`GraphIdsFromFsBuilder.processManyComponents depth ${this.depth}, ${components.length} components`); this.depth += 1; await this.importObjects(components); const allDependencies = await (0, _pMapSeries().default)(components, component => this.processOneComponent(component)); const allDependenciesFlattened = (0, _lodash().flatten)(allDependencies); if (allDependenciesFlattened.length) await this.processManyComponents(allDependenciesFlattened); } /** * only for components from the workspace that can be modified to add/remove dependencies, we need to make sure that * all their dependencies are imported. * once a component from scope is imported, we know that either we have its dependency graph or all flattened deps */ async importObjects(components) { const workspaceIds = this.workspace.listIds(); const compOnWorkspaceOnly = components.filter(comp => workspaceIds.find(id => id.isEqual(comp.id))); const notImported = compOnWorkspaceOnly.map(c => c.id).filter(id => !this.importedIds.includes(id.toString())); const exportedDeps = notImported.filter(dep => this.workspace.isExported(dep)); const scopeComponentsImporter = this.consumer.scope.scopeImporter; await scopeComponentsImporter.importMany({ ids: _componentId().ComponentIdList.uniqFromArray(exportedDeps), throwForDependencyNotFound: this.shouldThrowOnMissingDep, throwForSeederNotFound: this.shouldThrowOnMissingDep, reFetchUnBuiltVersion: false, lane: await this.workspace.getCurrentLaneObject(), reason: 'for building graph-ids from the workspace' }); notImported.map(id => this.importedIds.push(id.toString())); } async processOneComponent(component) { const idStr = component.id.toString(); if (this.completed.includes(idStr)) return []; const graphFromScope = await this.workspace.getSavedGraphOfComponentIfExist(component); if (graphFromScope?.edges.length) { const isOnWorkspace = await this.workspace.hasId(component.id); if (isOnWorkspace) { const allDependenciesComps = await this.processCompFromWorkspaceWithGraph(graphFromScope, component); this.completed.push(idStr); return allDependenciesComps; } this.graph.merge([graphFromScope]); this.completed.push(idStr); return []; } const deps = this.dependencyResolver.getComponentDependencies(component); const allDepsIds = deps.map(d => d.componentId); const allDependenciesComps = await this.loadManyComponents(allDepsIds, idStr); deps.forEach(dep => this.addDepEdge(idStr, dep)); this.completed.push(idStr); return allDependenciesComps; } /** * this is tricky. * the component is in the workspace so it can be modified. dependencies can be added/removed/updated/downgraded. * we have the graph-dependencies from the last snap, so we prefer to use it whenever possible for performance reasons. * if we can't use it, we have to recursively load dependencies components and get the data from there. * to maximize the performance, we iterate the direct dependencies, if we find a dep with the same id in the graph, * and that id is not in the workspace then ask the graph for all its successors. otherwise, if it's not there, or * it's there but it's also in the workspace (which therefore can be modified), we recursively load the dep components * and get its dependencies. */ async processCompFromWorkspaceWithGraph(graphFromScope, component) { const deps = this.dependencyResolver.getComponentDependencies(component); const workspaceIds = this.workspace.listIds(); const workspaceIdsStr = workspaceIds.map(id => id.toString()); const [depsInScopeGraph, depsNotInScopeGraph] = (0, _lodash().partition)(deps, dep => graphFromScope.hasNode(dep.componentId.toString()) && !workspaceIdsStr.includes(dep.componentId.toString())); const depsInScopeGraphIds = depsInScopeGraph.map(dep => dep.componentId.toString()); const depsInScopeGraphIdsNotCompleted = depsInScopeGraphIds.filter(id => !this.completed.includes(id)); if (depsInScopeGraphIdsNotCompleted.length) { const subGraphs = graphFromScope.successorsSubgraph(depsInScopeGraphIdsNotCompleted); // delete any edge that its source is from the workspace. if this component is modified, this edge could be // incorrect. we don't need these edges anyway because we add them directly. subGraphs.edges.forEach(edge => { if (workspaceIdsStr.includes(edge.sourceId)) subGraphs.deleteEdge(edge.sourceId, edge.targetId); }); this.graph.merge([subGraphs]); this.completed.push(...depsInScopeGraphIdsNotCompleted); } const allDepsIds = depsNotInScopeGraph.map(d => d.componentId); const idStr = component.id.toString(); const allDependenciesComps = await this.loadManyComponents(allDepsIds, idStr); deps.forEach(dep => this.addDepEdge(idStr, dep)); return allDependenciesComps; } addDepEdge(idStr, dep) { const depId = dep.componentId; if (!this.graph.hasNode(depId.toString())) { if (this.shouldThrowOnMissingDep) { throw new Error(`buildOneComponent: missing node of ${depId.toString()}`); } this.logger.warn(`ignoring missing ${depId.toString()}`); return; } this.graph.setEdge(new (_graph().Edge)(idStr, depId.toString(), lifecycleToDepType(dep))); } async loadManyComponents(componentsIds, dependenciesOf) { const components = await (0, _pMapSeries().default)(componentsIds, async compId => { const idStrPotentiallyWithoutVersion = compId.toString(); const fromCache = this.loadedComponents[idStrPotentiallyWithoutVersion]; if (fromCache) return fromCache; try { const component = await this.workspace.get(compId); const idStr = component.id.toString(); this.loadedComponents[idStr] = component; this.graph.setNode(new (_graph().Node)(idStr, component.id)); return component; } catch (err) { if (err instanceof _legacy2().ComponentNotFound || err instanceof _scope().ComponentNotFound || err instanceof _legacy2().ScopeNotFound) { if (dependenciesOf && !this.shouldThrowOnMissingDep) { this.logger.warn(`component ${idStrPotentiallyWithoutVersion}, dependency of ${dependenciesOf} was not found. continuing without it`); return null; } throw new (_bitError().BitError)(`error: component "${idStrPotentiallyWithoutVersion}" was not found.\nthis component is a dependency of "${dependenciesOf || '<none>'}" and is needed as part of the graph generation`); } if (_legacy().ConsumerComponent.isComponentInvalidByErrorType(err)) { if (dependenciesOf && !this.shouldThrowOnInvalidDeps) { this.logger.warn(`component ${idStrPotentiallyWithoutVersion}, dependency of ${dependenciesOf} is invalid. continuing without it`); return null; } throw new (_bitError().BitError)(`error: component "${idStrPotentiallyWithoutVersion}" is invalid (${err.message}).\nthis component is a dependency of "${dependenciesOf || '<none>'}" and is needed as part of the graph generation`); } if (dependenciesOf) this.logger.error(`failed loading dependencies of ${dependenciesOf}`); throw err; } }); return (0, _lodash2().default)(components); } } exports.GraphIdsFromFsBuilder = GraphIdsFromFsBuilder; //# sourceMappingURL=build-graph-ids-from-fs.js.map