mirror of
https://github.com/jsdoc/jsdoc.git
synced 2025-12-08 19:46:11 +00:00
Added events, added parse handlers, added plugins and markdown plugin support.
This commit is contained in:
parent
7b55f59263
commit
db62d9e7b4
18
LICENSE.md
18
LICENSE.md
@ -25,7 +25,7 @@ with JSDoc 3. Each is provided under its own license and has source
|
||||
available from other locations.
|
||||
|
||||
Rhino
|
||||
-----
|
||||
----
|
||||
|
||||
Rhino is open source and licensed by Mozilla under the MPL 1.1 or
|
||||
later/GPL 2.0 or later licenses.
|
||||
@ -36,7 +36,7 @@ You can obtain the source code for Rhino from the Mozilla web site at
|
||||
http://www.mozilla.org/rhino/download.html
|
||||
|
||||
json2xml (modules/goessner/json2xml)
|
||||
--------
|
||||
----
|
||||
|
||||
json2xml is copyright (c) Stefan Goessner 2006
|
||||
|
||||
@ -47,7 +47,7 @@ http://goessner.net/
|
||||
http://goessner.net/download/prj/jsonxml/
|
||||
|
||||
Node (modules/common/assert, modules/common/util)
|
||||
-------
|
||||
----
|
||||
|
||||
Node is Copyright 2009, 2010 Ryan Lienhart Dahl. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@ -71,7 +71,7 @@ IN THE SOFTWARE.
|
||||
https://github.com/ry/node
|
||||
|
||||
JSONSchema Validator (modules/sitepen/jsonschema)
|
||||
--------------------
|
||||
----
|
||||
|
||||
JSONSchema is copyright (c) 2007 Kris Zyp SitePen (www.sitepen.com)
|
||||
|
||||
@ -80,8 +80,16 @@ http://www.sitepen.com/blog/2010/03/02/commonjs-utilities/
|
||||
|
||||
Licensed under the MIT license.
|
||||
|
||||
markdown-js (modules/evilstreak/markdown)
|
||||
----
|
||||
|
||||
markdown-js is released under the MIT license.
|
||||
http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
http://github.com/evilstreak/markdown-js
|
||||
|
||||
Mustache (templates/lib/janl/mustache.js)
|
||||
-------------------
|
||||
----
|
||||
|
||||
Mustache is
|
||||
Copyright (c) 2009 Chris Wanstrath (Ruby)
|
||||
|
||||
@ -17,7 +17,7 @@ directory.
|
||||
|
||||
To test that the installed app is working, execute the following:
|
||||
|
||||
$ java -jar jsdoc.jar -T
|
||||
$ java -jar jsdoc.jar --test
|
||||
|
||||
Usage
|
||||
-----
|
||||
@ -25,11 +25,11 @@ Usage
|
||||
This example assumes your working directory is the JSDoc project
|
||||
base directory...
|
||||
|
||||
$ java -jar jsdoc.jar -d stdout yourSourceCode.js
|
||||
$ java -jar jsdoc.jar yourSourceCodeFile.js
|
||||
|
||||
For help regarding the supported commandline options use -h.
|
||||
|
||||
$ java -jar jsdoc.jar -h
|
||||
$ java -jar jsdoc.jar --help
|
||||
|
||||
See
|
||||
---
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
app.name=jsdoc-3
|
||||
app.version=0.0.0
|
||||
app.name=jsdoc
|
||||
app.version=3.0.0alpha1
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"name": "@app.name@",
|
||||
"version": "@app.version@",
|
||||
"revision": "@timestamp@",
|
||||
"description": "An automatic documentation generator for javascript",
|
||||
"description": "An automatic documentation generator for javascript.",
|
||||
"keywords": [ "documentation", "javascript" ],
|
||||
"licenses": [
|
||||
{
|
||||
@ -16,7 +16,13 @@
|
||||
"url": "http://github.com/micmath/JSDoc"
|
||||
}
|
||||
],
|
||||
"bugs": "http://jsdoc.lighthouseapp.com/",
|
||||
"contributors" : [
|
||||
{
|
||||
"name": "Michael Mathews",
|
||||
"email": "micmath@gmail.com",
|
||||
"web": "http://micmath.ws"
|
||||
}
|
||||
],
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Michael Mathews",
|
||||
|
||||
10
conf.json
10
conf.json
@ -1,3 +1,11 @@
|
||||
{
|
||||
"permitUnknownTags": false
|
||||
"tags": {
|
||||
"allowUnknownTags": false
|
||||
},
|
||||
"source": {
|
||||
"includePattern": ".+\\.js(doc)?$"
|
||||
},
|
||||
"plugins": [
|
||||
"plugins/markdown.js"
|
||||
]
|
||||
}
|
||||
309
main.js
309
main.js
@ -1,156 +1,171 @@
|
||||
/**
|
||||
* @project JSDoc
|
||||
* @copyright 2010 (c) Michael Mathews <micmath@gmail.com>
|
||||
* @license See LICENSE.md file included in this distribution.
|
||||
* @overview JSDoc/main.js
|
||||
* @copyright 2010, 2011 (c) Michael Mathews <micmath@gmail.com>
|
||||
* @license See LICENSE.md file included in this distribution.
|
||||
*/
|
||||
|
||||
//// bootstrap
|
||||
|
||||
/** @global */
|
||||
const BASEDIR = arguments[0].replace(/([\/\\])main\.js$/, '$1'); // jsdoc.jar sets argument[0] to the abspath to main.js
|
||||
|
||||
/** @global */
|
||||
function require(id) { // like commonjs
|
||||
var path = require.base + id + '.js',
|
||||
fileContent = '';
|
||||
|
||||
try {
|
||||
var file = new java.io.File(path),
|
||||
scanner = new java.util.Scanner(file).useDelimiter("\\Z"),
|
||||
fileContent = String( scanner.next() );
|
||||
}
|
||||
catch(e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
try {
|
||||
var f = new Function('require', 'exports', 'module', fileContent),
|
||||
exports = require.cache[path] || {},
|
||||
module = { id: id, uri: path };
|
||||
|
||||
require.cache[id] = exports;
|
||||
f.call({}, require, exports, module);
|
||||
}
|
||||
catch(e) {
|
||||
print('Unable to require source code from "' + path + '": ' + e);
|
||||
}
|
||||
return exports;
|
||||
}
|
||||
require.base = BASEDIR + '/modules/'; // assume all module paths are relative to here
|
||||
require.cache = {}; // cache module exports. Like: {id: exported}
|
||||
|
||||
////
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
|
||||
|
||||
//// main
|
||||
|
||||
/** @global */
|
||||
const BASEDIR = arguments[0].replace(/([\/\\])main\.js$/, '$1'); // jsdoc.jar sets argument[0] to the abspath to main.js
|
||||
|
||||
/** @global */
|
||||
function require(id) { // like commonjs
|
||||
var path = require.base + id + '.js',
|
||||
fileContent = '';
|
||||
|
||||
try {
|
||||
var file = new java.io.File(path),
|
||||
scanner = new java.util.Scanner(file).useDelimiter("\\Z"),
|
||||
fileContent = String( scanner.next() );
|
||||
}
|
||||
catch(e) {
|
||||
print('Unable to read source code from "' + path + '": ' + e);
|
||||
}
|
||||
|
||||
/** @global */
|
||||
env = {
|
||||
run: {
|
||||
start: new Date(),
|
||||
finish: null
|
||||
},
|
||||
args: arguments.slice(1), // jsdoc.jar sets argument[0] to the abspath to main.js, user args follow
|
||||
conf: {}, // TODO: populate from file BASEDIR+'/conf.json'
|
||||
opts: {}
|
||||
};
|
||||
|
||||
try { main(); }
|
||||
finally { env.run.finish = new Date(); }
|
||||
|
||||
/** @global */
|
||||
function print(/*...*/) {
|
||||
for (var i = 0, leni = arguments.length; i < leni; i++) {
|
||||
java.lang.System.out.println('' + arguments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/** @global */
|
||||
function dump(/*...*/) {
|
||||
for (var i = 0, leni = arguments.length; i < leni; i++) {
|
||||
print( require('common/dumper').dump(arguments[i]) );
|
||||
}
|
||||
|
||||
}
|
||||
try {
|
||||
var f = new Function('require', 'exports', 'module', fileContent),
|
||||
exports = require.cache[path] || {},
|
||||
module = { id: id, uri: path };
|
||||
|
||||
require.cache[id] = exports;
|
||||
f.call({}, require, exports, module);
|
||||
}
|
||||
catch(e) {
|
||||
print('Unable to require source code from "' + path + '": ' + e.toSource());
|
||||
}
|
||||
return exports;
|
||||
}
|
||||
require.base = BASEDIR + '/modules/'; // assume all module paths are relative to here
|
||||
require.cache = {}; // cache module exports. Like: {id: exported}
|
||||
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
|
||||
|
||||
/** @global */
|
||||
function include(filepath) {
|
||||
try {
|
||||
load(BASEDIR + filepath);
|
||||
}
|
||||
catch (e) {
|
||||
print('Cannot include "' + BASEDIR + filepath + '": '+e);
|
||||
}
|
||||
}
|
||||
|
||||
/** @global */
|
||||
function exit(v) {
|
||||
java.lang.System.exit(v);
|
||||
}
|
||||
|
||||
function main() {
|
||||
var sourceFiles,
|
||||
docs,
|
||||
jsdoc = {
|
||||
opts: {
|
||||
parser: require('jsdoc/opts/parser')
|
||||
},
|
||||
src: {
|
||||
scanner: require('jsdoc/src/scanner'),
|
||||
parser: require('jsdoc/src/parser')
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
env.conf = JSON.parse( require('common/fs').read(BASEDIR+'conf.json') );
|
||||
}
|
||||
catch (e) {
|
||||
throw('Configuration file cannot be evaluated. '+e);
|
||||
}
|
||||
|
||||
env.opts = jsdoc.opts.parser.parse(env.args);
|
||||
|
||||
if (env.opts.help) {
|
||||
print( jsdoc.optParser.help() );
|
||||
exit(0);
|
||||
}
|
||||
else if (env.opts.test) {
|
||||
include('test/runall.js');
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (env.opts._.length > 0) { // are there any source files to scan?
|
||||
sourceFiles = jsdoc.src.scanner.scan(env.opts._, (env.opts.recurse? 10 : undefined));
|
||||
//dump('sourceFiles...', sourceFiles);
|
||||
docs = jsdoc.src.parser.parse(sourceFiles, env.opts.encoding);
|
||||
//dump('jsdoc.docs...', docs);
|
||||
if (env.opts.template) {
|
||||
include('templates/'+env.opts.template+'/publish.js');
|
||||
if (typeof publish === 'function') {
|
||||
publish(docs, {});
|
||||
}
|
||||
/** @global */
|
||||
env = {
|
||||
run: {
|
||||
start: new Date(),
|
||||
finish: null
|
||||
},
|
||||
args: arguments.slice(1), // jsdoc.jar sets argument[0] to the abspath to main.js, user args follow
|
||||
conf: {}, // TODO: populate from file BASEDIR+'/conf.json'
|
||||
opts: {}
|
||||
};
|
||||
|
||||
/** @global */
|
||||
app = {
|
||||
jsdoc: {
|
||||
scanner: new (require('jsdoc/src/scanner').Scanner)(),
|
||||
parser: new (require('jsdoc/src/parser').Parser)()
|
||||
}
|
||||
}
|
||||
|
||||
try { main(); }
|
||||
finally { env.run.finish = new Date(); }
|
||||
|
||||
/** @global */
|
||||
function print(/*...*/) {
|
||||
for (var i = 0, leni = arguments.length; i < leni; i++) {
|
||||
java.lang.System.out.println('' + arguments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/** @global */
|
||||
function dump(/*...*/) {
|
||||
for (var i = 0, leni = arguments.length; i < leni; i++) {
|
||||
print( require('common/dumper').dump(arguments[i]) );
|
||||
}
|
||||
}
|
||||
|
||||
/** @global */
|
||||
function include(filepath) {
|
||||
try {
|
||||
load(BASEDIR + filepath);
|
||||
}
|
||||
catch (e) {
|
||||
print('Cannot include "' + BASEDIR + filepath + '": '+e);
|
||||
}
|
||||
}
|
||||
|
||||
/** @global */
|
||||
function exit(v) {
|
||||
java.lang.System.exit(v);
|
||||
}
|
||||
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
|
||||
|
||||
|
||||
/** @global */
|
||||
function main() {
|
||||
var sourceFiles,
|
||||
docs,
|
||||
jsdoc = {
|
||||
opts: {
|
||||
parser: require('jsdoc/opts/parser')
|
||||
}
|
||||
//
|
||||
// if (env.opts.validate) {
|
||||
// var jsonSchema = require('sitepen/jsonSchema');
|
||||
// var jsdocSchema = require('jsdoc/schema').jsdocSchema;
|
||||
// var validation = jsonSchema.validate(jsdoc.srcParser.result.toObject(), jsdocSchema);
|
||||
// print('Validation: ' + validation.toSource());
|
||||
// }
|
||||
//
|
||||
// if (!env.opts.destination || env.opts.destination.indexOf('stdout') === 0) {
|
||||
// print( jsdoc.srcParser.result.toString(env.opts.destination) );
|
||||
// }
|
||||
// else if (env.opts.template) {
|
||||
// try {
|
||||
// load(BASEDIR+'/templates/'+env.opts.template+'/publish.js');
|
||||
// }
|
||||
// catch (e) {
|
||||
// print('Cannot load the specified template: templates/'+env.opts.template+'/publish.js: '+e);
|
||||
// }
|
||||
//
|
||||
// publish(jsdoc.srcParser.result.toObject(), {});
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
////
|
||||
};
|
||||
|
||||
try {
|
||||
env.conf = JSON.parse( require('common/fs').read(BASEDIR+'conf.json') );
|
||||
}
|
||||
catch (e) {
|
||||
throw('Configuration file cannot be evaluated. '+e);
|
||||
}
|
||||
|
||||
env.opts = jsdoc.opts.parser.parse(env.args);
|
||||
|
||||
if (env.opts.help) {
|
||||
print( jsdoc.opts.parser.help() );
|
||||
exit(0);
|
||||
}
|
||||
else if (env.opts.test) {
|
||||
include('test/runner.js');
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// allow user-defined plugins to register listeners
|
||||
if (env.conf.plugins) {
|
||||
for (var i = 0, leni = env.conf.plugins.length; i < leni; i++) {
|
||||
include(env.conf.plugins[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (env.opts._.length > 0) { // are there any files to scan and parse?
|
||||
|
||||
// allow filtering of found source files
|
||||
if (env.conf.source && env.conf.source.includePattern) {
|
||||
var includeRegexp = new RegExp(env.conf.source.includePattern);
|
||||
app.jsdoc.scanner.on('sourceFileFound', function(e) {
|
||||
if ( !includeRegexp.test(e.fileName) ) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sourceFiles = app.jsdoc.scanner.scan(env.opts._, (env.opts.recurse? 10 : undefined));
|
||||
|
||||
require('jsdoc/src/handlers');
|
||||
|
||||
docs = app.jsdoc.parser.parse(sourceFiles, env.opts.encoding);
|
||||
|
||||
//dump(docs);
|
||||
//exit(0);
|
||||
|
||||
env.opts.template = env.opts.template || 'default';
|
||||
|
||||
// should define a global "publish" function
|
||||
include('templates/' + env.opts.template + '/publish.js');
|
||||
|
||||
if (typeof publish === 'function') {
|
||||
publish(
|
||||
docs,
|
||||
{ destination: env.opts.destination }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
88
modules/common/events.js
Normal file
88
modules/common/events.js
Normal file
@ -0,0 +1,88 @@
|
||||
/**
|
||||
@module common/events
|
||||
@author Michael Mathews <micmath@gmail.com>
|
||||
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
|
||||
*/
|
||||
(function() {
|
||||
/**
|
||||
@function module:common/events.mixin
|
||||
@param {*} o The object to recieve the EventEmitter members.
|
||||
*/
|
||||
exports.mixin = function(o) {
|
||||
for ( var p in EventEmitter ) {
|
||||
o[p] = EventEmitter[p];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@mixin module:common/events.EventEmitter
|
||||
*/
|
||||
var EventEmitter = {
|
||||
/**
|
||||
@function module:common/events.EventEmitter.on
|
||||
@param {string} type
|
||||
@param {function} handler
|
||||
@returns this
|
||||
*/
|
||||
on: function(type, handler, context) {
|
||||
if ( !type || !handler ) {
|
||||
return;
|
||||
}
|
||||
if ( !context ) { context = this; }
|
||||
|
||||
if ( !this.__bindings ) { this.__bindings = {}; }
|
||||
// TODO check here for hasOwnProperty?
|
||||
if ( !this.__bindings[type] ) { this.__bindings[type] = []; }
|
||||
|
||||
var call = function(e) {
|
||||
return handler.call(context, e);
|
||||
};
|
||||
this.__bindings[type].push( {handler: handler, call: call} );
|
||||
|
||||
return this; // chainable
|
||||
},
|
||||
|
||||
/**
|
||||
@function module:common/events.EventEmitter.fire
|
||||
@param {string} type
|
||||
@param {object} [eventData]
|
||||
@returns this
|
||||
*/
|
||||
fire: function(eventType, eventData) {
|
||||
if ( !eventType || !this.__bindings || !this.__bindings[eventType] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// run handlers in first-in-first-run order
|
||||
for (var i = 0, leni = this.__bindings[eventType].length; i < leni; i++) {
|
||||
if ( false === this.__bindings[eventType][i].call(eventData) ) {
|
||||
if (eventData) { eventData.defaultPrevented = true; }
|
||||
}
|
||||
}
|
||||
|
||||
return this; // chainable
|
||||
},
|
||||
|
||||
/**
|
||||
@function module:common/events.EventEmitter.removeListener
|
||||
@param {string} type
|
||||
@param {function} handler
|
||||
*/
|
||||
removeListener: function(type, handler) {
|
||||
if ( !type || !handler || !this.__bindings || !this.__bindings[type] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var i = this.__bindings[type].length;
|
||||
while ( i-- ) {
|
||||
if ( this.__bindings[type][i].handler === handler ) {
|
||||
this.__bindings[type].splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.__bindings[type].length === 0) {
|
||||
delete this.__bindings[type];
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
1446
modules/evilstreak/markdown.js
Normal file
1446
modules/evilstreak/markdown.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -6,8 +6,12 @@
|
||||
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
|
||||
*/
|
||||
(function() {
|
||||
var Tag = require('jsdoc/tag').Tag,
|
||||
tagDictionary = require('jsdoc/tag/dictionary');
|
||||
var jsdoc = {
|
||||
tag: {
|
||||
Tag: require('jsdoc/tag').Tag,
|
||||
dictionary: require('jsdoc/tag/dictionary')
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@constructor
|
||||
@ -27,13 +31,11 @@
|
||||
for (var i = 0, leni = newTags.length; i < leni; i++) {
|
||||
this.addTag(newTags[i].title, newTags[i].text);
|
||||
}
|
||||
|
||||
this.applyTags(this.tags);
|
||||
}
|
||||
|
||||
exports.Doclet.prototype.addTag = function(title, text) {
|
||||
var tagDef = tagDictionary.lookUp(title),
|
||||
newTag = new Tag(title, text, {});
|
||||
var tagDef = jsdoc.tag.dictionary.lookUp(title),
|
||||
newTag = new jsdoc.tag.Tag(title, text, {});
|
||||
|
||||
if (tagDef.onTagged) {
|
||||
if (tagDef.onTagged(this, newTag) !== false) { // onTagged handler prevents tag being added bt returning false
|
||||
@ -43,26 +45,22 @@
|
||||
else {
|
||||
this.tags.push(newTag);
|
||||
}
|
||||
|
||||
applyTag.call(this, newTag);
|
||||
}
|
||||
|
||||
exports.Doclet.prototype.applyTags = function(tags) {
|
||||
var tag;
|
||||
|
||||
for (var i = 0, leni = tags.length; i < leni; i++) {
|
||||
tag = tags[i];
|
||||
|
||||
if (tag.title === 'name') {
|
||||
this.name = tag.value;
|
||||
}
|
||||
|
||||
if (tag.title === 'kind') {
|
||||
this.kind = tag.value;
|
||||
}
|
||||
|
||||
if (tag.title === 'description') {
|
||||
this.description = tag.value;
|
||||
}
|
||||
}
|
||||
function applyTag(tag) {
|
||||
if (tag.title === 'name') {
|
||||
this.name = tag.value;
|
||||
}
|
||||
|
||||
if (tag.title === 'kind') {
|
||||
this.kind = tag.value;
|
||||
}
|
||||
|
||||
if (tag.title === 'description') {
|
||||
this.description = tag.value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,7 +73,6 @@
|
||||
|
||||
docletSrc = unwrap(docletSrc);
|
||||
tagSrcs = split(docletSrc);
|
||||
//dump('tagSrcs', tagSrcs);
|
||||
|
||||
for each(tagSrc in tagSrcs) {
|
||||
tags.push( {title: tagSrc.title, text: tagSrc.text} );
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
ourOptions,
|
||||
defaults = {
|
||||
template: 'default',
|
||||
destination: 'jsdoc.xml'
|
||||
destination: 'console'
|
||||
};
|
||||
|
||||
argsParser.addOption('t', 'template', true, 'The name of the template to use. Default: the "default" template');
|
||||
|
||||
17
modules/jsdoc/src/handlers.js
Normal file
17
modules/jsdoc/src/handlers.js
Normal file
@ -0,0 +1,17 @@
|
||||
(function() {
|
||||
var Doclet = require('jsdoc/doclet').Doclet;
|
||||
|
||||
app.jsdoc.parser.on('jsdocCommentFound', function(e) {
|
||||
var newDoclet = new Doclet(e.comment, e.node, e.filename);
|
||||
|
||||
if (newDoclet) {
|
||||
e = { doclet: newDoclet };
|
||||
this.fire('newDoclet', e);
|
||||
|
||||
if (!e.defaultPrevented) {
|
||||
this.addResult(newDoclet);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
||||
@ -1,307 +1,356 @@
|
||||
/**
|
||||
* @module jsdoc/src/parser
|
||||
@module jsdoc/src/parser
|
||||
@requires module:common/events
|
||||
@requires module:jsdoc/doclet
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var Token = Packages.org.mozilla.javascript.Token,
|
||||
Doclet = require('jsdoc/doclet').Doclet,
|
||||
_parseResult = [];
|
||||
|
||||
exports.result = function() {
|
||||
return _parseResult;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
function visitNode(node) {
|
||||
var commentSrc = '',
|
||||
thisDoclet = null,
|
||||
thisDocletName = '',
|
||||
thisDocletPath = '';
|
||||
|
||||
// look for all comments that have names provided
|
||||
if (node.type === Token.SCRIPT && node.comments) {
|
||||
for each(var comment in node.comments.toArray()) {
|
||||
if (comment.commentType === Token.CommentType.JSDOC) {
|
||||
commentSrc = '' + comment.toSource();
|
||||
|
||||
if (commentSrc) {
|
||||
thisDoclet = new Doclet(commentSrc, node, currentSourceName);
|
||||
|
||||
if (thisDoclet) {
|
||||
_parseResult.push(thisDoclet);
|
||||
}
|
||||
|
||||
// if ( thisDoclet.hasTag('name') && thisDoclet.hasTag('kind') ) {
|
||||
// jsdoc.doclets.addDoclet(thisDoclet);
|
||||
// if (thisDoclet.tagValue('kind') === 'module') {
|
||||
// jsdoc.name.setCurrentModule( thisDoclet.tagValue('path') );
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use the nocode option to shortcut all the following blah blah
|
||||
if (env.opts.nocode) { return true; }
|
||||
//
|
||||
// // like function foo() {}
|
||||
// if (node.type == Token.FUNCTION && String(node.name) !== '') {
|
||||
// commentSrc = (node.jsDoc)? String(node.jsDoc) : '';
|
||||
//
|
||||
// if (commentSrc) {
|
||||
// thisDoclet = jsdoc.doclet.makeDoclet(commentSrc, node, currentSourceName);
|
||||
// thisDocletName = thisDoclet.tagValue('name');
|
||||
//
|
||||
// if (!thisDoclet.hasTag('kind')) { // guess kind from the source code
|
||||
// thisDoclet.addTag('kind', 'method')
|
||||
// }
|
||||
//
|
||||
// if (!thisDocletName) { // guess name from the source code
|
||||
// nodeName = jsdoc.name.resolveInner(node.name, node, thisDoclet);
|
||||
// thisDoclet.setName(nodeName);
|
||||
// jsdoc.doclets.addDoclet(thisDoclet);
|
||||
// }
|
||||
// jsdoc.name.refs.push([node, thisDoclet]);
|
||||
// }
|
||||
// else { // an uncommented function?
|
||||
// // this thing may have commented members, so keep a ref to the thing but don't add it to the doclets list
|
||||
// thisDoclet = jsdoc.doclet.makeDoclet('[[undocumented]]', node, currentSourceName);
|
||||
//
|
||||
// nodeName = jsdoc.name.resolvePath(node.name, node, thisDoclet);
|
||||
// thisDoclet.setName(nodeName);
|
||||
// jsdoc.name.refs.push([
|
||||
// node,
|
||||
// thisDoclet
|
||||
// ]);
|
||||
// }
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// // like foo = function(){} or foo: function(){}
|
||||
// if (node.type === Token.ASSIGN || node.type === Token.COLON) {
|
||||
//
|
||||
// var nodeName = nodeToString(node.left),
|
||||
// nodeKind = '';
|
||||
// commentSrc = node.jsDoc || node.left.jsDoc;
|
||||
//
|
||||
// if (commentSrc) {
|
||||
// commentSrc = '' + commentSrc;
|
||||
//
|
||||
// thisDoclet = jsdoc.doclet.makeDoclet(commentSrc, node, currentSourceName);
|
||||
// thisDocletName = thisDoclet.tagValue('name');
|
||||
// nodeKind = thisDoclet.tagValue('kind');
|
||||
//
|
||||
// if (!thisDoclet.hasTag('kind')) { // guess kind from the source code
|
||||
// if (node.right.type == Token.FUNCTION) { // assume it's a method
|
||||
// thisDoclet.addTag('kind', 'method');
|
||||
// }
|
||||
// else {
|
||||
// thisDoclet.addTag('kind', 'property');
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (!thisDocletName) { // guess name from the source code
|
||||
// nodeName = jsdoc.name.resolvePath(nodeName, node, thisDoclet);
|
||||
//
|
||||
// thisDoclet.setName(nodeName);
|
||||
// jsdoc.doclets.addDoclet(thisDoclet);
|
||||
// }
|
||||
// jsdoc.name.refs.push([node.right, thisDoclet]);
|
||||
// }
|
||||
// else { // an uncommented objlit or anonymous function?
|
||||
//
|
||||
// // this thing may have commented members, so keep a ref to the thing but don't add it to the doclets list
|
||||
//
|
||||
// thisDoclet = jsdoc.doclet.makeDoclet('[[undocumented]]', node, currentSourceName);
|
||||
// nodeName = jsdoc.name.resolvePath(nodeName, node, thisDoclet);
|
||||
//
|
||||
// thisDoclet.setName(nodeName);
|
||||
// jsdoc.name.refs.push([
|
||||
// node.right,
|
||||
// thisDoclet
|
||||
// ]);
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// // like var foo = function(){} or var bar = {}
|
||||
// if (node.type == Token.VAR || node.type == Token.LET || node.type == Token.CONST) {
|
||||
// var counter = 0,
|
||||
// nodeKind;
|
||||
//
|
||||
// if (node.variables) for each (var n in node.variables.toArray()) {
|
||||
//
|
||||
// if (n.target.type === Token.NAME) {
|
||||
// var val = n.initializer;
|
||||
//
|
||||
// commentSrc = (counter++ === 0 && !n.jsDoc)? node.jsDoc : n.jsDoc;
|
||||
// if (commentSrc) {
|
||||
// thisDoclet = jsdoc.doclet.makeDoclet('' + commentSrc, node, currentSourceName);
|
||||
// thisDocletPath = thisDoclet.tagValue('path');
|
||||
// thisDocletName = thisDoclet.tagValue('name');
|
||||
//
|
||||
// if (!thisDoclet.hasTag('kind') && val) { // guess kind from the source code
|
||||
// if (val.type == Token.FUNCTION) {
|
||||
// thisDoclet.addTag('kind', 'method');
|
||||
// }
|
||||
// else {
|
||||
// thisDoclet.addTag('kind', 'property');
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (!thisDocletName) {
|
||||
// thisDocletName = n.target.string;
|
||||
// if (!thisDocletPath) { // guess path from the source code
|
||||
// thisDocletPath = jsdoc.name.resolveInner(thisDocletName, node, thisDoclet);
|
||||
// thisDoclet.setName(thisDocletPath);
|
||||
// }
|
||||
// else {
|
||||
// thisDoclet.setName(thisDocletName);
|
||||
// }
|
||||
// jsdoc.doclets.addDoclet(thisDoclet);
|
||||
// }
|
||||
//
|
||||
// if (val) { jsdoc.name.refs.push([val, thisDoclet]); }
|
||||
// }
|
||||
// else { // an uncommented objlit or anonymous function?
|
||||
// var nodeName = nodeToString(n.target);
|
||||
// // this thing may have commented members, so keep a ref to the thing but don't add it to the doclets list
|
||||
// thisDoclet = jsdoc.doclet.makeDoclet('[[undocumented]]', n.target, currentSourceName);
|
||||
//
|
||||
// nodeName = jsdoc.name.resolveInner(nodeName, n.target, thisDoclet);
|
||||
// thisDoclet.setName(nodeName);
|
||||
//
|
||||
// if (val) jsdoc.name.refs.push([val, thisDoclet]);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
return true;
|
||||
}
|
||||
|
||||
var currentSourceName = '';
|
||||
|
||||
/**
|
||||
*/
|
||||
exports.parseSource = function(source, sourceName) {
|
||||
currentSourceName = sourceName;
|
||||
var ast = getParser().parse(source, sourceName, 1);
|
||||
|
||||
ast.visit(
|
||||
new Packages.org.mozilla.javascript.ast.NodeVisitor({
|
||||
visit: visitNode
|
||||
})
|
||||
);
|
||||
|
||||
currentSourceName = '';
|
||||
|
||||
return _parseResult;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
exports.clear = function() {
|
||||
_parseResult = [];
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
exports.parse = function(sourceFiles, encoding) {
|
||||
var ast = getParser(),
|
||||
fs = require('common/fs'),
|
||||
sourceCode = '',
|
||||
filename = '',
|
||||
jsUriScheme = 'javascript:';
|
||||
|
||||
if (arguments.length === 0) {
|
||||
throw 'module:jsdoc/parser.parseFiles requires argument sourceFiles(none provided).';
|
||||
}
|
||||
|
||||
if (typeof sourceFiles === 'string') { sourceFiles = [sourceFiles]; }
|
||||
|
||||
for (i = 0, leni = sourceFiles.length; i < leni; i++) {
|
||||
if (sourceFiles[i].indexOf(jsUriScheme) === 0) {
|
||||
sourceCode = sourceFiles[i].substr(jsUriScheme.length);
|
||||
filename = '[[string' + i + ']]';
|
||||
}
|
||||
else {
|
||||
filename = sourceFiles[i];
|
||||
var Token = Packages.org.mozilla.javascript.Token,
|
||||
Doclet = require('jsdoc/doclet').Doclet,
|
||||
currentParser = null,
|
||||
currentSourceName = '';
|
||||
|
||||
/**
|
||||
@constructor module:jsdoc/src/parser.Parser
|
||||
@mixesIn module:common/events.EventEmitter
|
||||
*/
|
||||
var Parser = exports.Parser = function() {
|
||||
this._resultBuffer = [];
|
||||
}
|
||||
require('common/events').mixin(exports.Parser.prototype);
|
||||
|
||||
/**
|
||||
@event jsdocCommentFound
|
||||
@param e
|
||||
@param {string} e.comment The raw text of the JSDoc comment that will be parsed.
|
||||
This value may be modified in place by your event handler.
|
||||
@param {string} e.file The name of the file containing the comment.
|
||||
@defaultAction The comment text will be used to create a new Doclet.
|
||||
Returning false from your handler will prevent this.
|
||||
*/
|
||||
|
||||
/**
|
||||
@event newDoclet
|
||||
@param e
|
||||
@param {string} e.doclet The new doclet that will be added to the results.
|
||||
The properties of this value may be modified in place by your event handler.
|
||||
@defaultAction The new doclet will be added to the parsers result set.
|
||||
Returning false from your handler will prevent this.
|
||||
*/
|
||||
|
||||
/**
|
||||
@method module:jsdoc/src/parser.Parser#parse
|
||||
@param {Array<string>} sourceFiles
|
||||
@param {string} [encoding=utf8]
|
||||
@fires jsdocCommentFound
|
||||
@fires newDoclet
|
||||
*/
|
||||
Parser.prototype.parse = function(sourceFiles, encoding) {
|
||||
var sourceCode = '',
|
||||
filename = '',
|
||||
jsUriScheme = 'javascript:';
|
||||
|
||||
if (arguments.length === 0) {
|
||||
throw 'module:jsdoc/parser.parseFiles requires argument sourceFiles(none provided).';
|
||||
}
|
||||
|
||||
if (typeof sourceFiles === 'string') { sourceFiles = [sourceFiles]; }
|
||||
|
||||
for (i = 0, leni = sourceFiles.length; i < leni; i++) {
|
||||
if (sourceFiles[i].indexOf(jsUriScheme) === 0) {
|
||||
sourceCode = sourceFiles[i].substr(jsUriScheme.length);
|
||||
filename = '[[string' + i + ']]';
|
||||
}
|
||||
else {
|
||||
filename = sourceFiles[i];
|
||||
try {
|
||||
sourceCode = fs.read(filename, encoding);
|
||||
sourceCode = require('common/fs').read(filename, encoding);
|
||||
}
|
||||
catch(e) {
|
||||
print('FILE READ ERROR: in module:jsdoc/parser.parseFiles: "' + filename + '" ' + e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
exports.parseSource(sourceCode, filename);
|
||||
}
|
||||
|
||||
return _parseResult;
|
||||
}
|
||||
|
||||
/**
|
||||
@private
|
||||
@function getParser
|
||||
*/
|
||||
function getParser() {
|
||||
var cx = Packages.org.mozilla.javascript.Context.getCurrentContext();
|
||||
|
||||
var ce = new Packages.org.mozilla.javascript.CompilerEnvirons();
|
||||
ce.setRecordingComments(true);
|
||||
ce.setRecordingLocalJsDocComments(true);
|
||||
ce.initFromContext(cx);
|
||||
return new Packages.org.mozilla.javascript.Parser(ce, ce.getErrorReporter());
|
||||
}
|
||||
|
||||
/**
|
||||
@private
|
||||
@function nodeToString
|
||||
@param {org.mozilla.javascript.ast.AstNode} node
|
||||
@returns {string}
|
||||
*/
|
||||
// credit: ringojs ninjas
|
||||
function nodeToString(node) {
|
||||
var str;
|
||||
|
||||
if (node.type === Token.GETPROP) {
|
||||
str = [nodeToString(node.target), node.property.string].join('.');
|
||||
}
|
||||
else if (node.type === Token.NAME) {
|
||||
str = node.string;
|
||||
}
|
||||
else if (node.type === Token.STRING) {
|
||||
str = node.value;
|
||||
}
|
||||
else if (node.type === Token.THIS) {
|
||||
str = 'this';
|
||||
}
|
||||
else if (node.type === Token.GETELEM) {
|
||||
str = node.toSource(); // like: Foo['Bar']
|
||||
}
|
||||
else {
|
||||
str = getTypeName(node);
|
||||
}
|
||||
|
||||
return '' + str;
|
||||
};
|
||||
|
||||
/**
|
||||
@private
|
||||
@function getTypeName
|
||||
@param {org.mozilla.javascript.ast.AstNode} node
|
||||
@returns {string}
|
||||
*/
|
||||
// credit: ringojs ninjas
|
||||
function getTypeName(node) {
|
||||
return node ? ''+Packages.org.mozilla.javascript.Token.typeToName(node.getType()) : '' ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
currentParser = this;
|
||||
parseSource(sourceCode, filename);
|
||||
currentParser = null;
|
||||
}
|
||||
|
||||
return this._resultBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
@method module:jsdoc/src/parser.Parser#results
|
||||
@returns {Array<Doclet>} The accumulated results of any calls to parse.
|
||||
*/
|
||||
Parser.prototype.results = function() {
|
||||
return this._resultBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
@method module:jsdoc/src/parser.Parser#addResult
|
||||
*/
|
||||
Parser.prototype.addResult = function(o) {
|
||||
this._resultBuffer.push(o);
|
||||
}
|
||||
|
||||
/**
|
||||
@method module:jsdoc/src/parser.Parser#clearResults
|
||||
@desc Empty any accumulated results of calls to parse.
|
||||
*/
|
||||
Parser.prototype.clearResults = function() {
|
||||
this._resultBuffer = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@private
|
||||
@function parseSource
|
||||
*/
|
||||
function parseSource(source, sourceName) {
|
||||
currentSourceName = sourceName;
|
||||
|
||||
var ast = parserFactory().parse(source, sourceName, 1);
|
||||
|
||||
ast.visit(
|
||||
new Packages.org.mozilla.javascript.ast.NodeVisitor({
|
||||
visit: visitNode
|
||||
})
|
||||
);
|
||||
|
||||
currentSourceName = '';
|
||||
}
|
||||
|
||||
/**
|
||||
@private
|
||||
@function visitNode
|
||||
*/
|
||||
function visitNode(node) {
|
||||
var commentSrc = '',
|
||||
thisDoclet = null;//,
|
||||
//thisDocletName = '',
|
||||
//thisDocletPath = '';
|
||||
|
||||
// look for all comments that have names provided
|
||||
if (node.type === Token.SCRIPT && node.comments) {
|
||||
for each(var comment in node.comments.toArray()) {
|
||||
if (comment.commentType === Token.CommentType.JSDOC) {
|
||||
if (commentSrc = '' + comment.toSource()) {
|
||||
var e = {
|
||||
comment: commentSrc,
|
||||
filename: currentSourceName,
|
||||
node: node
|
||||
};
|
||||
currentParser.fire('jsdocCommentFound', e, currentParser);
|
||||
|
||||
// if ( thisDoclet.hasTag('name') && thisDoclet.hasTag('kind') ) {
|
||||
// jsdoc.doclets.addDoclet(thisDoclet);
|
||||
// if (thisDoclet.tagValue('kind') === 'module') {
|
||||
// jsdoc.name.setCurrentModule( thisDoclet.tagValue('path') );
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use the nocode option to shortcut all the following blah blah
|
||||
if (env.opts.nocode) { return true; }
|
||||
//
|
||||
// // like function foo() {}
|
||||
// if (node.type == Token.FUNCTION && String(node.name) !== '') {
|
||||
// commentSrc = (node.jsDoc)? String(node.jsDoc) : '';
|
||||
//
|
||||
// if (commentSrc) {
|
||||
// thisDoclet = jsdoc.doclet.makeDoclet(commentSrc, node, currentSourceName);
|
||||
// thisDocletName = thisDoclet.tagValue('name');
|
||||
//
|
||||
// if (!thisDoclet.hasTag('kind')) { // guess kind from the source code
|
||||
// thisDoclet.addTag('kind', 'method')
|
||||
// }
|
||||
//
|
||||
// if (!thisDocletName) { // guess name from the source code
|
||||
// nodeName = jsdoc.name.resolveInner(node.name, node, thisDoclet);
|
||||
// thisDoclet.setName(nodeName);
|
||||
// jsdoc.doclets.addDoclet(thisDoclet);
|
||||
// }
|
||||
// jsdoc.name.refs.push([node, thisDoclet]);
|
||||
// }
|
||||
// else { // an uncommented function?
|
||||
// // this thing may have commented members, so keep a ref to the thing but don't add it to the doclets list
|
||||
// thisDoclet = jsdoc.doclet.makeDoclet('[[undocumented]]', node, currentSourceName);
|
||||
//
|
||||
// nodeName = jsdoc.name.resolvePath(node.name, node, thisDoclet);
|
||||
// thisDoclet.setName(nodeName);
|
||||
// jsdoc.name.refs.push([
|
||||
// node,
|
||||
// thisDoclet
|
||||
// ]);
|
||||
// }
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// // like foo = function(){} or foo: function(){}
|
||||
// if (node.type === Token.ASSIGN || node.type === Token.COLON) {
|
||||
//
|
||||
// var nodeName = nodeToString(node.left),
|
||||
// nodeKind = '';
|
||||
// commentSrc = node.jsDoc || node.left.jsDoc;
|
||||
//
|
||||
// if (commentSrc) {
|
||||
// commentSrc = '' + commentSrc;
|
||||
//
|
||||
// thisDoclet = jsdoc.doclet.makeDoclet(commentSrc, node, currentSourceName);
|
||||
// thisDocletName = thisDoclet.tagValue('name');
|
||||
// nodeKind = thisDoclet.tagValue('kind');
|
||||
//
|
||||
// if (!thisDoclet.hasTag('kind')) { // guess kind from the source code
|
||||
// if (node.right.type == Token.FUNCTION) { // assume it's a method
|
||||
// thisDoclet.addTag('kind', 'method');
|
||||
// }
|
||||
// else {
|
||||
// thisDoclet.addTag('kind', 'property');
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (!thisDocletName) { // guess name from the source code
|
||||
// nodeName = jsdoc.name.resolvePath(nodeName, node, thisDoclet);
|
||||
//
|
||||
// thisDoclet.setName(nodeName);
|
||||
// jsdoc.doclets.addDoclet(thisDoclet);
|
||||
// }
|
||||
// jsdoc.name.refs.push([node.right, thisDoclet]);
|
||||
// }
|
||||
// else { // an uncommented objlit or anonymous function?
|
||||
//
|
||||
// // this thing may have commented members, so keep a ref to the thing but don't add it to the doclets list
|
||||
//
|
||||
// thisDoclet = jsdoc.doclet.makeDoclet('[[undocumented]]', node, currentSourceName);
|
||||
// nodeName = jsdoc.name.resolvePath(nodeName, node, thisDoclet);
|
||||
//
|
||||
// thisDoclet.setName(nodeName);
|
||||
// jsdoc.name.refs.push([
|
||||
// node.right,
|
||||
// thisDoclet
|
||||
// ]);
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// // like var foo = function(){} or var bar = {}
|
||||
// if (node.type == Token.VAR || node.type == Token.LET || node.type == Token.CONST) {
|
||||
// var counter = 0,
|
||||
// nodeKind;
|
||||
//
|
||||
// if (node.variables) for each (var n in node.variables.toArray()) {
|
||||
//
|
||||
// if (n.target.type === Token.NAME) {
|
||||
// var val = n.initializer;
|
||||
//
|
||||
// commentSrc = (counter++ === 0 && !n.jsDoc)? node.jsDoc : n.jsDoc;
|
||||
// if (commentSrc) {
|
||||
// thisDoclet = jsdoc.doclet.makeDoclet('' + commentSrc, node, currentSourceName);
|
||||
// thisDocletPath = thisDoclet.tagValue('path');
|
||||
// thisDocletName = thisDoclet.tagValue('name');
|
||||
//
|
||||
// if (!thisDoclet.hasTag('kind') && val) { // guess kind from the source code
|
||||
// if (val.type == Token.FUNCTION) {
|
||||
// thisDoclet.addTag('kind', 'method');
|
||||
// }
|
||||
// else {
|
||||
// thisDoclet.addTag('kind', 'property');
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (!thisDocletName) {
|
||||
// thisDocletName = n.target.string;
|
||||
// if (!thisDocletPath) { // guess path from the source code
|
||||
// thisDocletPath = jsdoc.name.resolveInner(thisDocletName, node, thisDoclet);
|
||||
// thisDoclet.setName(thisDocletPath);
|
||||
// }
|
||||
// else {
|
||||
// thisDoclet.setName(thisDocletName);
|
||||
// }
|
||||
// jsdoc.doclets.addDoclet(thisDoclet);
|
||||
// }
|
||||
//
|
||||
// if (val) { jsdoc.name.refs.push([val, thisDoclet]); }
|
||||
// }
|
||||
// else { // an uncommented objlit or anonymous function?
|
||||
// var nodeName = nodeToString(n.target);
|
||||
// // this thing may have commented members, so keep a ref to the thing but don't add it to the doclets list
|
||||
// thisDoclet = jsdoc.doclet.makeDoclet('[[undocumented]]', n.target, currentSourceName);
|
||||
//
|
||||
// nodeName = jsdoc.name.resolveInner(nodeName, n.target, thisDoclet);
|
||||
// thisDoclet.setName(nodeName);
|
||||
//
|
||||
// if (val) jsdoc.name.refs.push([val, thisDoclet]);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@private
|
||||
@function parserFactory
|
||||
*/
|
||||
function parserFactory() {
|
||||
var cx = Packages.org.mozilla.javascript.Context.getCurrentContext();
|
||||
|
||||
var ce = new Packages.org.mozilla.javascript.CompilerEnvirons();
|
||||
ce.setRecordingComments(true);
|
||||
ce.setRecordingLocalJsDocComments(true);
|
||||
ce.initFromContext(cx);
|
||||
return new Packages.org.mozilla.javascript.Parser(ce, ce.getErrorReporter());
|
||||
}
|
||||
|
||||
/**
|
||||
@private
|
||||
@function nodeToString
|
||||
@param {org.mozilla.javascript.ast.AstNode} node
|
||||
@returns {string}
|
||||
*/
|
||||
// credit: ringojs ninjas
|
||||
function nodeToString(node) {
|
||||
var str;
|
||||
|
||||
if (node.type === Token.GETPROP) {
|
||||
str = [nodeToString(node.target), node.property.string].join('.');
|
||||
}
|
||||
else if (node.type === Token.NAME) {
|
||||
str = node.string;
|
||||
}
|
||||
else if (node.type === Token.STRING) {
|
||||
str = node.value;
|
||||
}
|
||||
else if (node.type === Token.THIS) {
|
||||
str = 'this';
|
||||
}
|
||||
else if (node.type === Token.GETELEM) {
|
||||
str = node.toSource(); // like: Foo['Bar']
|
||||
}
|
||||
else {
|
||||
str = getTypeName(node);
|
||||
}
|
||||
|
||||
return '' + str;
|
||||
};
|
||||
|
||||
/**
|
||||
@private
|
||||
@function getTypeName
|
||||
@param {org.mozilla.javascript.ast.AstNode} node
|
||||
@returns {string}
|
||||
*/
|
||||
// credit: ringojs ninjas
|
||||
function getTypeName(node) {
|
||||
return node ? ''+Packages.org.mozilla.javascript.Token.typeToName(node.getType()) : '' ;
|
||||
}
|
||||
|
||||
})();
|
||||
@ -11,13 +11,22 @@
|
||||
fs: require('common/fs')
|
||||
};
|
||||
|
||||
/**
|
||||
@constructor
|
||||
*/
|
||||
exports.Scanner = function() {
|
||||
}
|
||||
require('common/events').mixin(exports.Scanner.prototype);
|
||||
|
||||
/**
|
||||
Recursively searches the given searchPaths for js files.
|
||||
@param {Array.<string>} searchPaths
|
||||
@param {number} [depth=1]
|
||||
@fires sourceFileFound
|
||||
*/
|
||||
exports.scan = function(searchPaths, depth) {
|
||||
var filePaths = [];
|
||||
exports.Scanner.prototype.scan = function(searchPaths, depth) {
|
||||
var filePaths = [],
|
||||
that = this;
|
||||
|
||||
searchPaths = searchPaths || [];
|
||||
depth = depth || 1;
|
||||
@ -26,9 +35,11 @@
|
||||
filePaths = filePaths.concat(common.fs.ls($, depth));
|
||||
});
|
||||
|
||||
// TODO: allow user-defined filtering of files
|
||||
filePaths = filePaths.filter(function($) {
|
||||
return /.+\.js(doc)?$/i.test($);
|
||||
var e = { fileName: $ };
|
||||
that.fire('sourceFileFound', e);
|
||||
|
||||
return !e.defaultPrevented;
|
||||
});
|
||||
|
||||
return filePaths;
|
||||
|
||||
@ -9,19 +9,23 @@
|
||||
*/
|
||||
(function() {
|
||||
|
||||
var dictionary = require('jsdoc/tag/dictionary'),
|
||||
validator = require('jsdoc/tag/validator'),
|
||||
tagType = require('jsdoc/tag/type');
|
||||
var jsdoc = {
|
||||
tag: {
|
||||
dictionary: require('jsdoc/tag/dictionary'),
|
||||
validator: require('jsdoc/tag/validator'),
|
||||
type: require('jsdoc/tag/type')
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@constructor Tag
|
||||
*/
|
||||
exports.Tag = function(tagTitle, tagBody, meta) {
|
||||
var tagDef = dictionary.lookUp(tagTitle),
|
||||
var tagDef = jsdoc.tag.dictionary.lookUp(tagTitle),
|
||||
meta = meta || {};
|
||||
|
||||
this.title = dictionary.normalise( trim(tagTitle) );
|
||||
this.text = trim(tagBody, tagDef.preservesWhitespace);
|
||||
this.title = jsdoc.tag.dictionary.normalise( trim(tagTitle) );
|
||||
this.text = trim(tagBody, tagDef.keepsWhitespace);
|
||||
|
||||
if (this.text) {
|
||||
if (tagDef.canHaveType) {
|
||||
@ -33,9 +37,7 @@
|
||||
/*?boolean*/ optional,
|
||||
/*?boolean*/ nullable,
|
||||
/*?boolean*/ variable
|
||||
] = tagType.parse(this.text);
|
||||
|
||||
|
||||
] = jsdoc.tag.type.parse(this.text);
|
||||
|
||||
if (typeNames.length) {
|
||||
this.value.type = {
|
||||
@ -45,6 +47,7 @@
|
||||
variable: variable
|
||||
};
|
||||
}
|
||||
|
||||
if (remainingText) {
|
||||
if (tagDef.canHaveName) {
|
||||
var [tagName, tagDesc, tagOptional, tagDefault] = parseTagText(remainingText);
|
||||
@ -64,7 +67,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
validator.validate(this, meta);
|
||||
jsdoc.tag.validator.validate(this, meta);
|
||||
}
|
||||
|
||||
function trim(text, newlines) {
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
});
|
||||
|
||||
dictionary.defineTag('example', {
|
||||
preservesWhitespace: true,
|
||||
keepsWhitespace: true,
|
||||
mustHaveValue: true
|
||||
});
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
exports.validate = function(tag, meta) {
|
||||
var tagDef = dictionary.lookUp(tag.title);
|
||||
|
||||
if (!tagDef && !env.conf.permitUnknownTags) {
|
||||
if (!tagDef && !env.conf.tags.allowUnknownTags) {
|
||||
throw new UnknownTagError(tag.title, meta);
|
||||
}
|
||||
|
||||
|
||||
@ -1,199 +0,0 @@
|
||||
/**
|
||||
* Normal Template
|
||||
*/
|
||||
|
||||
var TOKEN_RE = new RegExp("(\{[\=\:\#\/].+?\})"),
|
||||
COMMAND_RE = new RegExp("^\{[\:\/\=]");
|
||||
|
||||
var xpath = function (path) {
|
||||
if (path === '$last') {
|
||||
return "$last";
|
||||
}
|
||||
|
||||
if (/\||;|\$|~/.test(path)) {
|
||||
throw new Error("Invalid characters in path '" + path + "'");
|
||||
}
|
||||
|
||||
path = path.replace(/\//g, ".").replace(/'|"/, "");
|
||||
|
||||
if (path == ".") {
|
||||
return "d";
|
||||
} else if (/^\./.test(path)) {
|
||||
return "data" + path;
|
||||
} else {
|
||||
return "d." + path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template filters. Add your own to this dictionary.
|
||||
*/
|
||||
exports.filters = {
|
||||
str: function (val) { // used to override default filtering.
|
||||
return val.toString();
|
||||
},
|
||||
strip: function (val) {
|
||||
return val.toString().replace(/<([^>]+)>/g, "");
|
||||
},
|
||||
html: function (val) {
|
||||
return val.toString().replace(/&/g, "&").replace(/>/g, ">").
|
||||
replace(/</g, "<");
|
||||
},
|
||||
attr: function (val) {
|
||||
return val.toString().replace(/&/g, "&").replace(/>/g, ">").
|
||||
replace(/</g, "<").replace(/"/g, """);
|
||||
},
|
||||
uri: encodeURI
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the template source into the template function.
|
||||
*/
|
||||
exports.compile = function (src, options) {
|
||||
// v = curent value, d = cursor, a = reduced array, df = default filter, res = result
|
||||
var code = ['var v,a,d = data,res = [];'],
|
||||
stack = ["data"],
|
||||
nesting = [],
|
||||
tokens = src.split(TOKEN_RE);
|
||||
|
||||
var filters, tag;
|
||||
|
||||
if (options && options.filters) {
|
||||
filters = {};
|
||||
for (var i in exports.filters) filters[i] = exports.filters[i];
|
||||
for (var i in options.filters) filters[i] = options.filters[i];
|
||||
} else {
|
||||
filters = exports.filters;
|
||||
}
|
||||
|
||||
if (filters.defaultfilter) {
|
||||
code.push('var df = filters.defaultfilter;');
|
||||
}
|
||||
|
||||
for (var i = 0; i < tokens.length; i++) {
|
||||
var token = tokens[i];
|
||||
|
||||
if (token == "") continue;
|
||||
|
||||
if (token.match(COMMAND_RE)) {
|
||||
if (token[1] == ":") { // open tag
|
||||
var parts = token.substring(2, token.length-1).split(" "),
|
||||
cmd = parts[0],
|
||||
arg = parts[1],
|
||||
val;
|
||||
|
||||
switch (cmd) {
|
||||
case "if": // checks for undefined and boolean.
|
||||
nesting.push("if");
|
||||
val = xpath(arg);
|
||||
code.push('if (' + val + ') {');
|
||||
continue;
|
||||
|
||||
case "select":
|
||||
case "s":
|
||||
nesting.push("select");
|
||||
val = xpath(arg);
|
||||
code.push('d = ' + val + ';if (d != undefined) {');
|
||||
stack.unshift(val.replace(/^d\./, stack[0] + "."));
|
||||
continue;
|
||||
|
||||
case "reduce":
|
||||
case "r":
|
||||
nesting.push("reduce");
|
||||
val = xpath(arg);
|
||||
var depth = stack.length;
|
||||
code.push('var a' + depth + ' = ' + val + ';if ((a' + depth + ' != undefined) && (a' + depth + '.length > 0)) ');
|
||||
stack.unshift("a" + depth + "[i" + depth + "]");
|
||||
code.push('for (var i' + depth + ' = 0,l' + depth + ' = a' + depth + '.length; i' + depth + ' < l' + depth + '; i' + depth + '++) {$last = (i' + depth + ' == l' + depth + '-1); d = a' + depth + '[i' + depth + '];');
|
||||
continue;
|
||||
|
||||
case "else":
|
||||
case "e":
|
||||
tag = nesting.pop();
|
||||
if (tag) {
|
||||
code.push('} else {');
|
||||
nesting.push(tag);
|
||||
} else {
|
||||
throw new Error("Unbalanced 'else' tag");
|
||||
}
|
||||
continue;
|
||||
|
||||
case "lb": // output left curly bracket '{'
|
||||
code.push('res.push("{");');
|
||||
continue;
|
||||
|
||||
case "rb": // output right curly bracket '}'
|
||||
code.push('res.push("}");');
|
||||
continue;
|
||||
|
||||
case "!": // comment
|
||||
continue;
|
||||
}
|
||||
} else if (token[1] == "/") { // close tag
|
||||
if (token[2] == ":") {
|
||||
var cmd = token.substring(3, token.length-1).split(" ")[0];
|
||||
|
||||
switch (cmd) {
|
||||
case "if":
|
||||
tag = nesting.pop();
|
||||
if (tag == "if") {
|
||||
code.push('};');
|
||||
} else {
|
||||
throw new Error("Unbalanced 'if' close tag" + (tag ? ", expecting '" + tag + "' close tag" : ""));
|
||||
}
|
||||
continue;
|
||||
|
||||
case "select":
|
||||
case "s":
|
||||
tag = nesting.pop();
|
||||
if (tag == "select") {
|
||||
stack.shift();
|
||||
code.push('};d = ' + stack[0] + ';');
|
||||
} else {
|
||||
throw new Error("Unbalanced 'select' close tag" + (tag ? ", expecting '" + tag + "' close tag" : ""));
|
||||
}
|
||||
continue;
|
||||
|
||||
case "reduce":
|
||||
case "r":
|
||||
tag = nesting.pop();
|
||||
if (tag == "reduce") {
|
||||
stack.shift();
|
||||
code.push('}; $last = false; d = ' + stack[0] + ';');
|
||||
} else {
|
||||
throw new Error("Unbalanced 'reduce' close tag" + (tag ? ", expecting '" + tag + "' close tag" : ""));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (token[1] == "=") { // interpolation
|
||||
var parts = token.substring(2, token.length-1).split(" "),
|
||||
pre = "", post = "";
|
||||
for (var j = 0; j < parts.length-1; j++) {
|
||||
pre += "filters." + parts[j] + "("; post += ")";
|
||||
}
|
||||
if (pre == "") {
|
||||
if (filters.defaultfilter) {
|
||||
pre = "df("; post = ")";
|
||||
}
|
||||
}
|
||||
code.push('v = ' + xpath(parts[j]) + ';if (v != undefined) res.push(' + pre + 'v' + post +');');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// plain text
|
||||
code.push('res.push("' + token.replace(/\\/g, "\\\\").replace(/\r/g, "").replace(/\n/g, "\\n").replace(/"/g, '\\"') + '");');
|
||||
}
|
||||
|
||||
tag = nesting.pop();
|
||||
if (tag) {
|
||||
throw new Error("Unbalanced '" + tag + "' tag, is not closed");
|
||||
}
|
||||
|
||||
code.push('return res.join("");');
|
||||
|
||||
var func = new Function("data", "filters", code.join(""));
|
||||
|
||||
return function (data) { return func(data, filters) };
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jsdoc-3",
|
||||
"version": "0.0.0",
|
||||
"revision": "2010-07-28-0024",
|
||||
"name": "jsdoc",
|
||||
"version": "3.0.0alpha1",
|
||||
"revision": "2010-12-27-1223",
|
||||
"description": "An automatic documentation generator for javascript",
|
||||
"keywords": [ "documentation", "javascript" ],
|
||||
"licenses": [
|
||||
@ -16,7 +16,6 @@
|
||||
"url": "http://github.com/micmath/JSDoc"
|
||||
}
|
||||
],
|
||||
"bugs": "http://jsdoc.lighthouseapp.com/",
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Michael Mathews",
|
||||
|
||||
15
plugins/markdown.js
Normal file
15
plugins/markdown.js
Normal file
@ -0,0 +1,15 @@
|
||||
/**
|
||||
@overview Translate doclet descriptions from MarkDown into HTML.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
var markdown = require('evilstreak/markdown');
|
||||
|
||||
app.jsdoc.parser.on('newDoclet', function(e) {
|
||||
if (e.doclet.description) {
|
||||
e.doclet.description = markdown.toHTML(e.doclet.description);
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
||||
@ -4,25 +4,40 @@
|
||||
|
||||
publish = function(docs, opts) {
|
||||
var out = '',
|
||||
template = readFile(BASEDIR + 'templates/default/tmpl/index.html');
|
||||
templates = {
|
||||
index: readFile(BASEDIR + 'templates/default/tmpl/index.html')
|
||||
};
|
||||
|
||||
var summarize = function () {
|
||||
function summarize () {
|
||||
return function(text, render) {
|
||||
text = render(text);
|
||||
/^(.*?(\.|\n|\r|$))/.test(text);
|
||||
return RegExp.$1;
|
||||
var summary = trim(text);
|
||||
|
||||
summary = render(text);
|
||||
summary = summary.replace(/<\/?p>/gi, ''); // text may be HTML
|
||||
|
||||
/^(.*?(\.$|\.\s|\n|\r|$|<br>))/.test(summary);
|
||||
return RegExp.$1? RegExp.$1 : summary;
|
||||
}
|
||||
};
|
||||
|
||||
function trim(text) {
|
||||
return text.replace(/^\s+|\s+$/g, '');
|
||||
}
|
||||
|
||||
out = Mustache.to_html(
|
||||
template,
|
||||
templates.index,
|
||||
{
|
||||
docs: docs,
|
||||
summarize: summarize
|
||||
}
|
||||
);
|
||||
|
||||
print(out);
|
||||
|
||||
if (opts.destination === 'console') {
|
||||
print(out);
|
||||
}
|
||||
else {
|
||||
print('The only -d destination option currently supported is "console"!');
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
@ -12,7 +12,7 @@
|
||||
<ul>
|
||||
{{#docs}}
|
||||
<li class="symbol">
|
||||
<i>{{kind}}</i> {{name}}{{#description}} - {{#summarize}}{{description}}{{/summarize}}{{/description}}
|
||||
<i>{{kind}}</i> {{name}}{{#description}} - {{#summarize}}{{{description}}}{{/summarize}}{{/description}}
|
||||
</li>
|
||||
{{/docs}}
|
||||
</ul>
|
||||
|
||||
@ -3,73 +3,31 @@
|
||||
var src = { parser: require('jsdoc/src/parser')};
|
||||
|
||||
test('There is a src/parser module.', function() {
|
||||
assert.notEqual(typeof src, 'undefined', 'The src/parser module should be defined.');
|
||||
assert.equal(typeof src, 'object', 'The src/parser module should be an object.');
|
||||
});
|
||||
|
||||
test('The src/parser module has a "parse" function.', function() {
|
||||
assert.notEqual(typeof src.parser.parse, 'undefined', 'The src.parser.parse method should be defined.');
|
||||
assert.equal(typeof src.parser.parse, 'function', 'The src.parser.parse method should be a function.');
|
||||
test('The src/parser module has a "Parser" constructor.', function() {
|
||||
assert.equal(typeof src.parser.Parser, 'function', 'The src.parser.Parser member should be a function.');
|
||||
});
|
||||
|
||||
test('The src/parser module has a "parseSource" function.', function() {
|
||||
assert.notEqual(typeof src.parser.parseSource, 'undefined', 'The src.parser.parseSource method should be defined.');
|
||||
assert.equal(typeof src.parser.parseSource, 'function', 'The src.parser.parseSource method should be a function.');
|
||||
test('The src/parser module has a "Parser#parse" function.', function() {
|
||||
assert.equal(typeof src.parser.Parser.prototype.parse, 'function', 'The src.parser.Parser#parse member should be a function.');
|
||||
});
|
||||
|
||||
test('The src/parser module has a "result" function.', function() {
|
||||
assert.notEqual(typeof src.parser.result, 'undefined', 'The src.parser.result method should be defined.');
|
||||
assert.equal(typeof src.parser.result, 'function', 'The src.parser.result method should be a function.');
|
||||
test('The src/parser module has a "results" function.', function() {
|
||||
assert.equal(typeof src.parser.Parser.prototype.results, 'function', 'The src.parser.Parser#results member should be a function.');
|
||||
});
|
||||
|
||||
test('The src/parser module has a "clear" function.', function() {
|
||||
assert.notEqual(typeof src.parser.clear, 'undefined', 'The src.parser.clear method should be defined.');
|
||||
assert.equal(typeof src.parser.clear, 'function', 'The src.parser.clear method should be a function.');
|
||||
});
|
||||
|
||||
test('The src/parser.result function can return the result array.', function() {
|
||||
var docs = src.parser.result;
|
||||
|
||||
assert.notEqual(typeof docs, 'undefined', 'The src.parser.result method should return a result.');
|
||||
assert.equal(docs.length, 0, 'The src.parser.result method should return an array of 0 results if there has been nothing parsed yet.');
|
||||
});
|
||||
|
||||
test('The src/parser.parseSource function can parse js source code containing a jsdoc comment.', function() {
|
||||
var sourceCode = '/** @name foo */';
|
||||
test('The src/parser.Parser#parse function fires jsdocCommentFound events when parsing source code containing a jsdoc comment.', function() {
|
||||
var sourceCode = 'javascript:/** @name bar */',
|
||||
jsdocCounter = 0;
|
||||
|
||||
(new src.parser.Parser())
|
||||
.on('jsdocCommentFound', function(e) {
|
||||
jsdocCounter++;
|
||||
})
|
||||
.parse(sourceCode);
|
||||
|
||||
var docs = src.parser.parseSource(sourceCode, '/bar/foo.js');
|
||||
|
||||
assert.notEqual(typeof docs, 'undefined', 'The src.parser.parseSource method should return a result.');
|
||||
assert.equal(docs.length, 1, 'The src.parser.parseSource method should return an array of 1 result if there is 1 jsdoc comment.');
|
||||
assert.notEqual(typeof docs[0].tags, 'undefined', 'The result array returned by src.parser.parseSource should be a collection of Doclets with Tags.');
|
||||
});
|
||||
|
||||
test('The src/parser.clear function can empty the result array.', function() {
|
||||
src.parser.clear();
|
||||
var docs = src.parser.result;
|
||||
|
||||
assert.equal(docs.length, 0, 'The src.parser.result method should return an array of 0 results if clear was called.');
|
||||
});
|
||||
|
||||
test('The src/parser.parse function can parse js source code containing a doc comment.', function() {
|
||||
var sourceCode = 'javascript:/** @name bar */';
|
||||
|
||||
src.parser.clear();
|
||||
var docs = src.parser.parse([sourceCode]);
|
||||
|
||||
assert.notEqual(typeof docs, 'undefined', 'The src.parser.parse method should return a result.');
|
||||
assert.equal(docs.length, 1, 'The src.parser.parse method should return an array of 1 result if there is 1 jsdoc comment.');
|
||||
assert.notEqual(typeof docs[0].tags, 'undefined', 'The result array returned by src.parser.parse should be a collection of Doclets with Tags.');
|
||||
});
|
||||
|
||||
test('The src/parser.parse function can cope with source code containing no jsdoc comments.', function() {
|
||||
var sourceCode = 'javascript:var blah;';
|
||||
|
||||
src.parser.clear();
|
||||
var docs = src.parser.parse([sourceCode]);
|
||||
|
||||
assert.notEqual(typeof docs, 'undefined', 'The src.parser.parse method should return a result.');
|
||||
assert.equal(docs.length, 0, 'The src.parser.parse method should return an array of 0 Doclets if there are no jsdocc omments.');
|
||||
});
|
||||
|
||||
assert.equal(jsdocCounter, 1, 'The Parser#parse method should fire jsdocCommentFound once if there is 1 jsdoc comment.');
|
||||
});
|
||||
})();
|
||||
Loading…
x
Reference in New Issue
Block a user