UNPKG

@teambit/isolator

Version:
286 lines (285 loc) • 12.1 kB
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 {};