This commit is contained in:
chrosey
2017-09-13 07:52:34 +02:00
parent a1f16c37f4
commit 2340b0226b
24621 changed files with 2912161 additions and 149 deletions
+129
View File
@@ -0,0 +1,129 @@
marked-terminal
===
> Custom Renderer for [marked](https://github.com/chjj/marked)
allowing for printing Markdown to the Terminal. Supports pretty tables, syntax
highlighting for javascript, and overriding all colors and styles.
Could for instance be used to print usage information.
## Install
```sh
npm install marked marked-terminal
```
## Example
```javascript
var marked = require('marked');
var TerminalRenderer = require('marked-terminal');
marked.setOptions({
// Define custom renderer
renderer: new TerminalRenderer()
});
// Show the parsed data
console.log(marked('# Hello \n This is **markdown** printed in the `terminal`'));
```
This will produce the following:
![Screenshot of marked-terminal](./screenshot.png)
### Syntax Highlighting
Also have support for syntax highlighting using [cardinal](https://github.com/thlorenz/cardinal).
You can override highlight defaults by passing in settings as the second argument for TerminalRenderer,
or you can create a `.cardinalrc` as defined in the [cardinal README](https://github.com/thlorenz/cardinal).
Having the following markdown input:
<pre>
```js
var foo = function(bar) {
console.log(bar);
};
foo('Hello');
```
</pre>
...we will convert it into terminal format:
```javascript
// Show the parsed data
console.log(marked(exampleSource));
```
This will produce the following:
![Screenshot of marked-terminal](./screenshot2.png)
## API
Constructur: `new TerminalRenderer([options][, highlightOptions])`
### `options`
Optional
Used to override default styling.
Default values are:
```javascript
var defaultOptions = {
// Colors
code: chalk.yellow,
blockquote: chalk.gray.italic,
html: chalk.gray,
heading: chalk.green.bold,
firstHeading: chalk.magenta.underline.bold,
hr: chalk.reset,
listitem: chalk.reset,
table: chalk.reset,
paragraph: chalk.reset,
strong: chalk.bold,
em: chalk.italic,
codespan: chalk.yellow,
del: chalk.dim.gray.strikethrough,
link: chalk.blue,
href: chalk.blue.underline,
// Reflow and print-out width
width: 80, // only applicable when reflow is true
reflowText: false,
// Should it prefix headers?
showSectionPrefix: true,
// Whether or not to undo marked escaping
// of enitities (" -> &quot; etc)
unescape: true,
// Whether or not to show emojis
emoji: true,
// Options passed to cli-table
tableOptions: {},
// The size of tabs in number of spaces or as tab characters
tab: 3 // examples: 4, 2, \t, \t\t
};
```
#### Example of overriding defaults
```javascript
marked.setOptions({
renderer: new TerminalRenderer({
codespan: chalk.underline.magenta,
})
});
```
### `highlightOptions`
Options passed into [cardinal](https://github.com/thlorenz/cardinal). See
readme there to see what options to pass.
See [more examples](./example/)
+344
View File
@@ -0,0 +1,344 @@
"use strict";
var chalk = require('chalk');
var Table = require('cli-table');
var assign = require('lodash.assign');
var cardinal = require('cardinal');
var emoji = require('node-emoji');
var TABLE_CELL_SPLIT = '^*||*^';
var TABLE_ROW_WRAP = '*|*|*|*';
var TABLE_ROW_WRAP_REGEXP = new RegExp(escapeRegExp(TABLE_ROW_WRAP), 'g');
var COLON_REPLACER = '*#COLON|*';
var COLON_REPLACER_REGEXP = new RegExp(escapeRegExp(COLON_REPLACER), 'g');
var TAB_ALLOWED_CHARACTERS = ['\t'];
// HARD_RETURN holds a character sequence used to indicate text has a
// hard (no-reflowing) line break. Previously \r and \r\n were turned
// into \n in marked's lexer- preprocessing step. So \r is safe to use
// to indicate a hard (non-reflowed) return.
var HARD_RETURN = '\r',
HARD_RETURN_RE = new RegExp(HARD_RETURN),
HARD_RETURN_GFM_RE = new RegExp(HARD_RETURN + '|<br />');
var defaultOptions = {
code: chalk.yellow,
blockquote: chalk.gray.italic,
html: chalk.gray,
heading: chalk.green.bold,
firstHeading: chalk.magenta.underline.bold,
hr: chalk.reset,
listitem: chalk.reset,
table: chalk.reset,
paragraph: chalk.reset,
strong: chalk.bold,
em: chalk.italic,
codespan: chalk.yellow,
del: chalk.dim.gray.strikethrough,
link: chalk.blue,
href: chalk.blue.underline,
text: identity,
unescape: true,
emoji: true,
width: 80,
showSectionPrefix: true,
reflowText: false,
tab: 3,
tableOptions: {}
};
function Renderer(options, highlightOptions) {
this.o = assign({}, defaultOptions, options);
this.tab = sanitizeTab(this.o.tab, defaultOptions.tab);
this.tableSettings = this.o.tableOptions;
this.emoji = this.o.emoji ? insertEmojis : identity;
this.unescape = this.o.unescape ? unescapeEntities : identity;
this.highlightOptions = highlightOptions || {};
this.transform = compose(undoColon, this.unescape, this.emoji);
};
// Compute length of str not including ANSI escape codes.
// See http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
function textLength(str) {
return str.replace(/\u001b\[(?:\d{1,3})(?:;\d{1,3})*m/g, "").length;
};
Renderer.prototype.textLength = textLength;
function fixHardReturn(text, reflow) {
return reflow ? text.replace(HARD_RETURN, /\n/g) : text;
}
Renderer.prototype.text = function (text) {
return this.o.text(text);
};
Renderer.prototype.code = function(code, lang, escaped) {
return '\n' + indentify(highlight(code, lang, this.o, this.highlightOptions), this.tab) + '\n\n';
};
Renderer.prototype.blockquote = function(quote) {
return '\n' + this.o.blockquote(indentify(quote.trim(), this.tab)) + '\n\n';
};
Renderer.prototype.html = function(html) {
return this.o.html(html);
};
Renderer.prototype.heading = function(text, level, raw) {
text = this.transform(text);
var prefix = this.o.showSectionPrefix ?
(new Array(level + 1)).join('#')+' ' : '';
text = prefix + text;
if (this.o.reflowText) {
text = reflowText(text, this.o.width, this.options.gfm);
}
if (level === 1) {
return this.o.firstHeading(text) + '\n';
}
return this.o.heading(text) + '\n';
};
Renderer.prototype.hr = function() {
return this.o.hr(hr('-', this.o.reflowText && this.o.width)) + '\n';
};
Renderer.prototype.list = function(body, ordered) {
body = indentLines(this.o.listitem(body), this.tab);
if (!ordered) return body;
return changeToOrdered(body);
};
Renderer.prototype.listitem = function(text) {
var isNested = ~text.indexOf('\n');
if (isNested) text = text.trim();
return '\n * ' + this.transform(text);
};
Renderer.prototype.paragraph = function(text) {
var transform = compose(this.o.paragraph, this.transform);
text = transform(text);
if (this.o.reflowText) {
text = reflowText(text, this.o.width, this.options.gfm);
}
return text + '\n\n';
};
Renderer.prototype.table = function(header, body) {
var table = new Table(assign({}, {
head: generateTableRow(header)[0]
}, this.tableSettings));
generateTableRow(body, this.transform).forEach(function (row) {
table.push(row);
});
return this.o.table(table.toString()) + '\n\n';
};
Renderer.prototype.tablerow = function(content) {
return TABLE_ROW_WRAP + content + TABLE_ROW_WRAP + '\n';
};
Renderer.prototype.tablecell = function(content, flags) {
return content + TABLE_CELL_SPLIT;
};
// span level renderer
Renderer.prototype.strong = function(text) {
return this.o.strong(text);
};
Renderer.prototype.em = function(text) {
text = fixHardReturn(text, this.o.reflowText);
return this.o.em(text);
};
Renderer.prototype.codespan = function(text) {
text = fixHardReturn(text, this.o.reflowText);
return this.o.codespan(text.replace(/:/g, COLON_REPLACER));
};
Renderer.prototype.br = function() {
return this.o.reflowText ? HARD_RETURN : '\n';
};
Renderer.prototype.del = function(text) {
return this.o.del(text);
};
Renderer.prototype.link = function(href, title, text) {
if (this.options.sanitize) {
try {
var prot = decodeURIComponent(unescape(href))
.replace(/[^\w:]/g, '')
.toLowerCase();
} catch (e) {
return '';
}
if (prot.indexOf('javascript:') === 0) {
return '';
}
}
var hasText = text && text !== href;
var out = '';
if (hasText) out += this.emoji(text) + ' (';
out += this.o.href(href);
if (hasText) out += ')';
return this.o.link(out);
};
Renderer.prototype.image = function(href, title, text) {
var out = '!['+text;
if (title) out += ' ' + title;
return out + '](' + href + ')\n';
};
module.exports = Renderer;
// Munge \n's and spaces in "text" so that the number of
// characters between \n's is less than or equal to "width".
function reflowText (text, width, gfm) {
// Hard break was inserted by Renderer.prototype.br or is
// <br /> when gfm is true
var splitRe = gfm ? HARD_RETURN_GFM_RE : HARD_RETURN_RE,
sections = text.split(splitRe),
reflowed = [];
sections.forEach(function (section) {
var words = section.split(/[ \t\n]+/),
column = 0,
nextText = '';
words.forEach(function (word) {
var addOne = column != 0;
if ((column + textLength(word) + addOne) > width) {
nextText += '\n';
column = 0;
} else if (addOne) {
nextText += " ";
column += 1;
}
nextText += word;
column += textLength(word);
});
reflowed.push(nextText);
});
return reflowed.join('\n');
}
function indentLines (text, tab) {
return text.replace(/\n/g, '\n' + tab) + '\n\n';
}
function changeToOrdered(text) {
var i = 1;
return text.split('\n').reduce(function (acc, line) {
if (!line) return '\n' + acc;
return acc + line.replace(/(\s*)\*/, '$1' + (i++) + '.') + '\n';
});
}
function highlight(code, lang, opts, hightlightOpts) {
if (!chalk.enabled) return code;
var style = opts.code;
code = fixHardReturn(code, opts.reflowText);
if (lang !== 'javascript' && lang !== 'js') {
return style(code);
}
try {
return cardinal.highlight(code, hightlightOpts);
} catch (e) {
return style(code);
}
}
function insertEmojis(text) {
return text.replace(/:([A-Za-z0-9_\-\+]+?):/g, function (emojiString) {
var emojiSign = emoji.get(emojiString);
if (!emojiSign) return emojiString;
return emojiSign + ' ';
});
}
function hr(inputHrStr, length) {
length = length || process.stdout.columns;
return (new Array(length)).join(inputHrStr);
}
function undoColon (str) {
return str.replace(COLON_REPLACER_REGEXP, ':');
}
function indentify(text, tab) {
if (!text) return text;
return tab + text.split('\n').join('\n' + tab);
}
function generateTableRow(text, escape) {
if (!text) return [];
escape = escape || identity;
var lines = escape(text).split('\n');
var data = [];
lines.forEach(function (line) {
if (!line) return;
var parsed = line.replace(TABLE_ROW_WRAP_REGEXP, '').split(TABLE_CELL_SPLIT);
data.push(parsed.splice(0, parsed.length - 1));
});
return data;
}
function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
function unescapeEntities(html) {
return html
.replace(/&amp;/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'");
}
function identity (str) {
return str;
}
function compose () {
var funcs = arguments;
return function() {
var args = arguments;
for (var i = funcs.length; i-- > 0;) {
args = [funcs[i].apply(this, args)];
}
return args[0];
};
}
function isAllowedTabString (string) {
return TAB_ALLOWED_CHARACTERS.some(function (char) {
return string.match('^('+char+')+$');
});
}
function sanitizeTab (tab, fallbackTab) {
if (typeof tab === 'number') {
return (new Array(tab + 1)).join(' ');
} else if (typeof tab === 'string' && isAllowedTabString(tab)) {
return tab;
} else {
return (new Array(fallbackTab + 1)).join(' ');
}
}
+107
View File
@@ -0,0 +1,107 @@
{
"_args": [
[
{
"raw": "marked-terminal@^1.6.2",
"scope": null,
"escapedName": "marked-terminal",
"name": "marked-terminal",
"rawSpec": "^1.6.2",
"spec": ">=1.6.2 <2.0.0",
"type": "range"
},
"c:\\xampp\\htdocs\\laravel\\node_modules\\cli-usage"
]
],
"_from": "marked-terminal@>=1.6.2 <2.0.0",
"_id": "marked-terminal@1.7.0",
"_inCache": true,
"_location": "/marked-terminal",
"_nodeVersion": "6.5.0",
"_npmOperationalInternal": {
"host": "packages-12-west.internal.npmjs.com",
"tmp": "tmp/marked-terminal-1.7.0.tgz_1477416137016_0.9057166781276464"
},
"_npmUser": {
"name": "mikaelb",
"email": "mikaelbre@gmail.com"
},
"_npmVersion": "3.10.3",
"_phantomChildren": {},
"_requested": {
"raw": "marked-terminal@^1.6.2",
"scope": null,
"escapedName": "marked-terminal",
"name": "marked-terminal",
"rawSpec": "^1.6.2",
"spec": ">=1.6.2 <2.0.0",
"type": "range"
},
"_requiredBy": [
"/cli-usage"
],
"_resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-1.7.0.tgz",
"_shasum": "c8c460881c772c7604b64367007ee5f77f125904",
"_shrinkwrap": null,
"_spec": "marked-terminal@^1.6.2",
"_where": "c:\\xampp\\htdocs\\laravel\\node_modules\\cli-usage",
"author": {
"name": "Mikael Brevik"
},
"bugs": {
"url": "https://github.com/mikaelbr/marked-terminal/issues"
},
"dependencies": {
"cardinal": "^1.0.0",
"chalk": "^1.1.3",
"cli-table": "^0.3.1",
"lodash.assign": "^4.2.0",
"node-emoji": "^1.4.1"
},
"description": "A custom render for marked to output to the Terminal",
"devDependencies": {
"marked": "^0.3.6",
"mocha": "^3.1.2"
},
"directories": {
"example": "example"
},
"dist": {
"shasum": "c8c460881c772c7604b64367007ee5f77f125904",
"tarball": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-1.7.0.tgz"
},
"files": [
"index.js"
],
"gitHead": "c043e41193e26beb26d4adb6b46e1b983697a92f",
"homepage": "https://github.com/mikaelbr/marked-terminal",
"keywords": [
"marked",
"render",
"terminal",
"markdown",
"markdown-to-terminal"
],
"license": "MIT",
"main": "index.js",
"maintainers": [
{
"name": "mikaelb",
"email": "mikaelbre@gmail.com"
}
],
"name": "marked-terminal",
"optionalDependencies": {},
"peerDependencies": {
"marked": "^0.3.3"
},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/mikaelbr/marked-terminal.git"
},
"scripts": {
"test": "mocha tests/*.js --reporter spec"
},
"version": "1.7.0"
}