├── .babelrc ├── .flowconfig ├── .gitignore ├── .travis.yml ├── Makefile ├── README.md ├── package.json ├── src └── index.js ├── test.js └── test.png /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-3", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/esformatter/.* 3 | .*/node_modules/fbjs/.*.flow 4 | .*/node_modules/find-package-json/.* 5 | .*/node_modules/.store/find-package-json@.*/.* 6 | .*/node_modules/.store/fbjs@.*/.*.flow 7 | .*/node_modules/.store/gh-pages@.*/.* 8 | .*/node_modules/.store/browserify-zlib@.*/_/package.json 9 | .*/test/.* 10 | 11 | [include] 12 | 13 | [libs] 14 | decls 15 | 16 | [options] 17 | esproposal.class_static_fields=enable 18 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue 19 | suppress_comment=\\(.\\|\n\\)*\\$ExpectError 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | lib/ 3 | examples/webpack/bundle 4 | site/bundle 5 | npm-debug.log 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DELETE_ON_ERROR: 2 | 3 | BIN = ./node_modules/.bin 4 | TESTS = $(shell find src -path '*/__tests__/*-test.js') 5 | SRC = $(filter-out $(TESTS) $(FIXTURES), $(shell find src -name '*.js')) 6 | LIB = $(SRC:src/%=lib/%) 7 | 8 | build: build-lib build-typings 9 | 10 | build-lib:: 11 | @$(MAKE) -j 8 $(LIB) 12 | 13 | build-typings: $(SRC:src/%=lib/%.flow) 14 | 15 | doctoc: 16 | @$(BIN)/doctoc --title '**Table of Contents**' ./README.md 17 | 18 | build-silent:: 19 | @$(MAKE) -s -j 8 $(LIB) 20 | 21 | lint:: 22 | @$(BIN)/eslint src 23 | 24 | check:: 25 | @$(BIN)/flow --show-all-errors src 26 | 27 | test:: test-unit test-doc 28 | 29 | sloc:: 30 | @$(BIN)/sloc -e __tests__ src 31 | 32 | version-major version-minor version-patch:: lint check test 33 | @npm version $(@:version-%=%) 34 | 35 | publish:: build 36 | @git push --tags origin HEAD:master 37 | @npm publish 38 | 39 | clean:: 40 | @rm -rf lib 41 | 42 | lib/%.js: src/%.js 43 | @echo "Building $@" 44 | @mkdir -p $(@D) 45 | @$(BIN)/babel $(BABEL_OPTIONS) -o $@ $< 46 | 47 | lib/%.js.flow: src/%.js 48 | @echo "Building $@" 49 | @mkdir -p $(@D) 50 | @cp $< $@ 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # console-ui 2 | 3 | Composable console output: 4 | 5 | ## Installation 6 | 7 | ``` 8 | % npm install console-ui 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```js 14 | import {print, paragraph, indent, listItem, text} from 'console-ui' 15 | 16 | let em = text.white.bold 17 | 18 | let error = text.red 19 | 20 | function loc(line, col) { 21 | return ['line ', em(line), ' column ', em(col)] 22 | } 23 | 24 | function errorHeader(filename, line, col) { 25 | return ['At ', em(filename), ' ', loc(line, col)] 26 | } 27 | 28 | print( 29 | paragraph(error('Errors found:')), 30 | listItem( 31 | paragraph(errorHeader('index.js', 10, 23)), 32 | paragraph('Cannot find module ', em('react')) 33 | ) 34 | ) 35 | ``` 36 | 37 | Output: 38 | 39 | ![output][output] 40 | 41 | [output]: test.png 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "console-ui", 3 | "version": "0.1.0", 4 | "description": "Render console UI with easy", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "test": "make test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/andreypopp/console-ui.git" 12 | }, 13 | "files": [ 14 | "lib/" 15 | ], 16 | "keywords": [ 17 | "console", 18 | "ui", 19 | "terminal" 20 | ], 21 | "author": "Andrey Popp <8mayday@gmail.com>", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/andreypopp/console-ui/issues" 25 | }, 26 | "homepage": "https://github.com/andreypopp/console-ui#readme", 27 | "devDependencies": { 28 | "babel-cli": "^6.16.0", 29 | "babel-preset-es2015": "^6.16.0", 30 | "babel-preset-react": "^6.16.0", 31 | "babel-preset-stage-3": "^6.17.0", 32 | "flow-bin": "^0.33.0", 33 | "jest-cli": "^16.0.0" 34 | }, 35 | "dependencies": { 36 | "cli-color": "^1.1.0", 37 | "invariant": "^2.2.1", 38 | "word-wrap": "^1.1.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | import invariant from 'invariant'; 6 | import wrapString from 'word-wrap'; 7 | import color from 'cli-color'; 8 | 9 | type InlineElement 10 | = string 11 | | Array; 12 | 13 | type Element 14 | = {type: 'paragraph', children: InlineElement} 15 | | {type: 'indent', children: Element} 16 | | {type: 'listItem', children: Element} 17 | | string 18 | | Array; 19 | 20 | type Options = { 21 | indentSize: number; 22 | }; 23 | 24 | type Context = { 25 | width: number; 26 | indent: number; 27 | }; 28 | 29 | const DEFAULT_OPTIONS = { 30 | indentSize: 2, 31 | }; 32 | 33 | const INITIAL_CONTEXT = { 34 | indent: 0, 35 | width: 200, 36 | }; 37 | 38 | export let text = color; 39 | 40 | export function paragraph(...children: Array) { 41 | return {type: 'paragraph', children}; 42 | } 43 | 44 | export function indent(...children: Array) { 45 | return {type: 'indent', children}; 46 | } 47 | 48 | export function listItem(...children: Array) { 49 | return {type: 'listItem', children}; 50 | } 51 | 52 | export function format(...element: Array): string { 53 | return _format(INITIAL_CONTEXT, element, DEFAULT_OPTIONS).trimRight(); 54 | } 55 | 56 | export function print(...element: Array): void { 57 | console.log(format(...element)); // eslint-disable-line no-console 58 | } 59 | 60 | function _format(context: Context, element: Element, options: Options): string { 61 | if (typeof element === 'string') { 62 | element = paragraph(element); 63 | } 64 | if (Array.isArray(element)) { 65 | return element 66 | .map(element => _format(context, element, options)) 67 | .join(''); 68 | } else if (element.type === 'paragraph') { 69 | let text = _formatInline(element.children); 70 | return wrapString(text, { 71 | width: context.width, 72 | indent: _formatIndent(context.indent), 73 | }) + '\n\n'; 74 | } else if (element.type === 'indent') { 75 | let nextContext = {...context, indent: context.indent + options.indentSize}; 76 | return _format(nextContext, element.children, options); 77 | } else if (element.type === 'listItem') { 78 | let out = _format(context, indent(element.children), options); 79 | out = '- ' + out.slice(2); 80 | return out; 81 | } else { 82 | invariant(false, 'Unknown element: %s', element); 83 | } 84 | } 85 | 86 | function _formatInline(element: InlineElement): string { 87 | if (Array.isArray(element)) { 88 | return element.map(_formatInline).join(''); 89 | } else if (typeof element === 'string') { 90 | return element; 91 | } else { 92 | invariant(false, 'Unknown inline element: %s', element); 93 | } 94 | } 95 | 96 | function _formatIndent(size) { 97 | return (new Array(size)).fill(' ').join(''); 98 | } 99 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | let {print, paragraph, indent, listItem, text} = require('./lib') 2 | 3 | let em = text.white.bold; 4 | 5 | let error = text.red; 6 | 7 | function loc(line, col) { 8 | return ['line ', em(line), ' column ', em(col)] 9 | } 10 | 11 | function errorHeader(filename, line, col) { 12 | return ['At ', em(filename), ' ', loc(line, col)] 13 | } 14 | 15 | print( 16 | paragraph(error('Errors found:')), 17 | listItem( 18 | paragraph(errorHeader('index.js', 10, 23)), 19 | paragraph('Cannot find module ', em('react')) 20 | ) 21 | ) 22 | -------------------------------------------------------------------------------- /test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreypopp/console-ui/7f3997c65cb20fd1ff8afb21e4fa3f40226faa30/test.png --------------------------------------------------------------------------------