From fbbfbf09ed7b2b48a0b51325256166c9c5bd169a Mon Sep 17 00:00:00 2001 From: Jeff Dickey <216188+jdxcode@users.noreply.github.com> Date: Sun, 8 Apr 2018 18:05:12 -0700 Subject: [PATCH] perf: lazy-load deps --- package.json | 2 +- src/deps.ts | 12 ++++++++++++ src/errors.ts | 15 +++++++++++---- src/help.ts | 15 ++++++++------- src/index.ts | 8 ++++++-- src/parse.ts | 18 +++++++++++------- yarn.lock | 6 +++--- 7 files changed, 52 insertions(+), 24 deletions(-) create mode 100644 src/deps.ts diff --git a/package.json b/package.json index 6671ff9..9620831 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "chalk": "^2.3.2" }, "devDependencies": { - "@oclif/errors": "^1.0.3", + "@oclif/errors": "^1.0.4", "@oclif/tslint": "^1.1.0", "@types/chai": "^4.1.2", "@types/mocha": "^5.0.0", diff --git a/src/deps.ts b/src/deps.ts new file mode 100644 index 0000000..b1c8931 --- /dev/null +++ b/src/deps.ts @@ -0,0 +1,12 @@ +export default () => { + const cache: {[k: string]: any} = {} + return { + add(this: T, name: K, fn: () => U): T & {[P in K]: U} { + Object.defineProperty(this, name, { + enumerable: true, + get: () => cache[name] || (cache[name] = fn()), + }) + return this as any + }, + } +} diff --git a/src/errors.ts b/src/errors.ts index 18156d1..9a94843 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,11 +1,18 @@ import {CLIError} from '@oclif/errors' import {Arg} from './args' +import Deps from './deps' import * as flags from './flags' -import {flagUsages} from './help' -import {renderList} from './list' +import * as Help from './help' +import * as List from './list' import {ParserInput, ParserOutput} from './parse' +export {CLIError} from '@oclif/errors' + +const m = Deps() +.add('help', () => require('./help') as typeof Help) +.add('list', () => require('./list') as typeof List) + export interface ICLIParseErrorOptions { parse: { input?: ParserInput @@ -30,7 +37,7 @@ export class RequiredArgsError extends CLIParseError { let message = `Missing ${args.length} required arg${args.length === 1 ? '' : 's'}` const namedArgs = args.filter(a => a.name) if (namedArgs.length) { - const list = renderList(namedArgs.map(a => [a.name, a.description] as [string, string])) + const list = m.list.renderList(namedArgs.map(a => [a.name, a.description] as [string, string])) message += `:\n${list}` } super({parse, message}) @@ -42,7 +49,7 @@ export class RequiredFlagError extends CLIParseError { public flags: flags.IFlag[] constructor({flags, parse}: ICLIParseErrorOptions & { flags: flags.IFlag[] }) { - const usage = renderList(flagUsages(flags, {displayRequired: false})) + const usage = m.list.renderList(m.help.flagUsages(flags, {displayRequired: false})) const message = `Missing required flag${flags.length === 1 ? '' : 's'}:\n${usage}` super({parse, message}) this.flags = flags diff --git a/src/help.ts b/src/help.ts index f2a8135..cdabde5 100644 --- a/src/help.ts +++ b/src/help.ts @@ -1,12 +1,12 @@ -import chalk from 'chalk' +import Chalk from 'chalk' +import Deps from './deps' import {IFlag} from './flags' -import {sortBy} from './util' +import * as Util from './util' -function dim(s: string): string { - if (chalk) return chalk.dim(s) - return s -} +const m = Deps() +.add('chalk', () => require('chalk') as typeof Chalk) +.add('util', () => require('./util') as typeof Util) export interface FlagUsageOptions { displayRequired?: boolean } export function flagUsage(flag: IFlag, options: FlagUsageOptions = {}): [string, string | undefined] { @@ -18,13 +18,14 @@ export function flagUsage(flag: IFlag, options: FlagUsageOptions = {}): [st let description: string | undefined = flag.description || '' if (options.displayRequired && flag.required) description = `(required) ${description}` - description = description ? dim(description) : undefined + description = description ? m.chalk.dim(description) : undefined return [` ${label.join(',').trim()}${usage}`, description] as [string, string | undefined] } export function flagUsages(flags: IFlag[], options: FlagUsageOptions = {}): [string, string | undefined][] { if (!flags.length) return [] + const {sortBy} = m.util return sortBy(flags, f => [f.char ? -1 : 1, f.char, f.name]) .map(f => flagUsage(f, options)) } diff --git a/src/index.ts b/src/index.ts index 2e6da58..8b31453 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,9 +4,13 @@ import * as args from './args' import {OutputArgs, OutputFlags, Parser, ParserOutput as Output} from './parse' export {args} import * as flags from './flags' -import {validate} from './validate' +import * as Validate from './validate' export {flags} export {flagUsages} from './help' +import Deps from './deps' + +const m = Deps() +.add('validate', () => require('./validate').validate as typeof Validate.validate) export type Input = { flags?: flags.Input @@ -30,7 +34,7 @@ export function parse(argv: stri } const parser = new Parser(input) const output = parser.parse() - validate({input, output}) + m.validate({input, output}) return output as any } diff --git a/src/parse.ts b/src/parse.ts index c580475..04e1cb3 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -1,11 +1,14 @@ // tslint:disable interface-over-type-literal -import {CLIError} from '@oclif/errors' - import {Arg} from './args' +import Deps from './deps' import * as Errors from './errors' import * as Flags from './flags' -import {pickBy} from './util' +import * as Util from './util' + +const m = Deps() +.add('errors', () => require('./errors') as typeof Errors) +.add('util', () => require('./util') as typeof Util) let debug: any try { @@ -45,6 +48,7 @@ export class Parser } private readonly context: any constructor(private readonly input: T) { + const {pickBy} = m.util this.context = input.context || {} this.argv = input.argv.slice(0) this._setNames() @@ -95,7 +99,7 @@ export class Parser