markdown-toc
Advanced tools
Comparing version 0.5.0 to 0.5.1
132
index.js
@@ -10,2 +10,6 @@ /*! | ||
/** | ||
* Module dependencies | ||
*/ | ||
var Remarkable = require('remarkable'); | ||
@@ -16,21 +20,28 @@ var extend = require('extend-shallow'); | ||
/** | ||
* Expose `toc` | ||
* Load `generate` as a remarkable plugin and | ||
* expose the `toc` function. | ||
* | ||
* @param {String} `str` String of markdown | ||
* @param {Object} `options` | ||
* @return {String} Markdown-formatted table of contents | ||
*/ | ||
module.exports = toc; | ||
function toc(str, options) { | ||
module.exports = function toc(str, options) { | ||
return new Remarkable() | ||
.use(generate(options)) | ||
.render(str); | ||
} | ||
}; | ||
/** | ||
* Expose `insert` method | ||
*/ | ||
module.exports.insert = require('./lib/insert'); | ||
/** | ||
* Generate a markdown table of contents. | ||
* Generate a markdown table of contents. This is the | ||
* function that does all of the main work with Remarkable. | ||
* | ||
* @param {Object} `options` | ||
* @return {String} | ||
* @api private | ||
*/ | ||
@@ -43,11 +54,9 @@ | ||
md.renderer.render = function (tokens) { | ||
tokens = tokens.slice(); | ||
var res = [], i = 0, h = 0; | ||
var len = tokens.length; | ||
var tocstart = -1; | ||
var res = []; | ||
var i = 0; | ||
var h = 0; | ||
while (len--) { | ||
var token = tokens[i++]; | ||
if (/<!--[ \t]*toc[ \t]*-->/.test(token.content)) { | ||
@@ -59,15 +68,2 @@ tocstart = token.lines[1]; | ||
var lvl = tokens[i].lvl = tokens[i - 1].hLevel; | ||
// Keep the first h1? This is `true` by default | ||
if(opts.firsth1 === false) { | ||
// lvl -= 1; | ||
if (++h === 1) { | ||
continue; | ||
} | ||
} | ||
// if `lvl` is greater than the max depth | ||
if(lvl > opts.maxdepth) { | ||
break; | ||
} | ||
res.push(tokens[i]); | ||
@@ -80,5 +76,3 @@ } | ||
res = res.reduce(function(acc, token) { | ||
if (token.lines[0] > tocstart) { | ||
acc.push(token); | ||
} | ||
if (token.lines[0] > tocstart) { acc.push(token); } | ||
token = linkify(token, opts); | ||
@@ -99,2 +93,38 @@ return acc; | ||
/** | ||
* Render markdown list bullets | ||
* | ||
* @param {Array} `arr` Array of listitem objects | ||
* @param {Object} `opts` | ||
* @return {String} | ||
*/ | ||
function bullets(arr, opts) { | ||
var unindent = 0; | ||
// Keep the first h1? This is `true` by default | ||
if(opts && opts.firsth1 === false) { | ||
unindent = 1; | ||
arr.shift(); | ||
} | ||
var len = arr.length; | ||
var res = []; | ||
var i = 0; | ||
while (i < len) { | ||
var ele = arr[i++]; | ||
ele.lvl -= unindent; | ||
res.push(mdu.listitem(ele.content, ele.lvl, opts)); | ||
// break if heading level is greater than maxdepth | ||
if (ele.lvl === opts.maxdepth) { | ||
break; | ||
} | ||
} | ||
return res.join('\n'); | ||
} | ||
/** | ||
* Get the highest heading level in the array, so | ||
@@ -105,3 +135,2 @@ * we can un-indent the proper number of levels. | ||
* @return {Number} Highest level | ||
* @api private | ||
*/ | ||
@@ -115,2 +144,6 @@ | ||
/** | ||
* Turn headings into anchors | ||
*/ | ||
function linkify(ele, opts) { | ||
@@ -128,3 +161,12 @@ var slug = slugify(ele.content, opts); | ||
/** | ||
* Slugify links. | ||
* | ||
* @param {String} `str` The string to slugify | ||
* @param {Object} `opts` Pass a custom slugify function on `slugify` | ||
* @return {String} | ||
*/ | ||
function slugify(str, opts) { | ||
if (opts && opts.slugify === false) return str; | ||
if (opts && typeof opts.slugify === 'function') { | ||
@@ -136,23 +178,23 @@ return opts.slugify(str, opts); | ||
/** | ||
* Optionally strip specified words from headings. | ||
* | ||
* @param {String} `str` | ||
* @param {String} `opts` | ||
* @return {String} | ||
*/ | ||
function strip(str, opts) { | ||
if (opts && typeof opts.strip === 'function') { | ||
opts = opts || {}; | ||
if (!opts.strip) return str; | ||
if (typeof opts.strip === 'function') { | ||
return opts.strip(str, opts); | ||
} | ||
var words = opts.strip || []; | ||
var len = words.length; | ||
var i = 0; | ||
while (len--) { | ||
var word = words[i++]; | ||
var re = '-*' + word + '-*'; | ||
str = str.replace(new RegExp(re), '').trim(); | ||
} | ||
return str; | ||
var strip = opts.strip.join('|'); | ||
var re = new RegExp(strip, 'g'); | ||
return str.trim().replace(re, '') | ||
.replace(/^-|-$/g, ''); | ||
} | ||
function bullets(arr, opts) { | ||
return arr.map(function(ele) { | ||
return mdu.listitem(ele.content, ele.lvl, opts); | ||
}).join('\n'); | ||
} |
'use strict'; | ||
var fs = require('fs'); | ||
var matter = require('gray-matter'); | ||
var toc = require('..'); | ||
module.exports = function format(str) { | ||
var re = /(?:<!-- toc(?:stop)? -->)/g; | ||
/** | ||
* The basic idea: | ||
* | ||
* 1. when front-matter exists, we need to avoid turning its properties into headings. | ||
* 2. We need to detect toc markers on the page. For now it's a simple HTML code comment | ||
* to ensure the markdown is compatible with any parser. | ||
* | ||
* @param {String} `str` Pass a string of markdown | ||
* @return {String} Get the same string back with a TOC inserted | ||
*/ | ||
var file = matter(str); | ||
var lines = split(file.content, re); | ||
module.exports = function insert(str) { | ||
var re = /(?:<!-- toc(?:\s*stop)? -->)/g; | ||
var file; | ||
if (lines.length === 3) { | ||
lines.splice(2, 0, '<!-- tocstop -->'); | ||
lines.splice(1, 0, '<!-- toc -->'); | ||
if (/^---/.test(str)) { | ||
file = matter(str); | ||
str = file.content; | ||
} | ||
if (lines.length === 2) { | ||
lines.splice(1, 0, '<!-- toc -->'); | ||
var sections = split(str, re); | ||
var last = sections[sections.length - 1]; | ||
if (sections.length === 3) { | ||
sections.splice(1, 1, '<!-- toc -->\n\n' + toc(last).content); | ||
sections.splice(2, 0, '<!-- tocstop -->'); | ||
} | ||
var res = lines.join('\n\n'); | ||
return matter.stringify(res, file.data); | ||
} | ||
if (sections.length === 2) { | ||
sections.splice(1, 0, '<!-- toc -->\n\n' + toc(last).content + '\n\n<!-- tocstop -->'); | ||
} | ||
function read(fp) { | ||
return fs.readFileSync(fp, 'utf8'); | ||
} | ||
var res = sections.join('\n\n'); | ||
if (file) { | ||
return matter.stringify(res, file.data); | ||
} | ||
return res; | ||
}; | ||
@@ -37,5 +52,1 @@ function split(str, re) { | ||
} | ||
var str = read('test/expected/insert.md'); | ||
var res = format(str); | ||
console.log(res) |
{ | ||
"name": "markdown-toc", | ||
"description": "Generate a markdown TOC (table of contents).", | ||
"version": "0.5.0", | ||
"description": "Generate a markdown TOC (table of contents) with Remarkable.", | ||
"version": "0.5.1", | ||
"homepage": "https://github.com/jonschlinkert/markdown-toc", | ||
@@ -39,7 +39,19 @@ "author": { | ||
"keywords": [ | ||
"anchor", | ||
"commonmark", | ||
"docs", | ||
"document", | ||
"documentation", | ||
"heading", | ||
"markdown", | ||
"md", | ||
"readme", | ||
"markdown", | ||
"remarkable", | ||
"render", | ||
"renderer", | ||
"table of contents", | ||
"table", | ||
"toc", | ||
"table of contents" | ||
"write" | ||
] | ||
} |
287
README.md
@@ -1,282 +0,117 @@ | ||
# marked-toc [![NPM version](https://badge.fury.io/js/marked-toc.png)](http://badge.fury.io/js/marked-toc) | ||
# markdown-toc [![NPM version](https://badge.fury.io/js/markdown-toc.svg)](http://badge.fury.io/js/markdown-toc) | ||
> Generate a TOC (table of contents) for markdown files | ||
> Generate a markdown TOC (table of contents) with Remarkable. | ||
_(example)_ | ||
<!-- toc --> | ||
* [Getting Started](#getting-started) | ||
* [Usage](#usage) | ||
* [Options](#options) | ||
* [template](#template) | ||
* [bullet](#bullet) | ||
* [maxDepth](#maxdepth) | ||
* [firsth1](#firsth1) | ||
* [omit](#omit) | ||
* [clean](#clean) | ||
* [blacklist](#blacklist) | ||
* [allowedChars](#allowedchars) | ||
* [API](#api) | ||
* [toc](#toc) | ||
* [toc.insert](#tocinsert) | ||
* [toc.add](#tocadd) | ||
* [toc.raw](#tocraw) | ||
* [Contributing](#contributing) | ||
* [Author](#author) | ||
* [License](#license) | ||
## Install with [npm](npmjs.org) | ||
<!-- toc stop --> | ||
## Getting Started | ||
Install the module with [npm](npmjs.org): | ||
```bash | ||
npm i -g marked-toc --save | ||
npm i markdown-toc --save | ||
``` | ||
In any markdown file, add `<!-- toc -->` where you want to add the TOC. Then in the command line, run: | ||
```bash | ||
toc [filename] | ||
``` | ||
If you add the toc to a `README.md`, no need to add `[filename]`, just run `toc`. | ||
## Usage | ||
```javascript | ||
var toc = require('marked-toc'); | ||
var file = fs.readFileSync('README.md', 'utf8'); | ||
```js | ||
var toc = require('markdown-toc'); | ||
// Generate a TOC | ||
toc(file); | ||
toc('# One\n\n# Two').content; | ||
// Results in: | ||
// - [One](#one) | ||
// - [Two](#two) | ||
``` | ||
## Options | ||
To allow customization of the output, an object is returned with the following properties: | ||
All methods accept an object of options as the last argument. | ||
- `content` **{String}**: The generated table of contents. Unless you want to customize rendering, this is all you need. | ||
- `highest` **{Number}**: The highest level heading found. This is used to adjust indentation. | ||
- `tokens` **{Array}**: Headings tokens that can be used for custom rendering | ||
### template | ||
Type: `String` | ||
### toc.insert | ||
Default: `<%= depth %><%= bullet %>[<%= heading %>](#<%= url %>)\n` | ||
Insert a table of contents immediately after an _opening_ `<!-- toc -->` code comment, or replace an existing TOC if both an _opening_ comment and a _closing_ comment (`<!-- tocstop -->`) are found. (This strategy works well since code comments in markdown are hidden when viewed as HTML, e.g. on GitHub README's for example). | ||
The Lo-Dash template used to generate the Table of Contents. | ||
**Example** | ||
**Example (this is the default):** | ||
```js | ||
var tmpl = '<%= depth %><%= bullet %>[<%= heading %>](#<%= url %>)\n'; | ||
toc(file, {template: tmpl}); | ||
``` | ||
### bullet | ||
Type: `String|Array` | ||
Default: `* ` | ||
The bullet to use for each item in the generated TOC. This is passed as a variable to the `<%= bullet %>` template. | ||
If an array, like `['* ', '- ']`, the bullet point strings will be used based on the header depth. | ||
### maxDepth | ||
Type: `Number` | ||
Default: `3` | ||
Use headings whose depth is at most maxDepth. | ||
### firsth1 | ||
Type: `Boolean` | ||
Default: `False` | ||
Include the first h1-level heading in a file. For example, this prevent the first heading in a README from showing up in the TOC. | ||
### omit | ||
Type: `Array` | ||
Default: `['Table of Contents', 'TOC', 'TABLE OF CONTENTS']` | ||
Omit entire headings from the TOC if they have these strings. | ||
### clean | ||
Type: `Array` | ||
Default: `['mixin', 'helper', 'filter']` | ||
Strip "blacklisted" keywords from the headings. | ||
**Example:** | ||
```js | ||
toc(file, {clean: ['docs', 'methods']}); | ||
``` | ||
converts this: | ||
```markdown | ||
## docs-foo | ||
Foo | ||
<!-- toc --> | ||
- old toc 1 | ||
- old toc 2 | ||
- old toc 3 | ||
<!-- tocstop --> | ||
## methods-bar | ||
Bar | ||
## abc | ||
This is a b c. | ||
## xyz | ||
This is x y z. | ||
``` | ||
to: | ||
```markdown | ||
* [foo](#docs-foo) | ||
* [bar](#methods-bar) | ||
Would result in something like: | ||
``` | ||
### blacklist | ||
Type: `Boolean` | ||
Default: `true` | ||
An array of strings used the `omit` option: | ||
```js | ||
['grunt', 'helper', 'handlebars-helper', 'mixin', 'filter', 'assemble-contrib', 'assemble'] | ||
``` | ||
_(These strings are used a lot in documentation headings, but (usually) shouldn't show up in the gererated TOC.)_ | ||
### allowedChars | ||
Type: `String` | ||
Default: `-` | ||
String of chars that you want to be whitelisted when headings are "slugified" for links, e.g. `-_~`. | ||
**Example:** | ||
```markdown | ||
// This heading | ||
# Getting Started | ||
<!-- toc --> | ||
- [abc](#abc) | ||
- [xyz](#xyz) | ||
<!-- tocstop --> | ||
// Converts to this link | ||
* [Getting Started](#getting-started) | ||
## abc | ||
This is a b c. | ||
## xyz | ||
This is x y z. | ||
``` | ||
## API | ||
## Options | ||
Most methods expect a string as the first paramter, so unless otherwise noted, assume that each example gets the `str` variable from: | ||
### options.bullet | ||
```js | ||
var str = fs.readFileSync('README.md', 'utf8') | ||
``` | ||
Type: `String|Array` | ||
### toc | ||
Default: `*` | ||
Generates a Table of Contents from a string. | ||
The bullet to use for each item in the generated TOC. If passed as an array (`['*', '-', '+']`), the bullet point strings will be used based on the header depth. | ||
```js | ||
// Generate a TOC | ||
var table = toc(str); | ||
fs.writeFileSync('toc.md', table); | ||
``` | ||
### toc.insert | ||
### options.maxDepth | ||
Inject a TOC at the insertion point in a string, `<!-- toc -->`. | ||
Type: `Number` | ||
**Params:** | ||
Default: `3` | ||
* `str`: the content | ||
* `options`: object of options | ||
Use headings whose depth is at most maxDepth. | ||
```js | ||
toc.insert(str, options); | ||
``` | ||
### toc.add | ||
### options.firsth1 | ||
1. Read a file and inject a TOC at the specified insertion point, `<!-- toc -->`, | ||
2. Write the file to the specified `dest`, _(or re-write back to the source file if no `dest` is passed)_ | ||
Type: `Boolean` | ||
```js | ||
toc.add(src, dest, options) | ||
``` | ||
Default: `true` | ||
**Example:** | ||
Exclude the first h1-level heading in a file. For example, this prevents the first heading in a README from showing up in the TOC. | ||
```js | ||
toc.add('path/to/source.md', 'path/to/dest.md'); | ||
``` | ||
**Source only:** | ||
## Run tests | ||
```js | ||
toc.add('README.md'); | ||
```bash | ||
npm test | ||
``` | ||
### toc.raw | ||
Output a "raw" (JSON) Table of Contents **object**, for customization and usage in templates | ||
```js | ||
toc.raw(str, options); | ||
``` | ||
Returns an object (JSON) with two properties, `data` and `toc`: | ||
* `data`: array of headings and associated properties used to construct a TOC. **TIP**: this can be extended with properties, such as src path etc. | ||
* `toc`: the actual Table of Contents result, as a string | ||
**Example:** | ||
```json | ||
{ | ||
// Array of | ||
"data": [ | ||
{ | ||
"depth": "", | ||
"bullet": "* ", | ||
"heading": "Getting Started", | ||
"url": "getting-started" | ||
}, | ||
{ | ||
"depth": "", | ||
"bullet": "* ", | ||
"heading": "Usage", | ||
"url": "usage" | ||
} | ||
], | ||
// String. the actual TOC | ||
"toc": "* [Getting Started](#getting-started)\n* [Options](#options)\n* [Contributing](#contributing)\n" | ||
} | ||
``` | ||
See [an example](./examples/toc.json). | ||
## Contributing | ||
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint your code using [jshint](jshint.com) and run tests with `mocha -R spec` before making a pull request. | ||
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/markdown-toc/issues) | ||
## Author | ||
**Jon Schlinkert** | ||
+ [github/jonschlinkert](https://github.com/jonschlinkert) | ||
+ [twitter/jonschlinkert](http://twitter.com/jonschlinkert) | ||
+ [twitter/jonschlinkert](http://twitter.com/jonschlinkert) | ||
## License | ||
Copyright (c) 2014 Jon Schlinkert, contributors | ||
Licensed under the MIT license. | ||
Copyright (c) 2014 Jon Schlinkert | ||
Released under the MIT license | ||
*** | ||
_This file was generated by [verb](https://github.com/assemble/verb) on December 21, 2014._ |
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
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
196
0
10014
118