Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring cli index #2613

Merged
merged 2 commits into from
Feb 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "test_run_custom_script",
"version": "1.0.0",
"license": "UNLICENSED",
"scripts": {
"custom-script": "print() { echo \"A message from custom script with args \"$@\"\"; }; print "
}
}
5 changes: 5 additions & 0 deletions __tests__/fixtures/index/run-version/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "test_add",
"version": "1.0.0",
"license": "UNLICENSED"
}
150 changes: 123 additions & 27 deletions __tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import NoopReporter from '../src/reporters/base-reporter.js';
import makeTemp from './_temp';
import * as fs from '../src/util/fs.js';
const pkg = require('../package.json');

const path = require('path');
const exec = require('child_process').exec;
Expand Down Expand Up @@ -37,40 +38,68 @@ Promise<Array<?string>> {
});
}

test.concurrent('should add package', async () => {
const stdout = await execCommand('add', ['left-pad'], 'run-add', true);
function expectAddSuccessfullOutput(stdout, pkg) {
const lastLines = stdout.slice(stdout.length - 4);
expect(lastLines[0]).toEqual('success Saved lockfile.');
expect(lastLines[1]).toEqual('success Saved 1 new dependency.');
expect(lastLines[2]).toMatch(/left-pad/);
expect(lastLines[3]).toMatch(/^Done/);
expect(lastLines[2]).toContain(pkg);
expect(lastLines[3]).toContain('Done');
}

function expectAddSuccessfullOutputWithNoLockFile(stdout, pkg) {
const lastLines = stdout.slice(stdout.length - 4);
expect(lastLines[1]).toEqual('success Saved 1 new dependency.');
expect(lastLines[2]).toContain(pkg);
expect(lastLines[3]).toContain('Done');
}

function expectRunOutput(stdout) {
const lastLines = stdout.slice(stdout.length - 2);
expect(lastLines[0]).toMatch(/A message from custom script/);
expect(lastLines[1]).toMatch(/^Done/);
}

function expectHelpOutput(stdout) {
expect(stdout[0]).toEqual('Usage: yarn [command] [flags]');
const lastLines = stdout.slice(stdout.length - 2);
expect(lastLines[0]).toEqual('Run `yarn help COMMAND` for more information on specific commands.');
expect(lastLines[1]).toEqual('Visit https://yarnpkg.com/en/docs/cli/ to learn more about Yarn.');
}

function expectHelpOutputAsSubcommand(stdout) {
expect(stdout[0]).toEqual('Usage: yarn add [packages ...] [flags]');
expect(stdout[stdout.length - 1])
.toEqual('Visit https://yarnpkg.com/en/docs/cli/add for documentation about this command.');
}

function expectAnErrorMessage(command: Promise<Array<?string>>, error: string) : Promise<void> {
return command.catch((reason) =>
expect(reason.message).toContain(error),
);
}

function expectInstallOutput(stdout) {
expect(stdout[0]).toEqual(`yarn install v${pkg.version}`);
}

test.concurrent('should add package', async () => {
const stdout = await execCommand('add', ['left-pad'], 'run-add', true);
expectAddSuccessfullOutput(stdout, 'left-pad');
});

test.concurrent('should add package with no-lockfile option', async () => {
const stdout = await execCommand('add', ['repeating', '--no-lockfile'], 'run-add-option', true);
const lastLines = stdout.slice(stdout.length - 4);
expect(lastLines[0]).not.toMatch(/Saved lockfile/);
expect(lastLines[1]).toEqual('success Saved 1 new dependency.');
expect(lastLines[2]).toMatch(/repeating/);
expect(lastLines[3]).toMatch(/^Done/);
expectAddSuccessfullOutputWithNoLockFile(stdout, 'repeating');
});

test.concurrent('should add package with no-lockfile option in front', async () => {
const stdout = await execCommand('add', ['--no-lockfile', 'split-lines'], 'run-add-option-in-front', true);
const lastLines = stdout.slice(stdout.length - 4);
expect(lastLines[0]).not.toMatch(/Saved lockfile/);
expect(lastLines[1]).toEqual('success Saved 1 new dependency.');
expect(lastLines[2]).toMatch(/split-lines/);
expect(lastLines[3]).toMatch(/^Done/);
expectAddSuccessfullOutputWithNoLockFile(stdout, 'split-lines');
});

test.concurrent('should add lockfile package', async () => {
const stdout = await execCommand('add', ['lockfile'], 'run-add-lockfile', true);
const lastLines = stdout.slice(stdout.length - 4);
expect(lastLines[0]).toEqual('success Saved lockfile.');
expect(lastLines[1]).toEqual('success Saved 1 new dependency.');
expect(lastLines[2]).toMatch(/lockfile/);
expect(lastLines[3]).toMatch(/^Done/);
expectAddSuccessfullOutput(stdout, 'lockfile');
});

test.concurrent('should add progress package globally', async () => {
Expand All @@ -85,24 +114,91 @@ test.concurrent('should add progress package globally', async () => {

test.concurrent('should run custom script', async () => {
const stdout = await execCommand('run', ['custom-script'], 'run-custom-script');
const lastLines = stdout.slice(stdout.length - 2);
expect(lastLines[0]).toMatch(/A message from custom script/);
expect(lastLines[1]).toMatch(/^Done/);
expectRunOutput(stdout);
});

test.concurrent('should run custom script without run command', async () => {
const stdout = await execCommand('custom-script', [], 'run-custom-script');
const lastLines = stdout.slice(stdout.length - 2);
expect(lastLines[0]).toMatch(/A message from custom script/);
expect(lastLines[1]).toMatch(/^Done/);
expectRunOutput(stdout);
});

test.concurrent('should run help command', async () => {
const stdout = await execCommand('help', [], 'run-help');
expect(stdout[0]).toEqual('Usage: yarn [command] [flags]');
expectHelpOutput(stdout);
});

test.concurrent('should run help command with --help', async () => {
const stdout = await execCommand('--help', [], 'run-help');
expectHelpOutput(stdout);
});

test.concurrent('should run help command with -h', async () => {
const stdout = await execCommand('-h', [], 'run-help');
expectHelpOutput(stdout);
});

test.concurrent('should run add command with help option', async () => {
const stdout = await execCommand('add', ['--help'], 'run-help');
expect(stdout[0]).toEqual('Usage: yarn add [packages ...] [flags]');
expectHelpOutputAsSubcommand(stdout);
});

test.concurrent('should run add command with h option', async () => {
const stdout = await execCommand('add', ['-h'], 'run-help');
expectHelpOutputAsSubcommand(stdout);
});

test.concurrent('should run help command with add option', async () => {
const stdout = await execCommand('help', ['add'], 'run-help');
expectHelpOutputAsSubcommand(stdout);
});

test.concurrent('should run --help command with add option', async () => {
const stdout = await execCommand('--help', ['add'], 'run-help');
expectHelpOutputAsSubcommand(stdout);
});

test.concurrent('should run -h command with add option', async () => {
const stdout = await execCommand('-h', ['add'], 'run-help');
expectHelpOutputAsSubcommand(stdout);
});

test.concurrent('should run version command', async () => {
await expectAnErrorMessage(
execCommand('version', [], 'run-version'),
'Can\'t answer a question unless a user TTY',
);
});

test.concurrent('should run --version command', async () => {
const stdout = await execCommand('--version', [], 'run-version');
expect(stdout[0]).toEqual(pkg.version);
});

test.concurrent('should install if no args', async () => {
const stdout = await execCommand('', [], 'run-add', true);
expectInstallOutput(stdout);
});

test.concurrent('should install if first arg looks like a flag', async () => {
const stdout = await execCommand('--offline', [], 'run-add', true);
expectInstallOutput(stdout);
});

test.concurrent('should interpolate aliases', async () => {
await expectAnErrorMessage(
execCommand('i', [], 'run-add', true),
'Did you mean `yarn install`?',
);
});

test.concurrent('should run help of run command if --help is before --', async () => {
const stdout = await execCommand('run', ['custom-script', '--help', '--'], 'run-custom-script-with-arguments');
expect(stdout[0]).toEqual('Usage: yarn [command] [flags]');
expect(stdout[stdout.length - 1])
.toEqual('Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.');
});

test.concurrent('should run help of custom-script if --help is after --', async () => {
const stdout = await execCommand('run', ['custom-script', '--', '--help'], 'run-custom-script-with-arguments');
expect(stdout[stdout.length - 2]).toEqual('A message from custom script with args --help');
});
40 changes: 40 additions & 0 deletions src/cli/commands/help.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* @flow */

import * as commands from './index.js';
import * as constants from '../../constants.js';
import type {Reporter} from '../../reporters/index.js';
import type Config from '../../config.js';
import {sortAlpha, hyphenate} from '../../util/misc.js';
const chalk = require('chalk');

export function run(
config: Config,
reporter: Reporter,
commander: Object,
args: Array<string>,
): Promise<void> {
const getDocsLink = (name) => `${constants.YARN_DOCS}${name || ''}`;
const getDocsInfo = (name) => 'Visit ' + chalk.bold(getDocsLink(name)) + ' for documentation about this command.';

if (args.length) {
const helpCommand = hyphenate(args[0]);
if (commands[helpCommand]) {
commander.on('--help', () => console.log(' ' + getDocsInfo(helpCommand) + '\n'));
}
} else {
commander.on('--help', () => {
console.log(' Commands:\n');
for (const name of Object.keys(commands).sort(sortAlpha)) {
if (commands[name].useless) {
continue;
}

console.log(` - ${hyphenate(name)}`);
}
console.log('\n Run `' + chalk.bold('yarn help COMMAND') + '` for more information on specific commands.');
console.log(' Visit ' + chalk.bold(getDocsLink()) + ' to learn more about Yarn.\n');
});
}
commander.help();
return Promise.resolve();
}
1 change: 1 addition & 0 deletions src/cli/commands/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as clean from './clean.js'; export {clean};
import * as config from './config.js'; export {config};
import * as generateLockEntry from './generate-lock-entry.js'; export {generateLockEntry};
import * as global from './global.js'; export {global};
import * as help from './help.js'; export {help};
import * as import_ from './import.js'; export {import_ as import};
import * as info from './info.js'; export {info};
import * as init from './init.js'; export {init};
Expand Down
29 changes: 5 additions & 24 deletions src/cli/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
/* @flow */

import {ConsoleReporter, JSONReporter} from '../reporters/index.js';
import {sortAlpha} from '../util/misc.js';
import {registries, registryNames} from '../registries/index.js';
import * as commands from './commands/index.js';
import * as constants from '../constants.js';
import * as network from '../util/network.js';
import {MessageError} from '../errors.js';
import aliases from './aliases.js';
import Config from '../config.js';
import {hyphenate, camelCase} from '../util/misc.js';
import {camelCase} from '../util/misc.js';

const chalk = require('chalk');
const commander = require('commander');
Expand Down Expand Up @@ -99,27 +98,8 @@ const getDocsLink = (name) => `${constants.YARN_DOCS}${name || ''}`;
const getDocsInfo = (name) => 'Visit ' + chalk.bold(getDocsLink(name)) + ' for documentation about this command.';

//
if (commandName === 'help' || commandName === '--help' || commandName === '-h') {
if (commandName === '--help' || commandName === '-h') {
commandName = 'help';
if (args.length) {
const helpCommand = hyphenate(args[0]);
if (commands[helpCommand]) {
commander.on('--help', () => console.log(' ' + getDocsInfo(helpCommand) + '\n'));
}
} else {
commander.on('--help', () => {
console.log(' Commands:\n');
for (const name of Object.keys(commands).sort(sortAlpha)) {
if (commands[name].useless) {
continue;
}

console.log(` - ${hyphenate(name)}`);
}
console.log('\n Run `' + chalk.bold('yarn help COMMAND') + '` for more information on specific commands.');
console.log(' Visit ' + chalk.bold(getDocsLink()) + ' to learn more about Yarn.\n');
});
}
}

// if no args or command name looks like a flag then default to `install`
Expand Down Expand Up @@ -160,7 +140,7 @@ if (command && typeof command.setFlags === 'function') {
command.setFlags(commander);
}

if (commandName === 'help' || args.indexOf('--help') >= 0 || args.indexOf('-h') >= 0) {
if (args.indexOf('--help') >= 0 || args.indexOf('-h') >= 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why commandName is not used anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if now covers only the 2nd case, before it was covering the the 1st, 2nd and the 3rd.

const examples: Array<string> = (command && command.examples) || [];
if (examples.length) {
commander.on('--help', () => {
Expand All @@ -171,6 +151,7 @@ if (commandName === 'help' || args.indexOf('--help') >= 0 || args.indexOf('-h')
console.log();
});
}
commander.on('--help', () => console.log(' ' + getDocsInfo(commandName) + '\n'));

commander.parse(startArgs.concat(args));
commander.help();
Expand Down Expand Up @@ -217,7 +198,7 @@ if (typeof command.hasWrapper === 'function') {
if (commander.json) {
outputWrapper = false;
}
if (outputWrapper) {
if (outputWrapper && commandName !== 'help') {
reporter.header(commandName, pkg);
}

Expand Down