forked from meteor/meteor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
doctool.js
executable file
·154 lines (141 loc) · 4.9 KB
/
doctool.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#!/usr/bin/env node
/// # doctool.js
///
/// Usage: `doctool.js ...jsfiles...`
///
/// Reads each `.js` file and writes a `.md` file in the same directory.
/// The output file consists of the concatenation of the "doc comments"
/// in the input file, which are assumed to contain Markdown content,
/// including any section headings necessary to organize the file.
///
/// A "doc comment" must begin at the start of a line or after
/// whitespace. There are two kinds of doc comments: `/** ... */`
/// (block) comments and `/// ...` (triple-slash) comments.
///
/// If a file begins with the magic string "///!README", the output
/// filename is changed to `README.md`.
///
/// Examples:
///
/// ```
/// /**
/// * This is a block comment. The parser strips the sequence,
/// * [optional whitespace, `*`, optional single space] from
/// * every line that has it.
/// *
/// For lines that don't, no big deal.
///
/// Leading whitspace will be preserved here.
///
/// * We can create a bullet list in here:
/// *
/// * * This is a bullet
/// */
/// ```
///
/// ```
/// /** Single-line block comments are also ok. */
/// ```
///
/// ```
/// /**
/// A block comment whose first line doesn't have a `*` receives
/// no stripping of `*` characters on any line.
///
/// * This is a bullet
///
/// */
/// ```
///
/// ```
/// /// A triple-slash comment starts with `///` followed by an
/// /// optional space (i.e. one space is removed if present).
/// /// Multiple consecutive lines that start with `///` are
/// /// treated together as a single doc comment.
/// /** Separate doc comments get separate paragraphs. */
/// ```
var fs = require('fs');
var path = require('path');
process.argv.slice(2).forEach(function (fileName) {
var text = fs.readFileSync(fileName, "utf8");
var outFileName = fileName.replace(/\.js$/, '') + '.md';
if (text.slice(0, 10) === '///!README') {
outFileName = path.join(path.dirname(fileName), 'README.md');
text = text.slice(10);
}
var docComments = [];
for (;;) {
// This regex breaks down as follows:
//
// 1. Start of line
// 2. Optional whitespace (not newline!)
// 3. `///` (capturing group 1) or `/**` (group 2)
// 4. Looking ahead, NOT `/` or `*`
var nextOpener = /^[ \t]*(?:(\/\/\/)|(\/\*\*))(?![\/\*])/m.exec(text);
if (! nextOpener)
break;
text = text.slice(nextOpener.index + nextOpener[0].length);
if (nextOpener[1]) {
// triple-slash
text = text.replace(/^[ \t]/, ''); // optional space
var comment = text.match(/^[^\n]*/)[0];
text = text.slice(comment.length);
var match;
while ((match = /^\n[ \t]*\/\/\/[ \t]?/.exec(text))) {
// multiple lines in a row become one comment
text = text.slice(match[0].length);
var restOfLine = text.match(/^[^\n]*/)[0];
text = text.slice(restOfLine.length);
comment += '\n' + restOfLine;
}
if (comment.trim())
docComments.push(['///', comment]);
} else if (nextOpener[2]) {
// block comment
var rawComment = text.match(/^[\s\S]*?\*\//);
if ((! rawComment) || (! rawComment[0]))
continue;
rawComment = rawComment[0];
text = text.slice(rawComment.length);
rawComment = rawComment.slice(0, -2); // remove final `*/`
if (rawComment.slice(-1) === ' ')
// make that ' */' for the benefit of single-line blocks
rawComment = rawComment.slice(0, -1);
var lines = rawComment.split('\n');
var stripStars = false;
if (lines[0].trim().length === 0) {
// The comment has a newline after the `/**` (with possible whitespace
// between). This is like most comments, though occasionally people
// may write `/** foo */` on one line. Skip the blank line.
lines.splice(0, 1);
if (! lines.length)
continue;
// Now we determine whether this is block comment with a column of
// asterisks running down the left side, so we can strip them.
stripStars = /^[ \t]*\*/.test(lines[1]);
} else {
// Trim beginning of line after `/**`
lines[0] = lines[0].replace(/^\s*/, '');
}
lines = lines.map(function (s) {
// Strip either up to an asterisk and then an optional space,
// or just an optional space, depending on `stripStars`.
if (stripStars)
return s.replace(/^[ \t]*\* ?/, '');
else
return s;
});
var result = lines.join('\n');
if (result.trim())
docComments.push(['/**', result]);
}
}
if (docComments.length) {
var output = docComments.map(function (x) { return x[1]; }).join('\n\n');
var fileShortName = path.basename(fileName);
output = '*This file is automatically generated from [`' +
fileShortName + '`](' + fileShortName + ').*\n\n' + output;
fs.writeFileSync(outFileName, output, 'utf8');
console.log("Wrote " + docComments.length + " comments to " + outFileName);
}
});