├── .npmignore ├── .gitignore ├── lib ├── identity.js ├── get-fn-name.js ├── utils.js ├── get-fn-args.js ├── utils-browser.js └── str2color.js ├── examples ├── zipWith.js └── greet.js ├── package.json ├── README.md └── index.js /.npmignore: -------------------------------------------------------------------------------- 1 | etc 2 | examples 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | etc 3 | -------------------------------------------------------------------------------- /lib/identity.js: -------------------------------------------------------------------------------- 1 | module.exports = (x) => x 2 | -------------------------------------------------------------------------------- /lib/get-fn-name.js: -------------------------------------------------------------------------------- 1 | module.exports = () => { 2 | const CHAR = 'λ' 3 | let anonN = 0 4 | 5 | return (fn) => fn.name || CHAR + ++anonN 6 | } 7 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | const util = require('util') 2 | 3 | exports.inspect = (obj) => 4 | util.inspect(obj, { 5 | colors: true, 6 | depth: null 7 | }) 8 | 9 | exports.print = (str) => { 10 | process.stderr.write(str + '\n') 11 | } 12 | -------------------------------------------------------------------------------- /lib/get-fn-args.js: -------------------------------------------------------------------------------- 1 | const R = require('ramda') 2 | 3 | const parseStrs = R.compose(R.defaultTo([]), R.match(/([^\s,]+)/g)) 4 | const getParensContent = (str) => 5 | R.slice(R.indexOf('(', str) + 1, R.indexOf(')', str), str) 6 | 7 | module.exports = R.pipe(R.toString, getParensContent, parseStrs) 8 | -------------------------------------------------------------------------------- /examples/zipWith.js: -------------------------------------------------------------------------------- 1 | const R = require('ramda') 2 | const treis = require('../') 3 | 4 | const ABC = ['a', 'b', 'c', 'd', 'e', 'f'] 5 | const repeatAndJoin = R.compose(R.join(''), R.repeat) 6 | treis('zipWith', R.zipWith)( 7 | treis(repeatAndJoin), 8 | ABC, 9 | R.range(1, ABC.length + 1) 10 | ) 11 | -------------------------------------------------------------------------------- /examples/greet.js: -------------------------------------------------------------------------------- 1 | const R = require('ramda'); 2 | const treis = require('treis'); 3 | 4 | // greet ∷ String → String 5 | const greet = (name) => 6 | `Hello, ${name}!` 7 | 8 | // greetPeople ∷ [String] → String 9 | const greetPeople = 10 | R.compose(R.join('\n'), 11 | R.map(treis(greet))); 12 | 13 | const people = ['John', 'Jill', 'Bruce']; 14 | console.log(treis(greetPeople)(people)); 15 | -------------------------------------------------------------------------------- /lib/utils-browser.js: -------------------------------------------------------------------------------- 1 | const util = require('util') 2 | const ansi = require ('ansicolor') 3 | 4 | exports.inspect = obj => 5 | util.inspect(obj, { 6 | colors: true, 7 | depth: null 8 | }) 9 | 10 | // ansicolor has some issue with line breaks, splitting the message by line to 11 | // multiple console.log calls is a workaround 12 | exports.print = (msg) => { 13 | const msgs = msg.split('\n') 14 | msgs.forEach(m => { 15 | const parsed = ansi.parse(m) 16 | console.log(...parsed.asChromeConsoleLogArguments) 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "treis", 3 | "version": "2.6.0", 4 | "description": "a simple tool to debug and observe functions", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "browser": { 10 | "./lib/utils.js": "./lib/utils-browser.js" 11 | }, 12 | "author": "Raine Virta ", 13 | "license": "ISC", 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/raine/treis" 17 | }, 18 | "keywords": [ 19 | "trace", 20 | "debug", 21 | "ramda" 22 | ], 23 | "bugs": { 24 | "url": "https://github.com/raine/treis/issues" 25 | }, 26 | "homepage": "https://github.com/raine/treis", 27 | "dependencies": { 28 | "ansicolor": "^1.1.72", 29 | "chalk": "^1.1.1", 30 | "ramda": "^0.18.0", 31 | "strip-ansi": "^3.0.1" 32 | }, 33 | "prettier": { 34 | "semi": false, 35 | "singleQuote": true, 36 | "arrowParens": "always" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/str2color.js: -------------------------------------------------------------------------------- 1 | const bold = require('chalk').bold 2 | 3 | // deterministically color a string without reusing colors until all colors 4 | // are used once 5 | module.exports = (function() { 6 | const COLORS = ['red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'] 7 | const unusedColors = COLORS.slice() 8 | const colorByStr = {} 9 | 10 | return function(str) { 11 | const color = 12 | colorByStr[str] || 13 | (function() { 14 | if (unusedColors.length === 0) unusedColors = COLORS.slice() 15 | const color = strToElem(unusedColors, str) 16 | colorByStr[str] = color 17 | unusedColors.splice(unusedColors.indexOf(color), 1) 18 | return color 19 | })() 20 | 21 | return bold[color](str) 22 | } 23 | 24 | function strToElem(arr, str) { 25 | return arr[charCodeSum(str) % arr.length] 26 | } 27 | 28 | function charCodeSum(str) { 29 | return str.split('').reduce(function(sum, c) { 30 | return sum + c.charCodeAt() 31 | }, 0) 32 | } 33 | })() 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # treis [![npm version](https://badge.fury.io/js/treis.svg)](http://badge.fury.io/js/treis) 2 | 3 | > treɪs | a simple tool to debug and observe functions 4 | 5 | treis will answer the question "what arguments is this function called with 6 | and what does it return?". 7 | 8 | It can be particularly useful when programming in [point-free 9 | style](http://en.wikipedia.org/wiki/Tacit_programming). 10 | 11 | If you want to know what a function does in the middle of a `compose` 12 | pipeline, just do: 13 | 14 | ```js 15 | compose(h, treis(g), f); 16 | ``` 17 | 18 | ## install 19 | 20 | ```sh 21 | $ npm install treis 22 | ``` 23 | 24 | ## usage 25 | 26 | #### `treis(label?, Function) → Function` 27 | 28 | Returns a decorated version of `fn` that prints the arguments given to `fn` 29 | and its return value. 30 | 31 | You can _optionally_ label functions by passing a name before the function to 32 | be decorated, if not, treis will try to use `fn.name`. 33 | 34 | Writes output to `stderr`. 35 | 36 | ## example 37 | 38 | ```js 39 | const R = require('ramda'); 40 | const treis = require('treis'); 41 | 42 | // greet ∷ String → String 43 | const greet = (name) => 44 | `Hello, ${name}!` 45 | 46 | // greetPeople ∷ [String] → String 47 | const greetPeople = 48 | R.compose(R.join('\n'), 49 | R.map(treis(greet))); 50 | 51 | const people = ['John', 'Jill', 'Bruce']; 52 | console.log(treis(greetPeople)(people)); 53 | ``` 54 | 55 | #### output 56 | 57 | ![](https://raw.githubusercontent.com/raine/treis/media/greet.png) 58 | 59 | ## browser support 60 | 61 | Works with [browserify](http://browserify.org/). 62 | 63 | ## useful vim mappings 64 | 65 | These require [surround.vim](https://github.com/tpope/vim-surround): 66 | 67 | ```viml 68 | " Surround a word with treis() 69 | nmap tr ysiwftreisf( 70 | 71 | " Surround a visual selection with treis() 72 | vmap tr Sftreisf( 73 | 74 | nmap tR ysiwfrequire('treis')f( 75 | vmap tR Sfrequire('treis')f( 76 | ``` 77 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const R = require('ramda') 2 | const chalk = require('chalk') 3 | const utils = require('./lib/utils') 4 | const str2color = require('./lib/str2color') 5 | const getFnArgs = require('./lib/get-fn-args') 6 | const getFnName = require('./lib/get-fn-name') 7 | const stripAnsi = require('strip-ansi') 8 | const inspect = utils.inspect 9 | const print = utils.print 10 | 11 | const mapTail = (fn, arr) => R.slice(0, 1, arr).concat(R.map(fn, R.tail(arr))) 12 | 13 | const strRepeat = R.compose(R.join(''), R.repeat) 14 | const lines = R.split('\n') 15 | const unlines = R.join('\n') 16 | const indentTailLines = (n, str) => 17 | unlines(mapTail(R.concat(strRepeat(' ', n)), lines(str))) 18 | 19 | const treis = (__print, color) => { 20 | const fnNameGetter = getFnName() 21 | const print = R.pipe(color ? R.identity : stripAnsi, __print) 22 | 23 | return (name, fn) => { 24 | if (typeof name === 'function') { 25 | fn = name 26 | name = null 27 | } 28 | 29 | return function(/* args */) { 30 | if (name == null) name = fnNameGetter(fn) 31 | name = name.toString() 32 | 33 | const prefix = name ? str2color(name) + ' ' : '' 34 | print(prefix + formatArgs(name, fn, arguments)) 35 | const res = fn.apply(this, arguments) 36 | print(prefix + '=> ' + indentTailLines(name.length + 4, inspect(res))) 37 | return res 38 | } 39 | } 40 | } 41 | 42 | module.exports = treis(print, true) 43 | module.exports.__init = treis 44 | 45 | function formatArgs(name, fn, args) { 46 | const fnArgNames = getFnArgs(fn) 47 | const getArgPairs = (i, val) => [fnArgNames[i] || i.toString(), val] 48 | 49 | const pairs = R.zipWith(getArgPairs, R.range(0, args.length), args) 50 | const argLines = R.map(R.apply(formatArg), pairs) 51 | const space = strRepeat(' ', name.length + 1) 52 | return unlines(mapTail(R.concat(space), argLines)) 53 | 54 | function formatArg(argName, val) { 55 | const indentLevel = name.length + argName.length + 3 56 | return R.join(': ', [ 57 | chalk.green(argName), 58 | indentTailLines(indentLevel, inspect(val)) 59 | ]) 60 | } 61 | } 62 | --------------------------------------------------------------------------------