#!/usr/bin/env node 'use strict'; var swig = require('swig'); var fs = require('fs'); var config = require('./config'); var fileHelper = require('./file-helper'); var fileFinder = require('./file-finder'); var patternFinder = require('./pattern-finder'); var docBlocks = require('./doc-blocks'); var util = require('./util'); var path = require('path'); var fs = require('fs'); // global settings var settings = {}; var directory; /** * @name init * @description Initialize global settings. This is done automatically, * but you can call it again in case you screwed something up. * @param {String} [dir] The directory to be scanned. * @public */ var init = function (dir) { if (dir) { directory = dir; } // set defaults settings = config.getConfig(dir); }; /** * @description generate the API docs * @method go * @param {String} [dir = process.cwd()] * @public * @return {DocBlock[]} blocks The blocks used to generate * the documentation. */ var go = function (dir) { var files, blocks, stringifiedBlocks; init(dir); // find the files util.log('searching', directory); util.log('excluding', settings.exclude); files = fileFinder.getFiles(directory, 'js', settings.exclude); // find the blocks in each blocks = getDocBlocksFromFiles(files); blocks = getPublicBlocks(blocks); util.log('found %d blocks in %d files', blocks.public.length, files.length); stringifiedBlocks = stringify(blocks); writeResults(stringifiedBlocks); util.log('finished writing documentation\n'); return blocks; }; /** * @name writeResults * @description Writes the api doc string into the * destination file. * @param {String} results * @private */ var writeResults = function (results) { var destinationFile = path.join(directory, settings.outputFile); var destinationText = fileHelper.readFileSync(destinationFile); var newText = insertDocs(destinationText, results); fs.writeFileSync(destinationFile, newText); }; /** * @name insertDocs * @description Inserts docs at the proper place into another string. * This will replace old docs if found. * @param {String} target the string docs will be inserted into * @param {String} docs * @return {String} updated the updated target * @private */ var insertDocs = function (target, docs) { var regex = /\n/g; if (regex.test(target)) { target = target.replace(regex, docs); } else { target += docs; } return target; }; /** * @name getPublicBlocks * @description group blocks by privacy * @param {DocBlock[]} blocks * @return {Object} */ var getPublicBlocks = function (blocks) { var grouped = { 'public': [] }; util.each(blocks, function (block) { if (block.privacy === 'public') { grouped.public.push(block); } }); return grouped; }; /** * @description get the DocBlocks from an Array of files * @function getDocBlocksFromFiles * @param {String[]} filePaths * @return {DocBlock[]} * @private */ var getDocBlocksFromFiles = function (filePaths) { var i, max, result = []; var getBlocks = function (rawBlocks, filePath) { var blocks = []; util.each(rawBlocks, function (block, i) { blocks.push( new docBlocks.DocBlock(block, filePath) ); }); return blocks; }; util.each(filePaths, function (filePath) { var rawBlocks = patternFinder.findPattern(settings.docBlockRegex, filePath); result = result.concat(getBlocks(rawBlocks, filePath)); }); return result; }; /** * @description get the result of bunch of * stringified DocBlocks * @function stringify * @param {Object} groupedBlocks * @return {String} * @private */ var stringify = function (groupedBlocks) { var result = ''; var templateFilePath = path.join(__dirname, settings.template); var template = swig.compileFile(templateFilePath); var stringified = {}; var blockTemplate = getBlockTemplate(); util.each(groupedBlocks, function (blocks, privacy) { var str = ''; util.each(blocks, function (block) { str += block.stringify(blockTemplate); }); stringified[privacy] = str; }); return template({ 'public': stringified.public }); }; /** * @name getBlockTemplate * @description Get the file to use for generating block templates. * Check working directory and, if not found, use the default. * @return {String} template the file path of the template to use * @private */ var getBlockTemplate = function () { var tmplName = settings.blockTemplate; var blockTemplate = path.join(path.join(process.cwd(), directory), tmplName); if (!fs.existsSync(blockTemplate)) { blockTemplate = path.join(__dirname, tmplName); } return blockTemplate; }; /** * @name configure * @description Get/set global settings. If no args, return all settings. * If just name, return that setting. If name & value, set that setting. * @param {String/Object} [name] setting name - Optionally pass a an Object * to set multiple values at once. * @param [value] * @public value setting value * @return {Object/String/Number/Boolean} setting Depending on what args * were passed, this can return all settings as an Object or one setting's * value. */ var configure = function (name, value) { var result, args = arguments.length; if (args === 1) { if (!(name instanceof Object)) { result = settings[name]; } else { util.each(name, function (value, key) { settings[key] = value; }); } } else if (args === 2) { settings[name] = value; } return result === undefined ? settings : result; }; // ================================================ // TODO: Figure out a better way to determine if // this is being used from CLI or required in // another script. var dir = process.argv[2]; if (dir && dir !== '--reporter') { go(dir); } else { exports.init = init; exports.configure = configure; exports.go = go; }