UNPKG

@teambit/workspace

Version:
83 lines (82 loc) 4.49 kB
import { Graph } from '@teambit/graph.cleargraph'; import type { Component, ComponentID } from '@teambit/component'; import type { DependencyResolverMain } from '@teambit/dependency-resolver'; import type { Logger } from '@teambit/logger'; import type { Workspace } from './workspace'; export type ShouldLoadFunc = (id: ComponentID) => Promise<boolean>; export declare class GraphFromFsBuilder { private workspace; private logger; private dependencyResolver; private ignoreIds; private shouldLoadItsDeps?; private shouldThrowOnMissingDep; private graph; private completed; private depth; private consumer; private importedIds; private currentLane; constructor(workspace: Workspace, logger: Logger, dependencyResolver: DependencyResolverMain, ignoreIds?: string[], shouldLoadItsDeps?: ShouldLoadFunc | undefined, shouldThrowOnMissingDep?: boolean); /** * create a graph with all dependencies and flattened dependencies of the given components. * the nodes are components and the edges has a label of the dependency type. * * the way how it is done is iterations by depths. each depth we gather all the dependencies of * that depths, make sure all objects exist and then check their dependencies for the next depth. * once there is no dependency left, we're on the last depth level and the graph is ready. * * for example, imagine the following graph: * A1 -> A2 -> A3 * B1 -> B2 -> B3 * C1 -> C2 -> C3 * * where the buildGraph is given [A1, B1, C1]. * first, it saves all these components as nodes in the graph. then, it finds the dependencies of * the next level, in this case they're [A2, B2, C2]. it runs `importMany` in case some objects * are missing. then, it loads them all (some from FS, some from the model) and sets the edges * between the component and the dependencies. * once done, it finds all their dependencies, which are [A3, B3, C3] and repeat the process * above. since there are no more dependencies, the graph is completed. * in this case, the total depth levels are 3. * * even with a huge project, there are not many depth levels. by iterating through depth levels * we keep performance sane as the importMany doesn't run multiple time and therefore the round * trips to the remotes are minimal. * * LAZY IMPORT MODE (when shouldLoadItsDeps is provided): * when a filter function (shouldLoadItsDeps) is provided, we use "lazy import" mode to optimize * performance. instead of importing all flattened dependencies at once, we: * 1. only import dependencies that pass the filter (e.g., only aspects) * 2. don't fetch their flattened dependencies upfront (includeDependencies: false) * 3. let the recursive depth iteration handle importing deps as needed * this is much more efficient when building filtered graphs (e.g., aspects-only graph) because * we avoid fetching huge dependency trees for components we don't care about. * * TRADITIONAL MODE (without filter): * normally, one importMany of the seeders is enough as importMany knows to fetch all flattened. * however, since this buildGraph is performed on the workspace, a dependency may be new or * modified and as such, we don't know its flattened yet. */ buildGraph(ids: ComponentID[]): Promise<Graph<Component, string>>; private getAllDepsUnfiltered; private getAllDepsFiltered; private processManyComponents; /** * 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. * * when `shouldLoadItsDeps` is provided, we use "lazy import" mode: * - only import filtered dependencies (those passing the shouldLoadItsDeps check) * - use preferDependencyGraph to avoid fetching flattened dependencies * - the recursive depth iteration will handle importing deps as needed * this is much more efficient when building a filtered graph (e.g., aspects-only graph) * * without a filter, we use the traditional approach: * - `importMany` fetches all flattened dependencies (preferDependencyGraph: false) * - once a component from scope is imported, all its flattened dependencies are there */ private importObjects; private processOneComponent; private loadManyComponents; }