From 751bea1b0ae35b22667cf7723561decf39e0c71c Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Fri, 25 Oct 2013 23:30:56 -0700 Subject: [PATCH] fixes for Node.js compatibility (see details) - new Rhino .jar to help find module paths (https://github.com/hegemonic/rhino/commit/31b70105) - make __dirname and process.cwd() provide the current module path; use only env.dirname for JSDoc's home dir; fix callers - get rid of jsdoc/util/include (and update test framework accordingly) - avoid running Rhino/Node.js tests on the wrong runtime - remove support for global 'publish' function, which relied upon jsdoc/util/include - update jsdoc/util/dumper for consistency with Node.js' JSON.stringify() - fix jsdoc/util/runtime to detect Node.js correctly - add Node.js versions of jsdoc/fs and jsdoc/path - other minor cleanup --- jsdoc.js | 55 ++++-------- lib/initialize.js | 40 --------- lib/jsdoc/path.js | 12 +-- lib/jsdoc/src/filter.js | 9 +- lib/jsdoc/src/scanner.js | 8 +- lib/jsdoc/util/dumper.js | 3 + lib/jsdoc/util/include.js | 19 ---- lib/jsdoc/util/runtime.js | 110 +++++++++++++++++------- node/fs.js | 59 +++++++++++++ node/jsdoc/fs.js | 17 ---- node/jsdoc/util/include.js | 8 -- node/path.js | 7 ++ plugins/test/specs/commentConvert.js | 17 ++-- plugins/test/specs/escapeHtml.js | 17 ++-- plugins/test/specs/markdown.js | 21 +++-- plugins/test/specs/overloadHelper.js | 11 ++- plugins/test/specs/railsTemplate.js | 14 +-- plugins/test/specs/shout.js | 17 ++-- plugins/test/specs/sourcetag.js | 17 ++-- plugins/test/specs/verboseOutput.js | 15 ++-- rhino/fs.js | 6 +- rhino/js.jar | Bin 1131550 -> 1135406 bytes rhino/path.js | 10 +++ rhino/rhino-shim.js | 43 ++++++--- templates/default/publish.js | 2 +- test/fixtures/include.js | 3 - test/jasmine-jsdoc.js | 43 ++++----- test/reporter.js | 25 ++---- test/runner.js | 31 ++++--- test/spec-collection.js | 29 ++++--- test/specs/documentation/modules.js | 8 +- test/specs/jsdoc/src/filter.js | 4 +- test/specs/jsdoc/src/handlers.js | 2 +- test/specs/jsdoc/src/parser.js | 8 +- test/specs/jsdoc/src/scanner.js | 4 +- test/specs/jsdoc/tutorial/resolver.js | 25 +++--- test/specs/jsdoc/util/dumper.js | 2 +- test/specs/jsdoc/util/include.js | 36 -------- test/specs/jsdoc/util/templateHelper.js | 6 +- test/specs/jshint/jshint-clean.js | 14 +-- test/specs/tags/overviewtag.js | 7 +- 41 files changed, 414 insertions(+), 370 deletions(-) delete mode 100644 lib/initialize.js delete mode 100644 lib/jsdoc/util/include.js create mode 100644 node/fs.js delete mode 100644 node/jsdoc/fs.js delete mode 100644 node/jsdoc/util/include.js create mode 100644 node/path.js delete mode 100644 test/fixtures/include.js delete mode 100644 test/specs/jsdoc/util/include.js diff --git a/jsdoc.js b/jsdoc.js index d1314f3f..a7adcf67 100644 --- a/jsdoc.js +++ b/jsdoc.js @@ -15,7 +15,7 @@ * @namespace * @name env */ -require('lib/jsdoc/util/global').env = { +require('jsdoc/util/global').env = { /** * Running start and finish times. * @@ -46,14 +46,13 @@ require('lib/jsdoc/util/global').env = { * The absolute path to the base directory of the JSDoc application. * * @private - * @deprecated Use `__dirname` instead. * @type string * @memberof env */ dirname: '.', /** - * The command-line arguments, parsed into a key/value hash. + * The command-line options, parsed into a key/value hash. * * @type Object * @memberof env @@ -78,12 +77,7 @@ require('lib/jsdoc/util/global').env = { }; // initialize the environment for the current JavaScript VM -(function(args) { - var runtime = require('jsdoc/util/runtime').getRuntime(); - // TODO: may need to move this file to support Node.js - require('initialize')[runtime](args); -})( Array.prototype.slice.call(arguments, 0) ); - +require('jsdoc/util/runtime').initialize( Array.prototype.slice.call(arguments, 0) ); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// /** @@ -91,7 +85,7 @@ require('lib/jsdoc/util/global').env = { * @namespace * @name app */ -require('lib/jsdoc/util/global').app = { +require('jsdoc/util/global').app = { jsdoc: { scanner: new (require('jsdoc/src/scanner').Scanner)(), parser: null, @@ -144,9 +138,6 @@ function main() { }, tutorial: { resolver: require('jsdoc/tutorial/resolver') - }, - util: { - include: require('jsdoc/util/include') } }; @@ -157,9 +148,11 @@ function main() { var i; var info; var isFile; + var failCount; var l; var packageDocs; var packageJson; + var runner; var sourceFiles; var template; @@ -170,7 +163,7 @@ function main() { }; // get JSDoc version number - info = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')); + info = JSON.parse(fs.readFileSync(path.join(env.dirname, 'package.json'), 'utf8')); env.version = { number: info.version, revision: new Date(parseInt(info.revision, 10)).toUTCString() @@ -178,7 +171,7 @@ function main() { env.opts = jsdoc.opts.args.parse(env.args); - confPath = env.opts.configure || path.join(__dirname, 'conf.json'); + confPath = env.opts.configure || path.join(env.dirname, 'conf.json'); try { isFile = fs.statSync(confPath).isFile(); } @@ -187,7 +180,7 @@ function main() { } if ( !isFile && !env.opts.configure ) { - confPath = path.join(__dirname, 'conf.json.EXAMPLE'); + confPath = path.join(env.dirname, 'conf.json.EXAMPLE'); } try { @@ -205,8 +198,10 @@ function main() { console.log( jsdoc.opts.args.help() ); process.exit(0); } else if (env.opts.test) { - jsdoc.util.include('test/runner.js'); - process.exit(0); + runner = require( path.join(env.dirname, 'test/runner') ); + runner(function(failCount) { + process.exit(failCount); + }); } else if (env.opts.version) { console.log('JSDoc ' + env.version.number + ' (' + env.version.revision + ')'); process.exit(0); @@ -229,7 +224,7 @@ function main() { } } - if (env.conf.source && env.opts._.length > 0) { // are there any files to scan and parse? + if (env.conf.source && env.opts._.length) { // are there any files to scan and parse? filter = new jsdoc.src.filter.Filter(env.conf.source); env.sourceFiles = sourceFiles = app.jsdoc.scanner.scan(env.opts._, @@ -290,31 +285,17 @@ function main() { ); } else { - // old templates define a global "publish" function, which is deprecated - jsdoc.util.include(env.opts.template + '/publish.js'); - if (publish && typeof publish === 'function') { - console.log( env.opts.template + ' uses a global "publish" function, which is ' + - 'deprecated and may not be supported in future versions. ' + - 'Please update the template to use "exports.publish" instead.' ); - // convert this from a URI back to a path if necessary - env.opts.template = path._uriToPath(env.opts.template); - publish( - taffy(docs), - env.opts, - jsdoc.tutorial.resolver.root - ); - } - else { - throw new Error( env.opts.template + ' does not export a "publish" function.' ); - } + throw new Error(env.opts.template + ' does not export a "publish" function. Global ' + + '"publish" functions are no longer supported.'); } + + process.exit(0); } } try { main(); env.run.finish = new Date(); - process.exit(0); } catch(e) { env.run.finish = new Date(); diff --git a/lib/initialize.js b/lib/initialize.js deleted file mode 100644 index 774b9d76..00000000 --- a/lib/initialize.js +++ /dev/null @@ -1,40 +0,0 @@ -/*global env: true */ -exports.rhino = function(args) { - var myGlobal = require('jsdoc/util/global'); - - // note: mutates args - function getDirname() { - var dirname; - - // Rhino has no native way to get the base dirname of the current script, - // so this information must be manually passed in from the command line. - for (var i = 0; i < args.length; i++) { - if ( /^--dirname(?:=(.+?)(\/|\/\.)?)?$/i.test(args[i]) ) { - if (RegExp.$1) { - dirname = RegExp.$1; // last wins - args.splice(i--, 1); // remove --dirname opt from arguments - } - else { - dirname = args[i + 1]; - args.splice(i--, 2); - } - } - } - - return dirname; - } - - myGlobal.__dirname = env.dirname = getDirname(); - env.args = args; - - require('jsdoc/util/include')(__dirname + '/rhino/rhino-shim.js'); -}; - -exports.nodejs = function(args) { - throw new Error('Node.js is not currently supported!'); - /* - env.dirname = __dirname; - env.args = args; - // TODO: add lib/ to the library paths - */ -}; diff --git a/lib/jsdoc/path.js b/lib/jsdoc/path.js index 29b4cccb..e72d19e0 100644 --- a/lib/jsdoc/path.js +++ b/lib/jsdoc/path.js @@ -1,3 +1,4 @@ +/*global env: true */ /** * Extended version of the standard `path` module. * @module jsdoc/path @@ -16,6 +17,7 @@ function prefixReducer(previousPath, current) { return currentPath; } + // TODO: should probably replace process.cwd() with the CWD before launching JSDoc currentPath = path.resolve( process.cwd(), path.dirname(current) ).split(path.sep) || []; if (previousPath && currentPath.length) { @@ -65,7 +67,7 @@ exports.commonPrefix = function(paths) { return common.join(path.sep); }; -// TODO: do we need this? +// TODO: can we get rid of this? /** * If required by the current VM, convert a path to a URI that meets the operating system's * requirements. Otherwise, return the original path. @@ -74,9 +76,9 @@ exports.commonPrefix = function(paths) { * @param {string} path The path to convert. * @return {string} A URI that meets the operating system's requirements, or the original path. */ -var pathToUri = require( runtime.getModulePath('jsdoc') ).pathToUri; +var pathToUri = require( runtime.getModulePath('path') ).pathToUri; -// TODO: do we need this? if so, any way to stop exporting it? +// TODO: can we get rid of this, or at least stop exporting it? /** * If required by the current VM, convert a URI to a path that meets the operating system's * requirements. Otherwise, assume the "URI" is really a path, and return the original path. @@ -85,7 +87,7 @@ var pathToUri = require( runtime.getModulePath('jsdoc') ).pathToUri; * @param {string} uri The URI to convert. * @return {string} A path that meets the operating system's requirements. */ -exports._uriToPath = require( runtime.getModulePath('jsdoc') ).uriToPath; +exports._uriToPath = require( runtime.getModulePath('path') ).uriToPath; /** * Retrieve the fully qualified path to the requested resource. @@ -120,7 +122,7 @@ exports.getResourcePath = function(filepath, filename) { result = path.resolve(filepath); if ( !pathExists(result) ) { // next, try resolving it relative to the JSDoc directory - result = path.resolve(__dirname, filepath); + result = path.resolve(env.dirname, filepath); if ( !pathExists(result) ) { result = null; } diff --git a/lib/jsdoc/src/filter.js b/lib/jsdoc/src/filter.js index 4bd18055..e1d16c57 100644 --- a/lib/jsdoc/src/filter.js +++ b/lib/jsdoc/src/filter.js @@ -1,3 +1,4 @@ +/*global env: true */ /** @module jsdoc/src/filter @@ -15,11 +16,10 @@ var path = require('jsdoc/path'); @param {string|RegExp} opts.excludePattern */ exports.Filter = function(opts) { - var cwd = process.cwd(); - this.exclude = opts.exclude && Array.isArray(opts.exclude) ? opts.exclude.map(function($) { - return path.resolve(cwd, $); + // TODO: should we replace env.dirname with the CWD before launching JSDoc? + return path.resolve(env.dirname, $); }) : null; this.includePattern = opts.includePattern? @@ -35,7 +35,8 @@ exports.Filter = function(opts) { @returns {boolean} Should the given file be included? */ exports.Filter.prototype.isIncluded = function(filepath) { - filepath = path.resolve(process.cwd(), filepath); + // TODO: should we replace env.dirname with the CWD before launching JSDoc? + filepath = path.resolve(env.dirname, filepath); if ( this.includePattern && !this.includePattern.test(filepath) ) { return false; diff --git a/lib/jsdoc/src/scanner.js b/lib/jsdoc/src/scanner.js index f3b23519..d0850a90 100644 --- a/lib/jsdoc/src/scanner.js +++ b/lib/jsdoc/src/scanner.js @@ -1,3 +1,4 @@ +/*global env: true */ /** @module jsdoc/src/scanner @requires module:fs @@ -26,7 +27,6 @@ exports.Scanner.prototype = Object.create( require('events').EventEmitter.protot exports.Scanner.prototype.scan = function(searchPaths, depth, filter) { var isFile; - var cwd = process.cwd(); var filePaths = []; var self = this; @@ -44,11 +44,13 @@ exports.Scanner.prototype.scan = function(searchPaths, depth, filter) { } if (isFile) { - filePaths.push( path.resolve(cwd, filepath) ); + // TODO: should we replace env.dirname with the CWD before launching JSDoc? + filePaths.push( path.resolve(env.dirname, filepath) ); } else { filePaths = filePaths.concat( fs.ls(filepath, depth).map(function(item) { - return path.resolve(cwd, item); + // TODO: should we replace env.dirname with the CWD before launching JSDoc? + return path.resolve(env.dirname, item); }) ); } }); diff --git a/lib/jsdoc/util/dumper.js b/lib/jsdoc/util/dumper.js index e22279dc..ac838912 100644 --- a/lib/jsdoc/util/dumper.js +++ b/lib/jsdoc/util/dumper.js @@ -65,6 +65,9 @@ function walk(o) { else if ( util.isDate(o) ) { result = ''; } + else if ( util.isError(o) ) { + result = { message: o.message }; + } else if ( isFunction(o) ) { result = ''; } diff --git a/lib/jsdoc/util/include.js b/lib/jsdoc/util/include.js deleted file mode 100644 index a877a1f9..00000000 --- a/lib/jsdoc/util/include.js +++ /dev/null @@ -1,19 +0,0 @@ -var path = require('path'); -var runtime = require('jsdoc/util/runtime'); - -/** - * Read and execute a JavaScript file in global scope. - * @private - * @param {string} filepath The path to the JavaScript file. May contain an absolute path or a - * path relative to env.dirname. - */ -module.exports = function(filepath) { - filepath = path.resolve(__dirname, filepath); - - try { - require( runtime.getModulePath('jsdoc/util/include') )(filepath); - } - catch (e) { - console.log('Cannot include ' + filepath + ': ' + e); - } -}; diff --git a/lib/jsdoc/util/runtime.js b/lib/jsdoc/util/runtime.js index ce3c0786..656af1a8 100644 --- a/lib/jsdoc/util/runtime.js +++ b/lib/jsdoc/util/runtime.js @@ -1,4 +1,4 @@ -/*global Packages: true */ +/*global env: true */ /** * Helper functions to enable JSDoc to run on multiple JavaScript runtimes. * @@ -6,7 +6,8 @@ * @private */ - var os = require('os'); +var myGlobal = require('jsdoc/util/global'); +var os = require('os'); // These strings represent directory names; do not modify them! /** @private */ @@ -43,10 +44,10 @@ function pathToUri(filepath) { * @private */ var runtime = (function() { - if (Packages && typeof Packages === 'object' && - Object.prototype.toString.call(Packages) === '[object JavaPackage]') { + if (myGlobal.Packages && typeof myGlobal.Packages === 'object' && + Object.prototype.toString.call(myGlobal.Packages) === '[object JavaPackage]') { return RHINO; - } else if ( require && require.main && module && (require.main === module) ) { + } else if (require && require.main && module) { return NODE; } else { // unknown runtime @@ -54,32 +55,6 @@ var runtime = (function() { } })(); -/** - * Get the require path for the runtime-specific implementation of a module. - * - * @param {string} partialPath - The partial path to the module. Use the same format as when calling - * `require()`. - * @return {object} The require path for the runtime-specific implementation of the module. - */ -exports.getModulePath = function(partialPath) { - var modulePath = [__dirname, runtime, partialPath].join('/').replace(/ /g, '%20'); - if (os.platform() === 'win32') { - modulePath = pathToUri(modulePath); - } - - return modulePath; -}; - -/** - * Retrieve the identifier for the current JavaScript runtime. - * - * @private - * @return {string} The runtime identifier. - */ -exports.getRuntime = function() { - return runtime; -}; - /** * Check whether Mozilla Rhino is running JSDoc. * @return {boolean} Set to `true` if the current runtime is Mozilla Rhino. @@ -95,3 +70,76 @@ exports.isRhino = function() { exports.isNode = function() { return runtime === NODE; }; + +function initializeRhino(args) { + // note: mutates args + function getDirname() { + var dirname; + + // Rhino has no native way to get the base dirname of the current script, + // so this information must be manually passed in from the command line. + for (var i = 0, l = args.length; i < l; i++) { + if ( /^--dirname(?:=(.+?)(\/|\/\.)?)?$/i.test(args[i]) ) { + if (RegExp.$1) { + dirname = RegExp.$1; // last wins + args.splice(i--, 1); // remove --dirname opt from arguments + } + else { + dirname = args[i + 1]; + args.splice(i--, 2); + } + } + } + + return dirname; + } + + env.dirname = getDirname(); + env.args = args; + + require(env.dirname + '/rhino/rhino-shim.js'); +} + +function initializeNode(args) { + env.dirname = require('path').dirname(process.argv[1]); + env.args = process.argv.slice(2); +} + +exports.initialize = function(args) { + switch(runtime) { + case RHINO: + initializeRhino(args); + break; + case NODE: + initializeNode(); + break; + default: + throw new Error('Unable to initialize the JavaScript runtime!'); + } +}; + +/** + * Retrieve the identifier for the current JavaScript runtime. + * + * @private + * @return {string} The runtime identifier. + */ +exports.getRuntime = function() { + return runtime; +}; + +/** + * Get the require path for the runtime-specific implementation of a module. + * + * @param {string} partialPath - The partial path to the module. Use the same format as when calling + * `require()`. + * @return {object} The require path for the runtime-specific implementation of the module. + */ +exports.getModulePath = function(partialPath) { + var modulePath = [env.dirname, runtime, partialPath].join('/').replace(/ /g, '%20'); + if (os.platform() === 'win32') { + modulePath = pathToUri(modulePath); + } + + return modulePath; +}; diff --git a/node/fs.js b/node/fs.js new file mode 100644 index 00000000..1b47f6f4 --- /dev/null +++ b/node/fs.js @@ -0,0 +1,59 @@ +var fs = require('fs'); +var path = require('path'); +var stream = require('stream'); +var wrench = require('wrench'); + +var toDir = exports.toDir = function(_path) { + var isDirectory; + + try { + isDirectory = fs.statSync(_path).isDirectory(); + } + catch(e) { + isDirectory = false; + } + + if (isDirectory){ + return _path; + } else { + return path.dirname(_path); + } +}; + +exports.mkPath = function(/**Array*/ _path) { + if ( Array.isArray(_path) ) { + _path = _path.join(''); + } + + wrench.mkdirSyncRecursive(_path); +}; + +// adapted from http://procbits.com/2011/11/15/synchronous-file-copy-in-node-js +exports.copyFileSync = function(inFile, outDir, fileName) { + var BUF_LENGTH = 64 * 1024; + + var read; + var write; + + var buffer = new Buffer(BUF_LENGTH); + var bytesRead = 1; + var outFile = path.join( outDir, fileName || path.basename(inFile) ); + var pos = 0; + + wrench.mkdirSyncRecursive(outDir); + read = fs.openSync(inFile, 'r'); + write = fs.openSync(outFile, 'w'); + + while (bytesRead > 0) { + bytesRead = fs.readSync(read, buffer, 0, BUF_LENGTH, pos); + fs.writeSync(write, buffer, 0, bytesRead); + pos += bytesRead; + } + + fs.closeSync(read); + return fs.closeSync(write); +}; + +Object.keys(fs).forEach(function(key) { + exports[key] = fs[key]; +}); diff --git a/node/jsdoc/fs.js b/node/jsdoc/fs.js deleted file mode 100644 index 7115ff0c..00000000 --- a/node/jsdoc/fs.js +++ /dev/null @@ -1,17 +0,0 @@ -var e = ' is not implemented for Node.js!'; - -exports.ls = function() { - throw new Error('fs.ls' + e); -}; - -exports.toDir = function() { - throw new Error('fs.toDir' + e); -}; - -exports.mkPath = function() { - throw new Error('fs.mkpath' + e); -}; - -exports.copyFileSync = function() { - throw new Error('fs.copyFileSync' + e); -}; diff --git a/node/jsdoc/util/include.js b/node/jsdoc/util/include.js deleted file mode 100644 index c5c0d4de..00000000 --- a/node/jsdoc/util/include.js +++ /dev/null @@ -1,8 +0,0 @@ -// TODO: not tested -module.exports = function(filepath) { - var fs = require('jsdoc/fs'); - var vm = require('vm'); - - var script = fs.readFileSync(filepath, 'utf8'); - vm.runInNewContext(script, global, filepath); -}; diff --git a/node/path.js b/node/path.js new file mode 100644 index 00000000..0d460d98 --- /dev/null +++ b/node/path.js @@ -0,0 +1,7 @@ +exports.pathToUri = function(_path) { + return _path; +}; + +exports.uriToPath = function(uri) { + return uri; +}; diff --git a/plugins/test/specs/commentConvert.js b/plugins/test/specs/commentConvert.js index 3f1dc08c..07e73769 100644 --- a/plugins/test/specs/commentConvert.js +++ b/plugins/test/specs/commentConvert.js @@ -1,11 +1,16 @@ -/*global describe: true, expect: true, it: true, jasmine: true */ +/*global describe: true, env: true, expect: true, it: true, jasmine: true */ describe("commentConvert plugin", function() { - var parser = new (require("jsdoc/src/parser")).Parser(), - plugin = require('plugins/commentConvert'), - docSet; + var parser = new (require("jsdoc/src/parser")).Parser(); + var path = require('jsdoc/path'); - require('jsdoc/plugins').installPlugins(['plugins/commentConvert'], parser); - docSet = jasmine.getDocSetFromFile("plugins/commentConvert.js", parser); + var docSet; + + var pluginPath = 'plugins/commentConvert'; + var pluginPathResolved = path.join(env.dirname, pluginPath); + var plugin = require(pluginPathResolved); + + require('jsdoc/plugins').installPlugins([pluginPathResolved], parser); + docSet = jasmine.getDocSetFromFile(pluginPath + '.js', parser); it("should convert '///-style comments into jsdoc comments", function() { var doclet = docSet.getByLongname("module:plugins/commentConvert.handlers.beforeParse"); diff --git a/plugins/test/specs/escapeHtml.js b/plugins/test/specs/escapeHtml.js index 8bd88210..448e0a03 100644 --- a/plugins/test/specs/escapeHtml.js +++ b/plugins/test/specs/escapeHtml.js @@ -1,11 +1,16 @@ -/*global describe: true, expect: true, it: true, jasmine: true */ +/*global describe: true, env: true, expect: true, it: true, jasmine: true */ describe("escapeHtml plugin", function() { - var parser = new (require("jsdoc/src/parser")).Parser(), - plugin = require('plugins/escapeHtml'), - docSet; + var parser = new (require('jsdoc/src/parser')).Parser(); + var path = require('jsdoc/path'); - require('jsdoc/plugins').installPlugins(['plugins/escapeHtml'], parser); - docSet = jasmine.getDocSetFromFile("plugins/escapeHtml.js", parser); + var docSet; + + var pluginPath = 'plugins/escapeHtml'; + var pluginPathResolved = path.join(env.dirname,pluginPath); + var plugin = require(pluginPathResolved); + + require('jsdoc/plugins').installPlugins([pluginPathResolved], parser); + docSet = jasmine.getDocSetFromFile(pluginPath + '.js', parser); it("should escape '&', '<' and newlines in doclet descriptions", function() { var doclet = docSet.getByLongname("module:plugins/escapeHtml.handlers.newDoclet"); diff --git a/plugins/test/specs/markdown.js b/plugins/test/specs/markdown.js index ce08324f..c9d89cc1 100644 --- a/plugins/test/specs/markdown.js +++ b/plugins/test/specs/markdown.js @@ -1,22 +1,29 @@ +/*global describe: true, env: true, expect: true, it: true, jasmine: true */ + +var path = require('jsdoc/path'); + describe("markdown plugin", function() { //TODO }); describe("markdown see tag support", function() { - var plugin = require('plugins/markdown'), - docSet = jasmine.getDocSetFromFile('plugins/test/fixtures/seetag-markdown.js'), - foo = docSet.getByLongname('foo')[0], - bar = docSet.getByLongname('bar')[0]; + var pluginPath = 'plugins/markdown'; + var pluginPathResolved = path.join(env.dirname, pluginPath); + var plugin = require(pluginPathResolved); + + var docSet = jasmine.getDocSetFromFile('plugins/test/fixtures/seetag-markdown.js'); + var foo = docSet.getByLongname('foo')[0]; + var bar = docSet.getByLongname('bar')[0]; it ('should parse @see tags containing links', function() { plugin.handlers.newDoclet({doclet:foo}); expect(typeof foo).toEqual('object'); expect(foo.see[0]).toEqual('

Nowhere

'); - }) + }); it ('should not parse @see tags that do not contain links', function() { plugin.handlers.newDoclet({doclet:bar}); expect(typeof bar).toEqual('object'); expect(bar.see[0]).toEqual('AnObject#myProperty'); - }) -}); \ No newline at end of file + }); +}); diff --git a/plugins/test/specs/overloadHelper.js b/plugins/test/specs/overloadHelper.js index c57d2130..9f9c20a3 100644 --- a/plugins/test/specs/overloadHelper.js +++ b/plugins/test/specs/overloadHelper.js @@ -1,10 +1,15 @@ -/*global describe: true, expect: true, it: true, jasmine: true, xit: true */ +/*global describe: true, env: true, expect: true, it: true, jasmine: true, xit: true */ describe('plugins/overloadHelper', function() { var parser = new (require('jsdoc/src/parser')).Parser(); - var plugin = require('plugins/overloadHelper'); + var path = require('jsdoc/path'); + var docSet; - require('jsdoc/plugins').installPlugins(['plugins/overloadHelper'], parser); + var pluginPath = 'plugins/overloadHelper'; + var pluginPathResolved = path.resolve(env.dirname, pluginPath); + var plugin = require(pluginPathResolved); + + require('jsdoc/plugins').installPlugins([pluginPathResolved], parser); docSet = jasmine.getDocSetFromFile('plugins/test/fixtures/overloadHelper.js', parser); it('should exist', function() { diff --git a/plugins/test/specs/railsTemplate.js b/plugins/test/specs/railsTemplate.js index 2f89ee5a..c83dbeec 100644 --- a/plugins/test/specs/railsTemplate.js +++ b/plugins/test/specs/railsTemplate.js @@ -1,15 +1,17 @@ -/*global describe: true, expect: true, it: true */ +/*global describe: true, env: true, expect: true, it: true */ describe("railsTemplate plugin", function() { - var parser = new (require("jsdoc/src/parser")).Parser(), - plugin = require('plugins/railsTemplate'); + var parser = new (require("jsdoc/src/parser")).Parser(); + var path = require('jsdoc/path'); + + var pluginPath = path.join(env.dirname, 'plugins/railsTemplate'); + var plugin = require(pluginPath); - require('jsdoc/plugins').installPlugins(['plugins/railsTemplate'], parser); + require('jsdoc/plugins').installPlugins([pluginPath], parser); require('jsdoc/src/handlers').attachTo(parser); it("should remove <% %> rails template tags from the source of *.erb files", function() { - var path = require("path"), - docSet = parser.parse([path.join(__dirname, "plugins/test/fixtures/railsTemplate.js.erb")]); + var docSet = parser.parse([path.join(env.dirname, "plugins/test/fixtures/railsTemplate.js.erb")]); expect(docSet[2].description).toEqual("Remove rails tags from the source input (e.g. )"); }); diff --git a/plugins/test/specs/shout.js b/plugins/test/specs/shout.js index 75e70b64..0f9785ae 100644 --- a/plugins/test/specs/shout.js +++ b/plugins/test/specs/shout.js @@ -1,11 +1,16 @@ -/*global describe: true, expect: true, it: true, jasmine: true */ +/*global describe: true, env: true, expect: true, it: true, jasmine: true */ describe("shout plugin", function() { - var parser = new (require("jsdoc/src/parser")).Parser(), - plugin = require('plugins/shout'), - docSet; + var parser = new (require('jsdoc/src/parser')).Parser(); + var path = require('jsdoc/path'); - require('jsdoc/plugins').installPlugins(['plugins/shout'], parser); - docSet = jasmine.getDocSetFromFile("plugins/shout.js", parser); + var docSet; + + var pluginPath = 'plugins/shout'; + var pluginPathResolved = path.join(env.dirname, pluginPath); + var plugin = require(pluginPathResolved); + + require('jsdoc/plugins').installPlugins([pluginPathResolved], parser); + docSet = jasmine.getDocSetFromFile(pluginPath + '.js', parser); it("should make the description uppercase", function() { var doclet = docSet.getByLongname("module:plugins/shout.handlers.newDoclet"); diff --git a/plugins/test/specs/sourcetag.js b/plugins/test/specs/sourcetag.js index 84d65337..1cde7e1d 100644 --- a/plugins/test/specs/sourcetag.js +++ b/plugins/test/specs/sourcetag.js @@ -1,11 +1,16 @@ -/*global describe: true, expect: true, it: true, jasmine: true */ +/*global describe: true, env: true, expect: true, it: true, jasmine: true */ describe("sourcetag plugin", function() { - var parser = new (require("jsdoc/src/parser")).Parser(), - plugin = require('plugins/sourcetag'), - docSet; + var parser = new (require('jsdoc/src/parser')).Parser(); + var path = require('jsdoc/path'); - require('jsdoc/plugins').installPlugins(['plugins/sourcetag'], parser); - docSet = jasmine.getDocSetFromFile("plugins/sourcetag.js", parser); + var docSet; + + var pluginPath = 'plugins/sourcetag'; + var pluginPathResolved = path.join(env.dirname, pluginPath); + var plugin = require(pluginPathResolved); + + require('jsdoc/plugins').installPlugins([pluginPathResolved], parser); + docSet = jasmine.getDocSetFromFile(pluginPath + '.js', parser); it("should set the lineno and filename of the doclet's meta property", function() { var doclet = docSet.getByLongname("module:plugins/sourcetag.handlers.newDoclet"); diff --git a/plugins/test/specs/verboseOutput.js b/plugins/test/specs/verboseOutput.js index e8ac43bd..80abdf82 100644 --- a/plugins/test/specs/verboseOutput.js +++ b/plugins/test/specs/verboseOutput.js @@ -1,15 +1,20 @@ -/*global describe: true, expect: true, it: true, jasmine: true, xit: true */ +/*global describe: true, env: true, expect: true, it: true, jasmine: true, xit: true */ /** * @author Rob Taylor [manix84@gmail.com] */ +var path = require('jsdoc/path'); + describe("verbose output plugin", function () { - var parser = new (require("jsdoc/src/parser")).Parser(), - plugin = require('plugins/verboseOutput'), - docSet; + var parser = new (require('jsdoc/src/parser')).Parser(); + var path = require('jsdoc/path'); + + var docSet; + var pluginPath = 'plugins/verboseOutput'; + var plugin = require( path.resolve(env.dirname, pluginPath) ); //require('jsdoc/plugins').installPlugins(['plugins/verboseOutput'], parser); - docSet = jasmine.getDocSetFromFile("plugins/verboseOutput.js", parser); + docSet = jasmine.getDocSetFromFile(pluginPath + '.js', parser); xit("should log file names to console", function() { // TODO: this doesn't actually test the plugin... diff --git a/rhino/fs.js b/rhino/fs.js index c452428a..d3f8bed5 100644 --- a/rhino/fs.js +++ b/rhino/fs.js @@ -96,8 +96,10 @@ var mkdirSync = exports.mkdirSync = function(_path) { exports.mkdir = asyncify(mkdirSync); // JSDoc extension to `fs` module -exports.mkPath = function(/**Array*/ _path) { - if (_path.constructor == Array) { _path = _path.join(''); } +exports.mkPath = function(_path) { + if ( Array.isArray(_path) ) { + _path = _path.join(''); + } (new java.io.File(_path)).mkdirs(); }; diff --git a/rhino/js.jar b/rhino/js.jar index 244aa614d837794b4d04e6a4b67eb3dbcdd4970f..acbb643758e4fda6d3ac6bbeb423d4c4f174c74e 100644 GIT binary patch delta 17377 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