pnpm
Version:
A fast implementation of npm install
208 lines • 8.74 kB
JavaScript
;
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