From 7fecfa07174a2a7495e8a1ab915e940570fd39ee Mon Sep 17 00:00:00 2001 From: Michael Mathews Date: Sat, 18 Dec 2010 16:26:33 +0000 Subject: [PATCH] Added support for names in modules that are not safe variable names in JS. --- modules/jsdoc/name.js | 66 +++++++++++++++++++++++-------------- modules/jsdoc/parser.js | 2 +- test/runall.js | 1 + test/samples/tag_module.js | 4 +-- test/tests/27_tag_module.js | 47 ++++++++++++++++++++++++++ 5 files changed, 93 insertions(+), 27 deletions(-) create mode 100644 test/tests/27_tag_module.js diff --git a/modules/jsdoc/name.js b/modules/jsdoc/name.js index a5c3eccd..0b85926f 100644 --- a/modules/jsdoc/name.js +++ b/modules/jsdoc/name.js @@ -43,7 +43,9 @@ name = name.replace(/^exports\.(?=.+$)/, currentModule + '.'); } - path = name = name? (''+name).replace(/\.prototype\.?/g, '#') : ''; + name = name? (''+name).replace(/\.prototype\.?/g, '#') : ''; + name = name; + path = quoteUnsafe(name, kind); if (memberof) { // @memberof tag given memberof = memberof.replace(/\.prototype\.?/g, '#'); @@ -61,6 +63,7 @@ doclet.setTag('memberof', memberof); } else { + scope = doclet.tagValue('scope'); if (!scope) { @@ -74,32 +77,37 @@ } } } - else if (kind !== 'file') { // which don't have scopes or memberof - [prefix, scope, name] = exports.shorten(name); - - var taggedScope; - if ( taggedScope = doclet.tagValue('scope') ) { - scope = scopeToPunc[taggedScope]; - if (prefix) { path = prefix + scope + name; } - } - - if (prefix) { - doclet.setTag('memberof', prefix); - - if (name) { - doclet.addTag('scope', puncToScope[scope]); - } - } - else if (name) { - // global symbol? - doclet.addTag('scope', 'global'); - } + else { + if (kind !== 'file' && kind !== 'module') { // which don't have scopes or memberof + [prefix, scope, name] = exports.shorten(name); + + var taggedScope; + if ( taggedScope = doclet.tagValue('scope') ) { + scope = scopeToPunc[taggedScope]; + if (prefix) { path = prefix + scope + name; } + } + + if (prefix) { + doclet.setTag('memberof', prefix); + + if (name) { + doclet.addTag('scope', puncToScope[scope]); + } + } + else if (name) { + // global symbol? + doclet.addTag('scope', 'global'); + } + } + else { + + } } // if name doesn't already have a docspace and needs one if (jsdoc.tagDictionary.lookUp(kind).setsDocletDocspace) { // the namespace should appear in the path but not the name - if ( /^[a-z_$-]+:(\S+)/i.test(name) ) { + if ( /^[a-z_$-\/]+:"?(\S+)"?/i.test(name) ) { name = RegExp.$1; } @@ -110,10 +118,10 @@ if (name) doclet.setTag('name', name); if (!path && memberof && name.indexOf(memberof) !== 0) { - path = memberof + (scope? scope : '') + ns + name; + path = memberof + (scope? scope : '') + ns + name; } else if (ns) { - path = ns + name + path = ns + quoteUnsafe(name, kind) }; if (path) { @@ -123,6 +131,16 @@ return path; } + function quoteUnsafe(name, kind) { // docspaced names may have unsafe characters which need to be quoted by us + if ( (jsdoc.tagDictionary.lookUp(kind).setsDocletDocspace) && /[^$_a-zA-Z0-9]/.test(name) ) { + if (!/^[a-z_$-\/]+:\"/i.test(name)) { + return '"' + name.replace(/\"/g, '"') + '"' + } + } + + return name; + } + /** Given a path like "a.b#c", slice it up into ["a.b", "#", 'c'], representing the memberof, the scope, and the name. diff --git a/modules/jsdoc/parser.js b/modules/jsdoc/parser.js index 17ed7137..03efe243 100644 --- a/modules/jsdoc/parser.js +++ b/modules/jsdoc/parser.js @@ -28,7 +28,7 @@ if ( thisDoclet.hasTag('name') && thisDoclet.hasTag('kind') ) { jsdoc.doclets.addDoclet(thisDoclet); if (thisDoclet.tagValue('kind') === 'module') { - jsdoc.name.setCurrentModule( thisDoclet.tagValue('path') ); + jsdoc.name.setCurrentModule( thisDoclet.tagValue('path') ); } } } diff --git a/test/runall.js b/test/runall.js index a58737d0..55ebf45b 100644 --- a/test/runall.js +++ b/test/runall.js @@ -26,6 +26,7 @@ load(BASEDIR + '/test/tests/23_tag_fires.js'); load(BASEDIR + '/test/tests/24_tag_exception.js'); load(BASEDIR + '/test/tests/25_tag_scope.js'); load(BASEDIR + '/test/tests/26_tag_tag.js'); +load(BASEDIR + '/test/tests/27_tag_module.js'); // see http://visionmedia.github.com/jspec/ JSpec.run({ diff --git a/test/samples/tag_module.js b/test/samples/tag_module.js index 30676017..47a3d233 100644 --- a/test/samples/tag_module.js +++ b/test/samples/tag_module.js @@ -1,7 +1,7 @@ /** - * @module webui/utils + * @module ./webui/utils.strings */ /** @method */ exports.twiddle = function() { -} \ No newline at end of file +} diff --git a/test/tests/27_tag_module.js b/test/tests/27_tag_module.js new file mode 100644 index 00000000..a62ded0a --- /dev/null +++ b/test/tests/27_tag_module.js @@ -0,0 +1,47 @@ +(function() { + var jsdoc, + doclets; + + JSpec.describe('@module', function() { + + before(function() { + // docsets can only be created by parsers + jsdoc = { + tag: require('jsdoc/tag'), + parser: require('jsdoc/parser') + }; + + jsdoc.parser.parseFiles(BASEDIR + 'test/samples/tag_module.js'); + + doclets = jsdoc.parser.result.map(function($){ return $.toObject(); }); + }); + + describe('A doclet that lists a module', function() { + it('should have an `kind` property set to "module"', function() { + var doclet = doclets[0]; + expect(doclet).to(have_property, 'kind'); + expect(doclet.kind).to(eql, 'module'); + }); + + it('should have a `name` property set to the given name"', function() { + var doclet = doclets[0]; + expect(doclet).to(have_property, 'name'); + expect(doclet.name).to(eql, './webui/utils.strings'); + expect(doclet).to(have_property, 'path'); + expect(doclet.path).to(eql, 'module:"./webui/utils.strings"'); + }); + }); + + describe('A function exported by a moduke', function() { + it('should a path that includes the name of the module', function() { + var doclet = doclets[1]; + + expect(doclet).to(have_property, 'name'); + expect(doclet.name).to(eql, 'twiddle'); + expect(doclet).to(have_property, 'path'); + expect(doclet.path).to(eql, 'module:"./webui/utils.strings".twiddle'); + }); + }); + + }); +})(); \ No newline at end of file