Skip to content

Commit

Permalink
fix(@angular/cli): Organize the help output
Browse files Browse the repository at this point in the history
  • Loading branch information
Brocco authored and hansl committed Apr 4, 2018
1 parent 9d84529 commit 24bcc19
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 78 deletions.
7 changes: 4 additions & 3 deletions packages/@angular/cli/commands/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ export default class AddCommand extends SchematicCommand {
options: Option[] = [];

private async _parseSchematicOptions(collectionName: string): Promise<any> {
const availableOptions: Option[] = await this.getOptions({
const schematicOptions = await this.getOptions({
schematicName: 'ng-add',
collectionName,
});

const options = this.options.concat(availableOptions || []);
const options = this.options.concat(schematicOptions.options);
const args = schematicOptions.arguments.map(arg => arg.name);

return parseOptions(this._rawArgs, options, [], this.argStrategy);
return parseOptions(this._rawArgs, options, args, this.argStrategy);
}

validate(options: any) {
Expand Down
17 changes: 13 additions & 4 deletions packages/@angular/cli/commands/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ export default class GenerateCommand extends SchematicCommand {
const [collectionName, schematicName] = this.parseSchematicInfo(options);

if (!!schematicName) {
const availableOptions: Option[] = await this.getOptions({
const schematicOptions = await this.getOptions({
schematicName,
collectionName,
});
this.options = this.options.concat( availableOptions || []);
this.options = this.options.concat(schematicOptions.options);
this.arguments = this.arguments.concat(schematicOptions.arguments.map(a => a.name));
}
}

Expand Down Expand Up @@ -83,8 +84,16 @@ export default class GenerateCommand extends SchematicCommand {
}

public printHelp(options: any) {
if (options.schematic) {
super.printHelp(options);
const schematicName = options._[0];
if (schematicName) {
const argDisplay = this.arguments && this.arguments.length > 0
? ' ' + this.arguments.filter(a => a !== 'schematic').map(a => `<${a}>`).join(' ')
: '';
const optionsDisplay = this.options && this.options.length > 0
? ' [options]'
: '';
this.logger.info(`usage: ng generate ${schematicName}${argDisplay}${optionsDisplay}`);
this.printHelpOptions(options);
} else {
this.printHelpUsage(this.name, this.arguments, this.options);
const engineHost = getEngineHost();
Expand Down
11 changes: 5 additions & 6 deletions packages/@angular/cli/commands/new.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default class NewCommand extends SchematicCommand {
'Creates a new directory and a new Angular app.';
public static aliases = ['n'];
public scope = CommandScope.outsideProject;
public arguments: string[] = [];
public options: Option[] = [
...this.coreOptions,
{
Expand Down Expand Up @@ -41,12 +42,10 @@ export default class NewCommand extends SchematicCommand {
schematicName,
collectionName
})
.then((availableOptions: Option[]) => {
// if (availableOptions) {
// availableOptions = availableOptions.filter(opt => opt.name !== 'name');
// }

this.options = this.options.concat( availableOptions || []);
.then((schematicOptions) => {
this.options = this.options.concat(schematicOptions.options);
const args = schematicOptions.arguments.map(arg => arg.name);
this.arguments = this.arguments.concat(args);
});
}

Expand Down
7 changes: 4 additions & 3 deletions packages/@angular/cli/commands/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default class UpdateCommand extends SchematicCommand {
public readonly description = 'Updates your application and its dependencies.';
public static aliases: string[] = [];
public readonly scope = CommandScope.inProject;
public readonly arguments: string[] = [ 'packages' ];
public arguments: string[] = [ 'packages' ];
public options: Option[] = [
...this.coreOptions,
];
Expand All @@ -29,11 +29,12 @@ export default class UpdateCommand extends SchematicCommand {
super.initialize(options);
this.initialized = true;

const availableOptions: Option[] = await this.getOptions({
const schematicOptions = await this.getOptions({
schematicName: this.schematicName,
collectionName: this.collectionName,
});
this.options = this.options.concat( availableOptions || []);
this.options = this.options.concat(schematicOptions.options);
this.arguments = this.arguments.concat(schematicOptions.arguments.map(a => a.name));
}

public async run(options: UpdateOptions) {
Expand Down
3 changes: 2 additions & 1 deletion packages/@angular/cli/models/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,12 @@ export abstract class Command {
this.logger.info(`options:`);
this.options
.filter(o => !o.hidden)
.sort((a, b) => a.name >= b.name ? 1 : -1)
.forEach(o => {
const aliases = o.aliases && o.aliases.length > 0
? '(' + o.aliases.map(a => `-${a}`).join(' ') + ')'
: '';
this.logger.info(` ${cyan(o.name)} ${aliases}`);
this.logger.info(` ${cyan('--' + o.name)} ${aliases}`);
this.logger.info(` ${o.description}`);
});
}
Expand Down
91 changes: 30 additions & 61 deletions packages/@angular/cli/models/schematic-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { DryRunEvent, UnsuccessfulWorkflowExecution } from '@angular-devkit/sche
import { getCollection, getSchematic } from '../utilities/schematics';
import { take } from 'rxjs/operators';
import { WorkspaceLoader } from '../models/workspace-loader';
import chalk from 'chalk';

export interface CoreSchematicOptions {
dryRun: boolean;
Expand All @@ -28,20 +27,11 @@ export interface GetOptionsOptions {
schematicName: string;
}

export interface GetHelpOutputOptions {
collectionName: string;
schematicName: string;
nonSchematicOptions: any[];
export interface GetOptionsResult {
options: Option[];
arguments: Option[];
}

const hiddenOptions = [
'name',
'path',
'source-dir',
'app-root',
'link-cli',
];

export abstract class SchematicCommand extends Command {
readonly options: Option[] = [];
private _host = new NodeJsSyncHost();
Expand Down Expand Up @@ -185,7 +175,7 @@ export abstract class SchematicCommand extends Command {
return opts;
}

protected getOptions(options: GetOptionsOptions): Promise<Option[] | null> {
protected getOptions(options: GetOptionsOptions): Promise<GetOptionsResult> {
// TODO: get default collectionName
const collectionName = options.collectionName || '@schematics/angular';

Expand All @@ -195,7 +185,10 @@ export abstract class SchematicCommand extends Command {
this._deAliasedName = schematic.description.name;

if (!schematic.description.schemaJson) {
return Promise.resolve(null);
return Promise.resolve({
options: [],
arguments: []
});
}

const properties = schematic.description.schemaJson.properties;
Expand Down Expand Up @@ -228,7 +221,6 @@ export abstract class SchematicCommand extends Command {
if (opt.aliases) {
aliases = [...aliases, ...opt.aliases];
}

const schematicDefault = opt.default;

return {
Expand All @@ -243,54 +235,31 @@ export abstract class SchematicCommand extends Command {
})
.filter(x => x);

return Promise.resolve(availableOptions);
}

protected getHelpOutput(
{ schematicName, collectionName, nonSchematicOptions }: GetHelpOutputOptions):
Promise<string[]> {
const schematicOptions = availableOptions
.filter(opt => opt.$default === undefined || opt.$default.$source !== 'argv');

const SchematicGetOptionsTask = require('./schematic-get-options').default;
const getOptionsTask = new SchematicGetOptionsTask({
ui: this.ui,
project: this.project
});
return Promise.all([getOptionsTask.run({
schematicName: schematicName,
collectionName: collectionName,
}), nonSchematicOptions])
.then(([availableOptions, nonSchematicOptions]: [Option[], any[]]) => {
const output: string[] = [];
[...(nonSchematicOptions || []), ...availableOptions || []]
.filter(opt => hiddenOptions.indexOf(opt.name) === -1)
.forEach(opt => {
let text = chalk.cyan(` --${opt.name}`);
if (opt.schematicType) {
text += chalk.cyan(` (${opt.schematicType})`);
}
if (opt.schematicDefault) {
text += chalk.cyan(` (Default: ${opt.schematicDefault})`);
}
if (opt.description) {
text += ` ${opt.description}`;
}
output.push(text);
if (opt.aliases && opt.aliases.length > 0) {
const aliasText = opt.aliases.reduce(
(acc: string, curr: string) => {
return acc + ` -${curr}`;
},
'');
output.push(chalk.grey(` aliases: ${aliasText}`));
}
});
if (availableOptions === null) {
output.push(chalk.green('This schematic accept additional options, but did not provide '
+ 'documentation.'));
const schematicArguments = availableOptions
.filter(opt => opt.$default !== undefined && opt.$default.$source === 'argv')
.sort((a, b) => {
if (a.$default.index === undefined) {
return 1;
}
if (b.$default.index === undefined) {
return -1;
}
if (a.$default.index == b.$default.index) {
return 0;
} else if (a.$default.index > b.$default.index) {
return 1;
} else {
return -1;
}

return output;
});

return Promise.resolve({
options: schematicOptions,
arguments: schematicArguments
});
}

private _loadWorkspace() {
Expand Down
86 changes: 86 additions & 0 deletions tests/e2e/tests/generate/help-output.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {join} from 'path';
import {ng} from '../../utils/process';
import {writeMultipleFiles, createDir} from '../../utils/fs';


export default function() {
// setup temp collection
const genRoot = join('node_modules/fake-schematics/');

return Promise.resolve()
.then(() => createDir(genRoot))
.then(() => writeMultipleFiles({
[join(genRoot, 'package.json')]: `
{
"schematics": "./collection.json"
}`,
[join(genRoot, 'collection.json')]: `
{
"schematics": {
"fake": {
"factory": "./fake",
"description": "Fake schematic",
"schema": "./fake-schema.json"
}
}
}`,
[join(genRoot, 'fake-schema.json')]: `
{
"id": "FakeSchema",
"title": "Fake Schema",
"type": "object",
"properties": {
"b": {
"type": "string",
"description": "b.",
"$default": {
"$source": "argv",
"index": 1
}
},
"a": {
"type": "string",
"description": "a.",
"$default": {
"$source": "argv",
"index": 0
}
},
"optC": {
"type": "string",
"description": "optC"
},
"optA": {
"type": "string",
"description": "optA"
},
"optB": {
"type": "string",
"description": "optB"
}
},
"required": []
}`,
[join(genRoot, 'fake.js')]: `
function def(options) {
return (host, context) => {
return host;
};
}
exports.default = def;
`},
))
.then(() => ng('generate', 'fake-schematics:fake', '--help'))
.then(({stdout}) => {
console.warn('stdout start');
console.error(stdout);
console.warn('stdout end');
if (!/ng generate fake-schematics:fake <a> <b> \[options\]/.test(stdout)) {
throw new Error('Help signature is wrong.');
}
if (!/opt-a[\s\S]*opt-b[\s\S]*opt-c/.test(stdout)) {
throw new Error('Help signature options are incorrect.');
}
});

}

0 comments on commit 24bcc19

Please sign in to comment.