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
673 lines (510 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 _ramda() {
const data = _interopRequireDefault(require("ramda"));
_ramda = function () {
return data;
};
return data;
}
function _fsExtra() {
const data = _interopRequireDefault(require("fs-extra"));
_fsExtra = function () {
return data;
};
return data;
}
function _semver() {
const data = _interopRequireDefault(require("semver"));
_semver = function () {
return data;
};
return data;
}
function path() {
const data = _interopRequireWildcard(require("path"));
path = function () {
return data;
};
return data;
}
function _logger() {
const data = _interopRequireDefault(require("../../logger/logger"));
_logger = function () {
return data;
};
return data;
}
function _path2() {
const data = require("../../utils/path");
_path2 = function () {
return data;
};
return data;
}
function _constants() {
const data = require("../../constants");
_constants = function () {
return data;
};
return data;
}
function _componentNodeModulesPath() {
const data = _interopRequireDefault(require("../../utils/bit/component-node-modules-path"));
_componentNodeModulesPath = function () {
return data;
};
return data;
}
function _packageJsonUtils() {
const data = require("../component/package-json-utils");
_packageJsonUtils = function () {
return data;
};
return data;
}
function _dataToPersist() {
const data = _interopRequireDefault(require("../component/sources/data-to-persist"));
_dataToPersist = function () {
return data;
};
return data;
}
function _removePath() {
const data = _interopRequireDefault(require("../component/sources/remove-path"));
_removePath = function () {
return data;
};
return data;
}
function _envExtension() {
const data = _interopRequireDefault(require("../../legacy-extensions/env-extension"));
_envExtension = function () {
return data;
};
return data;
}
function _componentConfig() {
const data = _interopRequireDefault(require("../config/component-config"));
_componentConfig = function () {
return data;
};
return data;
}
function _showDoctorError() {
const data = _interopRequireDefault(require("../../error/show-doctor-error"));
_showDoctorError = function () {
return data;
};
return data;
}
class ComponentWriter {
// when using capsule, the consumer is not defined
constructor({
component,
writeToPath,
writeConfig = false,
writePackageJson = true,
override = true,
isolated = false,
origin,
consumer,
bitMap,
writeBitDependencies = false,
deleteBitDirContent,
existingComponentMap,
excludeRegistryPrefix = false,
applyExtensionsAddedConfig = false
}) {
(0, _defineProperty2().default)(this, "component", void 0);
(0, _defineProperty2().default)(this, "writeToPath", void 0);
(0, _defineProperty2().default)(this, "writeConfig", void 0);
(0, _defineProperty2().default)(this, "writePackageJson", void 0);
(0, _defineProperty2().default)(this, "override", void 0);
(0, _defineProperty2().default)(this, "isolated", void 0);
(0, _defineProperty2().default)(this, "origin", void 0);
(0, _defineProperty2().default)(this, "consumer", void 0);
(0, _defineProperty2().default)(this, "bitMap", void 0);
(0, _defineProperty2().default)(this, "writeBitDependencies", void 0);
(0, _defineProperty2().default)(this, "deleteBitDirContent", void 0);
(0, _defineProperty2().default)(this, "existingComponentMap", void 0);
(0, _defineProperty2().default)(this, "excludeRegistryPrefix", void 0);
(0, _defineProperty2().default)(this, "applyExtensionsAddedConfig", void 0);
this.component = component;
this.writeToPath = writeToPath;
this.writeConfig = writeConfig;
this.writePackageJson = writePackageJson;
this.override = override;
this.isolated = isolated;
this.origin = origin;
this.consumer = consumer;
this.bitMap = bitMap;
this.writeBitDependencies = writeBitDependencies;
this.deleteBitDirContent = deleteBitDirContent;
this.existingComponentMap = existingComponentMap;
this.excludeRegistryPrefix = excludeRegistryPrefix;
this.applyExtensionsAddedConfig = applyExtensionsAddedConfig;
}
static getInstance(componentWriterProps) {
return new ComponentWriter(componentWriterProps);
}
/**
* write the component to the filesystem and update .bitmap with the details.
*
* bitMap gets updated before writing the files to the filesystem, because as part of writing the
* package-json file, the componentMap is needed to be stored with the updated version.
*
* when a component is not new, write the files according to the paths in .bitmap.
*/
write() {
var _this = this;
return (0, _bluebird().coroutine)(function* () {
if (!_this.consumer) throw new Error('ComponentWriter.write expect to have a consumer');
yield _this.populateComponentsFilesToWrite(); // $FlowFixMe consumer is set
_this.component.dataToPersist.addBasePath(_this.consumer.getPath());
yield _this.component.dataToPersist.persistAllToFS();
return _this.component;
})();
}
populateComponentsFilesToWrite(packageManager) {
var _this2 = this;
return (0, _bluebird().coroutine)(function* () {
if (!_this2.component.files || !_this2.component.files.length) {
throw new (_showDoctorError().default)(`Component ${_this2.component.id.toString()} is invalid as it has no files`);
}
_this2.component.dataToPersist = new (_dataToPersist().default)();
_this2._updateFilesBasePaths();
_this2.component.componentMap = _this2.existingComponentMap || _this2.addComponentToBitMap(_this2.writeToPath);
_this2._copyFilesIntoDistsWhenDistsOutsideComponentDir();
_this2._determineWhetherToDeleteComponentDirContent();
yield _this2._handlePreviouslyNestedCurrentlyImportedCase();
_this2._determineWhetherToWriteConfig();
_this2._updateComponentRootPathAccordingToBitMap();
_this2._updateBitMapIfNeeded();
yield _this2._updateConsumerConfigIfNeeded();
_this2._determineWhetherToWritePackageJson();
yield _this2.populateFilesToWriteToComponentDir(packageManager);
_this2.populateArtifacts();
return _this2.component;
})();
}
populateFilesToWriteToComponentDir(packageManager) {
var _this3 = this;
return (0, _bluebird().coroutine)(function* () {
if (_this3.deleteBitDirContent) {
_this3.component.dataToPersist.removePath(new (_removePath().default)(_this3.writeToPath));
}
_this3.component.files.forEach(file => file.override = _this3.override);
_this3.component.files.map(file => _this3.component.dataToPersist.addFile(file));
const dists = yield _this3.component.dists.getDistsToWrite(_this3.component, _this3.bitMap, _this3.consumer, false);
if (dists) _this3.component.dataToPersist.merge(dists);
if (_this3.writeConfig && _this3.consumer) {
const configToWrite = yield _this3.component.getConfigToWrite(_this3.consumer, _this3.bitMap);
_this3.component.dataToPersist.merge(configToWrite.dataToPersist);
} // make sure the project's package.json is not overridden by Bit
// If a consumer is of isolated env it's ok to override the root package.json (used by the env installation
// of compilers / testers / extensions)
if (_this3.writePackageJson && (_this3.isolated || _this3.consumer && _this3.consumer.isolated || _this3.writeToPath !== '.')) {
const artifactsDir = _this3.getArtifactsDir();
const {
packageJson,
distPackageJson
} = (0, _packageJsonUtils().preparePackageJsonToWrite)(_this3.bitMap, _this3.component, artifactsDir || _this3.writeToPath, _this3.override, _this3.writeBitDependencies, _this3.excludeRegistryPrefix, packageManager);
const componentConfig = _componentConfig().default.fromComponent(_this3.component); // @todo: temporarily this is running only when there is no version (or version is "latest")
// so then package.json always has a valid version. we'll need to figure out when the version
// needs to be incremented and when it should not.
if ((!_this3.consumer || _this3.consumer.isolated) && !_this3.component.id.hasVersion()) {
// this only needs to be done in an isolated
// or consumerless (dependency in an isolated) environment
packageJson.addOrUpdateProperty('version', _this3._getNextPatchVersion());
}
if (!_this3.consumer || _this3.consumer.isolated) {
// bit-bin should not be installed in the capsule. it'll be symlinked later on.
// see package-manager.linkBitBinInCapsule();
packageJson.removeDependency('bit-bin');
}
componentConfig.setCompiler(_this3.component.compiler ? _this3.component.compiler.toBitJsonObject() : {});
componentConfig.setTester(_this3.component.tester ? _this3.component.tester.toBitJsonObject() : {});
packageJson.addOrUpdateProperty('bit', componentConfig.toPlainObject());
if (_this3.applyExtensionsAddedConfig) {
_this3._mergePackageJsonPropsFromExtensions(packageJson);
}
_this3._mergeChangedPackageJsonProps(packageJson);
_this3._mergePackageJsonPropsFromOverrides(packageJson);
_this3.component.dataToPersist.addFile(packageJson.toVinylFile());
if (distPackageJson) _this3.component.dataToPersist.addFile(distPackageJson.toVinylFile());
_this3.component.packageJsonFile = packageJson;
} // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
if (_this3.component.license && _this3.component.license.contents) {
_this3.component.license.updatePaths({
newBase: _this3.writeToPath
}); // $FlowFixMe this.component.license is set
_this3.component.license.override = _this3.override; // $FlowFixMe this.component.license is set
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
_this3.component.dataToPersist.addFile(_this3.component.license);
}
})();
}
/**
* currently, it writes all artifacts.
* later, this responsibility might move to pkg extension, which could write only artifacts
* that are set in package.json.files[], to have a similar structure of a package.
*/
populateArtifacts() {
const artifactsVinyl = _ramda().default.flatten(this.component.extensions.map(e => e.artifacts));
const artifactsDir = this.getArtifactsDir();
if (artifactsDir) {
artifactsVinyl.forEach(a => a.updatePaths({
newBase: artifactsDir
}));
}
this.component.dataToPersist.addManyFiles(artifactsVinyl);
}
getArtifactsDir() {
if (!this.consumer || this.consumer.isLegacy) return null;
if (this.origin === _constants().COMPONENT_ORIGINS.NESTED) return this.component.writtenPath;
return (0, _componentNodeModulesPath().default)(this.consumer.config._bindingPrefix, this.component.id, true, this.component.defaultScope);
}
addComponentToBitMap(rootDir) {
const filesForBitMap = this.component.files.map(file => {
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
return {
name: file.basename,
relativePath: (0, _path2().pathNormalizeToLinux)(file.relative),
test: file.test
};
});
return this.bitMap.addComponent({
componentId: this.component.id,
files: filesForBitMap,
mainFile: (0, _path2().pathNormalizeToLinux)(this.component.mainFile),
rootDir,
origin: this.origin,
trackDir: this.existingComponentMap && this.existingComponentMap.trackDir,
originallySharedDir: this.component.originallySharedDir,
wrapDir: this.component.wrapDir
});
}
/**
* these changes were added by extensions
*/
_mergePackageJsonPropsFromExtensions(packageJson) {
// The special keys will be merged in other place
const specialKeys = ['extensions', 'dependencies', 'devDependencies', 'peerDependencies'];
if (!this.component.extensionsAddedConfig || _ramda().default.isEmpty(this.component.extensionsAddedConfig)) return;
const valuesToMerge = _ramda().default.omit(specialKeys, this.component.extensionsAddedConfig);
packageJson.mergePackageJsonObject(valuesToMerge);
}
/**
* these changes were entered manually by a user via `overrides` key
*/
_mergePackageJsonPropsFromOverrides(packageJson) {
const valuesToMerge = this.component.overrides.componentOverridesPackageJsonData;
packageJson.mergePackageJsonObject(valuesToMerge);
}
/**
* these are changes done by a compiler
*/
_mergeChangedPackageJsonProps(packageJson) {
if (!this.component.packageJsonChangedProps) return;
const valuesToMerge = this._replaceDistPathTemplateWithCalculatedDistPath(packageJson);
packageJson.mergePackageJsonObject(valuesToMerge);
}
/**
* see https://github.com/teambit/bit/issues/1808 for more info why it's needed
*/
_replaceDistPathTemplateWithCalculatedDistPath(packageJson) {
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
const packageJsonChangedProps = this.component.packageJsonChangedProps;
const isReplaceNeeded = _ramda().default.values(packageJsonChangedProps).some(val => val.includes(_constants().COMPONENT_DIST_PATH_TEMPLATE));
if (!isReplaceNeeded) {
return packageJsonChangedProps;
}
const distRootDir = this.component.dists.getDistDir(this.consumer, this.writeToPath || '.');
const distRelativeToPackageJson = (0, _path2().getPathRelativeRegardlessCWD)(path().dirname(packageJson.filePath), // $FlowFixMe
distRootDir);
return Object.keys(packageJsonChangedProps).reduce((acc, key) => {
const val = packageJsonChangedProps[key].replace(_constants().COMPONENT_DIST_PATH_TEMPLATE, distRelativeToPackageJson);
acc[key] = val;
return acc;
}, {});
}
_copyFilesIntoDistsWhenDistsOutsideComponentDir() {
if (!this.consumer) return; // not relevant when consumer is not available
if (!this.consumer.shouldDistsBeInsideTheComponent() && this.component.dists.isEmpty()) {
// since the dists are set to be outside the components dir, the source files must be saved there
// otherwise, other components in dists won't be able to link to this component
this.component.copyFilesIntoDists();
}
}
_updateComponentRootPathAccordingToBitMap() {
// $FlowFixMe this.component.componentMap is set
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
this.writeToPath = this.component.componentMap.getRootDir();
this.component.writtenPath = this.writeToPath;
this._updateFilesBasePaths();
}
/**
* when there is componentMap, this component (with this version or other version) is already part of the project.
* There are several options as to what was the origin before and what is the origin now and according to this,
* we update/remove/don't-touch the record in bit.map.
* 1) current origin is AUTHORED - If the version is the same as before, don't update bit.map. Otherwise, update.
* 2) current origin is IMPORTED - If the version is the same as before, don't update bit.map. Otherwise, update.
* 3) current origin is NESTED - If it was not NESTED before, don't update.
*/
_updateBitMapIfNeeded() {
if (this.isolated) return;
const componentMapExistWithSameVersion = this.bitMap.isExistWithSameVersion(this.component.id);
if (componentMapExistWithSameVersion) {
if (this.existingComponentMap && // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
this.existingComponentMap !== _constants().COMPONENT_ORIGINS.NESTED && this.origin === _constants().COMPONENT_ORIGINS.NESTED) {
return;
}
this.bitMap.removeComponent(this.component.id);
} // $FlowFixMe this.component.componentMap is set
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
this.component.componentMap = this.addComponentToBitMap(this.component.componentMap.rootDir);
}
_updateConsumerConfigIfNeeded() {
var _this4 = this;
return (0, _bluebird().coroutine)(function* () {
var _this4$component$comp2;
// for authored components there is no bit.json/package.json component specific
// so if the overrides or envs were changed, it should be written to the consumer-config
const areEnvsChanged = /*#__PURE__*/function () {
var _ref = (0, _bluebird().coroutine)(function* () {
var _this4$component, _this4$component$comp;
// $FlowFixMe this.component.componentMap is set
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
const context = {
componentDir: (_this4$component = _this4.component) === null || _this4$component === void 0 ? void 0 : (_this4$component$comp = _this4$component.componentMap) === null || _this4$component$comp === void 0 ? void 0 : _this4$component$comp.getComponentDir()
};
const compilerFromConsumer = _this4.consumer ? yield _this4.consumer.getEnv(_constants().COMPILER_ENV_TYPE, context) : undefined;
const testerFromConsumer = _this4.consumer ? yield _this4.consumer.getEnv(_constants().TESTER_ENV_TYPE, context) : undefined;
const compilerFromComponent = _this4.component.compiler ? _this4.component.compiler.toModelObject() : undefined;
const testerFromComponent = _this4.component.tester ? _this4.component.tester.toModelObject() : undefined;
return _envExtension().default.areEnvsDifferent(compilerFromConsumer ? compilerFromConsumer.toModelObject() : undefined, compilerFromComponent) || _envExtension().default.areEnvsDifferent(testerFromConsumer ? testerFromConsumer.toModelObject() : undefined, testerFromComponent);
});
return function areEnvsChanged() {
return _ref.apply(this, arguments);
};
}();
if (((_this4$component$comp2 = _this4.component.componentMap) === null || _this4$component$comp2 === void 0 ? void 0 : _this4$component$comp2.origin) === _constants().COMPONENT_ORIGINS.AUTHORED && _this4.consumer) {
var _this4$consumer, _this4$consumer$confi, _this4$consumer$confi2;
const envsChanged = yield areEnvsChanged();
(_this4$consumer = _this4.consumer) === null || _this4$consumer === void 0 ? void 0 : (_this4$consumer$confi = _this4$consumer.config) === null || _this4$consumer$confi === void 0 ? void 0 : (_this4$consumer$confi2 = _this4$consumer$confi.componentsConfig) === null || _this4$consumer$confi2 === void 0 ? void 0 : _this4$consumer$confi2.updateOverridesIfChanged(_this4.component, envsChanged);
}
})();
}
_determineWhetherToWriteConfig() {
// $FlowFixMe this.component.componentMap is set
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
if (this.component.componentMap.origin === _constants().COMPONENT_ORIGINS.AUTHORED) {
this.writeConfig = false;
}
}
/**
* don't write the package.json for an authored component, because its dependencies are managed
* by the root package.json
*/
_determineWhetherToWritePackageJson() {
this.writePackageJson = this.writePackageJson && this.origin !== _constants().COMPONENT_ORIGINS.AUTHORED;
}
/**
* when a user imports a component that was a dependency before, write the component directly
* into the components directory for an easy access/change. Then, remove the current record from
* bit.map and add an updated one.
*/
_handlePreviouslyNestedCurrentlyImportedCase() {
var _this5 = this;
return (0, _bluebird().coroutine)(function* () {
if (!_this5.consumer) return; // $FlowFixMe this.component.componentMap is set
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
if (_this5.origin === _constants().COMPONENT_ORIGINS.IMPORTED && _this5.component.componentMap.origin === _constants().COMPONENT_ORIGINS.NESTED) {
yield _this5._cleanOldNestedComponent();
_this5.component.componentMap = _this5.addComponentToBitMap(_this5.writeToPath);
}
})();
}
/**
* For IMPORTED component we have to delete the content of the directory before importing.
* Otherwise, when the author adds new files outside of the previous originallySharedDir and this user imports them
* the environment will contain both copies, the old one with the old originallySharedDir and the new one.
* If a user made changes to the imported component, it will show a warning and stop the process.
*/
_determineWhetherToDeleteComponentDirContent() {
if (typeof this.deleteBitDirContent === 'undefined') {
this.deleteBitDirContent = this.origin === _constants().COMPONENT_ORIGINS.IMPORTED;
}
}
_updateFilesBasePaths() {
const newBase = this.writeToPath || '.';
this.component.files.forEach(file => file.updatePaths({
newBase
}));
if (!this.component.dists.isEmpty()) {
this.component.dists.get().forEach(dist => dist.updatePaths({
newBase
}));
}
}
_cleanOldNestedComponent() {
var _this6 = this;
return (0, _bluebird().coroutine)(function* () {
if (!_this6.consumer) throw new Error('ComponentWriter._cleanOldNestedComponent expect to have a consumer'); // $FlowFixMe this function gets called when it was previously NESTED, so the rootDir is set
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
const oldLocation = path().join(_this6.consumer.getPath(), _this6.component.componentMap.rootDir);
_logger().default.debugAndAddBreadCrumb('component-writer._cleanOldNestedComponent', 'deleting the old directory of a component at {oldLocation}', {
oldLocation
});
yield _fsExtra().default.remove(oldLocation);
yield _this6._removeNodeModulesLinksFromDependents();
_this6.bitMap.removeComponent(_this6.component.id);
})();
}
_removeNodeModulesLinksFromDependents() {
var _this7 = this;
return (0, _bluebird().coroutine)(function* () {
if (!_this7.consumer) {
throw new Error('ComponentWriter._removeNodeModulesLinksFromDependents expect to have a consumer');
}
const directDependentIds = yield _this7.consumer.getAuthoredAndImportedDependentsIdsOf([_this7.component]);
yield Promise.all(directDependentIds.map(dependentId => {
const dependentComponentMap = _this7.consumer ? _this7.consumer.bitMap.getComponent(dependentId) : null;
const relativeLinkPath = _this7.consumer ? (0, _componentNodeModulesPath().default)(_this7.consumer.config._bindingPrefix, _this7.component.id) : null;
const nodeModulesLinkAbs = _this7.consumer && dependentComponentMap && relativeLinkPath ? _this7.consumer.toAbsolutePath(path().join(dependentComponentMap.getRootDir(), relativeLinkPath)) : null;
if (nodeModulesLinkAbs) {
_logger().default.debug(`deleting an obsolete link to node_modules at ${nodeModulesLinkAbs}`);
}
return nodeModulesLinkAbs ? _fsExtra().default.remove(nodeModulesLinkAbs) : Promise.resolve();
}));
})();
}
_getNextPatchVersion() {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return _semver().default.inc(this.component.version, 'prerelease') || '0.0.1-0';
}
}
exports.default = ComponentWriter;