1 | #!/usr/bin/env node
|
2 |
|
3 | 'use strict';
|
4 |
|
5 | var path = require('path')
|
6 | , fs = require('fs')
|
7 | , minimist = require('minimist')
|
8 | , file = require('./lib/file')
|
9 | , transform = require('./lib/transform')
|
10 | , files;
|
11 |
|
12 | function cleanPath(path) {
|
13 | var homeExpanded = (path.indexOf('~') === 0) ? process.env.HOME + path.substr(1) : path;
|
14 |
|
15 |
|
16 | return homeExpanded.replace(/\s/g, '\\ ');
|
17 | }
|
18 |
|
19 | function transformAndSave(files, mode, maxHeaderLevel, title, notitle, entryPrefix, processAll, stdOut, updateOnly) {
|
20 | if (processAll) {
|
21 | console.log('--all flag is enabled. Including headers before the TOC location.')
|
22 | }
|
23 |
|
24 | if (updateOnly) {
|
25 | console.log('--update-only flag is enabled. Only updating files that already have a TOC.')
|
26 | }
|
27 |
|
28 | console.log('\n==================\n');
|
29 |
|
30 | var transformed = files
|
31 | .map(function (x) {
|
32 | var content = fs.readFileSync(x.path, 'utf8')
|
33 | , result = transform(content, mode, maxHeaderLevel, title, notitle, entryPrefix, processAll, updateOnly);
|
34 | result.path = x.path;
|
35 | return result;
|
36 | });
|
37 | var changed = transformed.filter(function (x) { return x.transformed; })
|
38 | , unchanged = transformed.filter(function (x) { return !x.transformed; })
|
39 | , toc = transformed.filter(function (x) { return x.toc; })
|
40 |
|
41 | if (stdOut) {
|
42 | toc.forEach(function (x) {
|
43 | console.log(x.toc)
|
44 | })
|
45 | }
|
46 |
|
47 | unchanged.forEach(function (x) {
|
48 | console.log('"%s" is up to date', x.path);
|
49 | });
|
50 |
|
51 | changed.forEach(function (x) {
|
52 | if (stdOut) {
|
53 | console.log('==================\n\n"%s" should be updated', x.path)
|
54 | } else {
|
55 | console.log('"%s" will be updated', x.path);
|
56 | fs.writeFileSync(x.path, x.data, 'utf8');
|
57 | }
|
58 | });
|
59 | }
|
60 |
|
61 | function printUsageAndExit(isErr) {
|
62 |
|
63 | var outputFunc = isErr ? console.error : console.info;
|
64 |
|
65 | outputFunc('Usage: doctoc [mode] [--entryprefix prefix] [--notitle | --title title] [--maxlevel level] [--all] [--update-only] <path> (where path is some path to a directory (e.g., .) or a file (e.g., README.md))');
|
66 | outputFunc('\nAvailable modes are:');
|
67 | for (var key in modes) {
|
68 | outputFunc(' --%s\t%s', key, modes[key]);
|
69 | }
|
70 | outputFunc('Defaults to \'' + mode + '\'.');
|
71 |
|
72 | process.exit(isErr ? 2 : 0);
|
73 | }
|
74 |
|
75 | var modes = {
|
76 | bitbucket : 'bitbucket.org'
|
77 | , nodejs : 'nodejs.org'
|
78 | , github : 'github.com'
|
79 | , gitlab : 'gitlab.com'
|
80 | , ghost : 'ghost.org'
|
81 | }
|
82 |
|
83 | var mode = modes['github'];
|
84 |
|
85 | var argv = minimist(process.argv.slice(2)
|
86 | , { boolean: [ 'h', 'help', 'T', 'notitle', 's', 'stdout', 'all' , 'u', 'update-only'].concat(Object.keys(modes))
|
87 | , string: [ 'title', 't', 'maxlevel', 'm', 'entryprefix' ]
|
88 | , unknown: function(a) { return (a[0] == '-' ? (console.error('Unknown option(s): ' + a), printUsageAndExit(true)) : true); }
|
89 | });
|
90 |
|
91 | if (argv.h || argv.help) {
|
92 | printUsageAndExit();
|
93 | }
|
94 |
|
95 | for (var key in modes) {
|
96 | if (argv[key]) {
|
97 | mode = modes[key];
|
98 | }
|
99 | }
|
100 |
|
101 | var title = argv.t || argv.title;
|
102 | var notitle = argv.T || argv.notitle;
|
103 | var entryPrefix = argv.entryprefix || '-';
|
104 | var processAll = argv.all;
|
105 | var stdOut = argv.s || argv.stdout
|
106 | var updateOnly = argv.u || argv['update-only']
|
107 |
|
108 | var maxHeaderLevel = argv.m || argv.maxlevel;
|
109 | if (maxHeaderLevel && isNaN(maxHeaderLevel) || maxHeaderLevel < 0) { console.error('Max. heading level specified is not a positive number: ' + maxHeaderLevel), printUsageAndExit(true); }
|
110 |
|
111 | for (var i = 0; i < argv._.length; i++) {
|
112 | var target = cleanPath(argv._[i])
|
113 | , stat = fs.statSync(target)
|
114 |
|
115 | if (stat.isDirectory()) {
|
116 | console.log ('\nDocToccing "%s" and its sub directories for %s.', target, mode);
|
117 | files = file.findMarkdownFiles(target);
|
118 | } else {
|
119 | console.log ('\nDocToccing single file "%s" for %s.', target, mode);
|
120 | files = [{ path: target }];
|
121 | }
|
122 |
|
123 | transformAndSave(files, mode, maxHeaderLevel, title, notitle, entryPrefix, processAll, stdOut, updateOnly);
|
124 |
|
125 | console.log('\nEverything is OK.');
|
126 | }
|
127 |
|
128 | module.exports.transform = transform;
|