From c89c75fd88adfc49f4a819797c4a48f75c8d0f86 Mon Sep 17 00:00:00 2001 From: Ernst Haagsman Date: Wed, 19 Jun 2013 15:22:21 +0200 Subject: [PATCH 01/40] parseComplete event now also supplies doclets The parseComplete event only supplied plugins a list of files that have been looked at before, now it also supplies the parsed doclets --- lib/jsdoc/src/parser.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/jsdoc/src/parser.js b/lib/jsdoc/src/parser.js index 5188cfe3..64fbc455 100644 --- a/lib/jsdoc/src/parser.js +++ b/lib/jsdoc/src/parser.js @@ -91,7 +91,8 @@ exports.Parser.prototype.parse = function(sourceFiles, encoding) { } this.emit('parseComplete', { - sourcefiles: parsedFiles + sourcefiles: parsedFiles, + doclets: this._resultBuffer }); return this._resultBuffer; From 39bf9adb2bdc949251ef6f076665d6eb402ccd0b Mon Sep 17 00:00:00 2001 From: Louis-Dominique Dubeau Date: Fri, 28 Jun 2013 09:33:05 -0400 Subject: [PATCH 02/40] Allow the presence of inline tags for a @returns tag that does not have a type spec. --- lib/jsdoc/tag/type.js | 2 +- test/fixtures/returnstag.js | 8 ++++++++ test/specs/tags/returnstag.js | 9 ++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/jsdoc/tag/type.js b/lib/jsdoc/tag/type.js index a14df5be..d36b6820 100644 --- a/lib/jsdoc/tag/type.js +++ b/lib/jsdoc/tag/type.js @@ -35,7 +35,7 @@ function unescapeBraces(text) { var count = 0; var position = 0; var expression = ''; - var startIndex = string.indexOf('{'); + var startIndex = string.search(/\{[^@]/); var textStartIndex; if (startIndex !== -1) { diff --git a/test/fixtures/returnstag.js b/test/fixtures/returnstag.js index d3cf4de4..f8282770 100644 --- a/test/fixtures/returnstag.js +++ b/test/fixtures/returnstag.js @@ -9,3 +9,11 @@ function find(targetName) { */ function bind(callback) { } + +// This test exists because there used to be a bug in jsdoc which +// would cause it to fail parsing. +/** +* @return An object to be passed to {@link find}. +*/ +function convert(name) { +} diff --git a/test/specs/tags/returnstag.js b/test/specs/tags/returnstag.js index 2fcf2e59..ac8d890f 100644 --- a/test/specs/tags/returnstag.js +++ b/test/specs/tags/returnstag.js @@ -1,7 +1,8 @@ describe("@returns tag", function() { var docSet = jasmine.getDocSetFromFile('test/fixtures/returnstag.js'), find = docSet.getByLongname('find')[0], - bind = docSet.getByLongname('bind')[0]; + bind = docSet.getByLongname('bind')[0], + convert = docSet.getByLongname('convert')[0]; it('When a symbol has an @returns tag with a type and description, the doclet has a returns array that includes that return.', function() { expect(typeof find.returns).toBe('object'); @@ -15,4 +16,10 @@ describe("@returns tag", function() { expect(bind.returns.length).toBe(1); expect(bind.returns[0].description).toBe('The binding id.'); }); + + it('When a symbol has an @returns tag wihtout a type but with an inline tag, the doclet does not confuse the inline tag for a type.', function() { + expect(typeof convert.returns).toBe('object'); + expect(convert.returns.length).toBe(1); + expect(convert.returns[0].description).toBe('An object to be passed to {@link find}.'); + }); }); From 8ca34b69adb215c1ae2fc110413be27123c5d483 Mon Sep 17 00:00:00 2001 From: Louis-Dominique Dubeau Date: Fri, 28 Jun 2013 09:37:01 -0400 Subject: [PATCH 03/40] Typo fix. --- test/specs/tags/returnstag.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/specs/tags/returnstag.js b/test/specs/tags/returnstag.js index ac8d890f..6b23fd5b 100644 --- a/test/specs/tags/returnstag.js +++ b/test/specs/tags/returnstag.js @@ -17,7 +17,7 @@ describe("@returns tag", function() { expect(bind.returns[0].description).toBe('The binding id.'); }); - it('When a symbol has an @returns tag wihtout a type but with an inline tag, the doclet does not confuse the inline tag for a type.', function() { + it('When a symbol has an @returns tag without a type but with an inline tag, the doclet does not confuse the inline tag for a type.', function() { expect(typeof convert.returns).toBe('object'); expect(convert.returns.length).toBe(1); expect(convert.returns[0].description).toBe('An object to be passed to {@link find}.'); From c236bf955f3304d2f18e583e4d9c9a3a9f61e044 Mon Sep 17 00:00:00 2001 From: cgwyllie Date: Thu, 1 Aug 2013 10:05:02 +0100 Subject: [PATCH 04/40] Update README.md Changed clone URL for repo to HTTPS read-only so it doesn't require keypair access. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c4a05467..fb0bff79 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Installation Use git to clone the [official JSDoc repository](https://github.com/jsdoc3/jsdoc): - git clone git@github.com:jsdoc3/jsdoc.git + git clone https://github.com/jsdoc3/jsdoc.git Alternatively, you can download a .zip file for the [latest development version](https://github.com/jsdoc3/jsdoc/archive/master.zip) From 6c4f59b1071cc2cbae91496cbdadb2d54bd89fbf Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sun, 4 Aug 2013 11:52:37 -0700 Subject: [PATCH 05/40] minor code cleanup --- lib/jsdoc/util/templateHelper.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/jsdoc/util/templateHelper.js b/lib/jsdoc/util/templateHelper.js index 739f2cc3..eff586db 100644 --- a/lib/jsdoc/util/templateHelper.js +++ b/lib/jsdoc/util/templateHelper.js @@ -464,11 +464,7 @@ exports.getMembers = function(data) { // functions that are also modules (as in "module.exports = function() {};") are not globals members.globals = members.globals.filter(function(doclet) { - if ( isModuleFunction(doclet) ) { - return false; - } - - return true; + return !isModuleFunction(doclet); }); return members; From d27619be179f8470bb1382ed2cce2ddc6ce200e0 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sun, 4 Aug 2013 12:14:00 -0700 Subject: [PATCH 06/40] improve debug stack traces by disabling optimization --- jsdoc | 2 +- jsdoc.cmd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jsdoc b/jsdoc index 5e6db50f..6ec0f5a5 100755 --- a/jsdoc +++ b/jsdoc @@ -21,7 +21,7 @@ ENCODEDBASEPATH=`echo "$BASEPATH" | sed -e 's/ /%20/g'` if test "$1" = "--debug" then echo "Running Debug" - CMD="org.mozilla.javascript.tools.debugger.Main -debug" + CMD="org.mozilla.javascript.tools.debugger.Main -debug -opt -1" # strip --debug argument shift else diff --git a/jsdoc.cmd b/jsdoc.cmd index a2ef8f11..9bd2a562 100644 --- a/jsdoc.cmd +++ b/jsdoc.cmd @@ -20,7 +20,7 @@ IF NOT "%_URLPATH%"=="%_URLPATH: =%" GOTO ESCAPE_SPACE IF [%1]==[--debug] ( ECHO Running Debug - SET CMD=org.mozilla.javascript.tools.debugger.Main -debug + SET CMD=org.mozilla.javascript.tools.debugger.Main -debug -opt -1 REM `SHIFT` doesn't affect %* :COLLECT_ARGS From 01ce3042d190c64547c5f3689aa9d79290f14fb6 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sun, 4 Aug 2013 18:07:27 -0700 Subject: [PATCH 07/40] prevent crashes in lenient mode when a returns tag has no value (#451) --- lib/jsdoc/util/templateHelper.js | 2 +- templates/default/publish.js | 5 ++++- templates/default/tmpl/returns.tmpl | 2 +- test/specs/jsdoc/util/templateHelper.js | 17 +++++++++++++++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/jsdoc/util/templateHelper.js b/lib/jsdoc/util/templateHelper.js index eff586db..adfbd72e 100644 --- a/lib/jsdoc/util/templateHelper.js +++ b/lib/jsdoc/util/templateHelper.js @@ -570,7 +570,7 @@ exports.getSignatureReturns = function(d, cssClass) { if (d.returns) { d.returns.forEach(function(r) { - if (r.type && r.type.names) { + if (r && r.type && r.type.names) { if (!returnTypes.length) { returnTypes = r.type.names; } diff --git a/templates/default/publish.js b/templates/default/publish.js index b79200a9..cca01ba8 100644 --- a/templates/default/publish.js +++ b/templates/default/publish.js @@ -66,7 +66,10 @@ function addSignatureParams(f) { function addSignatureReturns(f) { var returnTypes = helper.getSignatureReturns(f); - f.signature = ''+(f.signature || '') + '' + ''+(returnTypes.length? ' → {'+returnTypes.join('|')+'}' : '')+''; + f.signature = '' + (f.signature || '') + '' + + '' + + (returnTypes && returnTypes.length ? ' → {' + returnTypes.join('|') + '}' : '') + + ''; } function addSignatureTypes(f) { diff --git a/templates/default/tmpl/returns.tmpl b/templates/default/tmpl/returns.tmpl index 32e059ed..35d1ec15 100644 --- a/templates/default/tmpl/returns.tmpl +++ b/templates/default/tmpl/returns.tmpl @@ -1,5 +1,5 @@
diff --git a/test/specs/jsdoc/util/templateHelper.js b/test/specs/jsdoc/util/templateHelper.js index 9b944248..d3c87729 100644 --- a/test/specs/jsdoc/util/templateHelper.js +++ b/test/specs/jsdoc/util/templateHelper.js @@ -739,6 +739,23 @@ describe("jsdoc/util/templateHelper", function() { delete helper.longnameToUrl.MyClass; }); + + it("doesn't throw an error in lenient mode if a 'returns' item has no value", function() { + function getReturns() { + return helper.getSignatureReturns(doc); + } + + var doc; + var lenient = !!env.opts.lenient; + + env.opts.lenient = true; + spyOn(console, 'log'); + doc = new doclet.Doclet('/** @function myFunction\n@returns */', {}); + + expect(getReturns).not.toThrow(); + + env.opts.lenient = lenient; + }); }); describe("getAncestorLinks", function() { From 47fe83de3dbfc5af3c85159c32c81dd55c61ee20 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sun, 4 Aug 2013 23:21:01 -0700 Subject: [PATCH 08/40] prevent crashes in lenient mode when a param tag has an invalid type expression (#451) --- lib/jsdoc/src/handlers.js | 2 +- test/fixtures/paramtaginvalidtype.js | 9 +++++++++ test/specs/tags/paramtag.js | 25 +++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/paramtaginvalidtype.js diff --git a/lib/jsdoc/src/handlers.js b/lib/jsdoc/src/handlers.js index fef4bf13..83d784c6 100644 --- a/lib/jsdoc/src/handlers.js +++ b/lib/jsdoc/src/handlers.js @@ -17,7 +17,7 @@ function getNewDoclet(comment, e) { err = new Error( util.format('cannot create a doclet for the comment "%s": %s', comment.replace(/[\r\n]/g, ''), error.message) ); require('jsdoc/util/error').handle(err); - return new jsdoc.doclet.Doclet('', {}); + return new jsdoc.doclet.Doclet('', e); } } diff --git a/test/fixtures/paramtaginvalidtype.js b/test/fixtures/paramtaginvalidtype.js new file mode 100644 index 00000000..5e79d4e8 --- /dev/null +++ b/test/fixtures/paramtaginvalidtype.js @@ -0,0 +1,9 @@ +/** + * @constructor + */ +var Test = function () {}; + +/** + * @param {string, number} a + */ +Test.prototype.test = function (a) {}; diff --git a/test/specs/tags/paramtag.js b/test/specs/tags/paramtag.js index 7145c83f..e4dd33ed 100644 --- a/test/specs/tags/paramtag.js +++ b/test/specs/tags/paramtag.js @@ -1,3 +1,4 @@ +/*global describe: true, env: true, expect: true, it: true, jasmine: true, spyOn: true */ describe("@param tag", function() { var docSet = jasmine.getDocSetFromFile('test/fixtures/paramtag.js'), find = docSet.getByLongname('find')[0], @@ -77,4 +78,28 @@ describe("@param tag", function() { expect(commit.params[0].name).toBe('atomic'); }); + it('When a symbol has a @param tag with an invalid type expression, the doclet is generated in lenient mode, and the JSDoc comment is ignored.', function() { + var badDocSet; + var test; + var lenient = !!env.opts.lenient; + + env.opts.lenient = true; + spyOn(console, 'log'); + + badDocSet = jasmine.getDocSetFromFile('test/fixtures/paramtaginvalidtype.js'); + test = badDocSet.getByLongname('Test#test')[0]; + + expect(test).toBeDefined(); + expect(typeof test).toBe('object'); + + expect(test.meta).toBeDefined(); + expect(typeof test.meta).toBe('object'); + + expect(test.meta.filename).toBeDefined(); + expect(test.meta.filename).toBe('[[string0]]'); + + expect(test.description).not.toBeDefined(); + + env.opts.lenient = lenient; + }); }); From a2f947bd34bce65786a00f6f851241f9744efcee Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sun, 4 Aug 2013 23:34:21 -0700 Subject: [PATCH 09/40] change 'virtual' to 'abstract' in default template output (#432) --- templates/default/publish.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/templates/default/publish.js b/templates/default/publish.js index cca01ba8..7f1a6d2b 100644 --- a/templates/default/publish.js +++ b/templates/default/publish.js @@ -81,7 +81,9 @@ function addSignatureTypes(f) { function addAttribs(f) { var attribs = helper.getAttribs(f); - f.attribs = ''+htmlsafe(attribs.length? '<'+attribs.join(', ')+'> ' : '')+''; + f.attribs = '' + htmlsafe(attribs.length ? + // we want the template output to say 'abstract', not 'virtual' + '<' + attribs.join(', ').replace('virtual', 'abstract') + '> ' : '') + ''; } function shortenPaths(files, commonPrefix) { From 912f548343bc5d68ab96b1714f5c2274fd6f53f1 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sun, 4 Aug 2013 23:46:21 -0700 Subject: [PATCH 10/40] make the default template generate pretty-printed source files by default (#454) --- templates/default/publish.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/default/publish.js b/templates/default/publish.js index 7f1a6d2b..092514f4 100644 --- a/templates/default/publish.js +++ b/templates/default/publish.js @@ -483,9 +483,9 @@ exports.publish = function(taffyData, opts, tutorials) { attachModuleSymbols( find({ kind: ['class', 'function'], longname: {left: 'module:'} }), members.modules ); - // only output pretty-printed source files if requested; do this before generating any other - // pages, so the other pages can link to the source files - if (conf['default'].outputSourceFiles) { + // output pretty-printed source files by default; do this before generating any other pages, so + // that the other pages can link to the source files + if (!conf['default'] || conf['default'].outputSourceFiles !== false) { generateSourceFiles(sourceFiles); } From 770f5ebb435b82aa04f123a7585026e68dbdd539 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Mon, 5 Aug 2013 21:26:43 -0700 Subject: [PATCH 11/40] generate links correctly when text has extra [bracketed] strings (#470) --- lib/jsdoc/util/templateHelper.js | 15 ++++++++++----- test/specs/jsdoc/util/templateHelper.js | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/jsdoc/util/templateHelper.js b/lib/jsdoc/util/templateHelper.js index adfbd72e..35a47fce 100644 --- a/lib/jsdoc/util/templateHelper.js +++ b/lib/jsdoc/util/templateHelper.js @@ -342,13 +342,18 @@ exports.resolveLinks = function(str) { function extractLeadingText(string, completeTag) { var tagIndex = string.indexOf(completeTag); var leadingText = null; - var leadingTextInfo = /\[(.+?)\]/.exec(string); + var leadingTextRegExp = /\[(.+?)\]/g; + var leadingTextInfo = leadingTextRegExp.exec(string); // did we find leading text, and if so, does it immediately precede the tag? - if ( leadingTextInfo && leadingTextInfo.index && - (leadingTextInfo.index + leadingTextInfo[0].length === tagIndex) ) { - string = string.replace(leadingTextInfo[0], ''); - leadingText = leadingTextInfo[1]; + while (leadingTextInfo && leadingTextInfo.length) { + if (leadingTextInfo.index + leadingTextInfo[0].length === tagIndex) { + string = string.replace(leadingTextInfo[0], ''); + leadingText = leadingTextInfo[1]; + break; + } + + leadingTextInfo = leadingTextRegExp.exec(string); } return { diff --git a/test/specs/jsdoc/util/templateHelper.js b/test/specs/jsdoc/util/templateHelper.js index d3c87729..ccefe95b 100644 --- a/test/specs/jsdoc/util/templateHelper.js +++ b/test/specs/jsdoc/util/templateHelper.js @@ -1113,6 +1113,20 @@ describe("jsdoc/util/templateHelper", function() { expect(output).toBe('This is a hello there.'); }); + it('should translate [dummy text] and [hello there]{@link test} into an HTML link with the custom content.', function() { + var input = 'This is [dummy text] and [hello there]{@link test}.', + output = helper.resolveLinks(input); + + expect(output).toBe('This is [dummy text] and hello there.'); + }); + + it('should translate [dummy text] and [more] and [hello there]{@link test} into an HTML link with the custom content.', function() { + var input = 'This is [dummy text] and [more] and [hello there]{@link test}.', + output = helper.resolveLinks(input); + + expect(output).toBe('This is [dummy text] and [more] and hello there.'); + }); + it('should ignore [hello there].', function() { var input = 'This is a [hello there].', output = helper.resolveLinks(input); From 7459bbbd5247d32d113583a1ee95345388b958d2 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Tue, 6 Aug 2013 07:37:48 -0700 Subject: [PATCH 12/40] bump revision --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 17546150..d2550db0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jsdoc", "version": "3.2.0-dev", - "revision": "1366409063892", + "revision": "1375799851600", "description": "An API documentation generator for JavaScript.", "keywords": [ "documentation", "javascript" ], "licenses": [ From b4029d0559810a7c65b89979c94907e082beeeb7 Mon Sep 17 00:00:00 2001 From: Kienz Date: Mon, 12 Aug 2013 12:41:33 +0200 Subject: [PATCH 13/40] Fix for jump to source file line --- templates/default/static/styles/jsdoc-default.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/templates/default/static/styles/jsdoc-default.css b/templates/default/static/styles/jsdoc-default.css index cbcff70f..a0c157be 100644 --- a/templates/default/static/styles/jsdoc-default.css +++ b/templates/default/static/styles/jsdoc-default.css @@ -242,6 +242,11 @@ h6 border-left: 3px #ddd solid; } +.prettyprint code span +{ + display: inline-block; +} + .params, .props { border-spacing: 0; From c8167b8d7685b164849265564a399f7f7a6218eb Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Thu, 15 Aug 2013 07:44:30 -0700 Subject: [PATCH 14/40] set Rhino shell's JS version to 180 (#477) fixes a crash with JS 1.8 keywords (such as `let`) --- rhino/rhino-shim.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rhino/rhino-shim.js b/rhino/rhino-shim.js index 16c126f6..1ffb1e3b 100644 --- a/rhino/rhino-shim.js +++ b/rhino/rhino-shim.js @@ -4,6 +4,9 @@ * to get JSDoc to run. */ +// Set the JS version that the Rhino interpreter will use. +version(180); + /** * Emulate DOM timeout/interval functions. * @see https://developer.mozilla.org/en-US/docs/DOM/window#Methods From abeb539a33b5b6ec48b478a8a9e9ffb38eb505ec Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sat, 17 Aug 2013 00:24:36 -0700 Subject: [PATCH 15/40] unit tests for `let` keyword (#477) --- test/fixtures/letkeyword.js | 17 ++++++++++++++++ test/specs/documentation/letkeyword.js | 28 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 test/fixtures/letkeyword.js create mode 100644 test/specs/documentation/letkeyword.js diff --git a/test/fixtures/letkeyword.js b/test/fixtures/letkeyword.js new file mode 100644 index 00000000..3cbf88fd --- /dev/null +++ b/test/fixtures/letkeyword.js @@ -0,0 +1,17 @@ +/*global define: true */ +define( [], function() { + "use strict"; + + /** + * My example module. + * @exports exampleModule + */ + let myModule = { + /** + * My example method. + */ + exampleMethod: function() {} + }; + + return myModule; +} ); diff --git a/test/specs/documentation/letkeyword.js b/test/specs/documentation/letkeyword.js new file mode 100644 index 00000000..43d12063 --- /dev/null +++ b/test/specs/documentation/letkeyword.js @@ -0,0 +1,28 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ +describe('let keyword', function() { + var docSet; + var exampleModule; + var exampleMethod; + + function getDocSet() { + docSet = jasmine.getDocSetFromFile('test/fixtures/letkeyword.js'); + exampleModule = docSet.getByLongname('module:exampleModule'); + exampleMethod = docSet.getByLongname('module:exampleModule.exampleMethod'); + } + + it('should be able to compile JS files that contain the "let" keyword', function() { + expect(getDocSet).not.toThrow(); + }); + + it('should correctly recognize a module defined with the "let" keyword', function() { + expect(exampleModule).toBeDefined(); + expect( Array.isArray(exampleModule) ).toBe(true); + expect(exampleModule.length).toBe(1); + }); + + it('should correctly recognize members of a module defined with the "let" keyword', function() { + expect(exampleMethod).toBeDefined(); + expect( Array.isArray(exampleMethod) ).toBe(true); + expect(exampleMethod.length).toBe(1); + }); +}); From f7b6bd6988e4aa00a5fd160947a9035c42b3a520 Mon Sep 17 00:00:00 2001 From: Kienz Date: Sun, 18 Aug 2013 10:33:53 +0200 Subject: [PATCH 16/40] Add class line to span-tags for line-numbers Update css --- templates/default/static/scripts/linenumber.js | 2 +- templates/default/static/styles/jsdoc-default.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/default/static/scripts/linenumber.js b/templates/default/static/scripts/linenumber.js index a0c570d5..613865d0 100644 --- a/templates/default/static/scripts/linenumber.js +++ b/templates/default/static/scripts/linenumber.js @@ -9,7 +9,7 @@ numbered = source.innerHTML.split('\n'); numbered = numbered.map(function(item) { counter++; - return '' + item; + return '' + item; }); source.innerHTML = numbered.join('\n'); diff --git a/templates/default/static/styles/jsdoc-default.css b/templates/default/static/styles/jsdoc-default.css index a0c157be..7afd6850 100644 --- a/templates/default/static/styles/jsdoc-default.css +++ b/templates/default/static/styles/jsdoc-default.css @@ -242,7 +242,7 @@ h6 border-left: 3px #ddd solid; } -.prettyprint code span +.prettyprint code span.line { display: inline-block; } From c48bbc006a4b12cbc25b9a2077dae137804c52fc Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Mon, 19 Aug 2013 08:15:43 -0700 Subject: [PATCH 17/40] long-overdue update to the template README --- templates/README.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/templates/README.md b/templates/README.md index fbdd7dbf..48c04441 100644 --- a/templates/README.md +++ b/templates/README.md @@ -1,20 +1,27 @@ -To create or use your own template, create a folder, and give it the name of your template, for example "mycooltemplate". Within this folder create a file named "publish.js". That file must define a global method named "publish". For example: +To create or use your own template: + +1. Create a folder with the same name as your template (for example, `mycooltemplate`). +2. Within the template folder, create a file named `publish.js`. This file must be a CommonJS module that exports a method named `publish`. + +For example: ````javascript +/** @module publish */ + /** - * Turn the data about your docs into file output. - * @global + * Generate documentation output. + * * @param {TAFFY} data - A TaffyDB collection representing * all the symbols documented in your code. * @param {object} opts - An object with options information. */ -function publish(data, opts) { +exports.publish = function(data, opts) { // do stuff here to generate your output files -} +}; ```` -To invoke JSDoc 3 with your own template, use the `-t` command line option, giving it the path to your template folder. +To invoke JSDoc 3 with your own template, use the `-t` command line option, and specify the path to your template folder: ```` ./jsdoc mycode.js -t /path/to/mycooltemplate -```` \ No newline at end of file +```` From da524aa5586bc502e39974c3d9b13bce2f35f981 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Mon, 9 Sep 2013 22:24:44 -0700 Subject: [PATCH 18/40] allow `requires` tag text to be an inline link tag (#486) --- lib/jsdoc/tag/dictionary/definitions.js | 18 ++++++++--- lib/jsdoc/tag/inline.js | 43 +++++++++++++++++++++++-- test/fixtures/requirestag.js | 18 ++++++++--- test/specs/jsdoc/tag/inline.js | 42 ++++++++++++++++++++++++ test/specs/tags/requirestag.js | 24 ++++++++++---- 5 files changed, 127 insertions(+), 18 deletions(-) diff --git a/lib/jsdoc/tag/dictionary/definitions.js b/lib/jsdoc/tag/dictionary/definitions.js index 0bd66713..c0f370aa 100644 --- a/lib/jsdoc/tag/dictionary/definitions.js +++ b/lib/jsdoc/tag/dictionary/definitions.js @@ -539,12 +539,22 @@ exports.defineTags = function(dictionary) { dictionary.defineTag('requires', { mustHaveValue: true, onTagged: function(doclet, tag) { - var modName = firstWordOf(tag.value); - if (modName.indexOf('module:') !== 0) { - modName = 'module:'+modName; + var requiresName; + + // inline link tags are passed through as-is so that `@requires {@link foo}` works + if ( require('jsdoc/tag/inline').isInlineTag(tag.value, 'link\\S*') ) { + requiresName = tag.value; } + // otherwise, assume it's a module + else { + requiresName = firstWordOf(tag.value); + if (requiresName.indexOf('module:') !== 0) { + requiresName = 'module:' + requiresName; + } + } + if (!doclet.requires) { doclet.requires = []; } - doclet.requires.push(modName); + doclet.requires.push(requiresName); } }); diff --git a/lib/jsdoc/tag/inline.js b/lib/jsdoc/tag/inline.js index 8be46e26..952b5af9 100644 --- a/lib/jsdoc/tag/inline.js +++ b/lib/jsdoc/tag/inline.js @@ -35,6 +35,45 @@ * @return {string} An updated version of the complete string. */ +/** + * Create a regexp that matches a specific inline tag, or all inline tags. + * + * @private + * @memberof module:jsdoc/tag/inline + * @param {?string} tagName - The inline tag that the regexp will match. May contain regexp + * characters. If omitted, matches any string. + * @param {?string} prefix - A prefix for the regexp. Defaults to an empty string. + * @param {?string} suffix - A suffix for the regexp. Defaults to an empty string. + * @returns {RegExp} A regular expression that matches the requested inline tag. + */ +function regExpFactory(tagName, prefix, suffix) { + var tagMatcher = tagName || '\\S+'; + + prefix = prefix || ''; + suffix = suffix || ''; + + return new RegExp(prefix + '\\{@' + tagMatcher + '\\s+((?:.|\n)+?)\\}' + suffix, 'gi'); +} + +/** + * Check whether a string is an inline tag. You can check for a specific inline tag or for any valid + * inline tag. + * + * @param {string} string - The string to check. + * @param {?string} tagName - The inline tag to match. May contain regexp characters. If this + * parameter is omitted, this method returns `true` for any valid inline tag. + * @returns {boolean} Set to `true` if the string is a valid inline tag or `false` in all other + * cases. + */ +exports.isInlineTag = function(string, tagName) { + try { + return regExpFactory(tagName, '^', '$').test(string); + } + catch(e) { + return false; + } +}; + /** * Replace all instances of multiple inline tags with other text. * @@ -61,7 +100,7 @@ exports.replaceInlineTags = function(string, replacers) { string = string || ''; Object.keys(replacers).forEach(function(replacer) { - var tagRegExp = new RegExp('\\{@' + replacer + '\\s+((?:.|\n)+?)\\}', 'gi'); + var tagRegExp = regExpFactory(replacer); var matches; // call the replacer once for each match while ( (matches = tagRegExp.exec(string)) !== null ) { @@ -69,7 +108,7 @@ exports.replaceInlineTags = function(string, replacers) { } }); - return { + return { tags: tagInfo, newString: string.trim() }; diff --git a/test/fixtures/requirestag.js b/test/fixtures/requirestag.js index 3fe27229..49b626fe 100644 --- a/test/fixtures/requirestag.js +++ b/test/fixtures/requirestag.js @@ -1,12 +1,20 @@ /** -* @requires module:foo/helper -*/ + * @requires module:foo/helper + */ function foo() { } /** -* @requires foo -* @requires Pez#blat this text is ignored -*/ + * @requires foo + * @requires Pez#blat this text is ignored + */ function bar() { } + +/** + * @requires {@link module:zest} + * @requires {@linkplain module:zing} + * @requires {@linkstupid module:pizzazz} + */ +function baz() { +} diff --git a/test/specs/jsdoc/tag/inline.js b/test/specs/jsdoc/tag/inline.js index de206a76..a89fb47d 100644 --- a/test/specs/jsdoc/tag/inline.js +++ b/test/specs/jsdoc/tag/inline.js @@ -12,6 +12,11 @@ describe('jsdoc/tag/inline', function() { expect(typeof jsdoc.tag.inline).toBe('object'); }); + it('should export an isInlineTag function', function() { + expect(jsdoc.tag.inline.isInlineTag).toBeDefined(); + expect(typeof jsdoc.tag.inline.isInlineTag).toBe('function'); + }); + it('should export a replaceInlineTag function', function() { expect(jsdoc.tag.inline.replaceInlineTag).toBeDefined(); expect(typeof jsdoc.tag.inline.replaceInlineTag).toBe('function'); @@ -22,6 +27,43 @@ describe('jsdoc/tag/inline', function() { expect(typeof jsdoc.tag.inline.replaceInlineTag).toBe('function'); }); + describe('isInlineTag', function() { + var isInlineTag = jsdoc.tag.inline.isInlineTag; + + it('should correctly identify an inline tag', function() { + expect( isInlineTag('{@mytag hooray}', 'mytag') ).toBe(true); + }); + + it('should correctly identify a non-inline tag', function() { + expect( isInlineTag('mytag hooray', 'mytag') ).toBe(false); + }); + + it('should report that a string containing an inline tag is not an inline tag', function() { + expect( isInlineTag('this is {@mytag hooray}', 'mytag') ).toBe(false); + }); + + it('should default to allowing any inline tag', function() { + expect( isInlineTag('{@anyoldtag will do}') ).toBe(true); + }); + + it('should still identify non-inline tags when a tag name is not provided', function() { + expect( isInlineTag('mytag hooray') ).toBe(false); + }); + + it('should allow regexp characters in the tag name', function() { + expect( isInlineTag('{@mytags hooray}', 'mytag\\S') ).toBe(true); + }); + + it('should return false (rather than throwing) with invalid input', function() { + function badInput() { + return isInlineTag({}); + } + + expect(badInput).not.toThrow(); + expect( badInput() ).toBe(false); + }); + }); + describe('replaceInlineTag', function() { it('should throw if the tag is matched and the replacer is invalid', function() { function badReplacerUndefined() { diff --git a/test/specs/tags/requirestag.js b/test/specs/tags/requirestag.js index 40eae164..f6bcf7ac 100644 --- a/test/specs/tags/requirestag.js +++ b/test/specs/tags/requirestag.js @@ -1,14 +1,24 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ describe("@requires tag", function() { - var docSet = jasmine.getDocSetFromFile('test/fixtures/requirestag.js'), - foo = docSet.getByLongname('foo')[0], - bar = docSet.getByLongname('bar')[0]; + var docSet = jasmine.getDocSetFromFile('test/fixtures/requirestag.js'); + var foo = docSet.getByLongname('foo')[0]; + var bar = docSet.getByLongname('bar')[0]; + var baz = docSet.getByLongname('baz')[0]; - it('When a symbol has an @requires tag, the doclet has a requires property that includes that value, with the "module:" namespace added.', function() { - expect(typeof foo.requires).toBe('object'); + it('When a symbol has a @requires tag, the doclet has a requires property that includes that value, with the "module:" namespace added.', function() { + expect( Array.isArray(foo.requires) ).toBe(true); expect(foo.requires[0]).toBe('module:foo/helper'); - expect(typeof bar.requires).toBe('object'); + expect( Array.isArray(bar.requires) ).toBe(true); expect(bar.requires[0]).toBe('module:foo'); expect(bar.requires[1]).toBe('module:Pez#blat'); }); -}); \ No newline at end of file + + it('When a symbol has a @requires tag whose value is an inline {@link} tag, the doclet has a requires property that includes that tag without modification.', function() { + expect( Array.isArray(baz.requires) ).toBe(true); + expect(baz.requires[0]).toBe('{@link module:zest}'); + expect(baz.requires[1]).toBe('{@linkplain module:zing}'); + // by design, we don't validate the tag name, as long as it starts with @link + expect(baz.requires[2]).toBe('{@linkstupid module:pizzazz}'); + }); +}); From 30011780c7f505dc2d42ffd33c3fa3fb91b365aa Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Tue, 10 Sep 2013 02:48:52 -0700 Subject: [PATCH 19/40] cleanup --- lib/jsdoc/tag/inline.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/jsdoc/tag/inline.js b/lib/jsdoc/tag/inline.js index 952b5af9..c25ab4d1 100644 --- a/lib/jsdoc/tag/inline.js +++ b/lib/jsdoc/tag/inline.js @@ -47,12 +47,11 @@ * @returns {RegExp} A regular expression that matches the requested inline tag. */ function regExpFactory(tagName, prefix, suffix) { - var tagMatcher = tagName || '\\S+'; - + tagName = tagName || '\\S+'; prefix = prefix || ''; suffix = suffix || ''; - return new RegExp(prefix + '\\{@' + tagMatcher + '\\s+((?:.|\n)+?)\\}' + suffix, 'gi'); + return new RegExp(prefix + '\\{@' + tagName + '\\s+((?:.|\n)+?)\\}' + suffix, 'gi'); } /** From 7d6fefe1c276960466b3b865126d777a58f526cd Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Tue, 10 Sep 2013 03:04:05 -0700 Subject: [PATCH 20/40] update excludePattern to match conf.json.EXAMPLE (#487) --- lib/jsdoc/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jsdoc/config.js b/lib/jsdoc/config.js index 9b6a52c4..01edc2c2 100644 --- a/lib/jsdoc/config.js +++ b/lib/jsdoc/config.js @@ -33,7 +33,7 @@ const defaults = { }, "source": { "includePattern": ".+\\.js(doc)?$", - "excludePattern": "(^|\\/)_" + "excludePattern": "(^|\\/|\\\\)_" }, "plugins": [] }; From 700e5d9ea9f426e0a29b4e88cfaf030543fbde6b Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sat, 14 Sep 2013 08:33:54 -0700 Subject: [PATCH 21/40] fix `type` tag for type expressions that span multiple lines (#427) --- lib/jsdoc/tag/dictionary/definitions.js | 10 +++++-- test/fixtures/typetagwithnewline.js | 14 ++++++++++ .../specs/documentation/typetagwithnewline.js | 28 +++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/typetagwithnewline.js create mode 100644 test/specs/documentation/typetagwithnewline.js diff --git a/lib/jsdoc/tag/dictionary/definitions.js b/lib/jsdoc/tag/dictionary/definitions.js index c0f370aa..cb96cbf7 100644 --- a/lib/jsdoc/tag/dictionary/definitions.js +++ b/lib/jsdoc/tag/dictionary/definitions.js @@ -624,17 +624,21 @@ exports.defineTags = function(dictionary) { mustHaveValue: 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 ( ! /^\{.+\}$/.test(text) ) { - text = '{ '+text+' }'; + if ( !/^\{[\s\S]+\}$/.test(text) ) { + text = '{' + text + '}'; } return text; }, onTagged: function(doclet, tag) { if (tag.value && tag.value.type) { doclet.type = tag.value.type; + + // for backwards compatibility, we allow @type for functions to imply return type if (doclet.kind === 'function') { - doclet.addTag('returns', tag.text); // for backwards compatibility we allow @type for functions to imply return type + doclet.addTag('returns', tag.text); } } } diff --git a/test/fixtures/typetagwithnewline.js b/test/fixtures/typetagwithnewline.js new file mode 100644 index 00000000..88a12f18 --- /dev/null +++ b/test/fixtures/typetagwithnewline.js @@ -0,0 +1,14 @@ +/** @class Matryoshka */ +function Matryoshka() {} + +/** + * @type {(!Array.| + * !Array.>)} + */ +Matryoshka.mini; + +/** + * @type (!Array.|!Array.>| + * !Array.>>) + */ +Matryoshka.mega; diff --git a/test/specs/documentation/typetagwithnewline.js b/test/specs/documentation/typetagwithnewline.js new file mode 100644 index 00000000..f1198e97 --- /dev/null +++ b/test/specs/documentation/typetagwithnewline.js @@ -0,0 +1,28 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ + +describe('@type tag containing a newline character', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/typetagwithnewline.js'); + var mini = docSet.getByLongname('Matryoshka.mini')[0]; + var mega = docSet.getByLongname('Matryoshka.mega')[0]; + + it('When the type expression for a @type tag contains a newline character and is not ' + + 'enclosed in braces, the type expression is parsed correctly.', function() { + expect(mini).toBeDefined(); + expect(mini.type).toBeDefined(); + expect(mini.type.names).toBeDefined(); + expect(mini.type.names.length).toBe(2); + expect(mini.type.names[0]).toBe('!Array.'); + expect(mini.type.names[1]).toBe('!Array.>'); + }); + + it('When the type expression for a @type tag contains a newline character and is enclosed ' + + 'in braces, the type expression is parsed correctly.', function() { + expect(mega).toBeDefined(); + expect(mega.type).toBeDefined(); + expect(mega.type.names).toBeDefined(); + expect(mega.type.names.length).toBe(3); + expect(mega.type.names[0]).toBe('!Array.'); + expect(mega.type.names[1]).toBe('!Array.>'); + expect(mega.type.names[2]).toBe('!Array.>>'); + }); +}); From 7863eb3eb90594127a9a52f5e75ba4ecc06430bc Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sat, 14 Sep 2013 09:02:06 -0700 Subject: [PATCH 22/40] test for #439 (and fix existing parseComplete test) --- test/specs/jsdoc/src/parser.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/test/specs/jsdoc/src/parser.js b/test/specs/jsdoc/src/parser.js index d75f554a..b8b6996a 100644 --- a/test/specs/jsdoc/src/parser.js +++ b/test/specs/jsdoc/src/parser.js @@ -71,11 +71,25 @@ describe("jsdoc/src/parser", function() { }); it("should fire 'parseComplete' events after it finishes parsing files", function() { + var eventObject; + var spy = jasmine.createSpy(), - sourceCode = ['javascript:var bar = false;']; + sourceCode = ['javascript:/** @class */function Foo() {}']; + + require('jsdoc/src/handlers').attachTo(parser); parser.on('parseComplete', spy).parse(sourceCode); + expect(spy).toHaveBeenCalled(); - expect(spy.mostRecentCall.args[0].sourcefiles).toEqual(["[[string0]]"]); + + eventObject = spy.mostRecentCall.args[0]; + expect(eventObject).toBeDefined(); + expect( Array.isArray(eventObject.sourcefiles) ).toBe(true); + expect(eventObject.sourcefiles.length).toBe(1); + expect(eventObject.sourcefiles[0]).toBe('[[string0]]'); + expect( Array.isArray(eventObject.doclets) ).toBe(true); + expect(eventObject.doclets.length).toBe(1); + expect(eventObject.doclets[0].kind).toBe('class'); + expect(eventObject.doclets[0].longname).toBe('Foo'); }); it("should fire processingComplete when fireProcessingComplete is called", function() { From a36769ac7dd6cb086524dd6f383f496b41ec1263 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sun, 15 Sep 2013 22:39:21 -0700 Subject: [PATCH 23/40] fix syntax issue --- lib/jsdoc/name.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/jsdoc/name.js b/lib/jsdoc/name.js index b3b97d4b..695c37f6 100644 --- a/lib/jsdoc/name.js +++ b/lib/jsdoc/name.js @@ -198,7 +198,8 @@ exports.shorten = function(longname, forcedMemberof) { // like /** @name foo.bar(2) */ if ( /(.+)\(([^)]+)\)$/.test(name) ) { - name = RegExp.$1, variation = RegExp.$2; + name = RegExp.$1; + variation = RegExp.$2; } //// restore quoted strings back again From 79487b786a2d76efbd1526140196af04d9aa182f Mon Sep 17 00:00:00 2001 From: Brian Donovan Date: Tue, 17 Sep 2013 11:36:07 -0700 Subject: [PATCH 24/40] Account for relative jsdoc symlinks. This happens when you install jsdoc via homebrew on Mac OS X. The jsdoc binary is actually a link to "../Cellar/jsdoc3/3.2.0/bin/jsdoc", which without this patch the BASEPATH line below tries to cd up a level from the current working directory and into a non-existent Cellar directory. --- jsdoc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/jsdoc b/jsdoc index 6ec0f5a5..357c218a 100755 --- a/jsdoc +++ b/jsdoc @@ -2,7 +2,14 @@ # rhino discards the path to the current script file, so we must add it back SOURCE="$0" -while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done +while [ -h "$SOURCE" ] ; do + NEXTSOURCE="$(readlink "$SOURCE")" + if [[ "$NEXTSOURCE" = /* ]]; then + SOURCE="$NEXTSOURCE" + else + SOURCE="$(dirname $SOURCE)/$NEXTSOURCE" + fi +done # Get a Windows path under MinGW or Cygwin BASEPATH="$( cd -P "$( dirname "$SOURCE" )" && (pwd -W 2>/dev/null || cygpath -w $(pwd) 2>/dev/null || pwd))" if [ "${BASEPATH%${BASEPATH#?}}" != "/" ] ; then From 7093310ac14f8d3b3e48eeef2dd455533f17a065 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Tue, 17 Sep 2013 21:51:38 -0700 Subject: [PATCH 25/40] =?UTF-8?q?fix=20shells=20that=20don't=20support=20[?= =?UTF-8?q?[=20=E2=80=A6=20]]=20pattern=20matching=20(#492)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jsdoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jsdoc b/jsdoc index 357c218a..79714e15 100755 --- a/jsdoc +++ b/jsdoc @@ -4,7 +4,8 @@ SOURCE="$0" while [ -h "$SOURCE" ] ; do NEXTSOURCE="$(readlink "$SOURCE")" - if [[ "$NEXTSOURCE" = /* ]]; then + echo $NEXTSOURCE | grep -q -e "^/" + if [ $? = 0 ]; then SOURCE="$NEXTSOURCE" else SOURCE="$(dirname $SOURCE)/$NEXTSOURCE" From 687aceec042859afb8a88ecdf2987e1cd825d013 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Tue, 17 Sep 2013 21:57:53 -0700 Subject: [PATCH 26/40] bump version to 3.3.0-dev (belatedly) --- Jakefile.js | 2 +- package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index 751dd54e..ab5969d7 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -15,7 +15,7 @@ task('default', [], function(params) { var metadata = { appname : 'jsdoc', - appversion : '3.2.0-dev', + appversion : '3.3.0-dev', timestamp : '' + new Date().getTime() }; diff --git a/package.json b/package.json index d2550db0..3b1a6e88 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jsdoc", - "version": "3.2.0-dev", - "revision": "1375799851600", + "version": "3.3.0-dev", + "revision": "1379480242214", "description": "An API documentation generator for JavaScript.", "keywords": [ "documentation", "javascript" ], "licenses": [ From b31db5bab9fd83e3139f0258c5d28010cc598ba3 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Fri, 20 Sep 2013 08:37:44 -0700 Subject: [PATCH 27/40] require name/description separators to start on the same line as the name (#459) ensures we won't interpret a Markdown bullet on the following line as a name/description separator --- lib/jsdoc/name.js | 46 +++++++++------------------------------- lib/jsdoc/tag/type.js | 18 +++++++++++----- test/specs/jsdoc/name.js | 18 +++++++++++++++- 3 files changed, 40 insertions(+), 42 deletions(-) diff --git a/lib/jsdoc/name.js b/lib/jsdoc/name.js index 695c37f6..5157aff1 100644 --- a/lib/jsdoc/name.js +++ b/lib/jsdoc/name.js @@ -123,6 +123,7 @@ function quoteUnsafe(name, kind) { // docspaced names may have unsafe characters 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, "\\$&"); @@ -216,44 +217,17 @@ exports.shorten = function(longname, forcedMemberof) { }; /** - Split a string that starts with a name and ends with a description, into its parts. + Split a string that starts with a name and ends with a description into its parts. @param {string} nameDesc @returns {object} Hash with "name" and "description" properties. */ exports.splitName = function(nameDesc) { - var name = '', - desc = '', - thisChar = '', - inQuote = false; - - for (var i = 0, len = nameDesc.length; i < len; i++) { - thisChar = nameDesc.charAt(i); - - if (thisChar === '\\') { - name += thisChar + nameDesc.charAt(++i); - continue; - } - - if (thisChar === '"') { - inQuote = !inQuote; - } - - if (inQuote) { - name += thisChar; - continue; - } - - if (!inQuote) { - if ( /\s/.test(thisChar) ) { - desc = nameDesc.substr(i); - desc = desc.replace(/^[\s\-\s]+/, '').trim(); - break; - } - else { - name += thisChar; - } - } - } - - return { name: name, description: desc }; + // like: name, [name], name text, [name] text, name - text, or [name] - text + // the hyphen must be on the same line as the name; this prevents us from treating a Markdown + // dash as a separator + nameDesc.match(/^(\[[^\]]+\]|\S+)((?:[ \t]*\-\s*|\s+)(\S[\s\S]*))?$/); + return { + name: RegExp.$1, + description: RegExp.$3 + }; }; diff --git a/lib/jsdoc/tag/type.js b/lib/jsdoc/tag/type.js index d36b6820..e2d384a2 100644 --- a/lib/jsdoc/tag/type.js +++ b/lib/jsdoc/tag/type.js @@ -6,6 +6,13 @@ * @license Apache License 2.0 - See file 'LICENSE.md' in this project. */ +var jsdoc = { + name: require('jsdoc/name'), + tag: { + inline: require('jsdoc/tag/inline') + } +}; + /** * Information about a type expression extracted from tag text. * @@ -83,6 +90,7 @@ function getTagInfo(tagValue, canHaveName, canHaveType) { var typeExpression = ''; var text = tagValue; var expressionAndText; + var nameAndDescription; var typeOverride; if (canHaveType) { @@ -92,15 +100,14 @@ function getTagInfo(tagValue, canHaveName, canHaveType) { } if (canHaveName) { - // like: name, [name], name text, [name] text, name - text, or [name] - text - text.match(/^(\[[^\]]+\]|\S+)((?:\s*\-\s*|\s+)(\S[\s\S]*))?$/); - name = RegExp.$1; - text = RegExp.$3; + nameAndDescription = jsdoc.name.splitName(text); + name = nameAndDescription.name; + text = nameAndDescription.description; } // an inline @type tag, like {@type Foo}, overrides the type expression if (canHaveType) { - typeOverride = require('jsdoc/tag/inline').extractInlineTag(text, 'type'); + typeOverride = jsdoc.tag.inline.extractInlineTag(text, 'type'); if (typeOverride.tags && typeOverride.tags[0]) { typeExpression = typeOverride.tags[0].text || typeExpression; } @@ -134,6 +141,7 @@ function getTagInfo(tagValue, canHaveName, canHaveType) { * can vary (for example, in a function that accepts any number of parameters). */ +// TODO: move to module:jsdoc/name? /** * Extract JSDoc-style type information from the name specified in the tag info, including the * member name; whether the member is optional; and the default value of the member. diff --git a/test/specs/jsdoc/name.js b/test/specs/jsdoc/name.js index f8ba382b..21e3d720 100644 --- a/test/specs/jsdoc/name.js +++ b/test/specs/jsdoc/name.js @@ -85,7 +85,7 @@ describe("jsdoc/name", function() { expect(parts.memberof).toEqual('channels."#ops"'); expect(parts.scope).toEqual('#'); - startName = 'channels["#bots"]["log.max"]', + startName = 'channels["#bots"]["log.max"]'; parts = jsdoc.name.shorten(startName); expect(parts.name).toEqual('"log.max"'); @@ -174,6 +174,22 @@ describe("jsdoc/name", function() { expect(parts.name, 'ns.Page#"last \\"sentence\\"".words~sort(2)'); expect(parts.description, 'This is a description.'); }); + + it('should strip the separator when the separator starts on the same line as the name', function() { + var startName = 'socket - The networking kind, not the wrench.'; + var parts = jsdoc.name.splitName(startName); + + expect(parts.name).toBe('socket'); + expect(parts.description).toBe('The networking kind, not the wrench.'); + }); + + it('should not strip a separator that is preceded by a line break', function() { + var startName = 'socket\n - The networking kind, not the wrench.'; + var parts = jsdoc.name.splitName(startName); + + expect(parts.name).toBe('socket'); + expect(parts.description).toBe('- The networking kind, not the wrench.'); + }); }); describe("resolve", function() { From 193f7d18d6a1452aae1f92b0ab5c3014e859103c Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sat, 21 Sep 2013 16:38:07 -0700 Subject: [PATCH 28/40] support the type expression 'function[]' (#493) --- node_modules/catharsis/lib/parser.js | 6 +++--- node_modules/catharsis/package.json | 25 +++++++++++-------------- package.json | 12 +++++------- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/node_modules/catharsis/lib/parser.js b/node_modules/catharsis/lib/parser.js index 413fa56b..2ee8d967 100644 --- a/node_modules/catharsis/lib/parser.js +++ b/node_modules/catharsis/lib/parser.js @@ -1,3 +1,3 @@ -module.exports=function(){function peg$subclass(child,parent){function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor}function SyntaxError(expected,found,offset,line,column){function buildMessage(expected,found){function stringEscape(s){function hex(ch){return ch.charCodeAt(0).toString(16).toUpperCase()}return s.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\x08/g,"\\b").replace(/\t/g,"\\t").replace(/\n/g,"\\n").replace(/\f/g,"\\f").replace(/\r/g,"\\r").replace(/[\x00-\x07\x0B\x0E\x0F]/g,function(ch){return"\\x0"+hex(ch)}).replace(/[\x10-\x1F\x80-\xFF]/g,function(ch){return"\\x"+hex(ch)}).replace(/[\u0180-\u0FFF]/g,function(ch){return"\\u0"+hex(ch)}).replace(/[\u1080-\uFFFF]/g,function(ch){return"\\u"+hex(ch)})}var expectedDesc,foundDesc;switch(expected.length){case 0:expectedDesc="end of input";break;case 1:expectedDesc=expected[0];break;default:expectedDesc=expected.slice(0,-1).join(", ")+" or "+expected[expected.length-1]}foundDesc=found?'"'+stringEscape(found)+'"':"end of input";return"Expected "+expectedDesc+" but "+foundDesc+" found."}this.expected=expected;this.found=found;this.offset=offset;this.line=line;this.column=column;this.name="SyntaxError";this.message=buildMessage(expected,found)}peg$subclass(SyntaxError,Error);function parse(input){var options=arguments.length>1?arguments[1]:{},peg$startRuleFunctions={TypeExpression:peg$parseTypeExpression},peg$startRuleFunction=peg$parseTypeExpression,peg$c0=null,peg$c1="",peg$c2=function(unk){return unk},peg$c3="?",peg$c4='"?"',peg$c5="!",peg$c6='"!"',peg$c7=function(prefix,expr,postfix){if(prefix!==""&&postfix!==""){return null}[prefix,postfix].forEach(function(modifier){if(modifier!==""){expr.nullable=modifier==="?"?true:false}});return expr},peg$c8=function(repeat,lit,opt){var result=lit;if(opt.optional){result.optional=true}if(repeat.repeatable){result.repeatable=true}return result},peg$c9="*",peg$c10='"*"',peg$c11=function(){return{type:Types.AllLiteral}},peg$c12=function(){return{type:Types.NullLiteral}},peg$c13=function(){return{type:Types.UndefinedLiteral}},peg$c14="...",peg$c15='"..."',peg$c16=function(){return{repeatable:true}},peg$c17="=",peg$c18='"="',peg$c19=function(){return{optional:true}},peg$c20="[]",peg$c21='"[]"',peg$c22=function(name){var result;if(!options.jsdoc){return null}result={type:Types.TypeApplication,expression:{type:Types.NameExpression,name:"Array"},applications:[name]};result.applications[0].type=Types.NameExpression;return result},peg$c23=function(exp,appl,opt){var result={};var nameExp={type:Types.NameExpression,name:exp.name};if(appl.length){result.type=Types.TypeApplication;result.expression=nameExp;result.applications=appl}else{result=nameExp}if(exp.repeatable){result.repeatable=true}if(opt.optional){result.optional=true}return result},peg$c24=function(name){if(!options.jsdoc){return null}return name},peg$c25=function(exp,opt){var result={type:Types.NameExpression,name:exp.name,reservedWord:true};if(exp.repeatable){result.repeatable=true}if(opt.optional){result.optional=true}return result},peg$c26=".",peg$c27='"."',peg$c28="<",peg$c29='"<"',peg$c30=">",peg$c31='">"',peg$c32=function(sep,l){if(sep===""&&!options.jsdoc){return null}return l},peg$c33=[],peg$c34=",",peg$c35='","',peg$c36=function(expr,list){var result=[expr];for(var i=0,l=list.length;ipos){peg$cachedPos=0;peg$cachedPosDetails={line:1,column:1,seenCR:false}}peg$cachedPos=pos;advance(peg$cachedPosDetails,peg$cachedPos)}return peg$cachedPosDetails}function peg$fail(expected){if(peg$currPospeg$maxFailPos){peg$maxFailPos=peg$currPos;peg$maxFailExpected=[]}peg$maxFailExpected.push(expected)}function peg$cleanupExpected(expected){var i=0;expected.sort();while(ipeg$currPos){s0=input.charAt(peg$currPos);peg$currPos++}else{s0=null;if(peg$silentFails===0){peg$fail(peg$c271)}}return s0}function peg$parseHexEscapeSequence(){var s0,s1,s2,s3,s4,s5;s0=peg$currPos;if(input.charCodeAt(peg$currPos)===120){s1=peg$c269;peg$currPos++}else{s1=null;if(peg$silentFails===0){peg$fail(peg$c270)}}if(s1!==null){s2=peg$currPos;s3=peg$currPos;s4=peg$parseHexDigit();if(s4!==null){s5=peg$parseHexDigit();if(s5!==null){s4=[s4,s5];s3=s4}else{peg$currPos=s3;s3=peg$c0}}else{peg$currPos=s3;s3=peg$c0}if(s3!==null){s3=input.substring(s2,peg$currPos)}s2=s3;if(s2!==null){peg$reportedPos=s0;s1=peg$c216(s2);if(s1===null){peg$currPos=s0;s0=s1}else{s0=s1}}else{peg$currPos=s0;s0=peg$c0}}else{peg$currPos=s0;s0=peg$c0}return s0}function peg$parseLineContinuation(){var s0,s1,s2;s0=peg$currPos;if(input.charCodeAt(peg$currPos)===92){s1=peg$c212;peg$currPos++}else{s1=null;if(peg$silentFails===0){peg$fail(peg$c213)}}if(s1!==null){s2=peg$parseLineTerminatorSequence();if(s2!==null){peg$reportedPos=s0;s1=peg$c272(s2);if(s1===null){peg$currPos=s0;s0=s1}else{s0=s1}}else{peg$currPos=s0;s0=peg$c0}}else{peg$currPos=s0;s0=peg$c0}return s0}function peg$parse_(){var s0,s1;peg$silentFails++;s0=[];s1=peg$parseWhitespace();while(s1!==null){s0.push(s1);s1=peg$parseWhitespace()}peg$silentFails--;if(s0===null){s1=null;if(peg$silentFails===0){peg$fail(peg$c273)}}return s0}function peg$parse__(){var s0,s1;peg$silentFails++;s0=peg$c1;peg$silentFails--;if(s0===null){s1=null;if(peg$silentFails===0){peg$fail(peg$c274)}}return s0}function peg$parseWhitespace(){var s0;if(peg$c275.test(input.charAt(peg$currPos))){s0=input.charAt(peg$currPos);peg$currPos++}else{s0=null;if(peg$silentFails===0){peg$fail(peg$c276)}}if(s0===null){s0=peg$parseUnicodeZs()}return s0}var Types=require("./types");peg$result=peg$startRuleFunction();if(peg$result!==null&&peg$currPos===input.length){return peg$result}else{peg$cleanupExpected(peg$maxFailExpected);peg$reportedPos=Math.max(peg$currPos,peg$maxFailPos);throw new SyntaxError(peg$maxFailExpected,peg$reportedPos1?arguments[1]:{},peg$startRuleFunctions={TypeExpression:peg$parseTypeExpression},peg$startRuleFunction=peg$parseTypeExpression,peg$c0=null,peg$c1="",peg$c2=function(unk){return unk},peg$c3="?",peg$c4='"?"',peg$c5="!",peg$c6='"!"',peg$c7=function(prefix,expr,postfix){if(prefix!==""&&postfix!==""){return null}[prefix,postfix].forEach(function(modifier){if(modifier!==""){expr.nullable=modifier==="?"?true:false}});return expr},peg$c8=function(repeat,lit,opt){var result=lit;if(opt.optional){result.optional=true}if(repeat.repeatable){result.repeatable=true}return result},peg$c9="*",peg$c10='"*"',peg$c11=function(){return{type:Types.AllLiteral}},peg$c12=function(){return{type:Types.NullLiteral}},peg$c13=function(){return{type:Types.UndefinedLiteral}},peg$c14="...",peg$c15='"..."',peg$c16=function(){return{repeatable:true}},peg$c17="=",peg$c18='"="',peg$c19=function(){return{optional:true}},peg$c20="[]",peg$c21='"[]"',peg$c22=function(name){var result;if(!options.jsdoc){return null}result={type:Types.TypeApplication,expression:{type:Types.NameExpression,name:"Array"},applications:[name]};result.applications[0].type=Types.NameExpression;return result},peg$c23=function(exp,appl,opt){var result={};var nameExp={type:Types.NameExpression,name:exp.name};if(appl.length){result.type=Types.TypeApplication;result.expression=nameExp;result.applications=appl}else{result=nameExp}if(exp.repeatable){result.repeatable=true}if(opt.optional){result.optional=true}return result},peg$c24=function(name){if(!options.jsdoc){return null}return name},peg$c25=function(exp,opt){var result={type:Types.NameExpression,name:exp.name,reservedWord:true};if(exp.repeatable){result.repeatable=true}if(opt.optional){result.optional=true}return result},peg$c26=".",peg$c27='"."',peg$c28="<",peg$c29='"<"',peg$c30=">",peg$c31='">"',peg$c32=function(sep,l){if(sep===""&&!options.jsdoc){return null}return l},peg$c33=[],peg$c34=",",peg$c35='","',peg$c36=function(expr,list){var result=[expr];for(var i=0,l=list.length;ipos){peg$cachedPos=0;peg$cachedPosDetails={line:1,column:1,seenCR:false}}peg$cachedPos=pos;advance(peg$cachedPosDetails,peg$cachedPos)}return peg$cachedPosDetails}function peg$fail(expected){if(peg$currPospeg$maxFailPos){peg$maxFailPos=peg$currPos;peg$maxFailExpected=[]}peg$maxFailExpected.push(expected)}function peg$cleanupExpected(expected){var i=0;expected.sort();while(ipeg$currPos){s0=input.charAt(peg$currPos);peg$currPos++}else{s0=null;if(peg$silentFails===0){peg$fail(peg$c274)}}return s0}function peg$parseHexEscapeSequence(){var s0,s1,s2,s3,s4,s5;s0=peg$currPos;if(input.charCodeAt(peg$currPos)===120){s1=peg$c272;peg$currPos++}else{s1=null;if(peg$silentFails===0){peg$fail(peg$c273)}}if(s1!==null){s2=peg$currPos;s3=peg$currPos;s4=peg$parseHexDigit();if(s4!==null){s5=peg$parseHexDigit();if(s5!==null){s4=[s4,s5];s3=s4}else{peg$currPos=s3;s3=peg$c0}}else{peg$currPos=s3;s3=peg$c0}if(s3!==null){s3=input.substring(s2,peg$currPos)}s2=s3;if(s2!==null){peg$reportedPos=s0;s1=peg$c219(s2);if(s1===null){peg$currPos=s0;s0=s1}else{s0=s1}}else{peg$currPos=s0;s0=peg$c0}}else{peg$currPos=s0;s0=peg$c0}return s0}function peg$parseLineContinuation(){var s0,s1,s2;s0=peg$currPos;if(input.charCodeAt(peg$currPos)===92){s1=peg$c215;peg$currPos++}else{s1=null;if(peg$silentFails===0){peg$fail(peg$c216)}}if(s1!==null){s2=peg$parseLineTerminatorSequence();if(s2!==null){peg$reportedPos=s0;s1=peg$c275(s2);if(s1===null){peg$currPos=s0;s0=s1}else{s0=s1}}else{peg$currPos=s0;s0=peg$c0}}else{peg$currPos=s0;s0=peg$c0}return s0}function peg$parse_(){var s0,s1;peg$silentFails++;s0=[];s1=peg$parseWhitespace();while(s1!==null){s0.push(s1);s1=peg$parseWhitespace()}peg$silentFails--;if(s0===null){s1=null;if(peg$silentFails===0){peg$fail(peg$c276)}}return s0}function peg$parse__(){var s0,s1;peg$silentFails++;s0=peg$c1;peg$silentFails--;if(s0===null){s1=null;if(peg$silentFails===0){peg$fail(peg$c277)}}return s0}function peg$parseWhitespace(){var s0;if(peg$c278.test(input.charAt(peg$currPos))){s0=input.charAt(peg$currPos);peg$currPos++}else{s0=null;if(peg$silentFails===0){peg$fail(peg$c279)}}if(s0===null){s0=peg$parseUnicodeZs()}return s0}var Types=require("./types");peg$result=peg$startRuleFunction();if(peg$result!==null&&peg$currPos===input.length){return peg$result}else{peg$cleanupExpected(peg$maxFailExpected);peg$reportedPos=Math.max(peg$currPos,peg$maxFailPos);throw new SyntaxError(peg$maxFailExpected,peg$reportedPos= 0.6" @@ -33,12 +35,7 @@ "url": "http://github.com/hegemonic/catharsis/raw/master/LICENSE" } ], - "readme": "# Catharsis #\n\nA JavaScript parser for\n[Google Closure Compiler](https://developers.google.com/closure/compiler/docs/js-for-compiler#types)\nand [JSDoc](https://github.com/jsdoc3/jsdoc) type expressions.\n\nCatharsis is designed to be:\n\n+ **Accurate**. Catharsis is based on a [PEG.js](http://pegjs.majda.cz/) grammar that's designed to\nhandle any valid type expression. It uses a [Mocha](http://visionmedia.github.com/mocha/) test suite\nto verify the parser's accuracy.\n+ **Fast**. Parse results are cached, so the parser is invoked only when necessary.\n+ **Flexible**. Catharsis can convert parse results back into type expressions. In addition, it can\nparse [JSDoc](https://github.com/jsdoc3/jsdoc)-style type expressions.\n\n\n## Example ##\n\n\tvar catharsis = require('catharsis');\n\n var type;\n var jsdocType;\n var parsedType;\n var parsedJsdocType;\n\n // Google Closure Compiler parsing\n try {\n type = '!Object';\n parsedType = catharsis.parse(type);\n console.log('%j', parsedType); // {\"type\":\"NameExpression,\"name\":\"Object\",\"nullable\":false}\n }\n catch(e) {\n console.error('unable to parse %s: %s', type, e);\n }\n\n // JSDoc-style type expressions enabled\n try {\n jsdocType = 'string[]'; // Closure Compiler expects Array.\n parsedJsdocType = catharsis.parse(jsdocType, {jsdoc: true});\n }\n catch (e) {\n console.error('unable to parse %s: %s', jsdocType, e);\n }\n\n console.log(catharsis.stringify(parsedType)); // !Object\n console.log(catharsis.stringify(parsedJsdocType)); // string[]\n console.log(catharsis.stringify(parsedJsdocType, // Array.\n {restringify: true}));\n\n\nSee the `test/specs/` directory for more examples of Catharsis' parse results.\n\n\n## Methods ##\n\n### parse(typeExpression, options) ###\nParse `typeExpression`, and return the parse results. Throws an error if the type expression cannot\nbe parsed.\n\nWhen called without options, Catharsis attempts to parse type expressions in the same way as\nClosure Compiler. When the `jsdoc` option is enabled, Catharsis can also parse several kinds of\ntype expressions that are permitted in [JSDoc](https://github.com/jsdoc3/jsdoc):\n\n+ The string `function` is treated as a function type with no parameters.\n+ In a function type with repeatable parameters, the names of repeatable parameters are not required\nto be enclosed in square brackets (for example, `function(...foo)` is allowed).\n+ The period may be omitted from type applications. For example, `Array.` and\n`Array` will be parsed in the same way.\n+ You may append `[]` to a name expression (for example, `string[]`) to interpret it as a type\napplication with the expression `Array` (for example, `Array.`).\n+ Name expressions may contain the characters `#`, `~`, `:`, and `/`.\n+ Name expressions may contain a suffix that is similar to a function signature (for example,\n`MyClass(foo, bar)`).\n+ Name expressions may contain a reserved word.\n+ Record types may use types other than name expressions for keys.\n\n#### Parameters ####\n+ `type`: A string containing a Closure Compiler type expression.\n+ `options`: Options for parsing the type expression.\n + `options.jsdoc`: Specifies whether to enable parsing of JSDoc-style type expressions. Defaults\n to `false`.\n + `options.useCache`: Specifies whether to use the cache of parsed types. Defaults to `true`.\n\n#### Returns ####\nAn object containing the parse results. See the `test/specs/` directory for examples of the parse\nresults for different type expressions.\n\nThe object also includes two non-enumerable properties:\n\n+ `jsdoc`: A boolean indicating whether the type expression was parsed with JSDoc support enabled.\n+ `typeExpression`: A string containing the type expression that was parsed.\n\n### stringify(parsedType, options) ###\nStringify `parsedType`, and return the type expression. If validation is enabled, throws an error if\nthe stringified type expression cannot be parsed.\n\n#### Parameters ####\n+ `parsedType`: An object containing a parsed Closure Compiler type expression.\n+ `options`: Options for stringifying the parse results.\n + `options.cssClass`: A CSS class to add to HTML links. Used only if `options.links` is\n provided. By default, no CSS class is added.\n + `options.htmlSafe`: Specifies whether to return an HTML-safe string that replaces left angle\n brackets (`<`) with the corresponding entity (`<`). **Note**: Characters in name expressions\n are not escaped.\n + `options.links`: An object whose keys are name expressions and whose values are URIs. If a\n name expression matches a key in `options.links`, the name expression will be wrapped in an\n HTML `` tag that links to the URI. If `options.cssClass` is specified, the `` tag will\n include a `class` attribute. **Note**: When using this option, parsed types are always\n restringified, and the resulting string is not cached.\n + `options.restringify`: Forces Catharsis to restringify the parsed type. If this option is not\n specified, and the parsed type object includes a `typeExpression` property, Catharsis will\n return the `typeExpression` property without modification when possible. Defaults to `false`.\n + `options.useCache`: Specifies whether to use the cache of stringified parse results. Defaults\n to `true`.\n + `options.validate`: Specifies whether to validate the stringified parse results by attempting\n to parse them as a type expression. If the stringified results are not parsable by default, you\n must also provide the appropriate options to pass to the `parse()` method. Defaults to `false`.\n\n#### Returns ####\nA string containing the type expression.\n\n\n## Installation ##\n\nWith [npm](http://npmjs.org):\n\n npm install catharsis\n\nOr without:\n\n git clone git://github.com/hegemonic/catharsis.git\n\n\n## Roadmap and known issues ##\n\nTake a look at the [issue tracker](https://github.com/hegemonic/catharsis/issues) to see what's in\nstore for Catharsis.\n\nBug reports, feature requests, and pull requests are always welcome! If you're working on a large\npull request, please contact me in advance so I can help things go smoothly.\n\n**Note**: The parse tree's format should not be considered final until Catharsis reaches version\n1.0. I'll do my best to provide release notes for any changes.\n\n\n## Changelog ##\n\n+ 0.5.6 (April 2013):\n + For consistency with Google Closure Library, parentheses are no longer required around type\n unions. (In previous versions, the parentheses could be omitted when JSDoc support was enabled.)\n + For consistency with Google Closure Library, you can now use postfix notation for the `?`\n (nullable) and `!` (non-nullable) modifiers. For example, `?string` and `string?` are now\n treated as equivalent.\n + String literals and numeric literals are now allowed as property names within name\n expressions. For example, the name expression `Foo.\"bar\"` is now parsed correctly.\n+ 0.5.5 (April 2013): Corrected a parsing issue with name expressions that end with a value enclosed\nin parentheses.\n+ 0.5.4 (April 2013):\n + Repeatable literals (for example, `...*`) are now parsed correctly.\n + When JSDoc-style type expressions are enabled, a name expression can now contain a value\n enclosed in parentheses at the end of the name expression (for example, `MyClass(2)`).\n+ 0.5.3 (March 2013): The `parse()` method now correctly parses name expressions that contain\nhyphens.\n+ 0.5.2 (March 2013): The `parse()` method now correctly parses function types when JSDoc-style type\nexpressions are enabled.\n+ 0.5.1 (March 2013): Newlines and extra spaces are now removed from type expressions before they\nare parsed.\n+ 0.5.0 (March 2013):\n + The `parse()` method's `lenient` option has been renamed to `jsdoc`. **Note**: This change is\n not backwards-compatible with previous versions.\n + The `stringify()` method now accepts `cssClass` and `links` options, which you can use to\n add HTML links to a type expression.\n+ 0.4.3 (March 2013):\n + The `stringify()` method no longer caches HTML-safe type expressions as if they were normal\n type expressions.\n + The `stringify()` method's options parameter may now include an `options.restringify`\n property, and the behavior of the `options.useCache` property has changed.\n+ 0.4.2 (March 2013):\n + When lenient parsing is enabled, name expressions can now contain the characters `:` and `/`.\n + When lenient parsing is enabled, a name expression followed by `[]` (for example, `string[]`)\n will be interpreted as a type application with the expression `Array` (for example,\n `Array.`).\n+ 0.4.1 (March 2013):\n + The `parse()` and `stringify()` methods now honor all of the specified options.\n + When lenient parsing is enabled, name expressions can now contain a reserved word.\n+ 0.4.0 (March 2013):\n + Catharsis now supports a lenient parsing option that can parse several kinds of malformed type\n expressions. See the documentation for details.\n + The objects containing parse results are now frozen.\n + The objects containing parse results now have two non-enumerable properties:\n + `lenient`: A boolean indicating whether the type expression was parsed in lenient mode.\n + `typeExpression`: A string containing the original type expression.\n + The `stringify()` method now honors the `useCache` option. If a parsed type includes a\n `typeExpression` property, and `useCache` is not set to `false`, the stringified type will be\n identical to the original type expression.\n+ 0.3.1 (March 2013): Type expressions that begin with a reserved word, such as `integer`, are now\nparsed correctly.\n+ 0.3.0 (March 2013):\n + The `parse()` and `stringify()` methods are now synchronous, and the `parseSync()` and\n `stringifySync()` methods have been removed. **Note**: This change is not backwards-compatible\n with previous versions.\n + The parse results now use a significantly different format from previous versions. The new\n format is more expressive and is similar, but not identical, to the format used by the\n [doctrine](https://github.com/Constellation/doctrine) parser. **Note**: This change is not\n backwards-compatible with previous versions.\n + Name expressions that contain a reserved word now include a `reservedWord: true` property.\n + Union types that are optional or nullable, or that can be passed a variable number of times,\n are now parsed and stringified correctly.\n + Optional function types and record types are now parsed and stringified correctly.\n + Function types now longer include `new` or `this` properties unless the properties are defined\n in the type expression. In addition, the `new` and `this` properties can now use any type\n expression.\n + In record types, the key for a field type can now use any type expression.\n + Standalone single-character literals, such as ALL (`*`), are now parsed and stringified\n correctly.\n + `null` and `undefined` literals with additional properties, such as `repeatable`, are now\n stringified correctly.\n+ 0.2.0 (November 2012):\n + Added `stringify()` and `stringifySync()` methods, which convert a parsed type to a type\n expression.\n + Simplified the parse results for function signatures. **Note**: This change is not\n backwards-compatible with previous versions.\n + Corrected minor errors in README.md.\n+ 0.1.1 (November 2012): Added `opts` argument to `parse()` and `parseSync()` methods. **Note**: The\nchange to `parse()` is not backwards-compatible with previous versions.\n+ 0.1.0 (November 2012): Initial release.\n\n## License ##\n\n[MIT license](https://github.com/hegemonic/catharsis/blob/master/LICENSE).\n", - "readmeFilename": "README.md", - "_id": "catharsis@0.5.6", - "dist": { - "shasum": "210a0cfa23c5d09fa994d6fafe1a76383c170cbb" - }, - "_from": "catharsis@0.5.6", - "_resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.5.6.tgz" + "readme": "ERROR: No README data found!", + "_id": "catharsis@0.6.0", + "_from": "catharsis@0.6.0" } diff --git a/package.json b/package.json index 3b1a6e88..dae1bf67 100644 --- a/package.json +++ b/package.json @@ -10,15 +10,13 @@ "url": "http://www.apache.org/licenses/LICENSE-2.0" } ], - "repositories": [ - { - "type": "git", - "url": "https://github.com/jsdoc3/jsdoc" - } - ], + "repository": { + "type": "git", + "url": "https://github.com/jsdoc3/jsdoc" + }, "dependencies": { "async": "0.1.22", - "catharsis": "0.5.6", + "catharsis": "0.6.0", "crypto-browserify": "git+https://github.com/dominictarr/crypto-browserify.git#95c5d505", "js2xmlparser": "0.1.0", "jshint": "0.9.1", From 7415bda02ba849f0ba31411c5137a0a30abb8094 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sat, 21 Sep 2013 16:39:06 -0700 Subject: [PATCH 29/40] ignore Tern temp files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4c9f446a..929e4afa 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ jsdoc.jar test/tutorials/out conf.json out/ +.tern-port From 2b845d7590a4aab5d531534a4b5fee79844afdea Mon Sep 17 00:00:00 2001 From: livelazily Date: Sat, 28 Sep 2013 15:40:32 +0800 Subject: [PATCH 30/40] read source files using user defined encodeing, fix #496 --- templates/default/publish.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/templates/default/publish.js b/templates/default/publish.js index 092514f4..63a62f96 100644 --- a/templates/default/publish.js +++ b/templates/default/publish.js @@ -132,7 +132,8 @@ function generate(title, docs, filename, resolveLinks) { fs.writeFileSync(outpath, html, 'utf8'); } -function generateSourceFiles(sourceFiles) { +function generateSourceFiles(sourceFiles, encoding) { + encoding = encoding || 'utf8'; Object.keys(sourceFiles).forEach(function(file) { var source; // links are keyed to the shortened path in each doclet's `meta.filename` property @@ -142,7 +143,7 @@ function generateSourceFiles(sourceFiles) { try { source = { kind: 'source', - code: helper.htmlsafe( fs.readFileSync(sourceFiles[file].resolved, 'utf8') ) + code: helper.htmlsafe( fs.readFileSync(sourceFiles[file].resolved, encoding) ) }; } catch(e) { @@ -486,7 +487,7 @@ exports.publish = function(taffyData, opts, tutorials) { // output pretty-printed source files by default; do this before generating any other pages, so // that the other pages can link to the source files if (!conf['default'] || conf['default'].outputSourceFiles !== false) { - generateSourceFiles(sourceFiles); + generateSourceFiles(sourceFiles, opts.encoding); } if (members.globals.length) { generate('Global', [{kind: 'globalobj'}], globalUrl); } From 3ce43c2f326b20b7c21dc9ef1984501474fc650d Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Mon, 30 Sep 2013 22:24:50 -0700 Subject: [PATCH 31/40] remove hashbang before parsing JS file (#499) --- lib/jsdoc/src/parser.js | 3 +++ test/specs/jsdoc/src/parser.js | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/jsdoc/src/parser.js b/lib/jsdoc/src/parser.js index 64fbc455..cd4d6715 100644 --- a/lib/jsdoc/src/parser.js +++ b/lib/jsdoc/src/parser.js @@ -144,6 +144,9 @@ exports.Parser.prototype.getVisitors = function() { function pretreat(code) { return code + // comment out hashbang at the top of the file, like: #!/usr/bin/env node + .replace(/^(\#\![\S \t]+\n)/, '// $1') + // make starbangstar comments look like real jsdoc comments .replace(/\/\*\!\*/g, '/**') diff --git a/test/specs/jsdoc/src/parser.js b/test/specs/jsdoc/src/parser.js index b8b6996a..aae0cb64 100644 --- a/test/specs/jsdoc/src/parser.js +++ b/test/specs/jsdoc/src/parser.js @@ -113,6 +113,16 @@ describe("jsdoc/src/parser", function() { expect(parse).not.toThrow(); }); + + it("should comment out a POSIX hashbang at the start of the file", function() { + function parse() { + parser.parse(parserSrc); + } + + var parserSrc = 'javascript:#!/usr/bin/env node\n/** class */function Foo() {}'; + + expect(parse).not.toThrow(); + }); }); describe("results", function() { From 463dd0a05b8a78dc5ed8fed94416c512dea80353 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Tue, 1 Oct 2013 09:29:08 -0700 Subject: [PATCH 32/40] generate valid filenames for incorrectly tagged modules (#458) --- lib/jsdoc/util/templateHelper.js | 29 +++++++++++++++++++------ test/specs/jsdoc/util/templateHelper.js | 19 ++++++++++++++++ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/lib/jsdoc/util/templateHelper.js b/lib/jsdoc/util/templateHelper.js index 35a47fce..c85a5665 100644 --- a/lib/jsdoc/util/templateHelper.js +++ b/lib/jsdoc/util/templateHelper.js @@ -51,8 +51,16 @@ function makeFilenameUnique(filename, str) { return filename; } -// compute it here just once -var nsprefix = /^(event|module|external):/; +function cleanseFilename(str) { + // allow for namespace prefix + return str.replace(/^(event|module|external|package):/, '$1-') + // use - instead of ~ to denote 'inner' + .replace(/~/g, '-') + // use _ instead of # to denote 'instance' + .replace(/\#/g, '_') + // remove the variation, if any + .replace(/\([\s\S]*\)$/, ''); +} var htmlsafe = exports.htmlsafe = function(str) { return str.replace(/ Date: Wed, 2 Oct 2013 22:37:28 -0700 Subject: [PATCH 33/40] more fixes to ensure that we always generate valid filenames (#440) --- jsdoc.js | 10 +++++- lib/jsdoc/path.js | 5 ++- lib/jsdoc/tag/dictionary/definitions.js | 39 ++++++++++++--------- lib/jsdoc/util/templateHelper.js | 42 +++++++++++++++++------ test/specs/documentation/modules.js | 14 +++++--- test/specs/jsdoc/util/templateHelper.js | 45 +++++++++++++++++++++---- test/specs/tags/overviewtag.js | 22 ++++++++---- 7 files changed, 132 insertions(+), 45 deletions(-) diff --git a/jsdoc.js b/jsdoc.js index b1f27885..6312f1c9 100644 --- a/jsdoc.js +++ b/jsdoc.js @@ -61,6 +61,13 @@ require('lib/jsdoc/util/global').env = { */ opts: {}, + /** + * The source files that JSDoc will parse. + * @type Array + * @memberof env + */ + sourceFiles: [], + /** * The JSDoc version number and revision date. * @@ -220,7 +227,8 @@ function main() { if (env.conf.source && env.opts._.length > 0) { // are there any files to scan and parse? filter = new jsdoc.src.filter.Filter(env.conf.source); - sourceFiles = app.jsdoc.scanner.scan(env.opts._, (env.opts.recurse? 10 : undefined), filter); + env.sourceFiles = sourceFiles = app.jsdoc.scanner.scan(env.opts._, + (env.opts.recurse? 10 : undefined), filter); jsdoc.src.handlers.attachTo(app.jsdoc.parser); diff --git a/lib/jsdoc/path.js b/lib/jsdoc/path.js index 4ddb031d..ea405e8c 100644 --- a/lib/jsdoc/path.js +++ b/lib/jsdoc/path.js @@ -51,7 +51,10 @@ function prefixReducer(previousPath, current) { * @return {string} The common prefix, or an empty string if there is no common prefix. */ exports.commonPrefix = function(paths) { - var common = paths.reduce(prefixReducer, undefined); + var common; + + paths = paths || []; + common = paths.reduce(prefixReducer, undefined) || []; // if there's anything left (other than a placeholder for a leading slash), add a placeholder // for a trailing slash diff --git a/lib/jsdoc/tag/dictionary/definitions.js b/lib/jsdoc/tag/dictionary/definitions.js index cb96cbf7..b37e0753 100644 --- a/lib/jsdoc/tag/dictionary/definitions.js +++ b/lib/jsdoc/tag/dictionary/definitions.js @@ -7,7 +7,19 @@ @license Apache License 2.0 - See file 'LICENSE.md' in this project. */ -var path = require('path'); +var path = require('jsdoc/path'); + +function filepathMinusPrefix(filepath) { + var sourceFiles = env.sourceFiles || []; + var commonPrefix = path.commonPrefix( sourceFiles.concat(env.opts._ || []) ); + var result = (filepath + '/').replace(commonPrefix, ''); + + if (result.length > 0 && result[result.length - 1] !== '/') { + result += '/'; + } + + return result; +} /** @private */ function setDocletKindToTitle(doclet, tag) { @@ -34,12 +46,11 @@ function setDocletDescriptionToValue(doclet, tag) { } function setNameToFile(doclet, tag) { - var name = ''; + var name; + if (doclet.meta.filename) { - // TODO: find the shortest path shared by all input files, and remove that from - // doclet.meta.path - name += path.basename(doclet.meta.path) + '/'; - doclet.addTag( 'name', name + doclet.meta.filename ); + name = filepathMinusPrefix(doclet.meta.path) + doclet.meta.filename; + doclet.addTag('name', name); } } @@ -64,17 +75,13 @@ function applyNamespace(docletOrNs, tag) { } function setDocletNameToFilename(doclet, tag) { - // TODO: find the shortest path shared by all input files, and remove that from doclet.meta.path - var name = doclet.meta.path ? path.basename(doclet.meta.path) + '/' : ''; - name += doclet.meta.filename; - name = name.replace(/\.js$/i, ''); - - for (var i = 0, len = env.opts._.length; i < len; i++) { - if (name.indexOf(env.opts._[i]) === 0) { - name = name.replace(env.opts._[0], ''); - break; - } + var name = ''; + + if (doclet.meta.path) { + name = filepathMinusPrefix(doclet.meta.path); } + name += doclet.meta.filename.replace(/\.js$/i, ''); + doclet.name = name; } diff --git a/lib/jsdoc/util/templateHelper.js b/lib/jsdoc/util/templateHelper.js index c85a5665..c14f33cb 100644 --- a/lib/jsdoc/util/templateHelper.js +++ b/lib/jsdoc/util/templateHelper.js @@ -52,6 +52,8 @@ function makeFilenameUnique(filename, str) { } function cleanseFilename(str) { + str = str || ''; + // allow for namespace prefix return str.replace(/^(event|module|external|package):/, '$1-') // use - instead of ~ to denote 'inner' @@ -702,28 +704,46 @@ function getFilename(longname) { /** Turn a doclet into a URL. */ exports.createLink = function(doclet) { + var filename; + var fragment; + var match; + var fakeContainer; + var url = ''; var longname = doclet.longname; - var filename; - - var fakeContainerMatch = /(\S+):/.exec(longname); + // handle doclets in which doclet.longname implies that the doclet gets its own HTML file, but + // doclet.kind says otherwise. this happens due to mistagged JSDoc (for example, a module that + // somehow has doclet.kind set to `member`). + // TODO: generate a warning (ideally during parsing!) + if (containers.indexOf(doclet.kind) === -1) { + match = /(\S+):/.exec(longname); + if (match && containers.indexOf(match[1]) !== -1) { + fakeContainer = match[1]; + } + } + // the doclet gets its own HTML file if ( containers.indexOf(doclet.kind) !== -1 || isModuleFunction(doclet) ) { - url = getFilename(longname); + filename = getFilename(longname); } - // the doclet's longname suggests that it should get its own HTML file, but doclet.kind says - // otherwise. this happens due to mistagged JSDoc (for example, a module that somehow has - // doclet.kind set to `member`). use the container's filename plus a fragment. - else if ( containers.indexOf(doclet.kind) === -1 && fakeContainerMatch && - containers.indexOf(fakeContainerMatch[1]) !== -1 ) { - url = getFilename(longname) + '#' + doclet.name; + // mistagged version of a doclet that gets its own HTML file + else if ( containers.indexOf(doclet.kind) === -1 && fakeContainer ) { + filename = getFilename(doclet.memberof || longname); + if (doclet.name === doclet.longname) { + fragment = ''; + } + else { + fragment = doclet.name || ''; + } } // the doclet is within another HTML file else { filename = getFilename(doclet.memberof || exports.globalName); - url = filename + '#' + getNamespace(doclet.kind) + doclet.name; + fragment = getNamespace(doclet.kind) + (doclet.name || ''); } + + url = fragment ? (filename + '#' + fragment) : filename; return url; }; diff --git a/test/specs/documentation/modules.js b/test/specs/documentation/modules.js index 52481150..2c025d29 100644 --- a/test/specs/documentation/modules.js +++ b/test/specs/documentation/modules.js @@ -1,14 +1,20 @@ -/*global beforeEach: true, describe: true, env: true, expect: true, it: true */ +/*global afterEach: true, beforeEach: true, describe: true, env: true, expect: true, it: true */ describe("module names", function() { - var parser = require('jsdoc/src/parser'), - srcParser = null, doclets; + var parser = require('jsdoc/src/parser'); + var srcParser = null; + var doclets; + var sourcePaths = env.opts._.slice(0); beforeEach(function() { - env.opts._ = [__dirname + '/test/fixtures/modules/']; + env.opts._ = [__dirname + '/test/fixtures/modules/data/']; srcParser = new parser.Parser(); require('jsdoc/src/handlers').attachTo(srcParser); }); + afterEach(function() { + env.opts._ = sourcePaths; + }); + it("should create a name from the file path when no documented module name exists", function() { doclets = srcParser.parse(__dirname + '/test/fixtures/modules/data/mod-1.js'); expect(doclets.length).toBeGreaterThan(1); diff --git a/test/specs/jsdoc/util/templateHelper.js b/test/specs/jsdoc/util/templateHelper.js index 3ae04851..c3266177 100644 --- a/test/specs/jsdoc/util/templateHelper.js +++ b/test/specs/jsdoc/util/templateHelper.js @@ -1379,20 +1379,53 @@ describe("jsdoc/util/templateHelper", function() { it('should create a url for a doclet with the wrong kind (caused by incorrect JSDoc tags', function() { var moduleDoclet = { kind: 'module', - longname: 'module:bar', - name: 'module:bar' + longname: 'module:baz', + name: 'module:baz' }; var badDoclet = { kind: 'member', - longname: 'module:bar', - name: 'module:bar' + longname: 'module:baz', + name: 'module:baz' }; var moduleDocletUrl = helper.createLink(moduleDoclet); var badDocletUrl = helper.createLink(badDoclet); - expect(moduleDocletUrl).toBe('module-bar.html'); - expect(badDocletUrl).toBe('module-bar.html#module:bar'); + expect(moduleDocletUrl).toBe('module-baz.html'); + expect(badDocletUrl).toBe('module-baz.html'); + }); + + it('should create a url for a function that is a member of a doclet with the wrong kind', function() { + var badModuleDoclet = { + kind: 'member', + longname: 'module:qux', + name: 'module:qux' + }; + var memberDoclet = { + kind: 'function', + name: 'frozzle', + memberof: 'module:qux', + scope: 'instance', + longname: 'module:qux#frozzle' + }; + + var badModuleDocletUrl = helper.createLink(badModuleDoclet); + var memberDocletUrl = helper.createLink(memberDoclet); + + expect(badModuleDocletUrl).toBe('module-qux.html'); + expect(memberDocletUrl).toBe('module-qux.html#frozzle'); + }); + + it('should create a url for an empty package definition', function() { + var packageDoclet = { + kind: 'package', + name: undefined, + longname: 'package:undefined' + }; + + var packageDocletUrl = helper.createLink(packageDoclet); + + expect(packageDocletUrl).toBe('global.html'); }); }); diff --git a/test/specs/tags/overviewtag.js b/test/specs/tags/overviewtag.js index 6087be5b..f78761fc 100644 --- a/test/specs/tags/overviewtag.js +++ b/test/specs/tags/overviewtag.js @@ -1,18 +1,28 @@ -/*global describe: true, env: true, expect: true, it: true */ +/*global beforeEach: true, afterEach: true, describe: true, env: true, expect: true, it: true */ describe("@overview tag", function() { - var parser = require('jsdoc/src/parser'), - srcParser = new parser.Parser(), - doclets; + var parser = require('jsdoc/src/parser'); + var srcParser = null; + var doclets; + var sourcePaths = env.opts._.slice(0); - require('jsdoc/src/handlers').attachTo(srcParser); - doclets = srcParser.parse(__dirname + '/test/fixtures/file.js'); + beforeEach(function() { + env.opts._ = [__dirname + '/test/fixtures/']; + srcParser = new parser.Parser(); + require('jsdoc/src/handlers').attachTo(srcParser); + }); + + afterEach(function() { + env.opts._ = sourcePaths; + }); it('When a file overview tag appears in a doclet, the name of the doclet should contain the path to the file.', function() { + doclets = srcParser.parse(__dirname + '/test/fixtures/file.js'); expect(doclets[0].name).toMatch(/^(fixtures[\/\\]file\.js)$/); }); it("The name and longname should be equal", function() { + doclets = srcParser.parse(__dirname + '/test/fixtures/file.js'); expect(doclets[0].name).toBe(doclets[0].longname); }); }); From 4e3cf294b156a0acdd64360de2fa69324b65f92f Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sat, 5 Oct 2013 08:59:34 -0700 Subject: [PATCH 34/40] 3.2.1 changelog; bump revision Conflicts: package.json --- changes.md | 32 ++++++++++++++++++++++++++++++++ package.json | 5 +++++ 2 files changed, 37 insertions(+) diff --git a/changes.md b/changes.md index ce003292..4fc58726 100644 --- a/changes.md +++ b/changes.md @@ -3,6 +3,38 @@ This file describes notable changes in each version of JSDoc 3. To download a specific version of JSDoc 3, see [GitHub's tags page](https://github.com/jsdoc3/jsdoc/tags). +## 3.2.1 (October 2013) + +### Enhancements ++ JSDoc's parser now fires a `processingComplete` event after JSDoc has completed all post-processing of the parse results. This event has a `doclets` property containing an array of doclets. (#421) ++ When JSDoc's parser fires a `parseComplete` event, the event now includes a `doclets` property containing an array of doclets. (#431) ++ You can now use relative paths in the JSDoc configuration file's `source.exclude` option. Relative paths will be resolved relative to the current working directory. (#405) ++ If a symbol uses the `@default` tag, and its default value is an object literal, this value is now stored as a string, and the doclet will have a `defaultvaluetype` property containing the string `object`. This change enables templates to show the default value with appropriate syntax highlighting. (#419) ++ Inline `{@link}` tags can now contain newlines. (#441) + +### Bug fixes ++ Inherited symbols now indicate that they were inherited from the ancestor that defined the symbol, rather than the direct parent. (#422) ++ If the first line of a JavaScript file contains a hashbang (for example, `#!/usr/bin/env node`), the hashbang is now ignored when the file is parsed. (#499) ++ Resolved a crash when a JavaScript file contains a [JavaScript 1.8](https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/1.8) keyword, such as `let`. (#477) ++ The type expression `function[]` is now parsed correctly. (#493) ++ If a module is tagged incorrectly, the module's output file now has a valid filename. (#440, #458) ++ For tags that accept names, such as `@module` and `@param`, if a hyphen is used to separate the name and description, the hyphen must appear on the same line as the name. This change prevents a Markdown bullet on the followng line from being interpreted as a separator. (#459) ++ When lenient mode is enabled, a `@param` tag with an invalid type expression no longer causes a crash. (#448) ++ The `@requires` tag can now contain an inline tag in its tag text. (#486) ++ The `@returns` tag can now contain inline tags even if a type is not specified. (#444) ++ When lenient mode is enabled, a `@returns` tag with no value no longer causes a crash. (#451) ++ The `@type` tag now works correctly with type expressions that span multiple lines. (#427) ++ If a string contains inline `{@link}` tags preceded by bracketed link text (for example, `[test]{@link Test#test}`), HTML links are now generated correctly even if the string contains other bracketed text. (#470) ++ On POSIX systems, if you run JSDoc using a symlink to the startup script, JSDoc now works correctly. (#492) + +### Default template ++ Pretty-printed source files are now generated by default. To disable this feature, add the property `templates.default.outputSourceFiles: false` to your `conf.json` file. (#454) ++ Links to a specific line in a source file now work correctly. (#475) ++ Pretty-printed source files are now generated using the encoding specified in the `-e/--encoding` option. (#496) ++ If a `@default` tag is added to a symbol whose default value is an object, the value is now displayed in the output file. (#419) ++ Output files now identify symbols as "abstract" rather than "virtual." (#432) + + ## 3.2.0 (May 2013) ### Major changes diff --git a/package.json b/package.json index dae1bf67..d9acc324 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,12 @@ { "name": "jsdoc", +<<<<<<< HEAD "version": "3.3.0-dev", "revision": "1379480242214", +======= + "version": "3.2.1", + "revision": "1380988759465", +>>>>>>> e8753db... 3.2.1 changelog; bump revision "description": "An API documentation generator for JavaScript.", "keywords": [ "documentation", "javascript" ], "licenses": [ From 40587eb745a067d582a5f2c76ef2f03562de4372 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sat, 5 Oct 2013 09:05:13 -0700 Subject: [PATCH 35/40] update package.json --- Jake/templates/package.json.tmpl | 12 +++++------- package.json | 7 +------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/Jake/templates/package.json.tmpl b/Jake/templates/package.json.tmpl index d5159473..b6c9d2e3 100644 --- a/Jake/templates/package.json.tmpl +++ b/Jake/templates/package.json.tmpl @@ -10,15 +10,13 @@ "url": "http://www.apache.org/licenses/LICENSE-2.0" } ], - "repositories": [ - { - "type": "git", - "url": "https://github.com/jsdoc3/jsdoc" - } - ], + "repository": { + "type": "git", + "url": "https://github.com/jsdoc3/jsdoc" + }, "dependencies": { "async": "0.1.22", - "catharsis": "0.5.6", + "catharsis": "0.6.0", "crypto-browserify": "git+https://github.com/dominictarr/crypto-browserify.git#95c5d505", "js2xmlparser": "0.1.0", "jshint": "0.9.1", diff --git a/package.json b/package.json index d9acc324..176bb1a0 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,7 @@ { "name": "jsdoc", -<<<<<<< HEAD "version": "3.3.0-dev", - "revision": "1379480242214", -======= - "version": "3.2.1", - "revision": "1380988759465", ->>>>>>> e8753db... 3.2.1 changelog; bump revision + "revision": "1380989053246", "description": "An API documentation generator for JavaScript.", "keywords": [ "documentation", "javascript" ], "licenses": [ From 68fb9cab6a09e2c06ebe73221ba5ab90cd894094 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Mon, 7 Oct 2013 20:25:52 -0700 Subject: [PATCH 36/40] Revert "clean up redundant code" This reverts commit d78b10a890b6c99078694029175ed3a0ea657fdf. --- lib/jsdoc/src/parser.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/jsdoc/src/parser.js b/lib/jsdoc/src/parser.js index cd4d6715..f22797e5 100644 --- a/lib/jsdoc/src/parser.js +++ b/lib/jsdoc/src/parser.js @@ -387,9 +387,11 @@ exports.Parser.prototype._makeEvent = function(node, extras) { } // make sure the result includes extras that don't have default values - Object.keys(extras).forEach(function(prop) { - result[prop] = extras[prop]; - }); + for (var prop in extras) { + if ( hasOwnProp.call(extras, prop) ) { + result[prop] = extras[prop]; + } + } return result; }; @@ -608,8 +610,12 @@ exports.Parser.prototype.astnodeToMemberof = function(node) { while(scope.enclosingFunction) { id = 'astnode' + scope.enclosingFunction.hashCode(); doclet = this.refs[id]; - if ( doclet && doclet.meta.vars && hasOwnProp.call(doclet.meta.vars, basename) ) { - return [doclet.meta.vars[basename], basename]; + if (doclet && doclet.meta.vars && basename in doclet.meta.vars) { + alias = hasOwnProp.call(doclet.meta.vars, basename) ? + doclet.meta.vars[basename] : false; + if (alias !== false) { + return [alias, basename]; + } } // move up scope = scope.enclosingFunction; @@ -617,7 +623,10 @@ exports.Parser.prototype.astnodeToMemberof = function(node) { // First check to see if we have a global scope alias doclet = this.refs.__global__; if ( doclet && doclet.meta.vars && hasOwnProp.call(doclet.meta.vars, basename) ) { - return [doclet.meta.vars[basename], basename]; + alias = doclet.meta.vars[basename]; + if (alias !== false) { + return [alias, basename]; + } } id = 'astnode' + node.parent.hashCode(); From 30d9692c1704d3a58d14a8943a3c91bc0e91da70 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Mon, 14 Oct 2013 09:32:20 -0700 Subject: [PATCH 37/40] update catharsis to fix issues with repeatable types (#502) --- Jake/templates/package.json.tmpl | 2 +- node_modules/catharsis/catharsis.js | 2 +- node_modules/catharsis/lib/parser.js | 6 +-- node_modules/catharsis/lib/stringify.js | 57 ++++++++++++++----------- node_modules/catharsis/package.json | 18 +++++--- package.json | 4 +- 6 files changed, 52 insertions(+), 37 deletions(-) diff --git a/Jake/templates/package.json.tmpl b/Jake/templates/package.json.tmpl index b6c9d2e3..3b5a5c77 100644 --- a/Jake/templates/package.json.tmpl +++ b/Jake/templates/package.json.tmpl @@ -16,7 +16,7 @@ }, "dependencies": { "async": "0.1.22", - "catharsis": "0.6.0", + "catharsis": "0.7.0", "crypto-browserify": "git+https://github.com/dominictarr/crypto-browserify.git#95c5d505", "js2xmlparser": "0.1.0", "jshint": "0.9.1", diff --git a/node_modules/catharsis/catharsis.js b/node_modules/catharsis/catharsis.js index a9ac7c35..bb28e1d1 100644 --- a/node_modules/catharsis/catharsis.js +++ b/node_modules/catharsis/catharsis.js @@ -56,7 +56,7 @@ function cachedParse(expr, options) { var cache = getTypeExpressionCache(options); var parsedType; - if (cache && cache[expr]) { + if (cache && Object.prototype.hasOwnProperty.call(cache, expr)) { return cache[expr]; } else { parsedType = parse(expr, options); diff --git a/node_modules/catharsis/lib/parser.js b/node_modules/catharsis/lib/parser.js index 2ee8d967..5bdd45e4 100644 --- a/node_modules/catharsis/lib/parser.js +++ b/node_modules/catharsis/lib/parser.js @@ -1,3 +1,3 @@ -module.exports=function(){function peg$subclass(child,parent){function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor}function SyntaxError(expected,found,offset,line,column){function buildMessage(expected,found){function stringEscape(s){function hex(ch){return ch.charCodeAt(0).toString(16).toUpperCase()}return s.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\x08/g,"\\b").replace(/\t/g,"\\t").replace(/\n/g,"\\n").replace(/\f/g,"\\f").replace(/\r/g,"\\r").replace(/[\x00-\x07\x0B\x0E\x0F]/g,function(ch){return"\\x0"+hex(ch)}).replace(/[\x10-\x1F\x80-\xFF]/g,function(ch){return"\\x"+hex(ch)}).replace(/[\u0180-\u0FFF]/g,function(ch){return"\\u0"+hex(ch)}).replace(/[\u1080-\uFFFF]/g,function(ch){return"\\u"+hex(ch)})}var expectedDesc,foundDesc;switch(expected.length){case 0:expectedDesc="end of input";break;case 1:expectedDesc=expected[0];break;default:expectedDesc=expected.slice(0,-1).join(", ")+" or "+expected[expected.length-1]}foundDesc=found?'"'+stringEscape(found)+'"':"end of input";return"Expected "+expectedDesc+" but "+foundDesc+" found."}this.expected=expected;this.found=found;this.offset=offset;this.line=line;this.column=column;this.name="SyntaxError";this.message=buildMessage(expected,found)}peg$subclass(SyntaxError,Error);function parse(input){var options=arguments.length>1?arguments[1]:{},peg$startRuleFunctions={TypeExpression:peg$parseTypeExpression},peg$startRuleFunction=peg$parseTypeExpression,peg$c0=null,peg$c1="",peg$c2=function(unk){return unk},peg$c3="?",peg$c4='"?"',peg$c5="!",peg$c6='"!"',peg$c7=function(prefix,expr,postfix){if(prefix!==""&&postfix!==""){return null}[prefix,postfix].forEach(function(modifier){if(modifier!==""){expr.nullable=modifier==="?"?true:false}});return expr},peg$c8=function(repeat,lit,opt){var result=lit;if(opt.optional){result.optional=true}if(repeat.repeatable){result.repeatable=true}return result},peg$c9="*",peg$c10='"*"',peg$c11=function(){return{type:Types.AllLiteral}},peg$c12=function(){return{type:Types.NullLiteral}},peg$c13=function(){return{type:Types.UndefinedLiteral}},peg$c14="...",peg$c15='"..."',peg$c16=function(){return{repeatable:true}},peg$c17="=",peg$c18='"="',peg$c19=function(){return{optional:true}},peg$c20="[]",peg$c21='"[]"',peg$c22=function(name){var result;if(!options.jsdoc){return null}result={type:Types.TypeApplication,expression:{type:Types.NameExpression,name:"Array"},applications:[name]};result.applications[0].type=Types.NameExpression;return result},peg$c23=function(exp,appl,opt){var result={};var nameExp={type:Types.NameExpression,name:exp.name};if(appl.length){result.type=Types.TypeApplication;result.expression=nameExp;result.applications=appl}else{result=nameExp}if(exp.repeatable){result.repeatable=true}if(opt.optional){result.optional=true}return result},peg$c24=function(name){if(!options.jsdoc){return null}return name},peg$c25=function(exp,opt){var result={type:Types.NameExpression,name:exp.name,reservedWord:true};if(exp.repeatable){result.repeatable=true}if(opt.optional){result.optional=true}return result},peg$c26=".",peg$c27='"."',peg$c28="<",peg$c29='"<"',peg$c30=">",peg$c31='">"',peg$c32=function(sep,l){if(sep===""&&!options.jsdoc){return null}return l},peg$c33=[],peg$c34=",",peg$c35='","',peg$c36=function(expr,list){var result=[expr];for(var i=0,l=list.length;ipos){peg$cachedPos=0;peg$cachedPosDetails={line:1,column:1,seenCR:false}}peg$cachedPos=pos;advance(peg$cachedPosDetails,peg$cachedPos)}return peg$cachedPosDetails}function peg$fail(expected){if(peg$currPospeg$maxFailPos){peg$maxFailPos=peg$currPos;peg$maxFailExpected=[]}peg$maxFailExpected.push(expected)}function peg$cleanupExpected(expected){var i=0;expected.sort();while(ipeg$currPos){s0=input.charAt(peg$currPos);peg$currPos++}else{s0=null;if(peg$silentFails===0){peg$fail(peg$c274)}}return s0}function peg$parseHexEscapeSequence(){var s0,s1,s2,s3,s4,s5;s0=peg$currPos;if(input.charCodeAt(peg$currPos)===120){s1=peg$c272;peg$currPos++}else{s1=null;if(peg$silentFails===0){peg$fail(peg$c273)}}if(s1!==null){s2=peg$currPos;s3=peg$currPos;s4=peg$parseHexDigit();if(s4!==null){s5=peg$parseHexDigit();if(s5!==null){s4=[s4,s5];s3=s4}else{peg$currPos=s3;s3=peg$c0}}else{peg$currPos=s3;s3=peg$c0}if(s3!==null){s3=input.substring(s2,peg$currPos)}s2=s3;if(s2!==null){peg$reportedPos=s0;s1=peg$c219(s2);if(s1===null){peg$currPos=s0;s0=s1}else{s0=s1}}else{peg$currPos=s0;s0=peg$c0}}else{peg$currPos=s0;s0=peg$c0}return s0}function peg$parseLineContinuation(){var s0,s1,s2;s0=peg$currPos;if(input.charCodeAt(peg$currPos)===92){s1=peg$c215;peg$currPos++}else{s1=null;if(peg$silentFails===0){peg$fail(peg$c216)}}if(s1!==null){s2=peg$parseLineTerminatorSequence();if(s2!==null){peg$reportedPos=s0;s1=peg$c275(s2);if(s1===null){peg$currPos=s0;s0=s1}else{s0=s1}}else{peg$currPos=s0;s0=peg$c0}}else{peg$currPos=s0;s0=peg$c0}return s0}function peg$parse_(){var s0,s1;peg$silentFails++;s0=[];s1=peg$parseWhitespace();while(s1!==null){s0.push(s1);s1=peg$parseWhitespace()}peg$silentFails--;if(s0===null){s1=null;if(peg$silentFails===0){peg$fail(peg$c276)}}return s0}function peg$parse__(){var s0,s1;peg$silentFails++;s0=peg$c1;peg$silentFails--;if(s0===null){s1=null;if(peg$silentFails===0){peg$fail(peg$c277)}}return s0}function peg$parseWhitespace(){var s0;if(peg$c278.test(input.charAt(peg$currPos))){s0=input.charAt(peg$currPos);peg$currPos++}else{s0=null;if(peg$silentFails===0){peg$fail(peg$c279)}}if(s0===null){s0=peg$parseUnicodeZs()}return s0}var Types=require("./types");peg$result=peg$startRuleFunction();if(peg$result!==null&&peg$currPos===input.length){return peg$result}else{peg$cleanupExpected(peg$maxFailExpected);peg$reportedPos=Math.max(peg$currPos,peg$maxFailPos);throw new SyntaxError(peg$maxFailExpected,peg$reportedPos1?arguments[1]:{},peg$startRuleFunctions={TypeExpression:peg$parseTypeExpression},peg$startRuleFunction=peg$parseTypeExpression,peg$c0=null,peg$c1="",peg$c2=function(r,unk){var result=unk;if(r.repeatable){result=repeatable(result)}return result},peg$c3="?",peg$c4='"?"',peg$c5="!",peg$c6='"!"',peg$c7=function(r,prefix,expr){var result=expr;if(r.repeatable){result=repeatable(result)}return nullable(result,prefix)},peg$c8=function(expr,postfix){return nullable(expr,postfix)},peg$c9=function(lit,opt){var result=lit;if(opt.optional){result.optional=true}return result},peg$c10=function(lit){return repeatable(lit)},peg$c11="*",peg$c12='"*"',peg$c13=function(){return{type:Types.AllLiteral}},peg$c14=function(){return{type:Types.NullLiteral}},peg$c15=function(){return{type:Types.UndefinedLiteral}},peg$c16="...",peg$c17='"..."',peg$c18=function(){return{repeatable:true}},peg$c19="=",peg$c20='"="',peg$c21=function(){return{optional:true}},peg$c22="[]",peg$c23='"[]"',peg$c24=function(name){var result;if(!options.jsdoc){return null}result={type:Types.TypeApplication,expression:{type:Types.NameExpression,name:"Array"},applications:[name]};result.applications[0].type=Types.NameExpression;return result},peg$c25=function(exp,appl,opt){var result={};var nameExp={type:Types.NameExpression,name:exp.name};if(appl.length){result.type=Types.TypeApplication;result.expression=nameExp;result.applications=appl}else{result=nameExp}if(opt.optional){result.optional=true}return result},peg$c26=function(name){if(!options.jsdoc){return null}return name},peg$c27=function(t){return repeatable(t)},peg$c28=function(exp,opt){var result={type:Types.NameExpression,name:exp.name,reservedWord:true};if(opt.optional){result.optional=true}return result},peg$c29=".",peg$c30='"."',peg$c31="<",peg$c32='"<"',peg$c33=">",peg$c34='">"',peg$c35=function(sep,l){if(sep===""&&!options.jsdoc){return null}return l},peg$c36=[],peg$c37=",",peg$c38='","',peg$c39=function(expr,list){var result=[expr];for(var i=0,l=list.length;ipos){peg$cachedPos=0;peg$cachedPosDetails={line:1,column:1,seenCR:false}}peg$cachedPos=pos;advance(peg$cachedPosDetails,peg$cachedPos)}return peg$cachedPosDetails}function peg$fail(expected){if(peg$currPospeg$maxFailPos){peg$maxFailPos=peg$currPos;peg$maxFailExpected=[]}peg$maxFailExpected.push(expected)}function peg$cleanupExpected(expected){var i=0;expected.sort();while(ipeg$currPos){s0=input.charAt(peg$currPos);peg$currPos++}else{s0=null;if(peg$silentFails===0){peg$fail(peg$c277)}}return s0}function peg$parseHexEscapeSequence(){var s0,s1,s2,s3,s4,s5;s0=peg$currPos;if(input.charCodeAt(peg$currPos)===120){s1=peg$c275;peg$currPos++}else{s1=null;if(peg$silentFails===0){peg$fail(peg$c276)}}if(s1!==null){s2=peg$currPos;s3=peg$currPos;s4=peg$parseHexDigit();if(s4!==null){s5=peg$parseHexDigit();if(s5!==null){s4=[s4,s5];s3=s4}else{peg$currPos=s3;s3=peg$c0}}else{peg$currPos=s3;s3=peg$c0}if(s3!==null){s3=input.substring(s2,peg$currPos)}s2=s3;if(s2!==null){peg$reportedPos=s0;s1=peg$c223(s2);if(s1===null){peg$currPos=s0;s0=s1}else{s0=s1}}else{peg$currPos=s0;s0=peg$c0}}else{peg$currPos=s0;s0=peg$c0}return s0}function peg$parseLineContinuation(){var s0,s1,s2;s0=peg$currPos;if(input.charCodeAt(peg$currPos)===92){s1=peg$c219;peg$currPos++}else{s1=null;if(peg$silentFails===0){peg$fail(peg$c220)}}if(s1!==null){s2=peg$parseLineTerminatorSequence();if(s2!==null){peg$reportedPos=s0;s1=peg$c261(s2);if(s1===null){peg$currPos=s0;s0=s1}else{s0=s1}}else{peg$currPos=s0;s0=peg$c0}}else{peg$currPos=s0;s0=peg$c0}return s0}function peg$parse_(){var s0,s1;peg$silentFails++;s0=[];s1=peg$parseWhitespace();while(s1!==null){s0.push(s1);s1=peg$parseWhitespace()}peg$silentFails--;if(s0===null){s1=null;if(peg$silentFails===0){peg$fail(peg$c278)}}return s0}function peg$parse__(){var s0,s1;peg$silentFails++;s0=peg$c1;peg$silentFails--;if(s0===null){s1=null;if(peg$silentFails===0){peg$fail(peg$c279)}}return s0}function peg$parseWhitespace(){var s0;if(peg$c280.test(input.charAt(peg$currPos))){s0=input.charAt(peg$currPos);peg$currPos++}else{s0=null;if(peg$silentFails===0){peg$fail(peg$c281)}}if(s0===null){s0=peg$parseUnicodeZs()}return s0}var Types=require("./types");function repeatable(obj){obj.repeatable=true;return obj}function nullable(obj,modifier){if(modifier){obj.nullable=modifier==="?"?true:false}return obj}peg$result=peg$startRuleFunction();if(peg$result!==null&&peg$currPos===input.length){return peg$result}else{peg$cleanupExpected(peg$maxFailExpected);peg$reportedPos=Math.max(peg$currPos,peg$maxFailPos);throw new SyntaxError(peg$maxFailExpected,peg$reportedPos'; } - if (type.repeatable === true) { - return this._formatRepeatable(nameString, typeString); - } else { - return combineNameAndType(nameString, typeString); - } + return combineNameAndType(nameString, typeString); }; Stringifier.prototype._signature = function(type) { var params = []; var param; var result; + var signatureBase; // these go within the signature's parens, in this order var props = [ @@ -245,7 +253,8 @@ Stringifier.prototype._signature = function(type) { } this._inFunctionSignatureParams = false; - result = 'function(' + params.join(', ') + ')'; + signatureBase = 'function(' + params.join(', ') + ')'; + result = this._formatRepeatableAndNullable(type, '', signatureBase); result += this.result(type.result); return result; diff --git a/node_modules/catharsis/package.json b/node_modules/catharsis/package.json index 85e38d27..a4d04923 100644 --- a/node_modules/catharsis/package.json +++ b/node_modules/catharsis/package.json @@ -1,5 +1,5 @@ { - "version": "0.6.0", + "version": "0.7.0", "name": "catharsis", "description": "A JavaScript parser for Google Closure Compiler and JSDoc type expressions.", "author": { @@ -25,8 +25,8 @@ "node": ">= 0.6" }, "scripts": { - "build": "pegjs ./lib/parser.pegjs", - "prepublish": "pegjs ./lib/parser.pegjs; uglifyjs ./lib/parser.js -o ./lib/parser.js", + "build": "./node_modules/.bin/pegjs ./lib/parser.pegjs", + "prepublish": "./node_modules/.bin/pegjs ./lib/parser.pegjs; ./node_modules/.bin/uglifyjs ./lib/parser.js -o ./lib/parser.js", "test": "mocha" }, "licenses": [ @@ -35,7 +35,13 @@ "url": "http://github.com/hegemonic/catharsis/raw/master/LICENSE" } ], - "readme": "ERROR: No README data found!", - "_id": "catharsis@0.6.0", - "_from": "catharsis@0.6.0" + "readme": "# Catharsis #\n\nA JavaScript parser for\n[Google Closure Compiler](https://developers.google.com/closure/compiler/docs/js-for-compiler#types)\nand [JSDoc](https://github.com/jsdoc3/jsdoc) type expressions.\n\nCatharsis is designed to be:\n\n+ **Accurate**. Catharsis is based on a [PEG.js](http://pegjs.majda.cz/) grammar that's designed to\nhandle any valid type expression. It uses a [Mocha](http://visionmedia.github.com/mocha/) test suite\nto verify the parser's accuracy.\n+ **Fast**. Parse results are cached, so the parser is invoked only when necessary.\n+ **Flexible**. Catharsis can convert parse results back into type expressions. In addition, it can\nparse [JSDoc](https://github.com/jsdoc3/jsdoc)-style type expressions.\n\n\n## Example ##\n\n\tvar catharsis = require('catharsis');\n\n var type;\n var jsdocType;\n var parsedType;\n var parsedJsdocType;\n\n // Google Closure Compiler parsing\n try {\n type = '!Object';\n parsedType = catharsis.parse(type);\n console.log('%j', parsedType); // {\"type\":\"NameExpression,\"name\":\"Object\",\"nullable\":false}\n }\n catch(e) {\n console.error('unable to parse %s: %s', type, e);\n }\n\n // JSDoc-style type expressions enabled\n try {\n jsdocType = 'string[]'; // Closure Compiler expects Array.\n parsedJsdocType = catharsis.parse(jsdocType, {jsdoc: true});\n }\n catch (e) {\n console.error('unable to parse %s: %s', jsdocType, e);\n }\n\n console.log(catharsis.stringify(parsedType)); // !Object\n console.log(catharsis.stringify(parsedJsdocType)); // string[]\n console.log(catharsis.stringify(parsedJsdocType, // Array.\n {restringify: true}));\n\n\nSee the `test/specs/` directory for more examples of Catharsis' parse results.\n\n\n## Methods ##\n\n### parse(typeExpression, options) ###\nParse `typeExpression`, and return the parse results. Throws an error if the type expression cannot\nbe parsed.\n\nWhen called without options, Catharsis attempts to parse type expressions in the same way as\nClosure Compiler. When the `jsdoc` option is enabled, Catharsis can also parse several kinds of\ntype expressions that are permitted in [JSDoc](https://github.com/jsdoc3/jsdoc):\n\n+ The string `function` is treated as a function type with no parameters.\n+ In a function type with repeatable parameters, the names of repeatable parameters are not required\nto be enclosed in square brackets (for example, `function(...foo)` is allowed).\n+ The period may be omitted from type applications. For example, `Array.` and\n`Array` will be parsed in the same way.\n+ You may append `[]` to a name expression (for example, `string[]`) to interpret it as a type\napplication with the expression `Array` (for example, `Array.`).\n+ Name expressions may contain the characters `#`, `~`, `:`, and `/`.\n+ Name expressions may contain a suffix that is similar to a function signature (for example,\n`MyClass(foo, bar)`).\n+ Name expressions may contain a reserved word.\n+ Record types may use types other than name expressions for keys.\n\n#### Parameters ####\n+ `type`: A string containing a Closure Compiler type expression.\n+ `options`: Options for parsing the type expression.\n + `options.jsdoc`: Specifies whether to enable parsing of JSDoc-style type expressions. Defaults\n to `false`.\n + `options.useCache`: Specifies whether to use the cache of parsed types. Defaults to `true`.\n\n#### Returns ####\nAn object containing the parse results. See the `test/specs/` directory for examples of the parse\nresults for different type expressions.\n\nThe object also includes two non-enumerable properties:\n\n+ `jsdoc`: A boolean indicating whether the type expression was parsed with JSDoc support enabled.\n+ `typeExpression`: A string containing the type expression that was parsed.\n\n### stringify(parsedType, options) ###\nStringify `parsedType`, and return the type expression. If validation is enabled, throws an error if\nthe stringified type expression cannot be parsed.\n\n#### Parameters ####\n+ `parsedType`: An object containing a parsed Closure Compiler type expression.\n+ `options`: Options for stringifying the parse results.\n + `options.cssClass`: A CSS class to add to HTML links. Used only if `options.links` is\n provided. By default, no CSS class is added.\n + `options.htmlSafe`: Specifies whether to return an HTML-safe string that replaces left angle\n brackets (`<`) with the corresponding entity (`<`). **Note**: Characters in name expressions\n are not escaped.\n + `options.links`: An object whose keys are name expressions and whose values are URIs. If a\n name expression matches a key in `options.links`, the name expression will be wrapped in an\n HTML `` tag that links to the URI. If `options.cssClass` is specified, the `` tag will\n include a `class` attribute. **Note**: When using this option, parsed types are always\n restringified, and the resulting string is not cached.\n + `options.restringify`: Forces Catharsis to restringify the parsed type. If this option is not\n specified, and the parsed type object includes a `typeExpression` property, Catharsis will\n return the `typeExpression` property without modification when possible. Defaults to `false`.\n + `options.useCache`: Specifies whether to use the cache of stringified parse results. Defaults\n to `true`.\n + `options.validate`: Specifies whether to validate the stringified parse results by attempting\n to parse them as a type expression. If the stringified results are not parsable by default, you\n must also provide the appropriate options to pass to the `parse()` method. Defaults to `false`.\n\n#### Returns ####\nA string containing the type expression.\n\n\n## Installation ##\n\nWith [npm](http://npmjs.org):\n\n npm install catharsis\n\nOr without:\n\n git clone git://github.com/hegemonic/catharsis.git\n\n\n## Roadmap and known issues ##\n\nTake a look at the [issue tracker](https://github.com/hegemonic/catharsis/issues) to see what's in\nstore for Catharsis.\n\nBug reports, feature requests, and pull requests are always welcome! If you're working on a large\npull request, please contact me in advance so I can help things go smoothly.\n\n**Note**: The parse tree's format should not be considered final until Catharsis reaches version\n1.0. I'll do my best to provide release notes for any changes.\n\n\n## Changelog ##\n\n+ 0.7.0 (October 2013):\n + Repeatable type expressions other than name expressions (for example, `...function()`) are now\n parsed and stringified correctly.\n + Type expressions that are both repeatable and either nullable or non-nullable (for example,\n `...!number`) are now parsed and stringified correctly.\n + Name expressions are now parsed correctly when they match a property name in an object\n instance (for example, `constructor`).\n+ 0.6.0 (September 2013): Added support for the type expression `function[]` when JSDoc-style type\nexpressions are enabled.\n+ 0.5.6 (April 2013):\n + For consistency with Google Closure Library, parentheses are no longer required around type\n unions. (In previous versions, the parentheses could be omitted when JSDoc support was enabled.)\n + For consistency with Google Closure Library, you can now use postfix notation for the `?`\n (nullable) and `!` (non-nullable) modifiers. For example, `?string` and `string?` are now\n treated as equivalent.\n + String literals and numeric literals are now allowed as property names within name\n expressions. For example, the name expression `Foo.\"bar\"` is now parsed correctly.\n+ 0.5.5 (April 2013): Corrected a parsing issue with name expressions that end with a value enclosed\nin parentheses.\n+ 0.5.4 (April 2013):\n + Repeatable literals (for example, `...*`) are now parsed correctly.\n + When JSDoc-style type expressions are enabled, a name expression can now contain a value\n enclosed in parentheses at the end of the name expression (for example, `MyClass(2)`).\n+ 0.5.3 (March 2013): The `parse()` method now correctly parses name expressions that contain\nhyphens.\n+ 0.5.2 (March 2013): The `parse()` method now correctly parses function types when JSDoc-style type\nexpressions are enabled.\n+ 0.5.1 (March 2013): Newlines and extra spaces are now removed from type expressions before they\nare parsed.\n+ 0.5.0 (March 2013):\n + The `parse()` method's `lenient` option has been renamed to `jsdoc`. **Note**: This change is\n not backwards-compatible with previous versions.\n + The `stringify()` method now accepts `cssClass` and `links` options, which you can use to\n add HTML links to a type expression.\n+ 0.4.3 (March 2013):\n + The `stringify()` method no longer caches HTML-safe type expressions as if they were normal\n type expressions.\n + The `stringify()` method's options parameter may now include an `options.restringify`\n property, and the behavior of the `options.useCache` property has changed.\n+ 0.4.2 (March 2013):\n + When lenient parsing is enabled, name expressions can now contain the characters `:` and `/`.\n + When lenient parsing is enabled, a name expression followed by `[]` (for example, `string[]`)\n will be interpreted as a type application with the expression `Array` (for example,\n `Array.`).\n+ 0.4.1 (March 2013):\n + The `parse()` and `stringify()` methods now honor all of the specified options.\n + When lenient parsing is enabled, name expressions can now contain a reserved word.\n+ 0.4.0 (March 2013):\n + Catharsis now supports a lenient parsing option that can parse several kinds of malformed type\n expressions. See the documentation for details.\n + The objects containing parse results are now frozen.\n + The objects containing parse results now have two non-enumerable properties:\n + `lenient`: A boolean indicating whether the type expression was parsed in lenient mode.\n + `typeExpression`: A string containing the original type expression.\n + The `stringify()` method now honors the `useCache` option. If a parsed type includes a\n `typeExpression` property, and `useCache` is not set to `false`, the stringified type will be\n identical to the original type expression.\n+ 0.3.1 (March 2013): Type expressions that begin with a reserved word, such as `integer`, are now\nparsed correctly.\n+ 0.3.0 (March 2013):\n + The `parse()` and `stringify()` methods are now synchronous, and the `parseSync()` and\n `stringifySync()` methods have been removed. **Note**: This change is not backwards-compatible\n with previous versions.\n + The parse results now use a significantly different format from previous versions. The new\n format is more expressive and is similar, but not identical, to the format used by the\n [doctrine](https://github.com/Constellation/doctrine) parser. **Note**: This change is not\n backwards-compatible with previous versions.\n + Name expressions that contain a reserved word now include a `reservedWord: true` property.\n + Union types that are optional or nullable, or that can be passed a variable number of times,\n are now parsed and stringified correctly.\n + Optional function types and record types are now parsed and stringified correctly.\n + Function types now longer include `new` or `this` properties unless the properties are defined\n in the type expression. In addition, the `new` and `this` properties can now use any type\n expression.\n + In record types, the key for a field type can now use any type expression.\n + Standalone single-character literals, such as ALL (`*`), are now parsed and stringified\n correctly.\n + `null` and `undefined` literals with additional properties, such as `repeatable`, are now\n stringified correctly.\n+ 0.2.0 (November 2012):\n + Added `stringify()` and `stringifySync()` methods, which convert a parsed type to a type\n expression.\n + Simplified the parse results for function signatures. **Note**: This change is not\n backwards-compatible with previous versions.\n + Corrected minor errors in README.md.\n+ 0.1.1 (November 2012): Added `opts` argument to `parse()` and `parseSync()` methods. **Note**: The\nchange to `parse()` is not backwards-compatible with previous versions.\n+ 0.1.0 (November 2012): Initial release.\n\n## License ##\n\n[MIT license](https://github.com/hegemonic/catharsis/blob/master/LICENSE).\n", + "readmeFilename": "README.md", + "homepage": "https://github.com/hegemonic/catharsis", + "_id": "catharsis@0.7.0", + "dist": { + "shasum": "37b3854a9a79b71bd7750279913313b160978c6e" + }, + "_from": "catharsis@0.7.0", + "_resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.7.0.tgz" } diff --git a/package.json b/package.json index 176bb1a0..499204c5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jsdoc", "version": "3.3.0-dev", - "revision": "1380989053246", + "revision": "1381768141793", "description": "An API documentation generator for JavaScript.", "keywords": [ "documentation", "javascript" ], "licenses": [ @@ -16,7 +16,7 @@ }, "dependencies": { "async": "0.1.22", - "catharsis": "0.6.0", + "catharsis": "0.7.0", "crypto-browserify": "git+https://github.com/dominictarr/crypto-browserify.git#95c5d505", "js2xmlparser": "0.1.0", "jshint": "0.9.1", From ca1048d84cb46d4ccbda0339b0f57a90a4986500 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Mon, 14 Oct 2013 22:33:18 -0700 Subject: [PATCH 38/40] clarify option descriptions (#506) --- lib/jsdoc/opts/args.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jsdoc/opts/args.js b/lib/jsdoc/opts/args.js index ddc3e620..994502fa 100644 --- a/lib/jsdoc/opts/args.js +++ b/lib/jsdoc/opts/args.js @@ -63,8 +63,8 @@ function parseQuery(str) { return result; } -argParser.addOption('t', 'template', true, 'The name of the template to use. Default: the "default" template'); -argParser.addOption('c', 'configure', true, 'The path to the configuration file. Default: jsdoc __dirname + /conf.json'); +argParser.addOption('t', 'template', true, 'The path to the template to use. Default: path/to/jsdoc/templates/default'); +argParser.addOption('c', 'configure', true, 'The path to the configuration file. Default: path/to/jsdoc/conf.json'); argParser.addOption('e', 'encoding', true, 'Assume this encoding when reading all source files. Default: utf8'); argParser.addOption('T', 'test', false, 'Run all tests and quit.'); argParser.addOption('d', 'destination', true, 'The path to the output folder. Use "console" to dump data to the console. Default: ./out/'); From 5fb38393898df6d3a65f11736ebdf53e3307ba0e Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Mon, 14 Oct 2013 23:16:40 -0700 Subject: [PATCH 39/40] don't rename a function param called "prototype" (#505); minor cleanup --- lib/jsdoc/name.js | 9 +++++++-- test/specs/documentation/alias.js | 7 ++++--- test/specs/jsdoc/name.js | 22 +++++++++++++++++----- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/jsdoc/name.js b/lib/jsdoc/name.js index 5157aff1..ccba3dcb 100644 --- a/lib/jsdoc/name.js +++ b/lib/jsdoc/name.js @@ -20,12 +20,17 @@ var DEFAULT_SCOPE = 'static'; @param {module:jsdoc/doclet.Doclet} doclet */ exports.resolve = function(doclet) { - var name = doclet.name, + var name = doclet.name ? String(doclet.name) : '', memberof = doclet.memberof || '', about = {}, parentDoc; - doclet.name = name = name? (''+name).replace(/(^|\.)prototype\.?/g, '#') : ''; + // change MyClass.prototype.instanceMethod to MyClass#instanceMethod + // (but not in function params, which lack doclet.kind) + if (name && doclet.kind) { + name = name.replace(/(?:^|\.)prototype\.?/g, '#'); + } + doclet.name = name; // member of a var in an outer scope? if (name && !memberof && doclet.meta.code && doclet.meta.code.funcscope) { diff --git a/test/specs/documentation/alias.js b/test/specs/documentation/alias.js index 23508e1d..7846fe68 100644 --- a/test/specs/documentation/alias.js +++ b/test/specs/documentation/alias.js @@ -1,3 +1,4 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ describe("aliases", function() { describe("standard", function() { var docSet = jasmine.getDocSetFromFile('test/fixtures/alias.js'), @@ -26,7 +27,7 @@ describe("aliases", function() { expect(foundMember[0].scope).toEqual('instance'); }); - it('When a symbol is a member of an aliased class, a this-variables is documented as if it were a member that class.', function() { + it('When a symbol is a member of an aliased class, a this-variable is documented as if it were a member that class.', function() { var docSet = jasmine.getDocSetFromFile('test/fixtures/alias3.js'), tcm = docSet.getByLongname('trackr.CookieManager')[0], tcmValue = docSet.getByLongname('trackr.CookieManager#value')[0]; @@ -34,14 +35,14 @@ describe("aliases", function() { expect(tcmValue.memberof).toEqual('trackr.CookieManager'); }); - it('When a symbol is documented as a static member of it\'s scope is "global" and not "static".', function() { + it('When a symbol is documented as a static member of , its scope is "global" and not "static".', function() { var docSet = jasmine.getDocSetFromFile('test/fixtures/aliasglobal.js'), log = docSet.getByLongname('log')[0]; expect(log.scope).toEqual('global'); }); - it('When a symbol is documented as an instance member of class it\'s scope is "instance" and not "static".', function() { + it('When a symbol is documented as an instance member of , its scope is "instance" and not "static".', function() { var docSet = jasmine.getDocSetFromFile('test/fixtures/aliasglobal2.js'), run = docSet.getByLongname('Test#run')[0]; diff --git a/test/specs/jsdoc/name.js b/test/specs/jsdoc/name.js index 21e3d720..80036ef7 100644 --- a/test/specs/jsdoc/name.js +++ b/test/specs/jsdoc/name.js @@ -195,15 +195,16 @@ describe("jsdoc/name", function() { describe("resolve", function() { // TODO: further tests (namespaces, modules, ...) - // @event testing. - var event = '@event'; - var memberOf = '@memberof MyClass'; - var name = '@name A'; function makeDoclet(bits) { var comment = '/**\n' + bits.join('\n') + '\n*/'; return new jsdoc.doclet.Doclet(comment, {}); } + // @event testing. + var event = '@event'; + var memberOf = '@memberof MyClass'; + var name = '@name A'; + // Test the basic @event that is not nested. it('unnested @event gets resolved correctly', function() { var doclet = makeDoclet([event, name]), @@ -290,12 +291,23 @@ describe("jsdoc/name", function() { }); // a double-nested one just in case - it('@event @name MyClass.EventName @memberof somethingelse workse', function() { + it('@event @name MyClass.EventName @memberof somethingelse works', function() { var doclet = makeDoclet([event, '@name MyClass.A', '@memberof MyNamespace']), out = jsdoc.name.resolve(doclet); expect(doclet.name).toEqual('A'); expect(doclet.memberof).toEqual('MyNamespace.MyClass'); expect(doclet.longname).toEqual('MyNamespace.MyClass.event:A'); }); + + // other cases + it('correctly handles a function parameter named "prototype"', function() { + var doclet = makeDoclet(['@name Bar.prototype.baz', '@function', '@memberof module:foo', + '@param {string} qux']); + var out = jsdoc.name.resolve(doclet); + + expect(doclet.name).toBe('baz'); + expect(doclet.memberof).toBe('module:foo.Bar'); + expect(doclet.longname).toBe('module:foo.Bar#baz'); + }); }); }); From 752e871f9848f281c2cf267f4cce7a21a7027f11 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Tue, 15 Oct 2013 08:54:13 -0700 Subject: [PATCH 40/40] prevent undocumented overrides from replacing documented parents (#503) --- lib/jsdoc/augment.js | 3 +- test/fixtures/augmentstag4.js | 21 +++++++ test/specs/tags/augmentstag.js | 106 ++++++++++++++++++++------------- 3 files changed, 88 insertions(+), 42 deletions(-) create mode 100644 test/fixtures/augmentstag4.js diff --git a/lib/jsdoc/augment.js b/lib/jsdoc/augment.js index 9ac7bbbb..614fd314 100644 --- a/lib/jsdoc/augment.js +++ b/lib/jsdoc/augment.js @@ -120,7 +120,8 @@ exports.addInherited = function(docs) { // only build the list of longnames if we'll actually need it if (sorted.length) { longnames = docs.map(function(doc) { - if (doc.longname) { + // keep the ancestor's docs for a symbol if a local override is not documented + if (doc.longname && !doc.undocumented) { return doc.longname; } }); diff --git a/test/fixtures/augmentstag4.js b/test/fixtures/augmentstag4.js new file mode 100644 index 00000000..3ead2752 --- /dev/null +++ b/test/fixtures/augmentstag4.js @@ -0,0 +1,21 @@ +// used to test jsdoc/augments module directly + +/** + * @constructor + * @classdesc Base class + */ +var Base = function() { + /** member */ + this.test1 = "base"; + /** another member */ + this.test2 = null; +}; + +/** + * @constructor + * @extends Base + * @classdesc Extension of Base + */ +var Derived = function() { + this.test1 = "derived"; +}; diff --git a/test/specs/tags/augmentstag.js b/test/specs/tags/augmentstag.js index 98f15a6d..73ad3b4f 100644 --- a/test/specs/tags/augmentstag.js +++ b/test/specs/tags/augmentstag.js @@ -1,65 +1,54 @@ /*global describe: true, expect: true, it: true, jasmine: true */ describe("@augments tag", function() { - /*jshint unused: false */ - var docSet = jasmine.getDocSetFromFile('test/fixtures/augmentstag.js'), - foo = docSet.getByLongname('Foo')[0], - fooProp1 = docSet.getByLongname('Foo#prop1')[0], - fooProp2 = docSet.getByLongname('Foo#prop2')[0], - fooProp3 = docSet.getByLongname('Foo#prop3')[0], - fooMethod1 = docSet.getByLongname('Foo#method1')[0], - fooMethod2 = docSet.getByLongname('Foo#method2')[0], - bar = docSet.getByLongname('Bar')[0], - barProp1 = docSet.getByLongname('Bar#prop1')[0], - barProp2 = docSet.getByLongname('Bar#prop2')[0], - barProp3 = docSet.getByLongname('Bar#prop3')[0], - barMethod1 = docSet.getByLongname('Bar#method1')[0], - barMethod2 = docSet.getByLongname('Bar#method2')[0], - barMethod2All = docSet.getByLongname('Bar#method2'), - bazProp1 = docSet.getByLongname('Baz#prop1')[0], - bazProp1All = docSet.getByLongname('Baz#prop1'), - bazProp2 = docSet.getByLongname('Baz#prop2')[0], - bazProp3 = docSet.getByLongname('Baz#prop3')[0], - bazMethod1 = docSet.getByLongname('Baz#method1')[0], - bazMethod2 = docSet.getByLongname('Baz#method2')[0], - bazMethod3 = docSet.getByLongname('Baz#method3')[0], - - docSet2 = jasmine.getDocSetFromFile('test/fixtures/augmentstag2.js'), - qux = docSet2.getByLongname('Qux')[0], - - docSet3 = jasmine.getDocSetFromFile('test/fixtures/augmentstag3.js'), - FooMethod1 = docSet3.getByLongname('Foo#method1')[0], - BarMethod2 = docSet3.getByLongname('Bar#method2')[0], - FooBarMethod1 = docSet3.getByLongname('FooBar#method1')[0], - FooBarMethod2 = docSet3.getByLongname('FooBar#method2')[0]; + var docSet = jasmine.getDocSetFromFile('test/fixtures/augmentstag.js'); + var docSet2 = jasmine.getDocSetFromFile('test/fixtures/augmentstag2.js'); + var docSet3 = jasmine.getDocSetFromFile('test/fixtures/augmentstag3.js'); + var docSet4 = jasmine.getDocSetFromFile('test/fixtures/augmentstag4.js'); it('When a symbol has an @augments tag, the doclet has a augments property that includes that value.', function() { + var bar = docSet.getByLongname('Bar')[0]; + expect(typeof bar.augments).toBe('object'); expect(bar.augments[0]).toBe('Foo'); }); it('When an object is extended, the original is not modified', function() { + var fooProp3 = docSet.getByLongname('Foo#prop3')[0]; + expect(fooProp3).toBeUndefined(); }); it('When an object is extended, it inherits properties set in parent constructor', function() { + var fooProp1 = docSet.getByLongname('Foo#prop1')[0]; + var barProp1 = docSet.getByLongname('Bar#prop1')[0]; + expect(fooProp1.memberof).toBe("Foo"); expect(barProp1.memberof).toBe("Bar"); expect(barProp1.description).toBe(fooProp1.description); }); it('When an object is extended, it inherits properties set on parent prototype', function() { + var fooProp2 = docSet.getByLongname('Foo#prop2')[0]; + var barProp2 = docSet.getByLongname('Bar#prop2')[0]; + expect(fooProp2.memberof).toBe("Foo"); expect(barProp2.memberof).toBe("Bar"); expect(barProp2.description).toBe(fooProp2.description); }); it('When an object is extended, it inherits methods set on parent prototype', function() { + var fooMethod1 = docSet.getByLongname('Foo#method1')[0]; + var barMethod1 = docSet.getByLongname('Bar#method1')[0]; + expect(fooMethod1.memberof).toBe("Foo"); expect(barMethod1.memberof).toBe("Bar"); expect(barMethod1.description).toBe(fooMethod1.description); }); it('When an object is extended, it may override methods set on parent prototype', function() { + var fooMethod2 = docSet.getByLongname('Foo#method2')[0]; + var barMethod2 = docSet.getByLongname('Bar#method2')[0]; + expect(fooMethod2.memberof).toBe("Foo"); expect(fooMethod2.description).toBe("Second parent method."); expect(barMethod2.memberof).toBe("Bar"); @@ -67,10 +56,19 @@ }); it('When an object is extended, and it overrides an ancestor method, the child does not include docs for the ancestor method.', function() { + var barMethod2All = docSet.getByLongname('Bar#method2'); + expect(barMethod2All.length).toBe(1); }); it('When an object is extended, it inherits properties set on grandparent prototype', function() { + var fooProp1 = docSet.getByLongname('Foo#prop1')[0]; + var barProp1 = docSet.getByLongname('Bar#prop1')[0]; + var bazProp1 = docSet.getByLongname('Baz#prop1')[0]; + var bazMethod1 = docSet.getByLongname('Baz#method1')[0]; + var bazMethod2 = docSet.getByLongname('Baz#method2')[0]; + var bazMethod3 = docSet.getByLongname('Baz#method3')[0]; + expect(fooProp1.memberof).toBe("Foo"); expect(barProp1.memberof).toBe("Bar"); expect(bazProp1.memberof).toBe("Baz"); @@ -81,28 +79,54 @@ }); it('(Grand)children correctly identify the original source of inherited members', function(){ - expect(fooProp1.inherits).not.toBeDefined(); - expect(barProp3.inherits).not.toBeDefined(); - expect(barProp1.inherits).toBe("Foo#prop1"); - expect(bazProp2.inherits).toBe("Foo#prop2"); - expect(bazProp3.inherits).toBe("Bar#prop3"); - expect(bazMethod1.inherits).toBe("Foo#method1"); + var fooProp1 = docSet.getByLongname('Foo#prop1')[0]; + var barProp1 = docSet.getByLongname('Bar#prop1')[0]; + var barProp3 = docSet.getByLongname('Bar#prop3')[0]; + var bazProp2 = docSet.getByLongname('Baz#prop2')[0]; + var bazProp3 = docSet.getByLongname('Baz#prop3')[0]; + var bazMethod1 = docSet.getByLongname('Baz#method1')[0]; + + expect(fooProp1.inherits).not.toBeDefined(); + expect(barProp3.inherits).not.toBeDefined(); + expect(barProp1.inherits).toBe("Foo#prop1"); + expect(bazProp2.inherits).toBe("Foo#prop2"); + expect(bazProp3.inherits).toBe("Bar#prop3"); + expect(bazMethod1.inherits).toBe("Foo#method1"); }); it('When an object is extended, and it overrides an ancestor property, the child does not include docs for the ancestor property.', function() { + var bazProp1All = docSet.getByLongname('Baz#prop1'); + expect(bazProp1All.length).toBe(1); }); it('When a symbol has an @augments tag, and the parent is not documented, the doclet still has an augments property', function() { + var qux = docSet2.getByLongname('Qux')[0]; + expect(typeof qux.augments).toBe('object'); expect(qux.augments[0]).toBe('UndocumentedThing'); }); it('When a symbol @augments multiple parents, it inherits methods from all parents', function() { - expect(FooBarMethod1).toBeDefined(); - expect(FooBarMethod2).toBeDefined(); - expect(FooBarMethod1.description).toBe(FooMethod1.description); - expect(FooBarMethod2.description).toBe(BarMethod2.description); + var fooMethod1 = docSet3.getByLongname('Foo#method1')[0]; + var barMethod2 = docSet3.getByLongname('Bar#method2')[0]; + var fooBarMethod1 = docSet3.getByLongname('FooBar#method1')[0]; + var fooBarMethod2 = docSet3.getByLongname('FooBar#method2')[0]; + + expect(fooBarMethod1).toBeDefined(); + expect(fooBarMethod2).toBeDefined(); + expect(fooBarMethod1.description).toBe(fooMethod1.description); + expect(fooBarMethod2.description).toBe(barMethod2.description); + }); + + it('When a symbol overrides an inherited method without documenting the method, it uses the parent\'s docs', function() { + var baseMethod1 = docSet4.getByLongname('Base#test1')[0]; + var derivedMethod1All = docSet4.getByLongname('Derived#test1'); + var derivedMethod1 = derivedMethod1All[1]; + + expect(derivedMethod1All.length).toBe(2); + expect(derivedMethod1.undocumented).not.toBe(true); + expect(derivedMethod1.description).toBe(baseMethod1.description); }); });