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 🙏

© 2024 – Pkg Stats / Ryan Hefner

slet

v1.4.8

Published

Downloads

51

Readme

slet

slet = servlet in Node.js 7.x

NPM version Build codecov.io js-standard-style

Features

  • Micro Kernel
  • Pluggable Controller
  • Build-in Router && Auto-mount Router
  • Auto-inject Controller Dependency
  • Convention over Configuration

Getting Start

安装slet模块

$ npm i -S slet

从app.js开始

'use strict';

const Slet = require('slet');
const app = new Slet({
    root: __dirname,
    debug: true
});

app.router('/', './basicctrl')  

app.start(3000)

编写basicctrl.js

'use strict';

const BasicController = require('slet').BasicController

module.exports = class MyBasicController extends BasicController {
  constructor(app, ctx, next) {
    super(app, ctx, next)
  }
  
  get() { 
    let a = this.query.a
    // this.renderType='view'
    return {
      a: 'this is a',
      b: {
        c: 'ssddssdd a= ' + a
      }
    }
  } 
}

启动server

最后,执行app.js,启动server

$ node app.js

查验结果

在浏览器中打开 http://127.0.0.1:3000/?a=2

更多示例

Controller

生命周期

在src/Slet.js中,调用如下

// before
ctrl.before()

// only {verb} filter
ctrl.{verb}_filter = []

// execute {verb}(),比如get(){}、post(){}
ctrl.{verb}()

// after
ctrl.after()
  • before 是{verb}()之前调用的
  • after 是{verb}()之后调用的
  • ctrl.{verb}_filter = [] 利用koa 2.x中间件原理,拦截{verb}()请求前和请求后

另外还提供了一个辅助定义别名的 alias 方法

alias 别名定义:如果支持bodyparser,那么把this.ctx.request.body简化为this.pp,避免特别长的方法调用。

alias() {
  if (this.ctx.request.body) {
      this.pp = this.ctx.request.body
  }
}

all方法适用于处理所有verb的请求

如果有all方法,也有对应的verb请求,此种情况下,只会执行all()

BaseController

module.exports = class BaseController {
  constructor(app, ctx, next) {
    this.app = app
    this.ctx = ctx
    this.next = next
    this.renderType = 'default'
    this.data = {}
    this.tpl = 'index'
    
    this.result = '{verb}() call result'
  }

  // lifecycle
  before() {
    
  }
  
  alias() {
    if (this.ctx.request.body) {
        this.pp = this.ctx.request.body
    }
  }
  
  after() {
    
  }
}

说明

  • renderType:default | view

扩展controller

定义自己的controller,

  • 一定要注意名字,它会被用于子类继承,所以一定要见名之意。
  • 继承自BaseController
  • 可选实现上面的生命周期方法
'use strict';

const BaseController = require('slet-basecontroller')

module.exports = class BasicController extends BaseController{
  constructor(app, ctx, next) {
    super(app, ctx, next)
    
    this.query = ctx.query

    this.global_filter = ['koa-bodyparser']
  }
}

在app.js中使用defineController方法加载这个控制器

app.defineController(require('slet-basiccontroller'))

然后,在自己的类中

'use strict';

const BasicController = require('../../../').BasicController

module.exports = class SomeController extends BasicController {
  constructor(app, ctx, next) {
    super(app, ctx, next)
  }
  
  get() { 
    var a = this.query.a
    
    return {
      a:1,
      b: a
    }
  } 
}

Slet会自动注入依赖,所以请遵守以下约定

// 约定:
// 
// - 所有的模块必须以slet-开头
// - 对外暴露的class的小写,作为后面的名字
//    - 比如:BasicController,对应的模块为slet-basiccontroller
// 

Router

第一种,暴露path和controller

(controller内部不需要path)

app.router('/', require('./ctrl') )  

or

app.router('/', './ctrl')  

已测

第二种,将path写到controller里

2.1 通过对象(可以实现,但不如通过路径方便)

如果依赖已安装,即app.defineController(require('slet-viewcontroller')),这样才加载的时候才不会报错。

app.router(require('./pathctrl') )  

如果没有装依赖,那么需要在asyncRouter里注册这样的路由。asyncRouter会自动检测root下面的所有依赖,并提前注入。

app.asyncRouter(function(){
   app.router('/2', require('./viewctrl') )  
}) 

or

2.2 通过路径(推荐)

app.router('./pathctrl')  

已测

pathctrl代码

'use strict';

const ApiController = require('..').Base

class PathController extends ApiController {
  constructor(ctx, next) {
    super(ctx, next)
    
    this.path = '/c'
  }
  
  get() {
    var a = this.query.a
    console.log(a)
    return {
      dddd:1,
      b: a
    }
  } 
}

PathController.path = '/b'

module.exports = PathController

优先级

app.router('/d', './controller/a') > this.path= '/c' > Controller.path='/b'

亦即

'/d' > '/c' > '/b

第三种,指定路径加载

app.routerDir('app/controller' )  

此种情况会默认加载某个目录下的controller,请确保你的controller里有path,无论是属性,还是static属性方式都行。

已测

Filter

分类

  • app.use 和koa的use用法是一样的
  • global_filter 从全局的middlewares里按照名字取,一般定义在base类里,如果需要子类也可以重写
    • 示例this.global_filter = ['koa-bodyparser', 'koa-views']
  • {verb}_filter 是特定请求verb之的拦截器,仅限于当前verb
    • 示例this.get_filter = [this.log]

加载顺序

app.use > global_filter(父类默认继承) > {verb}_filter (当前文件) > {verb}() (当前文件)

app.use

和Koa 2.x的use用法是一样的,参数Koa 2.x的中间件,只能用于加载app级别的filter

参见 https://github.com/koajs/koa/tree/v2.x

global_filter说明

通过app.defineMiddleware()方法定义custom_filter(也可以写到配置里,稍后实现)

app.defineMiddleware('custom_filter', function(ctx, next){
    console.log('a before')
    return next().then(function(){
      console.log('a after')
    })    
})

或者

app.middlewares['koa-views'] = views(this.viewPath, this.opts.views.option)

然后在对应的类中

class PathController extends BaseController {
  constructor(ctx, next) {
    super(ctx, next)
    
    this.path = '/c'
    this.global_filter.push('custom_filter')
  }

  ...
}

至此,完成了自定义global filter功能。

其实this.global_filter是数组,如果想调整顺序也可以的。

{verb}_filter说明

在构造函数里指定filter,所谓的filter即Koa 2.x的中间件,规则是verb + '_filter'

举例:

  • this.get_filter = [] 已测
  • this.post_filter = [] 已测

此filter是请求发起之后才生效,所以测试请发起http请求。

'use strict';

const ApiController = require('..').Base

class PathController extends ApiController {
  constructor(ctx, next) {
    super(ctx, next)
    
    this.get_filter = [this.log]
  }
  
  log(ctx, next){
    console.log('before')
    return next().then(function(){
      console.log('after')
    })
  }

  get() {
    var a = this.query.a
    console.log(a)
    return {
      dddd:1,
      b: a
    }
  } 
}

PathController.path = '/b'

module.exports = PathController

这里log即Koa 2.x中间件经典写法

 log(ctx, next){
    console.log('before')
    return next().then(function(){
      console.log('after')
    })
  }

在处理get之前,会走next()方法之前的内容。在处理完get之后,会有next后的then部分。可以有效的拦截前后2种,所以filter无需分前后,一个即可。

Config

{
    "debug": false,

    "views" :{
        "path" : ".",
        "option": { "map": {"html": "nunjucks" }}
    },
    "automount": {
        "path": "controllers",
        "option": {
            "recurse": true
        }
    },

    "mockCtx": {
        "request":{
            "body": {

            }
        }
    }
}

可视化todo

  • 全局中间件
  • 创建Controller
    • path
    • get|post等方法
    • 方法前后,配置中间件

根据path决定,是否走全局路由,走哪些全局路由

每个请求都有自己的中间件栈

继承api

  • get|post
  • upload
  • sesssion
  • db 继承view

Plugins

已有

方法1

'use strict'

const StaticPlugin = require('slet-plugin-static')

const Slet = require('slet')

// 经典用法
Slet.plugin(StaticPlugin)

const app = new Slet({
  root: __dirname,
  debug: false
})

// lazy load plugin
app.static()

// app.start(3005)
module.exports = app

步骤

  • 1)const StaticPlugin = require('.')
  • 2)Slet.plugin(StaticPlugin)
    1. app.static()

方法2

'use strict';

const StaticPlugin = require('slet-plugin-static')

const Slet = require('slet');

// 经典用法
app.plugin(StaticPlugin)

const app = new Slet({
    root: __dirname,
    debug: true
});

app.static()

app.start(3000)

插件定义方法

module.exports = class StaticPlugin {

  static () {
    if (!this.opts.static) {
      this.opts.static = {
        path: this.opts.root + '/public',
        opts: {} // https://github.com/koajs/static#options
      }
    }
    
    this.use(require('koa-static')(this.opts.static.path, this.opts.static.opts));
  }
}

说明:

  • this.opts是从slet的构造函数里传入的,可以多处改写
  • this.use即koa 2.x的use方法,传中间件即可
  • 只有用到static()方法的时候,才加载require('koa-static'),懒加载

扩展

controller扩展

  • db
  • session
  • upload
  • uploadCdn

每个都继承相应的Controller即可,最好是作为单独模块,发布到npm上。可以非常好的复用。

filter扩展

通过filter方式,实现扩展。所有的扩展都是Koa 2.x标准中间件,可以更好的复用已有的中间件

plugin

采用mix的方式将插件挂载进去

集成

第三方集成

sletTest

supertest是express和koa里常用的api测试工具,非常简单,方便,如果在slet中想集成supertest也是非常简单的,原因是slet的listen和run方法返回的koa的app.listen,所以集成方式是一样的。为了简单,这里提供slettest简单封装。

$ npm i -D slettest

app.js

'use strict';

const Slet = require('slet');
const app = new Slet({
    root: __dirname,
    debug: true
});

// response
app.use(ctx => {
  ctx.body = 'Hello Koa'
})

module.exports = app

测试代码

import test from 'ava'

var app = require('./app')
var superkoa = require('.')

test.cb('superkoa()', t => {
  superkoa(app)
    .get('/')
    .expect(200, function (err, res) {
      t.ifError(err)
      t.is(res.text, 'Hello Koa', 'res.text == Hello Koa')
      t.end()
    })
})

test('yield superkoa()', function * (t) {
  let res = yield superkoa(app)
    .get('/')

  t.is(200, res.status)
  t.is(res.text, 'Hello Koa', 'res.text == Hello Koa')
})

test('async superkoa()', async (t) => {
  let res = await superkoa(app)
    .get('/')

  t.is(200, res.status)
  t.is(res.text, 'Hello Koa', 'res.text == Hello Koa')
})

更多文档,参见 http://visionmedia.github.io/superagent/