Skip to content

Commit 9d71ac7

Browse files
aorinevobcoe
authored andcommitted
feat: add support for global middleware, useful for shared tasks like metrics (#1119)
1 parent 744d299 commit 9d71ac7

6 files changed

Lines changed: 104 additions & 3 deletions

File tree

appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ install:
1212
# Get the latest stable version of Node 0.STABLE.latest
1313
- ps: Install-Product node $env:nodejs_version
1414
# Typical npm stuff.
15-
- npm -g install npm@latest
15+
- npm i npm@5
1616
- npm install
1717

1818
# Post-install test scripts.

docs/api.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,35 @@ To submit a new translation for yargs:
929929
<a name="nargs"></a>.nargs(key, count)
930930
-----------
931931

932+
.middleware(callbacks)
933+
------------------------------------
934+
935+
Define global middleware functions to be called first, in list order, for all cli command.
936+
937+
The `callbacks` parameter can be a function or a list of functions. Each callback gets passed a reference to argv.
938+
939+
```js
940+
const mwFunc1 = argv => console.log('I\'m a middleware function');
941+
const mwFunc2 = argv => console.log('I\'m another middleware function');
942+
943+
yargs
944+
.command('myCommand', 'some command', {}, function(argv){
945+
console.log('Running myCommand!');
946+
})
947+
.middleware([mwFunc1, mwFunc2]).argv;
948+
```
949+
When calling `myCommand` from the command line, mwFunc1 gets called first, then mwFunc2, and finally the command's handler. The console output is:
950+
```
951+
I'm a middleware function
952+
I'm another middleware function
953+
Running myCommand!
954+
```
955+
956+
957+
<a name="middleware"></a>.middleware(callbacks)
958+
--------------------
959+
960+
932961
The number of arguments that should be consumed after a key. This can be a
933962
useful hint to prevent parsing ambiguity. For example:
934963

lib/command.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@ const DEFAULT_MARKER = /(^\*)|(^\$0)/
99
// handles parsing positional arguments,
1010
// and populating argv with said positional
1111
// arguments.
12-
module.exports = function command (yargs, usage, validation) {
12+
module.exports = function command (yargs, usage, validation, globalMiddleware) {
1313
const self = {}
1414
let handlers = {}
1515
let aliasMap = {}
1616
let defaultCommand
17+
globalMiddleware = globalMiddleware || []
1718
self.addHandler = function addHandler (cmd, description, builder, handler, middlewares) {
1819
let aliases = []
1920
handler = handler || (() => {})
2021
middlewares = middlewares || []
22+
middlewares = globalMiddleware.concat(middlewares)
2123
if (Array.isArray(cmd)) {
2224
aliases = cmd.slice(1)
2325
cmd = cmd[0]

lib/middleware.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module.exports = function (globalMiddleware, context) {
2+
return function (callback) {
3+
if (Array.isArray(callback)) {
4+
Array.prototype.push.apply(globalMiddleware, callback)
5+
} else if (typeof callback === 'object') {
6+
globalMiddleware.push(callback)
7+
}
8+
return context
9+
}
10+
}

test/middleware.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
'use strict'
2+
/* global describe, it, beforeEach, afterEach */
3+
4+
const middlewareFactory = require('../lib/middleware')
5+
let yargs
6+
require('chai').should()
7+
8+
describe('middleware', () => {
9+
beforeEach(() => {
10+
yargs = require('../')
11+
})
12+
13+
afterEach(() => {
14+
delete require.cache[require.resolve('../')]
15+
})
16+
it('should add a list of callbacks to global middleware', () => {
17+
const globalMiddleware = []
18+
19+
middlewareFactory(globalMiddleware)(['callback1', 'callback2'])
20+
21+
globalMiddleware.should.have.lengthOf(2)
22+
})
23+
24+
it('should add a single callback to global middleware', () => {
25+
const globalMiddleware = []
26+
27+
middlewareFactory(globalMiddleware)({})
28+
29+
globalMiddleware.should.have.lengthOf(1)
30+
})
31+
32+
it('runs all middleware before reaching the handler', function (done) {
33+
yargs(['mw'])
34+
.middleware([
35+
function (argv) {
36+
argv.mw1 = 'mw1'
37+
},
38+
function (argv) {
39+
argv.mw2 = 'mw2'
40+
}
41+
])
42+
.command(
43+
'mw',
44+
'adds func list to middleware',
45+
function () {},
46+
function (argv) {
47+
// we should get the argv filled with data from the middleware
48+
argv.mw1.should.equal('mw1')
49+
argv.mw2.should.equal('mw2')
50+
return done()
51+
}
52+
)
53+
.exitProcess(false) // defaults to true.
54+
.argv
55+
})
56+
})

yargs.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const Y18n = require('y18n')
1111
const objFilter = require('./lib/obj-filter')
1212
const setBlocking = require('set-blocking')
1313
const applyExtends = require('./lib/apply-extends')
14+
const middlewareFactory = require('./lib/middleware')
1415
const YError = require('./lib/yerror')
1516

1617
exports = module.exports = Yargs
@@ -21,6 +22,7 @@ function Yargs (processArgs, cwd, parentRequire) {
2122
let command = null
2223
let completion = null
2324
let groups = {}
25+
let globalMiddleware = []
2426
let output = ''
2527
let preservedGroups = {}
2628
let usage = null
@@ -31,6 +33,8 @@ function Yargs (processArgs, cwd, parentRequire) {
3133
updateFiles: false
3234
})
3335

36+
self.middleware = middlewareFactory(globalMiddleware, self)
37+
3438
if (!cwd) cwd = process.cwd()
3539

3640
self.$0 = process.argv
@@ -117,7 +121,7 @@ function Yargs (processArgs, cwd, parentRequire) {
117121
// instances of all our helpers -- otherwise just reset.
118122
usage = usage ? usage.reset(localLookup) : Usage(self, y18n)
119123
validation = validation ? validation.reset(localLookup) : Validation(self, usage, y18n)
120-
command = command ? command.reset() : Command(self, usage, validation)
124+
command = command ? command.reset() : Command(self, usage, validation, globalMiddleware)
121125
if (!completion) completion = Completion(self, usage, command)
122126

123127
completionCommand = null

0 commit comments

Comments
 (0)