diff --git a/Jake/templates/package.json.tmpl b/Jake/templates/package.json.tmpl index d5159473..4d6773d7 100644 --- a/Jake/templates/package.json.tmpl +++ b/Jake/templates/package.json.tmpl @@ -28,7 +28,7 @@ "underscore": "1.4.2", "wrench": "1.3.9" }, - "bin": "./nodejs/bin/jsdoc", + "bin": "./node/bin/jsdoc", "bugs": "https://github.com/jsdoc3/jsdoc/issues", "author": { "name": "Michael Mathews", diff --git a/jsdoc.js b/jsdoc.js index b1f27885..9b2b614a 100644 --- a/jsdoc.js +++ b/jsdoc.js @@ -72,9 +72,9 @@ require('lib/jsdoc/util/global').env = { // initialize the environment for the current JavaScript VM (function(args) { - var vm = require('jsdoc/util/vm').vm; + var runtime = require('jsdoc/util/runtime').getRuntime(); // TODO: may need to move this file to support Node.js - require('initialize')[vm](args); + require('initialize')[runtime](args); })( Array.prototype.slice.call(arguments, 0) ); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// @@ -87,7 +87,11 @@ require('lib/jsdoc/util/global').env = { require('lib/jsdoc/util/global').app = { jsdoc: { scanner: new (require('jsdoc/src/scanner').Scanner)(), - parser: new (require('jsdoc/src/parser').Parser)(), + // TODO: allow the config file to specify the parser module + parser: (function() { + var modulePath = require('jsdoc/util/runtime').getModulePath('jsdoc/src/parser'); + return new ( require(modulePath) ).Parser(); + })(), name: require('jsdoc/name') } }; diff --git a/lib/jsdoc/fs.js b/lib/jsdoc/fs.js index ee365a8d..1993807a 100644 --- a/lib/jsdoc/fs.js +++ b/lib/jsdoc/fs.js @@ -4,11 +4,11 @@ */ var fs = exports.fs = require('fs'); -var vm = require('jsdoc/util/vm'); +var runtime = require('jsdoc/util/runtime'); // export the VM-specific implementations of the extra methods // TODO: document extra methods here -var extras = vm.getModule('fs'); +var extras = require( runtime.getModulePath('fs') ); Object.keys(extras).forEach(function(extra) { exports[extra] = extras[extra]; }); diff --git a/lib/jsdoc/path.js b/lib/jsdoc/path.js index 4ddb031d..0be64476 100644 --- a/lib/jsdoc/path.js +++ b/lib/jsdoc/path.js @@ -5,7 +5,7 @@ var fs = require('fs'); var path = require('path'); -var vm = require('jsdoc/util/vm'); +var runtime = require('jsdoc/util/runtime'); function prefixReducer(previousPath, current) { @@ -71,7 +71,7 @@ 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 = vm.getModule('jsdoc').pathToUri; +var pathToUri = require( runtime.getModulePath('jsdoc') ).pathToUri; // TODO: do we need this? if so, any way to stop exporting it? /** @@ -82,7 +82,7 @@ var pathToUri = vm.getModule('jsdoc').pathToUri; * @param {string} uri The URI to convert. * @return {string} A path that meets the operating system's requirements. */ -exports._uriToPath = vm.getModule('jsdoc').uriToPath; +exports._uriToPath = require( runtime.getModulePath('jsdoc') ).uriToPath; /** * Retrieve the fully qualified path to the requested resource. diff --git a/lib/jsdoc/plugins.js b/lib/jsdoc/plugins.js index 5e2637bc..963f0a97 100644 --- a/lib/jsdoc/plugins.js +++ b/lib/jsdoc/plugins.js @@ -36,10 +36,15 @@ exports.installPlugins = function(plugins, p) { plugin.defineTags(dictionary); } - //...add a node visitor + //...add a Rhino node visitor (deprecated in JSDoc 3.3) if (plugin.nodeVisitor) { parser.addNodeVisitor(plugin.nodeVisitor); } + + //...add a Mozilla Parser API node visitor + if (plugin.astNodeVisitor) { + parser.addAstNodeVisitor(plugin.astNodeVisitor); + } } } }; diff --git a/lib/jsdoc/src/parser.js b/lib/jsdoc/src/parser.js index 01e958f6..f0d02031 100644 --- a/lib/jsdoc/src/parser.js +++ b/lib/jsdoc/src/parser.js @@ -24,13 +24,33 @@ var SCHEMA = 'javascript:'; * @example Create a new parser. * var jsdocParser = new (require('jsdoc/src/parser').Parser)(); */ -var Parser = exports.Parser = function() { +var Parser = exports.Parser = function(builderInstance, visitorInstance, walkerInstance) { this.clear(); - // TODO: require the appropriate AstBuilder based on a config setting - this._astBuilder = new (require('jsdoc/src/rhino/astbuilder'))(); - this._visitor = new (require('jsdoc/src/visitor'))(this); - this._walker = new (require('jsdoc/src/walker')).Walker(); + // TODO: replace with a runtime-agnostic default builder + var runtime = require('jsdoc/util/runtime'); + this._astBuilder = builderInstance || + new ( require(runtime.getModulePath('jsdoc/src/astbuilder')) )(); + this._visitor = visitorInstance || new (require('jsdoc/src/visitor')).Visitor(this); + this._walker = walkerInstance || new (require('jsdoc/src/walker')).Walker(); + + Object.defineProperties(this, { + astBuilder: { + get: function() { + return this._astBuilder; + } + }, + visitor: { + get: function() { + return this._visitor; + } + }, + walker: { + get: function() { + return this._walker; + } + } + }); }; util.inherits(Parser, require('events').EventEmitter); @@ -42,12 +62,6 @@ Parser.prototype.clear = function() { meta: {} } }; - this._visitor = null; -}; - -// TODO: docs -Parser.prototype.getVisitor = function() { - return this._visitor; }; // TODO: docs @@ -134,9 +148,6 @@ Parser.prototype.fireProcessingComplete = function(doclets) { }; // TODO: docs -/** - * @returns {Array} The accumulated results of any calls to parse. - */ Parser.prototype.results = function() { return this._resultBuffer; }; @@ -149,20 +160,14 @@ Parser.prototype.addResult = function(o) { this._resultBuffer.push(o); }; -// TODO: update docs -/** - * Adds a node visitor to use in parsing - */ -Parser.prototype.addNodeVisitor = function(visitor) { - this._visitor._addRhinoNodeVisitor(visitor); +// TODO: docs +Parser.prototype.addAstNodeVisitor = function(visitor) { + this._visitor.addAstNodeVisitor(visitor); }; // TODO: docs -/** - * Get the node visitors used in parsing - */ -Parser.prototype.getVisitors = function() { - return this._visitor._getRhinoNodeVisitors(); +Parser.prototype.getAstNodeVisitors = function() { + return this._visitor.getAstNodeVisitors(); }; // TODO: docs @@ -424,7 +429,6 @@ Parser.prototype.getBasename = function(name) { if (name !== undefined) { return name.replace(/^([$a-z_][$a-z_0-9]*).*?$/i, '$1'); } - return name; }; // TODO: docs @@ -433,7 +437,6 @@ function definedInScope(doclet, basename) { hasOwnProp.call(doclet.meta.vars, basename); } - // TODO: docs /** * Given a node, determine what the node is a member of. diff --git a/lib/jsdoc/src/visitor.js b/lib/jsdoc/src/visitor.js index 51961903..7b7f3f28 100644 --- a/lib/jsdoc/src/visitor.js +++ b/lib/jsdoc/src/visitor.js @@ -68,13 +68,11 @@ function JsdocCommentFound(comment, filename) { // TODO: docs -var Visitor = module.exports = function(parser) { +var Visitor = exports.Visitor = function(parser) { this._parser = parser; // Mozilla Parser API node visitors added by plugins this._nodeVisitors = []; - // Rhino node visitors added by plugins (deprecated in JSDoc 3.3) - this._rhinoNodeVisitors = []; // built-in visitors this._visitors = [ this.visitNodeComments, @@ -83,12 +81,12 @@ var Visitor = module.exports = function(parser) { }; // TODO: docs -Visitor.prototype.addNodeVisitor = function(visitor) { +Visitor.prototype.addAstNodeVisitor = function(visitor) { this._nodeVisitors.push(visitor); }; // TODO: docs -Visitor.prototype.removeNodeVisitor = function(visitor) { +Visitor.prototype.removeAstNodeVisitor = function(visitor) { var idx = this._nodeVisitors.indexOf(visitor); if (idx !== -1) { this._nodeVisitors.splice(idx, 1); @@ -96,20 +94,10 @@ Visitor.prototype.removeNodeVisitor = function(visitor) { }; // TODO: docs -Visitor.prototype.getNodeVisitors = function() { +Visitor.prototype.getAstNodeVisitors = function() { return this._nodeVisitors; }; -// TODO: docs (deprecated) -Visitor.prototype._addRhinoNodeVisitor = function(visitor) { - this._rhinoNodeVisitors.push(visitor); -}; - -// TODO: docs (deprecated) -Visitor.prototype._getRhinoNodeVisitors = function() { - return this._rhinoNodeVisitors; -}; - // TODO: docs; visitor signature is (node, parser, filename) Visitor.prototype.visit = function(node, filename) { var i; @@ -203,18 +191,10 @@ Visitor.prototype.visitNodeComments = function(node, parser, filename) { // TODO: docs Visitor.prototype.visitNode = function(node, parser, filename) { - var e = this.makeSymbolFoundEvent(node, parser, filename); + var i; + var l; - function callNodeVisitors(visitors, nodeToVisit) { - if (visitors) { - for (var i = 0, l = visitors.length; i < l; i++) { - visitors[i].visitNode(nodeToVisit, e, parser, filename); - if (e.stopPropagation) { - break; - } - } - } - } + var e = this.makeSymbolFoundEvent(node, parser, filename); if (!node.nodeId) { Object.defineProperty(node, 'nodeId', { @@ -222,9 +202,13 @@ Visitor.prototype.visitNode = function(node, parser, filename) { }); } - callNodeVisitors(this._nodeVisitors, node); - if (e.code && e.code.node) { - callNodeVisitors(this._rhinoNodeVisitors, e.code.node); // TODO: check this!! + if (this._nodeVisitors && this._nodeVisitors.length) { + for (i = 0, l = this._nodeVisitors.length; i < l; i++) { + this._nodeVisitors[i].visitNode(node, e, parser, filename); + if (e.stopPropagation) { + break; + } + } } if (!e.preventDefault && e.comment && isValidJsdoc(e.comment)) { @@ -234,7 +218,7 @@ Visitor.prototype.visitNode = function(node, parser, filename) { // add the node to the parser's lookup table parser.addDocletRef(e); - for (var i = 0, l = e.finishers.length; i < l; i++) { + for (i = 0, l = e.finishers.length; i < l; i++) { e.finishers[i].call(parser, e); } diff --git a/lib/jsdoc/util/include.js b/lib/jsdoc/util/include.js index 54d1efa4..a877a1f9 100644 --- a/lib/jsdoc/util/include.js +++ b/lib/jsdoc/util/include.js @@ -1,5 +1,5 @@ var path = require('path'); -var vm = require('jsdoc/util/vm'); +var runtime = require('jsdoc/util/runtime'); /** * Read and execute a JavaScript file in global scope. @@ -11,7 +11,7 @@ module.exports = function(filepath) { filepath = path.resolve(__dirname, filepath); try { - vm.getModule('jsdoc/util/include')(filepath); + require( runtime.getModulePath('jsdoc/util/include') )(filepath); } catch (e) { console.log('Cannot include ' + filepath + ': ' + e); diff --git a/lib/jsdoc/util/vm.js b/lib/jsdoc/util/runtime.js similarity index 53% rename from lib/jsdoc/util/vm.js rename to lib/jsdoc/util/runtime.js index eb7048b1..ce3c0786 100644 --- a/lib/jsdoc/util/vm.js +++ b/lib/jsdoc/util/runtime.js @@ -1,16 +1,18 @@ /*global Packages: true */ /** - * Helper functions to enable JSDoc to run on multiple JavaScript virtual machines. - * @module jsdoc/util/vm + * Helper functions to enable JSDoc to run on multiple JavaScript runtimes. + * + * @module jsdoc/util/runtime + * @private */ var os = require('os'); // These strings represent directory names; do not modify them! /** @private */ -const RHINO = exports.RHINO = 'rhino'; +var RHINO = exports.RHINO = 'rhino'; /** @private */ -const NODEJS = exports.NODEJS = 'nodejs'; +var NODE = exports.NODE = 'node'; // hacky conversion from Windows path to URI function pathToUri(filepath) { @@ -33,51 +35,63 @@ function pathToUri(filepath) { } /** - * The JavaScript VM that is executing JSDoc: + * The JavaScript runtime that is executing JSDoc: * - * + `module:jsdoc/util/vm.RHINO`: Mozilla Rhino. - * + `module:jsdoc/util/vm.NODEJS`: Node.js (not currently supported). + * + `module:jsdoc/util/runtime~RHINO`: Mozilla Rhino. + * + `module:jsdoc/util/runtime~NODE`: Node.js (not currently supported). + * + * @private */ -var vm = exports.vm = (function() { +var runtime = (function() { if (Packages && typeof Packages === 'object' && Object.prototype.toString.call(Packages) === '[object JavaPackage]') { return RHINO; } else if ( require && require.main && module && (require.main === module) ) { - return NODEJS; + return NODE; } else { - // unknown VM - throw new Error('Unable to identify the current JavaScript VM.'); + // unknown runtime + throw new Error('Unable to identify the current JavaScript runtime.'); } })(); /** - * Load the VM-specific implementation of a module. + * Get the require path for the runtime-specific implementation of a module. * - * @param {string} modulePath - The relative path to the module. Use the same format as when calling + * @param {string} partialPath - The partial path to the module. Use the same format as when calling * `require()`. - * @return {object} An object containing VM-specific functions for the requested module. + * @return {object} The require path for the runtime-specific implementation of the module. */ -exports.getModule = function(modulePath) { - modulePath = [__dirname, vm, modulePath].join('/').replace(/ /g, '%20'); +exports.getModulePath = function(partialPath) { + var modulePath = [__dirname, runtime, partialPath].join('/').replace(/ /g, '%20'); if (os.platform() === 'win32') { modulePath = pathToUri(modulePath); } - return require(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 VM is Mozilla Rhino. + * @return {boolean} Set to `true` if the current runtime is Mozilla Rhino. */ exports.isRhino = function() { - return vm === RHINO; + return runtime === RHINO; }; /** * Check whether Node.js is running JSDoc. (Node.js is not currently supported.) - * @return {boolean} Set to `true` if the current VM is Node.js. + * @return {boolean} Set to `true` if the current runtime is Node.js. */ -exports.isNodejs = function() { - return vm === NODEJS; +exports.isNode = function() { + return runtime === NODE; }; diff --git a/nodejs/bin/jsdoc b/node/bin/jsdoc similarity index 66% rename from nodejs/bin/jsdoc rename to node/bin/jsdoc index c53efc4d..d2f56ad1 100755 --- a/nodejs/bin/jsdoc +++ b/node/bin/jsdoc @@ -7,19 +7,12 @@ var fs = require('fs'); var os = require('os'); var path = require('path'); -var spawnProc = require('child_process').spawn; var args = process.argv.slice(2); var script = path.normalize( path.join( path.dirname(fs.realpathSync(process.argv[1])), '..', '..', 'jsdoc' ) ); -var jsdoc; - -if (process.platform === 'win32') { - jsdoc = spawnProc(script + '.cmd', args, {stdio: 'inherit'}); -} -else { - jsdoc = spawnProc(script, args, {stdio: 'inherit'}); -} +var command = process.platform === 'win32' ? script + '.cmd' : script; +var jsdoc = require('child_process').spawn(command, args, {stdio: 'inherit'}); jsdoc.on('close', function(code) { process.exit(code); diff --git a/nodejs/jsdoc/fs.js b/node/jsdoc/fs.js similarity index 100% rename from nodejs/jsdoc/fs.js rename to node/jsdoc/fs.js diff --git a/node/jsdoc/src/parser.js b/node/jsdoc/src/parser.js new file mode 100644 index 00000000..1b146d91 --- /dev/null +++ b/node/jsdoc/src/parser.js @@ -0,0 +1 @@ +module.exports = require('jsdoc/src/parser'); diff --git a/nodejs/jsdoc/util/include.js b/node/jsdoc/util/include.js similarity index 100% rename from nodejs/jsdoc/util/include.js rename to node/jsdoc/util/include.js diff --git a/package.json b/package.json index 17546150..820a8ef3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jsdoc", "version": "3.2.0-dev", - "revision": "1366409063892", + "revision": "1373039791834", "description": "An API documentation generator for JavaScript.", "keywords": [ "documentation", "javascript" ], "licenses": [ @@ -28,7 +28,7 @@ "underscore": "1.4.2", "wrench": "1.3.9" }, - "bin": "./nodejs/bin/jsdoc", + "bin": "./node/bin/jsdoc", "bugs": "https://github.com/jsdoc3/jsdoc/issues", "author": { "name": "Michael Mathews", diff --git a/rhino/js.jar b/rhino/js.jar index 30e26793..244aa614 100644 Binary files a/rhino/js.jar and b/rhino/js.jar differ diff --git a/lib/jsdoc/src/rhino/astbuilder.js b/rhino/jsdoc/src/astbuilder.js similarity index 71% rename from lib/jsdoc/src/rhino/astbuilder.js rename to rhino/jsdoc/src/astbuilder.js index bb934b24..9a6c0cde 100644 --- a/lib/jsdoc/src/rhino/astbuilder.js +++ b/rhino/jsdoc/src/astbuilder.js @@ -1,7 +1,7 @@ /*global Packages: true */ /** * Creates an Esprima-compatible AST using Rhino's JavaScript parser. - * @module jsdoc/src/rhino/astbuilder + * @module rhino/jsdoc/src/astbuilder */ var AstBuilder = module.exports = function() { @@ -11,3 +11,7 @@ var AstBuilder = module.exports = function() { AstBuilder.prototype.build = function(sourceCode, sourceName) { return this._builder.build(sourceCode, sourceName); }; + +AstBuilder.prototype.getRhinoNodes = function() { + return this._builder.getRhinoNodes(); +}; diff --git a/rhino/jsdoc/src/parser.js b/rhino/jsdoc/src/parser.js new file mode 100644 index 00000000..b3c16177 --- /dev/null +++ b/rhino/jsdoc/src/parser.js @@ -0,0 +1,34 @@ +// TODO: module docs + +// TODO: docs +var Parser = exports.Parser = function() { + var astBuilder; + var visitor; + + var runtime = require('jsdoc/util/runtime'); + if ( !runtime.isRhino() ) { + throw new Error('You must run JSDoc on Mozilla Rhino to use the Rhino parser.'); + } + + astBuilder = new ( require(runtime.getModulePath('jsdoc/src/astbuilder')) )(); + visitor = new ( require(runtime.getModulePath('jsdoc/src/visitor')) ).Visitor(this); + + Parser.super_.call(this, astBuilder, visitor); +}; +require('util').inherits(Parser, require('jsdoc/src/parser').Parser); + +// TODO: update docs +/** + * Adds a node visitor to use in parsing + */ +Parser.prototype.addNodeVisitor = function(visitor) { + this._visitor.addRhinoNodeVisitor(visitor); +}; + +// TODO: docs +/** + * Get the node visitors used in parsing + */ +Parser.prototype.getNodeVisitors = function() { + return this._visitor.getRhinoNodeVisitors(); +}; diff --git a/rhino/jsdoc/src/visitor.js b/rhino/jsdoc/src/visitor.js new file mode 100644 index 00000000..a12fadff --- /dev/null +++ b/rhino/jsdoc/src/visitor.js @@ -0,0 +1,56 @@ +// TODO: module docs + +// TODO: docs +var Visitor = exports.Visitor = function(parser) { + var runtime = require('jsdoc/util/runtime'); + if ( !runtime.isRhino() ) { + throw new Error('You must run JSDoc on Mozilla Rhino to use the Rhino node visitor.'); + } + + Visitor.super_.call(this, parser); + + // Rhino node visitors added by plugins (deprecated in JSDoc 3.3) + this._rhinoNodeVisitors = []; + // Rhino nodes retrieved from the org.jsdoc.AstBuilder instance + this._rhinoNodes = null; + + this.addAstNodeVisitor({ + visitNode: this._visitRhinoNode.bind(this) + }); +}; +require('util').inherits(Visitor, require('jsdoc/src/visitor').Visitor); + +// TODO: docs (deprecated) +Visitor.prototype.addRhinoNodeVisitor = function(visitor) { + this._rhinoNodeVisitors.push(visitor); +}; + +// TODO: docs (deprecated) +Visitor.prototype.getRhinoNodeVisitors = function() { + return this._rhinoNodeVisitors; +}; + +// TODO: docs (deprecated) +Visitor.prototype._visitRhinoNode = function(astNode, e, parser, filename) { + var rhinoNode; + + var visitors = this._rhinoNodeVisitors; + // if there are no visitors, bail out before we retrieve all the nodes + if (!visitors.length) { + return; + } + + if (!this._rhinoNodes) { + this._rhinoNodes = parser.astBuilder.getRhinoNodes(); + } + + rhinoNode = this._rhinoNodes ? this._rhinoNodes.get(astNode.nodeId) : null; + if (rhinoNode) { + for (var i = 0, l = visitors.length; i < l; i++) { + visitors[i].visitNode(rhinoNode, e, parser, filename); + if (e.stopPropagation) { + break; + } + } + } +}; diff --git a/test/jasmine-jsdoc.js b/test/jasmine-jsdoc.js index f77b0f5f..79064337 100644 --- a/test/jasmine-jsdoc.js +++ b/test/jasmine-jsdoc.js @@ -110,10 +110,12 @@ jasmine.asyncSpecDone = function() { }; jasmine.getDocSetFromFile = function(filename, parser) { - var sourceCode = fs.readFileSync(__dirname + '/' + filename, 'utf8'), - testParser = parser || new (require('jsdoc/src/parser')).Parser(), - indexAll = require('jsdoc/borrow').indexAll, - doclets; + var sourceCode = fs.readFileSync(__dirname + '/' + filename, 'utf8'); + var runtime = require('jsdoc/util/runtime'); + var testParser = parser || + new ( require(runtime.getModulePath('jsdoc/src/parser')) ).Parser(); + var indexAll = require('jsdoc/borrow').indexAll; + var doclets; require('jsdoc/src/handlers').attachTo(testParser); diff --git a/test/specs/documentation/modules.js b/test/specs/documentation/modules.js index 52481150..9d31e402 100644 --- a/test/specs/documentation/modules.js +++ b/test/specs/documentation/modules.js @@ -1,7 +1,9 @@ /*global beforeEach: true, describe: true, env: true, expect: true, it: true */ describe("module names", function() { - var parser = require('jsdoc/src/parser'), - srcParser = null, doclets; + var runtime = require('jsdoc/util/runtime'); + var parser = require( runtime.getModulePath('jsdoc/src/parser') ); + var srcParser = null; + var doclets; beforeEach(function() { env.opts._ = [__dirname + '/test/fixtures/modules/']; diff --git a/test/specs/jsdoc/src/handlers.js b/test/specs/jsdoc/src/handlers.js index f43ac711..66f0368b 100644 --- a/test/specs/jsdoc/src/handlers.js +++ b/test/specs/jsdoc/src/handlers.js @@ -1,8 +1,9 @@ /*global describe: true, expect: true, it: true */ describe("jsdoc/src/handlers", function() { - var jsdoc = {src: { parser: require('jsdoc/src/parser')}}, - testParser = new jsdoc.src.parser.Parser(), - handlers = require('jsdoc/src/handlers'); + var runtime = require('jsdoc/util/runtime'); + var parser = require( runtime.getModulePath('jsdoc/src/parser') ); + var testParser = new parser.Parser(); + var handlers = require('jsdoc/src/handlers'); handlers.attachTo(testParser); @@ -52,4 +53,4 @@ describe("jsdoc/src/handlers", function() { describe("symbolFound handler", function() { //TODO }); -}); \ No newline at end of file +}); diff --git a/test/specs/jsdoc/src/parser.js b/test/specs/jsdoc/src/parser.js index d75f554a..6babbef8 100644 --- a/test/specs/jsdoc/src/parser.js +++ b/test/specs/jsdoc/src/parser.js @@ -1,15 +1,16 @@ -/*global beforeEach: true, describe: true, expect: true, it: true, jasmine: true, xdescribe: true, xit: true */ +/*global beforeEach: true, describe: true, expect: true, it: true, jasmine: true, spyOn: true, +xit: true */ describe("jsdoc/src/parser", function() { - var jsdoc = {src: { parser: require('jsdoc/src/parser')}}; + var jsdoc = { src: { parser: require('jsdoc/src/parser') } }; it("should exist", function() { expect(jsdoc.src.parser).toBeDefined(); - expect(typeof jsdoc.src.parser).toEqual("object"); + expect(typeof jsdoc.src.parser).toBe('object'); }); it("should export a 'Parser' constructor", function() { expect(jsdoc.src.parser.Parser).toBeDefined(); - expect(typeof jsdoc.src.parser.Parser).toEqual("function"); + expect(typeof jsdoc.src.parser.Parser).toBe('function'); }); describe("Parser", function() { @@ -19,14 +20,69 @@ describe("jsdoc/src/parser", function() { parser = new jsdoc.src.parser.Parser(); } - it("should have a 'parse' function", function() { - expect(jsdoc.src.parser.Parser.prototype.parse).toBeDefined(); - expect(typeof jsdoc.src.parser.Parser.prototype.parse).toEqual("function"); + newParser(); + + it('should have an "astBuilder" property', function() { + expect(parser.astBuilder).toBeDefined(); }); - it("should have a 'results' function", function() { - expect(jsdoc.src.parser.Parser.prototype.results).toBeDefined(); - expect(typeof jsdoc.src.parser.Parser.prototype.results).toEqual("function"); + it('should have a "visitor" property', function() { + expect(parser.visitor).toBeDefined(); + }); + + it('should have a "walker" property', function() { + expect(parser.walker).toBeDefined(); + }); + + it('should accept an astBuilder, visitor, and walker as arguments', function() { + var astBuilder = {}; + var visitor = {}; + var walker = {}; + + var myParser = new jsdoc.src.parser.Parser(astBuilder, visitor, walker); + + expect(myParser.astBuilder).toBe(astBuilder); + expect(myParser.visitor).toBe(visitor); + expect(myParser.walker).toBe(walker); + }); + + it('should have a "parse" method', function() { + expect(parser.parse).toBeDefined(); + expect(typeof parser.parse).toBe('function'); + }); + + it('should have a "results" method', function() { + expect(parser.results).toBeDefined(); + expect(typeof parser.results).toBe('function'); + }); + + it('should have an "addAstNodeVisitor" method', function() { + expect(parser.addAstNodeVisitor).toBeDefined(); + expect(typeof parser.addAstNodeVisitor).toBe('function'); + }); + + it('should have a "getAstNodeVisitors" method', function() { + expect(parser.getAstNodeVisitors).toBeDefined(); + expect(typeof parser.getAstNodeVisitors).toBe('function'); + }); + + describe('astBuilder', function() { + // TODO: enable + xit('should contain an appropriate astBuilder by default', function() { + expect( parser.astBuilder instanceof (require('jsdoc/src/astbuilder')) ).toBe(true); + }); + }); + + describe('visitor', function() { + it('should contain an appropriate visitor by default', function() { + expect( parser.visitor instanceof (require('jsdoc/src/visitor')).Visitor ).toBe(true); + }); + }); + + describe('walker', function() { + it('should contain an appropriate walker by default', function() { + expect( parser.walker instanceof (require('jsdoc/src/walker')).Walker ).toBe(true); + }); }); describe("parse", function() { @@ -70,6 +126,70 @@ describe("jsdoc/src/parser", function() { expect(spy).toHaveBeenCalled(); }); + it('should call AST node visitors', function() { + var Syntax = require('jsdoc/src/syntax').Syntax; + + var args; + + var sourceCode = ['javascript:/** foo */var foo;']; + var visitor = { + visitNode: function(node, e, parser, sourceName) { + if (e && e.code && !args) { + args = Array.prototype.slice.call(arguments); + } + } + }; + + require('jsdoc/src/handlers').attachTo(parser); + parser.addAstNodeVisitor(visitor); + parser.parse(sourceCode); + + expect(args).toBeDefined(); + expect( Array.isArray(args) ).toBe(true); + expect(args.length).toBe(4); + + // args[0]: AST node + expect(args[0].type).toBeDefined(); + expect(args[0].type).toBe(Syntax.VariableDeclarator); + + // args[1]: JSDoc event + expect(typeof args[1]).toBe('object'); + expect(args[1].code).toBeDefined(); + expect(args[1].code.name).toBeDefined(); + expect(args[1].code.name).toBe('foo'); + + // args[2]: parser + expect(typeof args[2]).toBe('object'); + expect(args[2] instanceof jsdoc.src.parser.Parser).toBe(true); + + // args[3]: current source name + expect( String(args[3]) ).toBe('[[string0]]'); + }); + + it('should reflect changes made by AST node visitors', function() { + var doclet; + + var sourceCode = ['javascript:/** foo */var foo;']; + var visitor = { + visitNode: function(node, e, parser, sourceName) { + if (e && e.code && e.code.name === 'foo') { + e.code.name = 'bar'; + } + } + }; + + require('jsdoc/src/handlers').attachTo(parser); + parser.addAstNodeVisitor(visitor); + parser.parse(sourceCode); + + doclet = parser.results()[0]; + + expect(doclet).toBeDefined(); + expect(typeof doclet).toBe('object'); + expect(doclet.name).toBeDefined(); + expect(doclet.name).toBe('bar'); + }); + it("should fire 'parseComplete' events after it finishes parsing files", function() { var spy = jasmine.createSpy(), sourceCode = ['javascript:var bar = false;']; @@ -101,15 +221,31 @@ describe("jsdoc/src/parser", function() { }); }); - describe("results", function() { + describe('results', function() { beforeEach(newParser); - xit("contains an empty array before files are parsed", function() { - // TODO + it('returns an empty array before files are parsed', function() { + var results = parser.results(); + + expect(results).toBeDefined(); + expect( Array.isArray(results) ).toBe(true); + expect(results.length).toBe(0); }); - xit("contains an array of doclets after files are parsed", function() { + it('returns an array of doclets after files are parsed', function() { + var source = 'javascript:var foo;'; + var results; + require('jsdoc/src/handlers').attachTo(parser); + + parser.parse(source); + results = parser.results(); + + expect(results).toBeDefined(); + expect(results[0]).toBeDefined(); + expect(typeof results[0]).toBe('object'); + expect(results[0].name).toBeDefined(); + expect(results[0].name).toBe('foo'); }); it("should reflect comment changes made by 'jsdocCommentFound' handlers", function() { @@ -129,5 +265,47 @@ describe("jsdoc/src/parser", function() { }); }); }); + + describe('addAstNodeVisitor', function() { + function visitorA() {} + function visitorB() {} + + var visitors; + + beforeEach(newParser); + + it('should work with a single node visitor', function() { + parser.addAstNodeVisitor(visitorA); + + visitors = parser.getAstNodeVisitors(); + + expect(visitors.length).toBe(1); + expect(visitors[0]).toBe(visitorA); + }); + + it('should work with multiple node visitors', function() { + parser.addAstNodeVisitor(visitorA); + parser.addAstNodeVisitor(visitorB); + + visitors = parser.getAstNodeVisitors(); + + expect(visitors.length).toBe(2); + expect(visitors[0]).toBe(visitorA); + expect(visitors[1]).toBe(visitorB); + }); + }); + + describe('getAstNodeVisitors', function() { + beforeEach(newParser); + + it('should return an empty array by default', function() { + var visitors = parser.getAstNodeVisitors(); + + expect( Array.isArray(visitors) ).toBe(true); + expect(visitors.length).toBe(0); + }); + + // other functionality is covered by the addNodeVisitors tests + }); }); -}); \ No newline at end of file +}); diff --git a/test/specs/jsdoc/util/runtime.js b/test/specs/jsdoc/util/runtime.js new file mode 100644 index 00000000..2efb1a81 --- /dev/null +++ b/test/specs/jsdoc/util/runtime.js @@ -0,0 +1,62 @@ +/*global describe: true, expect: true, it: true, xit: true */ +describe("jsdoc/util/runtime", function() { + var runtime = require('jsdoc/util/runtime'); + var isRhino; + var isNode; + + it("should exist", function() { + expect(runtime).toBeDefined(); + expect(typeof runtime).toEqual('object'); + }); + + it("should export a 'RHINO' constant", function() { + expect(runtime.RHINO).toBeDefined(); + expect(typeof runtime.RHINO).toEqual('string'); + }); + + it("should export a 'NODE' constant", function() { + expect(runtime.NODE).toBeDefined(); + expect(typeof runtime.NODE).toEqual('string'); + }); + it("should export an 'isRhino' function", function() { + expect(runtime.isRhino).toBeDefined(); + expect(typeof runtime.isRhino).toEqual('function'); + }); + + it("should export an 'isNode' function", function() { + expect(runtime.isNode).toBeDefined(); + expect(typeof runtime.isNode).toEqual('function'); + }); + + describe("isRhino", function() { + isRhino = runtime.isRhino(); + + it("should return a boolean", function() { + expect(typeof isRhino).toEqual('boolean'); + }); + + it("should return the opposite value from isNode()", function() { + if (isNode === undefined) { + isNode = runtime.isNode(); + } + + expect(!isRhino).toBe(isNode); + }); + }); + + describe("isNode", function() { + isNode = runtime.isNode(); + + it("should return a boolean", function() { + expect(typeof isNode).toEqual('boolean'); + }); + + it("should return the opposite value from isRhino()", function() { + if (isRhino === undefined) { + isRhino = runtime.isRhino(); + } + + expect(!isNode).toBe(isRhino); + }); + }); +}); diff --git a/test/specs/jsdoc/util/vm.js b/test/specs/jsdoc/util/vm.js deleted file mode 100644 index a141a4cc..00000000 --- a/test/specs/jsdoc/util/vm.js +++ /dev/null @@ -1,66 +0,0 @@ -/*global describe: true, expect: true, it: true, xit: true */ -describe("jsdoc/util/vm", function() { - var vm = require('jsdoc/util/vm'); - - it("should exist", function() { - expect(vm).toBeDefined(); - expect(typeof vm).toEqual('object'); - }); - - it("should export a 'RHINO' constant", function() { - expect(vm.RHINO).toBeDefined(); - expect(typeof vm.RHINO).toEqual('string'); - }); - - it("should export a 'NODEJS' constant", function() { - expect(vm.NODEJS).toBeDefined(); - expect(typeof vm.NODEJS).toEqual('string'); - }); - - it("should export a 'vm' property", function() { - expect(vm.vm).toBeDefined(); - expect(typeof vm.vm).toEqual('string'); - }); - - it("should export an 'isRhino' function", function() { - expect(vm.isRhino).toBeDefined(); - expect(typeof vm.isRhino).toEqual('function'); - }); - - it("should export an 'isNodejs' function", function() { - expect(vm.isNodejs).toBeDefined(); - expect(typeof vm.isNodejs).toEqual('function'); - }); - - - describe("vm", function() { - it("should match either 'vm.RHINO' or 'vm.NODEJS'", function() { - expect(vm.vm).toEqual(vm.RHINO || vm.NODEJS); - }); - - xit("should return the correct value for the current VM", function() { - // TODO: is there a reasonable test that doesn't just replicate getVm()? - }); - }); - - describe("isRhino", function() { - it("should return a boolean", function() { - expect( typeof vm.isRhino() ).toEqual('boolean'); - }); - - it("should reflect the value of 'vm.vm'", function() { - expect( vm.isRhino() ).toEqual(vm.vm === vm.RHINO ? true : false); - }); - }); - - describe("isNodejs", function() { - it("should return a boolean", function() { - expect( typeof vm.isNodejs() ).toEqual('boolean'); - }); - - it("should reflect the value of 'vm.vm'", function() { - expect( vm.isNodejs() ).toEqual(vm.vm === vm.NODEJS ? true : false); - }); - }); - -}); diff --git a/test/specs/rhino/src/parser.js b/test/specs/rhino/src/parser.js new file mode 100644 index 00000000..5926606d --- /dev/null +++ b/test/specs/rhino/src/parser.js @@ -0,0 +1,154 @@ +/*global beforeEach: true, describe: true, expect: true, it: true, jasmine: true, spyOn: true */ +describe('rhino/jsdoc/src/parser', function() { + var jsdoc = { + src: { + parser: (function() { + var runtime = require('jsdoc/util/runtime'); + return require( runtime.getModulePath('jsdoc/src/parser') ); + })() + } + }; + + it('should exist', function() { + expect(jsdoc.src.parser).toBeDefined(); + expect(typeof jsdoc.src.parser).toBe('object'); + }); + + it('should export a "Parser" constructor', function() { + expect(jsdoc.src.parser.Parser).toBeDefined(); + expect(typeof jsdoc.src.parser.Parser).toBe('function'); + }); + + describe('Parser', function() { + var parser; + + function newParser() { + parser = new jsdoc.src.parser.Parser(); + } + + newParser(); + + it('should inherit from jsdoc/src/parser', function() { + var parent = require('jsdoc/src/parser').Parser; + expect(parser instanceof parent).toBe(true); + }); + + it('should have an "addNodeVisitor" method', function() { + expect(parser.addNodeVisitor).toBeDefined(); + expect(typeof parser.addNodeVisitor).toBe('function'); + }); + + it('should have a "getNodeVisitors" method', function() { + expect(parser.getNodeVisitors).toBeDefined(); + expect(typeof parser.getNodeVisitors).toBe('function'); + }); + + describe('addNodeVisitor', function() { + function visitorA() {} + function visitorB() {} + + var visitors; + + beforeEach(newParser); + + it('should work with a single Rhino node visitor', function() { + parser.addNodeVisitor(visitorA); + + visitors = parser.getNodeVisitors(); + + expect(visitors.length).toBe(1); + expect(visitors[0]).toBe(visitorA); + }); + + it('should work with multiple Rhino node visitors', function() { + parser.addNodeVisitor(visitorA); + parser.addNodeVisitor(visitorB); + + visitors = parser.getNodeVisitors(); + + expect(visitors.length).toBe(2); + expect(visitors[0]).toBe(visitorA); + expect(visitors[1]).toBe(visitorB); + }); + }); + + describe('getNodeVisitors', function() { + beforeEach(newParser); + + it('should return an empty array by default', function() { + var visitors = parser.getNodeVisitors(); + + expect( Array.isArray(visitors) ).toBe(true); + expect(visitors.length).toBe(0); + }); + + // other functionality is covered by the addNodeVisitors tests + }); + + describe('parse', function() { + beforeEach(newParser); + + var sourceCode = ['javascript:/** foo */var foo;']; + + it('should call Rhino node visitors', function() { + var args; + + var visitor = { + visitNode: function(rhinoNode, e, parser, sourceName) { + if (e && e.code && !args) { + args = Array.prototype.slice.call(arguments); + } + } + }; + + require('jsdoc/src/handlers').attachTo(parser); + parser.addNodeVisitor(visitor); + parser.parse(sourceCode); + + expect(args).toBeDefined(); + expect( Array.isArray(args) ).toBe(true); + expect(args.length).toBe(4); + + // args[0]: Rhino node + expect(args[0].toSource).toBeDefined(); + expect( String(args[0].toSource()) ).toBe('foo'); + + // args[1]: JSDoc event + expect(typeof args[1]).toBe('object'); + expect(args[1].code).toBeDefined(); + expect(args[1].code.name).toBeDefined(); + expect( String(args[1].code.name) ).toBe('foo'); + + // args[2]: parser + expect(typeof args[2]).toBe('object'); + expect(args[2] instanceof jsdoc.src.parser.Parser).toBe(true); + + // args[3]: current source name + expect( String(args[3]) ).toBe('[[string0]]'); + }); + + it('should reflect changes made by Rhino node visitors', function() { + var doclet; + + var visitor = { + visitNode: function(rhinoNode, e, parser, sourceName) { + if (e && e.code && e.code.name === 'foo') { + e.code.name = 'bar'; + } + } + }; + + require('jsdoc/src/handlers').attachTo(parser); + parser.addNodeVisitor(visitor); + parser.parse(sourceCode); + + doclet = parser.results()[0]; + + expect(doclet).toBeDefined(); + expect(typeof doclet).toBe('object'); + expect(doclet.name).toBeDefined(); + expect(doclet.name).toBe('bar'); + }); + }); + }); +}); diff --git a/test/specs/rhino/src/visitor.js b/test/specs/rhino/src/visitor.js new file mode 100644 index 00000000..1c26ae23 --- /dev/null +++ b/test/specs/rhino/src/visitor.js @@ -0,0 +1,88 @@ +/*global beforeEach: true, describe: true, expect: true, it: true */ +describe('rhino/jsdoc/src/visitor', function() { + var runtime = require('jsdoc/util/runtime'); + var jsdoc = { + src: { + visitor: require( runtime.getModulePath('jsdoc/src/visitor') ) + } + }; + + it('should exist', function() { + expect(jsdoc.src.visitor).toBeDefined(); + expect(typeof jsdoc.src.visitor).toBe('object'); + }); + + it('should export a "Visitor" constructor', function() { + expect(jsdoc.src.visitor.Visitor).toBeDefined(); + expect(typeof jsdoc.src.visitor.Visitor).toBe('function'); + }); + + describe('Visitor', function() { + var parser; + var visitor; + + function newVisitor() { + parser = new ( require(runtime.getModulePath('jsdoc/src/parser')) ).Parser(); + visitor = new jsdoc.src.visitor.Visitor(parser); + } + + newVisitor(); + + it('should inherit from jsdoc/src/visitor', function() { + var parent = require('jsdoc/src/visitor').Visitor; + expect(visitor instanceof parent).toBe(true); + }); + + it('should have an "addRhinoNodeVisitor" method', function() { + expect(visitor.addRhinoNodeVisitor).toBeDefined(); + expect(typeof visitor.addRhinoNodeVisitor).toBe('function'); + }); + + it('should have a "getRhinoNodeVisitors" method', function() { + expect(visitor.getRhinoNodeVisitors).toBeDefined(); + expect(typeof visitor.getRhinoNodeVisitors).toBe('function'); + }); + + describe('addRhinoNodeVisitor', function() { + function visitorA() {} + function visitorB() {} + + var visitors; + + beforeEach(newVisitor); + + it('should work with a single Rhino node visitor', function() { + visitor.addRhinoNodeVisitor(visitorA); + + visitors = visitor.getRhinoNodeVisitors(); + + expect(visitors.length).toBe(1); + expect(visitors[0]).toBe(visitorA); + }); + + it('should work with multiple Rhino node visitors', function() { + visitor.addRhinoNodeVisitor(visitorA); + visitor.addRhinoNodeVisitor(visitorB); + + visitors = visitor.getRhinoNodeVisitors(); + + expect(visitors.length).toBe(2); + expect(visitors[0]).toBe(visitorA); + expect(visitors[1]).toBe(visitorB); + }); + }); + + describe('getRhinoNodeVisitors', function() { + beforeEach(newVisitor); + + it('should return an empty array by default', function() { + var visitors = visitor.getRhinoNodeVisitors(); + + expect( Array.isArray(visitors) ).toBe(true); + expect(visitors.length).toBe(0); + }); + + // other functionality is covered by the addNodeVisitors tests + }); + }); +}); diff --git a/test/specs/tags/overviewtag.js b/test/specs/tags/overviewtag.js index 6087be5b..925412a9 100644 --- a/test/specs/tags/overviewtag.js +++ b/test/specs/tags/overviewtag.js @@ -1,9 +1,10 @@ /*global describe: true, env: true, expect: true, it: true */ describe("@overview tag", function() { - var parser = require('jsdoc/src/parser'), - srcParser = new parser.Parser(), - doclets; + var runtime = require('jsdoc/util/runtime'); + var parser = require( runtime.getModulePath('jsdoc/src/parser') ); + var srcParser = new parser.Parser(); + var doclets; require('jsdoc/src/handlers').attachTo(srcParser); doclets = srcParser.parse(__dirname + '/test/fixtures/file.js');