├── .gitignore ├── README.md ├── UNLICENSE ├── index.js ├── package.json └── test └── all.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Markdown-it Named Headers 2 | 3 | A plugin for [markdown-it](https://github.com/markdown-it/markdown-it). Makes header elments have identifer attributes. 4 | 5 | ``` 6 | # Example Header -->

Example

7 | ``` 8 | 9 | By default, it uses [string.js](http://stringjs.com/)'s [slugify](http://stringjs.com/#methods/slugify) to translate header text into a url safe name. You can override this. See _Options_. 10 | 11 | Cribbed heavily from https://github.com/valeriangalliat/markdown-it-anchor 12 | 13 | ## Install 14 | 15 | ``` 16 | npm install --save-dev markdown-it-named-headers 17 | ``` 18 | 19 | ## Usage 20 | 21 | Use with plain old node: 22 | 23 | ```js 24 | var md = require('markdown-it'), 25 | mdnh = require('markdown-it-named-headers'); 26 | 27 | md.use(mdnh, options); 28 | ``` 29 | 30 | Use as part of a Gulp workflow: (Note: You don't need to require named-headers in your gulpfile. gulp-markdown-it takes care of that for you). 31 | 32 | ```js 33 | var gulp = require('gulp'), 34 | md = require('gulp-markdown-it'); 35 | gulp.task('md', [], function() { 36 | return gulp.src( '**/*.md' ) 37 | .pipe(md({ 38 | plugins: ['markdown-it-named-headers'] 39 | })) 40 | .pipe(gulp.dest('dist')); 41 | }); 42 | ``` 43 | 44 | 45 | ### Options 46 | 47 | 48 | #### Slugify 49 | 50 | ```js 51 | { 52 | slugify: my_slug_function 53 | } 54 | ``` 55 | 56 | If string.js's slugify doesn't fit your needs, you can simply pass in your own slugify function. Basically, the API is: accept any string, return a string suitable for a name attribute. Example: 57 | 58 | ```js 59 | function slugify(input_string) { 60 | var output_string = my_transform_logic(input_string); 61 | return output_string; 62 | } 63 | ``` 64 | 65 | Since we use IDs, we should avoid duplicating them. A second parameter is passed. It is an empty object that will persist across a single call to render. In other words, you can use it to maintain a hash of used_headers per page. 66 | 67 | The default slugify method looks something like this: 68 | 69 | ```js 70 | function slugify(input_string, used_headers) { 71 | var slug = string(input_string).slugify().toString(); 72 | if( used_headers[slug] ) { 73 | used_headers[slug]++; 74 | slug += used_headers[slug]; 75 | } else { 76 | used_headers[slug] += '-' + 1; 77 | } 78 | return slug; 79 | } 80 | ``` 81 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | var string = require('string'); 4 | 5 | var default_slugify = function(s, used_headers) { 6 | var slug = string(s).slugify().toString(); 7 | if( used_headers[slug] ) { 8 | used_headers[slug]++; 9 | slug += used_headers[slug]; 10 | } else { 11 | used_headers[slug] += '-' + 1; 12 | } 13 | return slug; 14 | }; 15 | 16 | var namedheaders = function(md, opts) { 17 | var slugify = (opts && opts.slugify) ? opts.slugify : default_slugify; 18 | var originalHeadingOpen = md.renderer.rules.heading_open; 19 | 20 | md.renderer.rules.heading_open = function (tokens, idx, something, somethingelse, self) { 21 | var used_headers = {}; 22 | 23 | tokens[idx].attrs = tokens[idx].attrs || []; 24 | 25 | var title = tokens[idx + 1].children.reduce(function (acc, t) { 26 | return acc + t.content; 27 | }, ''); 28 | 29 | var slug = slugify(title, used_headers); 30 | tokens[idx].attrs.push(['id', slug]); 31 | 32 | if (originalHeadingOpen) { 33 | return originalHeadingOpen.apply(this, arguments); 34 | } else { 35 | return self.renderToken.apply(self, arguments); 36 | } 37 | }; 38 | }; 39 | 40 | module.exports = namedheaders; 41 | 42 | })(); 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "markdown-it-named-headers", 3 | "version": "0.0.4", 4 | "description": "Headers have name attributes for markdown-it.", 5 | "keywords": [ 6 | "markdown-it-plugin" 7 | ], 8 | "homepage": "https://github.com/leff/markdown-it-named-headers", 9 | "license": { 10 | "type": "Unlicense", 11 | "url": "http://unlicense.org/" 12 | }, 13 | "author": { 14 | "name": "Jason Brackins", 15 | "url": "" 16 | }, 17 | "files": [ 18 | "README.md", 19 | "UNLICENSE", 20 | "index.js" 21 | ], 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/leff/markdown-it-named-headers" 25 | }, 26 | "dependencies": { 27 | "string": "^3.0.1" 28 | }, 29 | "devDependencies": { 30 | "markdown-it": "^4.0.1", 31 | "mocha": "^2.2.1", 32 | "test": ">=0.0.1" 33 | }, 34 | "scripts": { 35 | "test": "node_modules/.bin/mocha test/all.js" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/all.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | MarkdownIt = require('markdown-it'), 3 | md_nh = require('../'); 4 | 5 | 6 | describe('MarkdownIt', function() { 7 | describe('#named-headers', function() { 8 | var md = MarkdownIt().use(md_nh); 9 | 10 | describe('#render()', function(){ 11 | it('one word headers should have names', function(){ 12 | assert.equal( md.render('# test'), '

test

\n'); 13 | }); 14 | 15 | it('case should be normalized', function(){ 16 | assert.equal( md.render('# Test'), '

Test

\n'); 17 | }); 18 | 19 | it('spaces should be dashes', function(){ 20 | assert.equal( md.render('# test string'), '

test string

\n'); 21 | }); 22 | 23 | it('header levels 1 to 6 should be supported', function(){ 24 | assert.equal( md.render('# test'), '

test

\n'); 25 | assert.equal( md.render('## test'), '

test

\n'); 26 | assert.equal( md.render('### test'), '

test

\n'); 27 | assert.equal( md.render('#### test'), '

test

\n'); 28 | assert.equal( md.render('##### test'), '
test
\n'); 29 | assert.equal( md.render('###### test'), '
test
\n'); 30 | }); 31 | 32 | it('identical headers should have unique ids', function(){ 33 | assert.equal( md.render('# test\n# test'), '

test

\n

test

\n'); 34 | assert.equal( md.render('# test\n## test'), '

test

\n

test

\n'); 35 | }); 36 | }); 37 | 38 | }); 39 | }); 40 | --------------------------------------------------------------------------------