more fixes to ensure that we always generate valid filenames (#440)

This commit is contained in:
Jeff Williams 2013-10-02 22:37:28 -07:00
parent 463dd0a05b
commit 77546a9d52
7 changed files with 132 additions and 45 deletions

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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);

View File

@ -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');
});
});

View File

@ -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);
});
});