## # Provides CroJSDoc command line interface # @module cli fs = require 'fs' glob = require 'glob' walkdir = require 'walkdir' {basename,dirname,join,resolve} = require 'path' isWindows = process.platform is 'win32' ## # Reads a config file(crojsdoc.yaml) to build options # @param {Options} options # @memberOf cli _readConfig = (options) -> {safeLoad} = require 'js-yaml' try config = safeLoad fs.readFileSync(join(process.cwd(), 'crojsdoc.yaml'), 'utf-8') if config.hasOwnProperty 'output' options.output = config.output if config.hasOwnProperty 'title' options.title = config.title if config.hasOwnProperty 'quiet' or config.hasOwnProperty 'quite' options.quiet = config.quiet is true if config.hasOwnProperty 'files' options.files = config.files is true if config.hasOwnProperty('readme') and typeof config.readme is 'string' options._readme = config.readme if config.hasOwnProperty 'external-types' options['external-types'] = config['external-types'] if config.hasOwnProperty 'sources' if Array.isArray config.sources [].push.apply options._sources, config.sources else options._sources.push config.sources if config.hasOwnProperty 'github' options.github = config.github if options.github.branch is undefined options.github.branch = 'master' if config.hasOwnProperty 'reverse_see_also' options.reverse_see_also = config.reverse_see_also is true if config.hasOwnProperty 'plugins' plugins = config.plugins if not Array.isArray plugins plugins = [plugins] options.plugins = plugins.map (plugin) -> try require plugin catch e console.log "Plugin '#{plugin}' not found" .filter (plugin) -> plugin return ## # Parses the command line arguments to build options # @param {Options} options # @memberOf cli _parseArguments = (options) -> {OptionParser} = require 'optparse' switches = [ [ '-h', '--help', 'show help' ] [ '-o', '--output DIRECTORY', 'Output directory' ] [ '-t', '--title TITLE', 'Document Title' ] [ '-q', '--quiet', 'less output' ] [ '-r', '--readme DIRECTORY', 'README.md directory path'] [ '-f', '--files', 'included source files' ] [ '--external-types JSONFILE', 'external type definitions' ] ] parser = new OptionParser switches parser.banner = 'Usage: crojsdoc [-o DIRECTORY] [-t TITLE] [-q] [options..] SOURCES...' parser.on 'help', -> console.log parser.toString() process.exit(1) parser.on '*', (opt, value) -> if value is undefined value = true options[opt] = value [].push.apply options._sources, parser.parse process.argv.slice 2 ## # Reads additional type definitions from a file or a object # @param {String|Object} external_types # @param {Object} types # @memberOf cli _readExternalTypes = (external_types, types) -> return if not external_types if typeof external_types is 'string' try content = fs.readFileSync(external_types, 'utf-8').trim() try external_types = JSON.parse content catch e console.log "external-types: Invalid JSON file" catch e console.log "external-types: Cannot open #{external_types}" if typeof external_types is 'object' for type, url of external_types types[type] = url ## # Builds options from a config file(crojsdoc.yaml) or command line arguments # @return {Options} # @memberOf cli _buildOptions = -> options = _project_dir: process.cwd() types: # Links for pre-known types Object: 'https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object' Boolean: 'https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Boolean' String: 'https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String' Array: 'https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array' Number: 'https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Number' Date: 'https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date' Function: 'https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function' RegExp: 'https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/RegExp' Error: 'https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error' undefined: 'https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/undefined' _sources: [] _readConfig options _parseArguments options if options.plugins options.plugins.forEach (plugin) -> if plugin.externalTypes _readExternalTypes plugin.externalTypes, options.types return # process options.external-types after plugins' externalTypes # for user to override plugins' configurations _readExternalTypes options['external-types'], options.types options.output_dir = resolve options._project_dir, options.output or 'doc' return options ## # Reads source files # @param {Options} options # @return {Array<Content>} # @memberOf cli _readSourceFiles = (options) -> if isWindows project_dir_re = new RegExp("^" + options._project_dir.replace(/\\/g, '\\\\')) else project_dir_re = new RegExp("^" + options._project_dir) contents = [] for path in options._sources base_path = path = resolve options._project_dir, path base_path = dirname base_path while /[*?]/.test basename(base_path) glob.sync(path).forEach (path) => if fs.statSync(path).isDirectory() list = walkdir.sync path else list = [path] for file in list continue if fs.statSync(file).isDirectory() data = fs.readFileSync(file, 'utf-8').trim() continue if not data if isWindows contents.push full_path: file.replace(project_dir_re, '').replace(/\\/g, '/'), path: file.substr(base_path.length+1).replace(/\\/g, '/'), data: data else contents.push full_path: file.replace(project_dir_re, ''), path: file.substr(base_path.length+1), data: data return try data = fs.readFileSync "#{options._readme or options._project_dir}/README.md", 'utf-8' contents.push full_path: '', path: 'README', data: data return contents ## # Runs CroJSDoc via CLI # @memberOf cli exports.run = -> options = _buildOptions() contents = _readSourceFiles options result = require('./collect') contents, options require('./render') result, options