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
620 lines (468 loc) • 21.7 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 _ramda() {
const data = _interopRequireDefault(require("ramda"));
_ramda = function () {
return data;
};
return data;
}
function _glob() {
const data = _interopRequireDefault(require("glob"));
_glob = function () {
return data;
};
return data;
}
function _constants() {
const data = require("../constants");
_constants = function () {
return data;
};
return data;
}
function _logger() {
const data = _interopRequireDefault(require("../logger/logger"));
_logger = function () {
return data;
};
return data;
}
function _utils() {
const data = require("../utils");
_utils = function () {
return data;
};
return data;
}
function _linkGenerator() {
const data = require("./link-generator");
_linkGenerator = function () {
return data;
};
return data;
}
function _linkContent() {
const data = require("./link-content");
_linkContent = function () {
return data;
};
return data;
}
function _componentNodeModulesPath() {
const data = _interopRequireDefault(require("../utils/bit/component-node-modules-path"));
_componentNodeModulesPath = function () {
return data;
};
return data;
}
function _symlink() {
const data = _interopRequireDefault(require("./symlink"));
_symlink = function () {
return data;
};
return data;
}
function _dataToPersist() {
const data = _interopRequireDefault(require("../consumer/component/sources/data-to-persist"));
_dataToPersist = function () {
return data;
};
return data;
}
function _linkFile() {
const data = _interopRequireDefault(require("./link-file"));
_linkFile = function () {
return data;
};
return data;
}
function _componentsList() {
const data = _interopRequireDefault(require("../consumer/component/components-list"));
_componentsList = function () {
return data;
};
return data;
}
function _packageJsonFile() {
const data = _interopRequireDefault(require("../consumer/component/package-json-file"));
_packageJsonFile = function () {
return data;
};
return data;
}
function _path2() {
const data = require("../utils/path");
_path2 = function () {
return data;
};
return data;
}
function _removePath() {
const data = _interopRequireDefault(require("../consumer/component/sources/remove-path"));
_removePath = function () {
return data;
};
return data;
}
/**
* link given components to node_modules, so it's possible to use absolute link instead of relative
* for example, require('@bit/remote-scope.bar.foo)
*/
class NodeModuleLinker {
// preparation for the capsule, which is going to have only BitMap with no Consumer
constructor(components, consumer, bitMap) {
(0, _defineProperty2().default)(this, "components", void 0);
(0, _defineProperty2().default)(this, "consumer", void 0);
(0, _defineProperty2().default)(this, "bitMap", void 0);
(0, _defineProperty2().default)(this, "dataToPersist", void 0);
this.components = _componentsList().default.getUniqueComponents(components);
this.consumer = consumer;
this.bitMap = bitMap;
this.dataToPersist = new (_dataToPersist().default)();
}
link() {
var _this = this;
return (0, _bluebird().coroutine)(function* () {
const links = yield _this.getLinks();
const linksResults = _this.getLinksResults();
if (_this.consumer) links.addBasePath(_this.consumer.getPath());
yield links.persistAllToFS();
return linksResults;
})();
}
getLinks() {
var _this2 = this;
return (0, _bluebird().coroutine)(function* () {
_this2.dataToPersist = new (_dataToPersist().default)();
yield _this2._populateShouldDependenciesSavedAsComponentsData();
yield Promise.all(_this2.components.map(component => {
const componentId = component.id.toString();
_logger().default.debug(`linking component to node_modules: ${componentId}`);
const componentMap = _this2.bitMap.getComponent(component.id);
component.componentMap = componentMap;
switch (componentMap.origin) {
case _constants().COMPONENT_ORIGINS.IMPORTED:
return _this2._populateImportedComponentsLinks(component);
case _constants().COMPONENT_ORIGINS.NESTED:
return _this2._populateNestedComponentsLinks(component);
case _constants().COMPONENT_ORIGINS.AUTHORED:
return _this2._populateAuthoredComponentsLinks(component);
default:
throw new Error(`ComponentMap.origin ${componentMap.origin} of ${componentId} is not recognized`);
}
}));
return _this2.dataToPersist;
})();
}
getLinksResults() {
const linksResults = [];
const getExistingLinkResult = id => linksResults.find(linkResult => linkResult.id.isEqual(id));
const addLinkResult = (id, from, to) => {
if (!id) return;
const existingLinkResult = getExistingLinkResult(id);
if (existingLinkResult) {
existingLinkResult.bound.push({
from,
to
});
} else {
linksResults.push({
id,
bound: [{
from,
to
}]
});
}
};
this.dataToPersist.symlinks.forEach(symlink => {
addLinkResult(symlink.componentId, symlink.src, symlink.dest);
}); // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
this.dataToPersist.files.forEach(file => {
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
addLinkResult(file.componentId, file.srcPath, file.path);
});
this.components.forEach(component => {
const existingLinkResult = getExistingLinkResult(component.id);
if (!existingLinkResult) {
linksResults.push({
id: component.id,
bound: []
});
}
});
return linksResults;
}
_populateImportedComponentsLinks(component) {
var _this3 = this;
return (0, _bluebird().coroutine)(function* () {
if (_this3.consumer && !_this3.consumer.isLegacy) {
yield _this3._populateImportedNonLegacyComponentsLinks(component);
return;
}
const componentMap = component.componentMap;
const componentId = component.id; // @todo: this should probably be `const bindingPrefix = component.bindingPrefix;`
const bindingPrefix = component.bindingPrefix || _constants().DEFAULT_BINDINGS_PREFIX;
const linkPath = (0, _componentNodeModulesPath().default)(bindingPrefix, componentId, true, _this3._getDefaultScope(component)); // when a user moves the component directory, use component.writtenPath to find the correct target
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
const srcTarget = component.writtenPath || componentMap.rootDir;
const shouldDistsBeInsideTheComponent = _this3.consumer ? _this3.consumer.shouldDistsBeInsideTheComponent() : true;
if (_this3.consumer && !component.dists.isEmpty() && component.dists.writeDistsFiles && !shouldDistsBeInsideTheComponent) {
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
const distTarget = component.dists.getDistDir(_this3.consumer, componentMap.getRootDir());
const packagesSymlinks = _this3._getSymlinkPackages(srcTarget, distTarget, component);
_this3.dataToPersist.addManySymlinks(packagesSymlinks);
const distSymlink = _symlink().default.makeInstance(distTarget, linkPath, componentId);
distSymlink.forDistOutsideComponentsDir = true;
_this3.dataToPersist.addSymlink(distSymlink);
} else if (srcTarget !== '.') {
// avoid creating symlinks from node_modules to itself
_this3.dataToPersist.addSymlink(_symlink().default.makeInstance(srcTarget, linkPath, componentId));
}
yield _this3._populateDependenciesAndMissingLinks(component);
})();
}
_populateNestedComponentsLinks(component) {
var _this4 = this;
return (0, _bluebird().coroutine)(function* () {
yield _this4._populateDependenciesAndMissingLinks(component);
})();
}
_getDefaultScope(component) {
if (component) {
return component.defaultScope;
}
return this.consumer ? this.consumer.config.defaultScope : null;
}
/**
* for Harmony version and above, instead of symlink from the component dir to node_modules,
* create a directory on node_modules and symlink each one of the source files, this way, the
* structure is exactly the same as Authored
*/
_populateImportedNonLegacyComponentsLinks(component) {
var _this5 = this;
return (0, _bluebird().coroutine)(function* () {
const componentId = component.id;
const linkPath = (0, _componentNodeModulesPath().default)(component.bindingPrefix, componentId, true, _this5._getDefaultScope(component));
const componentMap = component.componentMap;
const filesToBind = componentMap.getAllFilesPaths();
filesToBind.forEach(file => {
const fileWithRootDir = componentMap.hasRootDir() ? path().join(componentMap.rootDir, file) : file;
const dest = path().join(linkPath, file);
_this5.dataToPersist.addSymlink(_symlink().default.makeInstance(fileWithRootDir, dest, componentId));
});
yield _this5._populateDependenciesAndMissingLinks(component);
})();
}
/**
* even when an authored component has rootDir, we can't just symlink that rootDir to
* node_modules/rootDir. it could work only when the main-file is index.js, not for other cases.
* node expects the module inside node_modules to have either package.json with valid "main"
* property or an index.js file. this main property can't be relative.
*/
_populateAuthoredComponentsLinks(component) {
const componentId = component.id;
const linkPath = (0, _componentNodeModulesPath().default)(component.bindingPrefix, componentId, true, this._getDefaultScope(component));
const componentMap = component.componentMap;
const filesToBind = componentMap.getAllFilesPaths(); // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
component.dists.updateDistsPerWorkspaceConfig(component.id, this.consumer, component.componentMap);
filesToBind.forEach(file => {
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
const isMain = file === componentMap.mainFile;
const fileWithRootDir = componentMap.hasRootDir() ? path().join(componentMap.rootDir, file) : file;
const possiblyDist = component.dists.calculateDistFileForAuthored(path().normalize(fileWithRootDir), // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
this.consumer, isMain);
const dest = path().join(linkPath, file);
const destRelative = (0, _path2().getPathRelativeRegardlessCWD)(path().dirname(dest), possiblyDist);
const fileContent = (0, _linkContent().getLinkToFileContent)(destRelative); // if component.compiler is set, this component is working with the old compiler (< v15)
// and not with compile extension. as such, the dists for author are in the root, not in the
// component directory. Having symlinks here instead of links causes import of module-paths to
// break. try e2e-test: 'as author, move individual component files to dedicated directory with bit move --component'
if (fileContent && component.compiler) {
const linkFile = _linkFile().default.load({
filePath: dest,
content: fileContent,
srcPath: file,
componentId,
override: true,
ignorePreviousSymlink: true // in case the component didn't have a compiler before, this file was a symlink
}); // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
this.dataToPersist.addFile(linkFile);
} else {
// it's an un-supported file, or it's Harmony version and above, create a symlink instead
this.dataToPersist.addSymlink(_symlink().default.makeInstance(fileWithRootDir, dest, componentId));
}
});
this._deleteOldLinksOfIdWithoutScope(component);
this._createPackageJsonForAuthor(component);
}
/**
* for AUTHORED components, when a component is new, upon build, we generate links on
* node_modules. The path doesn't have the scope-name as it doesn't exist yet. (e.g. @bit/foo).
* Later on, when the component is exported and has a scope-name, the path is complete.
* (e.g. @bit/scope.foo). At this stage, this function deletes the old-partial paths.
*/
_deleteOldLinksOfIdWithoutScope(component) {
if (component.id.scope) {
const previousDest = (0, _componentNodeModulesPath().default)(component.bindingPrefix, component.id.changeScope(null), true, this._getDefaultScope(component));
this.dataToPersist.removePath(new (_removePath().default)(previousDest));
}
}
/**
* for IMPORTED and NESTED components
*/
_populateDependenciesAndMissingLinks(component) {
var _this6 = this;
return (0, _bluebird().coroutine)(function* () {
// $FlowFixMe loaded from FS, componentMap must be set
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
const componentMap = component.componentMap;
if (component.issues && ( // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
component.issues.missingLinks || component.issues.missingCustomModuleResolutionLinks) && _this6.consumer && component.componentFromModel) {
const componentWithDependencies = yield component.toComponentWithDependencies(_this6.consumer);
component.copyAllDependenciesFromModel();
const componentsDependenciesLinks = (0, _linkGenerator().getComponentsDependenciesLinks)([componentWithDependencies], _this6.consumer, false, _this6.bitMap);
_this6.dataToPersist.addManyFiles(componentsDependenciesLinks.files);
_this6.dataToPersist.addManySymlinks(componentsDependenciesLinks.symlinks);
}
if (component.hasDependencies()) {
const dependenciesLinks = yield _this6._getDependenciesLinks(component, componentMap);
_this6.dataToPersist.addManySymlinks(dependenciesLinks);
}
})();
}
/**
* When the dists is outside the components directory, it doesn't have access to the node_modules of the component's
* root-dir. The solution is to go through the node_modules packages one by one and symlink them.
*/
_getSymlinkPackages(from, to, component) {
if (!this.consumer) throw new Error('getSymlinkPackages expects the Consumer to be defined');
const dependenciesSavedAsComponents = component.dependenciesSavedAsComponents;
const fromNodeModules = path().join(from, 'node_modules');
const toNodeModules = path().join(to, 'node_modules');
_logger().default.debug(`symlinkPackages for dists outside the component directory from ${fromNodeModules} to ${toNodeModules}`);
const unfilteredDirs = _glob().default.sync('*', {
cwd: fromNodeModules
}); // when dependenciesSavedAsComponents the node_modules/@bit has real link files, we don't want to touch them
// otherwise, node_modules/@bit has packages as any other directory in node_modules
const dirsToFilter = dependenciesSavedAsComponents ? [this.consumer.config._bindingPrefix] : [];
const customResolvedData = component.dependencies.getCustomResolvedData();
if (!_ramda().default.isEmpty(customResolvedData)) {
// filter out packages that are actually symlinks to dependencies
Object.keys(customResolvedData).forEach(importSource => dirsToFilter.push((0, _utils().first)(importSource.split('/'))));
}
const dirs = dirsToFilter.length ? unfilteredDirs.filter(dir => !dirsToFilter.includes(dir)) : unfilteredDirs;
if (!dirs.length) return [];
return dirs.map(dir => {
const fromDir = path().join(fromNodeModules, dir);
const toDir = path().join(toNodeModules, dir);
return _symlink().default.makeInstance(fromDir, toDir);
});
}
_getDependenciesLinks(component, componentMap) {
var _this7 = this;
return (0, _bluebird().coroutine)(function* () {
const getSymlinks = /*#__PURE__*/function () {
var _ref = (0, _bluebird().coroutine)(function* (dependency) {
var _this7$consumer;
const dependencyComponentMap = _this7.bitMap.getComponentIfExist(dependency.id);
const depModel = yield ((_this7$consumer = _this7.consumer) === null || _this7$consumer === void 0 ? void 0 : _this7$consumer.scope.getModelComponentIfExist(dependency.id)) || Promise.resolve();
const bindingPrefix = depModel ? depModel.bindingPrefix : _constants().DEFAULT_BINDINGS_PREFIX;
const dependenciesLinks = [];
if (!dependencyComponentMap || !dependencyComponentMap.hasRootDir()) return dependenciesLinks;
const parentRootDir = componentMap.getRootDir();
const dependencyRootDir = dependencyComponentMap.getRootDir();
dependenciesLinks.push(_this7._getDependencyLink(parentRootDir, dependency.id, dependencyRootDir, bindingPrefix));
if (_this7.consumer && !_this7.consumer.shouldDistsBeInsideTheComponent()) {
// when dists are written outside the component, it doesn't matter whether a component
// has dists files or not, in case it doesn't have, the files are copied from the component
// dir into the dist dir. (see consumer-component.write())
const from = component.dists.getDistDirForConsumer(_this7.consumer, parentRootDir);
const to = component.dists.getDistDirForConsumer(_this7.consumer, dependencyRootDir);
const distSymlink = _this7._getDependencyLink(from, dependency.id, to, bindingPrefix);
distSymlink.forDistOutsideComponentsDir = true;
dependenciesLinks.push(distSymlink);
}
return dependenciesLinks;
});
return function getSymlinks(_x) {
return _ref.apply(this, arguments);
};
}();
const symlinksP = component.getAllDependencies().map(dependency => getSymlinks(dependency));
const symlinks = yield Promise.all(symlinksP);
return _ramda().default.flatten(symlinks);
})();
}
_getDependencyLink(parentRootDir, bitId, rootDir, bindingPrefix) {
const relativeDestPath = (0, _componentNodeModulesPath().default)(bindingPrefix, bitId, true, this._getDefaultScope());
const destPathInsideParent = path().join(parentRootDir, relativeDestPath);
return _symlink().default.makeInstance(rootDir, destPathInsideParent, bitId);
}
/**
* create package.json on node_modules/@bit/component-name/package.json with a property 'main'
* pointing to the component's main file.
* It is needed for Authored components only.
* Since an authored component doesn't have rootDir, it's impossible to symlink to the component directory.
* It makes it easier for Author to use absolute syntax between their own components.
*/
_createPackageJsonForAuthor(component) {
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
const hasPackageJsonAsComponentFile = component.files.some(file => file.relative === _constants().PACKAGE_JSON);
if (hasPackageJsonAsComponentFile) return; // don't generate package.json on top of the user package.json
const dest = path().join((0, _componentNodeModulesPath().default)(component.bindingPrefix, component.id, true, this._getDefaultScope(component)));
const packageJson = _packageJsonFile().default.createFromComponent(dest, component);
this.dataToPersist.addFile(packageJson.toVinylFile());
}
/**
* links are normally generated by `bit import`, `bit link` and `bit install`.
* for `bit import` the data about whether dependenciesSavedAsComponents is already populated
* for the rest, it's not.
* @todo: avoid repopulating for imported. (not easy because by default, all components get "true").
*/
_populateShouldDependenciesSavedAsComponentsData() {
var _this8 = this;
return (0, _bluebird().coroutine)(function* () {
if (!_this8.components.length || !_this8.consumer) return;
const bitIds = _this8.components.map(c => c.id);
const shouldDependenciesSavedAsComponents = yield _this8.consumer.shouldDependenciesSavedAsComponents(bitIds);
_this8.components.forEach(component => {
const shouldSavedAsComponents = shouldDependenciesSavedAsComponents.find(c => c.id.isEqual(component.id));
if (!shouldSavedAsComponents) {
throw new Error(`_populateShouldDependenciesSavedAsComponentsData, saveDependenciesAsComponents is missing for ${component.id.toString()}`);
}
component.dependenciesSavedAsComponents = shouldSavedAsComponents.saveDependenciesAsComponents;
});
})();
}
}
exports.default = NodeModuleLinker;