Comparing version 3.3.0-alpha2 to 3.3.0-alpha3
322
cli.js
@@ -6,7 +6,7 @@ /** | ||
* | ||
* + The module should really export an instance of `JSDoc`, and `props` should be properties of a | ||
* `JSDoc` instance. However, Rhino interpreted `this` as a reference to `global` within the | ||
* + The module should really export an instance of `cli`, and `props` should be properties of a | ||
* `cli` instance. However, Rhino interpreted `this` as a reference to `global` within the | ||
* prototype's methods, so we couldn't do that. | ||
* + Use the `fs` and `path` modules rather than `jsdoc/fs` and `jsdoc/path`, which are not | ||
* initialized correctly when they're loaded this early. | ||
* + On Rhino, for unknown reasons, the `jsdoc/fs` and `jsdoc/path` modules can fail in some cases | ||
* when they are required by this module. You may need to use `fs` and `path` instead. | ||
* | ||
@@ -18,5 +18,11 @@ * @private | ||
var logger = require('jsdoc/util/logger'); | ||
var hasOwnProp = Object.prototype.hasOwnProperty; | ||
var props = { | ||
docs: [], | ||
packageJson: null | ||
packageJson: null, | ||
shouldExitWithError: false, | ||
tmpdir: null | ||
}; | ||
@@ -27,9 +33,12 @@ | ||
var JSDoc = {}; | ||
var FATAL_ERROR_MESSAGE = 'Exiting JSDoc because an error occurred. See the previous log ' + | ||
'messages for details.'; | ||
var cli = {}; | ||
// TODO: docs | ||
JSDoc.setVersionInfo = function() { | ||
cli.setVersionInfo = function() { | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
// allow this to throw--something is really wrong if we can't read our own package file | ||
var info = JSON.parse( fs.readFileSync(path.join(env.dirname, 'package.json'), 'utf8') ); | ||
@@ -42,7 +51,7 @@ | ||
return JSDoc; | ||
return cli; | ||
}; | ||
// TODO: docs | ||
JSDoc.loadConfig = function() { | ||
cli.loadConfig = function() { | ||
var _ = require('underscore'); | ||
@@ -62,3 +71,8 @@ var args = require('jsdoc/opts/args'); | ||
env.opts = args.parse(env.args); | ||
try { | ||
env.opts = args.parse(env.args); | ||
} | ||
catch (e) { | ||
cli.exit(1, e.message + '\n' + FATAL_ERROR_MESSAGE); | ||
} | ||
@@ -82,3 +96,4 @@ confPath = env.opts.configure || path.join(env.dirname, 'conf.json'); | ||
catch (e) { | ||
throw new Error('Cannot parse the config file ' + confPath + ': ' + e); | ||
cli.exit(1, 'Cannot parse the config file ' + confPath + ': ' + e + '\n' + | ||
FATAL_ERROR_MESSAGE); | ||
} | ||
@@ -89,7 +104,65 @@ | ||
return JSDoc; | ||
return cli; | ||
}; | ||
// TODO: docs | ||
JSDoc.runCommand = function(cb) { | ||
cli.configureLogger = function() { | ||
function recoverableError() { | ||
props.shouldExitWithError = true; | ||
} | ||
function fatalError() { | ||
cli.exit(1); | ||
} | ||
if (env.opts.debug) { | ||
logger.setLevel(logger.LEVELS.DEBUG); | ||
} | ||
else if (env.opts.verbose) { | ||
logger.setLevel(logger.LEVELS.INFO); | ||
} | ||
if (env.opts.pedantic) { | ||
logger.once('logger:warn', recoverableError); | ||
logger.once('logger:error', fatalError); | ||
} | ||
else { | ||
logger.once('logger:error', recoverableError); | ||
} | ||
logger.once('logger:fatal', fatalError); | ||
return cli; | ||
}; | ||
// TODO: docs | ||
cli.logStart = function() { | ||
var loggerFunc = env.opts.help ? console.log : logger.info; | ||
cli.printVersion(loggerFunc); | ||
logger.debug('Environment info: %j', { | ||
env: { | ||
conf: env.conf, | ||
opts: env.opts | ||
} | ||
}); | ||
}; | ||
// TODO: docs | ||
cli.logFinish = function() { | ||
var delta; | ||
var deltaSeconds; | ||
if (env.run.finish && env.run.start) { | ||
delta = env.run.finish.getTime() - env.run.start.getTime(); | ||
} | ||
if (delta !== undefined) { | ||
deltaSeconds = (delta / 1000).toFixed(2); | ||
logger.info('Finished running in %s seconds.', deltaSeconds); | ||
} | ||
}; | ||
// TODO: docs | ||
cli.runCommand = function(cb) { | ||
var cmd; | ||
@@ -99,21 +172,31 @@ | ||
function done(errorCode) { | ||
if (!errorCode && props.shouldExitWithError) { | ||
cb(1); | ||
} | ||
else { | ||
cb(errorCode); | ||
} | ||
} | ||
if (opts.help) { | ||
cmd = JSDoc.printHelp; | ||
cmd = cli.printHelp; | ||
} | ||
else if (opts.test) { | ||
cmd = JSDoc.runTests; | ||
cmd = cli.runTests; | ||
} | ||
else if (opts.version) { | ||
cmd = JSDoc.printVersion; | ||
cmd = function(callback) { callback(); }; | ||
} | ||
else { | ||
cmd = JSDoc.main; | ||
cmd = cli.main; | ||
} | ||
cmd(cb); | ||
cmd(done); | ||
}; | ||
// TODO: docs | ||
JSDoc.printHelp = function(cb) { | ||
console.log( require('jsdoc/opts/args').help() ); | ||
cli.printHelp = function(cb) { | ||
console.log( '\n' + require('jsdoc/opts/args').help() + '\n' ); | ||
console.log('Visit http://usejsdoc.org for more information.'); | ||
cb(0); | ||
@@ -123,3 +206,3 @@ }; | ||
// TODO: docs | ||
JSDoc.runTests = function(cb) { | ||
cli.runTests = function(cb) { | ||
var path = require('jsdoc/path'); | ||
@@ -136,13 +219,22 @@ | ||
// TODO: docs | ||
JSDoc.printVersion = function(cb) { | ||
console.log('JSDoc %s (%s)', env.version.number, env.version.revision); | ||
cb(0); | ||
cli.getVersion = function() { | ||
return 'JSDoc ' + env.version.number + ' (' + env.version.revision + ')'; | ||
}; | ||
// TODO: docs | ||
JSDoc.main = function(cb) { | ||
JSDoc.scanFiles(); | ||
cli.printVersion = function(loggerFunc, cb) { | ||
loggerFunc = loggerFunc || logger.info; | ||
loggerFunc.call( null, cli.getVersion() ); | ||
if (cb) { | ||
cb(0); | ||
} | ||
}; | ||
// TODO: docs | ||
cli.main = function(cb) { | ||
cli.scanFiles(); | ||
if (env.sourceFiles.length) { | ||
JSDoc.createParser() | ||
cli.createParser() | ||
.parseFiles() | ||
@@ -156,3 +248,83 @@ .processParseResults(); | ||
JSDoc.scanFiles = function() { | ||
function getRandomId() { | ||
var MIN = 100000; | ||
var MAX = 999999; | ||
return Math.floor(Math.random() * (MAX - MIN + 1) + MIN); | ||
} | ||
// TODO: docs | ||
function createTempDir() { | ||
var fs = require('jsdoc/fs'); | ||
var path = require('jsdoc/path'); | ||
var wrench = require('wrench'); | ||
var isRhino; | ||
var tempDirname; | ||
var tempPath; | ||
// We only need one temp directory | ||
if (props.tmpdir) { | ||
return props.tmpdir; | ||
} | ||
isRhino = require('jsdoc/util/runtime').isRhino(); | ||
tempDirname = 'tmp-' + Date.now() + '-' + getRandomId(); | ||
tempPath = path.join(env.dirname, tempDirname); | ||
try { | ||
fs.mkdirSync(tempPath); | ||
props.tmpdir = path; | ||
} | ||
catch (e) { | ||
logger.fatal('Unable to create the temp directory %s: %s', tempPath, e.message); | ||
return null; | ||
} | ||
try { | ||
// Delete the temp directory on exit | ||
if (isRhino) { | ||
( new java.io.File(tempPath) ).deleteOnExit(); | ||
} | ||
else { | ||
process.on('exit', function() { | ||
wrench.rmdirSyncRecursive(tempPath); | ||
}); | ||
} | ||
return tempPath; | ||
} | ||
catch (e) { | ||
logger.error('Cannot automatically delete the temp directory %s on exit: %s', tempPath, | ||
e.message); | ||
return null; | ||
} | ||
} | ||
// TODO: docs | ||
function copyResourceDir(filepath) { | ||
var fs = require('jsdoc/fs'); | ||
var path = require('jsdoc/path'); | ||
var wrench = require('wrench'); | ||
var resourceDir; | ||
var tmpDir; | ||
try { | ||
tmpDir = createTempDir(); | ||
resourceDir = path.join( tmpDir, path.basename(filepath) + '-' + getRandomId() ); | ||
fs.mkdirSync(resourceDir); | ||
wrench.copyDirSyncRecursive(filepath, resourceDir); | ||
return resourceDir; | ||
} | ||
catch (e) { | ||
logger.fatal('Unable to copy %s to the temp directory %s: %s', filepath, resourceDir, | ||
e.message); | ||
return null; | ||
} | ||
} | ||
// TODO: docs | ||
cli.scanFiles = function() { | ||
var Filter = require('jsdoc/src/filter').Filter; | ||
@@ -192,8 +364,35 @@ var fs = require('jsdoc/fs'); | ||
return JSDoc; | ||
return cli; | ||
}; | ||
JSDoc.createParser = function() { | ||
function resolvePluginPaths(paths) { | ||
var path = require('jsdoc/path'); | ||
var isNode = require('jsdoc/util/runtime').isNode(); | ||
var pluginPaths = []; | ||
paths.forEach(function(plugin) { | ||
var basename = path.basename(plugin); | ||
var dirname = path.dirname(plugin); | ||
var pluginPath = path.getResourcePath(dirname); | ||
if (!pluginPath) { | ||
logger.error('Unable to find the plugin "%s"', plugin); | ||
return; | ||
} | ||
// On Node.js, the plugin needs to be inside the JSDoc directory | ||
else if ( isNode && (pluginPath.indexOf(global.env.dirname) !== 0) ) { | ||
pluginPath = copyResourceDir(pluginPath); | ||
} | ||
pluginPaths.push( path.join(pluginPath, basename) ); | ||
}); | ||
return pluginPaths; | ||
} | ||
cli.createParser = function() { | ||
var handlers = require('jsdoc/src/handlers'); | ||
var parser = require('jsdoc/src/parser'); | ||
var path = require('jsdoc/path'); | ||
var plugins = require('jsdoc/plugins'); | ||
@@ -204,2 +403,3 @@ | ||
if (env.conf.plugins) { | ||
env.conf.plugins = resolvePluginPaths(env.conf.plugins); | ||
plugins.installPlugins(env.conf.plugins, app.jsdoc.parser); | ||
@@ -210,6 +410,6 @@ } | ||
return JSDoc; | ||
return cli; | ||
}; | ||
JSDoc.parseFiles = function() { | ||
cli.parseFiles = function() { | ||
var augment = require('jsdoc/augment'); | ||
@@ -222,4 +422,3 @@ var borrow = require('jsdoc/borrow'); | ||
props.docs = docs = app.jsdoc.parser.parse(env.sourceFiles, | ||
env.opts.encoding); | ||
props.docs = docs = app.jsdoc.parser.parse(env.sourceFiles, env.opts.encoding); | ||
@@ -231,4 +430,4 @@ // If there is no package.json, just create an empty package | ||
logger.debug('Adding inherited symbols...'); | ||
borrow.indexAll(docs); | ||
augment.addInherited(docs); | ||
@@ -239,20 +438,24 @@ borrow.resolveBorrows(docs); | ||
return JSDoc; | ||
return cli; | ||
}; | ||
JSDoc.processParseResults = function() { | ||
cli.processParseResults = function() { | ||
if (env.opts.explain) { | ||
JSDoc.dumpParseResults(); | ||
cli.dumpParseResults(); | ||
} | ||
else { | ||
JSDoc.resolveTutorials(); | ||
JSDoc.generateDocs(); | ||
cli.resolveTutorials(); | ||
cli.generateDocs(); | ||
} | ||
return cli; | ||
}; | ||
JSDoc.dumpParseResults = function() { | ||
cli.dumpParseResults = function() { | ||
global.dump(props.docs); | ||
return cli; | ||
}; | ||
JSDoc.resolveTutorials = function() { | ||
cli.resolveTutorials = function() { | ||
var resolver = require('jsdoc/tutorial/resolver'); | ||
@@ -264,5 +467,7 @@ | ||
} | ||
return cli; | ||
}; | ||
JSDoc.generateDocs = function() { | ||
cli.generateDocs = function() { | ||
var path = require('jsdoc/path'); | ||
@@ -275,5 +480,16 @@ var resolver = require('jsdoc/tutorial/resolver'); | ||
env.opts.template = (function() { | ||
var isNode = require('jsdoc/util/runtime').isNode(); | ||
var publish = env.opts.template || 'templates/default'; | ||
// if we don't find it, keep the user-specified value so the error message is useful | ||
return path.getResourcePath(publish) || env.opts.template; | ||
var templatePath = path.getResourcePath(publish); | ||
if (templatePath && isNode) { | ||
// On Node.js, the template needs to be inside the JSDoc folder | ||
if (templatePath.indexOf(env.dirname) !== 0) { | ||
templatePath = copyResourceDir(templatePath); | ||
} | ||
} | ||
// if we didn't find the template, keep the user-specified value so the error message is | ||
// useful | ||
return templatePath || env.opts.template; | ||
})(); | ||
@@ -285,3 +501,3 @@ | ||
catch(e) { | ||
throw new Error('Unable to load template: ' + e.message || e); | ||
logger.fatal('Unable to load template: ' + e.message || e); | ||
} | ||
@@ -291,4 +507,3 @@ | ||
if (template.publish && typeof template.publish === 'function') { | ||
// convert this from a URI back to a path if necessary | ||
env.opts.template = path._uriToPath(env.opts.template); | ||
logger.printInfo('Generating output files...'); | ||
template.publish( | ||
@@ -299,12 +514,19 @@ taffy(props.docs), | ||
); | ||
logger.info('complete.'); | ||
} | ||
else { | ||
throw new Error(env.opts.template + ' does not export a "publish" function. Global ' + | ||
logger.fatal(env.opts.template + ' does not export a "publish" function. Global ' + | ||
'"publish" functions are no longer supported.'); | ||
} | ||
return cli; | ||
}; | ||
return JSDoc; | ||
// TODO: docs | ||
cli.exit = function(exitCode) { | ||
process.exit(exitCode || 0); | ||
}; | ||
return cli; | ||
})(); |
@@ -48,5 +48,2 @@ Pull Requests | ||
should be separate branches and merge requests. | ||
Also, if you want to change `package.json`, note that you must make your changes | ||
in `Jake/templates/package.json.tmpl`, then run `jake`. | ||
@@ -53,0 +50,0 @@ 5. **Add tests** |
21
jsdoc.js
@@ -129,6 +129,3 @@ #!/usr/bin/env node | ||
function cb(errorCode) { | ||
process.exit(errorCode || 0); | ||
} | ||
var logger = require('jsdoc/util/logger'); | ||
var path = require('jsdoc/path'); | ||
@@ -142,2 +139,13 @@ var runtime = require('jsdoc/util/runtime'); | ||
if (!global.env.opts.test) { | ||
cli.configureLogger(); | ||
} | ||
cli.logStart(); | ||
function cb(errorCode) { | ||
cli.logFinish(); | ||
cli.exit(errorCode || 0); | ||
} | ||
// On Rhino, we use a try/catch block so we can log the Java exception (if available) | ||
@@ -150,7 +158,6 @@ if ( runtime.isRhino() ) { | ||
if (e.rhinoException) { | ||
e.rhinoException.printStackTrace(); | ||
process.exit(1); | ||
logger.fatal( e.rhinoException.printStackTrace() ); | ||
} else { | ||
console.trace(e); | ||
process.exit(1); | ||
cli.exit(1); | ||
} | ||
@@ -157,0 +164,0 @@ } |
@@ -9,3 +9,4 @@ /** | ||
var doop = require("jsdoc/util/doop").doop; | ||
var doop = require('jsdoc/util/doop'); | ||
var logger = require('jsdoc/util/logger'); | ||
@@ -35,3 +36,4 @@ var hasOwnProp = Object.prototype.hasOwnProperty; | ||
if (!docs.index) { | ||
throw 'Docs has not been indexed: docs.index must be defined here.'; | ||
logger.error('Unable to resolve borrowed symbols, because the docs have not been indexed.'); | ||
return; | ||
} | ||
@@ -38,0 +40,0 @@ |
@@ -58,4 +58,3 @@ /** | ||
var Syntax = jsdoc.src.Syntax; | ||
if (type === Syntax.FunctionDeclaration || type === Syntax.FunctionExpression || | ||
type === 'function') { | ||
if (type === Syntax.FunctionDeclaration || type === Syntax.FunctionExpression) { | ||
return 'function'; | ||
@@ -180,3 +179,2 @@ } | ||
if (!this.kind && this.meta && this.meta.code) { | ||
//console.log('adding kind to: %j', this.meta.code); | ||
this.addTag( 'kind', codetypeToKind(this.meta.code.type) ); | ||
@@ -339,3 +337,3 @@ } | ||
} | ||
/** | ||
@@ -342,0 +340,0 @@ * Information about the code symbol. |
/** | ||
Parse the command line arguments. | ||
@module jsdoc/opts/argparser | ||
@author Michael Mathews <micmath@gmail.com> | ||
@license Apache License 2.0 - See file 'LICENSE.md' in this project. | ||
* Parse the command line arguments. | ||
* @module jsdoc/opts/argparser | ||
* @author Michael Mathews <micmath@gmail.com> | ||
* @license Apache License 2.0 - See file 'LICENSE.md' in this project. | ||
*/ | ||
@@ -14,27 +14,43 @@ 'use strict'; | ||
/** | ||
Create an instance of the parser. | ||
@classdesc A parser to interpret the key-value pairs entered on the command | ||
line. | ||
@constructor | ||
* Create an instance of the parser. | ||
* @classdesc A parser to interpret the key-value pairs entered on the command line. | ||
* @constructor | ||
*/ | ||
var ArgParser = function() { | ||
this._options = []; | ||
this._shortNameIndex = {}; | ||
this._longNameIndex = {}; | ||
this._options = []; | ||
this._shortNameIndex = {}; | ||
this._longNameIndex = {}; | ||
}; | ||
ArgParser.prototype._getOptionByShortName = function(name) { | ||
if (hasOwnProp.call(this._shortNameIndex, name)) { | ||
return this._options[this._shortNameIndex[name]]; | ||
} | ||
return null; | ||
if (hasOwnProp.call(this._shortNameIndex, name)) { | ||
return this._options[this._shortNameIndex[name]]; | ||
} | ||
return null; | ||
}; | ||
ArgParser.prototype._getOptionByLongName = function(name) { | ||
if (hasOwnProp.call(this._longNameIndex, name)) { | ||
return this._options[this._longNameIndex[name]]; | ||
} | ||
return null; | ||
if (hasOwnProp.call(this._longNameIndex, name)) { | ||
return this._options[this._longNameIndex[name]]; | ||
} | ||
return null; | ||
}; | ||
ArgParser.prototype._addOption = function(option) { | ||
var currentIndex; | ||
var longName = option.longName; | ||
var shortName = option.shortName; | ||
this._options.push(option); | ||
currentIndex = this._options.length - 1; | ||
if (shortName) { | ||
this._shortNameIndex[shortName] = currentIndex; | ||
} | ||
if (longName) { | ||
this._longNameIndex[longName] = currentIndex; | ||
} | ||
}; | ||
/** | ||
@@ -53,130 +69,232 @@ * Provide information about a legal option. | ||
ArgParser.prototype.addOption = function(shortName, longName, hasValue, helpText, canHaveMultiple, coercer) { | ||
this._options.push({ | ||
shortName: shortName, | ||
longName: longName, | ||
hasValue: hasValue, | ||
helpText: helpText, | ||
canHaveMultiple: (canHaveMultiple || false), | ||
coercer: coercer | ||
}); | ||
if (shortName) { | ||
this._shortNameIndex[shortName] = this._options.length - 1; | ||
} | ||
if (longName) { | ||
this._longNameIndex[longName] = this._options.length - 1; | ||
} | ||
var option = { | ||
shortName: shortName, | ||
longName: longName, | ||
hasValue: hasValue, | ||
helpText: helpText, | ||
canHaveMultiple: (canHaveMultiple || false), | ||
coercer: coercer | ||
}; | ||
this._addOption(option); | ||
}; | ||
// TODO: refactor addOption to accept objects, then get rid of this method | ||
/** | ||
Generate a summary of all the options with corresponding help text. | ||
@returns {string} | ||
* Provide information about an option that should not cause an error if present, but that is always | ||
* ignored (for example, an option that was used in previous versions but is no longer supported). | ||
* | ||
* @private | ||
* @param {string} shortName - The short name of the option with a leading hyphen (for example, | ||
* `-v`). | ||
* @param {string} longName - The long name of the option with two leading hyphens (for example, | ||
* `--version`). | ||
*/ | ||
ArgParser.prototype.addIgnoredOption = function(shortName, longName) { | ||
var option = { | ||
shortName: shortName, | ||
longName: longName, | ||
ignore: true | ||
}; | ||
this._addOption(option); | ||
}; | ||
function padding(length) { | ||
return new Array(length + 1).join(' '); | ||
} | ||
function padLeft(str, length) { | ||
return padding(length) + str; | ||
} | ||
function padRight(str, length) { | ||
return str + padding(length); | ||
} | ||
function findMaxLength(arr) { | ||
var max = 0; | ||
arr.forEach(function(item) { | ||
if (item.length > max) { | ||
max = item.length; | ||
} | ||
}); | ||
return max; | ||
} | ||
function concatWithMaxLength(items, maxLength) { | ||
var result = ''; | ||
// to prevent endless loops, always use the first item, regardless of length | ||
result += items.shift(); | ||
while ( items.length && (result.length + items[0].length < maxLength) ) { | ||
result += ' ' + items.shift(); | ||
} | ||
return result; | ||
} | ||
// we want to format names and descriptions like this: | ||
// | -f, --foo Very long description very long description very long | | ||
// | description very long description. | | ||
function formatHelpInfo(options) { | ||
var MARGIN_LENGTH = 4; | ||
var results = []; | ||
var maxLength = process.stdout.columns; | ||
var maxNameLength = findMaxLength(options.names); | ||
var maxDescriptionLength = findMaxLength(options.descriptions); | ||
var wrapDescriptionAt = maxLength - (MARGIN_LENGTH * 3) - maxNameLength; | ||
// build the string for each option | ||
options.names.forEach(function(name, i) { | ||
var result; | ||
var partialDescription; | ||
var words; | ||
// add a left margin to the name | ||
result = padLeft(options.names[i], MARGIN_LENGTH); | ||
// and a right margin, with extra padding so the descriptions line up with one another | ||
result = padRight(result, maxNameLength - options.names[i].length + MARGIN_LENGTH); | ||
// split the description on spaces | ||
words = options.descriptions[i].split(' '); | ||
// add as much of the description as we can fit on the first line | ||
result += concatWithMaxLength(words, wrapDescriptionAt); | ||
// if there's anything left, keep going until we've consumed the description | ||
while (words.length) { | ||
partialDescription = padding( maxNameLength + (MARGIN_LENGTH * 2) ); | ||
partialDescription += concatWithMaxLength(words, wrapDescriptionAt); | ||
result += '\n' + partialDescription; | ||
} | ||
results.push(result); | ||
}); | ||
return results; | ||
} | ||
/** | ||
* Generate a summary of all the options with corresponding help text. | ||
* @returns {string} | ||
*/ | ||
ArgParser.prototype.help = function() { | ||
var helpArr = ['OPTIONS:'], | ||
option, optionHelp; | ||
var options = { | ||
names: [], | ||
descriptions: [] | ||
}; | ||
for (var i = 0, leni = this._options.length; i < leni; i++) { | ||
option = this._options[i]; | ||
optionHelp = '\t'; | ||
this._options.forEach(function(option) { | ||
var name = ''; | ||
if (option.shortName) { | ||
optionHelp += '-' + option.shortName + (option.longName ? ', ' : ''); | ||
} | ||
// don't show ignored options | ||
if (option.ignore) { | ||
return; | ||
} | ||
if (option.longName) { | ||
optionHelp += '--' + option.longName; | ||
} | ||
if (option.shortName) { | ||
name += '-' + option.shortName + (option.longName ? ', ' : ''); | ||
} | ||
if (option.hasValue) { | ||
optionHelp += ' <value>'; | ||
} | ||
if (option.longName) { | ||
name += '--' + option.longName; | ||
} | ||
optionHelp += '\t\t' + option.helpText; | ||
helpArr.push(optionHelp); | ||
} | ||
if (option.hasValue) { | ||
name += ' <value>'; | ||
} | ||
return helpArr.join('\n'); | ||
options.names.push(name); | ||
options.descriptions.push(option.helpText); | ||
}); | ||
return 'Options:\n' + formatHelpInfo(options).join('\n'); | ||
}; | ||
/** | ||
Get the options. | ||
@param {Array.<string>} args An array, like ['-x', 'hello'] | ||
@param {Object} [defaults={}] An optional collection of default values. | ||
@returns {Object} The keys will be the longNames, or the shortName if | ||
no longName is defined for that option. The values will be the values | ||
provided, or `true` if the option accepts no value. | ||
* Get the options. | ||
* @param {Array.<string>} args An array, like ['-x', 'hello'] | ||
* @param {Object} [defaults={}] An optional collection of default values. | ||
* @returns {Object} The keys will be the longNames, or the shortName if no longName is defined for | ||
* that option. The values will be the values provided, or `true` if the option accepts no value. | ||
*/ | ||
ArgParser.prototype.parse = function(args, defaults) { | ||
var result = defaults && _.defaults({}, defaults) || {}; | ||
var result = defaults && _.defaults({}, defaults) || {}; | ||
result._ = []; | ||
for (var i = 0, leni = args.length; i < leni; i++) { | ||
var arg = '' + args[i], | ||
next = (i < leni-1)? '' + args[i+1] : null, | ||
option, | ||
shortName = null, | ||
longName, | ||
name, | ||
value = null; | ||
result._ = []; | ||
for (var i = 0, leni = args.length; i < leni; i++) { | ||
var arg = '' + args[i], | ||
next = (i < leni-1)? '' + args[i+1] : null, | ||
option, | ||
shortName = null, | ||
longName, | ||
name, | ||
value = null; | ||
// like -t | ||
if (arg.charAt(0) === '-') { | ||
// like -t | ||
if (arg.charAt(0) === '-') { | ||
// like --template | ||
if (arg.charAt(1) === '-') { | ||
name = longName = arg.slice(2); | ||
option = this._getOptionByLongName(longName); | ||
} | ||
else { | ||
name = shortName = arg.slice(1); | ||
option = this._getOptionByShortName(shortName); | ||
} | ||
// like --template | ||
if (arg.charAt(1) === '-') { | ||
name = longName = arg.slice(2); | ||
option = this._getOptionByLongName(longName); | ||
} | ||
else { | ||
name = shortName = arg.slice(1); | ||
option = this._getOptionByShortName(shortName); | ||
} | ||
if (option === null) { | ||
throw new Error( 'Unknown command line option found: ' + name ); | ||
} | ||
if (option === null) { | ||
throw new Error( 'Unknown command line option found: ' + name ); | ||
} | ||
if (option.hasValue) { | ||
value = next; | ||
i++; | ||
if (option.hasValue) { | ||
value = next; | ||
i++; | ||
if (value === null || value.charAt(0) === '-') { | ||
throw new Error( 'Command line option requires a value: ' + name ); | ||
} | ||
} | ||
else { | ||
value = true; | ||
} | ||
if (value === null || value.charAt(0) === '-') { | ||
throw new Error( 'Command line option requires a value: ' + name ); | ||
} | ||
} | ||
else { | ||
value = true; | ||
} | ||
if (option.longName && shortName) { | ||
name = option.longName; | ||
} | ||
// skip ignored options now that we've consumed the option text | ||
if (option.ignore) { | ||
continue; | ||
} | ||
if (typeof option.coercer === 'function') { | ||
value = option.coercer(value); | ||
} | ||
// Allow for multiple options of the same type to be present | ||
if (option.canHaveMultiple && hasOwnProp.call(result, name)) { | ||
var val = result[name]; | ||
if (val instanceof Array) { | ||
val.push(value); | ||
} else { | ||
result[name] = [val, value]; | ||
} | ||
} | ||
else { | ||
result[name] = value; | ||
} | ||
} | ||
else { | ||
result._.push(arg); | ||
} | ||
} | ||
if (option.longName && shortName) { | ||
name = option.longName; | ||
} | ||
return result; | ||
if (typeof option.coercer === 'function') { | ||
value = option.coercer(value); | ||
} | ||
// Allow for multiple options of the same type to be present | ||
if (option.canHaveMultiple && hasOwnProp.call(result, name)) { | ||
var val = result[name]; | ||
if (val instanceof Array) { | ||
val.push(value); | ||
} else { | ||
result[name] = [val, value]; | ||
} | ||
} | ||
else { | ||
result[name] = value; | ||
} | ||
} | ||
else { | ||
result._.push(arg); | ||
} | ||
} | ||
return result; | ||
}; | ||
module.exports = ArgParser; |
/** | ||
@module jsdoc/opts/args | ||
@requires jsdoc/opts/argparser | ||
@author Michael Mathews <micmath@gmail.com> | ||
@license Apache License 2.0 - See file 'LICENSE.md' in this project. | ||
* @module jsdoc/opts/args | ||
* @requires jsdoc/opts/argparser | ||
* @author Michael Mathews <micmath@gmail.com> | ||
* @license Apache License 2.0 - See file 'LICENSE.md' in this project. | ||
*/ | ||
'use strict'; | ||
var ArgParser = require('jsdoc/opts/argparser'), | ||
argParser = new ArgParser(), | ||
hasOwnProp = Object.prototype.hasOwnProperty, | ||
ourOptions, | ||
querystring = require('querystring'), | ||
util = require('util'); | ||
var ArgParser = require('jsdoc/opts/argparser'); | ||
var querystring = require('querystring'); | ||
var util = require('util'); | ||
var ourOptions; | ||
var argParser = new ArgParser(); | ||
var hasOwnProp = Object.prototype.hasOwnProperty; | ||
// cast strings to booleans or integers where appropriate | ||
function castTypes(item) { | ||
var result = item; | ||
var integer; | ||
switch (result) { | ||
case 'true': | ||
return true; | ||
case 'false': | ||
return false; | ||
default: | ||
// might be an integer | ||
var integer = parseInt(result, 10); | ||
if (String(integer) === result && integer !== 'NaN') { | ||
return integer; | ||
} else { | ||
return result; | ||
} | ||
} | ||
var result = item; | ||
switch (result) { | ||
case 'true': | ||
result = true; | ||
break; | ||
case 'false': | ||
result = false; | ||
break; | ||
default: | ||
// might be an integer | ||
integer = parseInt(result, 10); | ||
if (String(integer) === result && integer !== 'NaN') { | ||
result = integer; | ||
} | ||
} | ||
return result; | ||
} | ||
@@ -39,28 +46,30 @@ | ||
function fixTypes(item) { | ||
var result = item; | ||
var result = item; | ||
// recursively process arrays and objects | ||
if ( util.isArray(result) ) { | ||
for (var i = 0, l = result.length; i < l; i++) { | ||
result[i] = fixTypes(result[i]); | ||
} | ||
} else if (typeof result === 'object') { | ||
Object.keys(result).forEach(function(prop) { | ||
result[prop] = fixTypes(result[prop]); | ||
}); | ||
} else { | ||
result = castTypes(result); | ||
} | ||
// recursively process arrays and objects | ||
if ( util.isArray(result) ) { | ||
for (var i = 0, l = result.length; i < l; i++) { | ||
result[i] = fixTypes(result[i]); | ||
} | ||
} | ||
else if (typeof result === 'object') { | ||
Object.keys(result).forEach(function(prop) { | ||
result[prop] = fixTypes(result[prop]); | ||
}); | ||
} | ||
else { | ||
result = castTypes(result); | ||
} | ||
return result; | ||
return result; | ||
} | ||
function parseQuery(str) { | ||
var result = querystring.parse(str); | ||
var result = querystring.parse(str); | ||
Object.keys(result).forEach(function(prop) { | ||
result[prop] = fixTypes(result[prop]); | ||
}); | ||
Object.keys(result).forEach(function(prop) { | ||
result[prop] = fixTypes(result[prop]); | ||
}); | ||
return result; | ||
return result; | ||
} | ||
@@ -75,3 +84,2 @@ | ||
argParser.addOption('r', 'recurse', false, 'Recurse into subdirectories when scanning for source code files.'); | ||
argParser.addOption('l', 'lenient', false, 'Continue to generate output if a doclet is incomplete or contains errors. Default: false'); | ||
argParser.addOption('h', 'help', false, 'Print this message and quit.'); | ||
@@ -82,30 +90,32 @@ argParser.addOption('X', 'explain', false, 'Dump all found doclet internals to console and quit.'); | ||
argParser.addOption('v', 'version', false, 'Display the version number and quit.'); | ||
argParser.addOption('', 'debug', false, 'Log information for debugging JSDoc. On Rhino, launches the debugger when passed as the first option.'); | ||
argParser.addOption('', 'verbose', false, 'Log detailed information to the console as JSDoc runs.'); | ||
argParser.addOption('', 'pedantic', false, 'Treat errors as fatal errors, and treat warnings as errors. Default: false'); | ||
//TODO [-R, recurseonly] = a number representing the depth to recurse | ||
//TODO [-f, filter] = a regex to filter on <-- this can be better defined in the configs? | ||
// Options specific to tests | ||
argParser.addOption(null, 'match', true, 'Only run tests containing <value>.', true); | ||
argParser.addOption(null, 'nocolor', false, 'Do not use color in console output from tests.'); | ||
//Here are options specific to tests | ||
argParser.addOption(null, 'verbose', false, 'Display verbose output for tests'); | ||
argParser.addOption(null, 'match', true, 'Only run tests containing <value>', true); | ||
argParser.addOption(null, 'nocolor', false, 'Do not use color in console output from tests'); | ||
// Options that are no longer supported and should be ignored | ||
argParser.addIgnoredOption('l', 'lenient'); // removed in JSDoc 3.3.0 | ||
/** | ||
Set the options for this app. | ||
@throws {Error} Illegal arguments will throw errors. | ||
@param {string|String[]} args The command line arguments for this app. | ||
* Set the options for this app. | ||
* @throws {Error} Illegal arguments will throw errors. | ||
* @param {string|String[]} args The command line arguments for this app. | ||
*/ | ||
exports.parse = function(args) { | ||
args = args || []; | ||
args = args || []; | ||
if (typeof args === 'string' || args.constructor === String) { | ||
args = (''+args).split(/\s+/g); | ||
} | ||
if (typeof args === 'string' || args.constructor === String) { | ||
args = (''+args).split(/\s+/g); | ||
} | ||
ourOptions = argParser.parse(args); | ||
ourOptions = argParser.parse(args); | ||
return ourOptions; | ||
return ourOptions; | ||
}; | ||
/** | ||
Display help message for options. | ||
* Retrieve help message for options. | ||
*/ | ||
@@ -117,16 +127,16 @@ exports.help = function() { | ||
/** | ||
Get a named option. | ||
@param {string} name The name of the option. | ||
@return {string} The value associated with the given name. | ||
* Get a named option. | ||
* @param {string} name The name of the option. | ||
* @return {string} The value associated with the given name. | ||
*//** | ||
Get all the options for this app. | ||
@return {Object} A collection of key/values representing all the options. | ||
* Get all the options for this app. | ||
* @return {Object} A collection of key/values representing all the options. | ||
*/ | ||
exports.get = function(name) { | ||
if (typeof name === 'undefined') { | ||
return ourOptions; | ||
} | ||
else { | ||
return ourOptions[name]; | ||
} | ||
if (typeof name === 'undefined') { | ||
return ourOptions; | ||
} | ||
else if ( hasOwnProp.call(ourOptions, name) ) { | ||
return ourOptions[name]; | ||
} | ||
}; |
@@ -21,4 +21,3 @@ /*global env: true */ | ||
// TODO: should probably replace process.cwd() with the CWD before launching JSDoc | ||
currentPath = path.resolve( process.cwd(), path.dirname(current) ).split(path.sep) || []; | ||
currentPath = path.resolve(global.env.pwd, current).split(path.sep) || []; | ||
@@ -72,29 +71,8 @@ if (previousPath && currentPath.length) { | ||
// TODO: can we get rid of this? | ||
/** | ||
* If required by the current VM, convert a path to a URI that meets the operating system's | ||
* requirements. Otherwise, return the original path. | ||
* @function | ||
* @private | ||
* @param {string} path The path to convert. | ||
* @return {string} A URI that meets the operating system's requirements, or the original path. | ||
*/ | ||
var pathToUri = require( runtime.getModulePath('path') ).pathToUri; | ||
// TODO: can we get rid of this, or at least stop exporting it? | ||
/** | ||
* If required by the current VM, convert a URI to a path that meets the operating system's | ||
* requirements. Otherwise, assume the "URI" is really a path, and return the original path. | ||
* @function | ||
* @private | ||
* @param {string} uri The URI to convert. | ||
* @return {string} A path that meets the operating system's requirements. | ||
*/ | ||
exports._uriToPath = require( runtime.getModulePath('path') ).uriToPath; | ||
/** | ||
* Retrieve the fully qualified path to the requested resource. | ||
* | ||
* If the resource path is specified as a relative path, JSDoc searches for the path in the current | ||
* working directory, then in the JSDoc directory. | ||
* If the resource path is specified as a relative path, JSDoc searches for the path in the | ||
* directory where the JSDoc configuration file is located, then in the current working directory, | ||
* and finally in the JSDoc directory. | ||
* | ||
@@ -110,3 +88,3 @@ * If the resource path is specified as a fully qualified path, JSDoc uses the path as-is. | ||
exports.getResourcePath = function(filepath, filename) { | ||
var result; | ||
var result = null; | ||
@@ -124,16 +102,14 @@ function pathExists(_path) { | ||
// first, try resolving it relative to the working directory (or just normalize it if it's an | ||
// absolute path) | ||
result = path.resolve(env.pwd, filepath); | ||
if ( !pathExists(result) ) { | ||
// next, try resolving it relative to the JSDoc directory | ||
result = path.resolve(env.dirname, filepath); | ||
if ( !pathExists(result) ) { | ||
result = null; | ||
// absolute paths are normalized by path.resolve on the first pass | ||
[path.dirname(global.env.opts.configure || ''), env.pwd, env.dirname].forEach(function(_path) { | ||
if (!result && _path) { | ||
_path = path.resolve(_path, filepath); | ||
if ( pathExists(_path) ) { | ||
result = _path; | ||
} | ||
} | ||
} | ||
}); | ||
if (result) { | ||
result = filename ? path.join(result, filename) : result; | ||
result = pathToUri(result); | ||
} | ||
@@ -140,0 +116,0 @@ |
@@ -8,3 +8,3 @@ /*global app: true */ | ||
var error = require('jsdoc/util/error'); | ||
var logger = require('jsdoc/util/logger'); | ||
var path = require('jsdoc/path'); | ||
@@ -17,14 +17,6 @@ | ||
var plugin; | ||
var pluginPath; | ||
for (var i = 0, l = plugins.length; i < l; i++) { | ||
pluginPath = path.getResourcePath(path.dirname(plugins[i]), path.basename(plugins[i])); | ||
plugin = require(plugins[i]); | ||
if (!pluginPath) { | ||
error.handle(new Error('Unable to find the plugin "' + plugins[i] + '"')); | ||
continue; | ||
} | ||
plugin = require(pluginPath); | ||
// allow user-defined plugins to... | ||
@@ -46,4 +38,4 @@ //...register event handlers | ||
if ( !parser.addNodeVisitor ) { | ||
error.handle( new Error('Unable to add the Rhino node visitor from ' + plugins[i] + | ||
', because JSDoc is not using the Rhino JavaScript parser.') ); | ||
logger.error('Unable to add the Rhino node visitor from %s, because JSDoc ' + | ||
'is not using the Rhino JavaScript parser.', plugins[i]); | ||
} | ||
@@ -50,0 +42,0 @@ else { |
/** | ||
@overview Schema for validating JSON produced by JSDoc Toolkit. | ||
@author Michael Mathews <micmath@gmail.com> | ||
@license Apache License 2.0 - See file 'LICENSE.md' in this project. | ||
@see <http://tools.ietf.org/html/draft-zyp-json-schema-02> | ||
* @overview Schema for validating JSDoc doclets. | ||
* | ||
* @author Michael Mathews <micmath@gmail.com> | ||
* @author Jeff Williams <jeffrey.l.williams@gmail.com> | ||
* @license Apache License 2.0 - See file 'LICENSE.md' in this project. | ||
* @see <http://tools.ietf.org/html/draft-zyp-json-schema-03> | ||
*/ | ||
'use strict'; | ||
exports.jsdocSchema = { | ||
"properties": { | ||
"doc": { | ||
"type": "array", | ||
"items": { | ||
"type": "object", | ||
"additionalProperties": false, | ||
"properties": { | ||
"author": { | ||
"type": ["string", "array"], | ||
"optional": true, | ||
"items": { | ||
"type": "string" | ||
} | ||
// JSON schema types | ||
var ARRAY = 'array'; | ||
var BOOLEAN = 'boolean'; | ||
var INTEGER = 'integer'; | ||
var NULL = 'null'; | ||
var NUMBER = 'number'; | ||
var OBJECT = 'object'; | ||
var STRING = 'string'; | ||
var UNDEFINED = 'undefined'; | ||
var EVENT_REGEXP = /event\:[\S]+/; | ||
var PACKAGE_REGEXP = /package\:[\S]+/; | ||
// information about the code associated with a doclet | ||
var META_SCHEMA = exports.META_SCHEMA = { | ||
type: OBJECT, | ||
optional: true, | ||
additionalProperties: false, | ||
properties: { | ||
code: { | ||
type: OBJECT, | ||
additionalProperties: false, | ||
properties: { | ||
funcscope: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
id: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
name: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
node: { | ||
type: OBJECT, | ||
optional: true | ||
}, | ||
paramnames: { | ||
type: ARRAY, | ||
optional: true, | ||
uniqueItems: true, | ||
items: { | ||
type: STRING | ||
} | ||
}, | ||
type: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
value: { | ||
optional: true | ||
} | ||
} | ||
}, | ||
filename: { | ||
title: 'The name of the file that contains the code associated with this doclet.', | ||
type: STRING, | ||
optional: true | ||
}, | ||
lineno: { | ||
title: 'The line number of the code associated with this doclet.', | ||
type: NUMBER, | ||
optional: true | ||
}, | ||
path: { | ||
title: 'The path in which the code associated with this doclet is located.', | ||
type: STRING, | ||
optional: true | ||
}, | ||
range: { | ||
title: 'The positions of the first and last characters of the code associated with ' + | ||
'this doclet.', | ||
type: ARRAY, | ||
optional: true, | ||
minItems: 2, | ||
maxItems: 2, | ||
items: { | ||
type: NUMBER | ||
} | ||
}, | ||
vars: { | ||
type: OBJECT | ||
} | ||
} | ||
}; | ||
// type property containing type names | ||
var TYPE_PROPERTY_SCHEMA = exports.TYPE_PROPERTY_SCHEMA = { | ||
type: OBJECT, | ||
additionalProperties: false, | ||
properties: { | ||
names: { | ||
type: ARRAY, | ||
minItems: 1, | ||
items: { | ||
type: STRING | ||
} | ||
} | ||
} | ||
}; | ||
// enumeration properties | ||
var ENUM_PROPERTY_SCHEMA = exports.ENUM_PROPERTY_SCHEMA = { | ||
type: OBJECT, | ||
additionalProperties: false, | ||
properties: { | ||
comment: { | ||
type: STRING | ||
}, | ||
defaultvalue: { | ||
// TODO: stop adding property if it's empty | ||
type: [STRING, NULL, UNDEFINED], | ||
optional: true | ||
}, | ||
description: { | ||
// TODO: stop adding property if it's empty | ||
type: [STRING, NULL, UNDEFINED], | ||
optional: true | ||
}, | ||
kind: { | ||
type: STRING, | ||
// TODO: get this from a real enum somewhere | ||
enum: ['member'] | ||
}, | ||
longname: { | ||
type: STRING | ||
}, | ||
memberof: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
meta: META_SCHEMA, | ||
name: { | ||
type: STRING | ||
}, | ||
scope: { | ||
type: STRING, | ||
// TODO: get this from a real enum somewhere | ||
enum: ['static'] | ||
}, | ||
type: TYPE_PROPERTY_SCHEMA | ||
} | ||
}; | ||
// function parameter, or object property defined with @property tag | ||
var PARAM_SCHEMA = exports.PARAM_SCHEMA = { | ||
type: OBJECT, | ||
additionalProperties: false, | ||
properties: { | ||
// what is the default value for this parameter? | ||
defaultvalue: { | ||
// TODO: stop adding property if it's empty | ||
type: [STRING, NULL, UNDEFINED], | ||
optional: true | ||
}, | ||
// a description of the parameter | ||
description: { | ||
// TODO: stop adding property if it's empty | ||
type: [STRING, NULL, UNDEFINED], | ||
optional: true | ||
}, | ||
// what name does this parameter have within the function? | ||
name: { | ||
type: STRING | ||
}, | ||
// can the value for this parameter be null? | ||
nullable: { | ||
// TODO: stop adding property if it's empty | ||
type: [BOOLEAN, NULL, UNDEFINED], | ||
optional: true | ||
}, | ||
// is a value for this parameter optional? | ||
optional: { | ||
// TODO: stop adding property if it's empty | ||
type: [BOOLEAN, NULL, UNDEFINED], | ||
optional: true | ||
}, | ||
// what are the types of value expected for this parameter? | ||
type: TYPE_PROPERTY_SCHEMA, | ||
// can this parameter be repeated? | ||
variable: { | ||
// TODO: stop adding property if it's empty | ||
type: [BOOLEAN, NULL, UNDEFINED], | ||
optional: true | ||
} | ||
} | ||
}; | ||
var DOCLET_SCHEMA = exports.DOCLET_SCHEMA = { | ||
type: OBJECT, | ||
additionalProperties: false, | ||
properties: { | ||
// what access privileges are allowed | ||
access: { | ||
type: STRING, | ||
optional: true, | ||
// TODO: define this as an enumeration elsewhere | ||
enum: [ | ||
'private', | ||
'protected' | ||
] | ||
}, | ||
alias: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
augments: { | ||
type: ARRAY, | ||
optional: true, | ||
uniqueItems: true, | ||
items: { | ||
type: STRING | ||
} | ||
}, | ||
author: { | ||
type: ARRAY, | ||
optional: true, | ||
items: { | ||
type: STRING | ||
} | ||
}, | ||
borrowed: { | ||
type: ARRAY, | ||
optional: true, | ||
uniqueItems: true, | ||
items: { | ||
type: OBJECT, | ||
additionalProperties: false, | ||
properties: { | ||
// name of the target | ||
as: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
"path": { // unique identifier for each doc | ||
"type": "string", | ||
"maxItems": 1 | ||
// name of the source | ||
from: { | ||
type: STRING | ||
} | ||
} | ||
} | ||
}, | ||
// a description of the class that this constructor belongs to | ||
classdesc: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
comment: { | ||
type: STRING | ||
}, | ||
copyright: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
defaultvalue: { | ||
optional: true | ||
}, | ||
defaultvaluetype: { | ||
type: STRING, | ||
optional: true, | ||
enum: [OBJECT] | ||
}, | ||
// is usage of this symbol deprecated? | ||
deprecated: { | ||
type: [STRING, BOOLEAN], | ||
optional: true | ||
}, | ||
// a description | ||
description: { | ||
// TODO: stop adding property if it's empty | ||
type: [STRING, NULL, UNDEFINED], | ||
optional: true | ||
}, | ||
// something else to consider | ||
examples: { | ||
type: ARRAY, | ||
optional: true, | ||
items: { | ||
type: STRING | ||
} | ||
}, | ||
exceptions: { | ||
type: ARRAY, | ||
optional: true, | ||
items: PARAM_SCHEMA | ||
}, | ||
// the path to another constructor | ||
extends: { | ||
type: ARRAY, | ||
optional: true, | ||
uniqueItems: true, | ||
items: { | ||
type: STRING | ||
} | ||
}, | ||
// the path to another doc object | ||
fires: { | ||
type: ARRAY, | ||
optional: true, | ||
uniqueItems: true, | ||
items: { | ||
type: STRING, | ||
pattern: EVENT_REGEXP | ||
} | ||
}, | ||
forceMemberof: { | ||
// TODO: stop adding property if it's empty | ||
type: [BOOLEAN, NULL, UNDEFINED], | ||
optional: true | ||
}, | ||
ignore: { | ||
type: BOOLEAN, | ||
optional: true | ||
}, | ||
implements: { | ||
type: ARRAY, | ||
optional: true, | ||
uniqueItems: true, | ||
items: { | ||
type: STRING | ||
} | ||
}, | ||
inherited: { | ||
type: BOOLEAN, | ||
optional: true | ||
}, | ||
inherits: { | ||
type: STRING, | ||
optional: true, | ||
dependency: { | ||
inherited: true | ||
} | ||
}, | ||
isEnum: { | ||
type: BOOLEAN, | ||
optional: true | ||
}, | ||
// what kind of symbol is this? | ||
kind: { | ||
type: STRING, | ||
// TODO: define this as an enumeration elsewhere | ||
enum: [ | ||
'class', | ||
'constant', | ||
'event', | ||
'external', | ||
'file', | ||
'function', | ||
'member', | ||
'mixin', | ||
'module', | ||
'namespace', | ||
'package', | ||
'typedef' | ||
] | ||
}, | ||
license: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
listens: { | ||
type: ARRAY, | ||
optional: true, | ||
uniqueItems: true, | ||
items: { | ||
type: STRING, | ||
pattern: EVENT_REGEXP | ||
} | ||
}, | ||
longname: { | ||
type: STRING | ||
}, | ||
// probably a leading substring of the path | ||
memberof: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
// information about this doc | ||
meta: META_SCHEMA, | ||
mixes: { | ||
type: ARRAY, | ||
optional: true, | ||
uniqueItems: true, | ||
items: { | ||
type: STRING | ||
} | ||
}, | ||
// probably a trailing substring of the path | ||
name: { | ||
type: STRING | ||
}, | ||
// are there function parameters associated with this doc? | ||
params: { | ||
type: ARRAY, | ||
optional: true, | ||
uniqueItems: true, | ||
items: PARAM_SCHEMA | ||
}, | ||
preserveName: { | ||
type: BOOLEAN, | ||
optional: true | ||
}, | ||
properties: { | ||
type: ARRAY, | ||
optional: true, | ||
uniqueItems: true, | ||
minItems: 1, | ||
items: { | ||
anyOf: [ENUM_PROPERTY_SCHEMA, PARAM_SCHEMA] | ||
} | ||
}, | ||
readonly: { | ||
type: BOOLEAN, | ||
optional: true | ||
}, | ||
// the symbol being documented requires another symbol | ||
requires: { | ||
type: ARRAY, | ||
optional: true, | ||
uniqueItems: true, | ||
minItems: 1, | ||
items: { | ||
type: STRING | ||
} | ||
}, | ||
returns: { | ||
type: ARRAY, | ||
optional: true, | ||
minItems: 1, | ||
items: PARAM_SCHEMA | ||
}, | ||
// what sort of parent scope does this symbol have? | ||
scope: { | ||
type: STRING, | ||
enum: [ | ||
// TODO: make these an enumeration | ||
'global', | ||
'inner', | ||
'instance', | ||
'static' | ||
] | ||
}, | ||
// something else to consider | ||
see: { | ||
type: ARRAY, | ||
optional: true, | ||
minItems: 1, | ||
items: { | ||
type: STRING | ||
} | ||
}, | ||
// at what previous version was this doc added? | ||
since: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
summary: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
// arbitrary tags associated with this doc | ||
tags: { | ||
type: ARRAY, | ||
optional: true, | ||
minItems: 1, | ||
items: { | ||
type: OBJECT, | ||
additionalProperties: false, | ||
properties: { | ||
originalTitle: { | ||
type: STRING | ||
}, | ||
"description": { // a description | ||
"type": "string", | ||
"optional": true, | ||
"maxItems": 1 | ||
text: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
"classdesc": { // a description of the class that this constructor belongs to | ||
"type": "string", | ||
"optional": true, | ||
"maxItems": 1 | ||
title: { | ||
type: STRING | ||
}, | ||
"name": { // probably a trailing substring of the path | ||
"type": "string", | ||
"maxItems": 1 | ||
}, | ||
"version": { // what is the version of this doc | ||
"type": "string", | ||
"optional": true, | ||
"maxItems": 1 | ||
}, | ||
"since": { // at what previous version was this doc added? | ||
"type": "string", | ||
"optional": true, | ||
"maxItems": 1 | ||
}, | ||
"see": { // some thing else to consider | ||
"type": ["string", "array"], | ||
"optional": true, | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"tutorials": { // extended tutorials | ||
"type": ["string", "array"], | ||
"optional": true, | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"deprecated": { // is usage of this symbol deprecated? | ||
"type": ["string", "boolean"], | ||
"optional": true | ||
}, | ||
"scope": { // how is this symbol attached to it's enclosing scope? | ||
"type": "string", | ||
"maxItems": 1, | ||
"enum": ["global", "static", "instance", "inner"] | ||
}, | ||
"memberof": { // probably a leading substring of the path | ||
"type": "string", | ||
"optional": true, | ||
"maxItems": 1 | ||
}, | ||
"extends": { // the path to another constructor | ||
"type": ["string", "array"], | ||
"optional": true, | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"fires": { // the path to another doc object | ||
"type": ["string", "array"], | ||
"optional": true, | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"requires": { // the symbol being documented requires another symbol | ||
"type": ["string", "array"], | ||
"optional": true, | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"implements": { | ||
"type": ["string", "array"], | ||
"optional": true, | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"kind": { // what kind of symbol is this? | ||
"type": "string", | ||
"maxItems": 1, | ||
"enum": ["constructor", "module", "event", "namespace", "method", "member", "enum", "class", "interface", "constant", "mixin", "file", "version"] | ||
}, | ||
"refersto": { // the path to another doc: this doc is simply a renamed alias to that | ||
"type": "string", | ||
"optional": true, | ||
"maxItems": 1 | ||
}, | ||
"access": { // what access priviledges are allowed | ||
"type": "string", | ||
"optional": true, | ||
"maxItems": 1, | ||
"enum": ["private", "protected", "public"] | ||
}, | ||
"virtual": { // is a member left to be implemented during inheritance? | ||
"type": "boolean", | ||
"optional": true, | ||
"default": false | ||
}, | ||
"attrib": { // other attributes, like "readonly" | ||
"type": "string", | ||
"optional": true | ||
}, | ||
"type": { // what type is the value that this doc is associated with, like "number" | ||
"type": ["string", "array"], | ||
"optional": true, | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"exception" : { | ||
"optional": true, | ||
"type": "object", | ||
"properties": { | ||
"type": { // what is the type of the value thrown? | ||
"type": "array", | ||
"optional": true, | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"description": { // a description of the thrown value | ||
"type": "string", | ||
"optional": true | ||
} | ||
}, | ||
"additionalProperties": false | ||
}, | ||
"returns" : { | ||
"optional": true, | ||
"type": "object", | ||
"properties": { | ||
"type": { // what is the type of the value returned? | ||
"type": ["string", "array"], | ||
"optional": true, | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"description": { // a description of the returned value | ||
"type": "string", | ||
"optional": true | ||
} | ||
}, | ||
"additionalProperties": false | ||
}, | ||
"param" : { // are there function parameters associated with this doc? | ||
"type": "array", | ||
"optional": true, | ||
"items": { | ||
"type": "object", | ||
"properties": { | ||
"type": { // what are the types of value expected for this parameter? | ||
"type": ["string", "array"], | ||
"optional": true, | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"optional": { // is a value for this parameter optional? | ||
"type": "boolean", | ||
"optional": true, | ||
"default": true | ||
}, | ||
"nullable": { // can the value for this parameter be null? | ||
"type": "boolean", | ||
"optional": true, | ||
"default": true | ||
}, | ||
"defaultvalue": { // what is the default value for this parameter? | ||
"type": "string", | ||
"optional": true | ||
}, | ||
"name": { // what name does this parameter have within the function? | ||
"type": "string" | ||
}, | ||
"description": { // a description of the parameter | ||
"type": "string", | ||
"optional": true | ||
} | ||
}, | ||
"additionalProperties": false | ||
} | ||
}, | ||
"thisobj": { | ||
"type": ["string", "array"], | ||
"optional": true, | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"example": { // some thing else to consider | ||
"type": ["string", "array"], | ||
"optional": true, | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"tags": { // arbitrary tags associated with this doc | ||
"type": "array", | ||
"optional": true, | ||
"additionalProperties": false, | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"meta": { // information about this doc | ||
"type": "object", | ||
"optional": true, | ||
"maxItems": 1, | ||
"properties": { | ||
"file": { // what is the name of the file this doc appears in? | ||
"type": "string", | ||
"optional": true, | ||
"maxItems": 1 | ||
}, | ||
"line": { // on what line of the file does this doc appear? | ||
"type": "number", | ||
"optional": true, | ||
"maxItems": 1 | ||
} | ||
}, | ||
"additionalProperties": false | ||
value: { | ||
type: [STRING, OBJECT], | ||
optional: true, | ||
properties: PARAM_SCHEMA | ||
} | ||
@@ -257,54 +499,108 @@ } | ||
}, | ||
"meta": { // information about the generation for all the docs | ||
"type": "object", | ||
"optional": true, | ||
"maxItems": 1, | ||
"properties": { | ||
"project": { // to what project does this doc belong? | ||
"type": "object", | ||
"optional": true, | ||
"maxItems": 1, | ||
"properties": { | ||
"name": { // the name of the project | ||
"type": "string", | ||
"maxItems": 1 | ||
}, | ||
"uri": { // the URI of the project | ||
"type": "string", | ||
"maxItems": 1, | ||
"format": "uri" | ||
}, | ||
"version": { // the version of the project | ||
"type": "string", | ||
"maxItems": 1 | ||
}, | ||
"lang": { // the programming language used in the project | ||
"type": "string", | ||
"maxItems": 1 | ||
} | ||
'this': { | ||
type: STRING, | ||
optional: true | ||
}, | ||
todo: { | ||
type: ARRAY, | ||
optional: true, | ||
minItems: 1, | ||
items: { | ||
type: STRING | ||
} | ||
}, | ||
// extended tutorials | ||
tutorials: { | ||
type: ARRAY, | ||
optional: true, | ||
minItems: 1, | ||
items: { | ||
type: STRING | ||
} | ||
}, | ||
// what type is the value that this doc is associated with, like `number` | ||
type: TYPE_PROPERTY_SCHEMA, | ||
undocumented: { | ||
type: BOOLEAN, | ||
optional: true | ||
}, | ||
variation: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
// what is the version of this doc | ||
version: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
// is a member left to be implemented during inheritance? | ||
virtual: { | ||
type: BOOLEAN, | ||
optional: true | ||
} | ||
} | ||
}; | ||
var PACKAGE_SCHEMA = exports.PACKAGE_SCHEMA = { | ||
type: OBJECT, | ||
additionalProperties: false, | ||
properties: { | ||
description: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
files: { | ||
type: ARRAY, | ||
uniqueItems: true, | ||
minItems: 1, | ||
items: { | ||
type: STRING | ||
} | ||
}, | ||
kind: { | ||
type: STRING, | ||
enum: ['package'] | ||
}, | ||
licenses: { | ||
type: ARRAY, | ||
optional: true, | ||
minItems: 1, | ||
items: { | ||
type: OBJECT, | ||
additionalProperties: false, | ||
properties: { | ||
type: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
"additionalProperties": false | ||
}, | ||
"generated": { // some information about the running of the doc generator | ||
"type": "object", | ||
"optional": true, | ||
"maxItems": 1, | ||
"properties": { | ||
"date": { // on what date and time was the doc generated? | ||
"type": "string", | ||
"maxItems": 1, | ||
"optional": true, | ||
"format": "date-time" | ||
}, | ||
"parser": { // what tool was used to generate the doc? | ||
"type": "string", | ||
"maxItems": 1, | ||
"optional": true | ||
} | ||
}, | ||
"additionalProperties": false | ||
url: { | ||
type: STRING, | ||
optional: true, | ||
format: 'uri' | ||
} | ||
} | ||
} | ||
}, | ||
longname: { | ||
type: STRING, | ||
optional: true, | ||
pattern: PACKAGE_REGEXP | ||
}, | ||
name: { | ||
type: STRING, | ||
optional: true | ||
}, | ||
version: { | ||
type: STRING, | ||
optional: true | ||
} | ||
} | ||
}; | ||
}; | ||
var DOCLETS_SCHEMA = exports.DOCLETS_SCHEMA = { | ||
type: ARRAY, | ||
uniqueItems: true, | ||
items: { | ||
anyOf: [DOCLET_SCHEMA, PACKAGE_SCHEMA] | ||
} | ||
}; |
@@ -9,5 +9,2 @@ 'use strict'; | ||
// Counter for generating unique node IDs. | ||
var uid = 100000000; | ||
// TODO: docs | ||
@@ -356,2 +353,3 @@ var acceptsLeadingComments = exports.acceptsLeadingComments = (function() { | ||
AstBuilder.prototype._postProcess = function(filename, ast) { | ||
var astnode = require('jsdoc/src/astnode'); | ||
var Walker = require('jsdoc/src/walker').Walker; | ||
@@ -366,8 +364,2 @@ | ||
} | ||
if (!node.nodeId) { | ||
Object.defineProperty(node, 'nodeId', { | ||
value: 'astnode' + uid++ | ||
}); | ||
} | ||
} | ||
@@ -374,0 +366,0 @@ }; |
@@ -21,3 +21,3 @@ /** | ||
comment.replace(/[\r\n]/g, ''), error.message) ); | ||
require('jsdoc/util/error').handle(err); | ||
require('jsdoc/util/logger').error(err); | ||
doclet = new Doclet('', e); | ||
@@ -24,0 +24,0 @@ } |
@@ -7,8 +7,15 @@ /*global env: true, Packages: true */ | ||
var Doclet = require('jsdoc/doclet'); | ||
var name = require('jsdoc/name'); | ||
var Syntax = require('jsdoc/src/syntax').Syntax; | ||
var jsdoc = { | ||
doclet: require('jsdoc/doclet'), | ||
name: require('jsdoc/name'), | ||
src: { | ||
astnode: require('jsdoc/src/astnode'), | ||
syntax: require('jsdoc/src/syntax') | ||
} | ||
}; | ||
var logger = require('jsdoc/util/logger'); | ||
var util = require('util'); | ||
var hasOwnProp = Object.prototype.hasOwnProperty; | ||
var Syntax = jsdoc.src.syntax.Syntax; | ||
@@ -24,2 +31,18 @@ // Prefix for JavaScript strings that were provided in lieu of a filename. | ||
// TODO: docs | ||
// TODO: not currently used | ||
function makeGlobalDoclet(globalScope) { | ||
var doclet = new jsdoc.doclet.Doclet('/** Auto-generated doclet for global scope */', {}); | ||
if (globalScope) { | ||
// TODO: handle global aliases | ||
Object.keys(globalScope.ownedVariables).forEach(function(variable) { | ||
doclet.meta.vars = doclet.meta.vars || {}; | ||
doclet.meta.vars[variable] = null; | ||
}); | ||
} | ||
return doclet; | ||
} | ||
// TODO: docs | ||
exports.createParser = function(type) { | ||
@@ -46,3 +69,3 @@ var path = require('jsdoc/path'); | ||
catch (e) { | ||
throw new Error('Unable to create the parser type "' + type + '": ' + e); | ||
logger.fatal('Unable to create the parser type "' + type + '": ' + e); | ||
} | ||
@@ -90,7 +113,5 @@ }; | ||
this._resultBuffer = []; | ||
this.refs = { | ||
__global__: { | ||
meta: {} | ||
} | ||
}; | ||
this.refs = {}; | ||
this.refs[jsdoc.src.astnode.GLOBAL_NODE_ID] = {}; | ||
this.refs[jsdoc.src.astnode.GLOBAL_NODE_ID].meta = {}; | ||
}; | ||
@@ -129,2 +150,3 @@ | ||
e.sourcefiles = sourceFiles; | ||
logger.debug('Parsing source files: %j', sourceFiles); | ||
@@ -146,6 +168,3 @@ this.emit('parseBegin', e); | ||
catch(e) { | ||
// TODO: shouldn't this be fatal if we're not in lenient mode? | ||
console.error('FILE READ ERROR: in module:jsdoc/src/parser.Parser#parse: "' + | ||
filename + '" ' + e); | ||
continue; | ||
logger.error('Unable to read and parse the source file %s: %s', filename, e); | ||
} | ||
@@ -164,2 +183,3 @@ } | ||
}); | ||
logger.debug('Finished parsing source files.'); | ||
@@ -218,2 +238,3 @@ return this._resultBuffer; | ||
var ast; | ||
var globalScope; | ||
@@ -225,2 +246,3 @@ var e = { | ||
this.emit('fileBegin', e); | ||
logger.printInfo('Parsing %s ...', sourceName); | ||
@@ -243,2 +265,3 @@ if (!e.defaultPrevented) { | ||
this.emit('fileComplete', e); | ||
logger.info('complete.'); | ||
}; | ||
@@ -261,3 +284,3 @@ | ||
this.refs[node.nodeId] = { | ||
longname: Doclet.ANONYMOUS_LONGNAME, | ||
longname: jsdoc.doclet.ANONYMOUS_LONGNAME, | ||
meta: { | ||
@@ -282,176 +305,2 @@ code: e.code | ||
/** | ||
* @private | ||
* @memberof module:src/parser.Parser | ||
*/ | ||
function nodeToString(node) { | ||
var str; | ||
if (!node || !node.type) { | ||
return; | ||
} | ||
switch (node.type) { | ||
case Syntax.AssignmentExpression: | ||
str = nodeToString(node.left); | ||
break; | ||
case Syntax.FunctionDeclaration: | ||
// falls through | ||
case Syntax.FunctionExpression: | ||
str = 'function'; | ||
break; | ||
case Syntax.Identifier: | ||
str = node.name; | ||
break; | ||
case Syntax.Literal: | ||
str = String(node.value); | ||
break; | ||
case Syntax.MemberExpression: | ||
// could be computed (like foo['bar']) or not (like foo.bar) | ||
str = nodeToString(node.object); | ||
if (node.computed) { | ||
str += util.format( '[%s]', node.property.raw || nodeToString(node.property) ); | ||
} | ||
else { | ||
str += '.' + nodeToString(node.property); | ||
} | ||
break; | ||
case Syntax.ThisExpression: | ||
str = 'this'; | ||
break; | ||
case Syntax.UnaryExpression: | ||
// like -1; operator can be prefix or postfix | ||
str = nodeToString(node.argument); | ||
if (node.prefix) { | ||
str = node.operator + str; | ||
} | ||
else { | ||
str = str + node.operator; | ||
} | ||
break; | ||
case Syntax.VariableDeclarator: | ||
str = nodeToString(node.id); | ||
break; | ||
default: | ||
str = Syntax[node.type] || 'UnknownType'; | ||
} | ||
return str; | ||
} | ||
// TODO: docs | ||
function getParamNames(node) { | ||
if (!node.params) { | ||
return []; | ||
} | ||
return node.params.map(function(param) { | ||
return nodeToString(param); | ||
}); | ||
} | ||
// TODO: docs | ||
function isAccessor(node) { | ||
return node.kind === 'get' || node.kind === 'set'; | ||
} | ||
// TODO: docs | ||
function isAssignment(node) { | ||
return node && (node.type === Syntax.AssignmentExpression || | ||
node.type === Syntax.VariableDeclarator); | ||
} | ||
// TODO: docs | ||
/** | ||
* Retrieve information about the node, including its name and type. | ||
* @memberof module:jsdoc/src/parser.Parser | ||
*/ | ||
Parser.prototype.getNodeInfo = function(node) { | ||
var string; | ||
var about = {}; | ||
var accessor = false; | ||
switch (node.type) { | ||
// like: "foo = 'bar'" (after foo has been declared) | ||
case Syntax.AssignmentExpression: | ||
about.node = node.right; | ||
about.name = nodeToString(node.left); | ||
about.type = about.node.type; | ||
about.value = nodeToString(about.node); | ||
break; | ||
// like: "function foo() {}" | ||
case Syntax.FunctionDeclaration: | ||
about.node = node; | ||
about.name = nodeToString(node.id); | ||
about.type = nodeToString(node); | ||
about.value = nodeToString(about.node); | ||
about.paramnames = getParamNames(node); | ||
break; | ||
// like the function in: "var foo = function() {}" | ||
case Syntax.FunctionExpression: | ||
about.node = node; | ||
about.name = ''; | ||
about.type = nodeToString(node); | ||
about.value = nodeToString(about.node); | ||
about.paramnames = getParamNames(node); | ||
break; | ||
// like "a.b.c" | ||
case Syntax.MemberExpression: | ||
about.node = node; | ||
about.name = nodeToString(about.node); | ||
about.type = about.node.type; | ||
break; | ||
// like "a: 0" in "var foo = {a: 0}" | ||
case Syntax.Property: | ||
accessor = isAccessor(node); | ||
about.node = node.value; | ||
about.name = nodeToString(node.key); | ||
about.value = nodeToString(about.node); | ||
if (accessor) { | ||
about.type = nodeToString(node); | ||
about.paramnames = getParamNames(node); | ||
} | ||
else { | ||
about.type = about.node.type; | ||
} | ||
break; | ||
// like: "var i = 0" (has init property) | ||
// like: "var i" (no init property) | ||
case Syntax.VariableDeclarator: | ||
about.node = node.init || node.id; | ||
about.name = node.id.name; | ||
about.type = about.node.type || 'undefined'; | ||
about.value = nodeToString(about.node); | ||
break; | ||
default: | ||
string = nodeToString(node); | ||
if (string) { | ||
about.name = string; | ||
} | ||
} | ||
return about; | ||
}; | ||
// TODO: docs | ||
/** | ||
* @param {string} name - The symbol's longname. | ||
@@ -468,3 +317,3 @@ * @return {string} The symbol's basename. | ||
function definedInScope(doclet, basename) { | ||
return !!doclet && !!doclet.meta && !!doclet.meta.vars && | ||
return !!doclet && !!doclet.meta && !!doclet.meta.vars && !!basename && | ||
hasOwnProp.call(doclet.meta.vars, basename); | ||
@@ -476,3 +325,3 @@ } | ||
* Given a node, determine what the node is a member of. | ||
* @param {astnode} node | ||
* @param {node} node | ||
* @returns {string} The long name of the node that this is a member of. | ||
@@ -493,6 +342,6 @@ */ | ||
if (!doclet) { | ||
result = Doclet.ANONYMOUS_LONGNAME + name.INNER; | ||
result = jsdoc.doclet.ANONYMOUS_LONGNAME + jsdoc.name.INNER; | ||
} | ||
else { | ||
result = (doclet.longname || doclet.name) + name.INNER; | ||
result = (doclet.longname || doclet.name) + jsdoc.name.INNER; | ||
} | ||
@@ -503,3 +352,3 @@ } | ||
scope = node; | ||
basename = this.getBasename( nodeToString(node) ); | ||
basename = this.getBasename( jsdoc.src.astnode.nodeToString(node) ); | ||
@@ -520,3 +369,3 @@ // walk up the scope chain until we find the scope in which the node is defined | ||
// do we know that it's a global? | ||
doclet = this.refs.__global__; | ||
doclet = this.refs[jsdoc.src.astnode.GLOBAL_NODE_ID]; | ||
if ( doclet && definedInScope(doclet, basename) ) { | ||
@@ -545,3 +394,3 @@ result = [doclet.meta.vars[basename], basename]; | ||
* Resolve what "this" refers to relative to a node. | ||
* @param {astnode} node - The "this" node | ||
* @param {node} node - The "this" node | ||
* @returns {string} The longname of the enclosing node. | ||
@@ -557,3 +406,3 @@ */ | ||
if (!doclet) { | ||
result = Doclet.ANONYMOUS_LONGNAME; // TODO handle global this? | ||
result = jsdoc.doclet.ANONYMOUS_LONGNAME; // TODO handle global this? | ||
} | ||
@@ -568,3 +417,3 @@ else if (doclet['this']) { | ||
// like: var foo = function(n) { /** blah */ this.bar = n; } | ||
else if ( doclet.kind === 'member' && isAssignment(node) ) { | ||
else if ( doclet.kind === 'member' && jsdoc.src.astnode.isAssignment(node) ) { | ||
result = doclet.longname || doclet.name; | ||
@@ -571,0 +420,0 @@ } |
@@ -12,2 +12,3 @@ /*global env: true */ | ||
var fs = require('jsdoc/fs'); | ||
var logger = require('jsdoc/util/logger'); | ||
var path = require('jsdoc/path'); | ||
@@ -29,2 +30,3 @@ | ||
exports.Scanner.prototype.scan = function(searchPaths, depth, filter) { | ||
var currentFile; | ||
var isFile; | ||
@@ -43,9 +45,10 @@ | ||
try { | ||
isFile = fs.statSync(filepath).isFile(); | ||
currentFile = fs.statSync(filepath); | ||
} | ||
catch(e) { | ||
isFile = false; | ||
catch (e) { | ||
logger.error('Unable to find the source file or directory %s', filepath); | ||
return; | ||
} | ||
if (isFile) { | ||
if ( currentFile.isFile() ) { | ||
filePaths.push(filepath); | ||
@@ -52,0 +55,0 @@ } |
@@ -8,7 +8,13 @@ /** | ||
var doclet = require('jsdoc/doclet'); | ||
var Syntax = require('jsdoc/src/syntax').Syntax; | ||
var jsdoc = { | ||
doclet: require('jsdoc/doclet'), | ||
src: { | ||
astnode: require('jsdoc/src/astnode'), | ||
syntax: require('jsdoc/src/syntax') | ||
} | ||
}; | ||
var util = require('util'); | ||
var hasOwnProp = Object.prototype.hasOwnProperty; | ||
var Syntax = jsdoc.src.syntax.Syntax; | ||
@@ -28,7 +34,7 @@ // TODO: docs | ||
// TODO: docs | ||
function makeVarsFinisher(scopeDoc) { | ||
function makeVarsFinisher(scopeDoclet) { | ||
return function(e) { | ||
// no need to evaluate all things related to scopeDoc again, just use it | ||
if (scopeDoc && e.doclet && e.doclet.alias) { | ||
scopeDoc.meta.vars[e.code.name] = e.doclet.longname; | ||
// no need to evaluate all things related to scopeDoclet again, just use it | ||
if (scopeDoclet && e.doclet && e.doclet.alias) { | ||
scopeDoclet.meta.vars[e.code.name] = e.doclet.longname; | ||
} | ||
@@ -38,3 +44,2 @@ }; | ||
// TODO: docs | ||
@@ -46,3 +51,3 @@ function SymbolFound(node, filename, extras) { | ||
this.id = extras.id || node.nodeId; | ||
this.comment = extras.comment || getLeadingComment(node) || doclet.UNDOCUMENTED_TAG; | ||
this.comment = extras.comment || getLeadingComment(node) || jsdoc.doclet.UNDOCUMENTED_TAG; | ||
this.lineno = extras.lineno || node.loc.start.line; | ||
@@ -232,16 +237,12 @@ this.range = extras.range || node.range; | ||
// TODO: docs | ||
// TODO: may be able to get rid of this using knownAliases | ||
function trackVars(parser, node, e) { | ||
// keep track of vars within a given scope | ||
var scope = '__global__'; | ||
var doclet = null; | ||
var enclosingScopeId = node.enclosingScope ? node.enclosingScope.nodeId : | ||
jsdoc.src.astnode.GLOBAL_NODE_ID; | ||
var doclet = parser.refs[enclosingScopeId]; | ||
if (node.enclosingScope) { | ||
scope = node.enclosingScope.nodeId; | ||
} | ||
doclet = parser.refs[scope]; | ||
if (doclet) { | ||
doclet.meta.vars = doclet.meta.vars || {}; | ||
doclet.meta.vars[e.code.name] = false; | ||
e.finishers.push(makeVarsFinisher(doclet)); | ||
doclet.meta.vars[e.code.name] = null; | ||
e.finishers.push( makeVarsFinisher(doclet) ); | ||
} | ||
@@ -258,3 +259,3 @@ } | ||
var extras = { | ||
code: parser.getNodeInfo(node) | ||
code: jsdoc.src.astnode.getInfo(node) | ||
}; | ||
@@ -265,3 +266,2 @@ | ||
case Syntax.AssignmentExpression: | ||
// TODO: ignore unless operator is '=' (for example, ignore '+=') | ||
// falls through | ||
@@ -268,0 +268,0 @@ |
@@ -10,2 +10,4 @@ /** | ||
var astnode = require('jsdoc/src/astnode'); | ||
var doclet = require('jsdoc/doclet'); | ||
var Syntax = require('jsdoc/src/syntax').Syntax; | ||
@@ -45,3 +47,3 @@ | ||
walkers[Syntax.ArrayExpression] = function(node, parent, state, cb) { | ||
walkers[Syntax.ArrayExpression] = function arrayExpression(node, parent, state, cb) { | ||
for (var i = 0, l = node.elements.length; i < l; i++) { | ||
@@ -56,3 +58,3 @@ var e = node.elements[i]; | ||
// TODO: verify correctness | ||
walkers[Syntax.ArrayPattern] = function(node, parent, state, cb) { | ||
walkers[Syntax.ArrayPattern] = function arrayPattern(node, parent, state, cb) { | ||
for (var i = 0, l = node.elements.length; i < l; i++) { | ||
@@ -67,3 +69,3 @@ var e = node.elements[i]; | ||
walkers[Syntax.AssignmentExpression] = function(node, parent, state, cb) { | ||
walkers[Syntax.AssignmentExpression] = function assignmentExpression(node, parent, state, cb) { | ||
cb(node.left, node, state); | ||
@@ -73,5 +75,8 @@ cb(node.right, node, state); | ||
walkers[Syntax.BinaryExpression] = walkers[Syntax.AssignmentExpression]; | ||
walkers[Syntax.BinaryExpression] = function binaryExpression(node, parent, state, cb) { | ||
cb(node.left, node, state); | ||
cb(node.right, node, state); | ||
}; | ||
walkers[Syntax.BlockStatement] = function(node, parent, state, cb) { | ||
walkers[Syntax.BlockStatement] = function blockStatement(node, parent, state, cb) { | ||
for (var i = 0, l = node.body.length; i < l; i++) { | ||
@@ -84,3 +89,3 @@ cb(node.body[i], node, state); | ||
walkers[Syntax.CallExpression] = function(node, parent, state, cb) { | ||
walkers[Syntax.CallExpression] = function callExpression(node, parent, state, cb) { | ||
var i; | ||
@@ -103,3 +108,4 @@ var l; | ||
// TODO: verify correctness | ||
walkers[Syntax.ComprehensionExpression] = function(node, parent, state, cb) { | ||
walkers[Syntax.ComprehensionExpression] = | ||
function comprehensionExpression(node, parent, state, cb) { | ||
cb(node.body, node, state); | ||
@@ -116,3 +122,3 @@ | ||
walkers[Syntax.ConditionalExpression] = function(node, parent, state, cb) { | ||
walkers[Syntax.ConditionalExpression] = function conditionalExpression(node, parent, state, cb) { | ||
cb(node.test, node, state); | ||
@@ -127,3 +133,3 @@ cb(node.consequent, node, state); | ||
walkers[Syntax.DoWhileStatement] = function(node, parent, state, cb) { | ||
walkers[Syntax.DoWhileStatement] = function doWhileStatement(node, parent, state, cb) { | ||
cb(node.test, node, state); | ||
@@ -135,7 +141,7 @@ cb(node.body, node, state); | ||
walkers[Syntax.ExpressionStatement] = function(node, parent, state, cb) { | ||
walkers[Syntax.ExpressionStatement] = function expressionStatement(node, parent, state, cb) { | ||
cb(node.expression, node, state); | ||
}; | ||
walkers[Syntax.ForInStatement] = function(node, parent, state, cb) { | ||
walkers[Syntax.ForInStatement] = function forInStatement(node, parent, state, cb) { | ||
cb(node.left, node, state); | ||
@@ -148,3 +154,3 @@ cb(node.right, node, state); | ||
walkers[Syntax.ForStatement] = function(node, parent, state, cb) { | ||
walkers[Syntax.ForStatement] = function forStatement(node, parent, state, cb) { | ||
if (node.init) { | ||
@@ -165,3 +171,3 @@ cb(node.init, node, state); | ||
walkers[Syntax.FunctionDeclaration] = function(node, parent, state, cb) { | ||
walkers[Syntax.FunctionDeclaration] = function functionDeclaration(node, parent, state, cb) { | ||
var i; | ||
@@ -190,3 +196,3 @@ var l; | ||
walkers[Syntax.IfStatement] = function(node, parent, state, cb) { | ||
walkers[Syntax.IfStatement] = function ifStatement(node, parent, state, cb) { | ||
cb(node.test, node, state); | ||
@@ -199,3 +205,3 @@ cb(node.consequent, node, state); | ||
walkers[Syntax.LabeledStatement] = function(node, parent, state, cb) { | ||
walkers[Syntax.LabeledStatement] = function labeledStatement(node, parent, state, cb) { | ||
cb(node.body, node, state); | ||
@@ -205,3 +211,3 @@ }; | ||
// TODO: add scope info?? | ||
walkers[Syntax.LetStatement] = function(node, parent, state, cb) { | ||
walkers[Syntax.LetStatement] = function letStatement(node, parent, state, cb) { | ||
for (var i = 0, l = node.head.length; i < l; i++) { | ||
@@ -220,5 +226,5 @@ var head = node.head[i]; | ||
walkers[Syntax.LogicalExpression] = walkers[Syntax.AssignmentExpression]; | ||
walkers[Syntax.LogicalExpression] = walkers[Syntax.BinaryExpression]; | ||
walkers[Syntax.MemberExpression] = function(node, parent, state, cb) { | ||
walkers[Syntax.MemberExpression] = function memberExpression(node, parent, state, cb) { | ||
if (node.property) { | ||
@@ -232,3 +238,3 @@ cb(node.property, node, state); | ||
walkers[Syntax.ObjectExpression] = function(node, parent, state, cb) { | ||
walkers[Syntax.ObjectExpression] = function objectExpression(node, parent, state, cb) { | ||
for (var i = 0, l = node.properties.length; i < l; i++) { | ||
@@ -243,3 +249,3 @@ cb(node.properties[i], node, state); | ||
walkers[Syntax.Property] = function(node, parent, state, cb) { | ||
walkers[Syntax.Property] = function property(node, parent, state, cb) { | ||
// move leading comments from key to property node | ||
@@ -251,3 +257,3 @@ moveComments(node.key, node); | ||
walkers[Syntax.ReturnStatement] = function(node, parent, state, cb) { | ||
walkers[Syntax.ReturnStatement] = function returnStatement(node, parent, state, cb) { | ||
if (node.argument) { | ||
@@ -258,3 +264,3 @@ cb(node.argument, node, state); | ||
walkers[Syntax.SequenceExpression] = function(node, parent, state, cb) { | ||
walkers[Syntax.SequenceExpression] = function sequenceExpression(node, parent, state, cb) { | ||
for (var i = 0, l = node.expressions.length; i < l; i++) { | ||
@@ -265,3 +271,3 @@ cb(node.expressions[i], node, state); | ||
walkers[Syntax.SwitchCase] = function(node, parent, state, cb) { | ||
walkers[Syntax.SwitchCase] = function switchCase(node, parent, state, cb) { | ||
if (node.test) { | ||
@@ -276,3 +282,3 @@ cb(node.test, node, state); | ||
walkers[Syntax.SwitchStatement] = function(node, parent, state, cb) { | ||
walkers[Syntax.SwitchStatement] = function switchStatement(node, parent, state, cb) { | ||
cb(node.discriminant, node, state); | ||
@@ -287,7 +293,7 @@ | ||
walkers[Syntax.ThrowStatement] = function(node, parent, state, cb) { | ||
walkers[Syntax.ThrowStatement] = function throwStatement(node, parent, state, cb) { | ||
cb(node.argument, node, state); | ||
}; | ||
walkers[Syntax.TryStatement] = function(node, parent, state, cb) { | ||
walkers[Syntax.TryStatement] = function tryStatement(node, parent, state, cb) { | ||
var i; | ||
@@ -317,3 +323,3 @@ var l; | ||
walkers[Syntax.UnaryExpression] = function(node, parent, state, cb) { | ||
walkers[Syntax.UnaryExpression] = function unaryExpression(node, parent, state, cb) { | ||
cb(node.argument, node, state); | ||
@@ -324,3 +330,3 @@ }; | ||
walkers[Syntax.VariableDeclaration] = function(node, parent, state, cb) { | ||
walkers[Syntax.VariableDeclaration] = function variableDeclaration(node, parent, state, cb) { | ||
// move leading comments to first declarator | ||
@@ -334,3 +340,3 @@ moveComments(node, node.declarations[0]); | ||
walkers[Syntax.VariableDeclarator] = function(node, parent, state, cb) { | ||
walkers[Syntax.VariableDeclarator] = function variableDeclarator(node, parent, state, cb) { | ||
cb(node.id, node, state); | ||
@@ -345,3 +351,3 @@ | ||
walkers[Syntax.WithStatement] = function(node, parent, state, cb) { | ||
walkers[Syntax.WithStatement] = function withStatement(node, parent, state, cb) { | ||
cb(node.object, node, state); | ||
@@ -370,3 +376,3 @@ cb(node.body, node, state); | ||
Walker.prototype._recurse = function(ast) { | ||
// TODO: look for other state that we can track/attach during the walk | ||
// TODO: track variables/aliases during the walk | ||
var state = { | ||
@@ -379,14 +385,17 @@ nodes: [], | ||
function cb(node, parent, state) { | ||
var isScope = isScopeNode(node); | ||
var currentScope; | ||
if (node.parent === undefined) { | ||
Object.defineProperty(node, 'parent', { | ||
value: parent || null | ||
}); | ||
var isScope = astnode.isScope(node); | ||
// for efficiency, if the node has a knownVariables property, assume that we've already | ||
// added the required properties | ||
if (!node.knownVariables) { | ||
astnode.addNodeProperties(node); | ||
} | ||
if (node.enclosingScope === undefined) { | ||
Object.defineProperty(node, 'enclosingScope', { | ||
value: getCurrentScope(state.scopes) | ||
}); | ||
node.parent = parent || null; | ||
currentScope = getCurrentScope(state.scopes); | ||
if (currentScope) { | ||
node.enclosingScope = currentScope; | ||
} | ||
@@ -393,0 +402,0 @@ |
@@ -22,10 +22,27 @@ /*global env: true */ | ||
type: require('jsdoc/tag/type') | ||
}, | ||
util: { | ||
logger: require('jsdoc/util/logger') | ||
} | ||
}; | ||
var path = require('jsdoc/path'); | ||
function trim(text, newlines) { | ||
function trim(text, opts) { | ||
var indentMatcher; | ||
var match; | ||
opts = opts || {}; | ||
if (!text) { return ''; } | ||
if (newlines) { | ||
return text.replace(/^[\n\r\f]+|[\n\r\f]+$/g, ''); | ||
if (opts.keepsWhitespace) { | ||
text = text.replace(/^[\n\r\f]+|[\n\r\f]+$/g, ''); | ||
if (opts.removesIndent) { | ||
match = text.match(/^([ \t]+)/); | ||
if (match && match[1]) { | ||
indentMatcher = new RegExp('^' + match[1], 'gm'); | ||
text = text.replace(indentMatcher, ''); | ||
} | ||
} | ||
return text; | ||
} | ||
@@ -88,2 +105,5 @@ else { | ||
var Tag = exports.Tag = function(tagTitle, tagBody, meta) { | ||
var tagDef; | ||
var trimOpts; | ||
meta = meta || {}; | ||
@@ -96,12 +116,27 @@ | ||
var tagDef = jsdoc.tag.dictionary.lookUp(this.title); | ||
tagDef = jsdoc.tag.dictionary.lookUp(this.title); | ||
trimOpts = { | ||
keepsWhitespace: tagDef.keepsWhitespace, | ||
removesIndent: tagDef.removesIndent | ||
}; | ||
/** The text part of the tag: @title text */ | ||
this.text = trim(tagBody, tagDef.keepsWhitespace); | ||
this.text = trim(tagBody, trimOpts); | ||
if (this.text) { | ||
processTagText(this, tagDef); | ||
try { | ||
processTagText(this, tagDef); | ||
jsdoc.tag.validator.validate(this, tagDef, meta); | ||
} | ||
catch (e) { | ||
// probably a type-parsing error | ||
jsdoc.util.logger.error( | ||
'Unable to create a Tag object%s with title "%s" and body "%s": %s', | ||
meta.filename ? ( ' for source file ' + path.join(meta.path, meta.filename) ) : '', | ||
tagTitle, | ||
tagBody, | ||
e.message | ||
); | ||
} | ||
} | ||
jsdoc.tag.validator.validate(this, tagDef, meta); | ||
}; |
@@ -17,3 +17,5 @@ /*global app: true, env: true */ | ||
var commonPrefix = path.commonPrefix( sourceFiles.concat(env.opts._ || []) ); | ||
var result = (filepath + '/').replace(commonPrefix, ''); | ||
// always use forward slashes | ||
var result = (filepath + path.sep).replace(commonPrefix, '') | ||
.replace(/\\/g, '/'); | ||
@@ -45,2 +47,8 @@ if (result.length > 0 && result[result.length - 1] !== '/') { | ||
function setDocletNameToValueName(doclet, tag) { | ||
if (tag.value && tag.value.name) { | ||
doclet.addTag('name', tag.value.name); | ||
} | ||
} | ||
function setDocletDescriptionToValue(doclet, tag) { | ||
@@ -52,2 +60,8 @@ if (tag.value) { | ||
function setDocletTypeToValueType(doclet, tag) { | ||
if (tag.value && tag.value.type) { | ||
doclet.type = tag.value.type; | ||
} | ||
} | ||
function setNameToFile(doclet, tag) { | ||
@@ -214,8 +228,7 @@ var name; | ||
canHaveType: true, | ||
canHaveName: true, | ||
onTagged: function(doclet, tag) { | ||
setDocletKindToTitle(doclet, tag); | ||
setDocletNameToValue(doclet, tag); | ||
if (tag.value && tag.value.type) { | ||
doclet.type = tag.value.type; | ||
} | ||
setDocletNameToValueName(doclet, tag); | ||
setDocletTypeToValueType(doclet, tag); | ||
} | ||
@@ -290,3 +303,3 @@ }) | ||
doclet.isEnum = true; | ||
if (tag.value && tag.value.type) { doclet.type = tag.value.type; } | ||
setDocletTypeToValueType(doclet, tag); | ||
} | ||
@@ -305,2 +318,3 @@ }); | ||
keepsWhitespace: true, | ||
removesIndent: true, | ||
mustHaveValue: true, | ||
@@ -319,5 +333,3 @@ onTagged: function(doclet, tag) { | ||
doclet.exceptions.push(tag.value); | ||
if (tag.value && tag.value.type) { | ||
doclet.type = tag.value.type; | ||
} | ||
setDocletTypeToValueType(doclet, tag); | ||
} | ||
@@ -332,5 +344,5 @@ }) | ||
setDocletKindToTitle(doclet, tag); | ||
setDocletNameToValue(doclet, tag); | ||
if (tag.value && tag.value.type) { | ||
doclet.type = tag.value.type; | ||
setDocletTypeToValueType(doclet, tag); | ||
doclet.addTag('name', doclet.type.names[0]); | ||
} | ||
@@ -441,8 +453,7 @@ } | ||
canHaveType: true, | ||
canHaveName: true, | ||
onTagged: function(doclet, tag) { | ||
setDocletKindToTitle(doclet, tag); | ||
setDocletNameToValue(doclet, tag); | ||
if (tag.value && tag.value.type) { | ||
doclet.type = tag.value.type; | ||
} | ||
setDocletNameToValueName(doclet, tag); | ||
setDocletTypeToValueType(doclet, tag); | ||
} | ||
@@ -485,5 +496,3 @@ }) | ||
} | ||
if (tag.value && tag.value.type) { | ||
doclet.type = tag.value.type; | ||
} | ||
setDocletTypeToValueType(doclet, tag); | ||
} | ||
@@ -501,5 +510,3 @@ }); | ||
setDocletNameToValue(doclet, tag); | ||
if (tag.value && tag.value.type) { | ||
doclet.type = tag.value.type; | ||
} | ||
setDocletTypeToValueType(doclet, tag); | ||
} | ||
@@ -625,3 +632,2 @@ }); | ||
onTagged: function(doclet, tag) { | ||
if (!doclet.see) { doclet.see = []; } | ||
doclet['this'] = firstWordOf(tag.value); | ||
@@ -661,3 +667,3 @@ } | ||
if (tag.value && tag.value.type) { | ||
doclet.type = tag.value.type; | ||
setDocletTypeToValueType(doclet, tag); | ||
@@ -679,5 +685,3 @@ // for backwards compatibility, we allow @type for functions to imply return type | ||
if (tag.value) { | ||
if (tag.value.name) { | ||
doclet.addTag('name', tag.value.name); | ||
} | ||
setDocletNameToValueName(doclet, tag); | ||
@@ -692,4 +696,4 @@ // callbacks are always type {function} | ||
} | ||
else if (tag.value.type) { | ||
doclet.type = tag.value.type; | ||
else { | ||
setDocletTypeToValueType(doclet, tag); | ||
} | ||
@@ -696,0 +700,0 @@ } |
@@ -242,4 +242,4 @@ /** | ||
// always re-throw so the caller has a chance to report which file was bad | ||
throw new Error( util.format('unable to parse the type expression "%s": %s', | ||
tagInfo.typeExpression, e.message) ); | ||
throw new Error( util.format('Invalid type expression "%s": %s', tagInfo.typeExpression, | ||
e.message) ); | ||
} | ||
@@ -246,0 +246,0 @@ |
@@ -13,2 +13,3 @@ /*global env: true */ | ||
var format = require('util').format; | ||
var logger = require('jsdoc/util/logger'); | ||
@@ -24,23 +25,2 @@ function buildMessage(tagName, meta, desc) { | ||
function UnknownTagError(tagName, meta) { | ||
this.name = 'UnknownTagError'; | ||
this.message = buildMessage(tagName, meta, 'is not a known tag'); | ||
} | ||
UnknownTagError.prototype = new Error(); | ||
UnknownTagError.prototype.constructor = UnknownTagError; | ||
function TagValueRequiredError(tagName, meta) { | ||
this.name = 'TagValueRequiredError'; | ||
this.message = buildMessage(tagName, meta, 'requires a value'); | ||
} | ||
TagValueRequiredError.prototype = new Error(); | ||
TagValueRequiredError.prototype.constructor = TagValueRequiredError; | ||
function TagValueNotPermittedError(tagName, meta) { | ||
this.name = 'TagValueNotPermittedError'; | ||
this.message = buildMessage(tagName, meta, 'does not permit a value'); | ||
} | ||
TagValueNotPermittedError.prototype = new Error(); | ||
TagValueNotPermittedError.prototype.constructor = TagValueNotPermittedError; | ||
/** | ||
@@ -51,8 +31,7 @@ * Validate the given tag. | ||
if (!tagDef && !env.conf.tags.allowUnknownTags) { | ||
require('jsdoc/util/error').handle( new UnknownTagError(tag.title, meta) ); | ||
logger.error( buildMessage(tag.title, meta, 'is not a known tag') ); | ||
} | ||
if (!tag.text) { | ||
else if (!tag.text) { | ||
if (tagDef.mustHaveValue) { | ||
require('jsdoc/util/error').handle( new TagValueRequiredError(tag.title, meta) ); | ||
logger.error( buildMessage(tag.title, meta, 'requires a value') ); | ||
} | ||
@@ -62,5 +41,5 @@ } | ||
if (tagDef.mustNotHaveValue) { | ||
require('jsdoc/util/error').handle( new TagValueNotPermittedError(tag.title, meta) ); | ||
logger.error( buildMessage(tag.title, meta, 'does not permit a value') ); | ||
} | ||
} | ||
}; |
@@ -41,4 +41,3 @@ /** | ||
exports.Template.prototype.load = function(file) { | ||
var _path = path.join(this.path, file); | ||
return _.template(fs.readFileSync(_path, 'utf8'), null, this.settings); | ||
return _.template(fs.readFileSync(file, 'utf8'), null, this.settings); | ||
}; | ||
@@ -57,2 +56,4 @@ | ||
exports.Template.prototype.partial = function(file, data) { | ||
file = path.resolve(this.path, file); | ||
// load template into cache | ||
@@ -59,0 +60,0 @@ if (!(file in this.cache)) { |
@@ -15,3 +15,3 @@ /*global env: true */ | ||
fs = require('jsdoc/fs'), | ||
error = require('jsdoc/util/error'), | ||
logger = require('jsdoc/util/logger'), | ||
path = require('path'), | ||
@@ -65,3 +65,3 @@ hasOwnProp = Object.prototype.hasOwnProperty, | ||
if (hasOwnProp.call(conf, name)) { | ||
error.handle(new Error("Tutorial " + name + "'s metadata is defined multiple times, only the first will be used.")); | ||
logger.warn('Metadata for the tutorial %s is defined more than once. Only the first definition will be used.', name ); | ||
} else { | ||
@@ -84,3 +84,3 @@ conf[name] = meta; | ||
if (hasOwnProp.call(tutorials, current.name)) { | ||
error.handle(new Error("Tutorial with name " + current.name + " exists more than once, not adding (same name, different file extensions?)")); | ||
logger.warn('The tutorial %s is defined more than once. Only the first definition will be used.', current.name); | ||
} else { | ||
@@ -185,3 +185,3 @@ tutorials[current.name] = current; | ||
if (!hasOwnProp.call(tutorials, child)) { | ||
error.handle( new Error("Missing child tutorial: " + child) ); | ||
logger.error('Missing child tutorial: %s', child); | ||
} | ||
@@ -188,0 +188,0 @@ else { |
@@ -21,3 +21,3 @@ /** | ||
else { | ||
clone = Object.create(o); | ||
clone = Object.create( Object.getPrototypeOf(o) ); | ||
props = Object.getOwnPropertyNames(o); | ||
@@ -24,0 +24,0 @@ for (i = 0, l = props.length; i < l; i++) { |
@@ -11,5 +11,8 @@ /** | ||
var seenItems = []; | ||
function seen(object) { | ||
if (seenItems.indexOf(object) !== -1) { | ||
function ObjectWalker() { | ||
this.seenItems = []; | ||
} | ||
ObjectWalker.prototype.seen = function(object) { | ||
if (this.seenItems.indexOf(object) !== -1) { | ||
return true; | ||
@@ -19,42 +22,44 @@ } | ||
return false; | ||
} | ||
}; | ||
// some objects are unwalkable, like Java native objects | ||
function isUnwalkable(o) { | ||
ObjectWalker.prototype.isUnwalkable = function(o) { | ||
return (o && typeof o === 'object' && typeof o.constructor === 'undefined'); | ||
} | ||
}; | ||
function isFunction(o) { | ||
ObjectWalker.prototype.isFunction = function(o) { | ||
return (o && typeof o === 'function' || o instanceof Function); | ||
} | ||
}; | ||
function isObject(o) { | ||
ObjectWalker.prototype.isObject = function(o) { | ||
return o && o instanceof Object || | ||
(o && typeof o.constructor !== 'undefined' && o.constructor.name === 'Object'); | ||
} | ||
}; | ||
function checkCircularRefs(o, func) { | ||
if ( seen(o) ) { | ||
ObjectWalker.prototype.checkCircularRefs = function(o, func) { | ||
if ( this.seen(o) ) { | ||
return '<CircularRef>'; | ||
} | ||
else { | ||
seenItems.push(o); | ||
this.seenItems.push(o); | ||
return func(o); | ||
} | ||
} | ||
}; | ||
function walk(o) { | ||
ObjectWalker.prototype.walk = function(o) { | ||
var result; | ||
if ( isUnwalkable(o) ) { | ||
var self = this; | ||
if ( this.isUnwalkable(o) ) { | ||
result = '<Object>'; | ||
} | ||
else if ( o === undefined ) { | ||
result = 'undefined'; | ||
result = null; | ||
} | ||
else if ( Array.isArray(o) ) { | ||
result = checkCircularRefs(o, function(arr) { | ||
result = this.checkCircularRefs(o, function(arr) { | ||
var newArray = []; | ||
arr.forEach(function(item) { | ||
newArray.push( walk(item) ); | ||
newArray.push( self.walk(item) ); | ||
}); | ||
@@ -74,10 +79,10 @@ | ||
} | ||
else if ( isFunction(o) ) { | ||
else if ( this.isFunction(o) ) { | ||
result = '<Function' + (o.name ? ' ' + o.name : '') + '>'; | ||
} | ||
else if ( isObject(o) && o !== null ) { | ||
result = checkCircularRefs(o, function(obj) { | ||
else if ( this.isObject(o) && o !== null ) { | ||
result = this.checkCircularRefs(o, function(obj) { | ||
var newObj = {}; | ||
Object.keys(obj).forEach(function(key) { | ||
newObj[key] = walk(obj[key]); | ||
newObj[key] = self.walk(obj[key]); | ||
}); | ||
@@ -94,3 +99,3 @@ | ||
return result; | ||
} | ||
}; | ||
@@ -101,3 +106,3 @@ /** | ||
exports.dump = function(object) { | ||
return JSON.stringify(walk(object), null, 4); | ||
return JSON.stringify(new ObjectWalker().walk(object), null, 4); | ||
}; |
/*global env: true */ | ||
/** | ||
Helper functions for handling errors. | ||
@module jsdoc/util/error | ||
* Helper functions for handling errors. | ||
* | ||
* @deprecated As of JSDoc 3.3.0. This module may be removed in a future release. Use the module | ||
* {@link module:jsdoc/util/logger} to log warnings and errors. | ||
* @module jsdoc/util/error | ||
*/ | ||
@@ -9,26 +12,25 @@ 'use strict'; | ||
/** | ||
Handle an exception appropriately based on whether lenient mode is enabled: | ||
+ If lenient mode is enabled, log the exception to the console. | ||
+ If lenient mode is not enabled, re-throw the exception. | ||
@param {Error} e - The exception to handle. | ||
@exception {Error} Re-throws the original exception unless lenient mode is enabled. | ||
@memberof module:jsdoc/util/error | ||
* Log an exception as an error. | ||
* | ||
* Prior to JSDoc 3.3.0, this method would either log the exception (if lenient mode was enabled) or | ||
* re-throw the exception (default). | ||
* | ||
* In JSDoc 3.3.0 and later, lenient mode has been replaced with strict mode, which is disabled by | ||
* default. If strict mode is enabled, calling the `handle` method causes JSDoc to exit immediately, | ||
* just as if the exception had been re-thrown. | ||
* | ||
* @deprecated As of JSDoc 3.3.0. This module may be removed in a future release. | ||
* @param {Error} e - The exception to log. | ||
* @memberof module:jsdoc/util/error | ||
*/ | ||
exports.handle = function(e) { | ||
var msg; | ||
var logger = require('jsdoc/util/logger'); | ||
var msg = e ? ( e.message || JSON.stringify(e) ) : ''; | ||
if (env.opts.lenient) { | ||
msg = e.message || JSON.stringify(e); | ||
// include the error type if it's an Error object | ||
if (e instanceof Error) { | ||
msg = e.name + ': ' + msg; | ||
} | ||
// include the error type if it's an Error object | ||
if (e instanceof Error) { | ||
msg = e.name + ': ' + msg; | ||
} | ||
console.log(msg); | ||
} | ||
else { | ||
throw e; | ||
} | ||
logger.error(msg); | ||
}; |
@@ -76,5 +76,5 @@ /*global env: true */ | ||
* returns the resulting HTML. | ||
* @throws {Error} If the name does not correspond to a known parser. | ||
*/ | ||
function getParseFunction(parserName, conf) { | ||
var logger = require('jsdoc/util/logger'); | ||
var marked = require('marked'); | ||
@@ -103,3 +103,4 @@ var parserFunction; | ||
else { | ||
throw new Error("unknown Markdown parser: '" + parserName + "'"); | ||
logger.error('Unrecognized Markdown parser "%s". Markdown support is disabled.', | ||
parserName); | ||
} | ||
@@ -113,5 +114,5 @@ } | ||
* HTML, then returns the HTML as a string. | ||
* @returns {Function} A function that accepts Markdown source, feeds it to the selected parser, and | ||
* | ||
* @returns {function} A function that accepts Markdown source, feeds it to the selected parser, and | ||
* returns the resulting HTML. | ||
* @throws {Error} If the value of `env.conf.markdown.parser` does not correspond to a known parser. | ||
*/ | ||
@@ -118,0 +119,0 @@ exports.getParser = function() { |
@@ -22,3 +22,3 @@ /*global env: true */ | ||
* + `module:jsdoc/util/runtime~RHINO`: Mozilla Rhino. | ||
* + `module:jsdoc/util/runtime~NODE`: Node.js (not currently supported). | ||
* + `module:jsdoc/util/runtime~NODE`: Node.js. | ||
* | ||
@@ -48,3 +48,3 @@ * @private | ||
/** | ||
* Check whether Node.js is running JSDoc. (Node.js is not currently supported.) | ||
* Check whether Node.js is running JSDoc. | ||
* @return {boolean} Set to `true` if the current runtime is Node.js. | ||
@@ -57,25 +57,7 @@ */ | ||
function initializeRhino(args) { | ||
// note: mutates args | ||
function getDirname() { | ||
var dirname; | ||
// the JSDoc dirname is the main module URI, minus the filename, converted to a path | ||
var uriParts = require.main.uri.split('/'); | ||
uriParts.pop(); | ||
// Rhino has no native way to get the base dirname of the current script, | ||
// so this information must be manually passed in from the command line. | ||
for (var i = 0, l = args.length; i < l; i++) { | ||
if ( /^--dirname(?:=(.+?)(\/|\/\.)?)?$/i.test(args[i]) ) { | ||
if (RegExp.$1) { | ||
dirname = RegExp.$1; // last wins | ||
args.splice(i--, 1); // remove --dirname opt from arguments | ||
} | ||
else { | ||
dirname = args[i + 1]; | ||
args.splice(i--, 2); | ||
} | ||
} | ||
} | ||
return dirname; | ||
} | ||
env.dirname = getDirname(); | ||
env.dirname = String( new java.io.File(new java.net.URI(uriParts.join('/'))) ); | ||
env.pwd = String( java.lang.System.getenv().get('PWD') ); | ||
@@ -92,3 +74,3 @@ env.args = args; | ||
var jsdocPath = args[0]; | ||
var pwd = process.env.PWD || args[1]; | ||
var pwd = args[1]; | ||
@@ -114,3 +96,3 @@ // resolve the path if it's a symlink | ||
default: | ||
throw new Error('Unable to initialize the JavaScript runtime!'); | ||
throw new Error('Cannot initialize the unknown JavaScript runtime "' + runtime + '"!'); | ||
} | ||
@@ -117,0 +99,0 @@ }; |
@@ -127,3 +127,3 @@ /*global env: true */ | ||
err = new Error('unable to parse ' + longname + ': ' + e.message); | ||
require('jsdoc/util/error').handle(err); | ||
require('jsdoc/util/logger').error(err); | ||
return longname; | ||
@@ -289,3 +289,3 @@ } | ||
if (!node) { | ||
require('jsdoc/util/error').handle( new Error('No such tutorial: '+tutorial) ); | ||
require('jsdoc/util/logger').error( new Error('No such tutorial: ' + tutorial) ); | ||
return; | ||
@@ -322,3 +322,3 @@ } | ||
if (!tutorial) { | ||
require('jsdoc/util/error').handle( new Error('Missing required parameter: tutorial') ); | ||
require('jsdoc/util/logger').error( new Error('Missing required parameter: tutorial') ); | ||
return; | ||
@@ -325,0 +325,0 @@ } |
@@ -264,2 +264,17 @@ # License # | ||
## node-browser-builtins ## | ||
Portions of the node-browser-builtins source code are incorporated into the | ||
following files: | ||
- `rhino/assert.js` | ||
- `rhino/rhino-shim.js` | ||
node-browser-builtins is distributed under the MIT license, which is reproduced | ||
above. | ||
The source code for node-browser-builtins is available at: | ||
https://github.com/alexgorbatchev/node-browser-builtins | ||
## node-browserify ## | ||
@@ -360,2 +375,11 @@ | ||
## tv4 ## | ||
tv4 is in the public domain. It is also distributed under the MIT license, which | ||
is reproduced above. | ||
The source code for tv4 is available at: | ||
https://github.com/geraintluff/tv4 | ||
## Underscore.js ## | ||
@@ -362,0 +386,0 @@ |
@@ -12,25 +12,58 @@ #!/usr/bin/env node | ||
fs.symlink(symlinkSrc, symlinkDest, 'dir', function(err) { | ||
if (err) { | ||
// On Windows, try to create a junction instead | ||
if (process.platform.indexOf('win') === 0) { | ||
fs.symlink(symlinkSrc, symlinkDest, 'junction', function(junctionErr) { | ||
if (junctionErr) { | ||
console.error('Unable to create a symbolic link or junction from %s to %s.\n' + | ||
'Symbolic link result: %s\nJunction result: %s\n' + | ||
'Make sure you have write privileges in the target directory. ' + | ||
'You may need to run the Windows shell as an administrator.', | ||
symlinkSrc, symlinkDest, err, junctionErr); | ||
process.exit(1); | ||
} | ||
else { | ||
process.exit(0); | ||
} | ||
}); | ||
function createJunction(err) { | ||
fs.symlink(symlinkSrc, symlinkDest, 'junction', function(junctionErr) { | ||
if (junctionErr) { | ||
console.error('Unable to create a symbolic link or junction from %s to %s.\n' + | ||
'Symbolic link result: %s\nJunction result: %s\n' + | ||
'Make sure you have write privileges in the target directory. ' + | ||
'You may need to run the Windows shell as an administrator.', | ||
symlinkSrc, symlinkDest, err, junctionErr); | ||
process.exit(1); | ||
} | ||
else { | ||
console.error('Unable to create a symbolic link from %s to %s. %s\n', | ||
symlinkSrc, symlinkDest, err); | ||
process.exit(0); | ||
} | ||
}); | ||
} | ||
function checkLink() { | ||
fs.readlink(symlinkDest, function(readlinkErr, linkString) { | ||
if (!readlinkErr) { | ||
linkString = path.resolve(path.dirname(symlinkDest), linkString); | ||
if (linkString === symlinkSrc) { | ||
// the existing symlink points to the right place | ||
process.exit(0); | ||
} | ||
else { | ||
console.error('The symlink at %s points to %s, but it should point to %s. ' + | ||
'Please remove the symlink and try again.', symlinkDest, linkString, | ||
symlinkSrc); | ||
process.exit(1); | ||
} | ||
} | ||
else { | ||
console.error('Unable to read the symlink at %s. Please remove the symlink and try ' + | ||
'again', symlinkDest); | ||
process.exit(1); | ||
} | ||
}); | ||
} | ||
fs.symlink(symlinkSrc, symlinkDest, 'dir', function(err) { | ||
if (err) { | ||
// Does the symlink already exist? If so, does it point to the right place? | ||
fs.lstat(symlinkDest, function(lstatErr, stats) { | ||
if ( stats && stats.isSymbolicLink() ) { | ||
checkLink(); | ||
} | ||
// On Windows, try to create a junction instead | ||
else if (process.platform.indexOf('win') === 0) { | ||
createJunction(); | ||
} | ||
else { | ||
console.error('Unable to create a symbolic link from %s to %s. %s\n', symlinkSrc, | ||
symlinkDest, err); | ||
process.exit(1); | ||
} | ||
}); | ||
} | ||
@@ -37,0 +70,0 @@ else { |
{ | ||
"name": "jsdoc", | ||
"version": "3.3.0-alpha2", | ||
"revision": "1384269864915", | ||
"version": "3.3.0-alpha3", | ||
"revision": "1387991255485", | ||
"description": "An API documentation generator for JavaScript.", | ||
"keywords": [ "documentation", "javascript" ], | ||
"keywords": [ | ||
"documentation", | ||
"javascript" | ||
], | ||
"licenses": [ | ||
{ | ||
"type": "Apache 2.0", | ||
"url": "http://www.apache.org/licenses/LICENSE-2.0" | ||
"url": "http://www.apache.org/licenses/LICENSE-2.0" | ||
} | ||
@@ -15,16 +18,19 @@ ], | ||
"type": "git", | ||
"url": "https://github.com/jsdoc3/jsdoc" | ||
"url": "https://github.com/jsdoc3/jsdoc" | ||
}, | ||
"dependencies": { | ||
"async": "0.1.22", | ||
"catharsis": "0.7.0", | ||
"esprima": "1.0.4", | ||
"js2xmlparser": "0.1.0", | ||
"marked": "0.2.8", | ||
"taffydb": "git+https://github.com/hegemonic/taffydb.git", | ||
"underscore": "1.4.2", | ||
"wrench": "1.3.9" | ||
"async": "~0.1.22", | ||
"catharsis": "~0.7.0", | ||
"esprima": "~1.0.4", | ||
"js2xmlparser": "~0.1.0", | ||
"marked": "~0.2.8", | ||
"taffydb": "git+https://github.com/hegemonic/taffydb.git", | ||
"underscore": "~1.4.2", | ||
"wrench": "~1.3.9" | ||
}, | ||
"devDependencies": { | ||
"jshint": "2.3.0" | ||
"grunt": "~0.4.2", | ||
"grunt-bumpup": "~0.4.2", | ||
"jshint": "~2.3.0", | ||
"tv4": "git+https://github.com/hegemonic/tv4.git#own-properties" | ||
}, | ||
@@ -42,3 +48,3 @@ "engines": { | ||
"author": { | ||
"name": "Michael Mathews", | ||
"name": "Michael Mathews", | ||
"email": "micmath@gmail.com" | ||
@@ -45,0 +51,0 @@ }, |
@@ -7,2 +7,4 @@ /** | ||
var logger = require('jsdoc/util/logger'); | ||
exports.handlers = { | ||
@@ -36,3 +38,4 @@ /** | ||
catch(e) { | ||
throw new Error('@source tag expects a valid JSON value, like { "filename": "myfile.js", "lineno": 123 }.'); | ||
logger.error('@source tag expects a valid JSON value, like { "filename": "myfile.js", "lineno": 123 }.'); | ||
return; | ||
} | ||
@@ -46,2 +49,2 @@ | ||
} | ||
}; | ||
}; |
@@ -38,5 +38,5 @@ JSDoc 3 | ||
./node_modules/bin/jsdoc yourJavaScriptFile.js | ||
./node_modules/.bin/jsdoc yourJavaScriptFile.js | ||
Or if you installed JSDoc globally: | ||
Or if you installed JSDoc globally, simply run the `jsdoc` command: | ||
@@ -43,0 +43,0 @@ jsdoc yourJavaScriptFile.js |
@@ -8,3 +8,3 @@ /*global env: true */ | ||
taffy = require('taffydb').taffy, | ||
handle = require('jsdoc/util/error').handle, | ||
logger = require('jsdoc/util/logger'), | ||
helper = require('jsdoc/util/templateHelper'), | ||
@@ -93,8 +93,6 @@ htmlsafe = helper.htmlsafe, | ||
function shortenPaths(files, commonPrefix) { | ||
// always use forward slashes | ||
var regexp = new RegExp('\\\\', 'g'); | ||
Object.keys(files).forEach(function(file) { | ||
files[file].shortened = files[file].resolved.replace(commonPrefix, '') | ||
.replace(regexp, '/'); | ||
// always use forward slashes | ||
.replace(/\\/g, '/'); | ||
}); | ||
@@ -111,3 +109,3 @@ | ||
return doclet.meta.path && doclet.meta.path !== 'null' ? | ||
doclet.meta.path + '/' + doclet.meta.filename : | ||
path.join(doclet.meta.path, doclet.meta.filename) : | ||
doclet.meta.filename; | ||
@@ -149,3 +147,3 @@ } | ||
catch(e) { | ||
handle(e); | ||
logger.error('Error while generating source file %s: %s', file, e.message); | ||
} | ||
@@ -333,3 +331,6 @@ | ||
// set up templating | ||
view.layout = 'layout.tmpl'; | ||
view.layout = conf['default'].layoutFile ? | ||
path.getResourcePath(path.dirname(conf['default'].layoutFile), | ||
path.basename(conf['default'].layoutFile) ) : | ||
'layout.tmpl'; | ||
@@ -377,3 +378,5 @@ // set up tutorials for helper | ||
}; | ||
sourceFilePaths.push(sourcePath); | ||
if (sourceFilePaths.indexOf(sourcePath) === -1) { | ||
sourceFilePaths.push(sourcePath); | ||
} | ||
} | ||
@@ -380,0 +383,0 @@ }); |
(function() { | ||
var counter = 0; | ||
var numbered; | ||
var source = document.getElementsByClassName('prettyprint source'); | ||
var source = document.getElementsByClassName('prettyprint source linenums'); | ||
var i = 0; | ||
var lineNumber = 0; | ||
var lineId; | ||
var lines; | ||
var totalLines; | ||
var anchorHash; | ||
if (source && source[0]) { | ||
source = source[0].getElementsByTagName('code')[0]; | ||
anchorHash = document.location.hash.substring(1); | ||
lines = source[0].getElementsByTagName('li'); | ||
totalLines = lines.length; | ||
numbered = source.innerHTML.split('\n'); | ||
numbered = numbered.map(function(item) { | ||
counter++; | ||
return '<span id="line' + counter + '" class="line"></span>' + item; | ||
}); | ||
source.innerHTML = numbered.join('\n'); | ||
for (; i < totalLines; i++) { | ||
lineNumber++; | ||
lineId = 'line' + lineNumber; | ||
lines[i].id = lineId; | ||
if (lineId === anchorHash) { | ||
lines[i].className += ' selected'; | ||
} | ||
} | ||
} | ||
})(); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
414600
9268
21
4
99
+ Addedcatharsis@0.7.1(transitive)
+ Addedjs2xmlparser@0.1.9(transitive)
+ Addedmarked@0.2.10(transitive)
+ Addedunderscore@1.4.4(transitive)
- Removedcatharsis@0.7.0(transitive)
- Removedjs2xmlparser@0.1.0(transitive)
- Removedmarked@0.2.8(transitive)
- Removedunderscore@1.4.2(transitive)
Updatedasync@~0.1.22
Updatedcatharsis@~0.7.0
Updatedesprima@~1.0.4
Updatedjs2xmlparser@~0.1.0
Updatedmarked@~0.2.8
Updatedunderscore@~1.4.2
Updatedwrench@~1.3.9