Skip to content

Commit 715cc91

Browse files
committed
feat(cli): improve cli help/version/commands options
1 parent afe72ad commit 715cc91

File tree

5 files changed

+207
-68
lines changed

5 files changed

+207
-68
lines changed

packages/cli/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,44 @@ Options:
9090
--skip-install # Do not automatically install dependencies Default: false
9191
```
9292

93+
5. To list available commands
94+
95+
`lb4 --commands` (or `lb4 -l`)
96+
97+
```sh
98+
Available commands:
99+
lb4 app
100+
lb4 extension
101+
lb4 controller
102+
lb4 example
103+
```
104+
105+
Please note `lb4 --help` also prints out available commands.
106+
107+
6. To print out version information
108+
109+
`lb4 --version` (or `lb4 -v`)
110+
111+
```sh
112+
@loopback/cli version: 0.8.0
113+
114+
@loopback/* dependencies:
115+
- @loopback/authentication: ^0.8.0
116+
- @loopback/boot: ^0.8.0
117+
- @loopback/build: ^0.5.0
118+
- @loopback/context: ^0.8.0
119+
- @loopback/core: ^0.6.0
120+
- @loopback/metadata: ^0.6.0
121+
- @loopback/openapi-spec-builder: ^0.5.0
122+
- @loopback/openapi-v3-types: ^0.4.0
123+
- @loopback/openapi-v3: ^0.7.0
124+
- @loopback/repository-json-schema: ^0.6.0
125+
- @loopback/repository: ^0.8.0
126+
- @loopback/rest: ^0.7.0
127+
- @loopback/testlab: ^0.7.0
128+
- @loopback/docs: ^0.5.0
129+
```
130+
93131
## Contributions
94132

95133
- [Guidelines](https://github.com/strongloop/loopback-next/blob/master/docs/CONTRIBUTING.md)

packages/cli/bin/cli.js

Lines changed: 100 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -12,64 +12,117 @@ const debug = require('../lib/debug')();
1212
const minimist = require('minimist');
1313
const path = require('path');
1414
const yeoman = require('yeoman-environment');
15+
const PREFIX = 'loopback4:';
1516

16-
const opts = minimist(process.argv.slice(2), {
17-
alias: {
18-
version: 'v',
19-
commands: 'l',
20-
},
21-
});
22-
23-
if (opts.version) {
24-
const ver = require('../package.json').version;
25-
console.log('Version: %s', ver);
26-
return;
17+
/**
18+
* Parse arguments and run corresponding command
19+
* @param env Yeoman env
20+
* @param {*} opts Command options
21+
* @param log Log function
22+
* @param dryRun flag for dryRun (for testing)
23+
*/
24+
function runCommand(env, opts, log, dryRun) {
25+
const args = opts._;
26+
const originalCommand = args.shift();
27+
let command = PREFIX + (originalCommand || 'app');
28+
const supportedCommands = env.getGeneratorsMeta();
29+
if (!(command in supportedCommands)) {
30+
command = PREFIX + 'app';
31+
args.unshift(originalCommand);
32+
args.unshift(command);
33+
} else {
34+
args.unshift(command);
35+
}
36+
debug('invoking generator', args);
37+
// `yo` is adding flags converted to CamelCase
38+
const options = camelCaseKeys(opts, {exclude: ['--', /^\w$/, 'argv']});
39+
Object.assign(options, opts);
40+
debug('env.run %j %j', args, options);
41+
if (!dryRun) {
42+
env.run(args, options);
43+
}
44+
// list generators
45+
if (opts.help && !originalCommand) {
46+
printCommands(env, log);
47+
}
2748
}
2849

29-
var env = yeoman.createEnv();
50+
/**
51+
* Set up yeoman generators
52+
*/
53+
function setupGenerators() {
54+
var env = yeoman.createEnv();
55+
env.register(path.join(__dirname, '../generators/app'), PREFIX + 'app');
56+
env.register(
57+
path.join(__dirname, '../generators/extension'),
58+
PREFIX + 'extension'
59+
);
60+
env.register(
61+
path.join(__dirname, '../generators/controller'),
62+
PREFIX + 'controller'
63+
);
64+
env.register(
65+
path.join(__dirname, '../generators/example'),
66+
PREFIX + 'example'
67+
);
68+
return env;
69+
}
3070

31-
env.register(path.join(__dirname, '../generators/app'), 'loopback4:app');
32-
env.register(
33-
path.join(__dirname, '../generators/extension'),
34-
'loopback4:extension'
35-
);
36-
env.register(
37-
path.join(__dirname, '../generators/controller'),
38-
'loopback4:controller'
39-
);
40-
env.register(
41-
path.join(__dirname, '../generators/example'),
42-
'loopback4:example'
43-
);
71+
/**
72+
* Print @loopback/* versions
73+
*/
74+
function printVersions(log) {
75+
const pkg = require('../package.json');
76+
const ver = pkg.version;
77+
log('@loopback/cli version: %s', ver);
78+
const deps = pkg.config.templateDependencies;
79+
log('\n@loopback/* dependencies:');
80+
for (const d in deps) {
81+
if (d.startsWith('@loopback/') && d !== '@loopback/cli') {
82+
log(' - %s: %s', d, deps[d]);
83+
}
84+
}
85+
}
4486

45-
// list generators
46-
if (opts.commands) {
47-
console.log('Available commands: ');
87+
/**
88+
* Print a list of available commands
89+
* @param {*} env Yeoman env
90+
* @param log Log function
91+
*/
92+
function printCommands(env, log) {
93+
log('Available commands: ');
4894
var list = Object.keys(env.getGeneratorsMeta())
4995
.filter(name => /^loopback4:/.test(name))
5096
.map(name => name.replace(/^loopback4:/, ' lb4 '));
51-
console.log(list.join('\n'));
52-
return;
97+
log(list.join('\n'));
5398
}
5499

55-
const args = opts._;
56-
const originalCommand = args.shift();
57-
let command = 'loopback4:' + (originalCommand || 'app');
58-
const supportedCommands = env.getGeneratorsMeta();
100+
function main(opts, log, dryRun) {
101+
log = log || console.log;
102+
if (opts.version) {
103+
printVersions(log);
104+
return;
105+
}
59106

60-
if (!(command in supportedCommands)) {
61-
command = 'loopback4:app';
62-
args.unshift(originalCommand);
63-
args.unshift(command);
64-
} else {
65-
args.unshift(command);
66-
}
107+
var env = setupGenerators();
67108

68-
debug('invoking generator', args);
109+
// list generators
110+
if (opts.commands) {
111+
printCommands(env, log);
112+
return;
113+
}
69114

70-
// `yo` is adding flags converted to CamelCase
71-
const options = camelCaseKeys(opts, {exclude: ['--', /^\w$/, 'argv']});
72-
Object.assign(options, opts);
115+
runCommand(env, opts, log, dryRun);
116+
}
117+
118+
module.exports = main;
73119

74-
debug('env.run %j %j', args, options);
75-
env.run(args, options);
120+
if (require.main === module) {
121+
const opts = minimist(process.argv.slice(2), {
122+
alias: {
123+
version: 'v', // --version or -v: print versions
124+
commands: 'l', // --commands or -l: print commands
125+
},
126+
});
127+
main(opts);
128+
}

packages/cli/package.json

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -65,38 +65,34 @@
6565
},
6666
"config": {
6767
"templateDependencies": {
68-
"//1": "This file contains dependency/version used by project templates",
69-
"//2": "It can be updated by `npm run update-template-deps` under `loopback-next`",
70-
"@loopback/context": "^0.8.0",
71-
"@loopback/boot": "^0.8.0",
72-
"@loopback/core": "^0.6.0",
73-
"@loopback/rest": "^0.7.0",
74-
"@loopback/openapi-v3": "^0.7.0",
75-
"@loopback/build": "^0.5.0",
76-
"@loopback/testlab": "^0.7.0",
7768
"@types/mocha": "^5.0.0",
7869
"@types/node": "^8.10.4",
79-
"mocha": "^5.0.5",
80-
"source-map-support": "^0.5.4",
81-
"prettier": "^1.11.1",
82-
"tslint": "^5.9.1",
83-
"typescript": "^2.6.2",
8470
"cross-spawn": "^6.0.5",
8571
"debug": "^3.1.0",
86-
"nyc": "^11.6.0",
72+
"fs-extra": "^5.0.0",
73+
"mocha": "^5.1.1",
74+
"nyc": "^11.7.1",
75+
"prettier": "^1.12.1",
76+
"rimraf": "^2.6.2",
77+
"source-map-support": "^0.5.4",
8778
"strong-docs": "^1.10.2",
79+
"tslint": "^5.9.1",
80+
"typescript": "^2.8.1",
8881
"@loopback/authentication": "^0.8.0",
82+
"@loopback/boot": "^0.8.0",
83+
"@loopback/build": "^0.5.0",
8984
"@loopback/cli": "^0.8.0",
85+
"@loopback/context": "^0.8.0",
86+
"@loopback/core": "^0.6.0",
9087
"@loopback/metadata": "^0.6.0",
9188
"@loopback/openapi-spec-builder": "^0.5.0",
92-
"@loopback/openapi-spec": "^0.3.0",
9389
"@loopback/openapi-v3-types": "^0.4.0",
90+
"@loopback/openapi-v3": "^0.7.0",
9491
"@loopback/repository-json-schema": "^0.6.0",
9592
"@loopback/repository": "^0.8.0",
96-
"@loopback/openapi-v2": "^0.3.0",
97-
"@loopback/docs": "^0.5.0",
98-
"fs-extra": "^5.0.0",
99-
"rimraf": "^2.6.2"
93+
"@loopback/rest": "^0.7.0",
94+
"@loopback/testlab": "^0.7.0",
95+
"@loopback/docs": "^0.5.0"
10096
}
10197
},
10298
"copyright.owner": "IBM Corp.",

packages/cli/test/acceptance/app-run.acceptance.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const build = require('@loopback/build');
1414
describe('app-generator (SLOW)', function() {
1515
const generator = path.join(__dirname, '../../generators/app');
1616
const rootDir = path.join(__dirname, '../../../..');
17-
const sandbox = path.join(__dirname, '../../sandbox/sandbox-app');
17+
const sandbox = path.join(rootDir, 'sandbox/sandbox-app');
1818
const cwd = process.cwd();
1919
const appName = '@loopback/sandbox-app';
2020
const props = {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright IBM Corp. 2017,2018. All Rights Reserved.
2+
// Node module: @loopback/cli
3+
// This file is licensed under the MIT License.
4+
// License text available at https://opensource.org/licenses/MIT
5+
6+
'use strict';
7+
8+
const expect = require('@loopback/testlab').expect;
9+
const util = require('util');
10+
const main = require('../../../bin/cli');
11+
12+
function getLog(buffer) {
13+
buffer = buffer || [];
14+
return function(format, ...params) {
15+
buffer.push(util.format(format, ...params));
16+
return buffer;
17+
};
18+
}
19+
20+
describe('cli', () => {
21+
it('lists available commands', () => {
22+
const entries = [];
23+
main({commands: true}, getLog(entries));
24+
expect(entries).to.eql([
25+
'Available commands: ',
26+
' lb4 app\n lb4 extension\n lb4 controller\n lb4 example',
27+
]);
28+
});
29+
30+
it('lists versions', () => {
31+
const entries = [];
32+
main({version: true}, getLog(entries));
33+
const logs = entries.join('');
34+
expect(logs).to.match(/@loopback\/cli version\:/);
35+
expect(logs).to.match(/@loopback\/\* dependencies:/);
36+
});
37+
38+
it('prints commands with --help', () => {
39+
const entries = [];
40+
main({help: true, _: []}, getLog(entries));
41+
expect(entries).to.containEql('Available commands: ');
42+
expect(entries).to.containEql(
43+
' lb4 app\n lb4 extension\n lb4 controller\n lb4 example'
44+
);
45+
});
46+
47+
it('does not print commands with --help for a given command', () => {
48+
const entries = [];
49+
main({help: true, _: ['app']}, getLog(entries), true);
50+
expect(entries).to.not.containEql('Available commands: ');
51+
});
52+
});

0 commit comments

Comments
 (0)