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

498 lines (386 loc) 16.5 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 _ramda() { const data = _interopRequireDefault(require("ramda")); _ramda = function () { return data; }; return data; } function _bitId() { const data = require("../../bit-id"); _bitId = function () { return data; }; return data; } function packageJsonUtils() { const data = _interopRequireWildcard(require("../component/package-json-utils")); packageJsonUtils = function () { return data; }; return data; } function _installPackages() { const data = require("../../npm-client/install-packages"); _installPackages = function () { return data; }; return data; } function _logger() { const data = _interopRequireDefault(require("../../logger/logger")); _logger = function () { return data; }; return data; } function _defaultErrorHandler() { const data = _interopRequireDefault(require("../../cli/default-error-handler")); _defaultErrorHandler = function () { return data; }; return data; } function _scopeRemotes() { const data = require("../../scope/scope-remotes"); _scopeRemotes = function () { return data; }; return data; } function _packageJsonFile() { const data = _interopRequireDefault(require("../component/package-json-file")); _packageJsonFile = function () { return data; }; return data; } function _componentVersion() { const data = _interopRequireDefault(require("../../scope/component-version")); _componentVersion = function () { return data; }; return data; } function _deleteComponentFiles() { const data = _interopRequireDefault(require("./delete-component-files")); _deleteComponentFiles = function () { return data; }; return data; } function _scopeGraph() { const data = _interopRequireDefault(require("../../scope/graph/scope-graph")); _scopeGraph = function () { return data; }; return data; } function _componentIdToPackageName() { const data = _interopRequireDefault(require("../../utils/bit/component-id-to-package-name")); _componentIdToPackageName = function () { return data; }; return data; } /** * @flow * a classic use case of eject is when a user imports a component using `bit import` to update it, * but the user has no intention to have the code as part of the project source code. * the eject provides the option to delete the component locally and install it via the NPM client. * * an implementation note, the entire process is done with rollback in mind. * since installing the component via NPM client is an error prone process, we do it first, before * removing the component files, so then it's easier to rollback. */ class EjectComponents { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! // for rollback in case of errors constructor(consumer, componentsIds, force) { (0, _defineProperty2().default)(this, "consumer", void 0); (0, _defineProperty2().default)(this, "componentsIds", void 0); (0, _defineProperty2().default)(this, "force", void 0); (0, _defineProperty2().default)(this, "componentsToEject", void 0); (0, _defineProperty2().default)(this, "notEjectedDependents", void 0); (0, _defineProperty2().default)(this, "failedComponents", void 0); (0, _defineProperty2().default)(this, "packageJsonFilesBeforeChanges", void 0); this.consumer = consumer; this.componentsIds = componentsIds; this.force = force || false; this.componentsToEject = new (_bitId().BitIds)(); this.failedComponents = { modifiedComponents: new (_bitId().BitIds)(), stagedComponents: new (_bitId().BitIds)(), notExportedComponents: new (_bitId().BitIds)(), selfHostedExportedComponents: new (_bitId().BitIds)() }; } eject() { var _this = this; return (0, _bluebird().coroutine)(function* () { yield _this.decideWhichComponentsToEject(); _logger().default.debugAndAddBreadCrumb('eject-components.eject', `${_this.componentsToEject.length} to eject`); if (_this.componentsToEject.length) { _this._validateIdsHaveScopesAndVersions(); yield _this.findNonEjectedDependents(); yield _this.loadPackageJsonFilesForPotentialRollBack(); yield _this.removeComponentsFromPackageJsonAndNodeModules(); yield _this.addComponentsAsPackagesToPackageJsonFiles(); yield _this.installPackagesUsingNPMClient(); yield _this.removeComponents(); } _logger().default.debug('eject: completed successfully'); return { ejectedComponents: _this.componentsToEject, failedComponents: _this.failedComponents }; })(); } /** * needed for update their package.json later with the dependencies version (instead of the * relative paths) */ findNonEjectedDependents() { var _this2 = this; return (0, _bluebird().coroutine)(function* () { // this also loads all non-nested components into the memory, so retrieving them later is at no cost const graph = yield _scopeGraph().default.buildGraphFromCurrentlyUsedComponents(_this2.consumer); const scopeGraph = new (_scopeGraph().default)(graph); const notEjectedData = []; _this2.componentsToEject.forEach(componentId => { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! const dependents = scopeGraph.getImmediateDependentsPerId(componentId, true); const notEjectedDependents = dependents.filter(d => !_this2.componentsToEject.hasWithoutScopeAndVersion(d)); notEjectedDependents.forEach(dependentId => { const foundInNotEjectedData = notEjectedData.find(d => d.id.isEqual(dependentId)); if (foundInNotEjectedData) foundInNotEjectedData.dependencies.push(componentId);else notEjectedData.push({ id: dependentId, dependencies: [componentId] }); }); }); const notEjectedComponentsDataP = notEjectedData.map( /*#__PURE__*/function () { var _ref = (0, _bluebird().coroutine)(function* (notEjectedItem) { const dependent = yield _this2.consumer.loadComponent(notEjectedItem.id); // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! const { components: ejectedDependencies } = yield _this2.consumer.loadComponents(notEjectedItem.dependencies); return { dependent, ejectedDependencies }; }); return function (_x) { return _ref.apply(this, arguments); }; }()); const notEjectedComponentsData = yield Promise.all(notEjectedComponentsDataP); _this2.notEjectedDependents = notEjectedComponentsData.filter(d => d.dependent.packageJsonFile); })(); } loadPackageJsonFilesForPotentialRollBack() { var _this3 = this; return (0, _bluebird().coroutine)(function* () { const rootPackageJson = yield _packageJsonFile().default.load(_this3.consumer.getPath()); _this3.packageJsonFilesBeforeChanges = [rootPackageJson]; _this3.notEjectedDependents.forEach(({ dependent }) => { // $FlowFixMe notEjectedDependents has only dependents with packageJsonFile // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! _this3.packageJsonFilesBeforeChanges.push(dependent.packageJsonFile.clone()); }); })(); } decideWhichComponentsToEject() { var _this4 = this; return (0, _bluebird().coroutine)(function* () { _logger().default.debug('eject: getting the components status'); if (_ramda().default.isEmpty(_this4.componentsIds)) return; const remotes = yield (0, _scopeRemotes().getScopeRemotes)(_this4.consumer.scope); const hubExportedComponents = new (_bitId().BitIds)(); _this4.componentsIds.forEach(bitId => { if (!bitId.hasScope()) _this4.failedComponents.notExportedComponents.push(bitId);else if (remotes.isHub(bitId.scope)) hubExportedComponents.push(bitId);else _this4.failedComponents.selfHostedExportedComponents.push(bitId); }); if (_this4.force) { _this4.componentsToEject = hubExportedComponents; } else { yield Promise.all(hubExportedComponents.map( /*#__PURE__*/function () { var _ref2 = (0, _bluebird().coroutine)(function* (id) { try { const componentStatus = yield _this4.consumer.getComponentStatusById(id); if (componentStatus.modified) _this4.failedComponents.modifiedComponents.push(id);else if (componentStatus.staged) _this4.failedComponents.stagedComponents.push(id);else _this4.componentsToEject.push(id); } catch (err) { _this4.throwEjectError(`eject operation failed getting the status of ${id.toString()}, no action has been done. please fix the issue to continue.`, err); } }); return function (_x2) { return _ref2.apply(this, arguments); }; }())); } })(); } removeComponentsFromPackageJsonAndNodeModules() { var _this5 = this; return (0, _bluebird().coroutine)(function* () { const action = 'removing the existing components from package.json and node_modules'; try { _logger().default.debugAndAddBreadCrumb('eject', action); yield packageJsonUtils().removeComponentsFromWorkspacesAndDependencies(_this5.consumer, _this5.componentsToEject); } catch (err) { _logger().default.warn(`eject: failed ${action}, restoring package.json`); yield _this5.rollBack(action); _this5.throwEjectError(_this5._buildExceptionMessageWithRollbackData(action), err); } })(); } addComponentsAsPackagesToPackageJsonFiles() { var _this6 = this; return (0, _bluebird().coroutine)(function* () { const action = 'adding the components as packages into package.json'; try { _logger().default.debugAndAddBreadCrumb('eject', action); const componentsVersions = yield Promise.all(_this6.componentsToEject.map( /*#__PURE__*/function () { var _ref3 = (0, _bluebird().coroutine)(function* (bitId) { const modelComponent = yield _this6.consumer.scope.getModelComponent(bitId); // $FlowFixMe componentsToEject has scope and version, see @_validateIdsHaveScopesAndVersions // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! return new (_componentVersion().default)(modelComponent, bitId.version); }); return function (_x3) { return _ref3.apply(this, arguments); }; }())); yield packageJsonUtils().addComponentsWithVersionToRoot(_this6.consumer, componentsVersions); _this6.notEjectedDependents.forEach(({ dependent, ejectedDependencies }) => { const dependenciesToReplace = ejectedDependencies.reduce((acc, dependency) => { const packageName = (0, _componentIdToPackageName().default)(dependency.id, dependency.bindingPrefix); acc[packageName] = dependency.version; return acc; }, {}); // $FlowFixMe notEjectedDependents has only dependents with packageJsonFile // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! dependent.packageJsonFile.replaceDependencies(dependenciesToReplace); }); // $FlowFixMe notEjectedDependents has only dependents with packageJsonFile // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! yield Promise.all(_this6.notEjectedDependents.map(({ dependent }) => dependent.packageJsonFile.write())); } catch (err) { _logger().default.error(`eject: failed ${action}, restoring package.json`, err); yield _this6.rollBack(action); _this6.throwEjectError(_this6._buildExceptionMessageWithRollbackData(action), err); } })(); } installPackagesUsingNPMClient() { var _this7 = this; return (0, _bluebird().coroutine)(function* () { const action = 'installing the components using the NPM client'; try { _logger().default.debugAndAddBreadCrumb('eject', action); // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! const dirs = _this7.notEjectedDependents // $FlowFixMe componentMap must be set for authored and imported // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! .map(({ dependent }) => dependent.componentMap.rootDir).filter(x => x); yield (0, _installPackages().installPackages)(_this7.consumer, dirs, true, true); } catch (err) { yield _this7.rollBack(action); _this7.throwEjectError(_this7._buildExceptionMessageWithRollbackData(action), err); } })(); } rollBack(action) { var _this8 = this; return (0, _bluebird().coroutine)(function* () { yield Promise.all(_this8.packageJsonFilesBeforeChanges.map( /*#__PURE__*/function () { var _ref4 = (0, _bluebird().coroutine)(function* (packageJsonFile) { if (packageJsonFile.fileExist) { _logger().default.warn(`eject: failed ${action}, restoring package.json at ${packageJsonFile.filePath}`); yield packageJsonFile.write(); } else { _logger().default.warn(`eject: failed ${action}, no package.json to restore at ${packageJsonFile.filePath}`); } }); return function (_x4) { return _ref4.apply(this, arguments); }; }())); })(); } _buildExceptionMessageWithRollbackData(action) { return `eject failed ${action}. your package.json (if existed) has been restored, however, some bit generated data may have been deleted, please run "bit link" to restore them.`; } removeComponents() { var _this9 = this; return (0, _bluebird().coroutine)(function* () { try { _logger().default.debug('eject: removing the components files from the filesystem'); yield _this9.removeLocalComponents(); } catch (err) { _this9.throwEjectError(`eject operation has installed your components successfully using the NPM client. however, it failed removing the old components from the filesystem. please use bit remove command to remove them.`, err); } })(); } /** * as part of the 'eject' operation, a component is removed locally. as opposed to the remove * command, in this case, no need to remove the objects from the scope, only remove from the * filesystem, which means, delete the component files, untrack from .bitmap and clean * package.json and bit.json traces. */ removeLocalComponents() { var _this10 = this; return (0, _bluebird().coroutine)(function* () { // @todo: what about the dependencies? if they are getting deleted we have to make sure they // not used anywhere else. Because this is part of the eject operation, the user probably // gets the dependencies as npm packages so we don't need to worry much about have extra // dependencies on the filesystem yield (0, _deleteComponentFiles().default)(_this10.consumer, _this10.componentsToEject, true); yield _this10.consumer.cleanFromBitMap(_this10.componentsToEject, new (_bitId().BitIds)()); })(); } throwEjectError(message, originalError) { const { message: originalErrorMessage } = (0, _defaultErrorHandler().default)(originalError); _logger().default.error(`eject has stopped due to an error ${originalErrorMessage}`, originalError); throw new Error(`${message} got the following error: ${originalErrorMessage}`); } _validateIdsHaveScopesAndVersions() { this.componentsToEject.forEach(id => { if (!id.hasScope() || !id.hasVersion()) { throw new TypeError(`EjectComponents expects ids with scope and version, got ${id.toString()}`); } }); } } exports.default = EjectComponents;