UNPKG

@teambit/lanes

Version:
1,250 lines (1,226 loc) • 48.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "Lane", { enumerable: true, get: function () { return _objects().Lane; } }); exports.default = exports.LanesMain = void 0; function _cli() { const data = require("@teambit/cli"); _cli = function () { return data; }; return data; } function _pMapSeries() { const data = _interopRequireDefault(require("p-map-series")); _pMapSeries = function () { return data; }; return data; } function _pMap() { const data = _interopRequireDefault(require("p-map")); _pMap = function () { return data; }; return data; } function _scope() { const data = require("@teambit/scope"); _scope = function () { return data; }; return data; } function _graphql() { const data = require("@teambit/graphql"); _graphql = function () { return data; }; return data; } function _express() { const data = require("@teambit/express"); _express = function () { return data; }; return data; } function _workspace() { const data = require("@teambit/workspace"); _workspace = function () { return data; }; return data; } function _scope2() { const data = require("@teambit/scope.remotes"); _scope2 = function () { return data; }; return data; } function _lanesModules() { const data = require("@teambit/lanes.modules.diff"); _lanesModules = function () { return data; }; return data; } function _legacy() { const data = require("@teambit/legacy.scope"); _legacy = function () { return data; }; return data; } function _laneId() { const data = require("@teambit/lane-id"); _laneId = function () { return data; }; return data; } function _bitError() { const data = require("@teambit/bit-error"); _bitError = function () { return data; }; return data; } function _logger() { const data = require("@teambit/logger"); _logger = function () { return data; }; return data; } function _merging() { const data = require("@teambit/merging"); _merging = function () { return data; }; return data; } function _componentModules() { const data = require("@teambit/component.modules.merge-helper"); _componentModules = function () { return data; }; return data; } function _importer() { const data = require("@teambit/importer"); _importer = function () { return data; }; return data; } function _componentId() { const data = require("@teambit/component-id"); _componentId = function () { return data; }; return data; } function _legacyBitId() { const data = require("@teambit/legacy-bit-id"); _legacyBitId = function () { return data; }; return data; } function _component() { const data = require("@teambit/component"); _component = function () { return data; }; return data; } function _objects() { const data = require("@teambit/objects"); _objects = function () { return data; }; return data; } function _component2() { const data = require("@teambit/component.snap-distance"); _component2 = function () { return data; }; return data; } function _export() { const data = require("@teambit/export"); _export = function () { return data; }; return data; } function _lodash() { const data = require("lodash"); _lodash = function () { return data; }; return data; } function _componentCompare() { const data = require("@teambit/component-compare"); _componentCompare = function () { return data; }; return data; } function _componentWriter() { const data = require("@teambit/component-writer"); _componentWriter = function () { return data; }; return data; } function _remove() { const data = require("@teambit/remove"); _remove = function () { return data; }; return data; } function _checkout() { const data = require("@teambit/checkout"); _checkout = function () { return data; }; return data; } function _lanesEntities() { const data = require("@teambit/lanes.entities.lane-diff"); _lanesEntities = function () { return data; }; return data; } function _harmonyModules() { const data = require("@teambit/harmony.modules.concurrency"); _harmonyModules = function () { return data; }; return data; } function _fsExtra() { const data = _interopRequireDefault(require("fs-extra")); _fsExtra = function () { return data; }; return data; } function _execa() { const data = _interopRequireDefault(require("execa")); _execa = function () { return data; }; return data; } function _gitModules() { const data = require("@teambit/git.modules.git-executable"); _gitModules = function () { return data; }; return data; } function _removeLanes() { const data = require("./remove-lanes"); _removeLanes = function () { return data; }; return data; } function _lanes() { const data = require("./lanes.aspect"); _lanes = function () { return data; }; return data; } function _lane() { const data = require("./lane.cmd"); _lane = function () { return data; }; return data; } function _lanes2() { const data = require("./lanes.graphql"); _lanes2 = function () { return data; }; return data; } function _switch() { const data = require("./switch.cmd"); _switch = function () { return data; }; return data; } function _switchLanes() { const data = require("./switch-lanes"); _switchLanes = function () { return data; }; return data; } function _lanesModules2() { const data = require("@teambit/lanes.modules.create-lane"); _lanesModules2 = function () { return data; }; return data; } function _lanesCreate() { const data = require("./lanes.create.route"); _lanesCreate = function () { return data; }; return data; } function _lanesDelete() { const data = require("./lanes.delete.route"); _lanesDelete = function () { return data; }; return data; } function _lanesRestore() { const data = require("./lanes.restore.route"); _lanesRestore = function () { return data; }; return data; } function _install() { const data = require("@teambit/install"); _install = function () { return data; }; return data; } function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return 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); } class LanesMain { constructor(workspace, scope, merging, componentAspect, logger, importer, exporter, componentCompare, componentWriter, remove, checkout, install) { this.workspace = workspace; this.scope = scope; this.merging = merging; this.componentAspect = componentAspect; this.logger = logger; this.importer = importer; this.exporter = exporter; this.componentCompare = componentCompare; this.componentWriter = componentWriter; this.remove = remove; this.checkout = checkout; this.install = install; } /** * return the lane data without the deleted components. * the deleted components are filtered out in legacyScope.lanes.getLanesData() */ async getLanes({ name, remote, merged, showDefaultLane, notMerged }) { const showMergeData = Boolean(merged || notMerged); const consumer = this.workspace?.consumer; if (!this.scope) throw new Error(`error: please run the command from a workspace or a scope directory`); if (remote) { const laneId = name ? _laneId().LaneId.from(name, remote) : undefined; const remoteObj = await (0, _scope2().getRemoteByName)(remote, consumer); const lanes = await remoteObj.listLanes(laneId?.toString(), showMergeData); // if the remote is http, it eventually calls this method from the remote without the "remote" param. // again, the deleted components are filtered out. return lanes; } if (name === _laneId().DEFAULT_LANE) { const defaultLane = await this.getLaneDataOfDefaultLane(); return defaultLane ? [defaultLane] : []; } const lanes = await this.scope.legacyScope.lanes.getLanesData(this.scope.legacyScope, name, showMergeData); if (showDefaultLane) { const defaultLane = await this.getLaneDataOfDefaultLane(); if (defaultLane) lanes.push(defaultLane); } return lanes; } async parseLaneId(idStr) { const scope = this.scope.legacyScope; return scope.lanes.parseLaneIdFromString(idStr); } async getLaneHistory(laneId) { const lane = await this.loadLane(laneId); if (!lane) throw new (_bitError().BitError)(`unable to find a lane "${laneId.toString()}"`); const laneHistory = await this.scope.legacyScope.lanes.getOrCreateLaneHistory(lane); return laneHistory; } async checkoutHistory(historyId, options) { const historyItem = await this.getHistoryItemOfCurrentLane(historyId); const ids = historyItem.components.map(id => _componentId().ComponentID.fromString(id)); const lane = await this.getCurrentLane(); const results = await this.checkout.checkout({ ids: ids.map(id => id.changeVersion(undefined)), versionPerId: ids, allowAddingComponentsFromScope: true, skipNpmInstall: options?.skipDependencyInstallation, lane }); return results; } async revertHistory(historyId, options) { const historyItem = await this.getHistoryItemOfCurrentLane(historyId); const ids = historyItem.components.map(id => _componentId().ComponentID.fromString(id)); const lane = await this.getCurrentLane(); const results = await this.checkout.checkout({ ids: ids.map(id => id.changeVersion(undefined)), versionPerId: ids, allowAddingComponentsFromScope: true, revert: true, skipNpmInstall: options?.skipDependencyInstallation, lane }); return results; } async getHistoryItemOfCurrentLane(historyId) { const laneId = this.getCurrentLaneId(); if (!laneId || laneId.isDefault()) { throw new (_bitError().BitError)(`unable to checkout history "${historyId}" while on main`); } await this.importLaneHistory(laneId); const laneHistory = await this.getLaneHistory(laneId); const history = laneHistory.getHistory(); const historyItem = history[historyId]; if (!historyItem) { throw new (_bitError().BitError)(`unable to find history "${historyId}" in lane "${laneId.toString()}"`); } return historyItem; } async importLaneHistory(laneId) { const existingLane = await this.loadLane(laneId); if (existingLane?.isNew) return; await this.importer.importLaneObject(laneId, undefined, true); } async isLaneExistsOnRemote(laneId) { const results = await this.scope.legacyScope.scopeImporter.importLanes([laneId]); return results.length > 0; } getCurrentLaneName() { return this.getCurrentLaneId()?.name || null; } getCurrentLaneNameOrAlias() { const currentLaneId = this.getCurrentLaneId(); if (!currentLaneId) return null; const trackingData = this.scope.legacyScope.lanes.getLocalTrackedLaneByRemoteName(currentLaneId.name, currentLaneId.scope); return trackingData || currentLaneId.name; } getCurrentLaneId() { if (!this.workspace) return null; return this.workspace.consumer.getCurrentLaneId(); } /** * get the currently checked out lane object, if on main - return null. */ async getCurrentLane() { const laneId = this.getCurrentLaneId(); if (!laneId || laneId.isDefault()) return undefined; return this.loadLane(laneId); } getDefaultLaneId() { return _laneId().LaneId.from(_laneId().DEFAULT_LANE, this.scope.name); } setCurrentLane(laneId, alias, exported) { this.workspace?.consumer.setCurrentLane(laneId, exported); } async createLane(name, { scope, alias, forkLaneNewScope } = {}) { if (!this.workspace) { const newLane = await (0, _lanesModules2().createLaneInScope)(name, this.scope); return { lane: newLane, laneId: newLane.toLaneId(), hash: newLane.hash().toString() }; } if (alias) { (0, _lanesModules2().throwForInvalidLaneName)(alias); } const currentLaneId = this.workspace.getCurrentLaneId(); const currentLaneScope = currentLaneId.isDefault() ? undefined : currentLaneId.scope; if (!forkLaneNewScope && !currentLaneId.isDefault() && scope && currentLaneScope !== scope) { throw new (_bitError().BitError)(`you're about to create a lane forked from ${currentLaneId.toString()} and assign it to a different scope "${scope}". if the lane components have a large history, it would be best to stick with the same scope as the current lane. to do that, re-run the command without the "--scope" flag. it will create the lane and set the scope to "${currentLaneScope}" if you wish to keep ${scope} scope, please re-run the command with "--fork-lane-new-scope" flag.`); } scope = scope || (currentLaneId.isDefault() ? this.workspace.defaultScope : currentLaneId.scope); const laneObj = await (0, _lanesModules2().createLane)(this.workspace, name, scope); const laneId = _laneId().LaneId.from(name, scope); this.setCurrentLane(laneId, alias, false); const trackLaneData = { localLane: alias || name, remoteLane: name, remoteScope: scope }; this.scope.legacyScope.lanes.trackLane(trackLaneData); this.scope.legacyScope.scopeJson.setLaneAsNew(name); await this.workspace.consumer.onDestroy('lane-create'); const results = { lane: laneObj, alias, laneId: laneObj.toLaneId(), hash: laneObj.hash().toString() }; return results; } async loadLane(id) { return this.scope.legacyScope.lanes.loadLane(id); } async trackLane(localName, remoteScope, remoteName) { if (!this.workspace) { throw new (_bitError().BitError)(`unable to track a lane outside of Bit workspace`); } const laneId = await this.scope.legacyScope.lanes.parseLaneIdFromString(localName); const lane = await this.loadLane(laneId); if (!lane) { throw new (_bitError().BitError)(`unable to find a local lane "${localName}"`); } const beforeTrackData = this.scope.legacyScope.lanes.getRemoteTrackedDataByLocalLane(localName); const beforeTrackDataCloned = beforeTrackData ? _objectSpread({}, beforeTrackData) : undefined; const afterTrackData = { localLane: localName, remoteLane: remoteName || beforeTrackData?.remoteLane || localName, remoteScope }; this.scope.legacyScope.lanes.trackLane(afterTrackData); await this.workspace.consumer.onDestroy('lane-track'); return { beforeTrackData: beforeTrackDataCloned, afterTrackData }; } async aliasLane(laneName, alias) { if (!this.workspace) { throw new (_bitError().BitError)(`unable to alias a lane outside of Bit workspace`); } if (alias.includes(_laneId().LANE_REMOTE_DELIMITER)) { throw new (_bitError().BitError)(`an alias cannot include a delimiter "${_laneId().LANE_REMOTE_DELIMITER}"`); } if (alias === laneName) { throw new (_bitError().BitError)(`an alias cannot be the same as the lane name`); } const laneId = await this.scope.legacyScope.lanes.parseLaneIdFromString(laneName); const lane = await this.loadLane(laneId); if (!lane) { throw new (_bitError().BitError)(`unable to find a local lane "${laneName}"`); } const trackData = { localLane: alias, remoteLane: laneId.name, remoteScope: laneId.scope }; const laneNameWithoutScope = laneName.includes(_laneId().LANE_REMOTE_DELIMITER) ? laneName.split(_laneId().LANE_REMOTE_DELIMITER)[1] : laneName; this.scope.legacyScope.lanes.removeTrackLane(laneNameWithoutScope); this.scope.legacyScope.lanes.trackLane(trackData); await this.workspace.consumer.onDestroy('lane-alias'); return { laneId }; } async changeScope(remoteScope, laneName) { if (!this.workspace) { throw new (_bitError().BitError)(`unable to change-scope of a lane outside of Bit workspace`); } let laneId; let laneNameWithoutScope; if (laneName) { laneNameWithoutScope = laneName.includes(_laneId().LANE_REMOTE_DELIMITER) ? laneName.split(_laneId().LANE_REMOTE_DELIMITER)[1] : laneName; laneId = await this.scope.legacyScope.lanes.parseLaneIdFromString(laneName); } else { laneId = this.workspace.getCurrentLaneId(); laneNameWithoutScope = laneId.name; } const lane = await this.loadLane(laneId); if (!lane) { throw new (_bitError().BitError)(`unable to find a local lane "${laneName}"`); } if (!lane.isNew) { throw new (_bitError().BitError)(`changing lane scope-name is allowed for new lanes only. this lane has been exported already. please create a new lane instead, which will include all components of this lane`); } if (!(0, _legacyBitId().isValidScopeName)(remoteScope)) { throw new (_legacyBitId().InvalidScopeName)(remoteScope); } const remoteScopeBefore = lane.scope; lane.changeScope(remoteScope); const newLaneId = _laneId().LaneId.from(laneId.name, remoteScope); const trackData = { localLane: laneNameWithoutScope, remoteLane: laneId.name, remoteScope }; this.scope.legacyScope.lanes.trackLane(trackData); await this.scope.legacyScope.lanes.saveLane(lane, { laneHistoryMsg: `change scope from ${remoteScopeBefore} to ${remoteScope}` }); this.workspace.consumer.bitMap.setCurrentLane(newLaneId, false); await this.workspace.consumer.onDestroy('lane-scope-change'); return { remoteScopeBefore, localName: laneNameWithoutScope }; } /** * change a lane-name and if possible, export the lane to the remote */ async rename(newName, laneName) { if (!this.workspace) { throw new (_bitError().BitError)(`unable to rename a lane outside of Bit workspace`); } (0, _lanesModules2().throwForInvalidLaneName)(newName); const currentName = laneName || this.workspace.getCurrentLaneId().name; const existingAliasWithNewName = this.scope.legacyScope.lanes.getRemoteTrackedDataByLocalLane(newName); if (existingAliasWithNewName) { const remoteIdStr = `${existingAliasWithNewName.remoteLane}/${existingAliasWithNewName.remoteScope}`; throw new (_bitError().BitError)(`unable to rename to ${newName}. this name is already used to track: ${remoteIdStr}`); } const laneNameWithoutScope = currentName.includes(_laneId().LANE_REMOTE_DELIMITER) ? currentName.split(_laneId().LANE_REMOTE_DELIMITER)[1] : currentName; const laneId = await this.scope.legacyScope.lanes.parseLaneIdFromString(currentName); const lane = await this.loadLane(laneId); if (!lane) { throw new (_bitError().BitError)(`unable to find a local lane "${currentName}"`); } // rename the ref file await this.scope.legacyScope.objects.remoteLanes.renameRefByNewLaneName(laneNameWithoutScope, newName, lane.scope); // change scope.json related data and change the lane object await this.scope.legacyScope.lanes.renameLane(lane, newName); // change current-lane if needed const currentLaneId = this.getCurrentLaneId(); if (currentLaneId?.isEqual(laneId)) { const newLaneId = _laneId().LaneId.from(newName, lane.scope); const isExported = this.workspace.consumer.bitMap.isLaneExported; this.setCurrentLane(newLaneId, undefined, isExported); } // this writes the changes done on scope.json file (along with .bitmap) await this.workspace.consumer.onDestroy('lane-rename'); return { currentName }; } async exportLane(lane) { await this.exporter.pushToScopes({ scope: this.scope.legacyScope, laneObject: lane, ids: new (_componentId().ComponentIdList)(), allVersions: false }); } async importLaneObject(laneId, persistIfNotExists = true, includeLaneHistory = false) { return this.importer.importLaneObject(laneId, persistIfNotExists, includeLaneHistory); } async eject(pattern) { if (!this.workspace) { throw new (_bitError().BitError)(`unable to eject a component outside of Bit workspace`); } const ids = await this.workspace.idsByPattern(pattern); await Promise.all(ids.map(async id => { const modelComp = await this.scope.getBitObjectModelComponent(id, true); if (!modelComp.head) { throw new (_bitError().BitError)(`unable to eject "${id.toString()}" as it has no main version`); } })); const deletedComps = await this.remove.deleteComps(pattern); const packages = deletedComps.map(c => c.getPackageName()); await this.install.install(packages); return deletedComps.map(c => c.id); } /** * get the head hash (snap) of main. return undefined if the component exists only on a lane and was never merged to main */ async getHeadOnMain(componentId) { const modelComponent = await this.scope.legacyScope.getModelComponent(componentId); return modelComponent.head?.toString(); } /** * fetch the lane object and its components from the remote. * save the objects and the lane to the local scope. * this method doesn't change anything in the workspace. */ async fetchLaneWithItsComponents(laneId, includeUpdateDependents = false) { this.logger.debug(`fetching lane ${laneId.toString()}`); const lane = await this.importer.importLaneObject(laneId); if (!lane) throw new Error(`unable to import lane ${laneId.toString()} from the remote`); await this.importer.fetchLaneComponents(lane, includeUpdateDependents); this.logger.debug(`fetching lane ${laneId.toString()} done, fetched ${lane.components.length} components`); return lane; } async removeLanes(laneNames, opts) { if (!this.workspace && !opts?.remote) { await this.scope.legacyScope.lanes.removeLanes(this.scope.legacyScope, laneNames, true); return laneNames; } const results = await (0, _removeLanes().removeLanes)(this.workspace?.consumer, laneNames, !!opts?.remote, !!opts?.force); if (this.workspace) await this.workspace.consumer.onDestroy('lane-remove'); return results.laneResults; } /** * when deleting a lane object, it is sent into the "trash" directory in the scope. * this method restores it and put it back in the "objects" directory. * as an argument, it needs a hash. the reason for not supporting lane-id is because the trash may have multiple * lanes with the same lane-id but different hashes. */ async restoreLane(laneHash) { const ref = _objects().Ref.from(laneHash); const objectsFromTrash = await this.scope.legacyScope.objects.getFromTrash([ref]); const laneIdFromTrash = objectsFromTrash[0].toLaneId(); const existingWithSameId = await this.loadLane(laneIdFromTrash); if (existingWithSameId) { if (existingWithSameId.hash().isEqual(ref)) { throw new (_bitError().BitError)(`unable to restore lane ${laneIdFromTrash.toString()}, as it already exists`); } throw new (_bitError().BitError)(`unable to restore lane ${laneIdFromTrash.toString()}, as a lane with the same id already exists`); } await this.scope.legacyScope.objects.restoreFromTrash([ref]); } /** * switch to a different local or remote lane. * switching to a remote lane also imports and writes the components of that remote lane. * by default, only the components existing on the workspace will be imported from that lane, unless the "getAll" * flag is true. */ async switchLanes(laneName, { alias, merge, forceOurs, forceTheirs, pattern, workspaceOnly, skipDependencyInstallation = false, head, branch = false }) { if (!this.workspace) { throw new (_workspace().OutsideWorkspaceError)(); } this.workspace.inInstallContext = true; let mergeStrategy; if (merge && typeof merge === 'string') { const mergeOptions = Object.keys(_componentModules().MergeOptions); if (!mergeOptions.includes(merge)) { throw new (_bitError().BitError)(`merge must be one of the following: ${mergeOptions.join(', ')}`); } mergeStrategy = merge; } if (alias) { (0, _lanesModules2().throwForInvalidLaneName)(alias); } const switchProps = { laneName, existingOnWorkspaceOnly: workspaceOnly, pattern, alias, head }; const checkoutProps = { mergeStrategy, forceOurs, forceTheirs, skipNpmInstall: skipDependencyInstallation, isLane: true, promptMergeOptions: false, reset: false, all: false }; // Create git branch if requested and git is available let gitBranchWarning; if (branch) { gitBranchWarning = await this.createGitBranchForLane(laneName); } const switchResult = await new (_switchLanes().LaneSwitcher)(this.workspace, this.logger, switchProps, checkoutProps, this).switch(); // Add git branch warning to the result if present if (gitBranchWarning) { switchResult.gitBranchWarning = gitBranchWarning; } return switchResult; } async createGitBranchForLane(laneName) { if (!this.workspace) return; try { // Check if git exists in the project const isGit = await _fsExtra().default.pathExists('.git'); if (!isGit) { const warning = 'Git repository not found. Skipping git branch creation.'; this.logger.warn(warning); return warning; } const gitExecutablePath = (0, _gitModules().getGitExecutablePath)(); // Create and checkout to the new git branch await (0, _execa().default)(gitExecutablePath, ['checkout', '-b', laneName], { cwd: this.workspace.path }); this.logger.info(`Created and checked out git branch: ${laneName}`); return undefined; // No warning } catch (err) { // Don't fail the lane import if git branch creation fails const detailedError = err.stderr?.trim() || err.message; const warning = `Failed to create git branch "${laneName}". - Command: ${err.command} - Reason: ${detailedError}`; this.logger.warn(warning); return warning; } } /** * the values array may include zero to two values and will be processed as following: * [] => diff between the current lane and default lane. (only inside workspace). * [to] => diff between the current lane (or default-lane when in scope) and "to" lane. * [from, to] => diff between "from" lane and "to" lane. */ async getDiff(values, diffOptions = {}, pattern) { const laneDiffGenerator = new (_lanesModules().LaneDiffGenerator)(this.workspace, this.scope, this.componentCompare); return laneDiffGenerator.generate(values, diffOptions, pattern); } async getLaneComponentModels(lane) { const host = this.componentAspect.getHost(); const laneComponentIds = await this.getLaneComponentIds(lane); const components = await host.getMany(laneComponentIds); return components; } async getLaneComponentIds(lane) { if (!lane) return []; const laneComponents = lane.components; const workspace = this.workspace; const bitIdsFromBitmap = workspace ? workspace.consumer.bitMap.getAllBitIdsFromAllLanes() : []; const filteredComponentIds = workspace ? laneComponents.filter(laneComponent => bitIdsFromBitmap.some(bitmapComponentId => bitmapComponentId.isEqualWithoutVersion(laneComponent.id))) : laneComponents; return filteredComponentIds.map(laneComponent => laneComponent.id.changeVersion(laneComponent.head)); } async getLaneReadmeComponent(lane) { if (!lane) return undefined; const laneReadmeComponent = lane.readmeComponent; if (!laneReadmeComponent) return undefined; const host = this.componentAspect.getHost(); const laneReadmeComponentId = laneReadmeComponent.id.changeVersion(laneReadmeComponent.head); const readmeComponent = await host.get(laneReadmeComponentId); return readmeComponent; } async removeLaneReadme(laneName) { if (!this.workspace) { throw new (_bitError().BitError)('unable to remove the lane readme component outside of Bit workspace'); } const currentLaneName = this.getCurrentLaneName(); if (!laneName && !currentLaneName) { return { result: false, message: 'unable to remove the lane readme component. Either pass a laneName or switch to a lane' }; } const scope = this.workspace.scope.legacyScope; const laneId = laneName ? await scope.lanes.parseLaneIdFromString(laneName) : this.getCurrentLaneId(); const lane = await scope.loadLane(laneId); if (!lane?.readmeComponent) { throw new (_bitError().BitError)(`there is no readme component added to the lane ${laneName || currentLaneName}`); } const readmeComponentId = lane.readmeComponent.id; const existingLaneConfig = (await this.workspace.getSpecificComponentConfig(readmeComponentId, _lanes().LanesAspect.id)) || {}; const remoteLaneIdStr = lane.toLaneId().toString(); if (existingLaneConfig.readme) { delete existingLaneConfig.readme[remoteLaneIdStr]; await this.workspace.removeSpecificComponentConfig(readmeComponentId, _lanes().LanesAspect.id, false); await this.workspace.addSpecificComponentConfig(readmeComponentId, _lanes().LanesAspect.id, existingLaneConfig); } lane.setReadmeComponent(undefined); await scope.lanes.saveLane(lane, { laneHistoryMsg: 'remove readme' }); await this.workspace.bitMap.write(`lane-remove-readme`); return { result: true }; } async diffStatus(sourceLaneId, targetLaneId, options) { this.logger.profile(`diff status for source lane: ${sourceLaneId.name} and target lane: ${targetLaneId?.name}`); const sourceLane = sourceLaneId.isDefault() ? await this.getLaneDataOfDefaultLane() : await this.loadLane(sourceLaneId); const sourceLaneComponents = sourceLaneId.isDefault() ? sourceLane?.components.map(main => ({ id: main.id, head: _objects().Ref.from(main.head) })) : sourceLane?.components; const targetLane = targetLaneId ? await this.loadLane(targetLaneId) : undefined; const targetLaneIds = targetLane?.toBitIds(); const host = this.componentAspect.getHost(); const targetMainHeads = !targetLaneId || targetLaneId?.isDefault() ? (0, _lodash().compact)(await Promise.all((sourceLaneComponents || []).map(async ({ id }) => { const componentId = await host.resolveComponentId(id); const headOnMain = await this.getHeadOnMain(componentId); return headOnMain ? id.changeVersion(headOnMain) : undefined; }))) : []; await this.importer.importObjectsFromMainIfExist(targetMainHeads, { cache: true }); const diffProps = (0, _lodash().compact)(await Promise.all((sourceLaneComponents || []).map(async ({ id, head }) => { const componentId = await host.resolveComponentId(id); const sourceVersionObj = await this.scope.legacyScope.objects.load(head, true); if (sourceVersionObj.isRemoved()) { return null; } const headOnTargetLane = targetLaneIds ? targetLaneIds.searchWithoutVersion(id)?.version : await this.getHeadOnMain(componentId); if (headOnTargetLane) { const targetVersionObj = await this.scope.legacyScope.objects.load(_objects().Ref.from(headOnTargetLane), true); if (targetVersionObj.isRemoved()) { return null; } } const sourceHead = head.toString(); const targetHead = headOnTargetLane; return { componentId, sourceHead, targetHead }; }))); const snapDistancesByComponentId = new Map(); this.logger.profile(`get snaps distance for source lane: ${sourceLane?.id.name} and target lane: ${targetLane?.id.name} with ${diffProps.length} components`); await (0, _pMap().default)(diffProps, async ({ componentId, sourceHead, targetHead }) => { const snapsDistance = await this.scope.getSnapsDistanceBetweenTwoSnaps(componentId, sourceHead, targetHead, false); if (snapsDistance) { snapDistancesByComponentId.set(componentId.toString(), { snapsDistance, sourceHead, targetHead, componentId }); } }, { concurrency: (0, _harmonyModules().concurrentComponentsLimit)() }); this.logger.profile(`get snaps distance for source lane: ${sourceLane?.id.name} and target lane: ${targetLane?.id.name} with ${diffProps.length} components`); const commonSnapsToImport = (0, _lodash().compact)([...snapDistancesByComponentId.values()].map(s => s.snapsDistance.commonSnapBeforeDiverge ? s.componentId.changeVersion(s.snapsDistance.commonSnapBeforeDiverge.hash) : null)); const sourceOrTargetLane = ((sourceLaneId.isDefault() ? null : sourceLane) || (targetLaneId?.isDefault() ? null : targetLane)) ?? undefined; if (commonSnapsToImport.length > 0 && !options?.skipChanges) { await this.scope.legacyScope.scopeImporter.importWithoutDeps(_componentId().ComponentIdList.fromArray(commonSnapsToImport), { cache: true, reason: `get the common snap for lane diff`, lane: sourceOrTargetLane }); } const results = await (0, _pMapSeries().default)(diffProps, async ({ componentId, sourceHead, targetHead }) => this.componentDiffStatus(componentId, sourceHead, targetHead, snapDistancesByComponentId.get(componentId.toString())?.snapsDistance, options)); this.logger.profile(`diff status for source lane: ${sourceLaneId.name} and target lane: ${targetLaneId?.name}`); return { source: sourceLaneId, target: targetLaneId || this.getDefaultLaneId(), componentsStatus: results }; } async componentDiffStatus(componentId, sourceHead, targetHead, snapsDistance, options) { if (snapsDistance?.err) { const noCommonSnap = snapsDistance.err instanceof _legacy().NoCommonSnap; return { componentId, sourceHead, targetHead, upToDate: snapsDistance?.isUpToDate(), unrelated: noCommonSnap || undefined, changes: [] }; } const commonSnap = snapsDistance?.commonSnapBeforeDiverge; const getChanges = async () => { if (!commonSnap) return [_lanesEntities().ChangeType.NEW]; // import common snap before we compare const compare = await this.componentCompare.compare(componentId.changeVersion(commonSnap.hash).toString(), componentId.changeVersion(sourceHead).toString()); if (!compare.fields.length && (!compare.code.length || !compare.code.some(c => c.status !== 'UNCHANGED'))) { return [_lanesEntities().ChangeType.NONE]; } const changed = []; if (compare.code.some(f => f.status !== 'UNCHANGED')) { changed.push(_lanesEntities().ChangeType.SOURCE_CODE); } if (compare.fields.length > 0) { changed.push(_lanesEntities().ChangeType.ASPECTS); } const depsFields = ['dependencies', 'devDependencies', 'extensionDependencies']; if (compare.fields.some(field => depsFields.includes(field.fieldName))) { changed.push(_lanesEntities().ChangeType.DEPENDENCY); } return changed; }; const changes = !options?.skipChanges ? await getChanges() : undefined; const changeType = changes ? changes[0] : undefined; return { componentId, changeType, changes, sourceHead, targetHead: commonSnap?.hash, upToDate: snapsDistance?.isUpToDate(), snapsDistance: { onSource: snapsDistance?.snapsOnSourceOnly.map(s => s.hash) ?? [], onTarget: snapsDistance?.snapsOnTargetOnly.map(s => s.hash) ?? [], common: snapsDistance?.commonSnapBeforeDiverge?.hash } }; } async componentDiffStatusOld(componentId, sourceHead, targetHead, options) { const snapsDistance = await this.scope.getSnapsDistanceBetweenTwoSnaps(componentId, sourceHead, targetHead, false); if (snapsDistance?.err) { const noCommonSnap = snapsDistance.err instanceof _legacy().NoCommonSnap; return { componentId, sourceHead, targetHead, upToDate: snapsDistance?.isUpToDate(), unrelated: noCommonSnap || undefined, changes: [] }; } const commonSnap = snapsDistance?.commonSnapBeforeDiverge; const getChanges = async () => { if (!commonSnap) return [_lanesEntities().ChangeType.NEW]; const compare = await this.componentCompare.compare(componentId.changeVersion(commonSnap.hash).toString(), componentId.changeVersion(sourceHead).toString()); if (!compare.fields.length && (!compare.code.length || !compare.code.some(c => c.status !== 'UNCHANGED'))) { return [_lanesEntities().ChangeType.NONE]; } const changed = []; if (compare.code.some(f => f.status !== 'UNCHANGED')) { changed.push(_lanesEntities().ChangeType.SOURCE_CODE); } if (compare.fields.length > 0) { changed.push(_lanesEntities().ChangeType.ASPECTS); } const depsFields = ['dependencies', 'devDependencies', 'extensionDependencies']; if (compare.fields.some(field => depsFields.includes(field.fieldName))) { changed.push(_lanesEntities().ChangeType.DEPENDENCY); } return changed; }; const changes = !options?.skipChanges ? await getChanges() : undefined; const changeType = changes ? changes[0] : undefined; return { componentId, changeType, changes, sourceHead, targetHead: commonSnap?.hash, upToDate: snapsDistance?.isUpToDate(), snapsDistance: { onSource: snapsDistance?.snapsOnSourceOnly.map(s => s.hash) ?? [], onTarget: snapsDistance?.snapsOnTargetOnly.map(s => s.hash) ?? [], common: snapsDistance?.commonSnapBeforeDiverge?.hash } }; } async recreateNewLaneIfDeleted() { if (!this.workspace) return; const laneId = this.getCurrentLaneId(); if (!laneId || laneId.isDefault() || this.workspace.consumer.bitMap.isLaneExported) { return; } const laneObj = await this.scope.legacyScope.getCurrentLaneObject(); if (laneObj) { return; } await this.createLane(laneId.name, { scope: laneId.scope }); } /** * if the local lane was forked from another lane, this gets the differences between the two. * it also fetches the original lane from the remote to make sure the data is up to date. */ async listUpdatesFromForked(componentsList) { const consumer = this.workspace?.consumer; if (!consumer) throw new Error(`unable to get listUpdatesFromForked outside of a workspace`); if (consumer.isOnMain()) { return []; } const lane = await consumer.getCurrentLaneObject(); const forkedFromLaneId = lane?.forkedFrom; if (!forkedFromLaneId) { return []; } const forkedFromLane = await consumer.scope.loadLane(forkedFromLaneId); if (!forkedFromLane) return []; // should we fetch it here? const workspaceIds = consumer.bitMap.getAllBitIds(); const duringMergeIds = componentsList.listDuringMergeStateComponents(); const componentsFromModel = await componentsList.getModelComponents(); const compFromModelOnWorkspace = componentsFromModel.filter(c => workspaceIds.hasWithoutVersion(c.toComponentId())) // if a component is merge-pending, it needs to be resolved first before getting more updates from main .filter(c => !duringMergeIds.hasWithoutVersion(c.toComponentId())); // by default, when on a lane, forked is not fetched. we need to fetch it to get the latest updates. await this.fetchLaneWithItsComponents(forkedFromLaneId); const remoteForkedLane = await consumer.scope.objects.remoteLanes.getRemoteLane(forkedFromLaneId); if (!remoteForkedLane.length) return []; const results = await Promise.all(compFromModelOnWorkspace.map(async modelComponent => { const headOnForked = remoteForkedLane.find(c => c.id.isEqualWithoutVersion(modelComponent.toComponentId())); const headOnLane = modelComponent.laneHeadLocal; if (!headOnForked || !headOnLane) return undefined; const divergeData = await (0, _component2().getDivergeData)({ repo: consumer.scope.objects, modelComponent, targetHead: headOnForked.head, sourceHead: headOnLane, throws: false }); if (!divergeData.snapsOnTargetOnly.length && !divergeData.err) return undefined; return { id: modelComponent.toComponentId(), divergeData }; })); return (0, _lodash().compact)(results); } /** * list components on a lane that their main got updates. */ async listUpdatesFromMainPending(componentsList) { const consumer = this.workspace?.consumer; if (!consumer) throw new Error(`unable to get listUpdatesFromForked outside of a workspace`); if (consumer.isOnMain()) { return []; } const allIds = consumer.bitMap.getAllBitIds(); const duringMergeIds = componentsList.listDuringMergeStateComponents(); const componentsFromModel = await componentsList.getModelComponents(); const compFromModelOnWorkspace = componentsFromModel.filter(c => allIds.hasWithoutVersion(c.toComponentId())) // if a component is merge-pending, it needs to be resolved first before getting more updates from main .filter(c => !duringMergeIds.hasWithoutVersion(c.toComponentId())); // by default, when on a lane, main is not fetched. we need to fetch it to get the latest updates. await consumer.scope.scopeImporter.importWithoutDeps(_componentId().ComponentIdList.fromArray(compFromModelOnWorkspace.map(c => c.toComponentId())), { cache: false, includeVersionHistory: true, ignoreMissingHead: true, reason: 'main components of the current lane to check for updates' }); const results = await Promise.all(compFromModelOnWorkspace.map(async modelComponent => { const headOnMain = modelComponent.head; if (!headOnMain) return undefined; const checkedOutVersion = allIds.searchWithoutVersion(modelComponent.toComponentId())?.version; if (!checkedOutVersion) { throw new Error(`listUpdatesFromMainPending: unable to find ${modelComponent.toComponentId()} in the workspace`); } const headOnLane = modelComponent.getRef(checkedOutVersion); const divergeData = await (0, _component2().getDivergeData)({ repo: consumer.scope.objects, modelComponent, targetHead: headOnMain, sourceHead: headOnLane, throws: false }); if (!divergeData.snapsOnTargetOnly.length && !divergeData.err) return undefined; return { id: modelComponent.toComponentId(), divergeData }; })); return (0, _lodash().compact)(results); } /** * default to remove all of them. * returns true if the lane has changed */ async removeUpdateDependents(laneId, ids) { const lane = await this.loadLane(laneId); if (!lane) throw new (_bitError().BitError)(`unable to find a lane ${laneId.toString()}`); if (ids?.length) { ids.forEach(id => lane.removeComponentFromUpdateDependentsIfExist(id)); } else { lane.removeAllUpdateDependents(); } if (lane.hasChanged) { await this.scope.legacyScope.lanes.saveLane(lane, { laneHistoryMsg: 'remove update-dependents' }); return true; } return false; } async getLaneDataOfDefaultLane() { const consumer = this.workspace?.consumer; let bitIds = []; if (!consumer) { const scopeComponents = await this.scope.list(); bitIds = scopeComponents.filter(component => component.head).map(component => component.id); } else { bitIds = await consumer.getIdsOfDefaultLane(); } return { name: _laneId().DEFAULT_LANE, remote: null, id: this.getDefaultLaneId(), components: bitIds.map(bitId => ({ id: bitId, head: bitId.version })), isMerged: null, hash: '' }; } get createRoutePath() { return '/lanes/create'; } get deleteRoutePath() { return '/lanes/delete'; } get restoreRoutePath() { return '/lanes/restore'; } static async provider([cli, scope, workspace, graphql, merging, component, loggerMain, importer, exporter, express, componentCompare, componentWriter, remove, checkout, install]) { const logger = loggerMain.createLogger(_lanes().LanesAspect.id); const lanesMain = new LanesMain(workspace, scope, merging, component, logger, importer, exporter, componentCompare, componentWriter, remove, checkout, install); const switchCmd = new (_switch().SwitchCmd)(lanesMain); const fetchCmd = new (_importer().FetchCmd)(importer); const laneCmd = new (_lane().LaneCmd)(lanesMain, workspace, scope); laneCmd.commands = [new (_lane().LaneListCmd)(lanesMain, workspace, scope), switchCmd, new (_lane().LaneShowCmd)(lanesMain, workspace, scope), new (_lane().LaneCreateCmd)(lanesMain), new (_lane().LaneRemoveCmd)(lanesMain), new (_lane().LaneChangeScopeCmd)(lanesMain), new (_lane().LaneAliasCmd)(lanesMain), new (_lane().LaneRenameCmd)(lanesMain), new (_lanesModules().LaneDiffCmd)(workspace, scope, componentCompare), new (_lane().LaneRemoveReadmeCmd)(lanesMain), new (_lane().LaneImportCmd)(switchCmd), new (_lane().LaneRemoveCompCmd)(workspace, lanesMain), new (_lane().LaneFetchCmd)(fetchCmd, lanesMain), new (_lane().LaneEjectCmd)(lanesMain)]; laneCmd.commands.push(new (_lane().LaneHistoryCmd)(lanesMain)); laneCmd.commands.push(new (_lanesModules().LaneHistoryDiffCmd)(lanesMain, workspace, scope, componentCompare)); laneCmd.commands.push(new (_lane().LaneCheckoutCmd)(lanesMain)); laneCmd.commands.push(new (_lane().LaneRevertCmd)(lanesMain)); cli.register(laneCmd, switchCmd, new (_lane().CatLaneHistoryCmd)(lanesMain)); cli.registerOnStart(async () => { await lanesMain.recreateNewLaneIfDeleted(); }); graphql.register(() => (0, _lanes2().lanesSchema)(lanesMain)); express.register([new (_lanesCreate().LanesCreateRoute)(lanesMain, logger), new (_lanesDelete().LanesDeleteRoute)(lanesMain, logger), new (_lanesRestore().LanesRestoreRoute)(lanesMain, logger)]); return lanesMain; } } exports.LanesMain = LanesMain; _defineProperty(LanesMain, "slots", []); _defineProperty(LanesMain, "dependencies", [_cli().CLIAspect, _scope().ScopeAspect, _workspace().WorkspaceAspect, _graphql().GraphqlAspect, _merging().MergingAspect, _component().ComponentAspect, _logger().LoggerAspect, _importer().ImporterAspect, _export().ExportAspect, _express().ExpressAspect, _componentCompare().ComponentCompareAspect, _componentWriter().ComponentWriterAspect, _remove().RemoveAspect, _checkout().CheckoutAspect, _install().InstallAspect]); _defineProperty(LanesMain, "runtime", _cli().MainRuntime); _lanes().LanesAspect.addRuntime(LanesMain); var _default = exports.default = LanesMain; //# sourceMappingURL=lanes.main.runtime.js.map