Comparing version 3.3.0-alpha5 to 3.3.0-alpha7
97
cli.js
@@ -0,1 +1,3 @@ | ||
/*global java */ | ||
/*eslint no-process-exit:0 */ | ||
/** | ||
@@ -18,2 +20,3 @@ * Helper methods for running JSDoc on the command line. | ||
var logger = require('jsdoc/util/logger'); | ||
var stripJsonComments = require('strip-json-comments'); | ||
@@ -88,3 +91,3 @@ var hasOwnProp = Object.prototype.hasOwnProperty; | ||
try { | ||
env.conf = new Config( fs.readFileSync(confPath, 'utf8') ) | ||
env.conf = new Config( stripJsonComments(fs.readFileSync(confPath, 'utf8')) ) | ||
.get(); | ||
@@ -234,2 +237,6 @@ } | ||
} | ||
else { | ||
console.log('There are no input files to process.\n'); | ||
cli.printHelp(cb); | ||
} | ||
@@ -248,74 +255,2 @@ env.run.finish = new Date(); | ||
// 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 = tempPath; | ||
} | ||
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() { | ||
@@ -352,3 +287,3 @@ var Filter = require('jsdoc/src/filter').Filter; | ||
env.sourceFiles = app.jsdoc.scanner.scan(env.opts._, (env.opts.recurse? 10 : undefined), | ||
env.sourceFiles = app.jsdoc.scanner.scan(env.opts._, (env.opts.recurse ? 10 : undefined), | ||
filter); | ||
@@ -375,6 +310,2 @@ } | ||
} | ||
// On Node.js, the plugin needs to be inside the JSDoc directory | ||
else if ( isNode && (pluginPath.indexOf(global.env.dirname) !== 0) ) { | ||
pluginPath = copyResourceDir(pluginPath); | ||
} | ||
@@ -408,3 +339,4 @@ pluginPaths.push( path.join(pluginPath, basename) ); | ||
var borrow = require('jsdoc/borrow'); | ||
var Package = require('jsdoc/package').Package; | ||
// Prevent Requizzle from picking up package.json. See #662. | ||
var Package = require('./lib/jsdoc/package').Package; | ||
@@ -472,9 +404,2 @@ var docs; | ||
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 | ||
@@ -481,0 +406,0 @@ // useful |
42
jsdoc.js
#!/usr/bin/env node | ||
/*global arguments, require: true */ | ||
/** | ||
@@ -86,8 +87,21 @@ * @project jsdoc | ||
(function(args) { | ||
'use strict'; | ||
var path; | ||
if (args[0] && typeof args[0] === 'object') { | ||
// we should be on Node.js | ||
args = [__dirname, process.cwd()]; | ||
path = require('path'); | ||
// Create a custom require method that adds `lib/jsdoc` to the module lookup path. | ||
// This makes it possible to `require('jsdoc/foo')` from external templates and plugins, | ||
// and within JSDoc itself. | ||
require = require('requizzle')({ | ||
requirePaths: [path.join(__dirname, 'lib')], | ||
infect: true | ||
}); | ||
} | ||
require('jsdoc/util/runtime').initialize(args); | ||
require('./lib/jsdoc/util/runtime').initialize(args); | ||
})( Array.prototype.slice.call(arguments, 0) ); | ||
@@ -103,5 +117,5 @@ | ||
jsdoc: { | ||
scanner: new (require('jsdoc/src/scanner').Scanner)(), | ||
name: require('./lib/jsdoc/name'), | ||
parser: null, | ||
name: require('jsdoc/name') | ||
scanner: new (require('./lib/jsdoc/src/scanner').Scanner)() | ||
} | ||
@@ -121,4 +135,6 @@ }; | ||
global.dump = function() { | ||
var doop = require('jsdoc/util/doop').doop; | ||
var _dump = require('jsdoc/util/dumper').dump; | ||
'use strict'; | ||
var doop = require('./lib/jsdoc/util/doop').doop; | ||
var _dump = require('./lib/jsdoc/util/dumper').dump; | ||
for (var i = 0, l = arguments.length; i < l; i++) { | ||
@@ -132,7 +148,10 @@ console.log( _dump(doop(arguments[i])) ); | ||
var logger = require('jsdoc/util/logger'); | ||
var path = require('jsdoc/path'); | ||
var runtime = require('jsdoc/util/runtime'); | ||
var logger = require('./lib/jsdoc/util/logger'); | ||
var runtime = require('./lib/jsdoc/util/runtime'); | ||
var cli = require('./cli'); | ||
var cli = require( path.join(global.env.dirname, 'cli') ); | ||
function cb(errorCode) { | ||
cli.logFinish(); | ||
cli.exit(errorCode || 0); | ||
} | ||
@@ -148,7 +167,2 @@ cli.setVersionInfo() | ||
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) | ||
@@ -155,0 +169,0 @@ if ( runtime.isRhino() ) { |
@@ -12,3 +12,3 @@ 'use strict'; | ||
doc = doclets[i]; | ||
if (doc.kind === "class" || doc.kind === "external") { | ||
if (doc.kind === 'class' || doc.kind === 'external') { | ||
dependencies[name] = {}; | ||
@@ -67,3 +67,3 @@ len = doc.augments && doc.augments.length || 0; | ||
candidate = docs[i]; | ||
if (candidate.memberof === longname && candidate.scope === "instance") { | ||
if (candidate.memberof === longname && candidate.scope === 'instance') { | ||
members.push(candidate); | ||
@@ -76,3 +76,3 @@ } | ||
function getAdditions(doclets, docs, longnames) { | ||
var doop = require("jsdoc/util/doop").doop; | ||
var doop = require('jsdoc/util/doop'); | ||
@@ -92,3 +92,3 @@ var additions = []; | ||
parents = doc.augments; | ||
if (parents && doc.kind === "class") { | ||
if (parents && doc.kind === 'class') { | ||
for (var j = 0, jj = parents.length; j < jj; j++) { | ||
@@ -106,5 +106,5 @@ members = getMembers(parents[j], docs); | ||
member.memberof = doc.longname; | ||
parts = member.longname.split("#"); | ||
parts = member.longname.split('#'); | ||
parts[0] = doc.longname; | ||
member.longname = parts.join("#"); | ||
member.longname = parts.join('#'); | ||
@@ -111,0 +111,0 @@ // if the child doesn't override the parent member, add the parent member |
@@ -34,2 +34,3 @@ /** | ||
exports.resolveBorrows = function(docs) { | ||
/*eslint max-nested-callbacks:[2, 3] */ | ||
if (!docs.index) { | ||
@@ -59,3 +60,4 @@ logger.error('Unable to resolve borrowed symbols, because the docs have not been indexed.'); | ||
clone.memberof = doc.longname; | ||
clone.longname = clone.memberof + (clone.scope === 'instance'? '#': '.') + clone.name; | ||
clone.longname = clone.memberof + (clone.scope === 'instance' ? '#' : '.') + | ||
clone.name; | ||
docs.push(clone); | ||
@@ -62,0 +64,0 @@ }); |
@@ -28,14 +28,14 @@ /** | ||
var defaults = { | ||
"tags": { | ||
"allowUnknownTags": true | ||
tags: { | ||
allowUnknownTags: true | ||
}, | ||
"templates": { | ||
"monospaceLinks": false, | ||
"cleverLinks": false | ||
templates: { | ||
monospaceLinks: false, | ||
cleverLinks: false | ||
}, | ||
"source": { | ||
"includePattern": ".+\\.js(doc)?$", | ||
"excludePattern": "" | ||
source: { | ||
includePattern: '.+\\.js(doc)?$', | ||
excludePattern: '' | ||
}, | ||
"plugins": [] | ||
plugins: [] | ||
}; | ||
@@ -49,3 +49,3 @@ | ||
function Config(json) { | ||
json = JSON.parse( (json || "{}") ); | ||
json = JSON.parse( (json || '{}') ); | ||
this._config = mergeRecurse(defaults, json); | ||
@@ -52,0 +52,0 @@ } |
@@ -16,2 +16,3 @@ /** | ||
src: { | ||
astnode: require('jsdoc/src/astnode'), | ||
Syntax: require('jsdoc/src/syntax').Syntax | ||
@@ -28,11 +29,5 @@ }, | ||
var path = require('jsdoc/path'); | ||
var Syntax = jsdoc.src.Syntax; | ||
var util = require('util'); | ||
// Longname used for doclets whose actual longname cannot be identified. | ||
exports.ANONYMOUS_LONGNAME = '<anonymous>'; | ||
// Longname used for doclets in global scope. | ||
exports.GLOBAL_LONGNAME = '<global>'; | ||
// Special tag identifying undocumented symbols; not to be used in actual JSDoc comments. | ||
exports.UNDOCUMENTED_TAG = '@undocumented'; | ||
function applyTag(doclet, tag) { | ||
@@ -53,9 +48,21 @@ if (tag.title === 'name') { | ||
// use the meta info about the source code to guess what the doclet kind should be | ||
function codetypeToKind(type) { | ||
var Syntax = jsdoc.src.Syntax; | ||
if (type === Syntax.FunctionDeclaration || type === Syntax.FunctionExpression) { | ||
return 'function'; | ||
function codeToKind(code) { | ||
var parent; | ||
var isFunction = jsdoc.src.astnode.isFunction; | ||
// default | ||
var kind = 'member'; | ||
if (code.type === Syntax.FunctionDeclaration || code.type === Syntax.FunctionExpression) { | ||
kind = 'function'; | ||
} | ||
else if (code.node && code.node.parent) { | ||
parent = code.node.parent; | ||
if ( isFunction(parent) ) { | ||
kind = 'param'; | ||
} | ||
} | ||
return 'member'; | ||
return kind; | ||
} | ||
@@ -72,3 +79,3 @@ | ||
docletSrc.replace(/^\/\*\*+/, '') // remove opening slash+stars | ||
.replace(/\**\*\/$/, "\\Z") // replace closing star slash with end-marker | ||
.replace(/\**\*\/$/, '\\Z') // replace closing star slash with end-marker | ||
.replace(/^\s*(\* ?|\\Z)/gm, '') // remove left margin like: spaces+star or spaces+end-marker | ||
@@ -173,7 +180,7 @@ .replace(/\s*\\Z$/g, ''); // remove end-marker | ||
if (this.memberof === '') { | ||
delete(this.memberof); | ||
delete this.memberof; | ||
} | ||
if (!this.kind && this.meta && this.meta.code) { | ||
this.addTag( 'kind', codetypeToKind(this.meta.code.type) ); | ||
this.addTag( 'kind', codeToKind(this.meta.code) ); | ||
} | ||
@@ -218,3 +225,3 @@ | ||
function removeGlobal(longname) { | ||
var globalRegexp = new RegExp('^' + exports.GLOBAL_LONGNAME + '\\.?'); | ||
var globalRegexp = new RegExp('^' + jsdoc.name.GLOBAL_LONGNAME + '\\.?'); | ||
@@ -221,0 +228,0 @@ return longname.replace(globalRegexp, ''); |
@@ -11,7 +11,10 @@ /** | ||
var _ = require('underscore'); | ||
var jsdoc = { | ||
doclet: require('jsdoc/doclet'), | ||
tagDictionary: require('jsdoc/tag/dictionary') | ||
}; | ||
// Longname used for doclets whose actual longname cannot be identified. | ||
var ANONYMOUS_LONGNAME = exports.ANONYMOUS_LONGNAME = '<anonymous>'; | ||
// Longname used for doclets in global scope. | ||
var GLOBAL_LONGNAME = exports.GLOBAL_LONGNAME = '<global>'; | ||
var INNER = exports.INNER = '~'; | ||
var INSTANCE = exports.INSTANCE = '#'; | ||
var MODULE_PREFIX = exports.MODULE_PREFIX = 'module:'; | ||
// Scope identifiers. | ||
@@ -24,4 +27,2 @@ var SCOPE_NAMES = exports.SCOPE_NAMES = { | ||
}; | ||
var INNER = exports.INNER = '~'; | ||
var INSTANCE = exports.INSTANCE = '#'; | ||
var STATIC = exports.STATIC = '.'; | ||
@@ -35,6 +36,15 @@ var scopeToPunc = exports.scopeToPunc = { | ||
var MODULE_PREFIX = exports.MODULE_PREFIX = 'module:'; | ||
var DEFAULT_SCOPE = SCOPE_NAMES.static; | ||
var REGEXP_SCOPE_PUNC = '([' + INNER + INSTANCE + STATIC + '])'; | ||
function nameIsLongname(name, memberof) { | ||
var regexp = new RegExp('^' + memberof + REGEXP_SCOPE_PUNC); | ||
return regexp.test(name); | ||
} | ||
function prototypeToPunc(name) { | ||
return name.replace(/(?:^|\.)prototype\.?/g, INSTANCE); | ||
} | ||
/** | ||
@@ -45,10 +55,10 @@ Resolves the longname, memberof, variation and name values of the given doclet. | ||
exports.resolve = function(doclet) { | ||
var name = doclet.name ? String(doclet.name) : '', | ||
memberof = doclet.memberof || '', | ||
about = {}, | ||
scopePunc = '([' + INNER + INSTANCE + STATIC + '])', | ||
leadingScope = new RegExp('^' + scopePunc), | ||
trailingScope = new RegExp(scopePunc + '$'), | ||
parentDoc; | ||
var about = {}; | ||
var leadingScope = new RegExp('^' + REGEXP_SCOPE_PUNC); | ||
var memberof = doclet.memberof || ''; | ||
var name = doclet.name ? String(doclet.name) : ''; | ||
var trailingScope = new RegExp(REGEXP_SCOPE_PUNC + '$'); | ||
var parentDoc; | ||
// change MyClass.prototype.instanceMethod to MyClass#instanceMethod | ||
@@ -58,3 +68,3 @@ // (but not in function params, which lack doclet.kind) | ||
if (name && doclet.kind) { | ||
name = name.replace(/(?:^|\.)prototype\.?/g, INSTANCE); | ||
name = prototypeToPunc(name); | ||
} | ||
@@ -69,6 +79,6 @@ doclet.name = name; | ||
if (memberof || doclet.forceMemberof) { // @memberof tag given | ||
memberof = ('' || memberof).replace(/\.prototype\.?/g, INSTANCE); | ||
memberof = prototypeToPunc(memberof); | ||
// the name is a fullname, like @name foo.bar, @memberof foo | ||
if (name && name.indexOf(memberof) === 0 && name !== memberof) { | ||
// the name is a complete longname, like @name foo.bar, @memberof foo | ||
if (name && nameIsLongname(name, memberof) && name !== memberof) { | ||
about = exports.shorten(name, (doclet.forceMemberof ? memberof : undefined)); | ||
@@ -108,3 +118,3 @@ } | ||
if (about.longname && !doclet.longname) { | ||
if (about.longname && (!doclet.longname || doclet.longname === doclet.name)) { | ||
doclet.setLongname(about.longname); | ||
@@ -118,3 +128,3 @@ } | ||
else if (about.scope) { | ||
if (about.memberof === jsdoc.doclet.GLOBAL_LONGNAME) { // via @memberof <global> ? | ||
if (about.memberof === GLOBAL_LONGNAME) { // via @memberof <global> ? | ||
doclet.scope = 'global'; | ||
@@ -143,25 +153,13 @@ } | ||
} | ||
}; | ||
/** | ||
@inner | ||
@memberof module:jsdoc/name | ||
@param {string} name | ||
@param {string} kind | ||
@returns {string} The name with unsafe names enclosed in quotes. | ||
*/ | ||
function quoteUnsafe(name, kind) { // docspaced names may have unsafe characters which need to be quoted by us | ||
if ( (jsdoc.tagDictionary.lookUp(kind).setsDocletDocspace) && /[^$_a-zA-Z0-9\/]/.test(name) ) { | ||
if (!/^[a-z_$-\/]+:\"/i.test(name)) { | ||
return '"' + name.replace(/\"/g, '"') + '"'; | ||
} | ||
// if we never found a longname, just use an empty string | ||
if (!doclet.longname) { | ||
doclet.longname = ''; | ||
} | ||
}; | ||
return name; | ||
} | ||
// TODO: make this a private method, or remove it if possible | ||
RegExp.escape = RegExp.escape || function(str) { | ||
var specials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g"); // .*+?|()[]{}\ | ||
return str.replace(specials, "\\$&"); | ||
var specials = new RegExp('[.*+?|()\\[\\]{}\\\\]', 'g'); // .*+?|()[]{}\ | ||
return str.replace(specials, '\\$&'); | ||
}; | ||
@@ -181,3 +179,3 @@ | ||
if ( !/^[a-zA-Z]+?:.+$/i.test(name) ) { | ||
longname = longname.replace( new RegExp(RegExp.escape(name)+'$'), ns + ':' + name ); | ||
longname = longname.replace( new RegExp(RegExp.escape(name) + '$'), ns + ':' + name ); | ||
} | ||
@@ -219,3 +217,3 @@ | ||
longname = longname.replace(/\.prototype\.?/g, INSTANCE); | ||
longname = prototypeToPunc(longname); | ||
@@ -230,5 +228,5 @@ if (typeof forcedMemberof !== 'undefined') { | ||
else { | ||
parts = longname? | ||
(longname.match( /^(:?(.+)([#.~]))?(.+?)$/ ) || []).reverse() | ||
: ['']; | ||
parts = longname ? | ||
(longname.match( /^(:?(.+)([#.~]))?(.+?)$/ ) || []).reverse() : | ||
['']; | ||
@@ -249,6 +247,6 @@ name = parts[0] || ''; // ensure name is always initialised to avoid error being thrown when calling replace on undefined [gh-24] | ||
while (i--) { | ||
longname = longname.replace('@{'+i+'}@', atoms[i]); | ||
memberof = memberof.replace('@{'+i+'}@', atoms[i]); | ||
scope = scope.replace('@{'+i+'}@', atoms[i]); | ||
name = name.replace('@{'+i+'}@', atoms[i]); | ||
longname = longname.replace('@{' + i + '}@', atoms[i]); | ||
memberof = memberof.replace('@{' + i + '}@', atoms[i]); | ||
scope = scope.replace('@{' + i + '}@', atoms[i]); | ||
name = name.replace('@{' + i + '}@', atoms[i]); | ||
} | ||
@@ -255,0 +253,0 @@ |
@@ -53,2 +53,4 @@ /** | ||
} | ||
return this; | ||
}; | ||
@@ -64,2 +66,3 @@ | ||
* @param {function} [coercer] A function to coerce the given value to a specific type. | ||
* @return {this} | ||
* @example | ||
@@ -79,3 +82,3 @@ * myParser.addOption('t', 'template', true, 'The path to the template.'); | ||
this._addOption(option); | ||
return this._addOption(option); | ||
}; | ||
@@ -101,3 +104,3 @@ | ||
this._addOption(option); | ||
return this._addOption(option); | ||
}; | ||
@@ -231,3 +234,3 @@ | ||
var arg = '' + args[i], | ||
next = (i < leni-1)? '' + args[i+1] : null, | ||
next = (i < leni - 1) ? '' + args[i + 1] : null, | ||
option, | ||
@@ -234,0 +237,0 @@ shortName = null, |
@@ -108,3 +108,3 @@ /** | ||
if (typeof args === 'string' || args.constructor === String) { | ||
args = (''+args).split(/\s+/g); | ||
args = String(args).split(/\s+/g); | ||
} | ||
@@ -111,0 +111,0 @@ |
@@ -19,3 +19,3 @@ /** | ||
exports.Package = function(json) { | ||
json = json || "{}"; | ||
json = json || '{}'; | ||
@@ -22,0 +22,0 @@ /** The source files associated with this package. |
@@ -11,2 +11,8 @@ /*global app: true */ | ||
function addHandlers(handlers, parser) { | ||
Object.keys(handlers).forEach(function(eventName) { | ||
parser.on(eventName, handlers[eventName]); | ||
}); | ||
} | ||
exports.installPlugins = function(plugins, parser) { | ||
@@ -24,5 +30,3 @@ var dictionary = require('jsdoc/tag/dictionary'); | ||
if (plugin.handlers) { | ||
Object.keys(plugin.handlers).forEach(function(eventName) { | ||
parser.on(eventName, plugin.handlers[eventName]); | ||
}); | ||
addHandlers(plugin.handlers, parser); | ||
} | ||
@@ -29,0 +33,0 @@ |
@@ -21,2 +21,5 @@ /** | ||
var BOOLEAN_OPTIONAL = [BOOLEAN, NULL, UNDEFINED]; | ||
var STRING_OPTIONAL = [STRING, NULL, UNDEFINED]; | ||
var EVENT_REGEXP = /event\:[\S]+/; | ||
@@ -124,9 +127,7 @@ var PACKAGE_REGEXP = /package\:[\S]+/; | ||
defaultvalue: { | ||
// TODO: stop adding property if it's empty | ||
type: [STRING, NULL, UNDEFINED], | ||
type: STRING_OPTIONAL, | ||
optional: true | ||
}, | ||
description: { | ||
// TODO: stop adding property if it's empty | ||
type: [STRING, NULL, UNDEFINED], | ||
type: STRING_OPTIONAL, | ||
optional: true | ||
@@ -150,2 +151,10 @@ }, | ||
}, | ||
// is this member nullable? (derived from the type expression) | ||
nullable: { | ||
type: BOOLEAN_OPTIONAL | ||
}, | ||
// is this member optional? (derived from the type expression) | ||
optional: { | ||
type: BOOLEAN_OPTIONAL | ||
}, | ||
scope: { | ||
@@ -156,3 +165,7 @@ type: STRING, | ||
}, | ||
type: TYPE_PROPERTY_SCHEMA | ||
type: TYPE_PROPERTY_SCHEMA, | ||
// can this member be provided more than once? (derived from the type expression) | ||
variable: { | ||
type: BOOLEAN_OPTIONAL | ||
} | ||
} | ||
@@ -168,4 +181,3 @@ }; | ||
defaultvalue: { | ||
// TODO: stop adding property if it's empty | ||
type: [STRING, NULL, UNDEFINED], | ||
type: STRING_OPTIONAL, | ||
optional: true | ||
@@ -175,4 +187,3 @@ }, | ||
description: { | ||
// TODO: stop adding property if it's empty | ||
type: [STRING, NULL, UNDEFINED], | ||
type: STRING_OPTIONAL, | ||
optional: true | ||
@@ -186,4 +197,3 @@ }, | ||
nullable: { | ||
// TODO: stop adding property if it's empty | ||
type: [BOOLEAN, NULL, UNDEFINED], | ||
type: BOOLEAN_OPTIONAL, | ||
optional: true | ||
@@ -193,4 +203,3 @@ }, | ||
optional: { | ||
// TODO: stop adding property if it's empty | ||
type: [BOOLEAN, NULL, UNDEFINED], | ||
type: BOOLEAN_OPTIONAL, | ||
optional: true | ||
@@ -202,4 +211,3 @@ }, | ||
variable: { | ||
// TODO: stop adding property if it's empty | ||
type: [BOOLEAN, NULL, UNDEFINED], | ||
type: BOOLEAN_OPTIONAL, | ||
optional: true | ||
@@ -281,3 +289,3 @@ } | ||
optional: true, | ||
enum: [OBJECT] | ||
enum: [OBJECT, ARRAY] | ||
}, | ||
@@ -291,4 +299,3 @@ // is usage of this symbol deprecated? | ||
description: { | ||
// TODO: stop adding property if it's empty | ||
type: [STRING, NULL, UNDEFINED], | ||
type: STRING_OPTIONAL, | ||
optional: true | ||
@@ -329,4 +336,3 @@ }, | ||
forceMemberof: { | ||
// TODO: stop adding property if it's empty | ||
type: [BOOLEAN, NULL, UNDEFINED], | ||
type: BOOLEAN_OPTIONAL, | ||
optional: true | ||
@@ -377,2 +383,3 @@ }, | ||
'package', | ||
'param', | ||
'typedef' | ||
@@ -416,2 +423,10 @@ ] | ||
}, | ||
// is this member nullable? (derived from the type expression) | ||
nullable: { | ||
type: BOOLEAN_OPTIONAL | ||
}, | ||
// is this member optional? (derived from the type expression) | ||
optional: { | ||
type: BOOLEAN_OPTIONAL | ||
}, | ||
// are there function parameters associated with this doc? | ||
@@ -540,2 +555,6 @@ params: { | ||
}, | ||
// can this member be provided more than once? (derived from the type expression) | ||
variable: { | ||
type: BOOLEAN_OPTIONAL | ||
}, | ||
variation: { | ||
@@ -542,0 +561,0 @@ type: STRING, |
@@ -9,5 +9,7 @@ 'use strict'; | ||
// TODO: docs | ||
var acceptsLeadingComments = exports.acceptsLeadingComments = (function() { | ||
// TODO: docs; empty array means any node type, otherwise only the node types in the array | ||
var acceptsLeadingComments = (function() { | ||
var accepts = {}; | ||
// these nodes always accept leading comments | ||
var commentable = [ | ||
@@ -25,7 +27,20 @@ Syntax.AssignmentExpression, | ||
]; | ||
for (var i = 0, l = commentable.length; i < l; i++) { | ||
accepts[commentable[i]] = true; | ||
accepts[commentable[i]] = []; | ||
} | ||
// these nodes accept leading comments if they have specific types of parent nodes | ||
// like: function foo(/** @type {string} */ bar) {} | ||
accepts[Syntax.Identifier] = [ | ||
Syntax.CatchClause, | ||
Syntax.FunctionDeclaration, | ||
Syntax.FunctionExpression | ||
]; | ||
// like: var Foo = Class.create(/** @lends Foo */{ // ... }) | ||
accepts[Syntax.ObjectExpression] = [ | ||
Syntax.CallExpression, | ||
Syntax.Property, | ||
Syntax.ReturnStatement | ||
]; | ||
return accepts; | ||
@@ -35,14 +50,19 @@ })(); | ||
// TODO: docs | ||
var searchDescendants = (function() { | ||
var search = {}; | ||
var shouldSearch = [ | ||
Syntax.CallExpression | ||
]; | ||
function canAcceptComment(node) { | ||
var canAccept = false; | ||
var spec = acceptsLeadingComments[node.type]; | ||
for (var i = 0, l = shouldSearch.length; i < l; i++) { | ||
search[shouldSearch[i]] = true; | ||
if (spec) { | ||
// empty array means we don't care about the parent type | ||
if (spec.length === 0) { | ||
canAccept = true; | ||
} | ||
// we can accept the comment if the spec contains the type of the node's parent | ||
else if (node.parent) { | ||
canAccept = spec.indexOf(node.parent.type) !== -1; | ||
} | ||
} | ||
return search; | ||
})(); | ||
return canAccept; | ||
} | ||
@@ -56,8 +76,2 @@ // TODO: docs | ||
// TODO: docs | ||
function isBetween(middleRange, beforeRange, afterRange) { | ||
return isBefore(middleRange, afterRange) && (beforeRange ? | ||
isBefore(beforeRange, middleRange) : true); | ||
} | ||
// TODO: docs | ||
function isWithin(innerRange, outerRange) { | ||
@@ -68,8 +82,2 @@ return innerRange[0] >= outerRange[0] && innerRange[1] <= outerRange[1]; | ||
// TODO: docs | ||
function isLeadingComment(comment, before, after) { | ||
return !!before && !!after && !!acceptsLeadingComments[after.type] && | ||
isBefore(before.range, after.range) && isBetween(comment.range, before.range, after.range); | ||
} | ||
// TODO: docs | ||
function isJsdocComment(comment) { | ||
@@ -109,2 +117,18 @@ return comment && (comment.type === 'Block') && (comment.value[0] === '*'); | ||
function parse(source, filename, esprimaOpts) { | ||
var esprima = require('esprima'); | ||
var logger = require('jsdoc/util/logger'); | ||
var ast; | ||
try { | ||
ast = esprima.parse(source, esprimaOpts); | ||
} | ||
catch (e) { | ||
logger.error('Unable to parse %s: %s', filename, e.message); | ||
} | ||
return ast; | ||
} | ||
// TODO: docs | ||
@@ -118,10 +142,11 @@ AstBuilder.prototype.build = function(source, filename) { | ||
range: true, | ||
// can probably remove when 1.1.0 is released | ||
raw: true, | ||
tokens: true | ||
}; | ||
ast = require('esprima').parse(source, esprimaOpts); | ||
this._postProcess(filename, ast); | ||
ast = parse(source, filename, esprimaOpts); | ||
if (ast) { | ||
this._postProcess(filename, ast); | ||
} | ||
return ast; | ||
@@ -157,2 +182,3 @@ }; | ||
// TODO: docs | ||
// TODO: export? | ||
function CommentAttacher(comments, tokens) { | ||
@@ -167,4 +193,2 @@ this._comments = comments || []; | ||
this._parentRange = []; | ||
this._resetPendingComment() | ||
@@ -200,7 +224,2 @@ ._resetCandidates(); | ||
// TODO: docs | ||
CommentAttacher.prototype._extractComments = function(index, count) { | ||
return this._comments.splice(index, count); | ||
}; | ||
// TODO: docs | ||
// find the index of the atom whose end position is closest to (but not after) the specified | ||
@@ -238,13 +257,5 @@ // position | ||
CommentAttacher.prototype._fastForwardComments = function(node) { | ||
var commentIndex; | ||
var position; | ||
var position = node.range[0]; | ||
var commentIndex = this._nextIndexBefore(0, this._comments, position); | ||
// don't fast-forward if there might be a sibling that can receive the comment | ||
if ( this._parentRange.length && isWithin(node.range, this._parentRange) ) { | ||
return; | ||
} | ||
position = node.range[0]; | ||
commentIndex = this._nextIndexBefore(0, this._comments, position); | ||
// all comments before the node (except the last one) are considered stray comments | ||
@@ -261,13 +272,15 @@ if (commentIndex > 0) { | ||
if (this._pendingComment) { | ||
if (this._candidates.length > 0) { | ||
target = this._candidates[this._candidates.length - 1]; | ||
target.leadingComments = target.leadingComments || []; | ||
target.leadingComments.push(this._pendingComment); | ||
} | ||
else { | ||
this._strayComments.push(this._pendingComment); | ||
} | ||
if (!this._pendingComment) { | ||
return this; | ||
} | ||
if (this._candidates.length > 0) { | ||
target = this._candidates[this._candidates.length - 1]; | ||
target.leadingComments = target.leadingComments || []; | ||
target.leadingComments.push(this._pendingComment); | ||
} | ||
else { | ||
this._strayComments.push(this._pendingComment); | ||
} | ||
this._resetPendingComment() | ||
@@ -306,7 +319,2 @@ ._resetCandidates(); | ||
// TODO: docs | ||
CommentAttacher.prototype._shouldSkipNode = function(node) { | ||
return !isWithin(node.range, this._pendingCommentRange) && !this._parentRange.length; | ||
}; | ||
// TODO: docs | ||
// TODO: do we ever get multiple candidate nodes? | ||
@@ -324,12 +332,2 @@ CommentAttacher.prototype.visit = function(node) { | ||
// clear the parent range if we've moved past it | ||
if ( this._parentRange.length && !isWithin(node.range, this._parentRange) ) { | ||
this._parentRange = []; | ||
} | ||
// if we need to search this node's descendants, set the range in which to search | ||
if (searchDescendants[node.type] && !this._parentRange.length) { | ||
this._parentRange = node.range.slice(0); | ||
} | ||
// move to the next token, and fast-forward past comments that can no longer be attached | ||
@@ -341,12 +339,8 @@ this._advanceTokenIndex(node); | ||
// if we already had a pending comment, and the current node is in the wrong place to be a | ||
// comment target, try attaching the comment | ||
if ( (this._pendingComment && !isWithin(node.range, this._pendingCommentRange)) || | ||
!searchDescendants[node.type] ) { | ||
this._attachPendingComment(); | ||
} | ||
// attach the pending comment, if there is one | ||
this._attachPendingComment(); | ||
// okay, now that we've done all that bookkeeping, we can check whether the current node accepts | ||
// leading comments and add it to the candidate list if needed | ||
if (isEligible && acceptsLeadingComments[node.type]) { | ||
if ( isEligible && canAcceptComment(node) ) { | ||
// make sure we don't go past the end of the outermost target node | ||
@@ -353,0 +347,0 @@ if (!this._pendingCommentRange) { |
// TODO: docs | ||
'use strict'; | ||
var doop = require('jsdoc/util/doop'); | ||
var Syntax = require('jsdoc/src/syntax').Syntax; | ||
@@ -12,5 +13,15 @@ var util = require('util'); | ||
// TODO: currently unused | ||
var GLOBAL_NODE_ID = exports.GLOBAL_NODE_ID = require('jsdoc/doclet').GLOBAL_LONGNAME; | ||
var GLOBAL_NODE_ID = exports.GLOBAL_NODE_ID = require('jsdoc/name').GLOBAL_LONGNAME; | ||
/** | ||
* Check whether an AST node represents a function. | ||
* | ||
* @param {Object} node - The AST node to check. | ||
* @return {boolean} Set to `true` if the node is a function or `false` in all other cases. | ||
*/ | ||
var isFunction = exports.isFunction = function(node) { | ||
return node.type === Syntax.FunctionDeclaration || node.type === Syntax.FunctionExpression; | ||
}; | ||
/** | ||
* Check whether an AST node creates a new scope. | ||
@@ -23,4 +34,4 @@ * | ||
// TODO: handle blocks with "let" declarations | ||
return !!node && typeof node === 'object' && (node.type === Syntax.CatchClause || | ||
node.type === Syntax.FunctionDeclaration || node.type === Syntax.FunctionExpression); | ||
return !!node && typeof node === 'object' && ( node.type === Syntax.CatchClause || | ||
isFunction(node) ); | ||
}; | ||
@@ -30,5 +41,4 @@ | ||
var addNodeProperties = exports.addNodeProperties = function(node) { | ||
var doop = require('jsdoc/util/doop'); | ||
var debugEnabled = !!global.env.opts.debug; | ||
var newProperties = {}; | ||
@@ -40,50 +50,50 @@ if (!node || typeof node !== 'object') { | ||
if (!node.nodeId) { | ||
Object.defineProperty(node, 'nodeId', { | ||
newProperties.nodeId = { | ||
value: 'astnode' + uid++, | ||
enumerable: debugEnabled | ||
}); | ||
}; | ||
} | ||
if (!node.parent && node.parent !== null) { | ||
Object.defineProperty(node, 'parent', { | ||
newProperties.parent = { | ||
// `null` means 'no parent', so use `undefined` for now | ||
value: undefined, | ||
writable: true | ||
}); | ||
}; | ||
} | ||
if (!node.enclosingScope && node.enclosingScope !== null) { | ||
Object.defineProperty(node, 'enclosingScope', { | ||
newProperties.enclosingScope = { | ||
// `null` means 'no enclosing scope', so use `undefined` for now | ||
value: undefined, | ||
writable: true | ||
}); | ||
}; | ||
} | ||
if (!node.knownVariables) { | ||
Object.defineProperty(node, 'knownVariables', { | ||
newProperties.knownVariables = { | ||
value: node.enclosingScope ? doop(node.enclosingScope.knownVariables) : {} | ||
}); | ||
}; | ||
} | ||
if (!node.ownedVariables) { | ||
Object.defineProperty(node, 'ownedVariables', { | ||
newProperties.ownedVariables = { | ||
value: {} | ||
}); | ||
}; | ||
} | ||
if (!node.knownAliases) { | ||
Object.defineProperty(node, 'knownAliases', { | ||
newProperties.knownAliases = { | ||
value: node.enclosingScope ? doop(node.enclosingScope.knownAliases) : {} | ||
}); | ||
}; | ||
} | ||
if (!node.ownedAliases) { | ||
Object.defineProperty(node, 'ownedAliases', { | ||
newProperties.ownedAliases = { | ||
value: {} | ||
}); | ||
}; | ||
} | ||
if (debugEnabled && !node.parentId) { | ||
Object.defineProperty(node, 'parentId', { | ||
newProperties.parentId = { | ||
enumerable: true, | ||
@@ -93,7 +103,7 @@ get: function() { | ||
} | ||
}); | ||
}; | ||
} | ||
if (debugEnabled && !node.enclosingScopeId) { | ||
Object.defineProperty(node, 'enclosingScopeId', { | ||
newProperties.enclosingScopeId = { | ||
enumerable: true, | ||
@@ -103,5 +113,7 @@ get: function() { | ||
} | ||
}); | ||
}; | ||
} | ||
Object.defineProperties(node, newProperties); | ||
return node; | ||
@@ -127,5 +139,22 @@ }; | ||
var nodeToString = exports.nodeToString = function(node) { | ||
var tempObject; | ||
var str = ''; | ||
switch (node.type) { | ||
case Syntax.ArrayExpression: | ||
tempObject = []; | ||
node.elements.forEach(function(el, i) { | ||
// preserve literal values so that the JSON form shows the correct type | ||
if (el.type === Syntax.Literal) { | ||
tempObject[i] = el.value; | ||
} | ||
else { | ||
tempObject[i] = nodeToString(el); | ||
} | ||
}); | ||
str = JSON.stringify(tempObject); | ||
break; | ||
case Syntax.AssignmentExpression: | ||
@@ -161,2 +190,18 @@ str = nodeToString(node.left); | ||
case Syntax.ObjectExpression: | ||
tempObject = {}; | ||
node.properties.forEach(function(prop) { | ||
var key = prop.key.name; | ||
// preserve literal values so that the JSON form shows the correct type | ||
if (prop.value.type === Syntax.Literal) { | ||
tempObject[key] = prop.value.value; | ||
} | ||
else { | ||
tempObject[key] = nodeToString(prop); | ||
} | ||
}); | ||
str = JSON.stringify(tempObject); | ||
break; | ||
case Syntax.ThisExpression: | ||
@@ -171,3 +216,4 @@ str = 'this'; | ||
if (node.prefix) { | ||
// workaround for https://code.google.com/p/esprima/issues/detail?id=526 | ||
if (node.prefix === true || node.prefix === undefined) { | ||
str = node.operator + str; | ||
@@ -177,3 +223,4 @@ } | ||
// this shouldn't happen | ||
throw new Error('Found a UnaryExpression with a postfix operator: %j', node); | ||
throw new Error( util.format('Found a UnaryExpression with a postfix operator: %j', | ||
node) ); | ||
} | ||
@@ -249,2 +296,9 @@ break; | ||
// like the param "bar" in: "function foo(bar) {}" | ||
case Syntax.Identifier: | ||
info.node = node; | ||
info.name = nodeToString(info.node); | ||
info.type = info.node.type; | ||
break; | ||
// like "a.b.c" | ||
@@ -251,0 +305,0 @@ case Syntax.MemberExpression: |
@@ -60,3 +60,3 @@ /*global env: true */ | ||
this.exclude.forEach(function(exclude) { | ||
if ( exclude === filepath || exclude === path.dirname(filepath) ) { | ||
if ( filepath.indexOf(exclude) === 0 ) { | ||
included = false; | ||
@@ -63,0 +63,0 @@ } |
@@ -6,2 +6,11 @@ /** | ||
var jsdoc = { | ||
doclet: require('jsdoc/doclet'), | ||
name: require('jsdoc/name'), | ||
util: { | ||
logger: require('jsdoc/util/logger') | ||
} | ||
}; | ||
var util = require('util'); | ||
var currentModule = null; | ||
@@ -12,7 +21,5 @@ | ||
function getNewDoclet(comment, e) { | ||
var Doclet = require('jsdoc/doclet').Doclet; | ||
var util = require('util'); | ||
var Doclet = jsdoc.doclet.Doclet; | ||
var doclet; | ||
var err; | ||
var doclet; | ||
@@ -25,3 +32,3 @@ try { | ||
comment.replace(/[\r\n]/g, ''), error.message) ); | ||
require('jsdoc/util/logger').error(err); | ||
jsdoc.util.logger.error(err); | ||
doclet = new Doclet('', e); | ||
@@ -39,2 +46,16 @@ } | ||
function setDefaultScopeMemberOf(doclet) { | ||
// add @inner and @memberof tags unless the current module exports only this symbol | ||
if (currentModule && currentModule !== doclet.name) { | ||
// add @inner unless the current module exports only this symbol | ||
if (!doclet.scope) { | ||
doclet.addTag('inner'); | ||
} | ||
if (!doclet.memberof && doclet.scope !== 'global') { | ||
doclet.addTag('memberof', currentModule); | ||
} | ||
} | ||
} | ||
/** | ||
@@ -45,25 +66,23 @@ * Attach these event handlers to a particular instance of a parser. | ||
exports.attachTo = function(parser) { | ||
var jsdoc = {doclet: require('jsdoc/doclet'), name: require('jsdoc/name')}; | ||
// handles JSDoc comments that include a @name tag -- the code is ignored in such a case | ||
parser.on('jsdocCommentFound', function(e) { | ||
var newDoclet = getNewDoclet(e.comment, e); | ||
if (!newDoclet.name) { | ||
return false; // only interested in virtual comments (with a @name) here | ||
function filter(doclet) { | ||
// you can't document prototypes | ||
if ( /#$/.test(doclet.longname) ) { | ||
return true; | ||
} | ||
addDoclet.call(parser, newDoclet); | ||
return false; | ||
} | ||
e.doclet = newDoclet; | ||
}); | ||
function addDoclet(newDoclet) { | ||
var e; | ||
if (newDoclet) { | ||
setCurrentModule(newDoclet); | ||
e = { doclet: newDoclet }; | ||
parser.emit('newDoclet', e); | ||
// handles named symbols in the code, may or may not have a JSDoc comment attached | ||
parser.on('symbolFound', function(e) { | ||
var subDoclets = e.comment.split(/@also\b/g); | ||
for (var i = 0, l = subDoclets.length; i < l; i++) { | ||
newSymbolDoclet.call(parser, subDoclets[i], e); | ||
if ( !e.defaultPrevented && !filter(e.doclet) ) { | ||
parser.addResult(e.doclet); | ||
} | ||
} | ||
}); | ||
} | ||
@@ -125,3 +144,3 @@ // TODO: for clarity, decompose into smaller functions | ||
memberofName = parser.resolveThis(e.astnode); | ||
scope = nameStartsWith === 'exports'? 'static' : 'instance'; | ||
scope = nameStartsWith === 'exports' ? 'static' : 'instance'; | ||
@@ -137,3 +156,4 @@ // like /** @module foo */ this.bar = 1; | ||
if (newDoclet.name) { | ||
newDoclet.name = memberofName + (scope === 'instance'? '#' : '.') + newDoclet.name; | ||
newDoclet.name = memberofName + (scope === 'instance' ? '#' : '.') + | ||
newDoclet.name; | ||
} | ||
@@ -159,13 +179,3 @@ else { newDoclet.name = memberofName; } | ||
else { | ||
// add @inner and @memberof tags unless the current module exports only this symbol | ||
if (currentModule && currentModule !== newDoclet.name) { | ||
// add @inner unless the current module exports only this symbol | ||
if (!newDoclet.scope) { | ||
newDoclet.addTag('inner'); | ||
} | ||
if (!newDoclet.memberof && newDoclet.scope !== 'global') { | ||
newDoclet.addTag('memberof', currentModule); | ||
} | ||
} | ||
setDefaultScopeMemberOf(newDoclet); | ||
} | ||
@@ -190,31 +200,29 @@ } | ||
parser.on('fileComplete', function(e) { | ||
currentModule = null; | ||
// handles JSDoc comments that include a @name tag -- the code is ignored in such a case | ||
parser.on('jsdocCommentFound', function(e) { | ||
var newDoclet = getNewDoclet(e.comment, e); | ||
if (!newDoclet.name) { | ||
return false; // only interested in virtual comments (with a @name) here | ||
} | ||
setDefaultScopeMemberOf(newDoclet); | ||
newDoclet.postProcess(); | ||
addDoclet.call(parser, newDoclet); | ||
e.doclet = newDoclet; | ||
}); | ||
function addDoclet(newDoclet) { | ||
var e; | ||
if (newDoclet) { | ||
setCurrentModule(newDoclet); | ||
e = { doclet: newDoclet }; | ||
parser.emit('newDoclet', e); | ||
// handles named symbols in the code, may or may not have a JSDoc comment attached | ||
parser.on('symbolFound', function(e) { | ||
var subDoclets = e.comment.split(/@also\b/g); | ||
if ( !e.defaultPrevented && !filter(e.doclet) ) { | ||
parser.addResult(e.doclet); | ||
} | ||
for (var i = 0, l = subDoclets.length; i < l; i++) { | ||
newSymbolDoclet.call(parser, subDoclets[i], e); | ||
} | ||
} | ||
}); | ||
function filter(doclet) { | ||
// you can't document prototypes | ||
if ( /#$/.test(doclet.longname) ) { | ||
return true; | ||
} | ||
// you can't document symbols added by the parser with a dummy name | ||
if (doclet.meta.code && doclet.meta.code.name === '____') { | ||
return true; | ||
} | ||
return false; | ||
} | ||
parser.on('fileComplete', function(e) { | ||
currentModule = null; | ||
}); | ||
}; |
@@ -1,2 +0,3 @@ | ||
/*global env: true, Packages: true */ | ||
/*global env, Packages */ | ||
/*eslint no-script-url:0 */ | ||
/** | ||
@@ -218,9 +219,3 @@ * @module jsdoc/src/parser | ||
// merge adjacent doclets | ||
.replace(/\*\/\/\*\*+/g, '@also') | ||
// add a dummy name to object literals that are lent to a function prototype | ||
// like: return @lends { | ||
.replace(/(\/\*\*[^\*\/]*?[\*\s]*@lends\s(?:[^\*]|\*(?!\/))*\*\/\s*)\{/g, '$1 ____ = {') | ||
// like: @lends return { | ||
.replace(/(\/\*\*[^\*\/]*?@lends\b[^\*\/]*?\*\/)(\s*)return(\s*)\{/g, | ||
'$2$3 return $1 ____ = {'); | ||
.replace(/\*\/\/\*\*+/g, '@also'); | ||
} | ||
@@ -252,3 +247,6 @@ | ||
ast = this._astBuilder.build(sourceCode, sourceName); | ||
this._walker.recurse(sourceName, ast, this._visitor); | ||
if (ast) { | ||
this._walker.recurse(sourceName, ast, this._visitor); | ||
} | ||
} | ||
@@ -275,3 +273,3 @@ | ||
this.refs[node.nodeId] = { | ||
longname: jsdoc.doclet.ANONYMOUS_LONGNAME, | ||
longname: jsdoc.name.ANONYMOUS_LONGNAME, | ||
meta: { | ||
@@ -330,3 +328,3 @@ code: e.code | ||
if (!doclet) { | ||
result = jsdoc.doclet.ANONYMOUS_LONGNAME + jsdoc.name.INNER; | ||
result = jsdoc.name.ANONYMOUS_LONGNAME + jsdoc.name.INNER; | ||
} | ||
@@ -365,6 +363,5 @@ else { | ||
if (!doclet) { | ||
// global? | ||
} | ||
else { | ||
// set the result if we found a doclet. (if we didn't, the AST node may describe a | ||
// global symbol.) | ||
if (doclet) { | ||
result = doclet.longname || doclet.name; | ||
@@ -395,3 +392,3 @@ } | ||
if (!doclet) { | ||
result = jsdoc.doclet.ANONYMOUS_LONGNAME; // TODO handle global this? | ||
result = jsdoc.name.ANONYMOUS_LONGNAME; // TODO handle global this? | ||
} | ||
@@ -398,0 +395,0 @@ else if (doclet['this']) { |
@@ -7,2 +7,3 @@ 'use strict'; | ||
ArrayPattern: 'ArrayPattern', | ||
ArrowFunctionExpression: 'ArrowFunctionExpression', | ||
AssignmentExpression: 'AssignmentExpression', | ||
@@ -14,2 +15,5 @@ BinaryExpression: 'BinaryExpression', | ||
CatchClause: 'CatchClause', | ||
ClassBody: 'ClassBody', | ||
ClassDeclaration: 'ClassDeclaration', | ||
ClassExpression: 'ClassExpression', | ||
ComprehensionBlock: 'ComprehensionBlock', | ||
@@ -22,2 +26,5 @@ ComprehensionExpression: 'ComprehensionExpression', | ||
EmptyStatement: 'EmptyStatement', | ||
ExportBatchSpecifier: 'ExportBatchSpecifier', | ||
ExportDeclaration: 'ExportDeclaration', | ||
ExportSpecifier: 'ExportSpecifier', | ||
ExpressionStatement: 'ExpressionStatement', | ||
@@ -31,7 +38,11 @@ ForInStatement: 'ForInStatement', | ||
IfStatement: 'IfStatement', | ||
ImportDeclaration: 'ImportDeclaration', | ||
ImportSpecifier: 'ImportSpecifier', | ||
LabeledStatement: 'LabeledStatement', | ||
LetStatement: 'LetStatement', | ||
LetStatement: 'LetStatement', // TODO: update Rhino to use VariableDeclaration | ||
Literal: 'Literal', | ||
LogicalExpression: 'LogicalExpression', | ||
MemberExpression: 'MemberExpression', | ||
MethodDefinition: 'MethodDefinition', | ||
ModuleDeclaration: 'ModuleDeclaration', | ||
NewExpression: 'NewExpression', | ||
@@ -44,4 +55,8 @@ ObjectExpression: 'ObjectExpression', | ||
SequenceExpression: 'SequenceExpression', | ||
SpreadElement: 'SpreadElement', | ||
SwitchCase: 'SwitchCase', | ||
SwitchStatement: 'SwitchStatement', | ||
TaggedTemplateExpression: 'TaggedTemplateExpression', | ||
TemplateElement: 'TemplateElement', | ||
TemplateLiteral: 'TemplateLiteral', | ||
ThisExpression: 'ThisExpression', | ||
@@ -48,0 +63,0 @@ ThrowStatement: 'ThrowStatement', |
@@ -13,2 +13,5 @@ /** | ||
syntax: require('jsdoc/src/syntax') | ||
}, | ||
util: { | ||
logger: require('jsdoc/util/logger') | ||
} | ||
@@ -43,2 +46,63 @@ }; | ||
/** | ||
* For function parameters that have inline documentation, create a function that will merge the | ||
* inline documentation into the function's doclet. If the parameter is already documented in the | ||
* function's doclet, the inline documentation will be ignored. | ||
* | ||
* @private | ||
* @param {module:jsdoc/src/parser.Parser} parser - The JSDoc parser. | ||
* @return {function} A function that merges a parameter's inline documentation into the function's | ||
* doclet. | ||
*/ | ||
function makeInlineParamsFinisher(parser) { | ||
return function(e) { | ||
var documentedParams; | ||
var knownParams; | ||
var param; | ||
var parentDoclet; | ||
var i = 0; | ||
if (e.doclet && e.doclet.meta && e.doclet.meta.code && e.doclet.meta.code.node && | ||
e.doclet.meta.code.node.parent) { | ||
parentDoclet = parser._getDoclet(e.doclet.meta.code.node.parent.nodeId); | ||
} | ||
if (!parentDoclet) { | ||
return; | ||
} | ||
parentDoclet.params = parentDoclet.params || []; | ||
documentedParams = parentDoclet.params; | ||
knownParams = parentDoclet.meta.code.paramnames; | ||
while (true) { | ||
param = documentedParams[i]; | ||
// is the param already documented? if so, we're done | ||
if (param && param.name === e.doclet.name) { | ||
// the doclet is no longer needed | ||
e.doclet.undocumented = true; | ||
break; | ||
} | ||
// if we ran out of documented params, or we're at the parameter's actual position, | ||
// splice in the param at the current index | ||
if ( !param || i === knownParams.indexOf(e.doclet.name) ) { | ||
documentedParams.splice(i, 0, { | ||
type: e.doclet.type, | ||
description: '', | ||
name: e.doclet.name | ||
}); | ||
// the doclet is no longer needed | ||
e.doclet.undocumented = true; | ||
break; | ||
} | ||
i++; | ||
} | ||
}; | ||
} | ||
// TODO: docs | ||
@@ -50,3 +114,3 @@ function SymbolFound(node, filename, extras) { | ||
this.id = extras.id || node.nodeId; | ||
this.comment = extras.comment || getLeadingComment(node) || jsdoc.doclet.UNDOCUMENTED_TAG; | ||
this.comment = extras.comment || getLeadingComment(node) || '@undocumented'; | ||
this.lineno = extras.lineno || node.loc.start.line; | ||
@@ -251,2 +315,4 @@ this.range = extras.range || node.range; | ||
Visitor.prototype.makeSymbolFoundEvent = function(node, parser, filename) { | ||
var logger = jsdoc.util.logger; | ||
var e; | ||
@@ -256,2 +322,3 @@ var basename; | ||
var l; | ||
var parent; | ||
@@ -270,5 +337,3 @@ var extras = { | ||
basename = parser.getBasename(e.code.name); | ||
// in addition to `this`, we need to special-case the string `____`, or else our | ||
// `@lends` tag hackery causes things to appear in the wrong scope | ||
if (basename !== 'this' && basename !== '____') { | ||
if (basename !== 'this') { | ||
e.code.funcscope = parser.resolveVar(node, basename); | ||
@@ -294,2 +359,14 @@ } | ||
// like "bar" in: function foo(/** @type {string} */ bar) {} | ||
// This is an extremely common type of node; we only care about function parameters with | ||
// inline type annotations. No need to fire events unless they're already commented. | ||
case Syntax.Identifier: | ||
parent = node.parent; | ||
if ( node.leadingComments && parent && jsdoc.src.astnode.isFunction(parent) ) { | ||
extras.finishers = [makeInlineParamsFinisher(parser)]; | ||
e = new SymbolFound(node, filename, extras); | ||
} | ||
break; | ||
// like "obj.prop" in: /** @typedef {string} */ obj.prop; | ||
@@ -305,2 +382,8 @@ // Closure Compiler uses this pattern extensively for enums. | ||
// like the object literal in: function Foo = Class.create(/** @lends Foo */ {}); | ||
case Syntax.ObjectExpression: | ||
e = new SymbolFound(node, filename, extras); | ||
break; | ||
// like "bar: true" in: var foo = { bar: true }; | ||
@@ -327,2 +410,23 @@ // like "get bar() {}" in: var foo = { get bar() {} }; | ||
// for now, log a warning for all ES6 nodes, since we don't do anything useful with them | ||
case Syntax.ArrowFunctionExpression: | ||
case Syntax.ClassBody: | ||
case Syntax.ClassDeclaration: | ||
case Syntax.ClassExpression: | ||
case Syntax.ExportBatchSpecifier: | ||
case Syntax.ExportDeclaration: | ||
case Syntax.ExportSpecifier: | ||
case Syntax.ImportDeclaration: | ||
case Syntax.ImportSpecifier: | ||
case Syntax.MethodDefinition: | ||
case Syntax.ModuleDeclaration: | ||
case Syntax.SpreadElement: | ||
case Syntax.TaggedTemplateExpression: | ||
case Syntax.TemplateElement: | ||
case Syntax.TemplateLiteral: | ||
logger.warn('JSDoc does not currently handle %s nodes. Source file: %s, line %s', | ||
node.type, filename, (node.loc && node.loc.start) ? node.loc.start.line : '??'); | ||
break; | ||
default: | ||
@@ -329,0 +433,0 @@ // ignore |
@@ -65,2 +65,27 @@ /** | ||
walkers[Syntax.ArrowFunctionExpression] = | ||
function arrowFunctionExpression(node, parent, state, cb) { | ||
var i; | ||
var l; | ||
// used for function declarations, so we include it here | ||
if (node.id) { | ||
cb(node.id, node, state); | ||
} | ||
for (i = 0, l = node.params.length; i < l; i++) { | ||
cb(node.params[i], node, state); | ||
} | ||
for (i = 0, l = node.defaults.length; i < l; i++) { | ||
cb(node.defaults[i], node, state); | ||
} | ||
cb(node.body, node, state); | ||
if (node.rest) { | ||
cb(node.rest, node, state); | ||
} | ||
}; | ||
walkers[Syntax.AssignmentExpression] = function assignmentExpression(node, parent, state, cb) { | ||
@@ -98,2 +123,20 @@ cb(node.left, node, state); | ||
walkers[Syntax.ClassBody] = walkers[Syntax.BlockStatement]; | ||
walkers[Syntax.ClassDeclaration] = function classDeclaration(node, parent, state, cb) { | ||
if (node.id) { | ||
cb(node.id, node, state); | ||
} | ||
if (node.superClass) { | ||
cb(node.superClass, node, state); | ||
} | ||
if (node.body) { | ||
cb(node.body, node, state); | ||
} | ||
}; | ||
walkers[Syntax.ClassExpression] = walkers[Syntax.ClassDeclaration]; | ||
// TODO: verify correctness | ||
@@ -133,2 +176,35 @@ walkers[Syntax.ComprehensionBlock] = walkers[Syntax.AssignmentExpression]; | ||
walkers[Syntax.ExportBatchSpecifier] = leafNode; | ||
walkers[Syntax.ExportDeclaration] = function exportDeclaration(node, parent, state, cb) { | ||
var i; | ||
var l; | ||
if (node.declaration) { | ||
for (i = 0, l = node.declaration.length; i < l; i++) { | ||
cb(node.declaration[i], node, state); | ||
} | ||
} | ||
if (node.specifiers) { | ||
for (i = 0, l = node.specifiers.length; i < l; i++) { | ||
cb(node.specifiers[i], node, state); | ||
} | ||
} | ||
if (node.source) { | ||
cb(node.source, node, state); | ||
} | ||
}; | ||
walkers[Syntax.ExportSpecifier] = function exportSpecifier(node, parent, state, cb) { | ||
if (node.id) { | ||
cb(node.id, node, state); | ||
} | ||
if (node.name) { | ||
cb(node.name, node, state); | ||
} | ||
}; | ||
walkers[Syntax.ExpressionStatement] = function expressionStatement(node, parent, state, cb) { | ||
@@ -162,24 +238,6 @@ cb(node.expression, node, state); | ||
walkers[Syntax.FunctionDeclaration] = function functionDeclaration(node, parent, state, cb) { | ||
var i; | ||
var l; | ||
walkers[Syntax.FunctionDeclaration] = walkers[Syntax.ArrowFunctionExpression]; | ||
// can be null for function expressions | ||
if (node.id) { | ||
cb(node.id, node, state); | ||
} | ||
walkers[Syntax.FunctionExpression] = walkers[Syntax.ArrowFunctionExpression]; | ||
if (node.params && node.params.length) { | ||
for (i = 0, l = node.params.length; i < l; i++) { | ||
cb(node.params[i], node, state); | ||
} | ||
} | ||
// ignore node.defaults and node.rest for now; always empty | ||
cb(node.body, node, state); | ||
}; | ||
walkers[Syntax.FunctionExpression] = walkers[Syntax.FunctionDeclaration]; | ||
walkers[Syntax.Identifier] = leafNode; | ||
@@ -195,2 +253,19 @@ | ||
walkers[Syntax.ImportDeclaration] = function importDeclaration(node, parent, state, cb) { | ||
var i; | ||
var l; | ||
if (node.specifiers) { | ||
for (i = 0, l = node.specifiers.length; i < l; i++) { | ||
cb(node.specifiers[i], node, state); | ||
} | ||
} | ||
if (node.source) { | ||
cb(node.source, node, state); | ||
} | ||
}; | ||
walkers[Syntax.ImportSpecifier] = walkers[Syntax.ExportSpecifier]; | ||
walkers[Syntax.LabeledStatement] = function labeledStatement(node, parent, state, cb) { | ||
@@ -218,8 +293,32 @@ cb(node.body, node, state); | ||
walkers[Syntax.MemberExpression] = function memberExpression(node, parent, state, cb) { | ||
cb(node.object, node, state); | ||
if (node.property) { | ||
cb(node.property, node, state); | ||
} | ||
cb(node.object, node, state); | ||
}; | ||
walkers[Syntax.MethodDefinition] = function methodDefinition(node, parent, state, cb) { | ||
if (node.key) { | ||
cb(node.key, node, state); | ||
} | ||
if (node.value) { | ||
cb(node.value, node, state); | ||
} | ||
}; | ||
walkers[Syntax.ModuleDeclaration] = function moduleDeclaration(node, parent, state, cb) { | ||
if (node.id) { | ||
cb(node.id, node, state); | ||
} | ||
if (node.source) { | ||
cb(node.source, node, state); | ||
} | ||
if (node.body) { | ||
cb(node.body, node, state); | ||
} | ||
}; | ||
walkers[Syntax.NewExpression] = walkers[Syntax.CallExpression]; | ||
@@ -256,2 +355,8 @@ | ||
walkers[Syntax.SpreadElement] = function spreadElement(node, parent, state, cb) { | ||
if (node.argument) { | ||
cb(node.argument, node, state); | ||
} | ||
}; | ||
walkers[Syntax.SwitchCase] = function switchCase(node, parent, state, cb) { | ||
@@ -275,2 +380,31 @@ if (node.test) { | ||
walkers[Syntax.TaggedTemplateExpression] = | ||
function taggedTemplateExpression(node, parent, state, cb) { | ||
if (node.tag) { | ||
cb(node.tag, node, state); | ||
} | ||
if (node.quasi) { | ||
cb(node.quasi, node, state); | ||
} | ||
}; | ||
walkers[Syntax.TemplateElement] = leafNode; | ||
walkers[Syntax.TemplateLiteral] = function templateLiteral(node, parent, state, cb) { | ||
var i; | ||
var l; | ||
if (node.quasis && node.quasis.length) { | ||
for (i = 0, l = node.quasis.length; i < l; i++) { | ||
cb(node.quasis[i], node, state); | ||
} | ||
} | ||
if (node.expressions && node.expressions.length) { | ||
for (i = 0, l = node.expressions.length; i < l; i++) { | ||
cb(node.expressions[i], node, state); | ||
} | ||
} | ||
}; | ||
walkers[Syntax.ThisExpression] = leafNode; | ||
@@ -354,5 +488,6 @@ | ||
// TODO: docs | ||
Walker.prototype._recurse = function(ast) { | ||
Walker.prototype._recurse = function(filename, ast) { | ||
// TODO: track variables/aliases during the walk | ||
var state = { | ||
filename: filename, | ||
nodes: [], | ||
@@ -402,3 +537,3 @@ scopes: [] | ||
Walker.prototype.recurse = function(filename, ast, visitor) { | ||
var state = this._recurse(ast); | ||
var state = this._recurse(filename, ast); | ||
@@ -405,0 +540,0 @@ if (visitor) { |
@@ -34,5 +34,4 @@ /*global env: true */ | ||
opts = opts || {}; | ||
text = text || ''; | ||
if (!text) { return ''; } | ||
if (opts.keepsWhitespace) { | ||
@@ -47,7 +46,8 @@ text = text.replace(/^[\n\r\f]+|[\n\r\f]+$/g, ''); | ||
} | ||
return text; | ||
} | ||
else { | ||
return text.replace(/^\s+|\s+$/g, ''); | ||
text = text.replace(/^\s+|\s+$/g, ''); | ||
} | ||
return text; | ||
} | ||
@@ -54,0 +54,0 @@ |
@@ -41,3 +41,3 @@ /** | ||
if (opts.isNamespace) { | ||
if (opts && opts.isNamespace) { | ||
_namespaces.push(def.title); | ||
@@ -44,0 +44,0 @@ } |
@@ -1,2 +0,2 @@ | ||
/*global app: true, env: true */ | ||
/*global app, env */ | ||
/** | ||
@@ -11,6 +11,20 @@ Define tags that are known in JSDoc. | ||
var logger = require('jsdoc/util/logger'); | ||
var jsdoc = { | ||
name: require('jsdoc/name'), | ||
src: { | ||
astnode: require('jsdoc/src/astnode') | ||
}, | ||
tag: { | ||
type: require('jsdoc/tag/type') | ||
}, | ||
util: { | ||
logger: require('jsdoc/util/logger') | ||
} | ||
}; | ||
var path = require('jsdoc/path'); | ||
var Syntax = require('jsdoc/src/syntax').Syntax; | ||
var GLOBAL_LONGNAME = jsdoc.name.GLOBAL_LONGNAME; | ||
var MODULE_PREFIX = jsdoc.name.MODULE_PREFIX; | ||
function getSourcePaths() { | ||
@@ -59,3 +73,3 @@ var sourcePaths = env.sourceFiles.slice(0) || []; | ||
catch(e) { | ||
logger.error(e.message); | ||
jsdoc.util.logger.error(e.message); | ||
} | ||
@@ -87,3 +101,6 @@ } | ||
if (tag.value && tag.value.type) { | ||
doclet.type = tag.value.type; | ||
// add the type names and other type properties (such as `optional`) | ||
Object.keys(tag.value).forEach(function(prop) { | ||
doclet[prop] = tag.value[prop]; | ||
}); | ||
} | ||
@@ -209,4 +226,4 @@ } | ||
onTagText: function(text) { | ||
var type = require('jsdoc/tag/type'), | ||
tagType = type.parse(text, false, true); | ||
var tagType = jsdoc.tag.type.parse(text, false, true); | ||
return tagType.typeExpression || text; | ||
@@ -298,2 +315,4 @@ }, | ||
var nodeToString = jsdoc.src.astnode.nodeToString; | ||
if (tag.value) { | ||
@@ -306,12 +325,21 @@ doclet.defaultvalue = tag.value; | ||
if (type === Syntax.Literal) { | ||
doclet.defaultvalue = String(value); | ||
switch(type) { | ||
case Syntax.ArrayExpression: | ||
doclet.defaultvalue = nodeToString(doclet.meta.code.node); | ||
doclet.defaultvaluetype = 'array'; | ||
break; | ||
case Syntax.Literal: | ||
doclet.defaultvalue = String(value); | ||
break; | ||
case Syntax.ObjectExpression: | ||
doclet.defaultvalue = nodeToString(doclet.meta.code.node); | ||
doclet.defaultvaluetype = 'object'; | ||
break; | ||
default: | ||
// do nothing | ||
break; | ||
} | ||
// TODO: reenable the changes for https://github.com/jsdoc3/jsdoc/pull/419 | ||
/* | ||
else if (doclet.meta.code.type === 'OBJECTLIT') { | ||
doclet.defaultvalue = String(doclet.meta.code.node.toSource()); | ||
doclet.defaultvaluetype = 'object'; | ||
} | ||
*/ | ||
} | ||
@@ -391,2 +419,5 @@ } | ||
} | ||
else { | ||
setDocletNameToValue(doclet, tag); | ||
} | ||
} | ||
@@ -460,4 +491,2 @@ }) | ||
onTagged: function(doclet, tag) { | ||
var GLOBAL_LONGNAME = require('jsdoc/doclet').GLOBAL_LONGNAME; | ||
doclet.alias = tag.value || GLOBAL_LONGNAME; | ||
@@ -499,4 +528,2 @@ doclet.addTag('undocumented'); | ||
onTagged: function(doclet, tag) { | ||
var GLOBAL_LONGNAME = require('jsdoc/doclet').GLOBAL_LONGNAME; | ||
if (tag.originalTitle === 'memberof!') { | ||
@@ -562,3 +589,3 @@ doclet.forceMemberof = true; | ||
if (!doclet.params) { doclet.params = []; } | ||
doclet.params.push(tag.value||{}); | ||
doclet.params.push(tag.value || {}); | ||
} | ||
@@ -614,4 +641,2 @@ }) | ||
var MODULE_PREFIX = require('jsdoc/name').MODULE_PREFIX; | ||
// inline link tags are passed through as-is so that `@requires {@link foo}` works | ||
@@ -697,10 +722,24 @@ if ( require('jsdoc/tag/inline').isInlineTag(tag.value, 'link\\S*') ) { | ||
mustHaveValue: true, | ||
mustNotHaveDescription: true, | ||
canHaveType: true, | ||
onTagText: function(text) { | ||
// remove line breaks so we can parse the type expression correctly | ||
text = text.replace(/[\n\r]/g, ''); | ||
// any text must be formatted as a type, but for back compat braces are optional | ||
if ( !/^\{[\s\S]+\}$/.test(text) ) { | ||
text = '{' + text + '}'; | ||
var closeIdx; | ||
var openIdx; | ||
var OPEN_BRACE = '{'; | ||
var CLOSE_BRACE = '}'; | ||
// remove line breaks | ||
text = text.replace(/[\f\n\r]/g, ''); | ||
// Text must be a type expression; for backwards compatibility, we add braces if they're | ||
// missing. But do NOT add braces to things like `@type {string} some pointless text`. | ||
openIdx = text.indexOf(OPEN_BRACE); | ||
closeIdx = text.indexOf(CLOSE_BRACE); | ||
// a type expression is at least one character long | ||
if ( openIdx !== 0 || closeIdx <= openIdx + 1) { | ||
text = OPEN_BRACE + text + CLOSE_BRACE; | ||
} | ||
return text; | ||
@@ -707,0 +746,0 @@ }, |
@@ -69,8 +69,3 @@ /** | ||
exports.isInlineTag = function(string, tagName) { | ||
try { | ||
return regExpFactory(tagName, '^', '$').test(string); | ||
} | ||
catch(e) { | ||
return false; | ||
} | ||
return regExpFactory(tagName, '^', '$').test(string); | ||
}; | ||
@@ -77,0 +72,0 @@ |
@@ -10,2 +10,3 @@ /** | ||
var catharsis = require('catharsis'); | ||
var jsdoc = { | ||
@@ -17,2 +18,3 @@ name: require('jsdoc/name'), | ||
}; | ||
var util = require('util'); | ||
@@ -42,4 +44,2 @@ /** | ||
function extractTypeExpression(string) { | ||
string = string || ''; | ||
var completeExpression; | ||
@@ -116,3 +116,3 @@ var count = 0; | ||
if (typeOverride.tags && typeOverride.tags[0]) { | ||
typeExpression = typeOverride.tags[0].text || typeExpression; | ||
typeExpression = typeOverride.tags[0].text; | ||
} | ||
@@ -175,8 +175,9 @@ text = typeOverride.newString; | ||
/** @private */ | ||
function getTypeStrings(parsedType) { | ||
function getTypeStrings(parsedType, isOutermostType) { | ||
var applications; | ||
var typeString; | ||
var types = []; | ||
var catharsis = require('catharsis'); | ||
var TYPES = catharsis.Types; | ||
var util = require('util'); | ||
@@ -200,3 +201,15 @@ switch(parsedType.type) { | ||
case TYPES.TypeApplication: | ||
types.push( catharsis.stringify(parsedType) ); | ||
// if this is the outermost type, we strip the modifiers; otherwise, we keep them | ||
if (isOutermostType) { | ||
applications = parsedType.applications.map(function(application) { | ||
return getTypeStrings(application); | ||
}).join(', '); | ||
typeString = util.format( '%s.<%s>', getTypeStrings(parsedType.expression), | ||
applications ); | ||
types.push(typeString); | ||
} | ||
else { | ||
types.push( catharsis.stringify(parsedType) ); | ||
} | ||
break; | ||
@@ -232,5 +245,2 @@ case TYPES.TypeUnion: | ||
function parseTypeExpression(tagInfo) { | ||
var catharsis = require('catharsis'); | ||
var util = require('util'); | ||
var errorMessage; | ||
@@ -253,16 +263,14 @@ var parsedType; | ||
if (parsedType) { | ||
tagInfo.type = tagInfo.type.concat( getTypeStrings(parsedType) ); | ||
tagInfo.type = tagInfo.type.concat( getTypeStrings(parsedType, true) ); | ||
// Catharsis and JSDoc use the same names for 'optional' and 'nullable'... | ||
['optional', 'nullable'].forEach(function(key) { | ||
if (parsedType[key] !== null && parsedType[key] !== undefined) { | ||
tagInfo[key] = parsedType[key]; | ||
} | ||
}); | ||
// Catharsis and JSDoc use the same names for 'optional' and 'nullable'... | ||
['optional', 'nullable'].forEach(function(key) { | ||
if (parsedType[key] !== null && parsedType[key] !== undefined) { | ||
tagInfo[key] = parsedType[key]; | ||
} | ||
}); | ||
// ...but not 'variable'. | ||
if (parsedType.repeatable !== null && parsedType.repeatable !== undefined) { | ||
tagInfo.variable = parsedType.repeatable; | ||
} | ||
// ...but not 'variable'. | ||
if (parsedType.repeatable !== null && parsedType.repeatable !== undefined) { | ||
tagInfo.variable = parsedType.repeatable; | ||
} | ||
@@ -269,0 +277,0 @@ |
@@ -28,15 +28,19 @@ /*global env: true */ | ||
exports.validate = function(tag, tagDef, meta) { | ||
// check for errors that make the tag useless | ||
if (!tagDef && !env.conf.tags.allowUnknownTags) { | ||
logger.error( buildMessage(tag.title, meta, 'is not a known tag') ); | ||
} | ||
else if (!tag.text) { | ||
if (tagDef.mustHaveValue) { | ||
logger.error( buildMessage(tag.title, meta, 'requires a value') ); | ||
} | ||
else if (!tag.text && tagDef.mustHaveValue) { | ||
logger.error( buildMessage(tag.title, meta, 'requires a value') ); | ||
} | ||
else { | ||
if (tagDef.mustNotHaveValue) { | ||
logger.error( buildMessage(tag.title, meta, 'does not permit a value') ); | ||
} | ||
// check for minor issues that are usually harmless | ||
else if (tag.text && tagDef.mustNotHaveValue) { | ||
logger.warn( buildMessage(tag.title, meta, | ||
'does not permit a value; the value will be ignored') ); | ||
} | ||
else if (tag.value && tag.value.description && tagDef.mustNotHaveDescription) { | ||
logger.warn( buildMessage(tag.title, meta, | ||
'does not permit a description; the description will be ignored') ); | ||
} | ||
}; |
@@ -17,3 +17,3 @@ /** | ||
var index = parent.children.indexOf(child); | ||
if (index != -1) { | ||
if (index !== -1) { | ||
parent.children.splice(index, 1); | ||
@@ -20,0 +20,0 @@ } |
@@ -13,11 +13,13 @@ /*global env: true */ | ||
var tutorial = require('jsdoc/tutorial'), | ||
fs = require('jsdoc/fs'), | ||
logger = require('jsdoc/util/logger'), | ||
path = require('path'), | ||
hasOwnProp = Object.prototype.hasOwnProperty, | ||
conf = {}, | ||
tutorials = {}, | ||
finder = /^(.*)\.(x(?:ht)?ml|html?|md|markdown|json)$/i; | ||
var logger = require('jsdoc/util/logger'); | ||
var fs = require('jsdoc/fs'); | ||
var path = require('path'); | ||
var tutorial = require('jsdoc/tutorial'); | ||
var hasOwnProp = Object.prototype.hasOwnProperty; | ||
var conf = {}; | ||
var finder = /^(.*)\.(x(?:ht)?ml|html?|md|markdown|json)$/i; | ||
var tutorials = {}; | ||
/** checks if `conf` is the metadata for a single tutorial. | ||
@@ -24,0 +26,0 @@ * A tutorial's metadata has a property 'title' and/or a property 'children'. |
@@ -13,3 +13,3 @@ /** | ||
if (o instanceof Object && o.constructor != Function) { | ||
if (o instanceof Object && o.constructor !== Function) { | ||
if ( Array.isArray(o) ) { | ||
@@ -16,0 +16,0 @@ clone = []; |
@@ -0,1 +1,2 @@ | ||
/*global Set */ | ||
/** | ||
@@ -10,15 +11,41 @@ * Recursively print out all names and values in a data structure. | ||
var util = require('util'); | ||
var setDefined = typeof Set !== 'undefined'; | ||
function ObjectWalker() { | ||
this.seenItems = []; | ||
if (setDefined) { | ||
this.seenItems = new Set(); | ||
} else { | ||
this.seenItems = []; | ||
} | ||
} | ||
ObjectWalker.prototype.seen = function(object) { | ||
if (this.seenItems.indexOf(object) !== -1) { | ||
return true; | ||
var result; | ||
if (setDefined) { | ||
result = this.seenItems.has(object); | ||
} else { | ||
result = object.hasBeenSeenByWalkerDumper; | ||
} | ||
return result; | ||
}; | ||
return false; | ||
ObjectWalker.prototype.markAsSeen = function(object) { | ||
if (setDefined) { | ||
this.seenItems.add(object); | ||
} else { | ||
object.hasBeenSeenByWalkerDumper = true; | ||
this.seenItems.push(object); | ||
} | ||
}; | ||
ObjectWalker.prototype.cleanSeenFlag = function() { | ||
if (setDefined) { | ||
this.seenItems = new Set(); | ||
} else { | ||
this.seenItems.forEach(function(object) { | ||
delete object.hasBeenSeenByWalkerDumper; | ||
}); | ||
} | ||
}; | ||
// some objects are unwalkable, like Java native objects | ||
@@ -43,3 +70,3 @@ ObjectWalker.prototype.isUnwalkable = function(o) { | ||
else { | ||
this.seenItems.push(o); | ||
this.markAsSeen(o); | ||
return func(o); | ||
@@ -86,2 +113,3 @@ } | ||
Object.keys(obj).forEach(function(key) { | ||
if (!setDefined && key === 'hasBeenSeenByWalkerDumper') { return; } | ||
newObj[key] = self.walk(obj[key]); | ||
@@ -105,3 +133,7 @@ }); | ||
exports.dump = function(object) { | ||
return JSON.stringify(new ObjectWalker().walk(object), null, 4); | ||
var walker = new ObjectWalker(); | ||
var result = JSON.stringify(walker.walk(object), null, 4); | ||
walker.cleanSeenFlag(); | ||
return result; | ||
}; |
@@ -88,3 +88,3 @@ /** | ||
var DEFAULT_LEVEL = LEVELS.ERROR; | ||
var DEFAULT_LEVEL = LEVELS.WARN; | ||
var logLevel = DEFAULT_LEVEL; | ||
@@ -91,0 +91,0 @@ |
@@ -21,3 +21,3 @@ /*global env: true */ | ||
*/ | ||
evilstreak: "marked", | ||
evilstreak: 'marked', | ||
/** | ||
@@ -27,7 +27,7 @@ * The "GitHub-flavored Markdown" parser. | ||
*/ | ||
gfm: "marked", | ||
gfm: 'marked', | ||
/** | ||
* The "[Marked](https://github.com/chjj/marked)" parser. | ||
*/ | ||
marked: "marked" | ||
marked: 'marked' | ||
}; | ||
@@ -96,2 +96,6 @@ | ||
}; | ||
// Allow prettyprint to work on inline code samples | ||
markedRenderer.code = function(code, language) { | ||
return '<pre class="prettyprint source"><code>' + code + '</code></pre>'; | ||
}; | ||
@@ -98,0 +102,0 @@ parserFunction = function(source) { |
@@ -1,2 +0,2 @@ | ||
/*global env: true */ | ||
/*global env, java */ | ||
/** | ||
@@ -3,0 +3,0 @@ * Helper functions to enable JSDoc to run on multiple JavaScript runtimes. |
@@ -289,3 +289,3 @@ /*global env: true */ | ||
require('jsdoc/util/logger').error( new Error('No such tutorial: ' + tutorial) ); | ||
return; | ||
return null; | ||
} | ||
@@ -322,3 +322,3 @@ | ||
require('jsdoc/util/logger').error( new Error('Missing required parameter: tutorial') ); | ||
return; | ||
return null; | ||
} | ||
@@ -501,3 +501,3 @@ | ||
if (d.virtual) { | ||
attribs.push('virtual'); | ||
attribs.push('abstract'); | ||
} | ||
@@ -510,3 +510,3 @@ | ||
if (d.scope && d.scope !== 'instance' && d.scope !== 'global') { | ||
if (d.kind == 'function' || d.kind == 'member' || d.kind == 'constant') { | ||
if (d.kind === 'function' || d.kind === 'member' || d.kind === 'constant') { | ||
attribs.push(d.scope); | ||
@@ -517,3 +517,3 @@ } | ||
if (d.readonly === true) { | ||
if (d.kind == 'member') { | ||
if (d.kind === 'member') { | ||
attribs.push('readonly'); | ||
@@ -527,2 +527,9 @@ } | ||
if (d.nullable === true) { | ||
attribs.push('nullable'); | ||
} | ||
else if (d.nullable === false) { | ||
attribs.push('non-null'); | ||
} | ||
return attribs; | ||
@@ -529,0 +536,0 @@ }; |
@@ -7,3 +7,4 @@ # License # | ||
Copyright (c) 2011-2012 Michael Mathews <micmath@gmail.com> | ||
Copyright (c) 2011-2014 Michael Mathews <micmath@gmail.com> and the | ||
[contributors to JSDoc](https://github.com/jsdoc3/jsdoc/graphs/contributors). | ||
All rights reserved. | ||
@@ -277,2 +278,12 @@ | ||
## Requizzle ## | ||
Requizzle is distributed under the MIT license, which is reproduced above. | ||
Copyright (c) 2014 Google Inc. All rights reserved. | ||
Copyright (c) 2012-2013 Johannes Ewald. | ||
The source code for Requizzle is available at: | ||
https://github.com/hegemonic/requizzle | ||
## Rhino ## | ||
@@ -369,6 +380,7 @@ | ||
Copyright (c) 2009-2012 Jeremy Ashkenas, DocumentCloud. | ||
Copyright (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative | ||
Reporters & Editors. | ||
The source code for Underscore.js is available at: | ||
https://github.com/documentcloud/underscore | ||
https://github.com/jashkenas/underscore | ||
@@ -375,0 +387,0 @@ ## wrench-js ## |
{ | ||
"name": "jsdoc", | ||
"version": "3.3.0-alpha5", | ||
"revision": "1395025186103", | ||
"version": "3.3.0-alpha7", | ||
"revision": "1402418188968", | ||
"description": "An API documentation generator for JavaScript.", | ||
@@ -22,17 +22,17 @@ "keywords": [ | ||
"async": "~0.1.22", | ||
"catharsis": "~0.7.0", | ||
"esprima": "~1.0.4", | ||
"catharsis": "~0.7.1", | ||
"esprima": "https://github.com/ariya/esprima/tarball/49a2eccb243f29bd653b11e9419241a9d726af7c", | ||
"js2xmlparser": "~0.1.0", | ||
"marked": "~0.3.1", | ||
"requizzle": "~0.1.0", | ||
"strip-json-comments": "~0.1.3", | ||
"taffydb": "https://github.com/hegemonic/taffydb/tarball/master", | ||
"underscore": "~1.4.2", | ||
"underscore": "~1.6.0", | ||
"wrench": "~1.3.9" | ||
}, | ||
"devDependencies": { | ||
"grunt": "~0.4.2", | ||
"grunt-bumpup": "~0.4.2", | ||
"grunt-contrib-jshint": "~0.8.0", | ||
"grunt-shell": "~0.6.1", | ||
"gulp": "~3.6.2", | ||
"gulp-eslint": "~0.1.6", | ||
"gulp-json-editor": "~2.0.2", | ||
"istanbul": "~0.2.1", | ||
"load-grunt-tasks": "~0.2.1", | ||
"tv4": "https://github.com/hegemonic/tv4/tarball/own-properties" | ||
@@ -44,4 +44,3 @@ }, | ||
"scripts": { | ||
"postinstall": "node ./node/postinstall.js", | ||
"test": "grunt test" | ||
"test": "gulp test" | ||
}, | ||
@@ -65,2 +64,2 @@ "bin": { | ||
} | ||
} | ||
} |
@@ -92,7 +92,4 @@ /*global env: true */ | ||
// never include functions that belong to the object | ||
else if (typeof e[prop] === 'function') { | ||
// do nothing | ||
} | ||
// don't call JSON.stringify() on Java native objects--Rhino will throw an exception | ||
else { | ||
else if (typeof e[prop] !== 'function') { | ||
// don't call JSON.stringify() on Java native objects--Rhino will throw an exception | ||
result[prop] = isJavaNativeObject(e[prop]) ? String(e[prop]) : e[prop]; | ||
@@ -99,0 +96,0 @@ } |
@@ -11,3 +11,3 @@ /*global env: true */ | ||
var conf = env.conf.markdown; | ||
var defaultTags = [ "classdesc", "description", "params", "properties", "returns", "see"]; | ||
var defaultTags = [ 'classdesc', 'description', 'params', 'properties', 'returns', 'see']; | ||
var hasOwnProp = Object.prototype.hasOwnProperty; | ||
@@ -44,3 +44,3 @@ var parse = require('jsdoc/util/markdown').getParser(); | ||
if (typeof doclet[tag] === "string" && shouldProcessString(tag, doclet[tag]) ) { | ||
if (typeof doclet[tag] === 'string' && shouldProcessString(tag, doclet[tag]) ) { | ||
doclet[tag] = parse(doclet[tag]); | ||
@@ -47,0 +47,0 @@ } |
@@ -17,5 +17,5 @@ /** | ||
if (e.filename.match(/\.erb$/)) { | ||
e.source = e.source.replace(/<%.*%>/g, ""); | ||
e.source = e.source.replace(/<%.*%>/g, ''); | ||
} | ||
} | ||
}; |
JSDoc 3 | ||
======= | ||
[![Build Status](https://secure.travis-ci.org/jsdoc3/jsdoc.png?branch=master)](http://travis-ci.org/jsdoc3/jsdoc) | ||
[![Build Status](https://img.shields.io/travis/jsdoc3/jsdoc.svg)](http://travis-ci.org/jsdoc3/jsdoc) | ||
@@ -5,0 +5,0 @@ An API documentation generator for JavaScript. |
/*global env: true */ | ||
'use strict'; | ||
var template = require('jsdoc/template'), | ||
fs = require('jsdoc/fs'), | ||
path = require('jsdoc/path'), | ||
taffy = require('taffydb').taffy, | ||
logger = require('jsdoc/util/logger'), | ||
helper = require('jsdoc/util/templateHelper'), | ||
htmlsafe = helper.htmlsafe, | ||
linkto = helper.linkto, | ||
resolveAuthorLinks = helper.resolveAuthorLinks, | ||
scopeToPunc = helper.scopeToPunc, | ||
hasOwnProp = Object.prototype.hasOwnProperty, | ||
data, | ||
view, | ||
outdir = env.opts.destination; | ||
var fs = require('jsdoc/fs'); | ||
var helper = require('jsdoc/util/templateHelper'); | ||
var logger = require('jsdoc/util/logger'); | ||
var path = require('jsdoc/path'); | ||
var taffy = require('taffydb').taffy; | ||
var template = require('jsdoc/template'); | ||
var util = require('util'); | ||
var htmlsafe = helper.htmlsafe; | ||
var linkto = helper.linkto; | ||
var resolveAuthorLinks = helper.resolveAuthorLinks; | ||
var scopeToPunc = helper.scopeToPunc; | ||
var hasOwnProp = Object.prototype.hasOwnProperty; | ||
var data; | ||
var view; | ||
var outdir = env.opts.destination; | ||
function find(spec) { | ||
@@ -61,21 +65,114 @@ return helper.find(data, spec); | ||
function getSignatureAttributes(item) { | ||
var attributes = []; | ||
if (item.optional) { | ||
attributes.push('opt'); | ||
} | ||
if (item.nullable === true) { | ||
attributes.push('nullable'); | ||
} | ||
else if (item.nullable === false) { | ||
attributes.push('non-null'); | ||
} | ||
return attributes; | ||
} | ||
function updateItemName(item) { | ||
var attributes = getSignatureAttributes(item); | ||
var itemName = item.name || ''; | ||
if (item.variable) { | ||
itemName = '…' + itemName; | ||
} | ||
if (attributes && attributes.length) { | ||
itemName = util.format( '%s<span class="signature-attributes">%s</span>', itemName, | ||
attributes.join(', ') ); | ||
} | ||
return itemName; | ||
} | ||
function addParamAttributes(params) { | ||
return params.map(updateItemName); | ||
} | ||
function buildItemTypeStrings(item) { | ||
var types = []; | ||
if (item.type && item.type.names) { | ||
item.type.names.forEach(function(name) { | ||
types.push( linkto(name, htmlsafe(name)) ); | ||
}); | ||
} | ||
return types; | ||
} | ||
function buildAttribsString(attribs) { | ||
var attribsString = ''; | ||
if (attribs && attribs.length) { | ||
attribsString = htmlsafe( util.format('(%s) ', attribs.join(', ')) ); | ||
} | ||
return attribsString; | ||
} | ||
function addNonParamAttributes(items) { | ||
var types = []; | ||
items.forEach(function(item) { | ||
types = types.concat( buildItemTypeStrings(item) ); | ||
}); | ||
return types; | ||
} | ||
function addSignatureParams(f) { | ||
var params = helper.getSignatureParams(f, 'optional'); | ||
var params = f.params ? addParamAttributes(f.params) : []; | ||
f.signature = (f.signature || '') + '('+params.join(', ')+')'; | ||
f.signature = util.format( '%s(%s)', (f.signature || ''), params.join(', ') ); | ||
} | ||
function addSignatureReturns(f) { | ||
var returnTypes = helper.getSignatureReturns(f); | ||
var attribs = []; | ||
var attribsString = ''; | ||
var returnTypes = []; | ||
var returnTypesString = ''; | ||
// jam all the return-type attributes into an array. this could create odd results (for example, | ||
// if there are both nullable and non-nullable return types), but let's assume that most people | ||
// who use multiple @return tags aren't using Closure Compiler type annotations, and vice-versa. | ||
if (f.returns) { | ||
f.returns.forEach(function(item) { | ||
helper.getAttribs(item).forEach(function(attrib) { | ||
if (attribs.indexOf(attrib) === -1) { | ||
attribs.push(attrib); | ||
} | ||
}); | ||
}); | ||
attribsString = buildAttribsString(attribs); | ||
} | ||
if (f.returns) { | ||
returnTypes = addNonParamAttributes(f.returns); | ||
} | ||
if (returnTypes.length) { | ||
returnTypesString = util.format( ' → %s{%s}', attribsString, returnTypes.join('|') ); | ||
} | ||
f.signature = '<span class="signature">' + (f.signature || '') + '</span>' + | ||
'<span class="type-signature">' + | ||
(returnTypes && returnTypes.length ? ' → {' + returnTypes.join('|') + '}' : '') + | ||
'</span>'; | ||
'<span class="type-signature">' + returnTypesString + '</span>'; | ||
} | ||
function addSignatureTypes(f) { | ||
var types = helper.getSignatureTypes(f); | ||
var types = f.type ? buildItemTypeStrings(f) : []; | ||
f.signature = (f.signature || '') + '<span class="type-signature">'+(types.length? ' :'+types.join('|') : '')+'</span>'; | ||
f.signature = (f.signature || '') + '<span class="type-signature">' + | ||
(types.length ? ' :' + types.join('|') : '') + '</span>'; | ||
} | ||
@@ -85,6 +182,5 @@ | ||
var attribs = helper.getAttribs(f); | ||
var attribsString = buildAttribsString(attribs); | ||
f.attribs = '<span class="type-signature">' + htmlsafe(attribs.length ? | ||
// we want the template output to say 'abstract', not 'virtual' | ||
'<' + attribs.join(', ').replace('virtual', 'abstract') + '> ' : '') + '</span>'; | ||
f.attribs = util.format('<span class="type-signature">%s</span>', attribsString); | ||
} | ||
@@ -104,3 +200,3 @@ | ||
if (!doclet.meta) { | ||
return; | ||
return null; | ||
} | ||
@@ -176,3 +272,3 @@ | ||
module.module = symbols[module.longname]; | ||
module.module.name = module.module.name.replace('module:', 'require("') + '")'; | ||
module.module.name = module.module.name.replace('module:', '(require("') + '"))'; | ||
} | ||
@@ -206,3 +302,3 @@ }); | ||
if ( !hasOwnProp.call(seen, m.longname) ) { | ||
nav += '<li>'+linkto(m.longname, m.name)+'</li>'; | ||
nav += '<li>' + linkto(m.longname, m.name) + '</li>'; | ||
} | ||
@@ -219,3 +315,3 @@ seen[m.longname] = true; | ||
if ( !hasOwnProp.call(seen, e.longname) ) { | ||
nav += '<li>'+linkto( e.longname, e.name.replace(/(^"|"$)/g, '') )+'</li>'; | ||
nav += '<li>' + linkto( e.longname, e.name.replace(/(^"|"$)/g, '') ) + '</li>'; | ||
} | ||
@@ -231,3 +327,3 @@ seen[e.longname] = true; | ||
if ( !hasOwnProp.call(seen, c.longname) ) { | ||
classNav += '<li>'+linkto(c.longname, c.name)+'</li>'; | ||
classNav += '<li>' + linkto(c.longname, c.name) + '</li>'; | ||
} | ||
@@ -248,3 +344,3 @@ seen[c.longname] = true; | ||
if ( !hasOwnProp.call(seen, e.longname) ) { | ||
nav += '<li>'+linkto(e.longname, e.name)+'</li>'; | ||
nav += '<li>' + linkto(e.longname, e.name) + '</li>'; | ||
} | ||
@@ -261,3 +357,3 @@ seen[e.longname] = true; | ||
if ( !hasOwnProp.call(seen, n.longname) ) { | ||
nav += '<li>'+linkto(n.longname, n.name)+'</li>'; | ||
nav += '<li>' + linkto(n.longname, n.name) + '</li>'; | ||
} | ||
@@ -274,3 +370,3 @@ seen[n.longname] = true; | ||
if ( !hasOwnProp.call(seen, m.longname) ) { | ||
nav += '<li>'+linkto(m.longname, m.name)+'</li>'; | ||
nav += '<li>' + linkto(m.longname, m.name) + '</li>'; | ||
} | ||
@@ -286,3 +382,3 @@ seen[m.longname] = true; | ||
members.tutorials.forEach(function(t) { | ||
nav += '<li>'+tutoriallink(t.name)+'</li>'; | ||
nav += '<li>' + tutoriallink(t.name) + '</li>'; | ||
}); | ||
@@ -289,0 +385,0 @@ |
@@ -0,1 +1,2 @@ | ||
/*eslint no-nested-ternary:0, space-infix-ops: 0 */ | ||
/** | ||
@@ -2,0 +3,0 @@ @overview Builds a tree-like JSON string from the doclet data. |
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
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
HTTP dependency
Supply chain riskContains a dependency which resolves to a remote HTTP URL which could be used to inject untrusted code and reduce overall package reliability.
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
Install scripts
Supply chain riskInstall scripts are run when the package is installed. The majority of malware in npm is hidden in install scripts.
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
431585
5
9762
19
10
96
+ Addedrequizzle@~0.1.0
+ Addedstrip-json-comments@~0.1.3
+ Addedrequizzle@0.1.1(transitive)
+ Addedstrip-json-comments@0.1.3(transitive)
+ Addedunderscore@1.6.0(transitive)
- Removedesprima@1.0.4(transitive)
- Removedunderscore@1.4.4(transitive)
Updatedcatharsis@~0.7.1
Updatedesprima@https://github.com/ariya/esprima/tarball/49a2eccb243f29bd653b11e9419241a9d726af7c
Updatedunderscore@~1.6.0