@teambit/isolator
Version:
286 lines (285 loc) • 12.1 kB
TypeScript
import type { CLIMain } from '@teambit/cli';
import type { AspectLoaderMain } from '@teambit/aspect-loader';
import type { ComponentMain, ComponentFactory } from '@teambit/component';
import type { GraphMain } from '@teambit/graph';
import type { SlotRegistry } from '@teambit/harmony';
import type { DependencyResolverMain, LinkingOptions, NodeLinker } from '@teambit/dependency-resolver';
import type { Logger, LoggerMain } from '@teambit/logger';
import type { ComponentID } from '@teambit/component-id';
import type { Scope as LegacyScope } from '@teambit/legacy.scope';
import type { GlobalConfigMain } from '@teambit/global-config';
import type { PathOsBasedAbsolute } from '@teambit/legacy.utils';
import { Capsule } from './capsule';
import { Network } from './network';
import type { ConfigStoreMain } from '@teambit/config-store';
export type ListResults = {
capsules: string[];
};
export type CapsuleTransferFn = (sourceDir: string, targetDir: string) => Promise<void>;
export type CapsuleTransferSlot = SlotRegistry<CapsuleTransferFn>;
/**
* Context for the isolation process
*/
export type IsolationContext = {
/**
* Whether the isolation done for aspects (as opposed to regular components)
*/
aspects?: boolean;
/**
* Workspace name where the isolation starts from
*/
workspaceName?: string;
};
export type IsolateComponentsInstallOptions = {
installPackages?: boolean;
dedupe?: boolean;
copyPeerToRuntimeOnComponents?: boolean;
copyPeerToRuntimeOnRoot?: boolean;
installPeersFromEnvs?: boolean;
installTeambitBit?: boolean;
packageManagerConfigRootDir?: string;
useNesting?: boolean;
};
type CreateGraphOptions = {
/**
* include components that exists in nested hosts. for example include components that exist in scope but not in the workspace
*/
includeFromNestedHosts?: boolean;
/**
* Force specific host to get the component from.
*/
host?: ComponentFactory;
};
export type IsolateComponentsOptions = CreateGraphOptions & {
name?: string;
/**
* absolute path to put all the capsules dirs inside.
*/
rootBaseDir?: string;
/**
* the capsule root-dir based on a *hash* of this baseDir, not on the baseDir itself.
* A folder with this hash as its name will be created in the rootBaseDir
* By default this value will be the host path
*/
baseDir?: string;
/**
* Whether to use hash function (of base dir) as capsules root dir name
*/
useHash?: boolean;
/**
* create a new capsule with a random string attached to the path suffix
*/
alwaysNew?: boolean;
/**
* If this is true -
* the isolator will check if there are missing capsules in the base dir
* if yes, it will create the capsule in a special dir inside a dir with the current date (without time)
* then inside that dir, it will create a dir with a random hash
* at the end of the process it will move missing capsules from the temp dir to the base dir so they can be used in
* the next iteration
*/
useDatedDirs?: boolean;
/**
* If this is true -
* the isolator will do few things:
* 1. in the end of the process it will only move the lock file (pnpm-lock.yaml) into the capsule cache
* 2. in the beginning of the process it will check if there is a lock file in the capsule cache, if yes it will move
* it to the temp dated dir
* 3. it will write env's file into the dated dir (as it only contain the lock file)
* 4. it will run install in the dated dir (as there is no node_modules there yet)
*/
cacheLockFileOnly?: boolean;
/**
* If set, along with useDatedDirs, then we will use the same hash dir for all capsules created with the same
* datedDirId
*/
datedDirId?: string;
/**
* installation options
*/
installOptions?: IsolateComponentsInstallOptions;
linkingOptions?: LinkingOptions;
/**
* delete the capsule rootDir first. it makes sure that the isolation process starts fresh with
* no previous capsules. for build and tag this is true.
*/
emptyRootDir?: boolean;
/**
* skip the reproduction of the capsule in case it exists.
*/
skipIfExists?: boolean;
/**
* get existing capsule without doing any changes, no writes, no installations.
*/
getExistingAsIs?: boolean;
/**
* place the package-manager cache on the capsule-root
*/
cachePackagesOnCapsulesRoot?: boolean;
/**
* do not build graph with all dependencies. isolate the seeders only.
*/
seedersOnly?: boolean;
/**
* relevant for tagging from scope, where we tag an existing snap without any code-changes.
* the idea is to have all build artifacts from the previous snap and run deploy pipeline on top of it.
*/
populateArtifactsFrom?: ComponentID[];
/**
* relevant when populateArtifactsFrom is set.
* by default, it uses the package.json created in the previous snap as a base and make the necessary changes.
* if this is set to true, it will ignore the package.json from the previous snap.
*/
populateArtifactsIgnorePkgJson?: boolean;
/**
* Force specific host to get the component from.
*/
host?: ComponentFactory;
/**
* Use specific package manager for the isolation process (override the package manager from the dep resolver config)
*/
packageManager?: string;
/**
* Use specific node linker for the isolation process (override the package manager from the dep resolver config)
*/
nodeLinker?: NodeLinker;
/**
* Dir where to read the package manager config from
* usually used when running package manager in the capsules dir to use the config
* from the workspace dir
*/
packageManagerConfigRootDir?: string;
context?: IsolationContext;
/**
* Root dir of capsulse cache (used mostly to copy lock file if used with cache lock file only option)
*/
cacheCapsulesDir?: string;
/**
* Generate a lockfile from the dependencies graph stored in the model
* and generate a dependency graph from the lockfile in the capsule.
*/
useDependenciesGraph?: boolean;
};
type GetCapsuleDirOpts = Pick<IsolateComponentsOptions, 'datedDirId' | 'useHash' | 'rootBaseDir' | 'useDatedDirs' | 'cacheLockFileOnly'> & {
baseDir: string;
};
/**
* File name to indicate that the capsule is ready (all packages are installed and links are created)
*/
export declare const CAPSULE_READY_FILE = ".bit-capsule-ready";
export declare class IsolatorMain {
private dependencyResolver;
private logger;
private componentAspect;
private graph;
private cli;
private globalConfig;
private aspectLoader;
private capsuleTransferSlot;
private configStore;
static runtime: import("@teambit/harmony").RuntimeDefinition;
static dependencies: import("@teambit/harmony").Aspect[];
static slots: ((registerFn: () => string) => SlotRegistry<CapsuleTransferFn>)[];
static defaultConfig: {};
_componentsPackagesVersionCache: {
[idStr: string]: string;
};
_datedHashForName: Map<string, string>;
_movedLockFiles: Set<unknown>;
static provider([dependencyResolver, loggerExtension, componentAspect, graphMain, globalConfig, aspectLoader, cli, configStore]: [
DependencyResolverMain,
LoggerMain,
ComponentMain,
GraphMain,
GlobalConfigMain,
AspectLoaderMain,
CLIMain,
ConfigStoreMain
], _config: any, [capsuleTransferSlot]: [CapsuleTransferSlot]): Promise<IsolatorMain>;
constructor(dependencyResolver: DependencyResolverMain, logger: Logger, componentAspect: ComponentMain, graph: GraphMain, cli: CLIMain, globalConfig: GlobalConfigMain, aspectLoader: AspectLoaderMain, capsuleTransferSlot: CapsuleTransferSlot, configStore: ConfigStoreMain);
isolateComponents(seeders: ComponentID[], opts: IsolateComponentsOptions, legacyScope?: LegacyScope): Promise<Network>;
private createGraph;
private registerMoveCapsuleOnProcessExit;
private getAllCapsulesDirsFromRoot;
private moveCapsulesLockFileToTargetDir;
private moveCapsulesToTargetDir;
/**
* The function moves a directory from a source location to a target location using a temporary directory.
* This is using temp dir because sometime the source dir and target dir might be in different FS
* (for example different mounts) which means the move might take a long time
* during the time of moving, another process will see that the capsule is not ready and will try to remove then
* move it again, which lead to the first process throwing an error
* @param sourceDir - The source directory from where the files or directories will be moved.
* @param targetDir - The target directory where the source directory will be moved to.
*/
private moveWithTempName;
/**
* Re-create the core aspects links in the real capsule dir
* This is required mainly for the first time when that folder is empty
*/
private relinkCoreAspectsInCapsuleDir;
private shouldUseDatedDirs;
/**
*
* @param originalCapsule the capsule that contains the original component
* @param newBaseDir relative path. (it will be saved inside `this.getRootDirOfAllCapsules()`. the final path of the capsule will be getRootDirOfAllCapsules() + newBaseDir + filenameify(component.id))
* @returns a new capsule with the same content of the original capsule but with a new baseDir and all packages
* installed in the newBaseDir.
*/
cloneCapsule(originalCapsule: Capsule, newBaseDir: string): Promise<Capsule>;
/**
* Create capsules for the provided components
* do not use this outside directly, use isolate components which build the entire network
* @param components
* @param opts
* @param legacyScope
*/
private createCapsules;
private addDependenciesGraphToComponents;
private markCapsulesAsReady;
private markCapsuleAsReady;
private removeCapsuleReadyFileSync;
private writeCapsuleReadyFileSync;
private getCapsuleReadyFilePath;
private installInCapsules;
private linkInCapsules;
private linkInCapsulesRoot;
private toLocalLinks;
private linkDetailToLocalDepEntry;
private getCapsulesWithModifiedPackageJson;
private writeComponentsInCapsules;
private getWorkspacePeersOnlyPolicy;
private toComponentMap;
list(rootDir: string): Promise<ListResults>;
registerCapsuleTransferFn(fn: CapsuleTransferFn): void;
private getCapsuleTransferFn;
private getDefaultCapsuleTransferFn;
private getCapsuleDirHash;
/** @deprecated use the new function signature with an object parameter instead */
getCapsulesRootDir(baseDir: string, rootBaseDir?: string, useHash?: boolean): PathOsBasedAbsolute;
getCapsulesRootDir(getCapsuleDirOpts: GetCapsuleDirOpts): PathOsBasedAbsolute;
deleteCapsules(rootDir?: string): Promise<string>;
private writeRootPackageJson;
private createCapsulesFromComponents;
private getRootDirOfAllCapsules;
private wereDependenciesInPackageJsonChanged;
private getCapsulesPreviousPackageJson;
private updateWithCurrentPackageJsonData;
private getCurrentPackageJson;
private populateComponentsFilesToWriteForCapsule;
private mergePkgJsonFromLastBuild;
private getCompForArtifacts;
private preparePackageJsonToWrite;
/**
* currently, it writes all artifacts.
* later, this responsibility might move to pkg extension, which could write only artifacts
* that are set in package.json.files[], to have a similar structure of a package.
*/
private getArtifacts;
/**
* Filter out unmodified exported dependencies to optimize capsule creation.
* These dependencies can be installed as packages instead of creating capsules.
*/
private filterUnmodifiedExportedDependencies;
}
export {};