function createTOC() {
	var forbidden;
	var keywords;
	var allowed;
	var aliases;
	var options;
	var paths;
	var clbk;
	var opts;
	var err;

	opts = {};
	if ( arguments.length < 2 ) {
		clbk = arguments[ 0 ];
	} else {
		options = arguments[ 0 ];
		clbk = arguments[ 1 ];
		err = validate( opts, options );
		if ( err ) {
			throw err;
		}
	}
	if ( !isFunction( clbk ) ) {
		throw new TypeError( format( 'invalid argument. Callback argument must be a function. Value: `%s`.', clbk ) );
	}
	if ( opts.dir ) {
		opts.dir = resolve( cwd(), opts.dir );
	} else {
		opts.dir = cwd();
	}
	if ( opts.pattern ) {
		opts.pattern += '/package.json';
	}
	readFile( resolve( opts.dir, 'lib', 'index.js' ), onFile );

	/**
	* Callback invoked after reading the `index.js` file of a namespace.
	*
	* @private
	* @param {(Error|null)} error - error object
	* @param {(EmptyArray|StringArray)} ns - code with all package exports
	* @returns {void}
	*/
	function onFile( error, ns ) {
		if ( error ) {
			return clbk( error );
		}
		aliases = extractAliases( ns.toString() );
		findPkgs( opts, onGlob );
	}

	/**
	* Callback invoked after matching files.
	*
	* @private
	* @param {(Error|null)} error - error object
	* @param {(EmptyArray|StringArray)} files - list of matching files
	* @returns {void}
	*/
	function onGlob( error, files ) {
		var word;
		var idx;
		var i;

		if ( error ) {
			return clbk( error );
		}
		debug( 'Matched packages: %s', files.join( ', ' ) );

		if ( files.length === 0 ) {
			return clbk( null, {
				'toc': '<div class="namespace-toc">\n\n\n</div>',
				'links': []
			});
		}

		if ( opts.keywords ) {
			keywords = opts.keywords.split( ',' );
			allowed = [];
			forbidden = [];
			for ( i = 0; i < keywords.length; i++ ) {
				word = trim( keywords[ i ] );
				if ( startsWith( word, '+' ) ) {
					allowed.push( removeFirst( word ) );
				} else if ( startsWith( word, '-' ) ) {
					forbidden.push( removeFirst( word ) );
				}
			}
			paths = [];
			idx = 0;
			readJSON( resolve( opts.dir, files[ idx ], 'package.json' ), onPackage );
		} else {
			paths = [];
			for ( i = 0; i < files.length; i++ ) {
				paths.push( resolve( opts.dir, files[ i ], 'README.md' ) );
			}
			readFileList( paths, onContents );
		}

		/**
		* Callback invoked after loading the `package.json` file of the current package (with the file index given by `idx`).
		*
		* @private
		* @param {(Error|null)} error - error object
		* @param {Object} json - package.json object
		* @returns {void}
		*/
		function onPackage( error, json ) {
			var pkgKeywords;
			var include;
			var omit;
			var j;

			if ( error ) {
				return clbk( error );
			}
			pkgKeywords = json.keywords;
			omit = false;
			include = false;
			if ( allowed.length > 0 && forbidden.length > 0 ) {
				for ( j = 0; j < pkgKeywords.length; j++ ) {
					if ( contains( forbidden, pkgKeywords[ j ] ) ) {
						omit = true;
						break;
					} else if ( contains( allowed, pkgKeywords[ j ] ) ) {
						include = true;
					}
				}
			} else if ( allowed.length === 0 && forbidden.length > 0 ) {
				for ( j = 0; j < pkgKeywords.length; j++ ) {
					if ( contains( forbidden, pkgKeywords[ j ] ) ) {
						omit = true;
						break;
					}
				}
				include = true;
			} else if ( allowed.length > 0 && forbidden.length === 0 ) {
				for ( j = 0; j < pkgKeywords.length; j++ ) {
					if ( contains( allowed, pkgKeywords[ j ] ) ) {
						include = true;
						break;
					}
				}
			} else {
				include = true;
			}
			if ( include && !omit ) {
				paths.push( resolve( opts.dir, files[ idx ], 'README.md' ) );
			}
			idx += 1;
			if ( idx === files.length ) {
				readFileList( paths, onContents );
			} else {
				readJSON( resolve( opts.dir, files[ idx ], 'package.json' ), onPackage );
			}
		}
	}

	/**
	* Callback invoked after loading `README.md` files.
	*
	* @private
	* @param {(Error|null)} error - error object
	* @param {(EmptyArray|StringArray)} contents - README.md contents
	* @returns {void}
	*/
	function onContents( error, contents ) {
		if ( error ) {
			debug( 'Encountered an error: %s', error.message );
			return clbk( error );
		}
		debug( 'Read %d README.md files', contents.length );
		clbk( null, toc( contents, aliases, opts.blacklist ) );
	}
}
Asynchronously creates a table of contents.

@param {Options} [options] - function options
@param {string} [options.dir] - root directory from which to search for files
@param {string} [options.pattern='**\/*'] - package pattern
@param {StringArray} [options.ignore] - ignore pattern
@param {string} [options.keywords] - string of comma-separated keywords (preceded by `+` or `-` to indicate inclusion or exclusion, respectively)
@param {StringArray} [options.blacklist] - blacklisted packages
@param {Callback} clbk - callback to invoke after creating the table of contents
@throws {TypeError} options argument must be an object
@throws {TypeError} must provide valid options
@throws {TypeError} must provide a function

@example
var resolve = require( 'path' ).resolve;

var opts = {
    'dir': resolve( __dirname, '..' )
};
createTOC( opts, onCreate );

function onCreate( error, toc ) {
  if ( error ) {
    throw error;
  }
  console.log( toc );
}