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

1,212 lines (965 loc) 52.8 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 _ramda() { const data = _interopRequireDefault(require("ramda")); _ramda = function () { return data; }; return data; } function RA() { const data = _interopRequireWildcard(require("ramda-adjunct")); RA = function () { return data; }; return data; } function _constants() { const data = require("../../../../constants"); _constants = 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 _logger() { const data = _interopRequireDefault(require("../../../../logger/logger")); _logger = function () { return data; }; return data; } function _dependencies() { const data = _interopRequireDefault(require("../dependencies")); _dependencies = function () { return data; }; return data; } function _generalError() { const data = _interopRequireDefault(require("../../../../error/general-error")); _generalError = function () { return data; }; return data; } function _linkContent() { const data = require("../../../../links/link-content"); _linkContent = function () { return data; }; return data; } function _overridesDependencies() { const data = _interopRequireDefault(require("./overrides-dependencies")); _overridesDependencies = function () { return data; }; return data; } function _showDoctorError() { const data = _interopRequireDefault(require("../../../../error/show-doctor-error")); _showDoctorError = function () { return data; }; return data; } function _packageJsonFile() { const data = _interopRequireDefault(require("../../package-json-file")); _packageJsonFile = function () { return data; }; return data; } function _incorrectRootDir() { const data = _interopRequireDefault(require("../../exceptions/incorrect-root-dir")); _incorrectRootDir = function () { return data; }; return data; } function _filesDependencyBuilder() { const data = require("../files-dependency-builder"); _filesDependencyBuilder = function () { return data; }; return data; } function _packageNameToComponentId() { const data = require("../../../../utils/bit/package-name-to-component-id"); _packageNameToComponentId = function () { return data; }; return data; } class DependencyResolver { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! constructor(component, consumer, componentId) { (0, _defineProperty2().default)(this, "component", void 0); (0, _defineProperty2().default)(this, "consumer", void 0); (0, _defineProperty2().default)(this, "componentId", void 0); (0, _defineProperty2().default)(this, "componentMap", void 0); (0, _defineProperty2().default)(this, "componentFromModel", void 0); (0, _defineProperty2().default)(this, "extensionsAddedConfig", void 0); (0, _defineProperty2().default)(this, "consumerPath", void 0); (0, _defineProperty2().default)(this, "tree", void 0); (0, _defineProperty2().default)(this, "allDependencies", void 0); (0, _defineProperty2().default)(this, "allPackagesDependencies", void 0); (0, _defineProperty2().default)(this, "issues", void 0); (0, _defineProperty2().default)(this, "processedFiles", void 0); (0, _defineProperty2().default)(this, "compilerFiles", void 0); (0, _defineProperty2().default)(this, "testerFiles", void 0); (0, _defineProperty2().default)(this, "overridesDependencies", void 0); this.component = component; this.consumer = consumer; this.componentId = componentId; // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! this.componentMap = this.component.componentMap; // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! this.componentFromModel = this.component.componentFromModel; this.extensionsAddedConfig = this.component.extensionsAddedConfig; this.consumerPath = this.consumer.getPath(); this.allDependencies = { dependencies: [], devDependencies: [] }; this.allPackagesDependencies = { packageDependencies: {}, devPackageDependencies: {}, compilerPackageDependencies: {}, testerPackageDependencies: {}, peerPackageDependencies: {} }; this.processedFiles = []; // later on, empty issues are removed. see `this.removeEmptyIssues();` this.issues = { missingPackagesDependenciesOnFs: {}, missingPackagesDependenciesFromOverrides: [], missingComponents: {}, untrackedDependencies: {}, missingDependenciesOnFs: {}, missingLinks: {}, missingCustomModuleResolutionLinks: {}, relativeComponents: {}, relativeComponentsAuthored: {}, parseErrors: {}, resolveErrors: {}, missingBits: {} // temporarily, will be combined with missingComponents. see combineIssues }; this.overridesDependencies = new (_overridesDependencies().default)(component, consumer); } setTree(tree) { this.tree = tree; // console.log(JSON.stringify(tree, null, 4)); // uncomment to easily watch the tree received from bit-javascript } /** * Load components and packages dependencies for a component. The process is as follows: * 1) Use the language driver to parse the component files and find for each file its dependencies. * 2) The results we get from the driver per file tells us what are the files and packages that depend on our file. * and also whether there are missing packages and files. * 3) Using the information from the driver, we go over each one of the dependencies files and find its counterpart * component. The way how we find it, is by using the bit.map file which has a mapping between the component name and * the file paths. * 4) If we find a component to the file dependency, we add it to component.dependencies. Otherwise, it's added to * component.issues.untrackedDependencies * 5) Similarly, when we find the packages dependencies, they are added to component.packageDependencies. Otherwise, * they're added to component.issues.missingPackagesDependenciesOnFs * 6) In case the driver found a file dependency that is not on the file-system, we add that file to * component.issues.missingDependenciesOnFs */ loadDependenciesForComponent(bitDir, cacheResolvedDependencies, cacheProjectAst) { var _this = this; return (0, _bluebird().coroutine)(function* () { const { nonTestsFiles, testsFiles } = _this.componentMap.getFilesGroupedByBeingTests(); const allFiles = [...nonTestsFiles, ...testsFiles]; // find the dependencies (internal files and packages) through automatic dependency resolution const dependenciesTree = yield (0, _filesDependencyBuilder().getDependencyTree)({ baseDir: bitDir, workspacePath: _this.consumerPath, filePaths: allFiles, bindingPrefix: _this.component.bindingPrefix, resolveModulesConfig: _this.consumer.config._resolveModules, visited: cacheResolvedDependencies, cacheProjectAst }); // we have the files dependencies, these files should be components that are registered in bit.map. Otherwise, // they are referred as "untracked components" and the user should add them later on in order to tag _this.setTree(dependenciesTree.tree); _this.populateDependencies(allFiles, testsFiles); _this.component.setDependencies(_this.allDependencies.dependencies); _this.component.setDevDependencies(_this.allDependencies.devDependencies); _this.component.packageDependencies = _this.allPackagesDependencies.packageDependencies; _this.component.devPackageDependencies = _this.allPackagesDependencies.devPackageDependencies; _this.component.peerPackageDependencies = _this.allPackagesDependencies.peerPackageDependencies; if (!_ramda().default.isEmpty(_this.overridesDependencies.missingPackageDependencies)) { _this.issues.missingPackagesDependenciesFromOverrides = _this.overridesDependencies.missingPackageDependencies; } if (!_ramda().default.isEmpty(_this.issues)) _this.component.issues = _this.issues; _this.component.manuallyRemovedDependencies = _this.overridesDependencies.manuallyRemovedDependencies; _this.component.manuallyAddedDependencies = _this.overridesDependencies.manuallyAddedDependencies; _this.throwForIncorrectRootDir(); return _this.component; })(); } /** * Given the tree of file dependencies from the driver, find the components of these files. * Each dependency file has a path, use bit.map to search for the component name by that path. * If the component is found, add it to "this.allDependencies.dependencies". Otherwise, add it to "this.issues.untrackedDependencies". * * For the found components, add their sourceRelativePath and destinationRelativePath, they are being used for * generating links upon import: * sourceRelativePath - location of the link file. * destinationRelativePath - destination written inside the link file. * * When a dependency is found in a regular (implementation) file, it goes to `dependencies`. If * it found on a test file, it goes to `devDependencies`. * Similarly, when a package is found in a regular file, it goes to `packageDependencies`. When * if found in a test file, it goes to `devPackageDependencies`. * An exception for the above is when a package is required in a regular or test file but is also * mentioned in the `package.json` file as a peerDependency, in that case, the package is added * to `peerPackageDependencies` and removed from other places. Unless this package is overridden * and marked as ignored in the consumer or component config file. */ populateDependencies(files, testsFiles) { files.forEach(file => { const fileType = { isTestFile: _ramda().default.contains(file, testsFiles) }; this.throwForNonExistFile(file); if (this.overridesDependencies.shouldIgnoreFile(file, fileType)) { return; } this.processMissing(file, fileType); this.processErrors(file); this.processPackages(file, fileType); this.processBits(file, fileType); this.processDepFiles(file, fileType); this.processUnidentifiedPackages(file, fileType); }); this.removeIgnoredPackagesByOverrides(); this.removeDevAndEnvDepsIfTheyAlsoRegulars(); this.combineIssues(); this.removeEmptyIssues(); this.populatePeerPackageDependencies(); this.manuallyAddDependencies(); this.applyOverridesOnEnvPackages(); } removeIgnoredPackagesByOverrides() { const shouldBeIncluded = (pkgVersion, pkgName) => !this.overridesDependencies.shouldIgnorePackageByType(pkgName, 'dependencies'); const shouldBeIncludedDev = (pkgVersion, pkgName) => !this.overridesDependencies.shouldIgnorePackageByType(pkgName, 'devDependencies'); this.allPackagesDependencies.packageDependencies = _ramda().default.pickBy(shouldBeIncluded, this.allPackagesDependencies.packageDependencies); this.allPackagesDependencies.devPackageDependencies = _ramda().default.pickBy(shouldBeIncludedDev, this.allPackagesDependencies.devPackageDependencies); this.allPackagesDependencies.compilerPackageDependencies = _ramda().default.pickBy(shouldBeIncludedDev, this.allPackagesDependencies.compilerPackageDependencies); this.allPackagesDependencies.testerPackageDependencies = _ramda().default.pickBy(shouldBeIncludedDev, this.allPackagesDependencies.testerPackageDependencies); } throwForNonExistFile(file) { if (!this.tree[file]) { throw new Error(`DependencyResolver: a file "${file}" was not returned from the driver, its dependencies are unknown`); } } throwForIncorrectRootDir() { const relatives = this.issues.relativeComponentsAuthored; if (relatives && !_ramda().default.isEmpty(relatives) && this.componentMap.doesAuthorHaveRootDir()) { const firstRelativeKey = Object.keys(relatives)[0]; throw new (_incorrectRootDir().default)(this.componentId.toString(), relatives[firstRelativeKey][0].importSource); } } manuallyAddDependencies() { const packageJson = this._getPackageJson(); const dependencies = this.overridesDependencies.getDependenciesToAddManually(packageJson, this.allDependencies); if (!dependencies) return; const { components, packages } = dependencies; _constants().DEPENDENCIES_FIELDS.forEach(depField => { if (components[depField] && components[depField].length) { components[depField].forEach(id => this.allDependencies[depField].push({ id, relativePaths: [] })); } if (packages[depField] && !_ramda().default.isEmpty(packages[depField])) { Object.assign(this.allPackagesDependencies[this._pkgFieldMapping(depField)], packages[depField]); } }); } applyOverridesOnEnvPackages() { [this.component.compilerPackageDependencies, this.component.testerPackageDependencies].forEach(packages => { _constants().DEPENDENCIES_FIELDS.forEach(fieldType => { if (!packages[fieldType]) return; const shouldBeIncluded = (pkgVersion, pkgName) => { return !this.overridesDependencies.shouldIgnoreComponentByStr(pkgName, fieldType) && !this.overridesDependencies.shouldIgnorePackageByType(pkgName, fieldType); }; packages[fieldType] = _ramda().default.pickBy(shouldBeIncluded, packages[fieldType]); }); }); } traverseTreeForComponentId(depFile) { if (!this.tree[depFile] || !this.tree[depFile].files && !this.tree[depFile].bits) return undefined; if (!this.componentMap.rootDir) { throw Error('traverseTreeForComponentId should get called only when rootDir is set'); } const rootDirFullPath = path().join(this.consumerPath, this.componentMap.rootDir); const files = this.tree[depFile].files || []; if (files && !_ramda().default.isEmpty(files)) { for (const file of files) { const fullDepFile = path().resolve(rootDirFullPath, file.file); const depRelativeToConsumer = (0, _utils().pathNormalizeToLinux)(path().relative(this.consumerPath, fullDepFile)); const componentId = this.consumer.bitMap.getComponentIdByPath(depRelativeToConsumer); if (componentId) return componentId; } } if (this.tree[depFile].bits && !_ramda().default.isEmpty(this.tree[depFile].bits)) { const bits = this.tree[depFile].bits || []; for (const bit of bits) { if (bit.componentId) { return bit.componentId; } if (bit.fullPath) { const componentId = this.consumer.getComponentIdFromNodeModulesPath(bit.fullPath, this.component.bindingPrefix); if (componentId) return componentId; } else { const componentId = (0, _packageNameToComponentId().packageNameToComponentId)(this.consumer, bit.name, this.component.bindingPrefix); return componentId; } } } if (files && !_ramda().default.isEmpty(files)) { for (const file of files) { if (file.file !== depFile) { const componentId = this.traverseTreeForComponentId(file.file); if (componentId) return componentId; } else { _logger().default.warn(`traverseTreeForComponentId found a cyclic dependency. ${file.file} depends on itself`); } } } return undefined; } getComponentIdByDepFile(depFile) { let depFileRelative = depFile; // dependency file path relative to consumer root let componentId; let destination; const rootDir = this.componentMap.rootDir; if (rootDir) { // The depFileRelative is relative to rootDir, change it to be relative to current consumer. // We can't use path.resolve(rootDir, fileDep) because this might not work when running // bit commands not from root, because resolve take by default the process.cwd const rootDirFullPath = path().join(this.consumerPath, rootDir); const fullDepFile = path().resolve(rootDirFullPath, depFile); depFileRelative = (0, _utils().pathNormalizeToLinux)(path().relative(this.consumerPath, fullDepFile)); } componentId = this.consumer.bitMap.getComponentIdByPath(depFileRelative); if (!componentId) componentId = this._getComponentIdFromCustomResolveToPackageWithDist(depFileRelative); // if not found here, the file is not a component file. It might be a bit-auto-generated file. // find the component file by the auto-generated file. // We make sure also that it's not an AUTHORED component, which shouldn't have auto-generated files. if (!componentId && this.componentMap.origin !== _constants().COMPONENT_ORIGINS.AUTHORED) { componentId = this.traverseTreeForComponentId(depFile); if (!rootDir) throw new Error('rootDir must be set for non authored components'); if (componentId) { // it is verified now that this depFile is an auto-generated file, therefore the sourceRelativePath and the // destinationRelativePath should be a partial-path and not full-relative-to-consumer path. // since the dep-file is a generated file, it is safe to assume that the componentFromModel has in its // dependencies array this component with the relativePaths array. Find the relativePath of this dep-file // to get the correct destinationRelativePath. There is no other way to obtain this info. ({ componentId, destination, depFileRelative } = this.getDependencyPathsFromModel(componentId, depFile, rootDir)); } else if (!(0, _linkContent().isSupportedExtension)(depFile) && this.componentFromModel) { // unsupported files, such as binary files, don't have link files. instead, they have a // symlink (or sometimes a copy on Windows) of the dependency inside the component. to // check whether a file is a symlink to a dependency we loop through the // sourceRelativePaths of the dependency, if there is match, we use the data from the model const dependenciesFromModel = this.componentFromModel.getAllDependenciesCloned(); const sourcePaths = dependenciesFromModel.getSourcesPaths(); if (sourcePaths.includes(depFile)) { const dependencyFromModel = dependenciesFromModel.getBySourcePath(depFile); // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! componentId = dependencyFromModel.id; ({ componentId, destination, depFileRelative } = this.getDependencyPathsFromModel(componentId, depFile, rootDir)); } } } return { componentId, depFileRelative, destination }; } /** * this is a workaround for cases where an alias points to a package with dist. * normally, aliases are created for local directories. * they can be however useful when a source code can't be touched and `require` to one package * needs to be replaced with a `require` to a component. in that case, our options are: * 1) point the alias to the package name. * 2) point the alias to the relative directory of the imported component * the ideal solution is #1, however, it requires changes in the Tree structure, which should * allow "bits" to have more data, such as importSource. * here, we go option #2, the alias is a relative path to the component. however, when there is * dist directory, the resolved path contains the "dist", which doesn't exist in the ComponentMap, * the solution we take is to identify such cases, strip the dist, then try to find them again. */ _getComponentIdFromCustomResolveToPackageWithDist(depFile) { if (!depFile.includes('dist')) return null; const resolveModules = this.consumer.config._resolveModules; if (!resolveModules || !resolveModules.aliases) return null; const aliases = resolveModules.aliases; const foundAlias = Object.keys(aliases).find(alias => depFile.startsWith(aliases[alias])); if (!foundAlias) return null; const newDepFile = depFile.replace(`${resolveModules.aliases[foundAlias]}/dist`, resolveModules.aliases[foundAlias]); return this.consumer.bitMap.getComponentIdByPath(newDepFile); } // eslint-disable-next-line @typescript-eslint/no-unused-vars getDependencyPathsFromModel(componentId, depFile, rootDir) { const dependency = this.componentFromModel.getAllDependencies().find(dep => dep.id.isEqualWithoutVersion(componentId)); if (!dependency) { throw new (_showDoctorError().default)( // $FlowFixMe `the auto-generated file ${depFile} should be connected to ${componentId}, however, it's not part of the model dependencies of ${this.componentFromModel.id}`); } const relativePath = dependency.relativePaths.find(r => r.sourceRelativePath === depFile); if (!relativePath) { throw new (_showDoctorError().default)(`unable to find ${relativePath} path in the dependencies relativePaths of ${this.componentFromModel.id}`); } return { componentId: dependency.id, destination: relativePath.destinationRelativePath, depFileRelative: depFile }; } processDepFiles(originFile, fileType, nested = false) { // We don't just return because different files of the component might import different things from the depFile // See more info here: https://github.com/teambit/bit/issues/1796 if (!this.processedFiles.includes(originFile)) { this.processedFiles.push(originFile); // We don't want to calculate nested files again after they calculated as direct files } else if (nested) { return; } const allDepsFiles = this.tree[originFile].files; if (!allDepsFiles || _ramda().default.isEmpty(allDepsFiles)) return; allDepsFiles.forEach(depFile => { if (!nested && this.overridesDependencies.shouldIgnoreFile(depFile.file, fileType)) return; if (depFile.isLink) this.processLinkFile(originFile, depFile, fileType);else { const isDepFileUntracked = this.processOneDepFile(originFile, depFile.file, depFile.importSpecifiers, undefined, fileType, depFile, nested); // Only continue recursively if the dep file is untracked // for tracked deps if they have untracked deps they will be shown under their own components if (isDepFileUntracked) { // Recursively check for untracked files (to show them all in bit status) // for nested files we don't really care about the file types since we won't do all the checking const dummyFileType = { isTestFile: false }; this.processDepFiles(depFile.file, dummyFileType, true); } } }); } // return true if the dep file is untracked // eslint-disable-next-line complexity processOneDepFile(originFile, depFile, importSpecifiers, linkFile, fileType, depFileObject, nested = false) { const { componentId, depFileRelative, destination } = this.getComponentIdByDepFile(depFile); // the file dependency doesn't have any counterpart component. Add it to this.issues.untrackedDependencies if (!componentId) { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! if (this.tree[depFile] && this.tree[depFile].missing && this.tree[depFile].missing.bits) { // this depFile is a dependency link and this dependency is missing // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! this._addToMissingComponentsIfNeeded(this.tree[depFile].missing.bits, originFile, fileType); return false; } this._pushToUntrackDependenciesIssues(originFile, depFileRelative, nested); return true; } if (this.overridesDependencies.shouldIgnoreComponent(componentId, fileType)) { // we can't support it because on the imported side, we don't know to convert the relative path // to the component name, as it won't have the component installed throw new (_generalError().default)(`unable to ignore "${componentId.toString()}" dependency of "${this.componentId.toString()}" by using ignore components syntax because the component is required with relative path. either, use the ignore file syntax or change the require statement to have a module path`); } // happens when in the same component one file requires another one. In this case, there is // noting to do regarding the dependencies if (componentId.isEqual(this.componentId)) { if (depFileObject.isCustomResolveUsed) { this.component.customResolvedPaths.push({ destinationPath: depFileObject.file, importSource: depFileObject.importSource }); } return false; } const depComponentMap = this.consumer.bitMap.getComponentIfExist(componentId); // found a dependency component. Add it to this.allDependencies.dependencies const depRootDir = depComponentMap ? depComponentMap.rootDir : undefined; const destinationRelativePath = destination || (depRootDir && depFileRelative.startsWith(depRootDir) ? (0, _utils().pathRelativeLinux)(depRootDir, depFileRelative) : depFileRelative); let sourceRelativePath; if (linkFile) { sourceRelativePath = linkFile; } else { // when there is no rootDir for the current dependency (it happens when it's AUTHORED), keep the original path sourceRelativePath = depRootDir ? depFileRelative : depFile; } const depsPaths = { sourceRelativePath, destinationRelativePath }; if (importSpecifiers) { importSpecifiers.forEach(importSpecifier => { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! if (importSpecifier.linkFile) delete importSpecifier.linkFile.exported; // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! if (importSpecifier.mainFile) delete importSpecifier.mainFile.exported; }); depsPaths.importSpecifiers = importSpecifiers; } if (depFileObject.isCustomResolveUsed) { depsPaths.isCustomResolveUsed = depFileObject.isCustomResolveUsed; depsPaths.importSource = depFileObject.importSource; } const currentComponentsDeps = { id: componentId, relativePaths: [depsPaths] }; if (depComponentMap && !depFileObject.isCustomResolveUsed && ( // for custom resolve, the link is written in node_modules, so it doesn't matter depComponentMap.origin === _constants().COMPONENT_ORIGINS.IMPORTED && this.componentMap.origin === _constants().COMPONENT_ORIGINS.AUTHORED || depComponentMap.origin === _constants().COMPONENT_ORIGINS.AUTHORED && this.componentMap.origin === _constants().COMPONENT_ORIGINS.IMPORTED)) { // prevent author using relative paths for IMPORTED component (to avoid long paths) // also prevent adding AUTHORED component to an IMPORTED component using relative syntax. The reason is that when // this component is imported somewhere else, a link-file between the IMPORTED and the AUTHORED must be written // outside the component directory, which might override user files. this._pushToRelativeComponentsIssues(originFile, componentId); return false; } this._pushToRelativeComponentsAuthoredIssues(originFile, componentId, depFileObject.importSource, depsPaths.importSpecifiers); const allDependencies = [...this.allDependencies.dependencies, ...this.allDependencies.devDependencies]; const existingDependency = this.getExistingDependency(allDependencies, componentId); if (existingDependency) { const existingDepRelativePaths = this.getExistingDepRelativePaths(existingDependency, depsPaths); if (!existingDepRelativePaths) { // it is another file of an already existing component. Just add the new path existingDependency.relativePaths.push(depsPaths); return false; } // The dep path already exists but maybe this dep-file has more importSpecifiers if (depsPaths.importSpecifiers) { // add them to the existing dep if (!existingDepRelativePaths.importSpecifiers) { existingDepRelativePaths.importSpecifiers = [...depsPaths.importSpecifiers]; } else { // both have importSpecifiers const nonExistingImportSpecifiers = this.getDiffSpecifiers(existingDepRelativePaths.importSpecifiers, depsPaths.importSpecifiers); existingDepRelativePaths.importSpecifiers.push(...nonExistingImportSpecifiers); } } // Handle cases when the first dep paths are not custom resolved and the new one is if (depsPaths.isCustomResolveUsed && !existingDepRelativePaths.isCustomResolveUsed) { existingDepRelativePaths.isCustomResolveUsed = depsPaths.isCustomResolveUsed; } if (depsPaths.importSource && !existingDepRelativePaths.importSource) { existingDepRelativePaths.importSource = depsPaths.importSource; } } else { this.pushToDependenciesArray(currentComponentsDeps, fileType); } return false; } processLinkFile(originFile, linkFile, fileType) { if (!linkFile.linkDependencies || _ramda().default.isEmpty(linkFile.linkDependencies)) return; const nonLinkImportSpecifiers = []; linkFile.linkDependencies.forEach(dependency => { const component = this.getComponentIdByDepFile(linkFile.file); if (component.componentId) { // the linkFile is already a component, no need to treat it differently than other depFile // aggregate all dependencies using the same linkFile and ultimately run processDepFile // with all importSpecifiers of that linkFile. // also, delete the linkFile attribute of importSpecifiers so then once the component is // imported and the link is generated, it won't be treated as a linkFile. // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! dependency.importSpecifiers.map(a => delete a.linkFile); // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! nonLinkImportSpecifiers.push(dependency.importSpecifiers); } else { this.processOneDepFile(originFile, // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! dependency.file, // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! dependency.importSpecifiers, linkFile.file, fileType, linkFile); } }); if (nonLinkImportSpecifiers.length) { this.processOneDepFile(originFile, linkFile.file, _ramda().default.flatten(nonLinkImportSpecifiers), undefined, fileType, linkFile); } } /** * process require/import of Bit components where the require statement is not a relative path * but a module path, such as `require('@bit/bit.envs/compiler/babel');` */ processBits(originFile, fileType) { const bits = this.tree[originFile].bits; if (!bits || _ramda().default.isEmpty(bits)) return; let componentId; bits.forEach(bitDep => { const version = bitDep.concreteVersion || bitDep.versionUsedByDependent; if (bitDep.componentId) { componentId = bitDep.componentId; } else if (bitDep.fullPath) { componentId = this.consumer.getComponentIdFromNodeModulesPath(bitDep.fullPath, this.component.bindingPrefix); } else { componentId = (0, _packageNameToComponentId().packageNameToComponentId)(this.consumer, bitDep.name, this.component.bindingPrefix); } if (componentId && version) { componentId = componentId.changeVersion(version); } if (componentId && this.overridesDependencies.shouldIgnoreComponent(componentId, fileType)) { return; } const getExistingId = () => { const existingIds = this.consumer.bitmapIds.filterWithoutVersion(componentId); if (existingIds.length === 1) return existingIds[0]; if (this.componentFromModel) { const modelDep = this.componentFromModel.getAllDependenciesIds().searchWithoutVersion(componentId); if (modelDep) return modelDep; } return undefined; }; const existingId = componentId && version ? componentId : getExistingId(); if (existingId) { if (existingId.isEqual(this.componentId)) { // happens when one of the component files requires another using module path // no need to enter anything to the dependencies return; } const currentComponentsDeps = { id: existingId, relativePaths: [] }; this._pushToDependenciesIfNotExist(existingId, currentComponentsDeps, fileType); } else { this._pushToMissingBitsIssues(originFile, componentId); } }); } processPackages(originFile, fileType) { const packages = this.tree[originFile].packages; if (!packages || _ramda().default.isEmpty(packages)) return; if (fileType.isTestFile) { Object.assign(this.allPackagesDependencies.devPackageDependencies, packages); } else { Object.assign(this.allPackagesDependencies.packageDependencies, packages); } this._addTypesPackagesForTypeScript(packages, originFile); } processMissing(originFile, fileType) { const missing = this.tree[originFile].missing; if (!missing) return; const processMissingFiles = () => { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! if (RA().isNilOrEmpty(missing.files)) return; const absOriginFile = this.consumer.toAbsolutePath(originFile); // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! const missingFiles = missing.files.filter(missingFile => { // convert from importSource (the string inside the require/import call) to the path relative to consumer const resolvedPath = path().resolve(path().dirname(absOriginFile), missingFile); const relativeToConsumer = this.consumer.getPathRelativeToConsumer(resolvedPath); return !this.overridesDependencies.shouldIgnoreFile(relativeToConsumer, fileType); }); if (_ramda().default.isEmpty(missingFiles)) return; this._pushToMissingDependenciesOnFs(originFile, missingFiles); }; const processMissingPackages = () => { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! if (RA().isNilOrEmpty(missing.packages)) return; // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! const missingPackages = missing.packages.filter(pkg => !this.overridesDependencies.shouldIgnorePackage(pkg, fileType)); if (_ramda().default.isEmpty(missingPackages)) return; const customResolvedDependencies = this.findOriginallyCustomModuleResolvedDependencies(missingPackages); if (customResolvedDependencies) { Object.keys(customResolvedDependencies).forEach(missingPackage => { const componentId = customResolvedDependencies[missingPackage].toString(); this._pushToMissingCustomModuleIssues(originFile, componentId); }); } const missingPackagesWithoutCustomResolved = customResolvedDependencies ? _ramda().default.difference(missingPackages, Object.keys(customResolvedDependencies)) : missingPackages; if (!_ramda().default.isEmpty(missingPackagesWithoutCustomResolved)) { this._pushToMissingPackagesDependenciesIssues(originFile, missingPackages); } }; const processMissingComponents = () => { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! if (RA().isNilOrEmpty(missing.bits)) return; // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! this._addToMissingComponentsIfNeeded(missing.bits, originFile, fileType); }; processMissingFiles(); processMissingPackages(); processMissingComponents(); } _addToMissingComponentsIfNeeded(missingComponents, originFile, fileType) { missingComponents.forEach(missingBit => { const componentId = this.consumer.getComponentIdFromNodeModulesPath(missingBit, this.component.bindingPrefix); if (this.overridesDependencies.shouldIgnoreComponent(componentId, fileType)) return; // todo: a component might be on bit.map but not on the FS, yet, it's not about missing links. if (this.consumer.bitMap.getBitIdIfExist(componentId, { ignoreVersion: true })) { this._pushToMissingLinksIssues(originFile, componentId); } else { this._pushToMissingComponentsIssues(originFile, componentId); } }); } processErrors(originFile) { const error = this.tree[originFile].error; if (!error) return; _logger().default.errorAndAddBreadCrumb('dependency-resolver.processErrors', 'got an error from the driver while resolving dependencies'); _logger().default.error('dependency-resolver.processErrors', error); // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! if (error.code === 'PARSING_ERROR') this.issues.parseErrors[originFile] = error.message;else this.issues.resolveErrors[originFile] = error.message; } /** * currently the only unidentified packages being process are the ones coming from custom-modules-resolution. * assuming the author used custom-resolution, which enable using non-relative import syntax, * for example, requiring the file 'src/utils/is-string' from anywhere as require('utils/is-string'); * now, when the component is imported, the driver recognizes 'utils/is-string' as a package, * because it's not relative. * the goal here is to use the 'package' the driver found and match it with one of the * dependencies from the model. In the example above, we might find in the model, a dependency * is-string with importSource of 'utils/is-string'. * Once a match is found, copy the relativePaths from the model. */ processUnidentifiedPackages(originFile, fileType) { const unidentifiedPackages = this.tree[originFile].unidentifiedPackages; if (!unidentifiedPackages) return; if (!this.componentFromModel) return; // not relevant, the component is not imported const getDependencies = () => { if (fileType.isTestFile) return this.componentFromModel.devDependencies; return this.componentFromModel.dependencies; }; const dependencies = getDependencies(); if (dependencies.isEmpty()) return; const importSourceMap = dependencies.getCustomResolvedData(); if (_ramda().default.isEmpty(importSourceMap)) return; const clonedDependencies = new (_dependencies().default)(dependencies.getClone()); unidentifiedPackages.forEach(unidentifiedPackage => { const packageLinuxFormat = (0, _utils().pathNormalizeToLinux)(unidentifiedPackage); const packageWithNoNodeModules = packageLinuxFormat.replace('node_modules/', ''); const foundImportSource = Object.keys(importSourceMap).find(importSource => packageWithNoNodeModules.startsWith(importSource)); if (foundImportSource) { const dependencyId = importSourceMap[foundImportSource]; const currentComponentDeps = { id: dependencyId, // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! relativePaths: clonedDependencies.getById(dependencyId).relativePaths }; this._pushToDependenciesIfNotExist(dependencyId, currentComponentDeps, fileType); } }); } _pushToDependenciesIfNotExist(dependencyId, dependency, fileType) { const existingDependency = this.getExistingDependency(this.allDependencies.dependencies, dependencyId); const existingDevDependency = this.getExistingDependency(this.allDependencies.devDependencies, dependencyId); if (fileType.isTestFile && !existingDevDependency) { this.allDependencies.devDependencies.push(dependency); } else if (!fileType.isTestFile && !existingDependency) { this.allDependencies.dependencies.push(dependency); } } pushToDependenciesArray(currentComponentsDeps, fileType) { if (fileType.isTestFile) { this.allDependencies.devDependencies.push(currentComponentsDeps); } else { this.allDependencies.dependencies.push(currentComponentsDeps); } } /** * Remove the dependencies which appear both in dev and regular deps from the dev * Because if a dependency is both dev dependency and regular dependency it should be treated as regular one * Apply for both packages and components dependencies */ removeDevAndEnvDepsIfTheyAlsoRegulars() { // remove dev and env packages that are also regular packages const getNotRegularPackages = packages => _ramda().default.difference(_ramda().default.keys(packages), _ramda().default.keys(this.allPackagesDependencies.packageDependencies)); this.allPackagesDependencies.devPackageDependencies = _ramda().default.pick(getNotRegularPackages(this.allPackagesDependencies.devPackageDependencies), this.allPackagesDependencies.devPackageDependencies); this.allPackagesDependencies.compilerPackageDependencies = _ramda().default.pick(getNotRegularPackages(this.allPackagesDependencies.compilerPackageDependencies), this.allPackagesDependencies.compilerPackageDependencies); this.allPackagesDependencies.testerPackageDependencies = _ramda().default.pick(getNotRegularPackages(this.allPackagesDependencies.testerPackageDependencies), this.allPackagesDependencies.testerPackageDependencies); // remove dev dependencies that are also regular dependencies // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! const componentDepsIds = new (_bitId().BitIds)(...this.allDependencies.dependencies.map(c => c.id)); this.allDependencies.devDependencies = this.allDependencies.devDependencies.filter(d => !componentDepsIds.has(d.id)); } /** * given missing packages name, find whether they were dependencies with custom-resolve before. */ findOriginallyCustomModuleResolvedDependencies(packages) { if (!packages) return undefined; if (!this.componentFromModel) return undefined; // not relevant, the component is not imported const dependencies = new (_dependencies().default)(this.componentFromModel.getAllDependencies()); if (dependencies.isEmpty()) return undefined; const importSourceMap = dependencies.getCustomResolvedData(); if (_ramda().default.isEmpty(importSourceMap)) return undefined; const foundPackages = packages.reduce((acc, value) => { const packageLinuxFormat = (0, _utils().pathNormalizeToLinux)(value); const foundImportSource = Object.keys(importSourceMap).find(importSource => importSource.startsWith(packageLinuxFormat)); if (foundImportSource) { const dependencyId = importSourceMap[foundImportSource]; acc[value] = dependencyId; } return acc; }, {}); return _ramda().default.isEmpty(foundPackages) ? undefined : foundPackages; } combineIssues() { Object.keys(this.issues.missingBits).forEach(missingBit => { if (this.issues.missingComponents[missingBit]) { this.issues.missingComponents[missingBit] = this.issues.missingComponents[missingBit].concat(this.issues.missingBits[missingBit]); } else { this.issues.missingComponents[missingBit] = this.issues.missingBits[missingBit]; } }); } removeEmptyIssues() { const notEmpty = item => !_ramda().default.isEmpty(item); this.issues = _ramda().default.filter(notEmpty, this.issues); } getExistingDependency(dependencies, id) { return dependencies.find(d => d.id.isEqualWithoutVersion(id)); } getExistingDepRelativePaths(dependency, relativePath) { if (!dependency.relativePaths || _ramda().default.isEmpty(dependency.relativePaths)) return null; return dependency.relativePaths.find(paths => paths.sourceRelativePath === relativePath.sourceRelativePath && paths.destinationRelativePath === relativePath.destinationRelativePath); } getDiffSpecifiers(originSpecifiers, targetSpecifiers) { const cmp = (specifier1, specifier2) => specifier1.mainFile.name === specifier2.mainFile.name; return _ramda().default.differenceWith(cmp, targetSpecifiers, originSpecifiers); } /** * For author, the peer-dependencies are set in the root package.json * For imported components, we don't want to change the peerDependencies of the author, unless * we're certain the user intent to do so. Therefore, we ignore the root package.json and look for * the package.json in the component's directory. */ populatePeerPackageDependencies() { const getPeerDependencies = () => { const packageJson = this._getPackageJson(); // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! if (packageJson && packageJson.peerDependencies) return packageJson.peerDependencies; return {}; }; const projectPeerDependencies = getPeerDependencies(); const peerPackages = {}; if (_ramda().default.isEmpty(projectPeerDependencies)) return; // check whether the peer-dependencies was actually require in the code. if so, remove it from // the packages/dev-packages and add it as a peer-package. // if it was not required in the code, don't add it to the peerPackages Object.keys(projectPeerDependencies).forEach(pkg => { if (this.overridesDependencies.shouldIgnorePeerPackage(pkg)) return; ['packageDependencies', 'devPackageDependencies'].forEach(field => { if (Object.keys(this.allPackagesDependencies[field]).includes(pkg)) { delete this.allPackagesDependencies[field][pkg]; peerPackages[pkg] = projectPeerDependencies[pkg]; } }); }); this.allPackagesDependencies.peerPackageDependencies = peerPackages; } /** * returns `package.json` of the component when it's imported, or `package.json` of the workspace * when it's authored. */ _getPackageJson() { const componentMap = this.component.componentMap; // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! const isAuthor = componentMap.origin === _constants().COMPONENT_ORIGINS.AUTHORED; if (isAuthor) { return this.consumer.packageJson.packageJsonObject; } if (this.component.packageJsonFile && this.component.packageJsonFile.fileExist) { return this.component.packageJsonFile.packageJsonObject; } if (this.componentFromModel) { // a component is imported but the package.json file is missing or never written // read the values from the model // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! const packageJson = _packageJsonFile().default.createFromComponent(componentMap.rootDir, this.componentFromModel); return packageJson.packageJsonObject; } return undefined; } /** * when requiring packages in typescript, sometimes there are the types packages with the same * name, which the user probably wants as well. for example, requiring `foo` package, will also * add `@types/foo` to the devDependencies if it has been found in the user `package.json` file. * * ideally this should be in bit-javascript. however, the decision where to put these `@types` * packages (dependencies/devDependencies) is done here according to the user `package.json` * and can't be done there because the `Tree` we get from bit-javascript doesn't have this * distinction. */ _addTypesPackagesForTypeScript(packages, originFile) { const isTypeScript = (0, _utils().getExt)(originFile) === 'ts' || (0, _utils().getExt)(originFile) === 'tsx'; if (!isTypeScript) return; const packageJson = this._getPackageJson(); if (!packageJson) return; const addIfNeeded = (depField, packageName) => { if (!packageJson[depField]) return; const typesPackage = `@types/${packageName}`; if (!packageJson[depField][typesPackage]) return; Object.assign(this.allPackagesDependencies[this._pkgFieldMapping(depField)], { [typesPackage]: packageJson[depField][typesPackage] }); }; Object.keys(packages).forEach(packageName => { _constants().DEPENDENCIES_FIELDS.forEach(depField => addIfNeeded(depField, packageName)); }); } _pkgFieldMapping(field) { switch (field) { case 'dependencies': return 'packageDependencies'; case 'devDependencies': return 'devPackageDependencies'; case 'peerDependencies': return 'peerPackageDependencies'; default: throw new Error(`${field} is not recognized`); } } _pushToUntrackDependenciesIssues(originFile, depFileRelative, nested = false) { const untracked = this.issues.untrackedDependencies[originFile]; const findExisting = () => { let result; _ramda().default.forEachObjIndexed(currentUntracked => { const found = currentUntracked.untrackedFiles.find(file => { return file.relativePath === depFileRelative; }); if (found) { result = found; } }, this.issues.untrackedDependencies); return result;