@teambit/workspace
Version:
251 lines (246 loc) • 11.6 kB
JavaScript
"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