diff --git a/jsdoc.js b/jsdoc.js index 553297e1..480b3f88 100644 --- a/jsdoc.js +++ b/jsdoc.js @@ -179,6 +179,10 @@ function main() { if (env.conf.plugins) { plugins.installPlugins(env.conf.plugins, app.jsdoc.parser); } + + if (env.conf.source && env.conf.source.include) { + env.opts._ = (env.opts._ || []).concat(env.conf.source.include); + } // any source file named package.json or README.md is treated special for (i = 0, l = env.opts._.length; i < l; i++ ) { @@ -193,10 +197,6 @@ function main() { } } - if (env.conf.source && env.conf.source.include) { - env.opts._ = (env.opts._ || []).concat(env.conf.source.include); - } - if (env.conf.source && env.opts._.length > 0) { // are there any files to scan and parse? filter = new Filter(env.conf.source); diff --git a/lib/jsdoc/src/parser.js b/lib/jsdoc/src/parser.js index 9e205655..f08821fd 100644 --- a/lib/jsdoc/src/parser.js +++ b/lib/jsdoc/src/parser.js @@ -300,6 +300,20 @@ function getBasename(name) { return name; } +/** @private + * @memberof module:src/parser.Parser + * @param {object} node + * @return {Array.} Start and end lines. + */ +function getRange(node) { + var range = []; + + range[0] = parseInt(String(node.getAbsolutePosition()), 10); + range[1] = range[0] + parseInt(String(node.getLength()), 10); + + return range; +} + /** @private * @memberof module:src/parser.Parser */ @@ -312,17 +326,7 @@ exports.Parser.prototype._makeEvent = function(node, extras) { id: extras.id || 'astnode' + node.hashCode(), comment: extras.comment || String(node.getJsDoc() || '@undocumented'), lineno: extras.lineno || node.left.getLineno(), - range: (function() { - var range = []; - if (extras.range) { - range = extras.range; - } - else { - range[0] = parseInt(String(node.getAbsolutePosition()), 10); - range[1] = range[0] + parseInt(String(node.getLength()), 10); - } - return range; - })(), + range: extras.range || getRange(node), filename: extras.filename || this._currentSourceName, astnode: extras.astnode || node, code: extras.code || aboutNode(node), @@ -388,8 +392,7 @@ exports.Parser.prototype._visitNode = function(node) { comment: commentSrc, lineno: comment.getLineno(), filename: this._currentSourceName, - linelength: comment.getLength(), - absposition: comment.getAbsolutePosition() + range: getRange(comment) }; this.emit('jsdocCommentFound', e, this); @@ -432,9 +435,7 @@ exports.Parser.prototype._visitNode = function(node) { } extras = { - lineno: node.getLineno(), - linelength: node.getLength(), - absposition: node.getAbsolutePosition() + lineno: node.getLineno() }; e = this._makeEvent(node, extras); @@ -442,9 +443,7 @@ exports.Parser.prototype._visitNode = function(node) { } else if (node.type == Token.FUNCTION || node.type == tkn.NAMEDFUNCTIONSTATEMENT) { extras = { - lineno: node.getLineno(), - linelength: node.getLength(), - absposition: node.getAbsolutePosition() + lineno: node.getLineno() }; e = this._makeEvent(node, extras); diff --git a/lib/jsdoc/tag/dictionary/definitions.js b/lib/jsdoc/tag/dictionary/definitions.js index db3ca55f..59a66113 100644 --- a/lib/jsdoc/tag/dictionary/definitions.js +++ b/lib/jsdoc/tag/dictionary/definitions.js @@ -349,7 +349,8 @@ exports.defineTags = function(dictionary) { applyNamespace('event', tag); doclet.fires.push(tag.value); } - }); + }) + .synonym('emits'); dictionary.defineTag('function', { onTagged: function(doclet, tag) { diff --git a/lib/jsdoc/util/templateHelper.js b/lib/jsdoc/util/templateHelper.js index 94867bc4..ef036c1a 100644 --- a/lib/jsdoc/util/templateHelper.js +++ b/lib/jsdoc/util/templateHelper.js @@ -3,7 +3,6 @@ * @module jsdoc/util/templateHelper */ -var crypto = require('crypto'); var dictionary = require('jsdoc/tag/dictionary'); var util = require('util'); @@ -14,7 +13,6 @@ var files = {}; // each container gets its own html file var containers = ['class', 'module', 'external', 'namespace', 'mixin']; -/** @external {jsdoc.tutorial.Tutorial} */ var tutorials; /** Sets tutorials map. @@ -68,18 +66,25 @@ var nsprefix = /^(event|module|external):/; * @return {string} The filename to use for the string. */ var getUniqueFilename = exports.getUniqueFilename = function(str) { - var result; - // allow for namespace prefix - var basename = str.replace(nsprefix, '$1-'); + var basename = str.replace(nsprefix, '$1-') + // and use - instead of ~ to denote 'inner' + .replace(/~/g, '-'); - if ( /[^$a-z0-9._\-]/i.test(basename) ) { - result = crypto.createHash('sha1').update(str).digest('hex').substr(0, 10); - } else { - result = makeFilenameUnique(basename, str); + // if the basename includes characters that we can't use in a filepath, remove everything up to + // and including the last bad character + var regexp = /[^$a-z0-9._\-](?=[$a-z0-9._\-]*$)/i; + var result = regexp.exec(basename); + if (result && result.index) { + basename = basename.substr(result.index + 1); } - return result + exports.fileExtension; + // make sure we don't create hidden files on POSIX systems + basename = basename.replace(/^\./, ''); + // and in case we've now stripped the entire basename (uncommon, but possible): + basename = basename.length ? basename : '_'; + + return makeFilenameUnique(basename, str) + exports.fileExtension; }; // two-way lookup diff --git a/lib/jsdoc/util/vm.js b/lib/jsdoc/util/vm.js index 61279b14..eb7048b1 100644 --- a/lib/jsdoc/util/vm.js +++ b/lib/jsdoc/util/vm.js @@ -58,7 +58,7 @@ var vm = exports.vm = (function() { * @return {object} An object containing VM-specific functions for the requested module. */ exports.getModule = function(modulePath) { - modulePath = [__dirname, vm, modulePath].join('/'); + modulePath = [__dirname, vm, modulePath].join('/').replace(/ /g, '%20'); if (os.platform() === 'win32') { modulePath = pathToUri(modulePath); } diff --git a/test/fixtures/eventfirestag.js b/test/fixtures/eventfirestag.js index 4fe3e746..331eaf8c 100644 --- a/test/fixtures/eventfirestag.js +++ b/test/fixtures/eventfirestag.js @@ -16,3 +16,15 @@ Hurl.prototype.snowball = function () { */ this.emit('snowball', {}); }; + +/** + * Throw a football match. + * + * @emits Hurl#footballMatch + */ +Hurl.prototype.footballMatch = function () { + /** + * @event Hurl#footballMatch + */ + this.emit('footballMatch', {}); +}; diff --git a/test/specs/jsdoc/util/templateHelper.js b/test/specs/jsdoc/util/templateHelper.js index 41ee1c15..54bd9a55 100644 --- a/test/specs/jsdoc/util/templateHelper.js +++ b/test/specs/jsdoc/util/templateHelper.js @@ -140,14 +140,16 @@ describe("jsdoc/util/templateHelper", function() { // disabled because Jasmine appears to execute this code twice, which causes getUniqueFilename // to return an unexpected variation on the name the second time xdescribe("getUniqueFilename", function() { + // TODO: needs more tests for unusual values and things that get special treatment (such as + // inner members) it('should convert a simple string into the string plus the default extension', function() { var filename = helper.getUniqueFilename('BackusNaur'); expect(filename).toEqual('BackusNaur.html'); }); - it('should convert a string with slashes into an alphanumeric hash plus the default extension', function() { + it('should convert a string with slashes into the text following the last slash plus the default extension', function() { var filename = helper.getUniqueFilename('tick/tock'); - expect(filename).toMatch(/^[A-Za-z0-9]+\.html$/); + expect(filename).toMatch(/^tock\.html$/); }); it('should not return the same filename twice', function() { @@ -743,7 +745,7 @@ describe("jsdoc/util/templateHelper", function() { expect(newUrl).toEqual(nestedNamespaceUrl); }); - it('should create a url for a name with invalid characters using a digest.', function() { + it('should create a url for a name with invalid characters.', function() { var mockDoclet = { kind: 'function', longname: 'ns1."!"."*foo"', @@ -752,7 +754,7 @@ describe("jsdoc/util/templateHelper", function() { }, url = helper.createLink(mockDoclet); - expect(url).toEqual('be9d9563a3.html#"*foo"'); + expect(url).toEqual('_.html#"*foo"'); }); }); }); diff --git a/test/specs/tags/eventfirestag.js b/test/specs/tags/eventfirestag.js index 952e9574..55d86f4a 100644 --- a/test/specs/tags/eventfirestag.js +++ b/test/specs/tags/eventfirestag.js @@ -1,23 +1,30 @@ -describe("@event and @fires tags", function() { +/*global describe: true, expect: true, it: true, jasmine: true */ +describe('@event and @fires/@emits tags', function() { var docSet = jasmine.getDocSetFromFile('test/fixtures/eventfirestag.js'), snowballMethod = docSet.getByLongname('Hurl#snowball')[0], - snowballEvent = docSet.getByLongname('Hurl#event:snowball')[0]; + snowballEvent = docSet.getByLongname('Hurl#event:snowball')[0], + footballMatchMethod = docSet.getByLongname('Hurl#footballMatch')[0]; // @event tag it('When a symbol has an @event tag, the doclet is of kind "event".', function() { expect(snowballEvent.kind).toBe('event'); }); - // @fires tag + // @fires/@emits tag it('When a symbol has a @fires tag, the doclet has an array named "fires".', function() { expect(typeof snowballMethod.fires).toBe('object'); }); - it('When a symbol has a fires array, the members have the event namespace.', function() { + it('When a symbol has an @emits tag, the doclet has an array named "fires".', function() { + expect(typeof footballMatchMethod.fires).toBe('object'); + }); + + it('When a symbol has a "fires" array, the members have the "event:" namespace.', function() { expect(snowballMethod.fires[0]).toBe('Hurl#event:snowball'); }); - it('When a symbol has a fires array with a name that already has an event: namespace, it doesn\'t have a secong namespace applied.', function() { + it('When a symbol has a "fires" array with a name that already has an "event:" namespace, ' + + 'it does not have a second namespace applied.', function() { expect(snowballMethod.fires[1]).toBe('Hurl#event:brick'); }); -}); \ No newline at end of file +});