From 4559c53f97d30202d27fee34d7b378240278b472 Mon Sep 17 00:00:00 2001 From: Joshua Holbrook Date: Thu, 27 Oct 2011 17:49:51 -0700 Subject: [PATCH] [doc] Comments and cleanup on helpers.js and fs2.js --- lib/fs2.js | 21 ++-- lib/helpers.js | 269 ++++++++++++++++++++++++++----------------------- 2 files changed, 152 insertions(+), 138 deletions(-) diff --git a/lib/fs2.js b/lib/fs2.js index 5ef2578..621252e 100644 --- a/lib/fs2.js +++ b/lib/fs2.js @@ -89,9 +89,8 @@ fs2.dirToTree = function (dir) { // Same as filesToTree, except we use Object.keys which is equivalent to the // original list of files. - var _tree = fs2.filesToTree(Object.keys(dir)); + return fs2.filesToTree(Object.keys(dir)); - return _tree; } @@ -109,10 +108,9 @@ fs2.orderedTree = function (tree) { // Take each subtree in the hash and replace it with an ordered subtree. var subtrees = Hash(tree).map(function (v) { - return ordered(v); - }).items; - - var _tree = []; + return ordered(v); + }).items, + _tree = []; // This tree still needs to be ordered itself! Object.keys(subtrees).forEach(function(k) { @@ -157,12 +155,13 @@ fs2.readDir = function (p, resolve, filter, cb) { } - var files = []; + var files = [], + finder; // Finder throws errors instead of passing them, so we use a "try" to funnel // any errors to the callback. try { - var finder = findit.find(p, function (f) { + finder = findit.find(p, function (f) { // This gets called for each individual file. Here we collect them all. files.push(f); }); @@ -188,10 +187,10 @@ fs2.readDir = function (p, resolve, filter, cb) { // throws its errors, but is otherwise equivalent. fs2.readDirSync = function (p, resolve, filter) { - // sync version returns a list of directories already. + // findit.sync returns a list of directories already. var files = findit.sync(p); - if( typeof filter === 'function') { + if ( typeof filter === 'function') { // Apply the filter, if any. files = files.filter(filter); } @@ -209,7 +208,7 @@ fs2.writeFile = function(filePath, contents, callback) { var fileDir = path.dirname(filePath); mkdirp(fileDir, 0755, function(err){ - if(err){ + if (err) { callback(err); } fs.writeFile(filePath, contents, callback); diff --git a/lib/helpers.js b/lib/helpers.js index 035d335..d4768fe 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -1,7 +1,9 @@ -// -// helpers.js -// - +/* + * helpers.js + * + * Contains helpers for the docs. + * + */ var helpers = exports; var docs = require("./docs"), @@ -12,35 +14,59 @@ var docs = require("./docs"), Hash = require('hashish'), traverse = require('traverse'); + +// This function inverts path.resolve() given the base path: +// +// helpers.unresolve(base, helpers.resolve(relative)) === relative +// +// It's helpful for cases where a path has been previously resolved when it +// should be relative to a source directory. helpers.unresolve = function (base, abs) { return abs.replace(base, "."); } +// Test to see if `e` is an element of `xs`. +// +// xs.indexOf(e) returns -1 on no match, and the element's index otherwise. +// -~x is truthy if x is non-negative. +var isElement = helpers.isElement = function(xs, e) { + return -~xs.indexOf(e); +} + +// This helper assists in building a Table of Contents. helpers.buildToc = function () { - // TODO: Needs to work with docs.src - var src = "./pages"; + // TODO: Instead of hard-coding the source folder, we should instead use + // docs.src. However, given that we're not supporting multi-site, this is + // fine. + var src = "./pages", + _articles; - // get list of directories. - var _articles = fs2.readDirSync(src, true, function (a) { + // Get a list of directories. + // TODO: Can this pull from docs.content.content ? + _articles = fs2.readDirSync(src, true, function (a) { return path.extname(a).length === 0; }); + // In this case, .isDirectory adds an unneeded layer to our tree so instances + // of .isDirectory are filtered out here. Object.keys(_articles).forEach(function (i) { if (_articles[i].isDirectory) { _articles[i] = null; } }); - // build up the tree. + // Generate a tree. var toc = fs2.dirToTree(_articles); + // Order the tree. toc = fs2.orderedTree(toc); - // sort the tree. - toc = helpers.tocSort(toc)[0]; + // Sort the tree. + toc = helpers.tocSort(toc); - // digs until there is a layer with >1 item in it. + // Dig until there is a layer with >1 item in it. This way, the ToC doesn't + // have a single item in it, with every relevant listing underneath it. while (toc.length === 1) { var row = toc[0]; var key = Object.keys(row)[0]; @@ -48,35 +74,43 @@ helpers.buildToc = function () { toc = toc[0][key]; } + // Convert the orderedTree into HTML. return helpers.treeToHTML(toc); } -// xs.indexOf(e) returns -1 if no element, the element's index otherwise. -// -~x is truthy if x is non-negative. -var isElement = helpers.isElement = function(xs, e) { - return -~xs.indexOf(e); -} - -// -// Function to use with sorting the ToC. -// ie. tocSort(toc); -// -helpers.tocSort = function(toc) { - return toc.map(function sortToc (row, paths) { - var paths = paths || []; - var key = Object.keys(row)[0]; - var subtoc = row[key]; - - var paths = paths.concat([[key]]); - - //Pretty sweet directory view XD - //console.log(Array(paths.length+1).join(Array(3).join(" ")) + "-"+paths[paths.length-1]); - - // Fails if there's no page.json - // todo: set page.json to a variable +// This helper sorts a table of contents structure. +helpers.tocSort = function (toc) { + + // Map over the ToC recursively. + return toc.map(function sorter (row, paths) { + + // Each row in the ToC has the form: + // + // { "key": subtoc } + // + var paths = paths || [], + key = Object.keys(row)[0], + subtoc = row[key], + paths = paths.concat([[key]]); + + // Uncomment this for a pretty directory view. + // console.log(Array(paths.length+1).join(Array(3).join(" ")) + "-"+paths[paths.length-1]); + + // If there is no page.json, this "try" will fail. + // For more, read comments under the "catch". + // + // TODO: Use docs.src instead of hard-coded page.json. + // TODO: Rewrite this to use more explicit file checks? try { - var order = JSON.parse(fs.readFileSync(paths.join("/")+"/../page.json").toString()).order; + // If the page.json exists, it may have an "order" property, which matches + // children directory names with list indices. + var order = JSON.parse( + fs.readFileSync(paths.join("/")+"/../page.json").toString() + ).order; + + // "Order" supports negative indices, as in Python. This map converts + // those negative indices into proper positive ones. order = Hash(order).map(function (n) { if (n < 0) { return Object.keys(subtoc).length + n; @@ -85,34 +119,44 @@ helpers.tocSort = function(toc) { } }).items; - // sorts the table of contents based on "order" property + // This immediately-executed function sorts the subtoc based on "order". subtoc = (function () { - var oldToc = subtoc; - var newToc = Array(subtoc.length); - // just the keys. - var elements = oldToc.map(function (t) { - return Object.keys(t)[0]; - }); + // We end up copying items from the "oldtoc" to the "newtoc" later. + // + // This algorithm checks for the keys on the root of this subtoc + // often, so we extract those out for easier matching later. + var oldToc = subtoc, + newToc = Array(subtoc.length); + elements = oldToc.map(function (t) { + return Object.keys(t)[0]; + }); + + // Place the elements that are explicitly ordered. + Object.keys(order).forEach(function (name) { + + var setOrder = order[name], + currentOrder = elements.indexOf(name); + + if (currentOrder !== -1) { + // Copy the row into the new Table of Contents. + newToc[setOrder] = subtoc[currentOrder]; - // place the elements that are explicitly ordered - Object.keys(order).forEach(function (label) { - var n = order[label]; - var m = elements.indexOf(label); - if (m !== -1) { - newToc[n] = subtoc[m]; // oldToc holds toc elements not in newToc, but we pull newTocs from // subtoc so that the indices match. + // Find the moved element in the oldToc and then remove it. + // Later, we use oldToc to fill in unspecified indices. var i = oldToc.map(function (t) { return Object.keys(t)[0]; - }).indexOf(label); + }).indexOf(name); + oldToc = oldToc.slice(0, i).concat(oldToc.slice(i+1)); } }); - // get the rest - // use a for loop because map ignores null values + // Use elements not explicitly ordered to fill out the rest of the + // newToc. for (var i = 0; i < newToc.length; i++) { newToc[i] = newToc[i] || oldToc.pop(); @@ -123,40 +167,41 @@ helpers.tocSort = function(toc) { })(); } catch (e) { - //this just means there's nothing to sort. + // Remember, if the "try" block throws it's likely because either the file + // doesn't exist (which is almost expected), it contains invalid JSON, or + // the contents of the JSON are somehow breaking things. + // + // Here, we can catch "bad file descriptor" errors and ignore them (since + // it means the user just didn't write out a page.json. if (e.code !== "EBADF") { throw e; } } - if (typeof subtoc === "object" && subtoc !== null) { + // + if (Array.isArray(subtoc)) { + // Map over the array. return subtoc.map(function (row) { var newRow = {}; var key = Object.keys(row)[0]; - newRow[key] = sortToc(row, paths); + newRow[key] = sorter(row, paths); return newRow; }); } else { return subtoc; } - }); + + })[0]; // Note that the end result has a list inside of a list. }; -// TODO: Use weld. + +// Takes a sortedTree and generates html with it. +// +// TODO: Use weld instead of string concats. helpers.treeToHTML = function(values, parent) { var str = '