UNPKG

pnpm

Version:

A fast implementation of npm install

208 lines • 8.74 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments)).next()); }); }; const path = require('path'); const seq = require('promisequence'); const chalk = require('chalk'); const createDebug = require('debug'); const RegClient = require('npm-registry-client'); const logger = require('@zkochan/logger'); const cloneDeep = require('lodash.clonedeep'); const got_1 = require('../network/got'); const getContext_1 = require('./getContext'); const installMultiple_1 = require('../installMultiple'); const save_1 = require('../save'); const linkPeers_1 = require('../install/linkPeers'); const runtimeError_1 = require('../runtimeError'); const getSaveType_1 = require('../getSaveType'); const runScript_1 = require('../runScript'); const postInstall_1 = require('../install/postInstall'); const linkBins_1 = require('../install/linkBins'); const extendOptions_1 = require('./extendOptions'); const pnpmPkgJson_1 = require('../pnpmPkgJson'); const lock_1 = require('./lock'); const storeJsonController_1 = require('../fs/storeJsonController'); const uninstall_1 = require('./uninstall'); function install(maybeOpts) { return __awaiter(this, void 0, void 0, function* () { const opts = extendOptions_1.default(maybeOpts); const ctx = yield getContext_1.default(opts); const installCtx = yield createInstallCmd(opts, ctx.storeJson, ctx.cache); if (!ctx.pkg) throw runtimeError_1.default('No package.json found'); const packagesToInstall = Object.assign({}, ctx.pkg.dependencies || {}); if (!opts.production) Object.assign(packagesToInstall, ctx.pkg.devDependencies || {}); return lock_1.default(ctx.store, () => installInContext('general', packagesToInstall, ctx, installCtx, opts)); }); } exports.install = install; /** * Perform installation. * * @example * install({'lodash': '1.0.0', 'foo': '^2.1.0' }, { silent: true }) */ function installPkgs(fuzzyDeps, maybeOpts) { return __awaiter(this, void 0, void 0, function* () { let packagesToInstall = mapify(fuzzyDeps); if (!Object.keys(packagesToInstall).length) { throw new Error('At least one package has to be installed'); } const opts = extendOptions_1.default(maybeOpts); const ctx = yield getContext_1.default(opts); const installCtx = yield createInstallCmd(opts, ctx.storeJson, ctx.cache); return lock_1.default(ctx.store, () => installInContext('named', packagesToInstall, ctx, installCtx, opts)); }); } exports.installPkgs = installPkgs; function installInContext(installType, packagesToInstall, ctx, installCtx, opts) { return __awaiter(this, void 0, void 0, function* () { // TODO: ctx.storeJson should not be muted. installMultiple should return a new storeJson const oldStoreJson = cloneDeep(ctx.storeJson); const pkgs = yield lock_1.default(ctx.cache, () => installMultiple_1.default(installCtx, packagesToInstall, ctx.pkg && ctx.pkg && ctx.pkg.optionalDependencies || {}, path.join(ctx.root, 'node_modules'), { linkLocal: opts.linkLocal, dependent: ctx.root, root: ctx.root, store: ctx.store, force: opts.force, depth: opts.depth, tag: opts.tag })); if (installType === 'named') { const saveType = getSaveType_1.default(opts); if (saveType) { if (!ctx.pkg) { throw new Error('Cannot save because no package.json found'); } const inputNames = Object.keys(packagesToInstall); const savedPackages = pkgs.filter((pkg) => inputNames.indexOf(pkg.pkg.name) > -1); const pkgJsonPath = path.join(ctx.root, 'package.json'); yield save_1.default(pkgJsonPath, savedPackages, saveType, opts.saveExact); } } const newStoreJson = Object.assign({}, ctx.storeJson, { pnpm: pnpmPkgJson_1.default.version }); yield removeOrphanPkgs(oldStoreJson, newStoreJson, ctx.root, ctx.store); storeJsonController_1.save(ctx.store, newStoreJson); yield linkPeers_1.default(ctx.store, installCtx.installs); // postinstall hooks if (!(opts.ignoreScripts || !installCtx.piq || !installCtx.piq.length)) { yield seq(installCtx.piq.map(pkg => () => linkBins_1.default(path.join(pkg.path, '_', 'node_modules')) .then(() => postInstall_1.default(pkg.path, installLogger(pkg.pkgId))) .catch(err => { if (installCtx.installs[pkg.pkgId].optional) { console.log('Skipping failed optional dependency ' + pkg.pkgId + ':'); console.log(err.message || err); return; } throw err; }))); } yield linkBins_1.default(path.join(ctx.root, 'node_modules')); if (!opts.ignoreScripts && ctx.pkg) { const scripts = ctx.pkg && ctx.pkg.scripts || {}; if (scripts['postinstall']) { npmRun('postinstall', ctx.root); } if (installType === 'general' && scripts['prepublish']) { npmRun('prepublish', ctx.root); } } }); } function removeOrphanPkgs(oldStoreJson, newStoreJson, root, store) { const oldDeps = oldStoreJson.packages[root] && oldStoreJson.packages[root].dependencies || {}; const newDeps = newStoreJson.packages[root] && newStoreJson.packages[root].dependencies || {}; const maybeUninstallPkgs = Object.keys(oldDeps) .filter(depName => oldDeps[depName] !== newDeps[depName]) .map(depName => oldDeps[depName]); const uninstallPkgs = uninstall_1.tryUninstall(maybeUninstallPkgs, newStoreJson, root); return Promise.all(uninstallPkgs.map(pkgId => uninstall_1.removePkgFromStore(pkgId, store))); } function createInstallCmd(opts, storeJson, cache) { return __awaiter(this, void 0, void 0, function* () { const client = new RegClient(adaptConfig(opts)); return { fetches: {}, builds: {}, installs: {}, got: got_1.default(client, { cachePath: cache, cacheTTL: opts.cacheTTL }), storeJson }; }); } function adaptConfig(opts) { return { proxy: { http: opts.proxy, https: opts.httpsProxy, localAddress: opts.localAddress }, ssl: { certificate: opts.cert, key: opts.key, ca: opts.ca, strict: opts.strictSsl }, retry: { count: opts.fetchRetries, factor: opts.fetchRetryFactor, minTimeout: opts.fetchRetryMintimeout, maxTimeout: opts.fetchRetryMaxtimeout }, userAgent: opts.userAgent, log: Object.assign({}, logger, { verbose: logger.log.bind(null, 'verbose'), http: logger.log.bind(null, 'http') }), defaultTag: opts.tag }; } function npmRun(scriptName, pkgRoot) { const result = runScript_1.sync('npm', ['run', scriptName], { cwd: pkgRoot, stdio: 'inherit' }); if (result.status !== 0) { process.exit(result.status); } } function installLogger(pkgId) { return (stream, line) => { createDebug('pnpm:post_install')(`${pkgId} ${line}`); if (stream === 'stderr') { console.log(chalk.blue(pkgId) + '! ' + chalk.gray(line)); return; } console.log(chalk.blue(pkgId) + ' ' + chalk.gray(line)); }; } function mapify(pkgs) { if (!pkgs) return {}; if (Array.isArray(pkgs)) { return pkgs.reduce((pkgsMap, pkgRequest) => { const matches = /(@?[^@]+)@(.*)/.exec(pkgRequest); if (!matches) { pkgsMap[pkgRequest] = '*'; } else { pkgsMap[matches[1]] = matches[2]; } return pkgsMap; }, {}); } return pkgs; } //# sourceMappingURL=install.js.map