dokker
Version:
Dokker.js creates professional Javascript code documentations.
505 lines (489 loc) • 15.9 kB
JavaScript
// Dokker.js 0.0.1
// http://dokker.oceanhouse21.com
// (c) 2014-2015 Georg Schlenkhoff and Oceanhouse21
// Dokker.js may be freely distributed under the MIT license.
;
// Define required dependencies
var Promise = require('promise');
var fs = require('fs');
var path = require('path');
var read = Promise.denodeify(fs.readFile);
var write = Promise.denodeify(fs.writeFile);
var docco = require('docco');
var docdown = require('docdown');
var marked = require('marked');
var ejs = require('ejs');
var cheerio = require('cheerio');
var exec = require('child_process').exec;
var Dokker = {};
/**
* The semantic version number.
*
* @static
* @memberOf Dokker
* @type string
*/
Dokker.VERSION = '0.1.2';
/**
* Creates a HTML file that transcludes the HTML snippet that was created
* by the docdown module into an ejs template which is defined by the
* .dokker.json configuration file.
*
* @static
* @category JSDoc
* @memberOf Dokker
* @type Function
* @param {Object} [options] The options object.
* @param {String} [options.template='template/index.ejs.html'] The path to
* the ejs template file.
* @param {string} [options.html='docs/index.html'] The path to the docdown
* generated JSDoc documentation.
* @param {string} [options.readme='README.md'] The path to the README.md
* file.
* @param {string} [options.title=''] The title for the documentation.
* @returns {Promise} Returns a resolved promise if file was created.
* @example
*
* var options = {
* template: 'template/index.ejs.html',
* html: 'docs/index.html',
* readme: 'docs/README.md',
* title: 'Dokker.js API Documentation'
* };
* Dokker.injectTemplate(options)
* .then(function(){
* // => resolved promise
* });
*/
// Transclude in a ejs template file the HTML snippet that was ultimately
// created with docdown. Also tweak the HTML page a bit and finally
// inject the README.md.
Dokker.injectTemplate = function(options) {
return new Promise(function(resolve, reject) {
var template, body, html, readme, $;
return read(options.template, 'utf8')
.then(function(data) {
template = data;
return read(options.readme, 'utf8');
}).then(function(data) {
readme = marked(data);
$ = cheerio.load(readme);
$('h1').remove();
readme = $.html();
return read(options.html, 'utf8');
}).then(function(data) {
$ = cheerio.load(data);
return ejs.render(template, {
apiToc: $('.toc-container').html(),
readme: readme,
apiDoc: $('.doc-container').html(),
// correctly setting links to your github/pages
page_url: options.site,
title: options.title,
github_url: options.github,
// fixed reference to generated annotation
// the 'literate' in .dokker.json shall not require a source since it's never used.
annotated_path: 'annotated/'+options.source
});
}).then(function(data) {
return write(options.html, data, 'utf8');
}).then(function() {
resolve();
}).then(null, function(err) {
reject(err);
});
});
};
/**
* Creates an HTML file to describe the features of your module from your
* **Mocha** tests. The module is using the mocha **doc** reporter to
* generate the HTML contents.
*
* @static
* @memberOf Dokker
* @category Mocha
* @type Function
* @param {Object} [options] The options object.
* @param {String} [options.path='docs.html'] The path where to save the
* HTML file
* @returns {Promise} Returns a resolved promise if file was created.
* @example
*
* var options = {
* path: 'docs/tests.html',
* };
* Dokker.createTests(options)
* .then(function(){
* // => resolved promise
* });
*/
// Create a HTML site from any mocha tests find when executing mocha
// --reporter doc terminal command.
Dokker.createTests = function(options) {
return new Promise(function(resolve, reject) {
var template, tests;
// allows specification of mocha.command in .dokker.json, e.g. 'mocha -u tdd --reporter doc'
var cmd = options.command || 'mocha --reporter doc';
exec(cmd, {cwd: process.cwd()}, function(error, stdout) {
if(error) return reject(error);
tests = stdout;
return read(options.template, 'utf8')
.then(function(data) {
template = data;
return ejs.render(template, {tests: tests});
}).then(function(data) {
return write(options.path, data, 'utf8');
}).then(function() {
resolve();
}).then(null, function(err) {
reject(err);
});
});
});
};
/**
* Runs a terminal shell with the git command to extract the docs branch
* into a seperate gh-pages branch and finally pushes that branch to Github,
* so that the Github page is updated.
*
* @static
* @memberOf Dokker
* @category Github Pages
* @type Function
* @returns {Promise} Returns a resolved promise if file was created.
* @example
*
* Dokker.ghPages()
* .then(function(){
* // => resolved promise
* });
*/
// Extract docs folder to separate git branch and finally push branch to
// Github repository
Dokker.ghPages = function(){
return new Promise(function(resolve, reject) {
exec('git subtree split -P docs -b gh-pages', function(error, stdout, stderr) {
if(error) return reject(stderr);
exec('git push origin gh-pages', function(error, stdout, stderr) {
if(error) return reject(stderr);
resolve();
});
});
});
};
/**
* Create a styled HTML file from your source code with the help of **docco**
* module.
*
* @static
* @memberOf Dokker
* @category Literate programming
* @type Function
* @param {Object} [options] The options object.
* @param {String} [options.source='app.js'] The path the source code with
* comments
* @param {string} [options.dir='docs/annotated'] The directory where to save
* the generated HTML file.
* @returns {Promise} Returns a resolved promise if file was created.
* @example
*
* var options = {
* dir: 'docs',
* source: 'app.js'
* };
* Dokker.literate()
* .then(function(){
* // => resolved promise
* });
*/
// Create an HTML file from any source code comments in the style of
// [literate programming](https://de.wikipedia.org/wiki/Literate_programming)
Dokker.literate = function (options) {
return new Promise(function(resolve, reject) {
var tmpDir = path.join(process.cwd(), '/.tmp');
var tmpFile = path.join(tmpDir, options.source);
var sourceFile = (options.cwd) ?
path.join(process.cwd(), options.cwd, options.source) :
path.join(process.cwd(), options.source);
return read(sourceFile, 'utf8')
.then(function(data) {
var source = data.replace(/^\s*(\*|\/\*).*[\r\n]/gm, '');
return Dokker.mkdir(tmpDir)
.then(function() {
return write(tmpFile, source, 'utf8');
}).then(function(){
docco.run(['', '', tmpFile, '-o', options.dir]);
resolve();
});
}).then(null, function(err) {
if (err) return reject(err);
});
});
};
/**
* Create a Markdown file from JSDoc tags.
*
* @static
* @memberOf Dokker
* @category JSDoc
* @type Function
* @param {Object} [options] The options object.
* @param {String} [options.source='app.js'] The path the source code with
* JSDoc tags.
* @param {String} [options.github =''] The path the Github repository.
* @param {string} [options.markdown='docs/READMDE.md'] The path where to
* save the Markdown file.
* @returns {Promise} Returns a resolved promise if file was created.
* @example
*
* var options = {
* source: 'app.js',
* markdown: 'docs/README.md'
* };
* Dokker.jsdocMarkdown()
* .then(function(){
* // => resolved promise
* });
*/
// Convert markdown from any source code comments with docdown module.
Dokker.jsdocMarkdown = function (options) {
return new Promise(function(resolve, reject) {
var source, tmpFile, tmpDir;
var sourceFile = (options.cwd) ?
path.join(process.cwd(), options.cwd, options.source) :
path.join(process.cwd(), options.source);
return read(sourceFile, 'utf8')
.then(function(data){
source = data.replace(/^\s*(\/\/).*[\r\n]/gm, '');
tmpDir = path.join(process.cwd(), '/.tmp');
return Dokker.mkdir(tmpDir);
}).then(function(){
tmpFile = path.join(tmpDir, options.source);
return write(tmpFile, source, 'utf8');
}).then(function(){
var markdown = docdown({
'path': tmpFile,
'url': options.github,
'toc': 'categories'
});
return write(options.markdown, markdown, 'utf8');
}).then(function() {
resolve();
}).then(null, function(err) {
reject(err);
});
});
};
/**
* Create an HTML file from the Markdown file that was create with
* [Dokker.jsdocMarkdown()](#Dokker-jsdocMarkdown)
*
* @static
* @memberOf Dokker
* @category JSDoc
* @type Function
* @param {Object} [options] The options object.
* @param {string} [options.markdown='docs/READMDE.md'] The path where to
* save the Markdown file.
* @returns {Promise} Returns a resolved promise if file was created.
* @example
*
* var options = {
* markdown: 'docs/README.md'
* };
* Dokker.jsdocHtml()
* .then(function(){
* // => resolved promise
* });
*/
// Create an HTML file from any source code comments in the style of
// [literate programming](https://de.wikipedia.org/wiki/Literate_programming)
Dokker.jsdocHtml = function (options) {
return new Promise(function(resolve, reject) {
return read(options.markdown, 'utf8')
.then(function(data) {
var html = marked(data).replace(/<!-- /g,'<').replace(/ -->/g,'>');
return write(options.html, html, 'utf8');
}).then(function() {
resolve();
}).then(null, function(err) {
reject(err);
});
});
};
// Internal helper function that creates any directory if not existing.
Dokker.mkdir = function(dir) {
return new Promise(function(resolve, reject) {
fs.exists(dir, function(exists) {
if (exists) {
resolve();
} else {
fs.mkdir(dir, function(err) {
if (err) return reject(err);
resolve();
});
}
});
});
};
/**
* Helper function that takes a couple of options arguments and
* normalises them and subsequently returns them.
*
* @static
* @memberOf Dokker
* @category Utility
* @type Function
* @param {Object} [options] The options object.
* @param {string} [options.li.markdown='docs/READMDE.md'] The path where to
* @param {String} [options.source='app.js'] The path the source code with
* JSDoc tags.
* @param {string} [options.jsdoc.markdown='docs/READMDE.md'] The path where to
* save the Markdown file.
* @param {string} [options.jsdoc.README ='READMDE.md'] The path to the
* project's README.md file
* @param {String} [options.jsodc.template ='templates/index.ejs.html'] The
* path to the ejs template.
* @param {String} [options.mocha.template='templates/tests.ejs.html'] The
* path to the mocha template.
* @param {String} [options.dir='docs'] The path where to store the generated
* files
* @returns {Promise} Returns a resolved promise with edited options object
* @example
*
* var options = {
* dir: 'docs',
* literate: {
* source: 'dokker.js',
* dir: 'annotated'
* },
* jsdoc: {
* title: 'Dokker.js',
* source: 'dokker.js',
* markdown: 'README.md',
* html: 'index.html',
* readme: 'README.md',
* }
* };
* Dokker.jsdocHtml()
* .then(function(){
* // => object with absolute pathes to the directory from
* // which the command was executed
* });
*/
// Parses the options file, .Dokker.json, and sets the default parameters.
Dokker.configure = function(options) {
return new Promise(function(resolve, reject){
var file = path.join(process.cwd(), '.dokker.json');
return read(file, 'utf8')
.then(function(data) {
data = JSON.parse(data);
data.jsdoc.template = (data.jsdoc.template) ? path.join(process.cwd(), data.jsdoc.template) : path.join(__dirname, 'templates/index.ejs.html');
data.mocha.template = (data.mocha.template) ? path.join(process.cwd(), data.mocha.template) : path.join(__dirname, 'templates/tests.ejs.html');
data.mocha.path = path.join(process.cwd(), data.dir, data.mocha.path);
// mocha.command commented out to fix build-breaking bug
// specifies mocha command in .dokker.json; e.g. mocha -u tdd -R spec
// data.mocha.command = path.join(process.cwd(), data.dir, data.mocha.command);
data.literate.dir = path.join(process.cwd(), data.dir, data.literate.dir);
data.jsdoc.markdown = path.join(process.cwd(), data.dir, data.jsdoc.markdown);
data.jsdoc.html = path.join(process.cwd(), data.dir, data.jsdoc.html);
data.jsdoc.readme = path.join(process.cwd(), data.jsdoc.readme);
resolve(data);
}).then(null, function(err) {
reject(err);
});
});
};
/**
* Copy the template to local directory so that one can make changes to ejs
* files.
*
* @static
* @memberOf Dokker
* @category Utility
* @type Function
* @param {Object} [options] The options object.
* @param {string} [options.dir='templates'] The path where to save the
* template files
* @returns {Promise} Returns a resolved promise if files were created.
* @example
*
* var options = {
* dir: 'template'
* };
* Dokker.copyTemplate()
* .then(function(){
* // => resolved promise
* });
*/
// Copy starter template to bootstrap any Dokker project.
Dokker.copyTemplate = function(options) {
return new Promise(function(resolve, reject) {
var templatesDir = path.join(process.cwd(), 'templates');
return Dokker.mkdir(templatesDir)
.then(function() {
var oldStyle = path.join(__dirname, 'templates', 'styles.css');
var newStyle = path.join(process.cwd(), options.dir, 'styles.css');
fs.createReadStream(oldStyle).pipe(fs.createWriteStream(newStyle));
var oldLogo = path.join(__dirname, 'templates', 'logo.png');
var newLogo = path.join(process.cwd(), options.dir, 'logo.png');
fs.createReadStream(oldLogo).pipe(fs.createWriteStream(newLogo));
var oldApp = path.join(__dirname, 'templates', 'app.js');
var newApp = path.join(process.cwd(), options.dir, 'app.js');
fs.createReadStream(oldApp).pipe(fs.createWriteStream(newApp));
resolve();
});
});
};
/**
* Create a .dokker.json file to bootstrap any Dokker project
*
* @static
* @memberOf Dokker
* @category Utility
* @type Function
* @returns {Promise} Returns a resolved promise if files were created.
* @example
*
* Dokker.init()
* .then(function(){
* // => resolved promise
* });
*/
// Copies the .Dokker.json file to bootstrap any Dokker project.
Dokker.init = function(){
return new Promise(function(resolve, reject) {
var oldDok = path.join(__dirname, '.dokker.json');
var newDok = path.join(process.cwd(), '.dokker.json');
fs.createReadStream(oldDok).pipe(fs.createWriteStream(newDok));
resolve();
});
};
/**
* Starts a Node.js/Express webserver in docs directory to watch
* the build Dokker project
*
* @static
* @memberOf Dokker
* @category Utility
* @type Function
* @returns {Promise} Returns a resolved promise if files were created.
* @example
*
* Dokker.init()
* .then(function(){
* // => resolved promise
* });
*/
// Starts the Node.js/Express server to watch Dokker.js project
// documentation.
Dokker.watch = function(options) {
var gulpex = require(__dirname + '/gulpfile.js').gulpex;
return gulpex();
// return new Promise(function(resolve, reject){
// exec('DIR='+ options.dir + ' node ' + options.dir + '/app.js', function(error, stdout, stderr){
// if(error) return reject(stderr);
// });
// });
};
module.exports = Dokker;