Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
wooorm committed Oct 22, 2016
0 parents commit f31a010
Show file tree
Hide file tree
Showing 27 changed files with 373 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
@@ -0,0 +1,9 @@
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
7 changes: 7 additions & 0 deletions .gitignore
@@ -0,0 +1,7 @@
.DS_Store
*.log
.nyc_output/
coverage/
node_modules/
rehype-autolink-headings.js
rehype-autolink-headings.min.js
23 changes: 23 additions & 0 deletions .travis.yml
@@ -0,0 +1,23 @@
language: node_js
node_js:
- '4.0'
- '5.0'
- '6.0'
after_script: bash <(curl -s https://codecov.io/bash)
deploy:
- provider: npm
email: tituswormer@gmail.com
api_key:
secure: HdFnAVT9XT3zqLNfU4s1eefgOjVeqM97VVE4tW5tL6mbC843pJrx0pfsaBWWw1EPrOZZjPLciqgfxYJSCGf2kNKHAWrIiEa1sRf9J9OSRMix8I18N/E4f4bwxd0No6EeYqUjiHQp0DP9RKxQRi3Gq/EPHA1lDTzcK7hNasoWLze02WGIFb8SVb9b3Z3jrncU/XNdGVkCd4dhyPCjZLkt8ITfnk/cLu4/T0utQdtdT5+MTjtjt3L83fCWP6B6UyForYHmq4jH3eoOfxxyr+CSQ0T0qFMEFPqRva1VxhNv40NFoOarZUhugd17UZjQCEqF7pXeEhKB2iqBZyvNsAnP+bxR0xnxXyGmYVqVExWWbqFVOw280FUfj3Mu+N24X0A1L0ptLkratIps5go3m9cgDyU9ChJgbm3dvm7E/cqMpwaY1nshxSCdil3X/K7tjL+HggXkV05lT3QiJsRUeLXoD2yj6M2hWZc/nHIMr5lXjp8NkvgxPag7WD5xRsrMGq89RiVnmMPVmMwRVWbFD+Bzwn6VRfVEu9t7Rj1sK4OerVjwKWkq/+9D/WZdLAWSAt+jUYF7jkpUrOkvHFzwkOWILALFMdnwphr9HUN7+Q8cAIbxE/GnIfEDudPW7rIkJpkEdWtM6Qs/coem3ReB8x4z2bIil6mR519Z9KCpXmnxqHc=
on:
tags: true
node: '5.0'
- provider: releases
api_key:
secure: XUaKdoAPUpaRfWxNrXWec3inkKR2kmdVnJFo/ywOpOF/XOPujHEKxRRbAl3KyOsSElknOs7qy8OqoAXqdB0fKKhohfGu7uXriJm0pRLdIM1nzHjKMcntb6ieV4393smkMW8zDquJgMVxUVj/shRio3Yd0lpmPhJTyUcjRK8X5cAtv73A9HJkXRXOnL44GteukzY83ivanNjrx320XL5q97YY6ePXcquv33O8FVQ6tJax+OuFGNt/6m1Ygxl5/J8r7PqFIL+I54W9USZmXbfsXeubdIDFfYV72nUP4ZrsEBG4Gwl29aB4lS5D1tifEHtGJ+2ZpzM3zSGI5rTdA2S6RwTHDj9PD/afVhWN+ldXF5VmBXZiGRRWlieL9tvSuAQspi9IDoFSjb/ejZcpu2+HwKI6gcTu6oOT9/AMEd8ZZrfBxCZOVAk2ZqpayzqWlyKb335BH8y+tcYSmyECOQwc3JPqXJxeGTnALjQRaNM5ajnFj5pNx2WV8kjfAuRKwZbWKI1rd0YG8QPYSxsbeZUppq4yYyVXJ1kIKO7mL3Uil8zewgWmHyMsYbXwL/5WNA2GE66q8PbxjahS3zYn6aIhXVkUe/Je79j90uim1sq08BiCr7ArXAxmmg+SPVA7k2pRjnsDJBvgnQNLFmZKfqL3zxJQajFawNz4H4loajlcLMg=
file:
- "rehype-autolink-headings.js"
- "rehype-autolink-headings.min.js"
on:
tags: true
node: '6.0'
22 changes: 22 additions & 0 deletions LICENSE
@@ -0,0 +1,22 @@
(The MIT License)

Copyright (c) 2016 Titus Wormer <tituswormer@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
68 changes: 68 additions & 0 deletions index.js
@@ -0,0 +1,68 @@
'use strict';

/* Dependencies. */
var xtend = require('xtend');
var visit = require('unist-util-visit');
var has = require('hast-util-has-property');
var is = require('hast-util-is-element');

/* Expose. */
module.exports = autolink;

/* Constants. */
var headings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
var methods = {prepend: 'unshift', append: 'push'};
var contentDefaults = {
type: 'element',
tagName: 'span',
properties: {className: ['icon', 'icon-link']}
};

/* Attacher - Add links to headings. */
function autolink(processor, options) {
var settings = options || {};
var props = settings.properties;
var behavior = settings.behaviour || settings.behavior || 'prepend';
var content = settings.content || contentDefaults;
var fn = behavior === 'wrap' ? wrap : inject;

if (behavior !== 'wrap' && !props) {
props = {ariaHidden: true};
}

if (content && typeof content === 'object' && !('length' in content)) {
content = [content];
}

return transformer;

function transformer(tree) {
visit(tree, visitor);
}

function visitor(node) {
if (is(node, headings) && has(node, 'id')) {
fn(node);
}
}

function wrap(node) {
var child = icon(node);
child.children = node.children;
node.children = [child];
}

function inject(node) {
var child = icon(node);
child.children = content.concat();
node.children[methods[behavior]](child);
}

function icon(node) {
return {
type: 'element',
tagName: 'a',
properties: xtend(props, {href: '#' + node.properties.id})
};
}
}
68 changes: 68 additions & 0 deletions package.json
@@ -0,0 +1,68 @@
{
"name": "rehype-autolink-headings",
"version": "0.0.0",
"description": "Add links to headings in HTML",
"license": "MIT",
"keywords": [
"rehype",
"plugin",
"heading",
"link",
"html"
],
"repository": "https://github.com/wooorm/rehype-autolink-headings",
"bugs": "https://github.com/wooorm/rehype-autolink-headings/issues",
"author": "Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)",
"contributors": [
"Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)"
],
"files": [
"index.js"
],
"dependencies": {
"hast-util-has-property": "^1.0.0",
"hast-util-is-element": "^1.0.0",
"unist-util-visit": "^1.1.0",
"xtend": "^4.0.1"
},
"devDependencies": {
"bail": "^1.0.1",
"browserify": "^13.0.0",
"esmangle": "^1.0.0",
"is-hidden": "^1.0.1",
"negate": "^1.0.0",
"nyc": "^8.3.1",
"rehype": "^3.0.0",
"remark-cli": "^2.0.0",
"remark-preset-wooorm": "^1.0.0",
"tape": "^4.0.0",
"to-vfile": "^2.1.1",
"xo": "^0.17.0"
},
"scripts": {
"build-md": "remark . --quiet --frail",
"build-bundle": "browserify index.js --bare -s rehypeAutolinkHeadings > rehype-autolink-headings.js",
"build-mangle": "esmangle rehype-autolink-headings.js > rehype-autolink-headings.min.js",
"build": "npm run build-md && npm run build-bundle && npm run build-mangle",
"lint": "xo",
"test-api": "node test/index.js",
"test-coverage": "nyc --reporter lcov tape test/index.js",
"test": "npm run build && npm run lint && npm run test-coverage"
},
"nyc": {
"check-coverage": true,
"lines": 100,
"functions": 100,
"branches": 100
},
"xo": {
"space": true,
"ignores": [
"rehype-autolink-headings.js"
]
},
"remarkConfig": {
"output": true,
"presets": "wooorm"
}
}
95 changes: 95 additions & 0 deletions readme.md
@@ -0,0 +1,95 @@
# rehype-autolink-headings [![Build Status][travis-badge]][travis] [![Coverage Status][codecov-badge]][codecov]

Automatically add links to headings (h1-h6) with [**rehype**][rehype].

## Installation

[npm][]:

```bash
npm install rehype-autolink-headings
```

## Usage

Say we have the following fragment:

```html
<h1>Lorem ipsum 😪</h1>
<h2>dolor—sit—amet</h2>
<h3>consectetur &amp; adipisicing</h3>
<h4>elit</h4>
<h5>elit</h5>
```

And our script, `example.js`, looks as follows:

```javascript
var fs = require('fs');
var rehype = require('rehype');
var slug = require('rehype-slug');
var link = require('rehype-autolink-headings');

var doc = fs.readFileSync('fragment.html');

rehype().use(slug).use(link).process(doc, {fragment: true}, function (err, file) {
if (err) throw err;
console.log(String(file));
});
```

Now, running `node example` yields:

```html
<h1 id="lorem-ipsum-"><a aria-hidden="true" href="#lorem-ipsum-"><span class="icon icon-link"></span></a>Lorem ipsum 😪</h1>
<h2 id="dolorsitamet"><a aria-hidden="true" href="#dolorsitamet"><span class="icon icon-link"></span></a>dolor—sit—amet</h2>
<h3 id="consectetur--adipisicing"><a aria-hidden="true" href="#consectetur--adipisicing"><span class="icon icon-link"></span></a>consectetur &#x26; adipisicing</h3>
<h4 id="elit"><a aria-hidden="true" href="#elit"><span class="icon icon-link"></span></a>elit</h4>
<h5 id="elit-1"><a aria-hidden="true" href="#elit-1"><span class="icon icon-link"></span></a>elit</h5>
```

## API

### `rehype().use(link[, options])`

Adds links to headings (h1-h6) with an `id`

###### `options`

* `behavior` (`string`, default: `prepend`)
— How to add a link:
* `'prepend'` and `'append'` inserts a link with `content`
in it respectively before or after the heading contents;
* `'wrap'` wraps a link around the current heading contents.
* `properties` (`Object`, default: `{}` if `'wrap'`,
`{ariaHidden: true}` otherwise)
— Properties for the added link;
* `content` (`Node` or `Array.<Node>`, default: a `span` element
with `icon` and `icon-link` classes)
— Content to add in link. Ignored if `'wrap'`.

## Related

* [**rehype-slug**](https://github.com/wooorm/rehype-slug).

## License

[MIT][license] © [Titus Wormer][author]

<!-- Definitions -->

[travis-badge]: https://img.shields.io/travis/wooorm/rehype-autolink-headings.svg

[travis]: https://travis-ci.org/wooorm/rehype-autolink-headings

[codecov-badge]: https://img.shields.io/codecov/c/github/wooorm/rehype-autolink-headings.svg

[codecov]: https://codecov.io/github/wooorm/rehype-autolink-headings

[npm]: https://docs.npmjs.com/cli/install

[license]: LICENSE

[author]: http://wooorm.com

[rehype]: https://github.com/wooorm/rehype
3 changes: 3 additions & 0 deletions test/fixtures/behavior-append/config.json
@@ -0,0 +1,3 @@
{
"behavior": "append"
}
1 change: 1 addition & 0 deletions test/fixtures/behavior-append/input.html
@@ -0,0 +1 @@
<h1 id="foo">Bar</h1>
1 change: 1 addition & 0 deletions test/fixtures/behavior-append/output.html
@@ -0,0 +1 @@
<h1 id="foo">Bar<a aria-hidden="true" href="#foo"><span class="icon icon-link"></span></a></h1>
1 change: 1 addition & 0 deletions test/fixtures/behavior-prepend/input.html
@@ -0,0 +1 @@
<h1 id="foo">Bar</h1>
1 change: 1 addition & 0 deletions test/fixtures/behavior-prepend/output.html
@@ -0,0 +1 @@
<h1 id="foo"><a aria-hidden="true" href="#foo"><span class="icon icon-link"></span></a>Bar</h1>
3 changes: 3 additions & 0 deletions test/fixtures/behavior-wrap/config.json
@@ -0,0 +1,3 @@
{
"behavior": "wrap"
}
1 change: 1 addition & 0 deletions test/fixtures/behavior-wrap/input.html
@@ -0,0 +1 @@
<h1 id="foo">Bar</h1>
1 change: 1 addition & 0 deletions test/fixtures/behavior-wrap/output.html
@@ -0,0 +1 @@
<h1 id="foo"><a href="#foo">Bar</a></h1>
7 changes: 7 additions & 0 deletions test/fixtures/content-multiple/config.json
@@ -0,0 +1,7 @@
{
"content": [
{"type": "comment", "value": "Foo "},
{"type": "text", "value": "bar"},
{"type": "comment", "value": " baz"}
]
}
1 change: 1 addition & 0 deletions test/fixtures/content-multiple/input.html
@@ -0,0 +1 @@
<h1 id="foo">Bar</h1>
1 change: 1 addition & 0 deletions test/fixtures/content-multiple/output.html
@@ -0,0 +1 @@
<h1 id="foo"><a aria-hidden="true" href="#foo"><!--Foo -->bar<!-- baz--></a>Bar</h1>
3 changes: 3 additions & 0 deletions test/fixtures/content-one/config.json
@@ -0,0 +1,3 @@
{
"content": {"type": "comment", "value": "Tada!"}
}
1 change: 1 addition & 0 deletions test/fixtures/content-one/input.html
@@ -0,0 +1 @@
<h1 id="foo">Bar</h1>
1 change: 1 addition & 0 deletions test/fixtures/content-one/output.html
@@ -0,0 +1 @@
<h1 id="foo"><a aria-hidden="true" href="#foo"><!--Tada!--></a>Bar</h1>
1 change: 1 addition & 0 deletions test/fixtures/default/input.html
@@ -0,0 +1 @@
<h1 id="foo">Bar</h1>
1 change: 1 addition & 0 deletions test/fixtures/default/output.html
@@ -0,0 +1 @@
<h1 id="foo"><a aria-hidden="true" href="#foo"><span class="icon icon-link"></span></a>Bar</h1>
6 changes: 6 additions & 0 deletions test/fixtures/properties/config.json
@@ -0,0 +1,6 @@
{
"properties": {
"className": ["foo", "bar", "baz"],
"dataQux": "quux"
}
}
1 change: 1 addition & 0 deletions test/fixtures/properties/input.html
@@ -0,0 +1 @@
<h1 id="foo">Bar</h1>
1 change: 1 addition & 0 deletions test/fixtures/properties/output.html
@@ -0,0 +1 @@
<h1 id="foo"><a class="foo bar baz" data-qux="quux" href="#foo"><span class="icon icon-link"></span></a>Bar</h1>
45 changes: 45 additions & 0 deletions test/index.js
@@ -0,0 +1,45 @@
'use strict';

/* Dependencies. */
var fs = require('fs');
var path = require('path');
var bail = require('bail');
var test = require('tape');
var rehype = require('rehype');
var vfile = require('to-vfile');
var negate = require('negate');
var hidden = require('is-hidden');
var autolink = require('..');

/* Tests. */
test('format', function (t) {
var root = path.join(__dirname, 'fixtures');

fs.readdir(root, function (err, files) {
bail(err);
files = files.filter(negate(hidden));
t.plan(files.length);

files.forEach(one);
});

function one(fixture) {
var base = path.join(root, fixture);
var input = vfile.readSync(path.join(base, 'input.html'));
var output = vfile.readSync(path.join(base, 'output.html'));
var config;

try {
config = JSON.parse(fs.readFileSync(path.join(base, 'config.json')));
} catch (err) {}

rehype().use(autolink, config).process(input, {fragment: true}, function (err) {
t.test(fixture, function (st) {
st.plan(3);
st.ifErr(err, 'shouldn’t throw');
st.equal(input.messages.length, 0, 'shouldn’t warn');
st.equal(String(input), String(output), 'should match');
});
});
}
});

0 comments on commit f31a010

Please sign in to comment.