bit-bin
Version:
<a href="https://opensource.org/licenses/Apache-2.0"><img alt="apache" src="https://img.shields.io/badge/License-Apache%202.0-blue.svg"></a> <a href="https://github.com/teambit/bit/blob/master/CONTRIBUTING.md"><img alt="prs" src="https://img.shields.io/b
847 lines (655 loc) • 24.3 kB
JavaScript
"use strict";
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
function _bluebird() {
const data = require("bluebird");
_bluebird = function () {
return data;
};
return data;
}
function _defineProperty2() {
const data = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
_defineProperty2 = function () {
return data;
};
return data;
}
function path() {
const data = _interopRequireWildcard(require("path"));
path = function () {
return data;
};
return data;
}
function _fsExtra() {
const data = _interopRequireDefault(require("fs-extra"));
_fsExtra = function () {
return data;
};
return data;
}
function _ramda() {
const data = _interopRequireDefault(require("ramda"));
_ramda = function () {
return data;
};
return data;
}
function _commentJson() {
const data = _interopRequireDefault(require("comment-json"));
_commentJson = function () {
return data;
};
return data;
}
function _logger() {
const data = _interopRequireDefault(require("../../logger/logger"));
_logger = function () {
return data;
};
return data;
}
function _constants() {
const data = require("../../constants");
_constants = function () {
return data;
};
return data;
}
function _exceptions() {
const data = require("./exceptions");
_exceptions = function () {
return data;
};
return data;
}
function _bitId() {
const data = require("../../bit-id");
_bitId = function () {
return data;
};
return data;
}
function _utils() {
const data = require("../../utils");
_utils = function () {
return data;
};
return data;
}
function _componentMap() {
const data = _interopRequireDefault(require("./component-map"));
_componentMap = function () {
return data;
};
return data;
}
function _showDoctorError() {
const data = _interopRequireDefault(require("../../error/show-doctor-error"));
_showDoctorError = function () {
return data;
};
return data;
}
class BitMap {
// path => componentId
// path => componentId
// trackDir key is PathLinux. (ts doesn't allow keys other than string and numbers)
constructor(projectRoot, mapPath, version) {
(0, _defineProperty2().default)(this, "projectRoot", void 0);
(0, _defineProperty2().default)(this, "mapPath", void 0);
(0, _defineProperty2().default)(this, "components", void 0);
(0, _defineProperty2().default)(this, "hasChanged", void 0);
(0, _defineProperty2().default)(this, "version", void 0);
(0, _defineProperty2().default)(this, "paths", void 0);
(0, _defineProperty2().default)(this, "pathsLowerCase", void 0);
(0, _defineProperty2().default)(this, "markAsChangedBinded", void 0);
(0, _defineProperty2().default)(this, "_cacheIds", void 0);
(0, _defineProperty2().default)(this, "allTrackDirs", void 0);
(0, _defineProperty2().default)(this, "_invalidateCache", () => {
this.paths = {};
this.pathsLowerCase = {};
this._cacheIds = {};
this.allTrackDirs = undefined;
});
this.projectRoot = projectRoot;
this.mapPath = mapPath;
this.components = [];
this.hasChanged = false;
this.version = version;
this.paths = {};
this.pathsLowerCase = {};
this._cacheIds = {};
this.markAsChangedBinded = this.markAsChanged.bind(this);
}
markAsChanged() {
this.hasChanged = true;
this._invalidateCache();
}
setComponent(bitId, componentMap) {
const id = bitId.toString();
if (!bitId.hasVersion() && bitId.scope) {
throw new (_showDoctorError().default)(`invalid bitmap id ${id}, a component must have a version when a scope-name is included`);
}
if (componentMap.origin !== _constants().COMPONENT_ORIGINS.NESTED) {
// make sure there are no duplications (same name)
const similarIds = this.findSimilarIds(bitId, true);
if (similarIds.length) {
throw new (_showDoctorError().default)(`your id ${id} is duplicated with ${similarIds.toString()}`);
}
}
componentMap.id = bitId;
this.components.push(componentMap);
this.markAsChanged();
} // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
setComponentProp(id, propName, val) {
const componentMap = this.getComponent(id, {
ignoreScopeAndVersion: true
});
componentMap[propName] = val;
this.markAsChanged();
return componentMap;
}
isEmpty() {
return _ramda().default.isEmpty(this.components);
} // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
removeComponentProp(id, propName) {
const componentMap = this.getComponent(id, {
ignoreScopeAndVersion: true
});
delete componentMap[propName];
this.markAsChanged();
return componentMap;
}
static load(dirPath) {
const {
currentLocation,
defaultLocation
} = BitMap.getBitMapLocation(dirPath);
const mapFileContent = BitMap.loadRawSync(dirPath);
if (!mapFileContent || !currentLocation) {
return new BitMap(dirPath, defaultLocation, _constants().BIT_VERSION);
}
let componentsJson;
try {
componentsJson = _commentJson().default.parse(mapFileContent.toString('utf8'), undefined, true);
} catch (e) {
_logger().default.error(`invalid bitmap at ${currentLocation}`, e);
throw new (_exceptions().InvalidBitMap)(currentLocation, e.message);
}
const version = componentsJson.version; // Don't treat version like component
delete componentsJson.version;
const bitMap = new BitMap(dirPath, currentLocation, version);
bitMap.loadComponents(componentsJson);
return bitMap;
}
static loadRawSync(dirPath) {
const {
currentLocation
} = BitMap.getBitMapLocation(dirPath);
if (!currentLocation) {
_logger().default.info(`bit.map: unable to find an existing ${_constants().BIT_MAP} file. Will create a new one if needed`);
return undefined;
}
const mapFileContent = _fsExtra().default.readFileSync(currentLocation);
return mapFileContent;
}
static getBitMapLocation(dirPath) {
const defaultLocation = path().join(dirPath, _constants().BIT_MAP);
const oldLocation = path().join(dirPath, _constants().OLD_BIT_MAP);
const getCurrentLocation = () => {
if (_fsExtra().default.existsSync(defaultLocation)) return defaultLocation;
if (_fsExtra().default.existsSync(oldLocation)) return oldLocation;
return undefined;
};
const currentLocation = getCurrentLocation();
return {
currentLocation,
defaultLocation
};
}
/**
* if resetHard, delete the bitMap file.
* Otherwise, try to load it and only if the file is corrupted then delete it.
*/
static reset(dirPath, resetHard) {
const bitMapPath = path().join(dirPath, _constants().BIT_MAP);
const deleteBitMapFile = () => {
_logger().default.info(`deleting the bitMap file at ${bitMapPath}`);
_fsExtra().default.removeSync(bitMapPath);
};
if (resetHard) {
deleteBitMapFile();
return;
}
try {
BitMap.load(dirPath);
} catch (err) {
if (err instanceof _exceptions().InvalidBitMap) {
deleteBitMapFile();
return;
}
throw err;
}
}
loadComponents(componentsJson) {
Object.keys(componentsJson).forEach(componentId => {
const componentFromJson = componentsJson[componentId];
const idHasScope = () => {
if (componentFromJson.origin !== _constants().COMPONENT_ORIGINS.AUTHORED) return true;
if ('exported' in componentFromJson) {
return componentFromJson.exported;
} // backward compatibility
return _bitId().BitId.parseObsolete(componentId).hasScope();
};
componentFromJson.id = _bitId().BitId.parse(componentId, idHasScope());
const componentMap = _componentMap().default.fromJson(componentsJson[componentId]);
componentMap.setMarkAsChangedCb(this.markAsChangedBinded);
this.components.push(componentMap);
});
}
getAllComponents(origin) {
if (!origin) return this.components;
const isOriginMatch = component => component.origin === origin; // $FlowFixMe we know origin is an array in that case
const isOriginMatchArray = component => origin.includes(component.origin);
const filter = Array.isArray(origin) ? isOriginMatchArray : isOriginMatch;
return _ramda().default.filter(filter, this.components);
}
getAllBitIds(origin) {
const ids = componentMaps => _bitId().BitIds.fromArray(componentMaps.map(c => c.id));
const getIdsOfOrigin = oneOrigin => {
const cacheKey = oneOrigin || 'all'; // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
if (this._cacheIds[cacheKey]) return this._cacheIds[cacheKey];
const allComponents = this.components;
const components = oneOrigin ? allComponents.filter(c => c.origin === oneOrigin) : allComponents;
const componentIds = ids(components);
this._cacheIds[cacheKey] = componentIds;
return componentIds;
};
if (!origin) return getIdsOfOrigin();
return _bitId().BitIds.fromArray(_ramda().default.flatten(origin.map(oneOrigin => getIdsOfOrigin(oneOrigin))));
}
/**
* get existing bitmap bit-id by bit-id.
* throw an exception if not found
* @see also getBitIdIfExist
*/
getBitId(bitId, {
ignoreVersion = false,
ignoreScopeAndVersion = false
} = {}) {
if (!(bitId instanceof _bitId().BitId)) {
throw new TypeError(`BitMap.getBitId expects bitId to be an instance of BitId, instead, got ${bitId}`);
}
const allIds = this.getAllBitIds();
const exactMatch = allIds.search(bitId);
if (exactMatch) return exactMatch;
if (ignoreVersion) {
const matchWithoutVersion = allIds.searchWithoutVersion(bitId);
if (matchWithoutVersion) return matchWithoutVersion;
}
if (ignoreScopeAndVersion) {
const matchWithoutScopeAndVersion = allIds.searchWithoutScopeAndVersion(bitId);
if (matchWithoutScopeAndVersion) return matchWithoutScopeAndVersion;
}
throw new (_exceptions().MissingBitMapComponent)(bitId.toString());
}
/**
* get existing bitmap bit-id by bit-id
* don't throw an exception if not found
* @see also getBitId
*/
getBitIdIfExist(bitId, {
ignoreVersion = false,
ignoreScopeAndVersion = false
} = {}) {
try {
const existingBitId = this.getBitId(bitId, {
ignoreVersion,
ignoreScopeAndVersion
});
return existingBitId;
} catch (err) {
if (err instanceof _exceptions().MissingBitMapComponent) return undefined;
throw err;
}
}
/**
* get componentMap from bitmap by bit-id.
* throw an exception if not found.
* @see also getComponentIfExist
*/
getComponent(bitId, {
ignoreVersion = false,
ignoreScopeAndVersion = false
} = {}) {
const existingBitId = this.getBitId(bitId, {
ignoreVersion,
ignoreScopeAndVersion
});
return this.components.find(c => c.id.isEqual(existingBitId));
}
/**
* get componentMap from bitmap by bit-id
* don't throw an exception if not found
* @see also getComponent
*/
getComponentIfExist(bitId, {
ignoreVersion = false,
ignoreScopeAndVersion = false
} = {}) {
try {
const componentMap = this.getComponent(bitId, {
ignoreVersion,
ignoreScopeAndVersion
});
return componentMap;
} catch (err) {
if (err instanceof _exceptions().MissingBitMapComponent) return undefined;
throw err;
}
}
getNonNestedComponentIfExist(bitId) {
const nonNestedIds = this.getAllBitIds([_constants().COMPONENT_ORIGINS.IMPORTED, _constants().COMPONENT_ORIGINS.AUTHORED]);
const id = nonNestedIds.searchWithoutScopeAndVersion(bitId);
if (!id) return undefined;
return this.getComponent(id);
}
getComponentPreferNonNested(bitId) {
return this.getNonNestedComponentIfExist(bitId) || this.getComponentIfExist(bitId, {
ignoreVersion: true
});
}
getAuthoredAndImportedBitIds() {
return this.getAllBitIds([_constants().COMPONENT_ORIGINS.AUTHORED, _constants().COMPONENT_ORIGINS.IMPORTED]);
}
getAuthoredExportedComponents() {
const authoredIds = this.getAllBitIds([_constants().COMPONENT_ORIGINS.AUTHORED]);
return authoredIds.filter(id => id.hasScope());
}
_makePathRelativeToProjectRoot(pathToChange) {
const absolutePath = path().resolve(pathToChange);
return path().relative(this.projectRoot, absolutePath);
}
/**
* find ids that have the same name but different version
* if compareWithoutScope is false, the scope should be identical in addition to the name
*/
findSimilarIds(id, compareWithoutScope = false) {
const allIds = this.getAllBitIds([_constants().COMPONENT_ORIGINS.IMPORTED, _constants().COMPONENT_ORIGINS.AUTHORED]);
const similarIds = allIds.filter(existingId => {
const isSimilar = compareWithoutScope ? existingId.isEqualWithoutScopeAndVersion(id) : existingId.isEqualWithoutVersion(id);
return isSimilar && !existingId.isEqual(id);
});
return _bitId().BitIds.fromArray(similarIds);
}
deleteOlderVersionsOfComponent(componentId) {
const similarIds = this.findSimilarIds(componentId);
similarIds.forEach(id => {
const idStr = id.toString();
_logger().default.debugAndAddBreadCrumb('BitMap.deleteOlderVersionsOfComponent', 'deleting an older version {idStr} of an existing component {componentId}', {
idStr,
componentId: componentId.toString()
});
this._removeFromComponentsArray(id);
});
}
/**
* --- Don't use this function when you have the ID parsed. Use this.getBitId() instead ---
*
* id entered by the user may or may not include scope-name
* search for a similar id in the bitmap and return the full BitId
*/
getExistingBitId(id, shouldThrow = true) {
if (!_ramda().default.is(String, id)) {
throw new TypeError(`BitMap.getExistingBitId expects id to be a string, instead, got ${typeof id}`);
}
const idHasVersion = id.includes(_constants().VERSION_DELIMITER); // start with a more strict comparison. assume the id from the user has a scope name
const componentWithScope = this.components.find(componentMap => {
return idHasVersion ? componentMap.id.toString() === id : componentMap.id.toStringWithoutVersion() === id;
});
if (componentWithScope) return componentWithScope.id; // continue with searching without the scope name
const idWithoutVersion = _bitId().BitId.getStringWithoutVersion(id);
const componentWithoutScope = this.components.find(componentMap => {
return idHasVersion ? componentMap.id.toStringWithoutScope() === id : componentMap.id.toStringWithoutScopeAndVersion() === idWithoutVersion;
});
if (componentWithoutScope) return componentWithoutScope.id;
if (shouldThrow) {
throw new (_exceptions().MissingBitMapComponent)(id);
}
return undefined;
}
/**
* check if both arrays are equal according to their 'relativePath', regardless the order
*/
_areFilesArraysEqual(filesA, filesB) {
if (filesA.length !== filesB.length) return false;
const cmp = (x, y) => x.relativePath === y.relativePath;
const diff = _ramda().default.differenceWith(cmp, filesA, filesB);
if (!diff.length) return true;
return false;
}
/**
* add files from filesB that are not in filesA
*/
mergeFilesArray(filesA, filesB) {
return _ramda().default.unionWith(_ramda().default.eqBy(_ramda().default.prop('relativePath')), filesA, filesB);
}
addComponent({
componentId,
files,
mainFile,
origin,
rootDir,
trackDir,
originallySharedDir,
wrapDir
}) {
const componentIdStr = componentId.toString();
_logger().default.debug(`adding to bit.map ${componentIdStr}`);
const getOrCreateComponentMap = () => {
const componentMap = this.getComponentIfExist(componentId);
if (componentMap) {
_logger().default.info(`bit.map: updating an exiting component ${componentIdStr}`);
componentMap.files = files;
return componentMap;
}
if (origin === _constants().COMPONENT_ORIGINS.IMPORTED || origin === _constants().COMPONENT_ORIGINS.AUTHORED) {
// if there are older versions, the user is updating an existing component, delete old ones from bit.map
this.deleteOlderVersionsOfComponent(componentId);
} // @ts-ignore not easy to fix, we can't instantiate ComponentMap with mainFile because we don't have it yet
const newComponentMap = new (_componentMap().default)({
files,
origin
});
newComponentMap.setMarkAsChangedCb(this.markAsChangedBinded);
this.setComponent(componentId, newComponentMap);
return newComponentMap;
};
const componentMap = getOrCreateComponentMap();
componentMap.mainFile = mainFile;
if (rootDir) {
componentMap.rootDir = (0, _utils().pathNormalizeToLinux)(rootDir);
}
if (trackDir) {
componentMap.trackDir = (0, _utils().pathNormalizeToLinux)(trackDir);
}
if (wrapDir) {
componentMap.wrapDir = wrapDir;
}
componentMap.removeTrackDirIfNeeded();
if (originallySharedDir) {
componentMap.originallySharedDir = originallySharedDir;
}
this.sortValidateAndMarkAsChanged(componentMap);
return componentMap;
}
addFilesToComponent({
componentId,
files
}) {
const componentIdStr = componentId.toString();
const componentMap = this.getComponentIfExist(componentId);
if (!componentMap) {
throw new (_showDoctorError().default)(`unable to add files to a non-exist component ${componentIdStr}`);
}
_logger().default.info(`bit.map: updating an exiting component ${componentIdStr}`);
componentMap.files = files;
this.sortValidateAndMarkAsChanged(componentMap);
return componentMap;
}
sortValidateAndMarkAsChanged(componentMap) {
componentMap.sort();
componentMap.validate();
this.markAsChanged();
}
_removeFromComponentsArray(componentId) {
this.components = this.components.filter(componentMap => !componentMap.id.isEqual(componentId));
this.markAsChanged();
}
removeComponent(bitId) {
const bitmapComponent = this.getBitIdIfExist(bitId, {
ignoreScopeAndVersion: true
});
if (bitmapComponent) this._removeFromComponentsArray(bitmapComponent);
return bitmapComponent;
}
removeComponents(ids) {
return ids.map(id => this.removeComponent(id));
}
isExistWithSameVersion(id) {
return Boolean(id.hasVersion() && this.getComponentIfExist(id));
}
/**
* needed after exporting or tagging a component.
* We don't support export/tag of nested components, only authored or imported. For authored/imported components, could be
* in the file-system only one instance with the same component-name. As a result, we can strip the
* scope-name and the version, find the older version in bit.map and update the id with the new one.
*/
updateComponentId(id, updateScopeOnly = false) {
const newIdString = id.toString();
const similarIds = this.findSimilarIds(id, true);
if (!similarIds.length) {
_logger().default.debug(`bit-map: no need to update ${newIdString}`);
return id;
}
if (similarIds.length > 1) {
throw new (_showDoctorError().default)(`Your ${_constants().BIT_MAP} file has more than one version of ${id.toStringWithoutScopeAndVersion()} and they
are authored or imported. This scenario is not supported`);
}
const oldId = similarIds[0];
const oldIdStr = oldId.toString();
const newId = updateScopeOnly ? oldId.changeScope(id.scope) : id;
if (newId.isEqual(oldId)) {
_logger().default.debug(`bit-map: no need to update ${oldIdStr}`);
return oldId;
}
_logger().default.debug(`BitMap: updating an older component ${oldIdStr} with a newer component ${newId.toString()}`);
const componentMap = this.getComponent(oldId);
if (componentMap.origin === _constants().COMPONENT_ORIGINS.NESTED) {
throw new Error('updateComponentId should not manipulate Nested components');
}
this._removeFromComponentsArray(oldId);
this.setComponent(newId, componentMap);
this.markAsChanged();
return newId;
}
/**
* Return a component id as listed in bit.map file
* by a path exist in the files object
*
* @param {string} componentPath relative to consumer - as stored in bit.map files object
* @returns {BitId} component id
* @memberof BitMap
*/
getComponentIdByPath(componentPath, caseSensitive = true) {
this._populateAllPaths();
return caseSensitive ? this.paths[componentPath] : this.pathsLowerCase[componentPath.toLowerCase()];
}
_populateAllPaths() {
if (_ramda().default.isEmpty(this.paths)) {
this.components.forEach(component => {
component.files.forEach(file => {
const relativeToConsumer = component.rootDir ? (0, _utils().pathJoinLinux)(component.rootDir, file.relativePath) : file.relativePath;
this.paths[relativeToConsumer] = component.id;
this.pathsLowerCase[relativeToConsumer.toLowerCase()] = component.id;
});
});
}
}
getAllTrackDirs() {
if (!this.allTrackDirs) {
this.allTrackDirs = {};
this.components.forEach(component => {
const trackDir = component.getTrackDir();
if (!trackDir) return; // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
this.allTrackDirs[trackDir] = component.id;
});
}
return this.allTrackDirs;
}
updatePathLocation(from, to, existingPath) {
const isPathDir = (0, _utils().isDir)(existingPath);
const allChanges = [];
this.components.forEach(componentMap => {
const changes = isPathDir ? componentMap.updateDirLocation(from, to) : componentMap.updateFileLocation(from, to); // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
if (changes && changes.length) allChanges.push({
id: componentMap.id.clone(),
changes
});
});
if (_ramda().default.isEmpty(allChanges)) {
const errorMsg = isPathDir ? `directory ${from} is not a tracked component` : `the file ${existingPath} is untracked`;
throw new (_showDoctorError().default)(errorMsg);
}
this.markAsChanged();
return allChanges;
}
/**
* remove the id property before saving the components to the file as they are redundant with the keys
*/
toObjects() {
const components = {};
this.components.forEach(componentMap => {
const componentMapCloned = componentMap.clone();
if (componentMapCloned.origin === _constants().COMPONENT_ORIGINS.AUTHORED) {
componentMapCloned.exported = componentMapCloned.id.hasScope();
}
const idStr = componentMapCloned.id.toString();
delete componentMapCloned.id;
components[idStr] = componentMapCloned.toPlainObject();
});
return (0, _utils().sortObject)(components);
}
/**
* do not call this function directly, let consumer.onDestroy() call it.
* consumer.onDestroy() is being called (manually) at the end of the command process.
* the risk of calling this method in other places is a parallel writing of this file, which
* may result in a damaged file
*/
write() {
var _this = this;
return (0, _bluebird().coroutine)(function* () {
if (!_this.hasChanged) return undefined;
_logger().default.debug('writing to bit.map');
const bitMapContent = _this.getContent();
return (0, _utils().outputFile)({
filePath: _this.mapPath,
content: JSON.stringify(bitMapContent, null, 4)
});
})();
}
getContent() {
const bitMapContent = Object.assign({}, this.toObjects(), {
version: this.version
});
return bitMapContent;
}
}
exports.default = BitMap;