├── .gitignore ├── LICENSE.md ├── README.md ├── bin └── benchr ├── example.js ├── lib ├── cli.js ├── docopt.txt ├── reporters │ ├── console.js │ └── json.js └── runner.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | test/ 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Robert Klep 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## benchr 2 | 3 | Node.js benchmark runner, modelled after [`Mocha`](http://mochajs.org/) and [`bencha`](https://www.npmjs.com/package/bencha), based on [Benchmark.js](http://benchmarkjs.com/). 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ npm i benchr [-g] 9 | ``` 10 | 11 | ### Usage 12 | 13 | Run the `benchr` script and provide it with files containing benchmarks: 14 | 15 | ``` 16 | $ benchr benchmarks/**/*.js 17 | $ benchr benchmarks/suite1.js benchmarks/suite2.js benchmarks/suite3.js 18 | ``` 19 | 20 | ### Options 21 | 22 | ``` 23 | $ benchr -h 24 | benchr – benchmark runner 25 | 26 | Usage: 27 | benchr [options] ... 28 | 29 | Options: 30 | -h --help Show this screen 31 | -V --version Show version 32 | -d --delay= Delay between test cycles, in seconds [default: 0] 33 | -M --min-time= Minimum run time per test cycle, in seconds [default: 0] 34 | -m --max-time= Maximum run time per test cycle, in seconds [default: 5] 35 | -g --grep= Only run suites matching pattern 36 | -R --reporter= Reporter to use, either a file path or built-in (`console` or `json`) [default: console] 37 | -r --require= `require()` a module before starting (for instance, `babel-register`) 38 | -p --progress Show progress (depending on reporter) 39 | -P --pretty-print Pretty-print output (depending on reporter) 40 | -v --verbose More verbose output (depending on reporter) 41 | ``` 42 | 43 | ### Suites + benchmarks 44 | 45 | A benchmark file declares one or more suites, each with one or more benchmarks to run. 46 | 47 | #### Syntax 48 | 49 | ```javascript 50 | suite(NAME[, OPTIONS], FN); 51 | benchmark(NAME[, OPTIONS], FN); 52 | ``` 53 | 54 | Calling `suite()` is optional. 55 | 56 | #### Synchronous benchmarks 57 | 58 | ```javascript 59 | suite('Finding a substring', () => { 60 | 61 | benchmark('RegExp#test', () => { 62 | /o/.test('Hello World!'); 63 | }); 64 | 65 | benchmark('String#indexOf', () => { 66 | 'Hello World!'.indexOf('o') > -1; 67 | }); 68 | 69 | benchmark('String#match', () => { 70 | !!'Hello World!'.match(/o/); 71 | }); 72 | 73 | }); 74 | ``` 75 | 76 | (taken from the example on the [Benchmark.js](http://benchmarkjs.com/) website) 77 | 78 | #### Asynchronous benchmarks 79 | 80 | ##### Using promises 81 | 82 | Return a promise from a benchmark function and it will be tested asynchronously: 83 | 84 | ```javascript 85 | suite('Timeouts', () => { 86 | 87 | benchmark('100ms', () => { 88 | return new Promise((resolve) => { 89 | setTimeout(resolve, 100); 90 | }); 91 | }); 92 | 93 | benchmark('200ms', () => { 94 | return new Promise((resolve) => { 95 | setTimeout(resolve, 200); 96 | }); 97 | }); 98 | 99 | }); 100 | ``` 101 | 102 | **NOTE**: to determine if a function under test returns a promise, it is called once before the tests start. If this is undesirable, for instance due to side-effects, set the `promises` option for the benchmark or the entire suite: 103 | 104 | ```javascript 105 | suite('Timeouts', { promises : true }, () => { ... }); 106 | benchmark('100ms', { promises : true }, () => { ... }); 107 | ``` 108 | 109 | ##### Using callbacks 110 | 111 | If a benchmark takes an argument, it is assumed to be a continuation callback (pass any errors as first argument to abort the test): 112 | 113 | ```javascript 114 | suite('Timeouts', () => { 115 | 116 | benchmark('100ms', (done) => { 117 | setTimeout(done, 100); 118 | }); 119 | 120 | benchmark('200ms', (done) => { 121 | setTimeout(done, 200); 122 | }); 123 | 124 | }); 125 | ``` 126 | 127 | #### Playing nice with linters 128 | 129 | To work around linter errors regarding `suite` and `benchmark` being undefined, your test files can export a function that would take `suite` and `benchmark` as its arguments, thereby making the linter happy: 130 | 131 | ```javascript 132 | module.exports = (suite, benchmark) => { 133 | suite('My test suite', () => { 134 | benchmark('Bench 1', ...); 135 | benchmark('Bench 2', ...); 136 | ... 137 | }) 138 | } 139 | ``` 140 | 141 | ### Using the Runner programmatically 142 | 143 | ``` 144 | const Runner = require('benchr'); 145 | const runner = new Runner({ 146 | reporter : Function, 147 | grep : String, 148 | delay : Number, 149 | minTime : Number, 150 | maxTime : Number, 151 | progress : Boolean, 152 | prettyPrint : Boolean, 153 | verbose : Boolean, 154 | }, [ "file1.js", "file2.js", ... ]); 155 | ``` 156 | 157 | All options map to the similarly-named command line options. 158 | 159 | ### Implementing a reporter 160 | 161 | A reporter is a CommonJS module that should export a function that gets passed a benchmark runner instance as argument. 162 | 163 | This runner instance implements the `EventEmitter` interface, and will emit the following events: 164 | 165 | * `run.start` / `run.complete` 166 | * `file.start` / `file.complete` 167 | * `suite.start` / `suite.complete` 168 | * `benchmark.start` / `benchmark.complete` 169 | 170 | The different parts are explained as follows: 171 | * a _"run"_ consists of one or more files containing benchmarks 172 | * a _"file"_ is a file that exports or contains one or more suites 173 | * a _"suite"_ is a structure that consists of one or more benchmarks (where each benchmark is used in a comparison between the other benchmarks in the same suite) 174 | * a _"benchmark"_ is a single test 175 | 176 | ### TODO 177 | 178 | - [x] Option to pass custom reported module 179 | - [x] Before/after hooks 180 | - [x] Benchmark/suite options (minTime, maxTime, ...) 181 | - [x] Separate reporters (very hardcoded now) 182 | - [x] Handle multiple "fastest" benchmarks better 183 | - [x] Promises support (just like Mocha) 184 | -------------------------------------------------------------------------------- /bin/benchr: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('../lib/cli')(); 4 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | suite('Finding a substring', function() { 2 | 3 | benchmark('RegExp#test', function() { 4 | /o/.test('Hello World!'); 5 | }); 6 | 7 | benchmark('String#indexOf', function() { 8 | 'Hello World!'.indexOf('o') > -1; 9 | }); 10 | 11 | benchmark('String#match', function() { 12 | !!'Hello World!'.match(/o/); 13 | }); 14 | 15 | }); 16 | 17 | // Using promises 18 | suite('Async timeout testing using promises', () => { 19 | 20 | benchmark('100ms', () => { 21 | return new Promise((resolve) => { 22 | setTimeout(resolve, 100); 23 | }); 24 | }); 25 | 26 | benchmark('200ms', () => { 27 | return new Promise((resolve) => { 28 | setTimeout(resolve, 200); 29 | }); 30 | }); 31 | 32 | }); 33 | 34 | suite('Async timeout testing using callbacks', () => { 35 | 36 | benchmark('100ms', (done) => { 37 | setTimeout(done, 100); 38 | }); 39 | 40 | benchmark('200ms', (done) => { 41 | setTimeout(done, 200); 42 | }); 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /lib/cli.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { docopt } = require('docopt'); 3 | 4 | // Parse command line options. 5 | const options = docopt(fs.readFileSync(__dirname + '/docopt.txt', 'utf8'), { 6 | version : require('../package').version 7 | }); 8 | 9 | module.exports = function Benchr() { 10 | let mod = options['--require']; 11 | if (mod) { 12 | try { 13 | require(mod); 14 | } catch(e) { 15 | return console.error(`Unable to require module '${ mod }':`, e); 16 | } 17 | } 18 | const Runner = require('./runner'); 19 | try { 20 | new Runner({ 21 | reporter : options['--reporter'], 22 | grep : options['--grep'], 23 | delay : options['--delay'], 24 | minTime : options['--min-time'], 25 | maxTime : options['--max-time'], 26 | progress : options['--progress'], 27 | prettyPrint : options['--pretty-print'], 28 | verbose : options['--verbose'], 29 | }, options['']).run(); 30 | } catch(e) { 31 | return console.error('Cannot instantiate Runner:', e.message); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /lib/docopt.txt: -------------------------------------------------------------------------------- 1 | 2 | benchr – benchmark runner 3 | 4 | Usage: 5 | benchr [options] ... 6 | 7 | Options: 8 | -h --help Show this screen 9 | -V --version Show version 10 | -d --delay= Delay between test cycles, in seconds [default: 0] 11 | -M --min-time= Minimum run time per test cycle, in seconds [default: 0] 12 | -m --max-time= Maximum run time per test cycle, in seconds [default: 5] 13 | -g --grep= Only run suites matching pattern 14 | -R --reporter= Reporter to use, either a file path or built-in (`console` or `json`) [default: console] 15 | -r --require= `require()` a module before starting (for instance, `babel-register`) 16 | -p --progress Show progress (depending on reporter) 17 | -P --pretty-print Pretty-print output (depending on reporter) 18 | -v --verbose More verbose output (depending on reporter) 19 | -------------------------------------------------------------------------------- /lib/reporters/console.js: -------------------------------------------------------------------------------- 1 | const { format } = require('util'); 2 | const Table = require('easy-table'); 3 | const chalk = require('chalk'); 4 | 5 | module.exports = runner => { 6 | runner.on('run.start', file => { 7 | }).on('run.complete', file => { 8 | }).on('file.start', file => { 9 | console.log('• %s:\n', file); 10 | }).on('file.complete', file => { 11 | }).on('suite.start', suite => { 12 | process.stdout.write(format('%s• %s', pad(2), suite.name)); 13 | }).on('suite.cycle', suite => { 14 | process.stdout.write('.'); 15 | }).on('error', err => { 16 | console.error(err); 17 | }).on('suite.complete', suite => { 18 | const fastest = suite.filter('fastest'); 19 | const successful = suite.filter('successful'); 20 | const table = new Table(); 21 | 22 | suite.forEach(function(bench) { 23 | if (bench.aborted) { 24 | table.cell('status', pad(4) + chalk.red('×')); 25 | } else { 26 | table.cell('status', pad(4) + chalk.green('✔')); 27 | } 28 | table.cell('name', italicize(chalk[ bench.aborted ? 'red' : 'green' ](bench.name))); 29 | if (bench.aborted) { 30 | table.cell('hz', chalk.red('ABORTED')); 31 | } else { 32 | table.cell('hz', bench.hz.toFixed(2).toString().replace(/\B(?=(\d{3})+\b)/g, ','), Table.padLeft); 33 | table.cell('label', 'ops/sec'); 34 | table.cell('rme', '±' + bench.stats.rme.toFixed(2) + '%'); 35 | table.cell('runs', '(' + bench.stats.sample.length + ' runs)'); 36 | 37 | // Show percentual difference between this benchmark and the fastest. 38 | if (fastest.length !== successful.length) { 39 | let diff = chalk.green(italicize('fastest')); 40 | if (bench !== fastest[0]) { 41 | diff = chalk.red('-' + ((1.0 - bench.hz / fastest[0].hz) * 100).toFixed(2) + '%'); 42 | } 43 | table.cell('diff', diff, Table.padLeft); 44 | } 45 | } 46 | table.newRow(); 47 | }); 48 | console.log('\n\n' + table.print()); 49 | if (successful.length > 1) { 50 | process.stdout.write(pad(4) + '➔ '); 51 | if (fastest.length === successful.length) { 52 | console.log('No discernible winner\n'); 53 | } else { 54 | console.log('Fastest is ' + chalk.green(italicize(fastest.map('name'))) + '\n'); 55 | } 56 | } 57 | }); 58 | } 59 | 60 | function italicize(s) { 61 | return '\x1b[3m' + s + '\x1b[23m'; 62 | } 63 | 64 | function pad(num, chr) { 65 | return Array(num + 1).join(chr || ' '); 66 | } 67 | -------------------------------------------------------------------------------- /lib/reporters/json.js: -------------------------------------------------------------------------------- 1 | const { platform } = require('benchmark'); 2 | 3 | module.exports = runner => { 4 | // Handle output options. 5 | const prettyPrint = runner.options.prettyPrint; 6 | const progress = runner.options.progress; 7 | const verbose = runner.options.verbose; 8 | 9 | // Handle runner events. 10 | let currentFile, currentSuite, stats = { platform, files : [] }; 11 | runner.on('run.start', file => { 12 | stats.started = new Date(); 13 | progress && process.stderr.write('R'); 14 | }).on('file.start', file => { 15 | progress && process.stderr.write('F'); 16 | currentFile = { 17 | name : file, 18 | suites : [], 19 | }; 20 | stats.files.push(currentFile); 21 | }).on('file.complete', file => { 22 | progress && process.stderr.write('f'); 23 | }).on('suite.start', suite => { 24 | progress && process.stderr.write('S'); 25 | currentSuite = { 26 | name : suite.name, 27 | benchmarks : [], 28 | }; 29 | currentFile.suites.push(currentSuite); 30 | }).on('suite.complete', suite => { 31 | progress && process.stderr.write('s'); 32 | 33 | // Add the name(s) of the fastest benchmark(s). 34 | currentSuite.fastest = suite.filter('fastest').map('name'); 35 | 36 | // Handle benchmark statistics. 37 | currentSuite.benchmarks = suite.map(bench => { 38 | if (verbose) return bench; 39 | delete bench.stats.sample; 40 | return { 41 | name : bench.name, 42 | hz : bench.hz, 43 | aborted : bench.aborted, 44 | stats : bench.stats, 45 | }; 46 | }); 47 | }).on('benchmark.start', () => { 48 | progress && process.stderr.write('B'); 49 | }).on('benchmark.complete', () => { 50 | progress && process.stderr.write('b'); 51 | }).on('run.complete', () => { 52 | progress && process.stderr.write('r\n'); 53 | stats.ended = new Date(); 54 | if (prettyPrint) { 55 | console.log(JSON.stringify(stats, null, 2)); 56 | } else { 57 | console.log('%j', stats); 58 | } 59 | }) 60 | }; 61 | -------------------------------------------------------------------------------- /lib/runner.js: -------------------------------------------------------------------------------- 1 | const { EventEmitter } = require('events'); 2 | const path = require('path'); 3 | const Benchmark = require('benchmark'); 4 | const reporters = require('require-all')(path.resolve(__dirname, 'reporters')); 5 | 6 | module.exports = class Runner extends EventEmitter { 7 | 8 | constructor(options, files) { 9 | super(); 10 | if (arguments.length === 0) { 11 | throw Error('invalid arguments'); 12 | } else if (arguments.length === 1) { 13 | files = options; 14 | options = {}; 15 | } 16 | this.files = Array.isArray(files) ? files : [ files ]; 17 | this.options = Object.assign({ 18 | reporter : require('./reporters/console'), 19 | grep : null, 20 | delay : 0, 21 | minTime : 0, 22 | maxTime : 5, 23 | progress : false, 24 | prettyPrint : false, 25 | verbose : false, 26 | }, options); 27 | 28 | // Initialize reporter. 29 | if (typeof this.options.reporter === 'string') { 30 | let { reporter } = this.options; 31 | try { 32 | // Try to resolve as a file name. 33 | this.options.reporter = require(path.resolve(process.cwd(), reporter)); 34 | } catch(e) { 35 | try { 36 | // Try to resolve as a built-in reporter. 37 | this.options.reporter = require(path.resolve(__dirname, 'reporters', reporter)); 38 | } catch(e) { 39 | throw Error(`unable to successfully resolve reporter '${ reporter }'`); 40 | } 41 | } 42 | } 43 | 44 | if (! (this.options.reporter instanceof Function)) { 45 | throw Error('`reporter` option not a function'); 46 | } 47 | this.options.reporter(this); 48 | } 49 | 50 | run(options) { 51 | this.emit('run.start'); 52 | this._run(this.files.shift()); 53 | } 54 | 55 | _run(file) { 56 | // No more files to run? 57 | if (file === undefined) { 58 | this.emit('run.complete'); 59 | return; 60 | } 61 | 62 | // Housekeeping 63 | this.emit('file.start', file); 64 | this.suites = []; 65 | 66 | // 😱 67 | global.suite = this.addSuite.bind(this); 68 | global.benchmark = this.addBenchmark.bind(this); 69 | 70 | // Import test file. 71 | let testFn = require(path.resolve(process.cwd(), file)); 72 | 73 | // Allow for ES6 default exports. 74 | if (typeof testFn === 'object' && testFn !== null && 'default' in testFn) { 75 | testFn = testFn.default; 76 | } 77 | 78 | // If the test file exports a factory function, call it to pass the `suite` 79 | // and `benchmark` functions. 80 | if (typeof testFn === 'function' && testFn.length === 2) { 81 | testFn(global.suite, global.benchmark); 82 | } 83 | 84 | const runSuite = suite => { 85 | // No more suites to run? 86 | if (suite === undefined) { 87 | // Run next file. 88 | this.emit('file.complete', file); 89 | return this._run(this.files.shift()); 90 | } 91 | 92 | // Check if suite matches the `grep` pattern (if any). 93 | if (this.options.grep) { 94 | let re = new RegExp(this.options.grep, 'i') 95 | if (! re.test(suite.name)) { 96 | // No match, start next suite. 97 | return runSuite(this.suites.shift()); 98 | } 99 | } 100 | this.emit('suite.run', suite); 101 | suite.run({ async : true }).on('complete', () => { 102 | //this.emit('suite.complete', suite); 103 | return runSuite(this.suites.shift()); 104 | }); 105 | } 106 | 107 | // No more suites left to run? Run next file. 108 | if (! this.suites.length) { 109 | this.emit('file.complete', file); 110 | return this._run(this.files.shift()); 111 | } 112 | return runSuite(this.suites.shift()); 113 | } 114 | 115 | addSuite(name, opts, fn) { 116 | if (typeof name === 'function') { 117 | fn = name; 118 | name = 'Suite#' + (this.suites.length + 1); 119 | opts = {}; 120 | } else if (typeof opts === 'function') { 121 | fn = opts; 122 | opts = {}; 123 | } 124 | 125 | // Create a new suite 126 | let suite = this.suites[this.suites.length] = new Benchmark.Suite(name); 127 | suite.opts = opts; 128 | 129 | // Register benchmarks and hooks. 130 | fn(); 131 | 132 | // Set up event handlers and start the suite. 133 | let suites = this.suites; 134 | let outputJSON = this.outputJSON; 135 | let completed = 0; 136 | let output = []; 137 | suite.on('start', () => { 138 | this.emit('suite.start', suite); 139 | }).on('cycle', () => { 140 | this.emit('suite.cycle', suite); 141 | }).on('complete', () => { 142 | this.emit('suite.complete', suite); 143 | }).on('error', err => { 144 | this.emit('suite.error', err); 145 | }); 146 | } 147 | 148 | addBenchmark(name, opts, fn) { 149 | // Implicitly add a suite when there aren't any yet while defining this benchmark. 150 | if (this.suites.length === 0) { 151 | this.addSuite(() => {}); 152 | } 153 | let suite = this.suites[this.suites.length - 1]; 154 | 155 | // Check arguments. 156 | if (typeof name === 'function') { 157 | fn = name; 158 | name = 'Benchmark#' + (suite.length + 1); 159 | opts = {}; 160 | } else if (typeof opts === 'function') { 161 | fn = opts; 162 | opts = {}; 163 | } 164 | 165 | // Nothing to do...? 166 | if (typeof fn !== 'function') return; 167 | 168 | // If fn accepts at least one argument, we'll assume that it takes 169 | // a callback, making the test asynchronous. 170 | let isDeferred = fn.length > 0 || opts.promises === true || suite.opts.promises === true; 171 | let usesPromises = opts.promises === true || suite.opts.promises === true; 172 | 173 | // Check to see if it returns something that looks like a promise, 174 | // also making it asynchronous. 175 | if (! isDeferred) { 176 | let ret = fn(); 177 | isDeferred = usesPromises = ret && ret.then; 178 | usesPromises = isDeferred; 179 | } 180 | 181 | // Wrap async tests. 182 | let func = isDeferred ? deferred => { 183 | if (usesPromises) { 184 | fn().then(function() { 185 | deferred.resolve(); 186 | }).catch(function(e) { 187 | deferred.benchmark.abort(); 188 | }); 189 | } else { 190 | fn(function(err) { 191 | if (err) return deferred.benchmark.abort(); 192 | deferred.resolve(); 193 | }); 194 | } 195 | } : fn; 196 | 197 | // Add the benchmark function to the suite. 198 | let runner = this; 199 | suite.add({ 200 | name : name, 201 | defer : isDeferred, 202 | fn : func, 203 | delay : Number(this.options.delay), 204 | minTime : Number(this.options.minTime), 205 | maxTime : Number(this.options.maxTime), 206 | onStart : function() { runner.emit('benchmark.start', this) }, 207 | onComplete : function() { runner.emit('benchmark.complete', this) }, 208 | }); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "benchr", 3 | "version": "4.3.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-regex": { 8 | "version": "3.0.0", 9 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 10 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" 11 | }, 12 | "ansi-styles": { 13 | "version": "3.2.0", 14 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", 15 | "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", 16 | "requires": { 17 | "color-convert": "1.9.1" 18 | } 19 | }, 20 | "benchmark": { 21 | "version": "2.1.4", 22 | "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", 23 | "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", 24 | "requires": { 25 | "lodash": "4.17.4", 26 | "platform": "1.3.5" 27 | } 28 | }, 29 | "chalk": { 30 | "version": "2.3.0", 31 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", 32 | "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", 33 | "requires": { 34 | "ansi-styles": "3.2.0", 35 | "escape-string-regexp": "1.0.5", 36 | "supports-color": "4.5.0" 37 | } 38 | }, 39 | "clone": { 40 | "version": "1.0.3", 41 | "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", 42 | "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=", 43 | "optional": true 44 | }, 45 | "color-convert": { 46 | "version": "1.9.1", 47 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", 48 | "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", 49 | "requires": { 50 | "color-name": "1.1.3" 51 | } 52 | }, 53 | "color-name": { 54 | "version": "1.1.3", 55 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 56 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 57 | }, 58 | "defaults": { 59 | "version": "1.0.3", 60 | "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", 61 | "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", 62 | "optional": true, 63 | "requires": { 64 | "clone": "1.0.3" 65 | } 66 | }, 67 | "docopt": { 68 | "version": "0.6.2", 69 | "resolved": "https://registry.npmjs.org/docopt/-/docopt-0.6.2.tgz", 70 | "integrity": "sha1-so6eIiDaXsSffqW7JKR3h0Be6xE=" 71 | }, 72 | "easy-table": { 73 | "version": "1.1.1", 74 | "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.1.tgz", 75 | "integrity": "sha512-C9Lvm0WFcn2RgxbMnTbXZenMIWcBtkzMr+dWqq/JsVoGFSVUVlPqeOa5LP5kM0I3zoOazFpckOEb2/0LDFfToQ==", 76 | "requires": { 77 | "ansi-regex": "3.0.0", 78 | "wcwidth": "1.0.1" 79 | } 80 | }, 81 | "escape-string-regexp": { 82 | "version": "1.0.5", 83 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 84 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 85 | }, 86 | "has-flag": { 87 | "version": "2.0.0", 88 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", 89 | "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" 90 | }, 91 | "lodash": { 92 | "version": "4.17.4", 93 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", 94 | "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" 95 | }, 96 | "platform": { 97 | "version": "1.3.5", 98 | "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz", 99 | "integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==" 100 | }, 101 | "require-all": { 102 | "version": "2.2.0", 103 | "resolved": "https://registry.npmjs.org/require-all/-/require-all-2.2.0.tgz", 104 | "integrity": "sha1-tEIMIzrAKC0P9Jsnf7iAqLXeCJQ=" 105 | }, 106 | "supports-color": { 107 | "version": "4.5.0", 108 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", 109 | "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", 110 | "requires": { 111 | "has-flag": "2.0.0" 112 | } 113 | }, 114 | "wcwidth": { 115 | "version": "1.0.1", 116 | "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", 117 | "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", 118 | "optional": true, 119 | "requires": { 120 | "defaults": "1.0.3" 121 | } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "benchr", 3 | "version": "4.3.0", 4 | "description": "Benchmark runner", 5 | "bin": "./bin/benchr", 6 | "main": "lib/runner.js", 7 | "author": "Robert Klep ", 8 | "license": "MIT", 9 | "repository": "robertklep/node-benchr", 10 | "keywords": [ 11 | "benchmark", 12 | "runner", 13 | "testing" 14 | ], 15 | "dependencies": { 16 | "benchmark": "^2.1.4", 17 | "chalk": "^2.3.0", 18 | "docopt": "^0.6.2", 19 | "easy-table": "^1.1.1", 20 | "require-all": "^2.2.0" 21 | }, 22 | "engines": { 23 | "node": ">=6.0.0" 24 | } 25 | } 26 | --------------------------------------------------------------------------------