Comparing version 0.1.2 to 0.2.0
65
index.js
'use strict'; | ||
const readline = require('readline'); | ||
const logUpdate = require('log-update'); | ||
@@ -25,5 +26,2 @@ const StringComponent = require('./lib/string-component'); | ||
exports.diff = diff; | ||
exports.renderToString = renderToString; | ||
const unmount = tree => callTree(tree, 'unmount'); | ||
@@ -33,3 +31,3 @@ const didMount = tree => callTree(tree, 'didMount'); | ||
const render = (nextTree, prevTree, onUpdate = noop, context = {}, autoLifecycle = true) => { | ||
const build = (nextTree, prevTree, onUpdate = noop, context = {}, autoLifecycle = true) => { | ||
const reconciledTree = diff(prevTree, nextTree, onUpdate, context); | ||
@@ -45,17 +43,30 @@ | ||
exports.render = render; | ||
exports.build = build; | ||
exports.diff = diff; | ||
exports.mount = (tree, stream) => { | ||
const log = logUpdate.create(stream || process.stdout); | ||
exports.renderToString = (...args) => renderToString(build(...args)); | ||
exports.render = (tree, options) => { | ||
if (options && typeof options.write === 'function') { | ||
options = { | ||
stdout: options | ||
}; | ||
} | ||
const {stdin, stdout} = Object.assign({ | ||
stdin: process.stdin, | ||
stdout: process.stdout | ||
}, options); | ||
const log = logUpdate.create(stdout); | ||
const context = {}; | ||
let isUnmounted = false; | ||
let currentTree; | ||
const context = {}; | ||
const onUpdate = () => { | ||
if (isUnmounted) { | ||
return; | ||
} | ||
readline.emitKeypressEvents(stdin); | ||
stdin.setRawMode(true); | ||
const nextTree = render(tree, currentTree, onUpdate, context, false); | ||
const update = () => { | ||
const nextTree = build(tree, currentTree, onUpdate, context, false); // eslint-disable-line no-use-before-define | ||
log(renderToString(nextTree)); | ||
@@ -68,8 +79,21 @@ didMount(nextTree); | ||
currentTree = render(tree, null, onUpdate, context, false); | ||
log(renderToString(currentTree)); | ||
didMount(currentTree); | ||
didUpdate(currentTree); | ||
const onUpdate = () => { | ||
if (isUnmounted) { | ||
return; | ||
} | ||
return () => { | ||
update(); | ||
}; | ||
update(); | ||
const onKeyPress = (ch, key) => { | ||
if (key.name === 'escape' || (key.ctrl && key.name === 'c')) { | ||
exit(); // eslint-disable-line no-use-before-define | ||
} | ||
}; | ||
stdin.on('keypress', onKeyPress); | ||
const exit = () => { | ||
if (isUnmounted) { | ||
@@ -79,2 +103,5 @@ return; | ||
stdin.setRawMode(false); | ||
stdin.removeListener('keypress', onKeyPress); | ||
isUnmounted = true; | ||
@@ -84,2 +111,4 @@ unmount(currentTree); | ||
}; | ||
return exit; | ||
}; |
@@ -7,2 +7,6 @@ 'use strict'; | ||
module.exports = (component, props, ...children) => { | ||
if (typeof component !== 'function' && typeof component !== 'string') { | ||
throw new TypeError(`Expected component to be a function, but received ${typeof component}. You may have forgotten to export a component.`); | ||
} | ||
props = props || {}; | ||
@@ -9,0 +13,0 @@ |
158
package.json
{ | ||
"name": "ink", | ||
"version": "0.1.2", | ||
"description": "React for CLI", | ||
"license": "MIT", | ||
"repository": "vadimdemedes/ink", | ||
"author": { | ||
"name": "vdemedes", | ||
"email": "vdemedes@gmail.com", | ||
"url": "github.com/vadimdemedes" | ||
}, | ||
"engines": { | ||
"node": ">= 4" | ||
}, | ||
"scripts": { | ||
"test": "xo && ava" | ||
}, | ||
"files": [ | ||
"lib", | ||
"index.js" | ||
], | ||
"keywords": [ | ||
"react", | ||
"cli" | ||
], | ||
"dependencies": { | ||
"arrify": "^1.0.1", | ||
"chalk": "^2.0.1", | ||
"indent-string": "^3.1.0", | ||
"lodash.flattendeep": "^4.4.0", | ||
"lodash.isequal": "^4.5.0", | ||
"log-update": "^1.0.2" | ||
}, | ||
"devDependencies": { | ||
"ava": "^0.19.1", | ||
"babel-plugin-transform-react-jsx": "^6.24.1", | ||
"babel-register": "^6.24.1", | ||
"eslint-config-xo-react": "^0.12.0", | ||
"eslint-plugin-react": "^7.1.0", | ||
"sinon": "^2.3.4", | ||
"xo": "^0.18.2" | ||
}, | ||
"ava": { | ||
"require": [ | ||
"babel-register" | ||
], | ||
"babel": { | ||
"presets": [ | ||
"@ava/stage-4" | ||
], | ||
"plugins": [ | ||
[ | ||
"transform-react-jsx", | ||
{ | ||
"pragma": "h" | ||
} | ||
] | ||
] | ||
} | ||
}, | ||
"xo": { | ||
"extends": [ | ||
"xo-react" | ||
], | ||
"plugins": [ | ||
"react" | ||
], | ||
"rules": { | ||
"react/prop-types": 0 | ||
}, | ||
"settings": { | ||
"react": { | ||
"pragma": "h" | ||
} | ||
} | ||
} | ||
"name": "ink", | ||
"version": "0.2.0", | ||
"description": "React for CLI", | ||
"license": "MIT", | ||
"repository": "vadimdemedes/ink", | ||
"author": { | ||
"name": "vdemedes", | ||
"email": "vdemedes@gmail.com", | ||
"url": "github.com/vadimdemedes" | ||
}, | ||
"engines": { | ||
"node": ">=4" | ||
}, | ||
"scripts": { | ||
"test": "xo && ava" | ||
}, | ||
"files": [ | ||
"lib", | ||
"index.js" | ||
], | ||
"keywords": [ | ||
"react", | ||
"cli", | ||
"jsx", | ||
"stdout", | ||
"components", | ||
"command-line", | ||
"preact", | ||
"redux", | ||
"print", | ||
"render", | ||
"colors", | ||
"text" | ||
], | ||
"dependencies": { | ||
"arrify": "^1.0.1", | ||
"chalk": "^2.0.1", | ||
"indent-string": "^3.1.0", | ||
"lodash.flattendeep": "^4.4.0", | ||
"lodash.isequal": "^4.5.0", | ||
"log-update": "^1.0.2" | ||
}, | ||
"devDependencies": { | ||
"ava": "^0.20.0", | ||
"babel-plugin-transform-react-jsx": "^6.24.1", | ||
"babel-register": "^6.24.1", | ||
"eslint-config-xo-react": "^0.13.0", | ||
"eslint-plugin-react": "^7.1.0", | ||
"sinon": "^2.3.4", | ||
"strip-ansi": "^4.0.0", | ||
"xo": "^0.18.2" | ||
}, | ||
"ava": { | ||
"require": [ | ||
"babel-register" | ||
], | ||
"babel": { | ||
"presets": [ | ||
"@ava/stage-4" | ||
], | ||
"plugins": [ | ||
[ | ||
"transform-react-jsx", | ||
{ | ||
"pragma": "h" | ||
} | ||
] | ||
] | ||
} | ||
}, | ||
"xo": { | ||
"extends": [ | ||
"xo-react" | ||
], | ||
"rules": { | ||
"react/prop-types": 0 | ||
}, | ||
"settings": { | ||
"react": { | ||
"pragma": "h" | ||
} | ||
} | ||
} | ||
} |
@@ -1,6 +0,12 @@ | ||
# Ink [![Build Status](https://travis-ci.org/vadimdemedes/ink.svg?branch=master)](https://travis-ci.org/vadimdemedes/ink) | ||
<h1 align="center"> | ||
<br> | ||
<img width="192" alt="Ink" src="media/logo.png"> | ||
<br> | ||
<br> | ||
<br> | ||
</h1> | ||
> React for CLIs | ||
> React for CLIs. Build and test your CLI output using components. | ||
Build and test your CLI output using components. | ||
[![Build Status](https://travis-ci.org/vadimdemedes/ink.svg?branch=master)](https://travis-ci.org/vadimdemedes/ink) | ||
@@ -11,3 +17,3 @@ | ||
``` | ||
$ npm install --save ink | ||
$ npm install ink | ||
``` | ||
@@ -19,3 +25,3 @@ | ||
```jsx | ||
const {h, mount, Component, Text} = require('ink'); | ||
const {h, render, Component, Text} = require('ink'); | ||
@@ -52,3 +58,3 @@ class Counter extends Component { | ||
mount(<Counter/>, process.stdout); | ||
render(<Counter/>); | ||
``` | ||
@@ -59,2 +65,10 @@ | ||
## Useful Components | ||
- [ink-redux](https://github.com/vadimdemedes/ink-redux) - Redux bindings. | ||
- [ink-text-input](https://github.com/vadimdemedes/ink-text-input) - Text input. | ||
- [ink-password-input](https://github.com/vadimdemedes/ink-password-input) - Password input. | ||
- [ink-progress-bar](https://github.com/brigand/ink-progress-bar) - Configurable component for rendering progress bars. | ||
## Guide | ||
@@ -73,10 +87,9 @@ | ||
Ink's goal is to provide the same component-based UI building experience that React provides, but for CLI. That's why it tries to implement the minimum required functionality of React. If you are already familiar with React (or Preact, since Ink borrows a few ideas from it), you already know Ink. | ||
Ink's goal is to provide the same component-based UI building experience that React provides, but for command-line apps. That's why it tries to implement the minimum required functionality of React. If you are already familiar with React (or Preact, since Ink borrows a few ideas from it), you already know Ink. | ||
They key difference you have to remember is that the rendering result isn't a DOM, but a string, which Ink writes to output. | ||
The key difference you have to remember is that the rendering result isn't a DOM, but a string, which Ink writes to the output. | ||
### Getting Started | ||
To ensure all examples work and you can begin your adventure with Ink, make sure to set up a JSX transpiler and set JSX pragma to `h`. | ||
Don' forget to import `h` into every file that contains JSX. | ||
To ensure all examples work and you can begin your adventure with Ink, make sure to set up a JSX transpiler and set JSX pragma to `h`. Don' forget to import `h` into every file that contains JSX. | ||
@@ -91,3 +104,3 @@ ```jsx | ||
#### mount(tree, stream) | ||
#### render(tree, stream) | ||
@@ -97,2 +110,4 @@ Mount a component, listen for updates and update the output. | ||
It automatically enables `keypress` events on `process.stdin`. Since it requires [raw mode](https://davidwalsh.name/node-raw-mode) to be enabled, Ink handles default behavior for you, like exiting with <kbd>Ctrl</kbd>+<kbd>C</kbd>. | ||
##### tree | ||
@@ -108,3 +123,3 @@ | ||
```jsx | ||
const {h, mount, Component} = require('ink'); | ||
const {h, render, Component} = require('ink'); | ||
@@ -137,6 +152,6 @@ class Counter extends Component { | ||
const unmount = mount(<Counter/>); | ||
const unmount = render(<Counter/>); | ||
setTimeout(() => { | ||
// enough counting | ||
// Enough counting | ||
unmount(); | ||
@@ -146,18 +161,13 @@ }, 1000); | ||
#### renderToString(tree) | ||
#### renderToString(tree, [prevTree]) | ||
Render a previously generated VDOM to a string, which you can flush to output. | ||
#### render(tree, [prevTree]) | ||
Build a VDOM representation of components. | ||
Render a component to a string and return it. | ||
Useful if you don't intend to use state or lifecycle methods and just want to render the UI once and exit. | ||
```jsx | ||
const {h, render, renderToString} = require('ink'); | ||
const {h, renderToString} = require('ink'); | ||
const Hello = () => 'Hello World'; | ||
const tree = render(<Hello/>); | ||
process.stdout.write(renderToString(tree)); | ||
process.stdout.write(renderToString(<Hello/>)); | ||
``` | ||
@@ -167,5 +177,3 @@ | ||
Similarly to React, there are 2 kinds of components: Stateful components (next, "component") and stateless function components. | ||
You can create a component by extending `Component` class. | ||
Unlike stateless function components, they have access to state, context, lifecycle methods and they can be accessed via refs. | ||
Similarly to React, there are two kinds of components: Stateful components (next, "component") and stateless function components. You can create a component by extending `Component` class. Unlike stateless function components, they have access to state, context, lifecycle methods and they can be accessed via refs. | ||
@@ -194,3 +202,3 @@ ```js | ||
// other initialization | ||
// Other initialization | ||
} | ||
@@ -254,3 +262,3 @@ ``` | ||
Component was rerendered and was written to output. | ||
Component was rerendered and was written to the output. | ||
@@ -260,6 +268,6 @@ #### State | ||
Each component can have its local state accessible via `this.state`. | ||
Whenever a state updates, component is rerendered. | ||
Whenever a state updates, the component is rerendered. | ||
To set the initial state, extend the constructor and assign an object to `this.state`. | ||
Anywhere in the component state is accessible via `this.state`, and it's also passed to `render()` as a second argument. | ||
The state is accessible via `this.state` anywhere in the component, and it's also passed to `render()` as a second argument. | ||
@@ -286,3 +294,3 @@ ```js | ||
Type: `Object`, `Function` | ||
Type: `Object` `Function` | ||
Default: `{}` | ||
@@ -436,3 +444,3 @@ | ||
As you may have noticed, stateless function component still get access to props and context. | ||
As you may have noticed, stateless function components still get access to props and context. | ||
@@ -439,0 +447,0 @@ #### Built-in Components |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
25849
487
473
8