Typedoc all the things
This commit is contained in:
parent
3b7c9cc09f
commit
d74566d1f7
104
src/Directive.ts
104
src/Directive.ts
@ -5,35 +5,93 @@ import {CLIOption} from "./directive/options/CLIOption"
|
||||
import {PositionalOption} from "./directive/options/PositionalOption";
|
||||
import {FlagOption} from "./directive/options/FlagOption";
|
||||
|
||||
/**
|
||||
* Type alias for a definition of a command-line option.
|
||||
*
|
||||
* This can be either an instance of CLIOption or a string describing an option.
|
||||
*
|
||||
* @example
|
||||
* Some examples of positional/flag options defined by strings:
|
||||
* `'{file name} | canonical name of the resource to create'`
|
||||
*
|
||||
* `'--push -p {value} | the value to be pushed'`
|
||||
*
|
||||
* `'--force -f | do a force push'`
|
||||
*/
|
||||
export type OptionDefinition = CLIOption<any> | string
|
||||
|
||||
/**
|
||||
* An error thrown when an invalid option was detected.
|
||||
*/
|
||||
export class OptionValidationError extends ErrorWithContext {}
|
||||
|
||||
/**
|
||||
* A base class representing a sub-command in the command-line utility.
|
||||
*/
|
||||
@Injectable()
|
||||
export abstract class Directive extends AppClass {
|
||||
@Inject()
|
||||
protected readonly logging!: Logging
|
||||
|
||||
/** Parsed option values. */
|
||||
private _optionValues: any
|
||||
|
||||
/**
|
||||
* Get the keyword or array of keywords that will specify this directive.
|
||||
*
|
||||
* @example
|
||||
* If this returns `['up', 'start']`, the directive can be run by either of:
|
||||
*
|
||||
* ```shell
|
||||
* ./ex up
|
||||
* ./ex start
|
||||
* ```
|
||||
*/
|
||||
public abstract getKeywords(): string | string[]
|
||||
|
||||
/**
|
||||
* Get the usage description of this directive. Should be brief (1 sentence).
|
||||
*/
|
||||
public abstract getDescription(): string
|
||||
|
||||
/**
|
||||
* Optionally, specify a longer usage text that is shown on the directive's `--help` page.
|
||||
*/
|
||||
public getHelpText(): string {
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of options defined for this command.
|
||||
*/
|
||||
public getOptions(): OptionDefinition[] {
|
||||
return []
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the directive is run from the command line.
|
||||
*
|
||||
* The raw arguments are provided as `argv`, but you are encouraged to use
|
||||
* `getOptions()` and `option()` helpers to access the parsed options instead.
|
||||
*
|
||||
* @param argv
|
||||
*/
|
||||
public abstract handle(argv: string[]): void | Promise<void>
|
||||
|
||||
/**
|
||||
* Sets the parsed option values.
|
||||
* @param optionValues
|
||||
* @private
|
||||
*/
|
||||
private _setOptionValues(optionValues: any) {
|
||||
this._optionValues = optionValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a parsed option. If none exists, return `defaultValue`.
|
||||
* @param name
|
||||
* @param defaultValue
|
||||
*/
|
||||
public option(name: string, defaultValue?: any) {
|
||||
if ( name in this._optionValues ) {
|
||||
return this._optionValues[name]
|
||||
@ -42,6 +100,15 @@ export abstract class Directive extends AppClass {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke this directive with the specified arguments.
|
||||
*
|
||||
* If usage was requested (see `didRequestUsage()`), it prints the extended usage info.
|
||||
*
|
||||
* Otherwise, it parses the options from `argv` and calls `handle()`.
|
||||
*
|
||||
* @param argv
|
||||
*/
|
||||
async invoke(argv: string[]) {
|
||||
const options = this.getResolvedOptions()
|
||||
|
||||
@ -115,6 +182,10 @@ export abstract class Directive extends AppClass {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the array of option definitions to CLIOption instances.
|
||||
* Of note, this resolves the string-form definitions to actual CLIOption instances.
|
||||
*/
|
||||
public getResolvedOptions(): CLIOption<any>[] {
|
||||
return this.getOptions().map(option => {
|
||||
if ( typeof option === 'string' ) {
|
||||
@ -125,6 +196,11 @@ export abstract class Directive extends AppClass {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main keyword displayed for this directive.
|
||||
* @example
|
||||
* If `getKeywords()` returns `['up', 'start']`, this will return `'up'`.
|
||||
*/
|
||||
public getMainKeyword(): string {
|
||||
const kws = this.getKeywords()
|
||||
|
||||
@ -135,32 +211,60 @@ export abstract class Directive extends AppClass {
|
||||
return kws
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given keyword should invoke this directive.
|
||||
* @param name
|
||||
*/
|
||||
public matchesKeyword(name: string) {
|
||||
let kws = this.getKeywords()
|
||||
if ( !Array.isArray(kws) ) kws = [kws]
|
||||
return kws.includes(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the given output to the log as success text.
|
||||
* @param output
|
||||
*/
|
||||
success(output: any) {
|
||||
this.logging.success(output, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the given output to the log as error text.
|
||||
* @param output
|
||||
*/
|
||||
error(output: any) {
|
||||
this.logging.error(output, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the given output to the log as warning text.
|
||||
* @param output
|
||||
*/
|
||||
warn(output: any) {
|
||||
this.logging.warn(output, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the given output to the log as info text.
|
||||
* @param output
|
||||
*/
|
||||
info(output: any) {
|
||||
this.logging.info(output, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the given output to the log as debugging text.
|
||||
* @param output
|
||||
*/
|
||||
debug(output: any) {
|
||||
this.logging.debug(output, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the given output to the log as verbose text.
|
||||
* @param output
|
||||
*/
|
||||
verbose(output: any) {
|
||||
this.logging.verbose(output, true)
|
||||
}
|
||||
|
@ -1,9 +1,64 @@
|
||||
import {UniversalPath} from '@extollo/util'
|
||||
|
||||
/**
|
||||
* Interface defining a template that can be generated using the TemplateDirective.
|
||||
*/
|
||||
export interface Template {
|
||||
/**
|
||||
* The name of the template as it will be specified from the command line.
|
||||
*
|
||||
* @example
|
||||
* If this is `'mytemplate'`, then the template will be created with:
|
||||
*
|
||||
* ```shell
|
||||
* ./ex new mytemplate some:path
|
||||
* ```
|
||||
*/
|
||||
name: string,
|
||||
|
||||
/**
|
||||
* The suffix of the file generated by this template.
|
||||
* @example `.mytemplate.ts`
|
||||
* @example `.controller.ts`
|
||||
*/
|
||||
fileSuffix: string,
|
||||
|
||||
/**
|
||||
* Brief description of the template displayed on the --help page for the TemplateDirective.
|
||||
* Should be brief (1 sentence).
|
||||
*/
|
||||
description: string,
|
||||
|
||||
/**
|
||||
* Array of path-strings that are resolved relative to the base `app` directory.
|
||||
* @example `['http', 'controllers']`
|
||||
* @example `['units']`
|
||||
*/
|
||||
baseAppPath: string[],
|
||||
|
||||
/**
|
||||
* Render the given template to a string which will be written to the file.
|
||||
* Note: this method should NOT write the contents to `targetFilePath`.
|
||||
*
|
||||
* @example
|
||||
* If the user enters:
|
||||
*
|
||||
* ```shell
|
||||
* ./ex new mytemplate path:to:NewInstance
|
||||
* ```
|
||||
*
|
||||
* Then, the following params are:
|
||||
* ```typescript
|
||||
* {
|
||||
* name: 'NewInstance',
|
||||
* fullCanonicalPath: 'path:to:NewInstance',
|
||||
* targetFilePath: UniversalPath { }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param name - the singular name of the resource
|
||||
* @param fullCanonicalName - the full canonical name of the resource
|
||||
* @param targetFilePath - the UniversalPath where the file will be written
|
||||
*/
|
||||
render: (name: string, fullCanonicalName: string, targetFilePath: UniversalPath) => string | Promise<string>
|
||||
}
|
||||
|
@ -4,6 +4,11 @@ import {Unit} from "@extollo/lib"
|
||||
import {ErrorWithContext} from "@extollo/util"
|
||||
import {CommandLineApplication} from "../service"
|
||||
|
||||
/**
|
||||
* A directive that starts the framework's final target normally.
|
||||
* In most cases, this runs the HTTP server, which would have been replaced
|
||||
* by the CommandLineApplication unit.
|
||||
*/
|
||||
@Injectable()
|
||||
export class RunDirective extends Directive {
|
||||
getDescription(): string {
|
||||
|
@ -3,12 +3,20 @@ import {Directive} from "../Directive"
|
||||
import * as colors from "colors/safe"
|
||||
import * as repl from 'repl'
|
||||
|
||||
/**
|
||||
* Launch an interactive REPL shell from within the application.
|
||||
* This is very useful for debugging and testing things during development.
|
||||
*/
|
||||
export class ShellDirective extends Directive {
|
||||
protected options: any = {
|
||||
welcome: `powered by Extollo, © ${(new Date).getFullYear()} Garrett Mills\nAccess your application using the "app" global.`,
|
||||
prompt: `${colors.blue('(')}extollo${colors.blue(') ➤ ')}`,
|
||||
}
|
||||
|
||||
/**
|
||||
* The created Node.js REPL server.
|
||||
* @protected
|
||||
*/
|
||||
protected repl?: repl.REPLServer
|
||||
|
||||
getDescription(): string {
|
||||
|
@ -4,6 +4,9 @@ import {ErrorWithContext} from "@extollo/util"
|
||||
import {PositionalOption} from "./options/PositionalOption"
|
||||
import {CommandLine} from "../service"
|
||||
|
||||
/**
|
||||
* Create a new file based on a template registered with the CommandLine service.
|
||||
*/
|
||||
@Injectable()
|
||||
export class TemplateDirective extends Directive {
|
||||
@Inject()
|
||||
@ -20,7 +23,7 @@ export class TemplateDirective extends Directive {
|
||||
getOptions(): OptionDefinition[] {
|
||||
const registeredTemplates = this.cli.getTemplates()
|
||||
const template = new PositionalOption('template_name', 'the template to base the new file on (e.g. model, controller)')
|
||||
template.whitelist(...registeredTemplates.pluck('name'))
|
||||
template.whitelist(...registeredTemplates.pluck('name').all())
|
||||
|
||||
const destination = new PositionalOption('file_name', 'canonical name of the file to create (e.g. auth:Group, dash:Activity)')
|
||||
|
||||
@ -43,7 +46,7 @@ export class TemplateDirective extends Directive {
|
||||
'',
|
||||
...(registeredTemplates.map(template => {
|
||||
return ` - ${template.name}: ${template.description}`
|
||||
}))
|
||||
}).all())
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,10 @@ import {Injectable, Inject} from "@extollo/di"
|
||||
import {padRight} from "@extollo/util"
|
||||
import {CommandLine} from "../service"
|
||||
|
||||
/**
|
||||
* Directive that prints the help message and usage information about
|
||||
* directives registered with the command line utility.
|
||||
*/
|
||||
@Injectable()
|
||||
export class UsageDirective extends Directive {
|
||||
@Inject()
|
||||
|
@ -6,9 +6,24 @@ import {CLIOption} from "./CLIOption"
|
||||
export class FlagOption<T> extends CLIOption<T> {
|
||||
|
||||
constructor(
|
||||
/**
|
||||
* The long-form flag for this option.
|
||||
* @example --path, --create
|
||||
*/
|
||||
public readonly longFlag?: string,
|
||||
/**
|
||||
* The short-form flag for this option.
|
||||
* @example -p, -c
|
||||
*/
|
||||
public readonly shortFlag?: string,
|
||||
/**
|
||||
* Usage message describing this flag.
|
||||
*/
|
||||
public readonly message?: string,
|
||||
/**
|
||||
* Description of the argument required by this flag.
|
||||
* If this is set, the flag will expect a positional argument to follow as a param.
|
||||
*/
|
||||
public readonly argumentDescription?: string
|
||||
) { super() }
|
||||
|
||||
|
@ -11,7 +11,14 @@ export class PositionalOption<T> extends CLIOption<T> {
|
||||
* @param {string} message - message describing the option
|
||||
*/
|
||||
constructor(
|
||||
/**
|
||||
* The display name of this positional argument.
|
||||
* @example path, filename
|
||||
*/
|
||||
public readonly name: string,
|
||||
/**
|
||||
* A usage message describing this parameter.
|
||||
*/
|
||||
public readonly message: string = ''
|
||||
) { super() }
|
||||
|
||||
|
@ -11,12 +11,19 @@ import {middleware_template} from "../templates/middleware";
|
||||
import {routes_template} from "../templates/routes";
|
||||
import {config_template} from "../templates/config";
|
||||
|
||||
/**
|
||||
* Service for managing directives, templates, and other resources related
|
||||
* to the command line utilities.
|
||||
*/
|
||||
@Singleton()
|
||||
export class CommandLine extends Unit {
|
||||
@Inject()
|
||||
protected readonly logging!: Logging
|
||||
|
||||
/** Directive classes registered with the CLI command. */
|
||||
protected directives: Collection<Instantiable<Directive>> = new Collection<Instantiable<Directive>>()
|
||||
|
||||
/** Templates registered with the CLI command. These can be created with the TemplateDirective. */
|
||||
protected templates: Collection<Template> = new Collection<Template>()
|
||||
|
||||
constructor() { super() }
|
||||
@ -30,10 +37,16 @@ export class CommandLine extends Unit {
|
||||
this.registerTemplate(config_template)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the application was started from the command line.
|
||||
*/
|
||||
public isCLI() {
|
||||
return this.app().hasUnit(CommandLineApplication)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string containing the Extollo ASCII logo.
|
||||
*/
|
||||
public getASCIILogo() {
|
||||
return ` _
|
||||
/ /\\ ______ _ _ _
|
||||
@ -45,20 +58,37 @@ export class CommandLine extends Unit {
|
||||
`
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a Directive class with this service. This will make
|
||||
* the directive available for use on the CLI.
|
||||
* @param directiveClass
|
||||
*/
|
||||
public registerDirective(directiveClass: Instantiable<Directive>) {
|
||||
if ( !this.directives.includes(directiveClass) ) {
|
||||
this.directives.push(directiveClass)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given directive is registered with this service.
|
||||
* @param directiveClass
|
||||
*/
|
||||
public hasDirective(directiveClass: Instantiable<Directive>) {
|
||||
return this.directives.includes(directiveClass)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of all registered directives.
|
||||
*/
|
||||
public getDirectives() {
|
||||
return this.directives.clone()
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given template with this service. This makes the template
|
||||
* available for use with the TemplateDirective service.
|
||||
* @param template
|
||||
*/
|
||||
public registerTemplate(template: Template) {
|
||||
if ( !this.templates.firstWhere('name', '=', template.name) ) {
|
||||
this.templates.push(template)
|
||||
@ -68,14 +98,25 @@ export class CommandLine extends Unit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a template with the given name exists.
|
||||
* @param name
|
||||
*/
|
||||
public hasTemplate(name: string) {
|
||||
return !!this.templates.firstWhere('name', '=', name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the template with the given name, if one exists.
|
||||
* @param name
|
||||
*/
|
||||
public getTemplate(name: string) {
|
||||
return this.templates.firstWhere('name', '=', name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of all registered templates.
|
||||
*/
|
||||
public getTemplates() {
|
||||
return this.templates.clone()
|
||||
}
|
||||
|
@ -7,13 +7,21 @@ import {ShellDirective} from "../directive/ShellDirective";
|
||||
import {TemplateDirective} from "../directive/TemplateDirective";
|
||||
import {RunDirective} from "../directive/RunDirective";
|
||||
|
||||
/**
|
||||
* Unit that takes the place of the final unit in the application that handles
|
||||
* invocations from the command line.
|
||||
*/
|
||||
@Singleton()
|
||||
export class CommandLineApplication extends Unit {
|
||||
/** The unit that was replaced by the CLI app. */
|
||||
private static replacement?: typeof Unit
|
||||
|
||||
/** Set the replaced unit. */
|
||||
public static setReplacement(unitClass?: typeof Unit) {
|
||||
this.replacement = unitClass
|
||||
}
|
||||
|
||||
/** Get the replaced unit. */
|
||||
public static getReplacement() {
|
||||
return this.replacement
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
import {Template} from "../Template"
|
||||
import {UniversalPath} from "@extollo/util"
|
||||
|
||||
/**
|
||||
* A template that generates a new configuration file in the app/configs directory.
|
||||
*/
|
||||
const config_template: Template = {
|
||||
name: 'config',
|
||||
fileSuffix: '.config.ts',
|
||||
|
@ -1,6 +1,9 @@
|
||||
import {Template} from "../Template"
|
||||
import {UniversalPath} from "@extollo/util"
|
||||
|
||||
/**
|
||||
* Template that generates a new controller in the app/http/controllers directory.
|
||||
*/
|
||||
const controller_template: Template = {
|
||||
name: 'controller',
|
||||
fileSuffix: '.controller.ts',
|
||||
|
@ -1,6 +1,9 @@
|
||||
import {Template} from "../Template"
|
||||
import {UniversalPath} from "@extollo/util"
|
||||
|
||||
/**
|
||||
* Template that generates a new Directive class in the app/directives directory.
|
||||
*/
|
||||
const directive_template: Template = {
|
||||
name: 'directive',
|
||||
fileSuffix: '.directive.ts',
|
||||
|
@ -1,6 +1,9 @@
|
||||
import {Template} from "../Template"
|
||||
import {UniversalPath} from "@extollo/util"
|
||||
|
||||
/**
|
||||
* Template that generates a new middleware class in app/http/middlewares.
|
||||
*/
|
||||
const middleware_template: Template = {
|
||||
name: 'middleware',
|
||||
fileSuffix: '.middleware.ts',
|
||||
|
@ -1,6 +1,9 @@
|
||||
import {Template} from "../Template"
|
||||
import {UniversalPath} from "@extollo/util"
|
||||
|
||||
/**
|
||||
* Template that generates a new route definition file in app/http/routes.
|
||||
*/
|
||||
const routes_template: Template = {
|
||||
name: 'routes',
|
||||
fileSuffix: '.routes.ts',
|
||||
|
@ -1,6 +1,9 @@
|
||||
import {Template} from "../Template"
|
||||
import {UniversalPath} from "@extollo/util"
|
||||
|
||||
/**
|
||||
* Template that generates a new application unit class in app/units.
|
||||
*/
|
||||
const unit_template: Template = {
|
||||
name: 'unit',
|
||||
fileSuffix: '.ts',
|
||||
|
Loading…
Reference in New Issue
Block a user