doctoc
Advanced tools
Comparing version 0.1.0 to 0.2.0
154
doctoc.js
#!/usr/bin/env node | ||
var path = require('path'), | ||
fs = require('fs'), | ||
_ = require('underscore'), | ||
file = require('./lib/file'), | ||
argv = process.argv, | ||
file; | ||
'use strict'; | ||
var path = require('path') | ||
, fs = require('fs') | ||
, file = require('./lib/file') | ||
, transform = require('./lib/transform') | ||
, argv = process.argv | ||
, files; | ||
function cleanPath(path) { | ||
var homeExpanded = (path.indexOf('~') === 0) ? process.env.HOME + path.substr(1) : path; | ||
var homeExpanded = (path.indexOf('~') === 0) ? process.env.HOME + path.substr(1) : path; | ||
// Escape all spaces | ||
return homeExpanded.replace(/\s/g, '\\ '); | ||
// Escape all spaces | ||
return homeExpanded.replace(/\s/g, '\\ '); | ||
} | ||
function notNull(x) { return x !== null; } | ||
function addLink(header) { | ||
return _(header).extend({ | ||
link: '#' + header.name.trim().toLowerCase() | ||
.replace(/ /g,'-') | ||
.replace(/[`.,()*]/g,'') | ||
function transformAndSave(files) { | ||
console.log('\n==================\n'); | ||
var transformed = files | ||
.map(function (x) { | ||
var content = fs.readFileSync(x.path, 'utf8') | ||
, result = transform(content); | ||
result.path = x.path; | ||
return result; | ||
}); | ||
} | ||
var changed = transformed.filter(function (x) { return x.transformed; }) | ||
, unchanged = transformed.filter(function (x) { return !x.transformed; }); | ||
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(); | ||
} | ||
unchanged.forEach(function (x) { | ||
console.log('"%s" is up to date', x.path); | ||
}); | ||
function getUnderlinedHeaders (_lines) { | ||
// Find headers of the form | ||
// h1 h2 | ||
// == -- | ||
return _lines | ||
.map(function (line, index, lines) { | ||
if (index === 0) return null; | ||
var rank; | ||
if (/^==+/.exec(line)) rank = 1; | ||
else if (/^--+/.exec(line)) rank = 2; | ||
else return null; | ||
return { | ||
rank : rank, | ||
name : lines[index - 1], | ||
line : index - 1 | ||
}; | ||
}) | ||
.filter(notNull) | ||
.value(); | ||
changed.forEach(function (x) { | ||
console.log('"%s" will be updated', x.path); | ||
fs.writeFileSync(x.path, x.data, 'utf8'); | ||
}); | ||
} | ||
function transform (f, 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); | ||
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') + | ||
'\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); | ||
// Skip all lines up to first header since that is the old table of content | ||
var remainingContent = _lines | ||
.rest(linkedHeaders[0].line) | ||
.value() | ||
.join('\n'); | ||
var data = toc + '\n' + remainingContent; | ||
return { | ||
transformed : true, | ||
data : data, | ||
path : f.path | ||
}; | ||
} | ||
function transformAndSave(files) { | ||
console.log('\n==================\n'); | ||
_(files) | ||
.chain() | ||
.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'); | ||
}); | ||
} | ||
if (argv.length !== 3) { | ||
console.log('Usage: doctoc <path> (where path is some path to a directory (i.e. .) or a file (i.e. README.md) )'); | ||
process.exit(0); | ||
console.log('Usage: doctoc <path> (where path is some path to a directory (i.e. .) or a file (i.e. README.md) )'); | ||
process.exit(0); | ||
} | ||
var target = cleanPath(argv[2]), | ||
stat = fs.statSync(target); | ||
stat = fs.statSync(target); | ||
@@ -140,0 +50,0 @@ if (stat.isDirectory()) { |
@@ -1,4 +0,4 @@ | ||
var path = require('path'), | ||
fs = require('fs'), | ||
_ = require('underscore'); | ||
var path = require('path') | ||
, fs = require('fs') | ||
, _ = require('underscore'); | ||
@@ -9,44 +9,44 @@ var markdownExts = ['.md', '.markdown']; | ||
function separateFilesAndDirs(fileInfos) { | ||
return { | ||
directories : _(fileInfos).filter(function (x) { | ||
return x.isDirectory() && !_(ignoredDirs).include(x.name); | ||
}), | ||
markdownFiles : _(fileInfos).filter(function (x) { | ||
return x.isFile() && _(markdownExts).include(path.extname(x.name)); | ||
}) | ||
}; | ||
return { | ||
directories : _(fileInfos).filter(function (x) { | ||
return x.isDirectory() && !_(ignoredDirs).include(x.name); | ||
}), | ||
markdownFiles : _(fileInfos).filter(function (x) { | ||
return x.isFile() && _(markdownExts).include(path.extname(x.name)); | ||
}) | ||
}; | ||
} | ||
function findRec(currentPath) { | ||
function getStat (entry) { | ||
var target = path.join(currentPath, entry), | ||
stat = fs.statSync(target); | ||
function getStat (entry) { | ||
var target = path.join(currentPath, entry), | ||
stat = fs.statSync(target); | ||
return _(stat).extend({ | ||
name: entry, | ||
path: target | ||
}); | ||
} | ||
function process (fileInfos) { | ||
var res = separateFilesAndDirs(fileInfos); | ||
var tgts = _(res.directories).pluck('path'); | ||
return _(stat).extend({ | ||
name: entry, | ||
path: target | ||
}); | ||
} | ||
function process (fileInfos) { | ||
var res = separateFilesAndDirs(fileInfos); | ||
var tgts = _(res.directories).pluck('path'); | ||
if (res.markdownFiles.length > 0) | ||
console.log('\nFound %s in "%s"', _(res.markdownFiles).pluck('name').join(', '), currentPath); | ||
else | ||
console.log('\nFound nothing in "%s"', currentPath); | ||
if (res.markdownFiles.length > 0) | ||
console.log('\nFound %s in "%s"', _(res.markdownFiles).pluck('name').join(', '), currentPath); | ||
else | ||
console.log('\nFound nothing in "%s"', currentPath); | ||
return { | ||
markdownFiles : res.markdownFiles, | ||
subdirs : tgts | ||
}; | ||
} | ||
return { | ||
markdownFiles : res.markdownFiles, | ||
subdirs : tgts | ||
}; | ||
} | ||
var stats = _(fs.readdirSync(currentPath)).map(getStat), | ||
res = process(stats), | ||
markdownsInSubdirs = _(res.subdirs).map(findRec), | ||
allMarkdownsHereAndSub = res.markdownFiles.concat(markdownsInSubdirs); | ||
var stats = _(fs.readdirSync(currentPath)).map(getStat) | ||
, res = process(stats) | ||
, markdownsInSubdirs = _(res.subdirs).map(findRec) | ||
, allMarkdownsHereAndSub = res.markdownFiles.concat(markdownsInSubdirs); | ||
return _(allMarkdownsHereAndSub).flatten(); | ||
return _(allMarkdownsHereAndSub).flatten(); | ||
} | ||
@@ -57,3 +57,3 @@ | ||
exports.findMarkdownFiles = function(dir) { | ||
return findRec(dir); | ||
return findRec(dir); | ||
}; | ||
@@ -60,0 +60,0 @@ |
@@ -5,3 +5,3 @@ { | ||
"description": "Generates TOC for markdown files of local git repo.", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"repository": { | ||
@@ -11,2 +11,5 @@ "type": "git", | ||
}, | ||
"scripts": { | ||
"test": "node-trap test/{**/*.js,*.js}" | ||
}, | ||
"main": "doctoc.js", | ||
@@ -18,6 +21,9 @@ "bin": "doctoc.js", | ||
"dependencies": { | ||
"underscore": ">=1.3.3" | ||
"underscore": ">=1.3.3", | ||
"anchor-markdown-header": "~0.1.1" | ||
}, | ||
"devDependencies": {}, | ||
"license": "MIT/X11", | ||
"devDependencies": { | ||
"trap": "~0.3.2" | ||
}, | ||
"license": "MIT", | ||
"keywords": [ | ||
@@ -24,0 +30,0 @@ "github", |
@@ -0,11 +1,14 @@ | ||
# DocToc [![build status](https://secure.travis-ci.org/thlorenz/doctoc.png)](http://travis-ci.org/thlorenz/doctoc) | ||
Generates table of contents for markdown files inside local git repository. Links are compatible with anchors generated by github | ||
**Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)* | ||
- [DocToc](#doctoc) | ||
- [Installation](#installation) | ||
- [Usage](#usage) | ||
- [Installation](#installation) | ||
- [Usage](#usage) | ||
- [Adding toc to all files in a directory and sub directories](#adding-toc-to-all-files-in-a-directory-and-sub-directories) | ||
- [Adding toc to a single file](#adding-toc-to-a-single-file) | ||
- [Example](#example) | ||
# DocToc | ||
Generates table of contents for markdown files inside local git repository. Links are compatible with anchors generated by github | ||
## Installation | ||
@@ -17,2 +20,4 @@ | ||
### Adding toc to all files in a directory and sub directories | ||
Go into the directory that contains you local git project and type: | ||
@@ -26,2 +31,4 @@ | ||
### Adding toc to a single file | ||
If you want to convert only a specific file, do: | ||
@@ -31,4 +38,4 @@ | ||
Example: | ||
#### Example | ||
doctoc README.md |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
10547
10
229
39
2
1
1
+ Addedanchor-markdown-header@0.1.2(transitive)