Skip to content

Commit

Permalink
Merge branch 'torifat-feature/update'
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastian McKenzie committed Nov 11, 2016
2 parents 0c17423 + b6f3aec commit fcf6cfa
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 4 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"detect-indent": "^4.0.0",
"diff": "^2.2.1",
"ini": "^1.3.4",
"inquirer": "^1.2.2",
"invariant": "^2.2.0",
"is-builtin-module": "^1.0.0",
"is-ci": "^1.0.9",
Expand Down
2 changes: 1 addition & 1 deletion src/cli/aliases.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default ({
rm: 'remove',
show: 'info',
uninstall: 'remove',
update: 'upgrade',
udpate: 'upgrade',
verison: 'version',
view: 'info',
}: { [key: string]: ?string });
1 change: 1 addition & 0 deletions src/cli/commands/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import * as upgrade from './upgrade.js'; export {upgrade};
import * as version from './version.js'; export {version};
import * as versions from './versions.js'; export {versions};
import * as why from './why.js'; export {why};
import * as upgradeInteractive from './upgrade-interactive.js'; export {upgradeInteractive};

import buildUseless from './_useless.js';

Expand Down
156 changes: 156 additions & 0 deletions src/cli/commands/upgrade-interactive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/* @flow */

import type {Reporter} from '../../reporters/index.js';
import type Config from '../../config.js';
import inquirer from 'inquirer';
import repeat from 'repeating';
import {MessageError} from '../../errors.js';
import PackageRequest from '../../package-request.js';
import {Add} from './add.js';
import {Install} from './install.js';
import Lockfile from '../../lockfile/wrapper.js';

export const requireLockfile = true;

export function setFlags(commander: Object) {
// TODO: support some flags that install command has
commander.usage('update');
}

type Dependency = {
name: string,
current: string,
wanted: string,
latest: string,
hint: ?string
};

type InquirerResponses<K, T> = {[key: K]: Array<T>};

// Prompt user with Inquirer
async function prompt(choices): Promise<Array<Dependency>> {
const answers: InquirerResponses<'packages', Dependency> = await inquirer.prompt([{
name: 'packages',
type: 'checkbox',
message: 'Choose which packages to update.',
choices,
// Couldn't make it work, I guess I'm missing something here
// $FlowFixMe: https://github.com/facebook/flow/blob/f41e66e27b227235750792c34f5a80f38bde6320/lib/node.js#L1197
pageSize: process.stdout.rows - 2,
validate: (answer) => !!answer.length || 'You must choose at least one package.',
}]);
return answers.packages;
}

export async function run(
config: Config,
reporter: Reporter,
flags: Object,
args: Array<string>,
): Promise<void> {
const lockfile = await Lockfile.fromDirectory(config.cwd);
const install = new Install(flags, config, reporter, lockfile);
const [deps] = await install.fetchRequestFromCwd();

const allDeps = (await Promise.all(deps.map(async ({pattern, hint}): Promise<Dependency> => {
const locked = lockfile.getLocked(pattern);
if (!locked) {
throw new MessageError(reporter.lang('lockfileOutdated'));
}

const {name, version: current} = locked;
let latest = '';
let wanted = '';

const normalized = PackageRequest.normalizePattern(pattern);

if (PackageRequest.getExoticResolver(pattern) ||
PackageRequest.getExoticResolver(normalized.range)) {
latest = wanted = 'exotic';
} else {
({latest, wanted} = await config.registries[locked.registry].checkOutdated(config, name, normalized.range));
}

return ({name, current, wanted, latest, hint});
})));

const isDepOld = ({latest, current}) => latest !== current;
const isDepExpected = ({current, wanted}) => current === wanted;

const outdatedDeps = allDeps
.filter(isDepOld)
.sort((depA, depB) => {
if (isDepExpected(depA) && !isDepExpected(depB)) {
return 1;
}
return -1;
});

const getNameFromHint = (hint) => hint ? `${hint}Dependencies` : 'dependencies';

const maxLengthArr = {name: 0, current: 0, latest: 0};
outdatedDeps.forEach((dep) =>
['name', 'current', 'latest'].forEach((key) => {
maxLengthArr[key] = Math.max(maxLengthArr[key], dep[key].length);
}),
);

// Depends on maxLengthArr
const addPadding = (dep) => (key) =>
`${dep[key]}${repeat(' ', maxLengthArr[key] - dep[key].length)}`;

const colorizeName = ({current, wanted}) =>
(current === wanted) ? reporter.format.yellow : reporter.format.red;

const makeRow = (dep) => {
const padding = addPadding(dep);
const name = colorizeName(dep)(padding('name'));
const current = reporter.format.blue(padding('current'));
const latest = reporter.format.green(padding('latest'));
return `${name} ${current}${latest}`;
};

const groupedDeps = outdatedDeps.reduce((acc, dep) => {
const {hint, name, latest} = dep;
const key = getNameFromHint(hint);
const xs = acc[key] || [];
acc[key] = xs.concat({
name: makeRow(dep),
value: dep,
short: `${name}@${latest}`,
});
return acc;
}, {});

const flatten = (xs) => xs.reduce(
(ys, y) => ys.concat(Array.isArray(y) ? flatten(y) : y), [],
);

const choices = Object.keys(groupedDeps).map((key) => [
new inquirer.Separator(reporter.format.bold.underline.green(key)),
groupedDeps[key],
new inquirer.Separator(' '),
]);

const answers = await prompt(flatten(choices));

const getName = ({name}) => name;
const isHint = (x) => ({hint}) => hint === x;

await [null, 'dev', 'optional', 'peer'].reduce(async (promise, hint) => {
// Wait for previous promise to resolve
await promise;
// Reset dependency flags
flags.dev = hint === 'dev';
flags.peer = hint === 'peer';
flags.optional = hint === 'optional';

const deps = answers.filter(isHint(hint)).map(getName);
if (deps.length) {
reporter.info(reporter.lang('updateInstalling', getNameFromHint(hint)));
const add = new Add(deps, flags, config, reporter, lockfile);
return await add.init();
}
return Promise.resolve();
}, Promise.resolve());
}
2 changes: 1 addition & 1 deletion src/reporters/lang/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,11 +210,11 @@ const messages = {
cantRequestOffline: 'Can\'t make a request in offline mode',
requestManagerNotSetupHAR: 'RequestManager was not setup to capture HAR files',
requestError: 'Request $0 returned a $1',

tarballNotInNetworkOrCache: '$0: Tarball is not in network and can not be located in cache ($1)',
fetchBadHash: 'Bad hash. Expected $0 but got $1 ',
fetchErrorCorrupt: '$0. Mirror tarball appears to be corrupt. You can resolve this by running:\n\n $ rm -rf $1\n $ yarn install',
errorDecompressingTarball: '$0. Error decompressing $1, it appears to be corrupt.',
updateInstalling: 'Installing $0...',
};

export type LanguageKeys = $Keys<typeof messages>;
Expand Down
57 changes: 55 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1441,7 +1441,7 @@ [email protected]:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"

concat-stream@^1.4.6:
concat-stream@^1.4.6, concat-stream@^1.4.7:
version "1.5.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266"
dependencies:
Expand Down Expand Up @@ -2006,6 +2006,14 @@ extend@^3.0.0, extend@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4"

external-editor@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-1.1.0.tgz#c7fe15954b09af852b89aaec82a2707a0dc5597a"
dependencies:
extend "^3.0.0"
spawn-sync "^1.0.15"
temp "^0.8.3"

extglob@^0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
Expand Down Expand Up @@ -2612,6 +2620,25 @@ ini@^1.3.4, ini@~1.3.0:
version "1.3.4"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"

inquirer:
version "1.2.2"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-1.2.2.tgz#f725c1316f0020e7f3d538c8c5ad0c2732c1c451"
dependencies:
ansi-escapes "^1.1.0"
chalk "^1.0.0"
cli-cursor "^1.0.1"
cli-width "^2.0.0"
external-editor "^1.1.0"
figures "^1.3.5"
lodash "^4.3.0"
mute-stream "0.0.6"
pinkie-promise "^2.0.0"
run-async "^2.2.0"
rx "^4.1.0"
string-width "^1.0.1"
strip-ansi "^3.0.0"
through "^2.3.6"

inquirer@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e"
Expand Down Expand Up @@ -2766,6 +2793,10 @@ is-primitive@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"

is-promise@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"

is-property@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
Expand Down Expand Up @@ -3668,7 +3699,7 @@ multipipe@^0.1.2:
dependencies:
duplexer2 "0.0.2"

mute-stream@~0.0.4:
mute-stream@~0.0.4, [email protected]:
version "0.0.6"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.6.tgz#48962b19e169fd1dfc240b3f1e7317627bbc47db"

Expand Down Expand Up @@ -3903,6 +3934,10 @@ os-locale@^1.4.0:
dependencies:
lcid "^1.0.0"

os-shim@^0.1.2:
version "0.1.3"
resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917"

os-tmpdir@^1.0.0, os-tmpdir@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
Expand Down Expand Up @@ -4431,10 +4466,21 @@ run-async@^0.1.0:
dependencies:
once "^1.3.0"

run-async@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.2.0.tgz#8783abd83c7bb86f41ee0602fc82404b3bd6e8b9"
dependencies:
is-promise "^2.1.0"
pinkie-promise "^2.0.0"

rx-lite@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"

rx@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"

sane@~1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/sane/-/sane-1.4.1.tgz#88f763d74040f5f0c256b6163db399bf110ac715"
Expand Down Expand Up @@ -4560,6 +4606,13 @@ sparkles@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3"

spawn-sync@^1.0.15:
version "1.0.15"
resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476"
dependencies:
concat-stream "^1.4.7"
os-shim "^0.1.2"

spdx-correct@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
Expand Down

0 comments on commit fcf6cfa

Please sign in to comment.