Giter Club home page Giter Club logo

artus-cli's Introduction

@artus-cli/artus-cli

artus-cli

NPM version NPM quality NPM download Continuous Integration Test coverage Oss Insight Analytics

artus-cli aims to provide a modern command-line-apps framework.

  • Powerful, powered by artusjs.
  • Modern, TypeScript and IoC ready.
  • Customizable, command inheritance, and support Plugin and Framework (wrap it as a upper layer CLI).
  • Community, enjoy the eco-friendliness, use the same plugin with your artusjs web apps.

How it looks

import { DefineCommand, Option, Command } from '@artus-cli/artus-cli';

@DefineCommand({
  command: 'dev',
  description: 'Run the development server',
  alias: [ 'd' ],
})
export class DevCommand extends Command {
  @Option({
    alias: 'p',
    default: 3000,
    description: 'server port',
  })
  port: number;

  async run() {
    console.info('port: %s', this.port);
  }
}

Run it with:

$ my-cli dev --port 8080

Document

https://artus-cli.github.io

artus-cli's People

Contributors

whxaxes avatar atian25 avatar thonatos avatar fengmk2 avatar

Stargazers

Heart Sco avatar Xi Yuan avatar wuxx avatar chenlei avatar backpast avatar Daniel Bodnar avatar Gregory Tereshko avatar  avatar monjer avatar Ray avatar Lan Qingyong avatar coldfannn avatar ARam avatar Leon Shi avatar Khaidi Chu avatar H avatar 王子凌 avatar 随风 avatar  avatar Janlay avatar MacTavish Lee avatar TracyYangg avatar  avatar 日王 avatar Jruif avatar zao avatar Cygra Wang avatar Ming avatar E3 avatar 多小凯 avatar 丑牛 avatar Andrew Johnson avatar Beace avatar Roman Vasilev avatar Michael Wang 汪東陽 avatar Haitao Lee avatar laohan avatar  avatar 易容 (hyj1991) avatar Wang Zishi avatar App Service avatar  avatar Ke Ding avatar h7ml avatar Gadzan Mak avatar  avatar  avatar code_for_the_poem avatar

Watchers

 avatar  avatar laohan avatar code_for_the_poem avatar

Forkers

jruif knoxnoe

artus-cli's Issues

企业级的命令行框架 - artus-cli

背景

命令行工具,是 Node.js 最初也是最大的应用场景,我们日常工作中会经常用到 NPM、Babel、Webpack 等 CLI,我们的应用的工程化也往往需要依托于自有的命令行工具。

目前业界已经有大量 CLI 工具库,以下几个均是大家比较耳熟能详的:

  • commander.js
    • 经典老牌类库,小巧精简,适合实现一些小 CLI 工具。
    • 函数式定义指令配置,指令少的时候比较简洁直观。
    • 缺少插件、框架、中间件等通用的拓展方案。
    • 学习成本较低。
  • yargs
    • 功能比 commander 多且强大,能满足大部分独立 CLI 工具的需求。
    • 也是函数式定义指令配置,指令少的时候比较简单直观。
    • 提供了配置继承的能力,但是也缺少插件、框架等通用的拓展方案。
    • 学习成本中等,主要是功能太多且复杂。
  • oclif
    • 定位为 CLI 框架,功能强大且齐全。
    • 通过传统类定义的方式定义指令配置,支持指令继承(但是似乎不支持继承配置),OOP 的编程风格让代码拆分较为简单。
    • 具备较强的拓展能力和插件、生命周期钩子机制,但缺少框架方案,不易提供场景化的 CLI 框架封装,生命周期的钩子也没有中间件那么灵活。
    • 学习成本中等偏上,比前两个多了不少概念。

为什么在上面的分析里面,我们都会考虑一个『扩展性』的需求呢?因为在我们过往的实践中,会涉及到社区开源和内部上层框架之间的协同问题:内部工具往往会继承于我们开源的社区工具,做一些私有逻辑,并会持续把企业的最佳实践下沉到社区。

image

这是 Egg 那边的一些真实实践:

image

其中的 common-bin 就是基于 yargs 封装的命令行框架,但是在多年实践中,我们也发现了 common-bin 的一些问题,比如年久失修、缺少插件机制、TS 不友好、缺少通用的切面逻辑 等等 ...


命令行框架

上文所说的场景,在 Artus 的体系里面,也有类似的情况,作为一个定位为框架的框架的开源项目,我们也不可避免的需要考虑对应的命令行场景。

在企业场景下,一个命令行除了常规的能力外,往往还有以下要求:

  • 支持身份鉴权
  • 支持灵活的定制化和扩展能力
  • 具备一定的统一管控能力
  • 自动化升级能力
  • 现代化、健壮、便于测试

是不是觉得有点眼熟?这些要求其实跟一个 Web 框架很像,正好 Artus 的一个核心卖点就是协议无关性。那我们是不是可以基于 Artus 来封装一个 CLI 框架的框架呢?

试着对标下:

  • 每一个 Command 是不是类似一个 Controller?
  • Command 之间的公共逻辑是不是类似一个 Service?
  • 支持身份鉴权,可以通过 Middleware 来统一拦截?
  • 定制性和扩展能力,不正是插件机制么?
  • 开源版本和企业版本的关系,不正是框架机制么?

Artus 天然具备了流水线、插件、框架、IoC 等能力,只需要额外再封装几个装饰器,实现下 argv 的 parser 能力,就很快能实现了。

不仅能让 CLI 的编程界面与我们应用的编程界面一致,减少开发者学习成本的同时,还能拓展 Artus 的使用场景,吃自己的狗粮。

甚至我们有时在命令行场景需要的一些能力,如 oss、redis 都可以直接复用 Artus 的生态了,也能直接引入 Artus 上层框架的一些 Loader 用于构建期的分析。

那还等什么? 干吧~

POC 验证

设计思路

  • 基于 Artus 的 pipeline 流水线设计,将指令输入作为协议,将指令执行通过中间件模式串联,支持指令重定向。
  • 用户编程风格方面,采用 IoC 的方式来定义指令、参数配置、中间件。

image

具体实现

该想法其实最早萌生于 8/9 月份

  • 当时天猪抽空搞了个第一版基于 yargs 的大概思路( 初版代码 );
  • 后续我基于该版本将其完善,并脱离了对 yargs 的依赖,便有了 第二版代码

现在这个是第三版代码:https://github.com/artus-cli/artus-cli ,为了方便验证及体验已经发了 0.0.x 的 beta 版本。

Examples

体验用的相关 demo 例子都在:https://github.com/artus-cli/examples

编程界面

目录结构

跟 Artus 的应用一样,除了 config 目录的约定之外,其他原则上不约束目录结构,但是我们也会有一些推荐的规范( 比如入口文件统一放到 bin 目录下,指令文件统一放到 cmd 目录下 ),比如 simple-bin

如果不想分那么多目录或文件也可以参考 singlefile 例子的单文件结构

simple-bin
├── bin
│   └── cli.ts     入口文件
├── cmd      
│   └── main.ts    主命令
└── package.json   描述文件

simple-bin 中只有一个指令,如果存在多个指令的情况,也可以参考 egg-bin 的目录结构:

egg-bin
├── bin
│   └── cli.ts
├── cmd
│   ├── cov.ts
│   ├── debug.ts
│   ├── dev.ts
│   ├── main.ts
│   └── test.ts
├── config
│   ├── framework.ts
│   └── plugin.ts
├── index.ts
├── meta.json
└── package.json   

入口文件

CLI 的可执行文件,只要引入 @artus-cli/artus-clistart 方法并且执行即可,一般定义在项目的 bin 目录中。

#!/usr/bin/env node

import { start } from '@artus-cli/artus-cli';

start();

定义指令

DefineCommand

通过 DefineCommand 这个装饰器可以定义一个指令。

// index.ts
import { DefineCommand, Command } from '@artus-cli/artus-cli';

@DefineCommand()
export class MyCommand extends Command {
  async run() {
    console.info('trigger me');
  }
}

如果 DefineCommand 不传 command ,那么该指令会自动设置为主指令,比如上面的例子的 bin 名称是叫 my-bin,那么直接在命令行执行 my-bin 就会打印 trigger me

Option

可以通过 Option 装饰器定义参数:

// index.ts
import { DefineCommand, Option, Command } from '@artus-cli/artus-cli';

@DefineCommand()
export class MyCommand extends Command {
  @Option({
    alias: 'p',
    default: 3000,
    description: 'port'
  })
  port: number;

  async run() {
    console.info('Run with port', this.option.port);
  }
}

此时再执行 my-bin 将会打印 Run with port 3000 ,因为 port 默认是 3000 ,也可以指定执行 my-bin --port=7001 或者 my-bin -p 7001

综合使用

上面的例子中的 DefineCommand 也可以传入 command 指定执行参数以及描述:

// index.ts
import { DefineCommand, Option, Command } from '@artus-cli/artus-cli';

@DefineCommand({
  command: 'my-bin [baseDir]',
  description: 'My First Bin'
})
export class MyCommand extends Command {
  @Option({
    alias: 'p',
    default: 3000,
    description: 'port'
  })
  port: number;
  
  @Option()
  baseDir: string;

  async run() {
    console.info('Run with port %s in %s', this.port, this.baseDir);
  }
}

当执行 my-bin ./src --port=7001 即可打印 Run with port 7001 in ./src

定义子指令

常规用法

定义子指令也是通过 DefineCommand 这个装饰器,比如上面的例子改成一个 dev 的子指令,只需要改一下 command 即可:

// dev.ts
import { DefineCommand, Option, Command } from '@artus-cli/artus-cli';

@DefineCommand({
  command: 'dev [baseDir]',
  description: 'Run Dev Server',
  alias: 'd',
})
export class MyDevCommand extends Command {
  @Option({
    alias: 'p',
    default: 3000,
    description: 'port'
  })
  port: number;
  
  @Option()
  baseDir: string;

  async run() {
    console.info('Run with port %s in %s', this.port, this.baseDir);
  }
}

然后执行 my-bin dev ./src --port=7001 或者 my-bin d ./src --port=7001 即可 ,如果需要定义更多子指令也可以使用同样的配置方式,比如

// test.ts
import { DefineCommand, Command } from '@artus-cli/artus-cli';

@DefineCommand({
  command: 'test',
  description: 'Run Unittest',
  alias: 't',
})
export class MyTestCommand extends Command {
  async run() {
    console.info('Run Unittest');
  }
}

当定义 command 的时候,上面例子中的 bin 名称( 即 my-bin )也可以省略,比如上面的例子可以精简成以下写法:

// test.ts
import { DefineCommand, DefineOption, Command } from '@artus-cli/artus-cli';

@DefineCommand({
  command: 'test',
  description: 'Run Unittest',
  alias: 't',
})
export class MyTestCommand extends Command {
  async run() {
    console.info('Run Unittest');
  }
}

定义好之后就可以执行 my-bin test 看到效果。

指定父指令

常规用法中的父子关系,是通过解析 command 字符串支持的,在定义 Command 的时候也可以通过配置 parent 主动指定父指令。比如

@DefineCommand({
  command: 'my-bin module',
  description: 'Module Commands',
})
export class ModuleMainCommand extends Command {
  async run() {
    console.info('module is run');
  }
}

@DefineCommand({
  command: 'dev',
  description: 'Module Dev Commands',
  parent: ModuleMainCommand,
})
export class ModuleDevCommand extends Command {
  async run() {
    console.info('module dev');
  }
}

@DefineCommand({
  command: 'debug',
  description: 'Module Debug Commands',
  parent: ModuleMainCommand,
})
export class ModuleDebugCommand extends Command {
  async run() {
    console.info('module debug');
  }
}

然后就可以有了以下三个指令:

  • my-bin module
  • my-bin module dev
  • my-bin module debug

就不用挨个写 module devmodule debug

使用场景:比如已经有一个 DevCommand ,需要在 module 这个父指令下也有一个 dev 指令,就可以新增一个 ModuleDevCommand 继承 DevCommand ,只需要配置 parent 为 ModuleCommand 即可 。

Arguments

配置在 DefineCommand 的 command 参数中,两种配置方式

  • <options> 必传参数,比如 command: 'test <file>'
  • [options] 可选参数,比如 command: 'dev [baseDir]'

也可以配置动态参数

  • <options...> 必传的动态参数,比如 command: 'test <files...>' ,最终拿到的 files 将是个数组。
  • [options...] 可选动态参数,跟上面效果一样。

Option

Option 是 Arguments 与 Flags(--port 这种参数) 的统一配置,通过 Option 装饰器定义

export interface OptionProps {
  alias?: string | string[]; // 别名
  default?: any;  // 默认值
  required?: boolean; // 是否必须
  description?: string; // 描述
}

当配置以下格式时

@DefineCommand()
export class DevCommand extends Command {
  // 可以传入详细配置
  @Option({
    alias: 'p',
    description: 'port'
  })
  port: number;
  
  // 传入字符代表 description
  @Option('daemon')
  daemon: boolean;
  
  @Option('node flags')
  nodeFlags: string;
}

执行指令可传入 --port=7001 --node-flags=--inspect --daemon

转换成在 run 函数中获取的 options 为

{
    "port": 7001,
    "nodeFlags": "--inspect",
    "daemon": true
}
  • 如果是 boolean 类型,当传参为 --no-daemon 等同于 --daemon=false
  • Arguments 的详细配置也可以在其中配置,但只支持配置 typedefault 两个属性。

中间件

分成几种中间件:

  • 触发器中间件( 由 artus/pipeline 提供的 pipeline middlewares )
  • 跟指令绑定的中间件
    • 跟指令类绑定的中间件( command middlewares )
    • 跟 run 函数绑定的中间件( method middlewares )

执行流水大概如下

# 输入
input -> pipeline middlewares -> command middlewares -> method middlewares -> run

# 输出
run -> method middlewares -> command middlewares -> pipeline middlewares -> output

触发器中间件

在生命周期中注入 @artus-cli/artus-cli 的 Program ,然后调用 use 函数即可。这类型的中间件一般是用于全局或者针对性拦截一些指令输入。

Program 是框架提供的一些开放 API ,可以用于获取指令列表,注册中间件,注册全局 Option 等,后面高级功能有介绍。

// lifecycle.ts
import { Inject, ApplicationLifecycle, LifecycleHook, LifecycleHookUnit, CommandContext, Program, CommandContext } from '@artus-cli/artus-cli';

@LifecycleHookUnit()
export default class UsageLifecycle implements ApplicationLifecycle {
  @Inject()
  private readonly program: Program;

  @LifecycleHook()
  async didLoad() {
    this.program.use(async (ctx: CommandContext, next) => {
        // do something
        await next();
    });
  }
}

使用场景:比如 plugin-help 中就通过中间件拦截了 --help-h 的输入,然后重定向到 help 指令。

指令中间件

通过 Middleware 装饰器可以定义指令中间件,可以使用在指令类或者 run 函数中,比如下面的例子

import { DefineCommand, Command, Middleware } from '@artus-cli/artus-cli';

@DefineCommand({
  command: 'dev',
  description: 'Run the development server',
})
@Middleware(async (_ctx, next) => {
  console.info('prerun 1');
  await next();
  console.info('postrun 1');
})
export class DevCommand extends Command {
  @Middleware(async (_ctx, next) => {
    console.info('prerun 2');
    await next();
    console.info('postrun 2');
  })
  async run() {
    // nothing
  }
}

输出结果为

prerun 1
prerun 2
postrun 2
postrun 1

中间件也可以传数组,再比如下面这个例子

import { DefineCommand, Command, Middleware } from '@artus-cli/artus-cli';

@DefineCommand({
  command: 'dev',
  description: 'Run the development server',
})
@Middleware([
  async (_ctx, next) => {
    console.info('prerun 1');
    await next();
    console.info('postrun 1');
  },
  async (_ctx, next) => {
    console.info('prerun 2');
    await next();
    console.info('postrun 2');
  },
])
export class DevCommand extends Command {
  @Middleware([
    async (_ctx, next) => {
      console.info('prerun 3');
      await next();
      console.info('postrun 3');
    },
    async (_ctx, next) => {
      console.info('prerun 4');
      await next();
      console.info('postrun 4');
    },
  ])
  async run() {
    // nothing
  }
}

输出内容为

prerun 1
prerun 2
prerun 3
prerun 4
postrun 4
postrun 3
postrun 2
postrun 1

指令继承

直接使用类的继承方式即可。

配置继承

当指令继承时,子指令类会继承父指令类定义的配置信息,比如下面的例子

// dev command
@DefineCommand({
  command: 'dev',
})
export class DevCommand extends Command {
  @Option({
    alias: 'p',
    default: 3000,
  })
  port: number;

  async run() {
    console.info('Run In', this.options.port);
  }
}

// debug command
@DefineCommand({
  command: 'debug',
})
export class DebugCommand extends DevCommand {
  @Option({
    default: 8080,
    description: 'inspect port'
  })
  inspectPort: number;

  async run() {
    super.run();
    console.info('Debug In', this.options.inspectPort);
  }
}

执行 my-bin debug 的话,会输出

Run In 3000
Debug In 8080

中间件继承

除了上面说的指令配置会自动继承之外,在类上挂载的中间件也能够被继承,还是继续看例子:

// dev command
@DefineCommand({
  command: 'dev',
})
@Middleware(async (_ctx, next) => {
  console.info('dev prerun 1');
  await next();
  console.info('dev postrun 1');
})
export class DevCommand extends Command {
  @Middleware(async (_ctx, next) => {
    console.info('dev prerun 2');
    await next();
    console.info('dev postrun 2');
  })
  async run() {
    // nothing
  }
}

// debug command
@DefineCommand({
  command: 'debug',
})
@Middleware(async (_ctx, next) => {
  console.info('debug prerun 1');
  await next();
  console.info('debug postrun 1');
})
export class DebugCommand extends DevCommand {
  @Middleware(async (_ctx, next) => {
    console.info('debug prerun 2');
    await next();
    console.info('debug postrun 2');
  })
  async run() {
    super.run();
  }
}

执行 my-bin debug 后会输出

# -----   指令执行开始 ----
dev prerun 1            # --> DevCommand>class_middleware
debug prerun 1          # --> DebugCommand>class_middleware
# -----   run() 执行开始 ----
debug prerun 2          # --> DebugCommand>run_middleware
# -----   super.run() 执行开始 ----
dev prerun 2            # --> DevCommand>run_middleware
dev postrun 2           # --> DevCommand>run_middleware
# -----   super.run() 执行结束 ----
debug postrun 2         # --> DebugCommand>run_middleware
# -----   run() 执行结束 ----
debug postrun 1         # --> DebugCommand>run_middleware
dev postrun 1           # --> DevCommand>run_middleware
# -----   指令执行结束 ----

这里分两种情况:

  • 一种是绑定在类上的中间件,会直接合并:
    • 比如 Dev 定义的类中间件是 A ,Debug 定义的类中间件是 B ,那么在 Debug 中的类中间件列表会组合成 [ A, B ]
  • 另一种是绑定在 run 函数的中间件,当在 DebugCommand 的 run 函数中调用 super.run 的时候,就会执行 Dev 的 run 函数中间件。
    • 所以如果不想触发 Dev 的 run 函数中间件,不调用 super.run 即可 ...

高级功能

插件机制

插件的机制跟定义跟 artus 的插件一样的,当插件中通过 DefineCommand 定义了指令,也会自动被加载,所以插件可以做的很强大且方便,可以用来拓展指令,也可以用来全局拦截,甚至能够用来覆盖已有指令。

插件的实现,可以参考 examples 中的插件例子

配置及开启插件的方式

// config/plugin.ts
export default {
  help: {
    enable: true,
    package: 'plugin-help',
  },
};

框架继承

跟 artus 中的上层框架的继承一样,在 config/framework.ts 中定义需要继承的 CLI 框架即可。可以参考 examples 中的上层封装例子:

每个 CLI 都可以作为一个上层框架被更上层的 CLI 所继承,只需要配置

// config/framework.ts

export default {
  package: 'your-cli-name'
}

多环境

该能力也是 artus 提供的,可以根据不同环境,让同一个指令产生不同的功能,只需要在 plugin.{env}.ts 配置不同插件即可,比如如下配置

// config/plugin.ts
export default {
  codegen: {
    enable: true,
    package: 'plugin-codegen',
  },
};

// config/plugin.prod.ts
export default {
  codegen: false,
  codegenExtra: {
    enable: true,
    package: 'plugin-codegen-extra',
  },
};

当默认环境执行 CLI ,此时 codegen 插件起作用,当时当带上环境变量 ARTUS_CLI_ENV=prod 执行 CLI 时,codegen 会被关闭,codegenExtra 将会起作用。

该功能适合的场景:比如同个 dev 指令,在不同租户环境下执行不同的逻辑,或者同个 build 指令,本地跟在构建机器上跑不同逻辑。非常适合做这种同指令不同场景做差异化的功能。

除了通过环境变量传环境参数,在入口文件的 start 方法中也可以传,CLI 可以自己决定如何控制该参数( 比如读取文件配置等 )

#!/usr/bin/env node

import { start } from '@artus-cli/artus-cli';

start({
  baseDir: __dirname,
  artusEnv: 'prod', // 可以在这里传环境
});

指令注入

DefineCommand 内部对 Injectable 做了包装,所以定义的指令也可以直接注入到其他指令中执行,比如

// test command
@DefineCommand({
  command: 'test',
})
export class TestCommand extends Command {
  async run() {
    console.info('test');
  }
}

// coverage command
@DefineCommand({
  command: 'cov',
})
export class CovCommand extends Command {
  @Inject()
  testCommand: TestCommand;

  async run() {
    console.info('coverage');
    return this.testCommand.run();
  }
}

指令重定向

存在一种场景需要对指令做重定向( 即更改执行指令 ),框架提供了 Utils 类( 下文有详细介绍 ),其中具备一些实用的工具函数。

比如上面的注入指令的例子,可以直接用重定向的方式

import { DefineCommand, Command, Helper } from '@artus-cli/artus-cli';

// test command
@DefineCommand({
  command: 'test',
})
export class TestCommand extends Command {
  async run() {
    console.info('test');
  }
}

// coverage command
@DefineCommand({
  command: 'cov',
})
export class CovCommand extends Command {
  @Inject()
  helper: Helper;

  async run() {
    console.info('coverage');

    // 参数格式跟 process.argv 一致,也可以写 flags 
    return this.helper.redirect([ 'test' ]);
  }
}

指令冲突与覆盖

如果两个指令的 command 除了 arugments 之外是一样的,为了避免开发时不小心写了同样的 command 导致难以快速排查出原因,框架目前针对同样的 command 会报错提醒指令冲突。

如果开发者确认就是需要覆盖指令,可以在 DefineCommand 的参数中传入 overrideCommand 参数来强制覆盖。

import { DefineCommand, Command } from '@artus-cli/artus-cli';

// test command
@DefineCommand({
  command: 'test',
})
export class TestCommand extends Command {
  async run() {
    console.info('test');
  }
}

// new test command
@DefineCommand({
  command: 'test',
}, { overrideCommand: true }) // 标识强制覆盖
export class NewTestCommand extends Command {
  async run() {
    console.info('new test');
  }
}

Program

Program 是框架提供的 Singleton 原型,内置了一些便捷 API ,可以在生命周期中注入并使用,相关能力如下:

注册 Option

可以通过 Program 的 option 方法指定全局 Option 或者针对部分指令添加 Option 。使用方式如下

@LifecycleHookUnit()
export default class UsageLifecycle implements ApplicationLifecycle {
  @Inject()
  private readonly program: Program;

  @LifecycleHook()
  async configDidLoad() {
    const { rootCommand } = this.program;
    // 注册全局生效的 Option
    this.program.option({
      help: {
        type: 'boolean',
        alias: 'h',
        description: 'Show Help',
      },
    });

    // 注册只对根指令生效的 option
    this.program.option({
      version: {
        type: 'boolean',
        alias: 'v',
        description: 'Show Version',
      },
    }, [ rootCommand ]);
  }
}
注册中间件

除了前面通过装饰器注册中间件,也可以在 lifecycle 中通过 program 注册中间件( 三种中间件均支持 ),比如内置的 plugin-version 的实现:

// index.ts

@LifecycleHookUnit()
export default class VersionLifecycle implements ApplicationLifecycle {
  @Inject()
  private readonly program: Program;

  @LifecycleHook()
  async configDidLoad() {
    const { rootCommand } = this.program;
    this.program.option({
      version: {
        type: 'boolean',
        alias: 'v',
        description: 'Show Version',
      },
    }, [ rootCommand ]);

    // intercept root command and show version
    this.program.useInCommand(rootCommand, async (ctx: CommandContext, next) => {
      const { args } = ctx;
      if (args.version) {
        return console.info(this.program.version || '1.0.0');
      }

      await next();
    });
  }
}

注册三种中间件的方法分别是:

  • use 注册 pipeline 中间件
  • useInCommand 注册指令中间件( 通过 program 注册到 command 的中间件不会被继承 )
  • useInExecution 注册 run 函数中间件
Utils

Utils 是框架提供的在 Execution 阶段使用的工具类,可以用于中间件或者在指令中注入并使用。提供了两个方法:

  • redirect(argv: string[]) 重定向指令,会新建 pipeline ,上面有过介绍。
  • forward(clz: typeof Command, args?: T) 转发指令,入参是指令类,也可以传入参数( 如果传了会覆盖已有解析出来的参数 )。
    • 跟 redirect 的差异:在当前 pipeline 触发( 即不会触发 pipeline middleware ),而 redirect 会新建 pipeline。

其他待实现功能

  • 支持应用侧的配置,类似 .eslintrc
  • 支持 Hooks( 其实中间件似乎已经够用? )
  • Async hooks 引入( 是否可以优化编程界面? )

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.