Permalink
Browse files

Add support for running Docco via an exported public API:

 - run(args=process.argv) : Run Docco from the command line
 - document(sources,options,callback) : Run Docco over a list of source wildcard matches, with the specified options, with an optional completion callback
 - resolve_source(source) : Useful utility for resolving a source input (supports wildcards) to a list of matched files.

 Add Docco configurable options:
 - use commander.js for command line option parsing, and usage output generation.  (Tried to borrow CoffeeScript optparser, but it's not exposed.)
 - add options for specifying what jst template to use, which css file to copy, and what output folder to use.

Misc
 - Update docco binary to invoke Docco.run() rather than just requiring the library.

Add command line coffee file (second file, drats!):
In order to be able to use docco outside of a direct command line invocation, the main Docco file cannot automatically process the argv and run.  It needed to export an API that can be used to run it from the command line, as well as directly from within another application.  The good news is that this second file is really just for convenience for developers running Docco directly.  e.g. "coffee src/cmd -t myproj/custom_template.jst src/*.coffee")
  • Loading branch information...
1 parent 9ea1a35 commit 608870d7cb0b11d8917411e547c2517fa95f48da @justindujardin justindujardin committed Mar 14, 2012
Showing with 166 additions and 64 deletions.
  1. +1 −3 bin/docco
  2. +5 −0 lib/cmd.js
  3. +72 −25 lib/docco.js
  4. +3 −0 src/cmd.coffee
  5. +85 −36 src/docco.coffee
View
4 bin/docco
@@ -3,6 +3,4 @@
var path = require('path');
var fs = require('fs');
var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib');
-
-process.ARGV = process.argv = process.argv.slice(2, process.argv.length);
-require(lib + '/docco.js');
+require(lib + '/docco.js').run();
View
5 lib/cmd.js
@@ -0,0 +1,5 @@
+(function() {
+
+ (new require('./docco')).run();
+
+}).call(this);
View
97 lib/docco.js
@@ -1,13 +1,13 @@
(function() {
- var destination, docco_styles, docco_template, ensure_directory, exec, ext, fs, generate_documentation, generate_html, get_language, highlight, highlight_end, highlight_start, l, languages, options, parse, path, showdown, spawn, template, version, _ref;
+ var DEFAULTS, commander, ensure_directory, exec, ext, fs, generate_documentation, generate_html, get_language, highlight, highlight_end, highlight_start, l, languages, parse, path, showdown, spawn, template, version, _ref;
- generate_documentation = function(source, callback) {
+ generate_documentation = function(source, config, callback) {
return fs.readFile(source, "utf-8", function(error, code) {
var sections;
if (error) throw error;
sections = parse(source, code);
return highlight(source, sections, function() {
- generate_html(source, sections);
+ generate_html(source, sections, config);
return callback();
});
});
@@ -82,14 +82,17 @@
}
};
- generate_html = function(source, sections) {
- var dest, html, title;
+ generate_html = function(source, sections, config) {
+ var dest, destination, html, title;
+ destination = function(filepath) {
+ return path.join(config.output, path.basename(filepath, path.extname(filepath)) + '.html');
+ };
title = path.basename(source);
dest = destination(source);
- html = docco_template({
+ html = config.docco_template({
title: title,
sections: sections,
- sources: options.args,
+ sources: config.sources,
path: path,
destination: destination
});
@@ -105,7 +108,7 @@
_ref = require('child_process'), spawn = _ref.spawn, exec = _ref.exec;
- options = require('commander');
+ commander = require('commander');
languages = {
'.coffee': {
@@ -154,10 +157,6 @@
return languages[path.extname(source)];
};
- destination = function(filepath) {
- return options.output + path.basename(filepath, path.extname(filepath)) + '.html';
- };
-
ensure_directory = function(dir, callback) {
return exec("mkdir -p " + dir, function() {
return callback();
@@ -170,28 +169,76 @@
version = JSON.parse(fs.readFileSync("" + __dirname + "/../package.json")).version;
- options.version(version).usage("[options] <file_pattern ...>").option("-t, --template [file]", "use a custom .jst template", "" + __dirname + "/../resources/docco.jst").option("-c, --css [file]", "use a custom css file", "" + __dirname + "/../resources/docco.css").option("-o, --output [path]", "use a custom output path (defaults to 'docs/')", "docs/").parse(process.argv).name = "docco";
-
- docco_template = template(fs.readFileSync(options.template).toString());
-
- docco_styles = fs.readFileSync(options.css).toString();
+ DEFAULTS = {
+ template: "" + __dirname + "/../resources/docco.jst",
+ css: "" + __dirname + "/../resources/docco.css",
+ output: "docs/"
+ };
highlight_start = '<div class="highlight"><pre>';
highlight_end = '</pre></div>';
- if (options.args.length) {
- ensure_directory(options.output, function() {
+ exports.resolve_source = function(source) {
+ var file, match_files, match_path, match_regex, match_string;
+ if (!source.match(/([\*\?])/)) return source;
+ match_path = path.dirname(source);
+ match_string = path.basename(source).replace(/\./g, "\\$&").replace(/\*/, ".*").replace(/\?/, ".");
+ match_regex = new RegExp('(' + match_string + ')');
+ match_files = fs.readdirSync(match_path);
+ return (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = match_files.length; _i < _len; _i++) {
+ file = match_files[_i];
+ if (file.match(match_regex)) _results.push(path.join(match_path, file));
+ }
+ return _results;
+ })();
+ };
+
+ exports.run = function(args) {
+ if (args == null) args = process.argv;
+ commander.version(version).usage("[options] <file_pattern ...>").option("-t, --template [file]", "use a custom .jst template", DEFAULTS.template).option("-c, --css [file]", "use a custom css file", DEFAULTS.css).option("-o, --output [path]", "use a custom output path (defaults to 'docs/')", DEFAULTS.output).parse(args).name = "docco";
+ if (commander.args.length) {
+ return exports.document(commander.args.slice(), commander);
+ } else {
+ return console.log(commander.helpInformation());
+ }
+ };
+
+ exports.document = function(sources, options, callback) {
+ var config, docco_styles, key, src, value, _i, _len;
+ config = {};
+ for (key in DEFAULTS) {
+ value = DEFAULTS[key];
+ config[key] = DEFAULTS[key];
+ }
+ if (key in DEFAULTS) {
+ for (key in options) {
+ value = options[key];
+ config[key] = value;
+ }
+ }
+ config.sources = [];
+ for (_i = 0, _len = sources.length; _i < _len; _i++) {
+ src = sources[_i];
+ config.sources = config.sources.concat(exports.resolve_source(src));
+ }
+ config.docco_template = template(fs.readFileSync(config.template).toString());
+ docco_styles = fs.readFileSync(config.css).toString();
+ return ensure_directory(config.output, function() {
var files, next_file;
- fs.writeFile("" + options.output + "docco.css", docco_styles);
- files = options.args.slice(0);
+ fs.writeFile(path.join(config.output, path.basename(config.css)), docco_styles);
+ files = config.sources.slice();
next_file = function() {
- if (files.length) return generate_documentation(files.shift(), next_file);
+ if ((callback != null) && !files.length) callback();
+ if (files.length) {
+ return generate_documentation(files.shift(), config, next_file);
+ }
};
return next_file();
});
- } else {
- console.log(options.helpInformation());
- }
+ };
}).call(this);
View
3 src/cmd.coffee
@@ -0,0 +1,3 @@
+# A simple coffee file to enable direct invocation of docco.coffee,
+# now that the file is not runnable.
+(new require './docco').run()
View
121 src/docco.coffee
@@ -55,12 +55,12 @@
# Generate the documentation for a source file by reading it in, splitting it
# up into comment/code sections, highlighting them for the appropriate language,
# and merging them into an HTML template.
-generate_documentation = (source, callback) ->
+generate_documentation = (source, config, callback) ->
fs.readFile source, "utf-8", (error, code) ->
throw error if error
sections = parse source, code
highlight source, sections, ->
- generate_html source, sections
+ generate_html source, sections, config
callback()
# Given a string of source code, parse out each comment and the code that
@@ -132,11 +132,20 @@ highlight = (source, sections, callback) ->
# Once all of the code is finished highlighting, we can generate the HTML file
# and write out the documentation. Pass the completed sections into the template
# found in `resources/docco.jst`
-generate_html = (source, sections) ->
+generate_html = (source, sections, config) ->
+ # Compute the destination HTML path for an input source file path. If the source
+ # is `lib/example.coffee`, the HTML will be at `[config.output]/example.html`
+ destination = (filepath) ->
+ path.join(config.output, path.basename(filepath, path.extname(filepath)) + '.html')
+
title = path.basename source
dest = destination source
- html = docco_template {
- title: title, sections: sections, sources: options.args, path: path, destination: destination
+ html = config.docco_template {
+ title : title,
+ sections : sections,
+ sources : config.sources,
+ path : path,
+ destination: destination
}
console.log "docco: #{source} -> #{dest}"
fs.writeFile dest, html
@@ -149,7 +158,7 @@ fs = require 'fs'
path = require 'path'
showdown = require('showdown').Showdown
{spawn, exec} = require 'child_process'
-options = require 'commander'
+commander = require 'commander'
# A list of the languages that Docco supports, mapping the file extension to
# the name of the Pygments lexer and the symbol that indicates a comment. To
@@ -194,11 +203,6 @@ for ext, l of languages
# Get the current language we're documenting, based on the extension.
get_language = (source) -> languages[path.extname(source)]
-# Compute the destination HTML path for an input source file path. If the source
-# is `lib/example.coffee`, the HTML will be at `[output_path]/example.html`
-destination = (filepath) ->
- options.output + path.basename(filepath, path.extname(filepath)) + '.html'
-
# Ensure that the destination directory exists.
ensure_directory = (dir, callback) ->
exec "mkdir -p #{dir}", -> callback()
@@ -220,38 +224,83 @@ template = (str) ->
# Extract the docco version from `package.json`
version = JSON.parse(fs.readFileSync("#{__dirname}/../package.json")).version
-
-# Configure [Commander JS](https://github.com/visionmedia/commander.js) options
-# to parse out of the command line.
-options.version(version)
- .usage("[options] <file_pattern ...>")
- .option("-t, --template [file]","use a custom .jst template","#{__dirname}/../resources/docco.jst")
- .option("-c, --css [file]","use a custom css file","#{__dirname}/../resources/docco.css")
- .option("-o, --output [path]","use a custom output path (defaults to 'docs/')","docs/")
- .parse(process.argv)
- .name = "docco"
-# Create the template that we will use to generate the Docco HTML page.
-docco_template = template fs.readFileSync(options.template).toString()
-
-# The CSS styles we'd like to apply to the documentation.
-docco_styles = fs.readFileSync(options.css).toString()
+# Default configuration options.
+DEFAULTS =
+ template: "#{__dirname}/../resources/docco.jst"
+ css : "#{__dirname}/../resources/docco.css"
+ output : "docs/"
# The start of each Pygments highlight block.
highlight_start = '<div class="highlight"><pre>'
# The end of each Pygments highlight block.
highlight_end = '</pre></div>'
-# Run the script.
-# For each source file passed in as an argument, generate the documentation.
-# If no sources are specified, print the usage help and exit.
-if options.args.length
- ensure_directory options.output, ->
- fs.writeFile "#{options.output}docco.css", docco_styles
- files = options.args.slice(0)
- next_file = -> generate_documentation files.shift(), next_file if files.length
+#### Public API
+
+# Resolve a wildcard source input to the files it matches.
+# Return an array of matched files, or `source` if it does
+# not contain any wildcards.
+exports.resolve_source = (source) ->
+ # If it is not a wildcard, just return the input.
+ return source if not source.match(/([\*\?])/)
+
+ # Convert the wildcard match to a regular expression.
+ match_path = path.dirname(source)
+ match_string = path.basename(source)
+ .replace(/\./g, "\\$&")
+ .replace(/\*/,".*")
+ .replace(/\?/,".")
+ match_regex = new RegExp('(' + match_string + ')')
+
+ # Look for files in the match path, and return the ones
+ # that match the regex.
+ match_files = fs.readdirSync match_path
+ return (path.join(match_path,file) for file in match_files when file.match match_regex)
+
+# Run Docco from a set of command line arguments, defaulting to `process.argv`.
+exports.run = (args=process.argv) ->
+ # Parse command line options using [Commander JS](https://github.com/visionmedia/commander.js).
+ commander.version(version)
+ .usage("[options] <file_pattern ...>")
+ .option("-t, --template [file]","use a custom .jst template",DEFAULTS.template)
+ .option("-c, --css [file]","use a custom css file",DEFAULTS.css)
+ .option("-o, --output [path]","use a custom output path (defaults to 'docs/')",DEFAULTS.output)
+ .parse(args)
+ .name = "docco"
+
+ # Generate the documentation for provided source arguments.
+ # If no sources are specified, print the usage help and exit.
+ if commander.args.length
+ exports.document(commander.args.slice(),commander)
+ else
+ console.log commander.helpInformation()
+
+# Run Docco with overs `sources` with the given `options`
+exports.document = (sources,options,callback) ->
+ # Construct the Docco config to use with this documentation pass
+ # by taking the `DEFAULTS` first, then merging in specified options
+ # from the passed `config` object.
+ config = {}
+ config[key] = DEFAULTS[key] for key,value of DEFAULTS
+ config[key] = value for key,value of options if key of DEFAULTS
+ config.sources = []
+ config.sources = config.sources.concat(exports.resolve_source(src)) for src in sources
+
+ # Create the template that we will use to generate the Docco HTML page.
+ config.docco_template = template fs.readFileSync(config.template).toString()
+
+ # The CSS styles we'd like to apply to the documentation.
+ docco_styles = fs.readFileSync(config.css).toString()
+
+ # Run the script.
+ # For each source file passed in, generate the documentation.
+ ensure_directory config.output, ->
+ fs.writeFile path.join(config.output,path.basename(config.css)), docco_styles
+ files = config.sources.slice()
+ next_file = ->
+ callback() if callback? and not files.length
+ generate_documentation files.shift(), config, next_file if files.length
next_file()
-else
- console.log options.helpInformation()

0 comments on commit 608870d

Please sign in to comment.