tablemark
Advanced tools
Comparing version 1.1.0 to 1.2.0
120
index.js
@@ -5,5 +5,8 @@ 'use strict' | ||
const sentence = require('sentence-case') | ||
const split = require('split-text-to-chunks') | ||
const columnWidthMin = 5 | ||
const ALIGN = [ 'LEFT', 'CENTER', 'RIGHT' ] | ||
const width = split.width | ||
const columnsWidthMin = 5 | ||
const ALIGN = ['LEFT', 'CENTER', 'RIGHT'] | ||
const PIPE_REGEX = /\|/g | ||
@@ -16,10 +19,16 @@ module.exports = (input, options) => { | ||
options = Object.assign({ | ||
stringify: v => typeof v === 'undefined' ? '' : String(v) | ||
}, options) | ||
stringify: toString | ||
}, options, { | ||
wrap: Object.assign({ | ||
width: Infinity, | ||
gutters: false | ||
}, options && options.wrap) | ||
}) | ||
const stringify = options.stringify | ||
const columnsMaxWidth = options.wrap.width | ||
const gutters = options.wrap.gutters | ||
const keys = Object.keys(input[0]) | ||
let table = '' | ||
const titles = keys.map((key, i) => { | ||
@@ -41,5 +50,5 @@ if (Array.isArray(options.columns) && options.columns[i]) { | ||
(sizes, item) => keys.map( | ||
(key, i) => Math.max(columnWidthMin, stringify(item[key]).length, sizes[i]) | ||
(key, i) => Math.max(width(stringify(item[key]), columnsMaxWidth), sizes[i]) | ||
), | ||
titles.map(t => t.length) | ||
titles.map(t => Math.max(columnsWidthMin, width(t, columnsMaxWidth))) | ||
) | ||
@@ -63,10 +72,10 @@ | ||
let table = '' | ||
// header line | ||
table += row(titles.map((title, i) => { | ||
return pad(alignments[i], widths[i], title) | ||
})) | ||
table += row(alignments, widths, titles, gutters) | ||
// header separator | ||
table += row(alignments.map((align, i) => { | ||
return ( | ||
table += line(alignments.map( | ||
(align, i) => ( | ||
(align === 'LEFT' || align === 'CENTER' ? ':' : '-') + | ||
@@ -76,10 +85,10 @@ repeat('-', widths[i] - 2) + | ||
) | ||
})) | ||
), true) | ||
// table body | ||
input.forEach(item => { | ||
table += row(keys.map((key, i) => { | ||
return pad(alignments[i], widths[i], stringify(item[key])) | ||
})) | ||
}) | ||
table += input.map( | ||
(item, i) => row(alignments, widths, keys.map( | ||
key => stringify(item[key]) | ||
), gutters) | ||
).join('') | ||
@@ -89,24 +98,65 @@ return table | ||
function pad (alignment, target, value) { | ||
if (!alignment || alignment === 'LEFT') { | ||
return padEnd(value, target) | ||
function row (alignments, widths, columns, gutters) { | ||
const width = columns.length | ||
const values = new Array(width) | ||
const first = new Array(width) | ||
let height = 1 | ||
for (let h = 0; h < width; h++) { | ||
const cells = values[h] = split(columns[h], widths[h]) | ||
if (cells.length > height) height = cells.length | ||
first[h] = pad(alignments[h], widths[h], cells[0]) | ||
} | ||
if (alignment === 'RIGHT') { | ||
return padStart(value, target) | ||
if (height === 1) return line(first, true) | ||
const lines = new Array(height) | ||
lines[0] = line(first, true) | ||
for (let v = 1; v < height; v++) { | ||
lines[v] = new Array(width) | ||
} | ||
// CENTER | ||
const remainder = (target - value.length) % 2 | ||
const sides = (target - value.length - remainder) / 2 | ||
for (let h = 0; h < width; h++) { | ||
const cells = values[h] | ||
let v = 1 | ||
return repeat(' ', sides) + value + repeat(' ', sides + remainder) | ||
for (;v < cells.length; v++) { | ||
lines[v][h] = pad(alignments[h], widths[h], cells[v]) | ||
} | ||
for (;v < height; v++) { | ||
lines[v][h] = repeat(' ', widths[h]) | ||
} | ||
} | ||
for (let h = 1; h < height; h++) { | ||
lines[h] = line(lines[h], gutters) | ||
} | ||
return lines.join('') | ||
} | ||
function row (v) { | ||
if (Array.isArray(v)) { | ||
v = v.join(' | ') | ||
function line (columns, gutters) { | ||
return ( | ||
(gutters ? '| ' : ' ') + | ||
columns.join((gutters ? ' | ' : ' ')) + | ||
(gutters ? ' |' : ' ') + os.EOL | ||
) | ||
} | ||
function pad (alignment, width, what) { | ||
if (!alignment || alignment === 'LEFT') { | ||
return padEnd(what, width) | ||
} | ||
return '| ' + v + ' |' + os.EOL | ||
if (alignment === 'RIGHT') { | ||
return padStart(what, width) | ||
} | ||
// CENTER | ||
const remainder = (width - what.length) % 2 | ||
const sides = (width - what.length - remainder) / 2 | ||
return repeat(' ', sides) + what + repeat(' ', sides + remainder) | ||
} | ||
@@ -125,1 +175,7 @@ | ||
} | ||
function toString (v) { | ||
if (typeof v === 'undefined') return '' | ||
return String(v).replace(PIPE_REGEX, '\\|') | ||
} |
{ | ||
"name": "tablemark", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "Generate markdown tables from JSON data.", | ||
@@ -8,3 +8,3 @@ "author": { | ||
"email": "lingenbw@gmail.com", | ||
"url": "github.com/citycide" | ||
"url": "https://github.com/citycide" | ||
}, | ||
@@ -34,5 +34,7 @@ "license": "MIT", | ||
"test": "ava", | ||
"test:watch": "ava -w" | ||
"watch": "ava -w", | ||
"changelog": "changelog" | ||
}, | ||
"devDependencies": { | ||
"@citycide/changelog": "^1.0.0", | ||
"ava": "^0.20.0", | ||
@@ -43,4 +45,5 @@ "snazzy": "^7.0.0", | ||
"dependencies": { | ||
"sentence-case": "^2.1.1" | ||
"sentence-case": "^2.1.1", | ||
"split-text-to-chunks": "^1.0.0" | ||
} | ||
} |
123
readme.md
@@ -27,7 +27,7 @@ # tablemark · [![Version](https://img.shields.io/npm/v/tablemark.svg?style=flat-square&maxAge=3600)](https://www.npmjs.com/package/tablemark) [![License](https://img.shields.io/npm/l/tablemark.svg?style=flat-square&maxAge=3600)](https://www.npmjs.com/package/tablemark) [![Travis CI](https://img.shields.io/travis/citycide/tablemark.svg?style=flat-square&maxAge=3600)](https://travis-ci.org/citycide/tablemark) [![JavaScript Standard Style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square&maxAge=3600)](https://standardjs.com) | ||
// | Name | Age | IsCool | | ||
// | ----- | ----- | ------ | | ||
// | Bob | 21 | false | | ||
// | Sarah | 22 | true | | ||
// | Lee | 23 | true | | ||
// | Name | Age | Is cool | | ||
// | ----- | ----- | ------- | | ||
// | Bob | 21 | false | | ||
// | Sarah | 22 | true | | ||
// | Lee | 23 | true | | ||
``` | ||
@@ -37,9 +37,36 @@ | ||
| Name | Age | IsCool | | ||
| ----- | ----- | ------ | | ||
| Bob | 21 | false | | ||
| Sarah | 22 | true | | ||
| Lee | 23 | true | | ||
| Name | Age | Is cool | | ||
| ----- | ----- | ------- | | ||
| Bob | 21 | false | | ||
| Sarah | 22 | true | | ||
| Lee | 23 | true | | ||
## api | ||
```js | ||
tablemark(input[, options = {}]) | ||
``` | ||
> **Arguments** | ||
- `{Array<Object>} input`: the data to table-ify | ||
- `{Object} [options = {}]` | ||
| key | type | default | description | | ||
| :------------: | :----------: | :--------: | ---------------------------------------------- | | ||
| `columns` | `<Array>` | - | Array of column descriptors. | | ||
| `caseHeaders` | `<Boolean>` | `true` | Sentence case headers derived from keys. | | ||
| `stringify` | `<Function>` | - | Provide a custom "toString" function. | | ||
| `wrap.width` | `<Number>` | `Infinity` | Wrap texts at this length. | | ||
| `wrap.gutters` | `<Boolean>` | `false` | Add sides (`\| <content> \|`) to wrapped rows. | | ||
The `columns` array can either contain objects, in which case their | ||
`name` and `align` properties will be used to alter the display of | ||
the column in the table, or any other type which will be coerced | ||
to a string if necessary and used as a replacement for the column | ||
name. | ||
### `columns` | ||
```js | ||
tablemark([ | ||
@@ -72,26 +99,70 @@ { name: 'Bob', age: 21, isCool: false }, | ||
## api | ||
### `stringify` | ||
### tablemark | ||
```js | ||
tablemark(input, [options = {}]) | ||
tablemark([ | ||
{ name: 'Bob', pet_owner: true, studying: false }, | ||
{ name: 'Sarah', pet_owner: false, studying: true }, | ||
{ name: 'Sarah', pet_owner: true, studying: true } | ||
], { | ||
stringify, | ||
columns: [ | ||
{ align: 'left' }, | ||
{ align: 'center' }, | ||
{ align: 'center' } | ||
] | ||
}) | ||
function stringify (v) { | ||
if (v === true) return '✔' | ||
if (!v) return '' | ||
return v | ||
} | ||
// | Name | Pet owner | Studying | | ||
// | :---- | :-------: | :------: | | ||
// | Bob | ✔︎ | | | ||
// | Sarah | | ✔ | | ||
// | Lee | ✔ | ✔ | | ||
``` | ||
> **Arguments** | ||
### `wrap` | ||
- `{Array<Object>} input`: the data to table-ify | ||
- `{Object} [options = {}]` | ||
To output valid [GitHub Flavored Markdown](https://github.github.com/gfm/) a | ||
cell must not contain newlines. Consider replacing those with `<br />` (e.g. | ||
using the `stringify` option). | ||
| key | type | default | description | | ||
| :-----------: | :----------: | :-----: | ---------------------------------------- | | ||
| `columns` | `<Array>` | - | Array of column descriptors. | | ||
| `caseHeaders` | `<Boolean>` | `true` | Sentence case headers derived from keys. | | ||
| `stringify` | `<Function>` | - | Provide a custom "toString" function. | | ||
Set the `wrap.width` option to wrap any content at that length onto a new | ||
adjacent line: | ||
The `columns` array can either contain objects, in which case their | ||
`name` and `align` properties will be used to alter the display of | ||
the column in the table, or any other type which will be coerced | ||
to a string if necessary and used as a replacement for the column | ||
name. | ||
```js | ||
tablemark([ | ||
{ star: false, name: 'Benjamin' }, | ||
{ star: true, name: 'Jet Li' } | ||
], { wrap: { width: 5 } }) | ||
// | Star | Name | | ||
// | ----- | ----- | | ||
// | false | Benja | | ||
// min | ||
// | true | Jet | | ||
// Li | ||
``` | ||
Enable `wrap.gutters` to add pipes on all lines: | ||
```js | ||
tablemark([ | ||
{ star: false, name: 'Benjamin' }, | ||
{ star: true, name: 'Jet Li' } | ||
], { wrap: { width: 5, gutters: true } }) | ||
// | Star | Name | | ||
// | ----- | ----- | | ||
// | false | Benja | | ||
// | | min | | ||
// | true | Jet | | ||
// | | Li | | ||
``` | ||
## see also | ||
@@ -98,0 +169,0 @@ |
Sorry, the diff of this file is not supported yet
12919
5
136
185
2
4
+ Addedsplit-text-to-chunks@^1.0.0
+ Addedget-stdin@5.0.1(transitive)
+ Addedminimist@1.2.8(transitive)
+ Addedsplit-text-to-chunks@1.0.0(transitive)