UNPKG

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
"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;