npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

slowly

v1.2.1

Published

一个node.js命令行工具的框架,类似与egg,你可以像写egg一个写命令行工具,slowly的目的很简单,让你更专注业务代码的开发,剩下的交给slowly就行了。

Downloads

2

Readme

简介

一个node.js命令行工具的框架,类似与egg,你可以像写egg一个写命令行工具,slowly的目的很简单,让你更专注业务代码的开发,剩下的交给slowly就行了。

快速入门

一步步教你搭建一个基于slowly框架的命令行工具

安装

npm i slowly --save

如果你需要使用typescript来开发,还需安装

npm i typescript -g

初始化

创建文件,目录结构如下:

slowly-example
├── dist
├── src
│   ├── config
│   │   └── index.ts //config
│   ├── controller // controller
│   │   ├── home.ts 
│   │   └── index.ts
│   ├── index.ts
│   ├── middleware
│   │   └── inquirer.ts
│   └── service
│       └── index.ts
└── package.json

创建命令行工具的入口文件,并且引入slowly,初始化app,注册命令行路由

// src/index.ts

#!/usr/bin/env node
import { App, Router } from 'slowly'
const app = new App({
  version: '1.0.0',
  name: 'test-slowly'
});
const router = new Router()
const { controller } = app.ctx;
router.register('init', 'test-slowly init', async (ctx) => {
  //you code
})
app.use(router.routes());

你没有看错,就是egg的写法,是不是很酷!!!!!

App Config

{
  version: string; //命令行版本号
  name: string; //名称
  userConfigFile?: string; //用户配置文件路径
}

Option

router支持直接注册或者链式注册option

直接注册

直接注册无法给option添加额外说明

router.register('init [-u | --uri] <-n | --name>', 'test-slowly init', async (ctx) => {
  //you code
})

链式注册

使用option方法注册,可以添加说明

router.register('init', 'test-slowly init')
      .option('<-u | --uri>', 'swagger docs url')
      .option('<-n | --name>', 'swagger service name')
      .action(controller1, controller2)

slowly的option配置规则比较简单,如下:

 <-u | --uri> or  [-u | --uri]

其中u为缩写, uri为option全称,可以通过ctx.query.uri获取其值

使用<>包含起来的代表该项为必须项,如果不输入slowly会自动提示

[Error]: 
option <-u | --uri> is required

如果用的是[]则表示该项非必须

特别地

  1. 只输入"test-slowly init -u"时,uri默认为true
  2. 如遇到输入多个option,slowly会自动收集为数组,例如输入"test-slowly init -u a -u b -u",uri为 ['a', 'b', true]
  3. 默认类型转换,因为命令行输入都为字符串,slowly将"true"、"false"、/\d+/分别转换为truefalsenumber
  4. 当遇到[...reset],会将非option项收集到ctx.query.reset里面,被收集过的项将从ctx.params里移除

router注册:

router.register('init [...reset] <-n | --name>', 'test-slowly init');

命令行输入:

test-slowly init template1 template2 -n test_user template3

结果为:

ctx.query = {
    name: 'test_user',
    reset: ['template1', 'template2', 'template3']
}

添加配置文件

slowly的配置文件在config文件夹下,会自动去读取合并,可以通过app.configapp.ctx.config获取

编写Controller

// src/controller/index.ts
import { Controller } from 'slowly'

export default class TestController extends Controller {
  async init() {
    this.ctx.message = 'hello world'
  }
}

将控制器方法注册到路径里面,register方法可以添加middleware、自定义函数、controller方法,特别地controller方法必须放在最后

// src/index.ts
const { controller } = app;
router.register('init', 'test-slowly init', async (ctx, next) => {
    //your code
    await next()
},  controller.index.init);

编写service

controller只处理简单逻辑,如需要获取数据或者复杂业务逻辑,就交给Service

//src/service/index.ts
import { Service } from 'slowly'
export default class TestService extends Service {
  async getMessage() {
    return 'hello world'
  }
}

可以在之前的编写的controller使用

// src/controller/index.ts
import { Controller } from 'slowly'

export default class TestController extends Controller {
  async init() {
    const { service } = this.ctx;
    this.ctx.message = await service.index.getMessage();
  }
}

编写middleware

当我们需要在命令行里面跟用户有交互,这时就可以使用middleware来处理,结合inquirer来捕获用户的输入数据,再交给下一步处理

//src/middleware/inquirer.ts

import inquirer from 'inquirer';
export default function inquirerMiddleware(questions) {
  return async function(ctx, next) {
    const data = inquirer.prompt(questions)
    ctx.query = {
      ...ctx.query,
      ...data
    }
    await next();
  }
}

然后将这个中间件注册到路由里即可

// src/index.ts

const { controller, middleware } = app;
router.register('init', 'test-slowly init', middleware.inquirer([{
  type: 'input',
  message: 'set name',
  name: 'name'
}]), controller.index.init);

事件监听

很多时候我们都需要额外的监听一些事件,slowly可以通过app.ctx.emitter对象去监听对应的事件,例如需要监听help

app.ctx.emitter.on('help', (name) => {
  //其中name为对应的命令名称
})

事件名称说明如下:

  1. on("command:help", (command: string) => {}): 子命令onHelp
  2. on("illegality:option", (command: string, illegalityRouteOptions) => {}): 输入了非法的option
  3. on("verifyOption:fail", (command, options) => {}): option验证失败

ctx对象

从输入命令行到node进程关闭,ctx对象都会贯彻整个生命周期

cwd: string; //引入app的入口文件路径
argv: IArgv, //命令行输入
routes: {
    [key: string]: IRouteConfig
}
version: string;
emitter: EventEmitter; //事件对象
name?: string;
middleware?: {
    [key: string]: any
};
controller: {
    [key: string]: any
},
service?: {
    [key: string]: any
},
query?: { //命令行option
    [key: string]: any;
};
readonly params: Array<string>;
curl: (url: string, ...args: Array<any>) => Promise<any> | void; //基于urllib封装的方法