mirror of
https://github.com/meteor/meteor.git
synced 2026-01-08 15:24:00 -05:00
155 lines
4.9 KiB
JavaScript
Executable File
155 lines
4.9 KiB
JavaScript
Executable File
#!/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 whitespace 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);
|
|
}
|
|
|
|
|
|
});
|