UNPKG

@zkochan/pnpm

Version:

Fast, disk space efficient package manager

255 lines 11.9 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const logger_1 = require("@pnpm/logger"); const utils_1 = require("@pnpm/utils"); const camelcaseKeys = require("camelcase-keys"); const graphSequencer = require("graph-sequencer"); const mem = require("mem"); const pLimit = require("p-limit"); const path = require("path"); const pkgs_graph_1 = require("pkgs-graph"); const readIniFile = require("read-ini-file"); const supi_1 = require("supi"); const createStoreController_1 = require("../../createStoreController"); const findWorkspacePackages_1 = require("../../findWorkspacePackages"); const getCommandFullName_1 = require("../../getCommandFullName"); const parsePackageSelectors_1 = require("../../parsePackageSelectors"); const requireHooks_1 = require("../../requireHooks"); const help_1 = require("../help"); const exec_1 = require("./exec"); const filter_1 = require("./filter"); const list_1 = require("./list"); const outdated_1 = require("./outdated"); const recursiveSummary_1 = require("./recursiveSummary"); const run_1 = require("./run"); const supportedRecursiveCommands = new Set([ 'install', 'uninstall', 'update', 'link', 'unlink', 'list', 'outdated', 'rebuild', 'run', 'test', 'exec', ]); exports.default = (input, opts) => __awaiter(this, void 0, void 0, function* () { if (opts.workspaceConcurrency < 1) { const err = new Error('Workspace concurrency should be at least 1'); err['code'] = 'ERR_PNPM_INVALID_WORKSPACE_CONCURRENCY'; // tslint:disable-line:no-string-literal throw err; } const cmd = input.shift(); if (!cmd) { help_1.default(['recursive']); return; } const cmdFullName = getCommandFullName_1.default(cmd); if (!supportedRecursiveCommands.has(cmdFullName)) { help_1.default(['recursive']); const err = new Error(`"recursive ${cmdFullName}" is not a pnpm command. See "pnpm help recursive".`); err['code'] = 'ERR_PNPM_INVALID_RECURSIVE_COMMAND'; // tslint:disable-line:no-string-literal throw err; } const workspacePrefix = opts.workspacePrefix || process.cwd(); const allWorkspacePkgs = yield findWorkspacePackages_1.default(workspacePrefix); if (!allWorkspacePkgs.length) { logger_1.default.info({ message: `No packages found in "${workspacePrefix}"`, prefix: workspacePrefix }); return; } if (opts.filter) { // TODO: maybe @pnpm/config should return this in a parsed form already? // We don't use opts.prefix in this case because opts.prefix searches for a package.json in parent directories and // selects the directory where it finds one opts['packageSelectors'] = opts.filter.map((f) => parsePackageSelectors_1.default(f, process.cwd())); // tslint:disable-line } const atLeastOnePackageMatched = yield recursive(allWorkspacePkgs, input, opts, cmdFullName, cmd); if (!atLeastOnePackageMatched) { logger_1.default.info({ message: `No packages matched the filters in "${workspacePrefix}"`, prefix: workspacePrefix }); return; } }); function recursive(allPkgs, input, opts, cmdFullName, cmd) { return __awaiter(this, void 0, void 0, function* () { if (allPkgs.length === 0) { // It might make sense to throw an exception in this case return false; } const pkgGraphResult = pkgs_graph_1.default(allPkgs); let pkgs; if (opts.scope) { pkgGraphResult.graph = filter_1.filterGraphByScope(pkgGraphResult.graph, opts.scope); pkgs = allPkgs.filter((pkg) => pkgGraphResult.graph[pkg.path]); } else if (opts.packageSelectors && opts.packageSelectors.length) { pkgGraphResult.graph = filter_1.filterGraph(pkgGraphResult.graph, opts.packageSelectors); pkgs = allPkgs.filter((pkg) => pkgGraphResult.graph[pkg.path]); } else { pkgs = allPkgs; } if (pkgs.length === 0) { return false; } const throwOnFail = recursiveSummary_1.throwOnCommandFail.bind(null, `pnpm recursive ${cmd}`); switch (cmdFullName) { case 'list': yield list_1.default(pkgs, input, cmd, opts); // tslint:disable-line:no-any return true; case 'outdated': yield outdated_1.default(pkgs, input, cmd, opts); // tslint:disable-line:no-any return true; } const chunks = opts.sort ? sortPackages(pkgGraphResult.graph) : [Object.keys(pkgGraphResult.graph).sort()]; switch (cmdFullName) { case 'test': throwOnFail(yield run_1.default(chunks, pkgGraphResult.graph, ['test', ...input], cmd, opts)); // tslint:disable-line:no-any return true; case 'run': throwOnFail(yield run_1.default(chunks, pkgGraphResult.graph, input, cmd, opts)); // tslint:disable-line:no-any return true; case 'update': opts = Object.assign({}, opts, { update: true, allowNew: false }); // tslint:disable-line:no-any break; case 'exec': throwOnFail(yield exec_1.default(chunks, input, cmd, opts)); // tslint:disable-line:no-any return true; } const store = yield createStoreController_1.default(opts); // It is enough to save the store.json file once, // once all installations are done. // That's why saveState that is passed to the install engine // does nothing. const saveState = store.ctrl.saveState; const storeController = Object.assign({}, store.ctrl, { saveState: () => __awaiter(this, void 0, void 0, function* () { return undefined; }) }); if (cmdFullName === 'link' && opts.linkWorkspacePackages) { const err = new Error('"pnpm recursive link" is deprecated with link-workspace-packages = true. Please use "pnpm recursive install" instead'); err['code'] = 'ERR_PNPM_RECURSIVE_LINK_DEPRECATED'; // tslint:disable-line:no-string-literal throw err; } const localPackages = cmdFullName === 'link' || opts.linkWorkspacePackages ? findWorkspacePackages_1.arrayOfLocalPackagesToMap(allPkgs) : {}; const installOpts = Object.assign(opts, { localPackages, ownLifecycleHooksStdio: 'pipe', store: store.path, storeController, }); const limitInstallation = pLimit(opts.workspaceConcurrency); const result = { fails: [], passes: 0, }; const memReadLocalConfigs = mem(readLocalConfigs); if (cmdFullName !== 'rebuild') { let action; // tslint:disable-line:no-any switch (cmdFullName) { case 'unlink': action = (input.length === 0 ? supi_1.unlink : supi_1.unlinkPkgs.bind(null, input)); break; case 'uninstall': action = supi_1.uninstall.bind(null, input); break; default: action = (input.length === 0 ? supi_1.install : supi_1.installPkgs.bind(null, input)); break; } const pkgPaths = chunks.length === 0 ? chunks[0] : Object.keys(pkgGraphResult.graph).sort(); yield Promise.all(pkgPaths.map((prefix) => limitInstallation(() => __awaiter(this, void 0, void 0, function* () { const hooks = opts.ignorePnpmfile ? {} : requireHooks_1.default(prefix, opts); try { if (opts.ignoredPackages && opts.ignoredPackages.has(prefix)) { return; } const localConfigs = yield memReadLocalConfigs(prefix); yield action(Object.assign({}, installOpts, localConfigs, { bin: path.join(prefix, 'node_modules', '.bin'), hooks, ignoreScripts: true, prefix, rawNpmConfig: Object.assign({}, installOpts.rawNpmConfig, localConfigs.rawNpmConfig), storeController })); result.passes++; } catch (err) { logger_1.default.info(err); if (!opts.bail) { result.fails.push({ error: err, message: err.message, prefix, }); return; } err['prefix'] = prefix; // tslint:disable-line:no-string-literal throw err; } })))); utils_1.stageLogger.debug('recursive_importing_done'); yield saveState(); } if (cmdFullName === 'rebuild' || !opts.ignoreScripts && (cmdFullName === 'install' || cmdFullName === 'update' || cmdFullName === 'unlink')) { const action = (cmdFullName !== 'rebuild' || input.length === 0 ? supi_1.rebuild : supi_1.rebuildPkgs.bind(null, input)); for (const chunk of chunks) { yield Promise.all(chunk.map((prefix) => limitInstallation(() => __awaiter(this, void 0, void 0, function* () { try { if (opts.ignoredPackages && opts.ignoredPackages.has(prefix)) { return; } const localConfigs = yield memReadLocalConfigs(prefix); yield action(Object.assign({}, installOpts, localConfigs, { bin: path.join(prefix, 'node_modules', '.bin'), prefix, rawNpmConfig: Object.assign({}, installOpts.rawNpmConfig, localConfigs.rawNpmConfig) })); result.passes++; } catch (err) { logger_1.default.info(err); if (!opts.bail) { result.fails.push({ error: err, message: err.message, prefix, }); return; } err['prefix'] = prefix; // tslint:disable-line:no-string-literal throw err; } })))); } } throwOnFail(result); return true; }); } exports.recursive = recursive; function sortPackages(pkgGraph) { const keys = Object.keys(pkgGraph); const graph = new Map(keys.map((pkgPath) => [pkgPath, pkgGraph[pkgPath].dependencies])); const graphSequencerResult = graphSequencer({ graph, groups: [keys], }); return graphSequencerResult.chunks; } function readLocalConfigs(prefix) { return __awaiter(this, void 0, void 0, function* () { try { const ini = yield readIniFile(path.join(prefix, '.npmrc')); return camelcaseKeys(ini); } catch (err) { if (err.code !== 'ENOENT') throw err; return {}; } }); } //# sourceMappingURL=index.js.map