2015-08-06 18:44:28 +00:00
|
|
|
/*****************************************************************************
|
2022-01-18 19:27:28 +00:00
|
|
|
* Open MCT, Copyright (c) 2014-2022, United States Government
|
2015-08-06 18:44:28 +00:00
|
|
|
* as represented by the Administrator of the National Aeronautics and Space
|
|
|
|
* Administration. All rights reserved.
|
|
|
|
*
|
2016-07-12 23:21:58 +00:00
|
|
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
2015-08-06 18:44:28 +00:00
|
|
|
* "License"); you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
* under the License.
|
|
|
|
*
|
2016-07-12 23:21:58 +00:00
|
|
|
* Open MCT includes source code licensed under additional open source
|
2015-08-06 18:44:28 +00:00
|
|
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
|
|
* this source code distribution or the Licensing information page available
|
|
|
|
* at runtime from the About dialog for additional information.
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2015-10-29 19:30:13 +00:00
|
|
|
/*global require,process,__dirname,GLOBAL*/
|
2015-08-06 18:57:57 +00:00
|
|
|
/*jslint nomen: false */
|
2015-07-30 22:32:17 +00:00
|
|
|
|
|
|
|
|
2015-08-06 18:44:28 +00:00
|
|
|
// Usage:
|
|
|
|
// node gendocs.js --in <source directory> --out <dest directory>
|
|
|
|
|
2015-07-30 22:32:17 +00:00
|
|
|
var CONSTANTS = {
|
|
|
|
DIAGRAM_WIDTH: 800,
|
|
|
|
DIAGRAM_HEIGHT: 500
|
2015-10-20 22:52:49 +00:00
|
|
|
},
|
|
|
|
TOC_HEAD = "# Table of Contents";
|
2015-07-30 22:32:17 +00:00
|
|
|
|
2015-07-30 23:16:33 +00:00
|
|
|
GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be defined
|
2015-07-30 22:32:17 +00:00
|
|
|
(function () {
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
var fs = require("fs"),
|
|
|
|
mkdirp = require("mkdirp"),
|
|
|
|
path = require("path"),
|
|
|
|
glob = require("glob"),
|
2015-08-04 20:20:19 +00:00
|
|
|
marked = require("marked"),
|
2015-07-30 22:32:17 +00:00
|
|
|
split = require("split"),
|
|
|
|
stream = require("stream"),
|
|
|
|
nomnoml = require('nomnoml'),
|
2015-10-20 22:52:49 +00:00
|
|
|
toc = require("markdown-toc"),
|
2015-07-30 22:32:17 +00:00
|
|
|
Canvas = require('canvas'),
|
2015-10-29 19:30:13 +00:00
|
|
|
header = fs.readFileSync(path.resolve(__dirname, 'header.html')),
|
|
|
|
footer = fs.readFileSync(path.resolve(__dirname, 'footer.html')),
|
2015-07-30 22:32:17 +00:00
|
|
|
options = require("minimist")(process.argv.slice(2));
|
|
|
|
|
2015-08-06 18:44:28 +00:00
|
|
|
// Convert from nomnoml source to a target PNG file.
|
2015-07-30 22:32:17 +00:00
|
|
|
function renderNomnoml(source, target) {
|
|
|
|
var canvas =
|
|
|
|
new Canvas(CONSTANTS.DIAGRAM_WIDTH, CONSTANTS.DIAGRAM_HEIGHT);
|
|
|
|
nomnoml.draw(canvas, source, 1.0);
|
|
|
|
canvas.pngStream().pipe(fs.createWriteStream(target));
|
|
|
|
}
|
|
|
|
|
2015-08-06 18:44:28 +00:00
|
|
|
// Stream transform.
|
|
|
|
// Pulls out nomnoml diagrams from fenced code blocks and renders them
|
|
|
|
// as PNG files in the output directory, prefixed with a provided name.
|
|
|
|
// The fenced code blocks will be replaced with Markdown in the
|
|
|
|
// output of this stream.
|
2015-07-30 22:32:17 +00:00
|
|
|
function nomnomlifier(outputDirectory, prefix) {
|
|
|
|
var transform = new stream.Transform({ objectMode: true }),
|
|
|
|
isBuilding = false,
|
|
|
|
counter = 1,
|
|
|
|
outputPath,
|
|
|
|
source = "";
|
|
|
|
|
|
|
|
transform._transform = function (chunk, encoding, done) {
|
2015-07-30 23:16:33 +00:00
|
|
|
if (!isBuilding) {
|
2015-07-30 22:32:17 +00:00
|
|
|
if (chunk.trim().indexOf("```nomnoml") === 0) {
|
|
|
|
var outputFilename = prefix + '-' + counter + '.png';
|
|
|
|
outputPath = path.join(outputDirectory, outputFilename);
|
|
|
|
this.push([
|
|
|
|
"\n![Diagram ",
|
|
|
|
counter,
|
|
|
|
"](",
|
|
|
|
outputFilename,
|
|
|
|
")\n\n"
|
|
|
|
].join(""));
|
|
|
|
isBuilding = true;
|
|
|
|
source = "";
|
2015-07-30 23:16:33 +00:00
|
|
|
counter += 1;
|
2015-07-30 22:32:17 +00:00
|
|
|
} else {
|
|
|
|
// Otherwise, pass through
|
2015-07-30 23:16:33 +00:00
|
|
|
this.push(chunk + '\n');
|
2015-07-30 22:32:17 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (chunk.trim() === "```") {
|
|
|
|
// End nomnoml
|
|
|
|
renderNomnoml(source, outputPath);
|
|
|
|
isBuilding = false;
|
|
|
|
} else {
|
2015-07-30 23:16:33 +00:00
|
|
|
source += chunk + '\n';
|
2015-07-30 22:32:17 +00:00
|
|
|
}
|
|
|
|
}
|
2015-07-30 23:16:33 +00:00
|
|
|
done();
|
2015-07-30 22:32:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
return transform;
|
|
|
|
}
|
|
|
|
|
2015-08-06 18:44:28 +00:00
|
|
|
// Convert from Github-flavored Markdown to HTML
|
2015-12-11 17:51:58 +00:00
|
|
|
function gfmifier(renderTOC) {
|
2015-07-30 22:32:17 +00:00
|
|
|
var transform = new stream.Transform({ objectMode: true }),
|
|
|
|
markdown = "";
|
|
|
|
transform._transform = function (chunk, encoding, done) {
|
|
|
|
markdown += chunk;
|
2015-07-30 23:16:33 +00:00
|
|
|
done();
|
2015-07-30 22:32:17 +00:00
|
|
|
};
|
2015-07-30 23:16:33 +00:00
|
|
|
transform._flush = function (done) {
|
2015-12-11 17:51:58 +00:00
|
|
|
if (renderTOC){
|
|
|
|
// Prepend table of contents
|
|
|
|
markdown =
|
|
|
|
[ TOC_HEAD, toc(markdown).content, "", markdown ].join("\n");
|
|
|
|
}
|
2015-10-29 19:30:13 +00:00
|
|
|
this.push(header);
|
2015-08-04 20:20:19 +00:00
|
|
|
this.push(marked(markdown));
|
2015-10-29 19:30:13 +00:00
|
|
|
this.push(footer);
|
2015-07-30 23:16:33 +00:00
|
|
|
done();
|
2015-07-30 22:32:17 +00:00
|
|
|
};
|
|
|
|
return transform;
|
|
|
|
}
|
|
|
|
|
2015-08-06 18:44:28 +00:00
|
|
|
// Custom renderer for marked; converts relative links from md to html,
|
|
|
|
// and makes headings linkable.
|
2015-08-04 20:37:45 +00:00
|
|
|
function CustomRenderer() {
|
|
|
|
var renderer = new marked.Renderer(),
|
|
|
|
customRenderer = Object.create(renderer);
|
|
|
|
customRenderer.heading = function (text, level) {
|
|
|
|
var escapedText = (text || "").trim().toLowerCase().replace(/\W/g, "-"),
|
|
|
|
aOpen = "<a name=\"" + escapedText + "\" href=\"#" + escapedText + "\">",
|
|
|
|
aClose = "</a>";
|
|
|
|
return aOpen + renderer.heading.apply(renderer, arguments) + aClose;
|
|
|
|
};
|
|
|
|
// Change links to .md files to .html
|
|
|
|
customRenderer.link = function (href, title, text) {
|
|
|
|
// ...but only if they look like relative paths
|
|
|
|
return (href || "").indexOf(":") === -1 && href[0] !== "/" ?
|
2015-10-20 22:52:49 +00:00
|
|
|
renderer.link(href.replace(/\.md/, ".html"), title, text) :
|
|
|
|
renderer.link.apply(renderer, arguments);
|
2015-08-04 20:37:45 +00:00
|
|
|
};
|
|
|
|
return customRenderer;
|
|
|
|
}
|
|
|
|
|
2015-07-30 22:32:17 +00:00
|
|
|
options['in'] = options['in'] || options.i;
|
|
|
|
options.out = options.out || options.o;
|
|
|
|
|
2015-08-04 20:20:19 +00:00
|
|
|
marked.setOptions({
|
2015-08-04 20:37:45 +00:00
|
|
|
renderer: new CustomRenderer(),
|
2015-08-04 20:20:19 +00:00
|
|
|
gfm: true,
|
|
|
|
tables: true,
|
|
|
|
breaks: false,
|
|
|
|
pedantic: false,
|
|
|
|
sanitize: true,
|
|
|
|
smartLists: true,
|
|
|
|
smartypants: false
|
|
|
|
});
|
|
|
|
|
2015-08-06 18:44:28 +00:00
|
|
|
// Convert all markdown files.
|
|
|
|
// First, pull out nomnoml diagrams.
|
|
|
|
// Then, convert remaining Markdown to HTML.
|
2015-07-30 22:32:17 +00:00
|
|
|
glob(options['in'] + "/**/*.md", {}, function (err, files) {
|
|
|
|
files.forEach(function (file) {
|
|
|
|
var destination = file.replace(options['in'], options.out)
|
|
|
|
.replace(/md$/, "html"),
|
|
|
|
destPath = path.dirname(destination),
|
2015-12-11 17:51:58 +00:00
|
|
|
prefix = path.basename(destination).replace(/\.html$/, ""),
|
2015-12-11 17:53:55 +00:00
|
|
|
//Determine whether TOC should be rendered for this file based
|
|
|
|
//on regex provided as command line option
|
2015-12-11 17:51:58 +00:00
|
|
|
renderTOC = file.match(options['suppress-toc'] || "") === null;
|
2015-07-30 22:32:17 +00:00
|
|
|
|
|
|
|
mkdirp(destPath, function (err) {
|
|
|
|
fs.createReadStream(file, { encoding: 'utf8' })
|
|
|
|
.pipe(split())
|
|
|
|
.pipe(nomnomlifier(destPath, prefix))
|
2015-12-11 17:51:58 +00:00
|
|
|
.pipe(gfmifier(renderTOC))
|
2015-07-30 22:32:17 +00:00
|
|
|
.pipe(fs.createWriteStream(destination, {
|
|
|
|
encoding: 'utf8'
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2015-08-03 19:49:35 +00:00
|
|
|
// Also copy over all HTML, CSS, or PNG files
|
|
|
|
glob(options['in'] + "/**/*.@(html|css|png)", {}, function (err, files) {
|
2015-07-30 23:36:17 +00:00
|
|
|
files.forEach(function (file) {
|
|
|
|
var destination = file.replace(options['in'], options.out),
|
2015-10-15 00:11:08 +00:00
|
|
|
destPath = path.dirname(destination),
|
|
|
|
streamOptions = {};
|
2015-10-29 19:30:13 +00:00
|
|
|
if (file.match(/png$/)) {
|
2015-10-28 18:09:52 +00:00
|
|
|
streamOptions.encoding = null;
|
2015-10-15 00:11:08 +00:00
|
|
|
} else {
|
|
|
|
streamOptions.encoding = 'utf8';
|
|
|
|
}
|
2015-10-29 19:30:13 +00:00
|
|
|
|
2015-07-30 23:36:17 +00:00
|
|
|
mkdirp(destPath, function (err) {
|
2015-10-15 00:11:08 +00:00
|
|
|
fs.createReadStream(file, streamOptions)
|
|
|
|
.pipe(fs.createWriteStream(destination, streamOptions));
|
2015-07-30 23:36:17 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2015-07-30 22:32:17 +00:00
|
|
|
}());
|