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

625 lines (506 loc) 18.6 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 _execa() { const data = _interopRequireDefault(require("execa")); _execa = function () { return data; }; return data; } function _child_process() { const data = require("child_process"); _child_process = function () { return data; }; return data; } function _pMapSeries() { const data = _interopRequireDefault(require("p-map-series")); _pMapSeries = function () { return data; }; return data; } function _semver() { const data = _interopRequireDefault(require("semver")); _semver = function () { return data; }; return data; } function _ramda() { const data = _interopRequireWildcard(require("ramda")); _ramda = function () { return data; }; return data; } function _chalk() { const data = _interopRequireDefault(require("chalk")); _chalk = function () { return data; }; return data; } function _fsExtra() { const data = _interopRequireDefault(require("fs-extra")); _fsExtra = 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 _constants() { const data = require("../constants"); _constants = function () { return data; }; return data; } function _analytics() { const data = require("../analytics/analytics"); _analytics = function () { return data; }; return data; } function _showDoctorError() { const data = _interopRequireDefault(require("../error/show-doctor-error")); _showDoctorError = function () { return data; }; return data; } const objectToArray = obj => (0, _ramda().map)((0, _ramda().join)('@'), (0, _ramda().toPairs)(obj)); const rejectNils = _ramda().default.reject(_ramda().isNil); const defaultNpmArgs = []; const defaultYarnArgs = []; const defaultPnpmArgs = []; const defaultPackageManagerArgs = { npm: defaultNpmArgs, yarn: defaultYarnArgs, pnpm: defaultPnpmArgs }; const defaultPackageManagerProcessOptions = { cwd: process.cwd }; const warningPrefix = packageManager => { return packageManager === 'npm' ? 'npm WARN' : 'warning'; }; const errorPrefix = packageManager => { return packageManager === 'npm' ? 'npm ERR!' : 'error'; }; const peerDependenciesMissing = packageManager => { return packageManager === 'npm' ? 'requires a peer' : 'unmet peer'; }; const stripNonNpmErrors = (errors, packageManager) => { // a workaround to remove all 'npm warn' and 'npm notice'. // NPM itself returns them even when --loglevel = error or when --silent/--quiet flags are set const prefix = errorPrefix(packageManager); return errors.split('\n').filter(error => error.startsWith(prefix)).join('\n'); }; const stripNonPeerDependenciesWarnings = (errors, packageManager) => { const prefix = warningPrefix(packageManager); const peer = peerDependenciesMissing(packageManager); return errors.split('\n').filter(error => error.startsWith(prefix) && error.includes(peer)).join('\n'); }; /** * Pick only allowed to be overridden options * @param {Object} userOptions */ const getAllowdPackageManagerProcessOptions = userOptions => { const allowdOptions = ['shell', 'env', 'extendEnv', 'uid', 'gid', 'preferLocal', 'localDir', 'timeout']; return _ramda().default.pick(allowdOptions, userOptions); }; /** * Install packages in specific directory */ const _installInOneDirectory = ({ modules = [], packageManager = _constants().DEFAULT_PACKAGE_MANAGER, packageManagerArgs = [], packageManagerProcessOptions = {}, dir, installProdPackagesOnly = false, verbose = false }) => { // Handle process options const allowedPackageManagerProcessOptions = getAllowdPackageManagerProcessOptions(packageManagerProcessOptions); const concretePackageManagerProcessOptions = (0, _ramda().merge)(defaultPackageManagerProcessOptions, allowedPackageManagerProcessOptions); concretePackageManagerProcessOptions.cwd = dir || concretePackageManagerProcessOptions.cwd; const cwd = concretePackageManagerProcessOptions.cwd; // taking care of object case const processedModules = (0, _ramda().is)(Object, modules) && !Array.isArray(modules) ? objectToArray(modules) : modules; // Handle process args const concretePackageManagerDefaultArgs = ['install', ...processedModules, ...defaultPackageManagerArgs[packageManager]]; const concretePackageManagerArgs = rejectNils(_ramda().default.concat(concretePackageManagerDefaultArgs, packageManagerArgs)); // Add npm verbose flag if (verbose && packageManager === 'npm') {// we may want to use it later. For now, it print too much information // concretePackageManagerArgs.push('--verbose'); } if (installProdPackagesOnly) { concretePackageManagerArgs.push('--production'); } _fsExtra().default.ensureDirSync(path().join(cwd, 'node_modules')); _logger().default.debug(`installing npm packages using ${packageManager} at ${cwd} with args: ${concretePackageManagerArgs} and options:`, concretePackageManagerProcessOptions); // Set the shell to true to prevent problems with post install scripts when running as root const packageManagerClientName = packageManager; const childProcess = (0, _execa().default)(packageManagerClientName, concretePackageManagerArgs, concretePackageManagerProcessOptions); // Remove the install from args since it's always there const printArgs = concretePackageManagerArgs.filter(arg => arg !== 'install'); const argsString = printArgs && printArgs.length > 0 ? `with args: ${printArgs}` : ''; return childProcess.then(({ stdout, stderr }) => { const successMessage = `\nsuccessfully ran ${packageManager} install at ${cwd} ${argsString}`; const peerWarnings = stripNonPeerDependenciesWarnings(stderr, packageManager); stdout = verbose ? stdout + successMessage : _chalk().default.white(peerWarnings) + successMessage; stderr = verbose ? stderr : ''; return { stdout, stderr }; }).catch(err => { let stderr = `failed running ${packageManager} install at ${cwd} ${argsString} \n`; stderr += verbose ? err.stderr : stripNonNpmErrors(err.stderr, packageManager); throw new (_showDoctorError().default)(`${stderr}\n\n${_chalk().default.yellow(`see troubleshooting at https://${_constants().BASE_DOCS_DOMAIN}/docs/installing-components`)}`); }); }; const _getNpmList = /*#__PURE__*/function () { var _ref = (0, _bluebird().coroutine)(function* (packageManager, dir) { // We don't use here execa since there is a bug with execa (2.*) with some node versions // execa uses util.getSystemErrorName which not available in some node versions // see more here - https://github.com/sindresorhus/execa/issues/318 // We also use spwan instead of exec since the output might be very long and exec is limited with // handling such long outputs // once we stop support node < 10 we can replace it with something like // npmList = await execa(packageManager, ['list', '-j'], { cwd: dir }); return new Promise(resolve => { let stdout = ''; let stderr = ''; const shell = _constants().IS_WINDOWS; const ls = (0, _child_process().spawn)(packageManager, ['list', '-j'], { cwd: dir, shell }); ls.stdout.on('data', data => { stdout += data; }); ls.stderr.on('data', data => { stderr += data; }); ls.on('error', err => { stderr += err; }); ls.on('close', code => { const res = { stdout, stderr, code }; resolve(res); }); }); }); return function _getNpmList(_x, _x2) { return _ref.apply(this, arguments); }; }(); /** * Get peer dependencies for directory * you should run this after you run npm install * internally it uses npm list -j */ const _getPeerDeps = /*#__PURE__*/function () { var _ref2 = (0, _bluebird().coroutine)(function* (dir) { const packageManager = _constants().DEFAULT_PACKAGE_MANAGER; const npmList = yield _getNpmList(packageManager, dir); // If the npmList.stdout starts with '{' it's probably a valid json so no throw an error if (npmList.stderr && !npmList.stdout.startsWith('{')) { _logger().default.error('npm-client got an error', npmList.stderr); throw new Error(`failed running ${packageManager} list on folder ${dir} to find the peer dependencies due to an error: ${npmList.stderr}`); } const peerDepsObject = yield getPeerDepsFromNpmList(npmList.stdout, packageManager); return objectToArray(peerDepsObject); }); return function _getPeerDeps(_x3) { return _ref2.apply(this, arguments); }; }(); function getPeerDepsFromNpmList(_x4, _x5) { return _getPeerDepsFromNpmList.apply(this, arguments); } // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! function _getPeerDepsFromNpmList() { _getPeerDepsFromNpmList = (0, _bluebird().coroutine)(function* (npmList, packageManager) { const parsePeers = deps => { const result = {}; _ramda().default.forEachObjIndexed(dep => { if (dep.peerMissing) { const name = dep.required.name; const version = dep.required.version; result[name] = version; } }, deps); return result; }; const npmListObject = yield parseNpmListJsonGracefully(npmList, packageManager); // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! return parsePeers(npmListObject.dependencies); }); return _getPeerDepsFromNpmList.apply(this, arguments); } function parseNpmListJsonGracefully(_x6, _x7) { return _parseNpmListJsonGracefully.apply(this, arguments); } /** * A wrapper function to call the install * then get the peers * then install the peers */ function _parseNpmListJsonGracefully() { _parseNpmListJsonGracefully = (0, _bluebird().coroutine)(function* (str, packageManager) { try { const json = JSON.parse(str); return json; } catch (err) { _logger().default.error('npm-client got an error', err); if (packageManager === 'npm') { const version = yield getNpmVersion(); _analytics().Analytics.setExtraData('npmVersion', version); if (version && _semver().default.gte(version, '5.0.0') && _semver().default.lt(version, '5.1.0')) { // see here for more info about this issue with npm 5.0.0 // https://github.com/npm/npm/issues/17331 throw new Error(`error: your npm version "${version}" has issues returning json, please upgrade to 5.1.0 or above (npm install -g npm@5.1.0)`); } } throw new Error(`failed parsing the output of npm list due to an error: ${err.message}`); } }); return _parseNpmListJsonGracefully.apply(this, arguments); } const _installInOneDirectoryWithPeerOption = /*#__PURE__*/function () { var _ref3 = (0, _bluebird().coroutine)(function* ({ modules = [], packageManager = _constants().DEFAULT_PACKAGE_MANAGER, packageManagerArgs = [], packageManagerProcessOptions = {}, dir, installPeerDependencies = false, installProdPackagesOnly = false, verbose = false }) { const rootDirResults = yield _installInOneDirectory({ modules, packageManager, packageManagerArgs, packageManagerProcessOptions, dir, // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! installPeerDependencies, installProdPackagesOnly, verbose }); if (installPeerDependencies) { const peers = yield _getPeerDeps(dir); const peerResults = yield _installInOneDirectory({ // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! modules: peers, packageManager, packageManagerArgs, packageManagerProcessOptions, dir, installProdPackagesOnly, verbose }); return [rootDirResults, peerResults]; } return rootDirResults; }); return function _installInOneDirectoryWithPeerOption(_x8) { return _ref3.apply(this, arguments); }; }(); /** * when modules is empty, it runs 'npm install' without any package, which installs according to package.json file */ const installAction = /*#__PURE__*/function () { var _ref4 = (0, _bluebird().coroutine)(function* ({ modules, packageManager = _constants().DEFAULT_PACKAGE_MANAGER, packageManagerArgs = [], packageManagerProcessOptions = {}, useWorkspaces = false, dirs = [], rootDir, installRootPackageJson = false, installPeerDependencies = false, installProdPackagesOnly = false, verbose = false }) { if (useWorkspaces && packageManager === 'yarn') { yield _installInOneDirectoryWithPeerOption({ // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! modules, packageManager, // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! packageManagerArgs, packageManagerProcessOptions, dir: rootDir, installPeerDependencies, installProdPackagesOnly, verbose }); } const results = []; if (installRootPackageJson) { // installation of the root package.json has to be completed before installing the sub-directories package.json. const rootDirResults = yield _installInOneDirectoryWithPeerOption({ // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! modules, packageManager, // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! packageManagerArgs, packageManagerProcessOptions, dir: rootDir, installPeerDependencies, installProdPackagesOnly, verbose }); if (Array.isArray(rootDirResults)) { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! results.concat(rootDirResults); } else { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! results.push(rootDirResults); } } const installInDir = dir => _installInOneDirectoryWithPeerOption({ // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! modules, packageManager, // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! packageManagerArgs, packageManagerProcessOptions, dir, installPeerDependencies, installProdPackagesOnly, verbose }); // run npm install for each one of the directories serially, not in parallel. Don’t use Promise.all() here. // running them in parallel result in race condition and random NPM errors. (see https://github.com/teambit/bit/issues/1617) const promisesResults = yield (0, _pMapSeries().default)(dirs, installInDir); return results.concat(_ramda().default.flatten(promisesResults)); }); return function installAction(_x9) { return _ref4.apply(this, arguments); }; }(); const printResults = ({ stdout, stderr }) => { _logger().default.console(_chalk().default.yellow(stdout)); // eslint-disable-line _logger().default.console(_chalk().default.yellow(stderr)); // eslint-disable-line }; function getNpmVersion() { return _getNpmVersion.apply(this, arguments); } function _getNpmVersion() { _getNpmVersion = (0, _bluebird().coroutine)(function* () { try { const { stdout, stderr } = yield (0, _execa().default)('npm', ['--version']); if (stdout && !stderr) return stdout; } catch (err) { _logger().default.debugAndAddBreadCrumb('npm-client', `got an error when executing "npm --version". ${err.message}`); } return null; }); return _getNpmVersion.apply(this, arguments); } function getYarnVersion() { return _getYarnVersion.apply(this, arguments); } /** * a situation where rootDir and subDir have package.json, some of the packages may be shared * and some may be conflicted. And the "npm/yarn install" is done from the root dir. * package managers install the shared packages only once in the rootDir. * however, as to the conflicted packages, only npm@5 and above install it in the subDir. * others, install it in the root, which, result in an incorrect package resolution for the subDir. */ function _getYarnVersion() { _getYarnVersion = (0, _bluebird().coroutine)(function* () { try { const { stdout } = yield (0, _execa().default)('yarn', ['-v']); return stdout; } catch (e) { _logger().default.debugAndAddBreadCrumb('npm-client', `can't find yarn version by running yarn -v. ${e.message}`); } return null; }); return _getYarnVersion.apply(this, arguments); } function isSupportedInstallationOfSubDirFromRoot(_x10) { return _isSupportedInstallationOfSubDirFromRoot.apply(this, arguments); } function _isSupportedInstallationOfSubDirFromRoot() { _isSupportedInstallationOfSubDirFromRoot = (0, _bluebird().coroutine)(function* (packageManager) { if (packageManager === 'npm') { const version = yield getNpmVersion(); if (version && _semver().default.gte(version, '5.0.0')) { return true; } } return false; }); return _isSupportedInstallationOfSubDirFromRoot.apply(this, arguments); } function getPackageLatestVersion(_x11) { return _getPackageLatestVersion.apply(this, arguments); } function _getPackageLatestVersion() { _getPackageLatestVersion = (0, _bluebird().coroutine)(function* (packageName) { try { const { stdout } = yield (0, _execa().default)('npm', ['show', packageName, 'version']); return stdout; } catch (e) { _logger().default.debugAndAddBreadCrumb('npm-client', `can't find ${packageName} version by running npm show ${packageName} version. ${e.message}`); } return null; }); return _getPackageLatestVersion.apply(this, arguments); } var _default = { install: installAction, printResults, isSupportedInstallationOfSubDirFromRoot, getNpmVersion, getYarnVersion, getPeerDepsFromNpmList, getPackageLatestVersion }; exports.default = _default;