Skip to content

Commit

Permalink
adding tests and correctly handling headers of the form '## xxx ##'
Browse files Browse the repository at this point in the history
  • Loading branch information
thlorenz committed Feb 9, 2013
1 parent 60bb2fa commit c7e42da
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 58 deletions.
33 changes: 20 additions & 13 deletions doctoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@

'use strict';

var path = require('path')
, fs = require('fs')
, _ = require('underscore')
, file = require('./lib/file')
var path = require('path')
, fs = require('fs')
, file = require('./lib/file')
, transform = require('./lib/transform')
, argv = process.argv
, argv = process.argv
, files;

function cleanPath(path) {
Expand All @@ -20,16 +19,24 @@ function cleanPath(path) {
function transformAndSave(files) {
console.log('\n==================\n');

_(files)
.chain()
var transformed = files
.map(function (x) {
var content = fs.readFileSync(x.path, 'utf8');
return transform(x, content);
})
.filter(function (x) { return x.transformed; })
.each(function (x) {
fs.writeFileSync(x.path, x.data, 'utf8');
var content = fs.readFileSync(x.path, 'utf8')
, result = transform(content);
result.path = path;
return result;
});
var changed = transformed.filter(function (x) { return x.transformed; })
, unchanged = transformed.filter(function (x) { return !x.transformed; });

unchanged.forEach(function (x) {
console.log('"%s" is up to date', x.path);
});

changed.forEach(function (x) {
console.log('"%s" will be updated', x.path);
fs.writeFileSync(x.path, x.data, 'utf8');
});
}

if (argv.length !== 3) {
Expand Down
81 changes: 37 additions & 44 deletions lib/transform.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
'use strict';

var _ = require('underscore');
var _ = require('underscore')
, anchor = require('anchor-markdown-header');

function notNull(x) { return x !== null; }

function addLink(header) {
return _(header).extend({
link: '#' +
header.name
.trim()
.toLowerCase()
.replace(/ /g,'-')
.replace(/[\[\]`.,()*]/g,'')
});
function addAnchor(header) {
header.anchor = anchor(header.name);
return header;
}


function getHashedHeaders (_lines) {
// Find headers of the form '### xxxx xxx xx'
return _lines
.map(function (x, index) {
var match = /^(\#{1,8})[ ]*(.+)$/.exec(x);

return match
? { rank : match[1].length
, name : match[2]
, line : index
}
: null;
})
.filter(notNull)
.value();
// Turn all headers into '## xxx' even if they were '## xxx ##'
function normalize(header) {
return header.replace(/[ #]+$/, '');
}

// Find headers of the form '### xxxx xxx xx [###]'
return _lines
.map(function (x, index) {
var match = /^(\#{1,8})[ ]*(.+)$/.exec(x);

return match
? { rank : match[1].length
, name : normalize(match[2])
, line : index
}
: null;
})
.filter(notNull)
.value();
}

function getUnderlinedHeaders (_lines) {
Expand All @@ -56,39 +57,35 @@ function getUnderlinedHeaders (_lines) {
.value();
}

module.exports = function transform(f, content) {
module.exports = function transform(content) {
var lines = content.split('\n')
, _lines = _(lines).chain()

, allHeaders = getHashedHeaders(_lines).concat(getUnderlinedHeaders(_lines))
, lowestRank = _(allHeaders).chain().pluck('rank').min().value()
, linkedHeaders = _(allHeaders).map(addLink);
, linkedHeaders = _(allHeaders).map(addAnchor);

if (linkedHeaders.length === 0) return { transformed: false };

var toc =
'**Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)*'
+ '\n\n'
+ linkedHeaders.map(function (x) {
var indent = _(_.range(x.rank - lowestRank))
.reduce(function (acc, x) { return acc + '\t'; }, '');

return indent + '- [' + x.name + '](' + x.link + ')';
})
.join('\n')
+ linkedHeaders
.map(function (x) {
var indent = _(_.range(x.rank - lowestRank))
.reduce(function (acc, x) { return acc + '\t'; }, '');

return indent + '- ' + x.anchor;
})
.join('\n')
+ '\n';

var currentToc = _lines
.first(linkedHeaders[0].line)
.value()
.join('\n');

if (currentToc === toc) {
console.log('"%s" is up to date', f.path);
return { transformed: false };
}

console.log('"%s" will be updated', f.path);
if (currentToc === toc) return { transformed: false };

// Skip all lines up to first header since that is the old table of content
var remainingContent = _lines
Expand All @@ -98,9 +95,5 @@ module.exports = function transform(f, content) {

var data = toc + '\n' + remainingContent;

return {
transformed : true,
data : data,
path : f.path
};
return { transformed : true, data : data };
};
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@
"type": "git",
"url": "git://github.com/thlorenz/doctoc.git"
},
"scripts": {
"test": "node-trap test/*.js"
},
"main": "doctoc.js",
"bin": "doctoc.js",
"engines": {
"node": ">=0.4"
},
"dependencies": {
"underscore": ">=1.3.3"
"underscore": ">=1.3.3",
"anchor-markdown-header": "~0.1.1"
},
"devDependencies": {
"trap": "~0.3.2"
Expand Down
Empty file added test/doctoc.js
Empty file.
57 changes: 57 additions & 0 deletions test/lib/transform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use strict';
/*jshint asi: true */

var test = require('trap').test
, transform = require('../../lib/transform')

function check(md, anchors) {
test('transforming ' + md , function (t) {
var res = transform(md)
t.ok(res.transformed, 'transforms it')
console.log(require('util').inspect(res.data))
t.equal(res.data, anchors + md, 'generates correct anchors')
})
}

check(
[ '# My Module'
, 'Some text here'
, '## API'
, '### Method One'
, 'works like this'
, '### Method Two'
, '#### Main Usage'
, 'some main usage here'
].join('\n')
, [ '**Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)*\n\n'
, '- [My Module](#my-module)\n'
, '\t- [API](#api)\n'
, '\t\t- [Method One](#method-one)\n'
, '\t\t- [Method Two](#method-two)\n'
, '\t\t\t- [Main Usage](#main-usage)\n\n'
].join('')
)

check(
[ 'My Module'
, '========='
, 'Some text here'
, 'API'
, '---------'
].join('\n')
, [ '**Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)*\n\n'
, '- [My Module](#my-module)\n'
, '\t- [API](#api)\n\n'
].join('')
)

check(
[ '# My Module #'
, 'Some text here'
, '## API ##'
].join('\n')
, [ '**Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)*\n\n'
, '- [My Module](#my-module)\n'
, '\t- [API](#api)\n\n'
].join('')
)

0 comments on commit c7e42da

Please sign in to comment.