/***************************************************************************** * Open MCT Web, Copyright (c) 2014-2015, United States Government * as represented by the Administrator of the National Aeronautics and Space * Administration. All rights reserved. * * Open MCT Web is licensed under the Apache License, Version 2.0 (the * "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. * * Open MCT Web includes source code licensed under additional open source * 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. *****************************************************************************/ /*global require,process,__dirname,GLOBAL*/ /*jslint nomen: false */ // Usage: // node gendocs.js --in --out var CONSTANTS = { DIAGRAM_WIDTH: 800, DIAGRAM_HEIGHT: 500 }, TOC_HEAD = "# Table of Contents"; GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be defined (function () { "use strict"; var fs = require("fs"), mkdirp = require("mkdirp"), path = require("path"), glob = require("glob"), marked = require("marked"), split = require("split"), stream = require("stream"), nomnoml = require('nomnoml'), toc = require("markdown-toc"), Canvas = require('canvas'), header = fs.readFileSync(path.resolve(__dirname, 'header.html')), footer = fs.readFileSync(path.resolve(__dirname, 'footer.html')), options = require("minimist")(process.argv.slice(2)); // Convert from nomnoml source to a target PNG file. 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)); } // 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. function nomnomlifier(outputDirectory, prefix) { var transform = new stream.Transform({ objectMode: true }), isBuilding = false, counter = 1, outputPath, source = ""; transform._transform = function (chunk, encoding, done) { if (!isBuilding) { 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 = ""; counter += 1; } else { // Otherwise, pass through this.push(chunk + '\n'); } } else { if (chunk.trim() === "```") { // End nomnoml renderNomnoml(source, outputPath); isBuilding = false; } else { source += chunk + '\n'; } } done(); }; return transform; } // Convert from Github-flavored Markdown to HTML function gfmifier() { var transform = new stream.Transform({ objectMode: true }), markdown = ""; transform._transform = function (chunk, encoding, done) { markdown += chunk; done(); }; transform._flush = function (done) { // Prepend table of contents markdown = [ TOC_HEAD, toc(markdown).content, "", markdown ].join("\n"); this.push(header); this.push(marked(markdown)); this.push(footer); done(); }; return transform; } // Custom renderer for marked; converts relative links from md to html, // and makes headings linkable. 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 = "", aClose = ""; 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] !== "/" ? renderer.link(href.replace(/\.md/, ".html"), title, text) : renderer.link.apply(renderer, arguments); }; return customRenderer; } options['in'] = options['in'] || options.i; options.out = options.out || options.o; marked.setOptions({ renderer: new CustomRenderer(), gfm: true, tables: true, breaks: false, pedantic: false, sanitize: true, smartLists: true, smartypants: false }); // Convert all markdown files. // First, pull out nomnoml diagrams. // Then, convert remaining Markdown to HTML. 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), prefix = path.basename(destination).replace(/\.html$/, ""); mkdirp(destPath, function (err) { fs.createReadStream(file, { encoding: 'utf8' }) .pipe(split()) .pipe(nomnomlifier(destPath, prefix)) .pipe(gfmifier()) .pipe(fs.createWriteStream(destination, { encoding: 'utf8' })); }); }); }); // Also copy over all HTML, CSS, or PNG files glob(options['in'] + "/**/*.@(html|css|png)", {}, function (err, files) { files.forEach(function (file) { var destination = file.replace(options['in'], options.out), destPath = path.dirname(destination), streamOptions = {}; if (file.match(/png$/)) { streamOptions.encoding = null; } else { streamOptions.encoding = 'utf8'; } mkdirp(destPath, function (err) { fs.createReadStream(file, streamOptions) .pipe(fs.createWriteStream(destination, streamOptions)); }); }); }); }());