@teambit/lanes
Version:
1,250 lines (1,226 loc) • 48.5 kB
JavaScript
;
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