diff --git a/packages/tao/index.ts b/packages/tao/index.ts index 56ce4759b2074..a6c3745cc9a08 100644 --- a/packages/tao/index.ts +++ b/packages/tao/index.ts @@ -4,41 +4,59 @@ import './src/compat/compat'; export async function invokeCommand( command: string, root: string, - commandArgs: string[] + commandArgs: string[] = [] ) { if (command === undefined) { command = 'help'; } + + let verboseFlagIndex = commandArgs.indexOf('--verbose'); + if (verboseFlagIndex < 0) { + verboseFlagIndex = commandArgs.indexOf('-v'); + } + const isVerbose = verboseFlagIndex >= 0; + if (isVerbose) { + commandArgs.splice(verboseFlagIndex, 1); + } + switch (command) { case 'new': return (await import('./src/commands/generate')).taoNew( root, - commandArgs + commandArgs, + isVerbose ); case 'generate': case 'g': return (await import('./src/commands/generate')).generate( root, - commandArgs + commandArgs, + isVerbose ); case 'run': case 'r': - return (await import('./src/commands/run')).run(root, commandArgs); + return (await import('./src/commands/run')).run( + root, + commandArgs, + isVerbose + ); case 'migrate': return (await import('./src/commands/migrate')).migrate( root, - commandArgs + commandArgs, + isVerbose ); case 'help': case '--help': - return (await import('./src/commands/help')).printHelp(); + return (await import('./src/commands/help')).help(); default: const projectName = commandArgs[0] ? commandArgs[0] : ''; // this is to make `tao test mylib` same as `tao run mylib:test` - return (await import('./src/commands/run')).run(root, [ - `${projectName}:${command}`, - ...commandArgs.slice(1) - ]); + return (await import('./src/commands/run')).run( + root, + [`${projectName}:${command}`, ...commandArgs.slice(1)], + isVerbose + ); } } diff --git a/packages/tao/src/commands/generate.ts b/packages/tao/src/commands/generate.ts index 3c9120e21002e..0934d5ce13ff9 100644 --- a/packages/tao/src/commands/generate.ts +++ b/packages/tao/src/commands/generate.ts @@ -1,10 +1,3 @@ -import { - coerceTypes, - convertAliases, - convertToCamelCase, - handleErrors, - Schema -} from '../shared/params'; import { experimental, JsonObject, @@ -15,24 +8,30 @@ import { terminal, virtualFs } from '@angular-devkit/core'; +import { NodeJsSyncHost } from '@angular-devkit/core/node'; import { DryRunEvent, + formats, HostTree, - Schematic, - formats + Schematic } from '@angular-devkit/schematics'; -import { NodeJsSyncHost } from '@angular-devkit/core/node'; import { NodeWorkflow, - validateOptionsWithSchema, - FileSystemSchematicDescription + validateOptionsWithSchema } from '@angular-devkit/schematics/tools'; +import { execSync } from 'child_process'; +import * as fs from 'fs'; import * as inquirer from 'inquirer'; -import { logger } from '../shared/logger'; +import { getLogger } from '../shared/logger'; +import { + coerceTypes, + convertAliases, + convertToCamelCase, + handleErrors, + Schema +} from '../shared/params'; import { commandName, printHelp } from '../shared/print-help'; -import * as fs from 'fs'; import minimist = require('minimist'); -import { execSync } from 'child_process'; interface GenerateOptions { collectionName: string; @@ -290,7 +289,11 @@ function getCollection(workflow: NodeWorkflow, name: string) { return collection; } -function printGenHelp(opts: GenerateOptions, schema: Schema) { +function printGenHelp( + opts: GenerateOptions, + schema: Schema, + logger: logging.Logger +) { printHelp( `${commandName} generate ${opts.collectionName}:${opts.schematicName}`, { @@ -299,7 +302,8 @@ function printGenHelp(opts: GenerateOptions, schema: Schema) { ...schema.properties, dryRun: `Runs through and reports activity without writing to disk.` } - } + }, + logger ); } @@ -346,7 +350,7 @@ async function runSchematic( .toPromise(); if (opts.help) { - printGenHelp(opts, flattenedSchema as any); + printGenHelp(opts, flattenedSchema as any, logger); } else { const defaults = opts.schematicName === 'tao-new' @@ -381,8 +385,14 @@ async function runSchematic( return 0; } -export async function generate(root: string, args: string[]) { - return handleErrors(logger, async () => { +export async function generate( + root: string, + args: string[], + isVerbose: boolean = false +) { + const logger = getLogger(isVerbose); + + return handleErrors(logger, isVerbose, async () => { const fsHost = new virtualFs.ScopedHost( new NodeJsSyncHost(), normalize(root) @@ -412,8 +422,14 @@ async function readDefaultCollection(host: virtualFs.Host) { return workspaceJson.cli ? workspaceJson.cli.defaultCollection : null; } -export async function taoNew(root: string, args: string[]) { - return handleErrors(logger, async () => { +export async function taoNew( + root: string, + args: string[], + isVerbose: boolean = false +) { + const logger = getLogger(isVerbose); + + return handleErrors(logger, isVerbose, async () => { const fsHost = new virtualFs.ScopedHost( new NodeJsSyncHost(), normalize(root) diff --git a/packages/tao/src/commands/help.ts b/packages/tao/src/commands/help.ts index 8a43a580ac06c..1186a2f588157 100644 --- a/packages/tao/src/commands/help.ts +++ b/packages/tao/src/commands/help.ts @@ -1,9 +1,10 @@ -import { tags } from '@angular-devkit/core'; -import { logger } from '../shared/logger'; -import { toolDescription, commandName } from '../shared/print-help'; -import { terminal } from '@angular-devkit/core'; +import { tags, terminal } from '@angular-devkit/core'; +import { getLogger } from '../shared/logger'; +import { commandName, toolDescription } from '../shared/print-help'; + +export function help() { + const logger = getLogger(true); -export function printHelp() { logger.info(tags.stripIndent` ${terminal.bold(toolDescription)} diff --git a/packages/tao/src/commands/migrate.ts b/packages/tao/src/commands/migrate.ts index 868326357a3cc..8608048494155 100644 --- a/packages/tao/src/commands/migrate.ts +++ b/packages/tao/src/commands/migrate.ts @@ -1,20 +1,18 @@ -import { gt, lte } from 'semver'; -import { handleErrors, convertToCamelCase } from '../shared/params'; -import { logger } from '../shared/logger'; -import minimist = require('minimist'); -import { commandName } from '../shared/print-help'; -import { virtualFs, normalize, logging } from '@angular-devkit/core'; +import { logging, normalize, virtualFs } from '@angular-devkit/core'; +import * as core from '@angular-devkit/core/node'; import { NodeJsSyncHost } from '@angular-devkit/core/node'; -import { HostTree } from '@angular-devkit/schematics'; -import { dirSync } from 'tmp'; -import { readFileSync, writeFileSync, statSync } from 'fs'; -import { NodeModulesEngineHost } from '@angular-devkit/schematics/tools'; import { BaseWorkflow } from '@angular-devkit/schematics/src/workflow'; -import * as stripJsonComments from 'strip-json-comments'; - -import * as path from 'path'; -import * as core from '@angular-devkit/core/node'; +import { NodeModulesEngineHost } from '@angular-devkit/schematics/tools'; import { execSync } from 'child_process'; +import { readFileSync, writeFileSync } from 'fs'; +import * as path from 'path'; +import { gt, lte } from 'semver'; +import * as stripJsonComments from 'strip-json-comments'; +import { dirSync } from 'tmp'; +import { getLogger } from '../shared/logger'; +import { convertToCamelCase, handleErrors } from '../shared/params'; +import { commandName } from '../shared/print-help'; +import minimist = require('minimist'); export type MigrationsJson = { version: string; @@ -484,8 +482,14 @@ async function runMigrations( await p; } -export async function migrate(root: string, args: string[]) { - return handleErrors(logger, async () => { +export async function migrate( + root: string, + args: string[], + isVerbose: boolean = false +) { + const logger = getLogger(isVerbose); + + return handleErrors(logger, isVerbose, async () => { const opts = parseMigrationsOptions(args); if (opts.type === 'generateMigrations') { await generateMigrationsJsonAndUpdatePackageJson(logger, root, opts); diff --git a/packages/tao/src/commands/run.ts b/packages/tao/src/commands/run.ts index ba9d237420672..7486099a51050 100644 --- a/packages/tao/src/commands/run.ts +++ b/packages/tao/src/commands/run.ts @@ -1,22 +1,22 @@ -import { - convertToCamelCase, - handleErrors, - Schema, - coerceTypes -} from '../shared/params'; +import { Architect } from '@angular-devkit/architect'; +import { WorkspaceNodeModulesArchitectHost } from '@angular-devkit/architect/node'; import { experimental, json, + logging, normalize, - schema, - tags + schema } from '@angular-devkit/core'; import { NodeJsSyncHost } from '@angular-devkit/core/node'; -import { WorkspaceNodeModulesArchitectHost } from '@angular-devkit/architect/node'; -import { Architect } from '@angular-devkit/architect'; -import { logger } from '../shared/logger'; +import { getLogger } from '../shared/logger'; +import { + coerceTypes, + convertToCamelCase, + handleErrors, + Schema +} from '../shared/params'; +import { commandName, printHelp } from '../shared/print-help'; import minimist = require('minimist'); -import { printHelp, commandName } from '../shared/print-help'; export interface RunOptions { project: string; @@ -70,12 +70,22 @@ function parseRunOpts( return res; } -function printRunHelp(opts: RunOptions, schema: Schema) { - printHelp(`${commandName} run ${opts.project}:${opts.target}`, schema); +function printRunHelp( + opts: RunOptions, + schema: Schema, + logger: logging.Logger +) { + printHelp( + `${commandName} run ${opts.project}:${opts.target}`, + schema, + logger + ); } -export async function run(root: string, args: string[]) { - return handleErrors(logger, async () => { +export async function run(root: string, args: string[], isVerbose: boolean) { + const logger = getLogger(isVerbose); + + return handleErrors(logger, isVerbose, async () => { const fsHost = new NodeJsSyncHost(); const workspace = await new experimental.workspace.Workspace( normalize(root) as any, @@ -102,7 +112,7 @@ export async function run(root: string, args: string[]) { .flatten(builderDesc.optionSchema! as json.JsonObject) .toPromise(); if (opts.help) { - printRunHelp(opts, flattenedSchema as any); + printRunHelp(opts, flattenedSchema as any, logger); return 0; } else { const runOptions = coerceTypes(opts.runOptions, flattenedSchema as any); diff --git a/packages/tao/src/shared/logger.ts b/packages/tao/src/shared/logger.ts index 09d4c19d45ae9..00ee1d959142d 100644 --- a/packages/tao/src/shared/logger.ts +++ b/packages/tao/src/shared/logger.ts @@ -1,13 +1,14 @@ +import { logging, terminal } from '@angular-devkit/core'; import { createConsoleLogger } from '@angular-devkit/core/node'; -import { terminal } from '@angular-devkit/core'; -export const logger = createConsoleLogger( - false, - process.stdout, - process.stderr, - { - warn: s => terminal.bold(terminal.yellow(s)), - error: s => terminal.bold(terminal.red(s)), - fatal: s => terminal.bold(terminal.red(s)) +let logger: logging.Logger; +export const getLogger = (isVerbose: boolean = false) => { + if (!logger) { + logger = createConsoleLogger(isVerbose, process.stdout, process.stderr, { + warn: s => terminal.bold(terminal.yellow(s)), + error: s => terminal.bold(terminal.red(s)), + fatal: s => terminal.bold(terminal.red(s)) + }); } -); + return logger; +}; diff --git a/packages/tao/src/shared/params.ts b/packages/tao/src/shared/params.ts index 14bacb5c4ac05..5a6ba06568557 100644 --- a/packages/tao/src/shared/params.ts +++ b/packages/tao/src/shared/params.ts @@ -7,7 +7,11 @@ export type Schema = { description: string; }; -export async function handleErrors(logger: logging.Logger, fn: Function) { +export async function handleErrors( + logger: logging.Logger, + isVerbose: boolean, + fn: Function +) { try { return await fn(); } catch (err) { @@ -16,6 +20,9 @@ export async function handleErrors(logger: logging.Logger, fn: Function) { } else { logger.fatal(err.message); } + if (isVerbose && err.stack) { + logger.info(err.stack); + } return 1; } } diff --git a/packages/tao/src/shared/print-help.ts b/packages/tao/src/shared/print-help.ts index 8223904ce967a..b9a3f17e375b4 100644 --- a/packages/tao/src/shared/print-help.ts +++ b/packages/tao/src/shared/print-help.ts @@ -1,9 +1,11 @@ +import { logging, tags, terminal } from '@angular-devkit/core'; import { Schema } from './params'; -import { logger } from './logger'; -import { tags } from '@angular-devkit/core'; -import { terminal } from '@angular-devkit/core'; -export function printHelp(header: string, schema: Schema) { +export function printHelp( + header: string, + schema: Schema, + logger: logging.Logger +) { const allPositional = Object.keys(schema.properties).filter(key => { const p = schema.properties[key]; return p['$default'] && p['$default']['$source'] === 'argv';