Permalink
Browse files

Initial revision.

0 parents commit 3ebaa95ed85ff86bd1ea97e1b051534bc925db76 Adam Moore committed Nov 24, 2010
Showing with 1,494 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +2 −0 .npmignore
  3. +25 −0 LICENSE
  4. +10 −0 README.md
  5. +1 −0 cli.js
  6. +900 −0 lib/yuidoc.js
  7. +31 −0 package.json
  8. +48 −0 test/test.js
  9. +108 −0 test2/dump/dump.js
  10. +368 −0 test2/oop/oop.js
1 .gitignore
@@ -0,0 +1 @@
+lib/yui3
2 .npmignore
@@ -0,0 +1,2 @@
+.git
+sandbox
25 LICENSE
@@ -0,0 +1,25 @@
+Copyright 2010 Yahoo! Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the Yahoo! Inc. nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL YAHOO! INC. BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
10 README.md
@@ -0,0 +1,10 @@
+
+Updated yuidoc parser, written in js -- early work in progress
+
+Usage:
+lib/yuidoc.js ../yui3/src
+
+This will produce a data structure in out/data.json by default.
+
+Random notes below.
+
1 cli.js
@@ -0,0 +1 @@
+#!/usr/bin/env node
900 lib/yuidoc.js
@@ -0,0 +1,900 @@
+#! /usr/bin/env node
+// -*- coding: utf-8 -*-
+// vim: et sw=4 ts=4
+
+var YUI = require("yui3").YUI;
+
+YUI.add('docparser', function(Y) {
+
+ var Lang = Y.Lang,
+ trim = Lang.trim,
+ stringify = Y.JSON.stringify,
+
+ CURRENT_NAMESPACE = 'currentnamespace',
+ CURRENT_MODULE = 'currentmodule',
+ CURRENT_SUBMODULE = 'currentsubmodule',
+ CURRENT_FILE = 'currentfile',
+ CURRENT_CLASS = 'currentclass',
+
+ REGEX_TYPE = /(.*)\{(.*)\}(.*)/,
+ REGEX_FIRSTWORD = /^\s*?([^\s]+)(.*)/,
+ REGEX_OPTIONAL = /\[(.*)\]/,
+ REGEX_START_COMMENT = /^\s*\/\*\*/,
+ REGEX_END_COMMENT = /\*\/\s*$/,
+ REGEX_LINES = /\r\n|\n/,
+
+ /**
+ * A list of known tags. This populates a member variable
+ * during initialization, and will be updated if additional
+ * digesters are added.
+ * @property TAGLIST
+ * @inner
+ * @final
+ */
+ TAGLIST = [
+ "async", // bool, events
+ "attribute",
+ "augments",
+ "beta", // module maturity
+ "broadcast", // bool, events
+ "bubbles",
+ "category",
+ "chainable",
+ "class",
+ "conditional", // conditional module
+ "config",
+ "const", // not standardized yet, converts to final property
+ "constructs", // factory methods
+ "constructor",
+ "default",
+ "deprecated",
+ "description",
+ "emitfacade", // bool, events
+ "extension", // this is an extension for [entity]
+ "fireonce", // bool, events
+ "event",
+ "example",
+ "experimental", // module maturity
+ "extends",
+ "file", // file name (used by the parser)
+ "final",
+ "for",
+ "global",
+ "in", // indicates module this lives in (obsolete now)
+ "initonly", // attribute writeonce value
+ "knownissue",
+ "line", // line number for the comment block (used by the parser)
+ "method",
+ "module",
+ "name",
+ "namespace",
+ "optional", // for module requirements (like 'requires')
+ "param",
+ "plugin", // this is a plugin for [entityl]
+ "preventable",
+ "private",
+ "property",
+ "protected",
+ "public",
+ "queuable", // bool, events
+ "readonly",
+ "requires",
+ "return",
+ "see",
+ "since",
+ "static",
+ "submodule",
+ "throws",
+ "title",
+ "todo",
+ "type",
+ "uses",
+ "value", // the value of a constant
+ "writeonce"
+ ],
+
+ /**
+ * Common errors will get scrubbed instead of being ignored.
+ * @property CORRECTIONS
+ * @inner
+ * @final
+ */
+ CORRECTIONS = {
+ 'depreciated': 'deprecated',
+ 'desciption': 'description',
+ 'extend': 'extends',
+ 'function': 'method',
+ 'member': 'method',
+ 'parm': 'param',
+ 'propery': 'property',
+ 'returns': 'return'
+ },
+
+ /**
+ * A map of the default tag processors, keyed by the
+ * tag name. Multiple tags can use the same digester
+ * by supplying the string name that points to the
+ * implementation rather than a function.
+ * @property DIGESTERS
+ * @inner
+ * @final
+ */
+ DIGESTERS = {
+
+ // @param {type} name description -or-
+ // @param name {type} description
+ // #2173362 optional w/ or w/o default
+ // @param {type} [optvar=default] description
+ // #12 document config objects
+ // @param {object|config} config description
+ // @param {type} config.prop1 description
+ // @param {type} config.prop2 description
+ // #11 document callback argument signature
+ // @param {callback|function} callback description
+ // @param {type} callback.arg1 description
+ // @param {type} callback.arg2 description
+ // #2173362 document event facade decorations for custom events
+ // @param {event} event description
+ // @param {type} event.child description
+ // @param {type} event.index description
+ 'param': function(tagname, value, target, block) {
+ // Y.log('param digester' + value);
+ target.params = target.params || [];
+
+ if (!value) {
+Y.log('param name/type/descript missing: ' + stringify(block, null, 4), 'warn');
+ return;
+ }
+
+ var type, name, desc = trim(value),
+ match = REGEX_TYPE.exec(desc),
+ optional, optdefault, parent,
+ host = target.params;
+
+ // Extract {type}
+ if (match) {
+ type = trim(match[2]);
+ desc = trim(match[1] + match[3]);
+ }
+
+ // extract the first word, this is the param name
+ match = REGEX_FIRSTWORD.exec(desc);
+ if (match) {
+ name = trim(match[1]);
+ desc = trim(match[2]);
+ }
+
+ if (!name) {
+Y.log('param name missing: ' + value + ':' + stringify(block, null, 4), 'warn');
+ name = 'UNKNOWN';
+ }
+
+ // extract [name], optional param
+ if (name.indexOf('[') > -1) {
+ match = REGEX_OPTIONAL.exec(name);
+ if (match) {
+ optional = true;
+ name = trim(match[1]);
+ // extract optional=defaultvalue
+ parts = name.split('=');
+ if (parts.length > 1) {
+ name = parts[0];
+ optdefault = parts[1];
+ }
+ }
+ }
+
+ // parse object.prop, indicating a child property for object
+ if (name.indexOf('.') > -1) {
+ match = name.split('.');
+ parent = trim(match[0]);
+
+ Y.some(target.params, function(param) {
+ if (param.name == parent) {
+ param.props = param.props || [];
+ host = param.props;
+ name = trim(match[1]);
+ return true;
+ }
+ });
+ }
+
+ result = {
+ name: name,
+ description: desc
+ };
+
+ if (type) {
+ result.type = type;
+ }
+
+ if (optional) {
+ result.optional = true;
+ if (optdefault) {
+ result.optdefault = optdefault;
+ }
+ }
+
+ host.push(result);
+
+ },
+
+ // @return {type} description // methods
+ // @returns {type} description // methods
+ // @throws {type} an error #2173342
+ // can be used by anthing that has an optional {type} and a description
+ 'return': function(tagname, value, target, block) {
+
+ var desc = value, type, match = REGEX_TYPE.exec(desc),
+ result = {};
+ if (match) {
+ type = trim(match[2]);
+ desc = trim(match[1] + match[3]);
+ }
+
+ result = {
+ description: desc
+ };
+
+ if (type) {
+ result.type = type;
+ }
+
+ target[tagname] = result;
+
+ },
+ 'throws': 'return',
+
+ // trying to overwrite the constructor value is a bad idea
+ 'constructor': function(tagname, value, target, block) {
+ target.is_constructor = 1;
+ },
+
+ // @author {twitter: @arthurdent | github: ArthurDent}
+ // Arthur Dent adent@h2g2.earth #23, multiple // modules/class/method
+ // 'author': function(tagname, value, target, block) {
+ // // Y.log('author digester');
+ // },
+
+ 'module': function(tagname, value, target, block) {
+ this.set(CURRENT_MODULE, value);
+ var go = true;
+ Y.each(block, function(o) {
+ if (trim(o.tag) == 'submodule') {
+ go = false;
+ }
+ });
+ if (go) {
+ host = this.modules[value];
+ return host;
+ }
+ return null;
+ },
+
+ 'submodule': function(tagname, value, target, block) {
+ this.set(CURRENT_SUBMODULE, value);
+ var host = this.modules[value],
+ parent = this.get(CURRENT_MODULE);
+ if (parent) {
+ host.module = parent;
+ }
+ return host;
+ },
+ 'class': function(tagname, value, target, block) {
+ this.set(CURRENT_CLASS, value);
+ var fullname = this.get(CURRENT_CLASS);
+ var host = this.classes[fullname],
+ parent = this.get(CURRENT_MODULE);
+ if (parent) {
+ host.module = parent;
+ }
+ parent = this.get(CURRENT_SUBMODULE);
+ if (parent) {
+ host.submodule = parent;
+ }
+ return host;
+ },
+
+ // change 'const' to final property
+ 'const': function(tagname, value, target, block) {
+ target.itemtype = property;
+ target.name = value;
+ target.final = ''
+ },
+
+ // supported classitems
+ 'property': function(tagname, value, target, block) {
+ target.itemtype = tagname;
+ target.name = value;
+ },
+ 'method': 'property',
+ 'attribute': 'property',
+ 'config': 'property',
+ 'event': 'property',
+
+ // access fields
+ 'public': function(tagname, value, target, block) {
+ target.access = tagname;
+ target.tagname = value;
+ },
+ 'private': 'public',
+ 'protected': 'public',
+ 'inner': 'public',
+
+ // tags that can have multiple tag occurances in a block
+ 'todo': function(tagname, value, target, block) {
+ if (!Lang.isArray(target[tagname])) {
+ target[tagname] = [];
+ }
+ target[tagname].push(value);
+ },
+ 'example': 'todo',
+ 'author': 'todo',
+ 'see': 'todo',
+ 'throws': 'todo',
+ 'requires': 'todo',
+ 'knownissue': 'todo',
+ 'optional': 'todo',
+ 'uses': 'todo',
+ 'category': 'todo',
+
+ // updates the current namespace
+ 'namespace': function(tagname, value, target, block) {
+ this.set(CURRENT_NAMESPACE, value);
+ var file = this.get(CURRENT_FILE);
+ if (file) {
+ this.files[file].namespaces[value] = 1;
+ }
+ var mod = this.get(CURRENT_MODULE);
+ if (mod) {
+ this.modules[mod].namespaces[value] = 1;
+ }
+
+ var mod = this.get(CURRENT_SUBMODULE);
+ if (mod) {
+ this.modules[mod].namespaces[value] = 1;
+ }
+ },
+
+ // updates the current class only
+ 'for': function(tagname, value, target, block) {
+ this.set(CURRENT_CLASS, value);
+ var file = this.get(CURRENT_FILE);
+ if (file) {
+ this.files[file].fors[value] = 1;
+ }
+ var mod = this.get(CURRENT_MODULE);
+ if (mod) {
+ this.modules[mod].fors[value] = 1;
+ }
+
+ var mod = this.get(CURRENT_SUBMODULE);
+ if (mod) {
+ this.modules[mod].fors[value];
+ }
+ }
+
+ },
+
+ DocParser = function(o) {
+ this.digesters = Y.merge(DIGESTERS);
+ this.knowntags = Y.Array.hash(TAGLIST);
+ DocParser.superclass.constructor.apply(this, arguments);
+ };
+
+ DocParser.NAME = 'DocParser';
+
+ DocParser.ATTRS = {
+ digesters: {
+ setter: function(val) {
+ Y.mix(this.digesters, val, true);
+ Y.mix(this.knowntags, val, true);
+ return val;
+ }
+ },
+ emitters: {
+ setter: function(val) {
+ Y.mix(this.emitters, val, true);
+ }
+ },
+ filemap: {
+ writeOnce : true
+ },
+ dirmap: {
+ writeOnce : true
+ },
+ currentfile: {
+ setter: function(val) {
+ val = trim(val);
+ // this.set(CURRENT_NAMESPACE, '');
+ if (!(val in this.files)) {
+ this.files[val] = {
+ name: val,
+ modules: {},
+ classes: {},
+ fors: {},
+ namespaces: {}
+ };
+ }
+ return val;
+ }
+ },
+ currentmodule: {
+ setter: function(val) {
+ if (!val) {
+ return val;
+ }
+ val = trim(val);
+ this.set(CURRENT_SUBMODULE, '');
+ this.set(CURRENT_NAMESPACE, '');
+ if (!(val in this.modules)) {
+ this.modules[val] = {
+ name: val,
+ submodules: {},
+ classes: {},
+ fors: {},
+ namespaces: {}
+ };
+ }
+ return val;
+ }
+ },
+ currentsubmodule: {
+ setter: function(val) {
+ if (!val) {
+ return val;
+ }
+ val = trim(val);
+ if (!(val in this.modules)) {
+ var mod = this.modules[val] = {
+ name: val,
+ submodules: {},
+ classes: {},
+ fors: {},
+ is_submodule: 1,
+ namespaces: {}
+ };
+ mod.module = this.get(CURRENT_MODULE);
+ mod.namespace = this.get(CURRENT_NAMESPACE);
+ }
+ return val;
+ }
+ },
+ currentclass: {
+ setter: function(val) {
+ if (!val) {
+ return val;
+ }
+ val = trim(val);
+ if (!(val in this.classes)) {
+ var ns = this.get(CURRENT_NAMESPACE),
+ name = (ns) ? ns + '.' + val : val,
+ clazz = this.classes[name] = {
+ name: name,
+ shortname: val,
+ classitems: [],
+ plugins: [],
+ extensions: [],
+ plugin_for: [],
+ extension_for: []
+ };
+ clazz.module = this.get(CURRENT_MODULE);
+ clazz.submodule = this.get(CURRENT_SUBMODULE)
+ clazz.namespace = ns;
+ }
+ return name;
+ }
+ }
+ };
+
+ Y.extend(DocParser, Y.Base, {
+
+ initializer: function() {
+
+ var self = this;
+
+ self.after('currentmoduleChange', function(e) {
+ var mod = e.newVal, classes = self.classes;
+ Y.each(classes, function(clazz) {
+ if (!(clazz.module)) {
+ clazz.module = mod;
+ }
+ });
+ });
+
+ self.after('currentsubmoduleChange', function(e) {
+ var mod = e.newVal, classes = self.classes,
+ parent;
+
+ if (mod) {
+ parent = self.modules[mod].module;
+ Y.each(classes, function(clazz) {
+ if (!(clazz.submodule)) {
+ if ((!clazz.module) || clazz.module == parent) {
+ clazz.submodule = mod;
+ }
+ }
+ });
+ }
+ });
+
+ self.after('currentclassChange', function(e) {
+ var clazz = e.newVal;
+ Y.each(self.classitems, function(item) {
+ if (!(item["class"])) {
+ item["class"] = clazz;
+ }
+ });
+ // Y.log(self.classitems);
+ });
+
+ },
+
+ /**
+ * Transforms a JavaDoc style comment block (less the start
+ * and end of it) into a list
+ * of tag/text pairs. The leading space and '*' are removed,
+ * but the remaining whitespace is preserved so that the
+ * output should be friendly for both markdown and html
+ * parsers.
+ */
+ handlecomment: function(comment, file, line) {
+ var lines = comment.split(REGEX_LINES),
+ len = lines.length, i,
+ parts, part, peek, skip,
+ results = [{tag: 'file', value: file},
+ {tag: 'line', value: line}],
+ linefix = /^\s*\*\s?/;
+
+// trim leading space and *, preserve the rest of the white space
+ for (i = 0; i < len; i++) {
+ lines[i] = lines[i].replace(linefix, ' ');
+ }
+
+// reconsitute and tokenize the comment block
+ comment = lines.join('\n');
+ parts = comment.split(/ (@\w*)/);
+ len = parts.length;
+ for (i = 0; i < len; i++) {
+ value = '';
+ part = parts[i];
+ skip = false;
+
+// the first token may be the description, otherwise it should be a tag
+ if (i === 0 && part.substr(0, 1) !== '@') {
+ if (part) {
+ tag = '@description';
+ value = part;
+ } else {
+ skip = true;
+ }
+ } else {
+ tag = part;
+ // lookahead for the tag value
+ peek = parts[i + 1];
+ if (peek) {
+ value = peek;
+ i++;
+ }
+ }
+
+ if (!skip && tag) {
+ results.push({
+ tag: tag.substr(1).toLowerCase(),
+ value: value
+ });
+ }
+ }
+
+ return results;
+ },
+
+ /**
+ * Accepts a map of filenames to file content. Returns
+ * a map of filenames to an array of API comment block
+ * text. This expects the comment to start with / **
+ * on its own line, and end with &amp;/ on its own
+ * line. Override this function to provide an
+ * alternative comment parser.
+ * @method extract
+ * @param {Object} filemap a map of filenames to file content'
+ * @return {Object} a map of filenames to an array of extracted
+ * comment text.
+ */
+ extract: function(filemap, dirmap) {
+ filemap = filemap || this.get('filemap');
+ dirmap = dirmap || this.get('dirmap');
+ var commentmap = {};
+ Y.each(filemap, function(code, filename) {
+
+ var commentlines, comment,
+ lines = code.split(REGEX_LINES),
+ len = lines.length, i, linenum;
+
+ for (i = 0; i < len; i++) {
+ line = lines[i];
+ if(REGEX_START_COMMENT.test(line)) {
+ commentlines = [];
+
+ linenum = i + 1;
+
+ while (i < len && (!REGEX_END_COMMENT.test(line))) {
+ commentlines.push(line);
+ i++;
+ line = lines[i];
+ }
+
+ // we can look ahead here if we need to guess the
+ // name/type like we do in the python version.
+
+ // remove /**
+ commentlines.shift();
+ comment = commentlines.join('\n');
+ commentmap[filename] = commentmap[filename] || [];
+ commentmap[filename]
+ .push(this.handlecomment(comment, filename, linenum));
+ }
+ }
+ }, this);
+
+ this.commentmap = commentmap;
+ return commentmap;
+ },
+
+ processblock: function(block, data) {
+ var target = {},
+ clazz,
+ module,
+ submodule,
+ digestname,
+ digester,
+ host;
+ // Y.log(block);
+ Y.each(block, function(tag) {
+ var name = trim(tag.tag),
+ value = trim(tag.value),
+ parent, ret;
+
+ if (tag && tag.tag) {
+
+ if (!(name in this.knowntags)) {
+ if (name in CORRECTIONS) {
+Y.log('replacing incorrect tag: ' + name + ' with ' + CORRECTIONS[name], 'warn');
+// Y.log(stringify(block, null, 4), 'warn');
+
+ name = CORRECTIONS[name];
+ } else {
+Y.log('unknown tag: ' + name + ',' + stringify(block, null, 4), 'warn');
+ }
+ }
+ digestname = name;
+ if (digestname in this.digesters) {
+ digester = this.digesters[digestname];
+ if (Lang.isString(digester)) {
+ digester = this.digesters[digester];
+ }
+ ret = digester.call(this, name, value, target, block);
+ host = host || ret;
+ } else {
+ target[name] = value;
+ }
+ }
+
+ }, this);
+
+ if (host) {
+ Y.mix(host, target);
+ // // constructor is a legal tag skipped by mix
+ // if ('constructor' in target) {
+ // Y.log('CONSTRUCTOR: ' + target.constructor);
+ // host.constructor = target.constructor;
+ // }
+ } else {
+ this.classitems.push(target);
+ target['class'] = this.get(CURRENT_CLASS);
+ target.module = this.get(CURRENT_MODULE);
+ host = this.get(CURRENT_SUBMODULE);
+ if (host) {
+ target.submodule = host;
+ }
+ host = this.get(CURRENT_NAMESPACE);
+ if (host) {
+ target.namespace = host;
+ }
+ }
+ },
+
+ /**
+ * Transforms a map of filenames to arrays of comment blocks into a
+ * JSON structure that represents the entire processed API doc info
+ * and relationships between elements for the entire project.
+ * @method transform
+ * @param {object} commentmap
+ * @return {object} the transformed data for the project
+ */
+ transform: function(commentmap) {
+ var self = this,
+ files = self.files = {},
+ modules = self.modules = {},
+ classes = self.classes = {},
+ classitems = self.classitems = [],
+ data = self.data = {
+ files: files,
+ modules: modules,
+ classes: classes,
+ classitems: classitems
+ };
+
+ commentmap = commentmap || self.commentmap;
+
+ // process
+ Y.each(commentmap, function(blocks, file) {
+ Y.log('transform: ' + file);
+ self.set(CURRENT_FILE, file);
+ Y.each(blocks, function(block) {
+ self.processblock(block, data);
+ });
+ });
+
+ // cross reference
+ Y.each(modules, function(module, name) {
+ if (module.file) {
+ files[module.file].modules[name] = 1;
+ }
+ });
+
+ Y.each(classes, function(clazz, name) {
+ if (clazz.module) {
+ modules[clazz.module].classes[name] = 1;
+ }
+ if (clazz.submodule) {
+ modules[clazz.submodule].classes[name] = 1;
+ }
+ if (clazz.file) {
+ files[clazz.file].classes[name] = 1;
+ }
+ });
+
+ return self;
+ },
+
+ /**
+ * Extracts and transforms the filemap provided to constructor
+ * @method parse
+ * @param filemap a map of filenames to file content'
+ * @return {DocParser} this parser instance. The total results
+ * are available in parser.data.
+ */
+ parse: function(filemap, dirmap) {
+ filemap = filemap || this.get('filemap');
+ dirmap = dirmap || this.get('dirmap');
+ return this.transform(this.extract(filemap, dirmap));
+ }
+
+ });
+
+ Y.DocParser = DocParser;
+
+}, '0.1.0', { requires: ['base-base', 'json-stringify'] });
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+
+YUI({
+ // filter: 'debug'
+}).use('docparser', function(Y) {
+
+var fs = require("fs"),
+ sys = require("sys"),
+ path = require("path"),
+
+ options = {
+ outdir: 'out',
+ extension: '.js',
+ exclude: '.DS_Store,.svn,CVS,.git,build_rollup_tmp,build_tmp',
+ norecurse: false,
+ version: '0.1.0'
+ },
+ args = Y.Array(process.argv, 2),
+ paths = [],
+ config = function() {
+ while (args.length > 0) {
+ var v = args.shift();
+ switch (v) {
+ case "-e":
+ case "--extension":
+ options.extension = args.shift();
+ break;
+ case "-x":
+ case "--exclude":
+ options.extension = args.shift();
+ break;
+ case "-v":
+ case "--version":
+ options.version = args.shift();
+ break;
+ case "-n":
+ case "--norecurse":
+ options.norecurse = true;
+ break;
+ case "-o":
+ case "--outdir":
+ options.outdir = args.shift();
+ break;
+ default:
+ paths.push(v);
+ }
+ }
+ options.extensions = Y.Array.hash(options.extension.split(','));
+ options.excludes = Y.Array.hash(options.exclude.split(','));
+ },
+
+ output = function(parser) {
+ // Y.log(Y.JSON.stringify(parser.data, null, 4));
+ var file = path.join(options.outdir, 'data.json'), out;
+ try {
+ fs.mkdirSync(options.outdir, 0777);
+ } catch(e) { }
+ out = fs.createWriteStream(file, {
+ flags: "w", encoding: "utf8", mode: 0644
+ });
+ out.write(Y.JSON.stringify(parser.data, null, 4));
+ out.end();
+ },
+
+ filemap = {},
+ dirmap = {},
+
+ run = function() {
+ output(new Y.DocParser({
+ filemap: filemap,
+ dirmap: dirmap
+ }).parse());
+ },
+
+ parsefiles = function(dir, files) {
+ Y.each(files, function(filename) {
+ var dot = filename.indexOf('.'), ext, text, fullpath;
+ if (dot) {
+ ext = filename.substr(dot);
+ if (ext in options.extensions) {
+ fullpath = path.join(dir, filename);
+ text = fs.readFileSync(fullpath, "utf8");
+ filemap[fullpath] = text;
+ dirmap[fullpath] = dir;
+ }
+ }
+ });
+ },
+
+ parsedir = function(dir) {
+ var allfiles = fs.readdirSync(dir), stats,
+ files = [], fullpath;
+ Y.each(allfiles, function(filename) {
+ if (!(filename in options.excludes)) {
+ fullpath = path.join(dir, filename);
+ stats = fs.statSync(fullpath);
+ if (stats.isDirectory() && !options.norecurse) {
+ parsedir(fullpath);
+ } else {
+ files.push(filename);
+ }
+ }
+ });
+
+ parsefiles(dir, files);
+ },
+
+ load = function() {
+ Y.each(paths, function(dir) {
+ parsedir(dir);
+ });
+ };
+
+ config();
+ load();
+ run();
+
+});
+
31 package.json
@@ -0,0 +1,31 @@
+{
+ "name": "yuidocjs",
+ "version": "0.1.0",
+ "description": "YUI Doc, JavaScript port",
+ "author": "Adam Moore <amoore@gmail.com>",
+ "bugs": { "web" : "http://github.com/apm/yuidocjs/issues" },
+ "os": ["darwin", "linux"],
+ "contributors": [
+ ],
+ "engines": {
+ "node" : ">=0.1.100"
+ },
+ "directories": {
+ "lib" : "lib"
+ },
+ "main": "./lib/yuidoc",
+ "bin" : { "yuidocdocs" : "./cli.js" },
+ "dependencies": {
+ "yui3-core": ">=3.2.0"
+ },
+ "licenses":[
+ {
+ "type" : "BSD",
+ "url" : "http://developer.yahoo.com/yui/license.html"
+ }
+ ],
+ "repository": {
+ "type":"git",
+ "url":"http://github.com/apm/yuidocjs.git"
+ }
+}
48 test/test.js
@@ -0,0 +1,48 @@
+/**
+ * The module
+ * @module mymodule
+ * @category one,two
+ * @category three
+ * @requires one
+ * @requires two
+ * @uses three
+ * @uses four
+ */
+
+/**
+ * The submodule
+ * @submodule mysubmodule
+ * @category three,four
+ */
+
+
+/**
+ * The class def
+ * @class myclass
+ * @constructor
+ */
+
+/**
+ * test optional
+ * @method testoptional
+ * @param notype my desc
+ * @param {int} namesecond my desc
+ * @param namefirst {string} my desc
+ * @param [optionalvar] {bool} my desc
+ * @param {string} [optionalwithdefault="defaultval"] my desc
+ * @returns something without a type
+ * @example
+ * This is code
+ * @example
+ * This is more code
+ */
+
+/**
+ * test object param
+ * @method testobjectparam
+ * @param {object} anobject the object
+ * @param {string} anobject.prop1 prop1
+ * @param {bool} anobject.prop2 prop2
+ * @return {string} something with a type
+ */
+
108 test2/dump/dump.js
@@ -0,0 +1,108 @@
+/**
+ * Returns a simple string representation of the object or array.
+ * Other types of objects will be returned unprocessed. Arrays
+ * are expected to be indexed. Use object notation for
+ * associative arrays.
+ *
+ * If included, the dump method is added to the YUI instance.
+ *
+ * @module dump
+ */
+
+ var L = Y.Lang,
+ OBJ = '{...}',
+ FUN = 'f(){...}',
+ COMMA = ', ',
+ ARROW = ' => ',
+
+ /**
+ * The following methods are added to the YUI instance
+ * @class YUI~dump
+ */
+
+ /**
+ * Returns a simple string representation of the object or array.
+ * Other types of objects will be returned unprocessed. Arrays
+ * are expected to be indexed. Use object notation for
+ * associative arrays.
+ *
+ * This method is in the 'dump' module, which is not bundled with
+ * the core YUI object
+ *
+ * @method dump
+ * @param {object} o The object to dump.
+ * @param {int} d How deep to recurse child objects, default 3.
+ * @return {string} the dump result.
+ */
+ dump = function(o, d) {
+ var i, len, s = [], type = L.type(o);
+
+ // Cast non-objects to string
+ // Skip dates because the std toString is what we want
+ // Skip HTMLElement-like objects because trying to dump
+ // an element will cause an unhandled exception in FF 2.x
+ if (!L.isObject(o)) {
+ return o + '';
+ } else if (type == 'date') {
+ return o;
+ } else if (o.nodeType && o.tagName) {
+ return o.tagName + '#' + o.id;
+ } else if (o.document && o.navigator) {
+ return 'window';
+ } else if (o.location && o.body) {
+ return 'document';
+ } else if (type == 'function') {
+ return FUN;
+ }
+
+ // dig into child objects the depth specifed. Default 3
+ d = (L.isNumber(d)) ? d : 3;
+
+ // arrays [1, 2, 3]
+ if (type == 'array') {
+ s.push('[');
+ for (i = 0, len = o.length; i < len; i = i + 1) {
+ if (L.isObject(o[i])) {
+ s.push((d > 0) ? L.dump(o[i], d - 1) : OBJ);
+ } else {
+ s.push(o[i]);
+ }
+ s.push(COMMA);
+ }
+ if (s.length > 1) {
+ s.pop();
+ }
+ s.push(']');
+ // regexp /foo/
+ } else if (type == 'regexp') {
+ s.push(o.toString());
+ // objects {k1 => v1, k2 => v2}
+ } else {
+ s.push('{');
+ for (i in o) {
+ if (o.hasOwnProperty(i)) {
+ try {
+ s.push(i + ARROW);
+ if (L.isObject(o[i])) {
+ s.push((d > 0) ? L.dump(o[i], d - 1) : OBJ);
+ } else {
+ s.push(o[i]);
+ }
+ s.push(COMMA);
+ } catch (e) {
+ s.push('Error: ' + e.message);
+ }
+ }
+ }
+ if (s.length > 1) {
+ s.pop();
+ }
+ s.push('}');
+ }
+
+ return s.join('');
+ };
+
+ Y.dump = dump;
+ L.dump = dump;
+
368 test2/oop/oop.js
@@ -0,0 +1,368 @@
+/**
+ * Supplies object inheritance and manipulation utilities. This adds
+ * additional functionaity to what is provided in yui-base, and the
+ * methods are applied directly to the YUI instance. This module
+ * is required for most YUI components.
+ * @module oop
+ */
+
+/**
+ * The following methods are added to the YUI instance
+ * @class YUI~oop
+ */
+
+ var L = Y.Lang,
+ A = Y.Array,
+ OP = Object.prototype,
+ CLONE_MARKER = '_~yuim~_',
+ EACH = 'each',
+ SOME = 'some',
+
+ dispatch = function(o, f, c, proto, action) {
+ if (o && o[action] && o !== Y) {
+ return o[action].call(o, f, c);
+ } else {
+ switch (A.test(o)) {
+ case 1:
+ return A[action](o, f, c);
+ case 2:
+ return A[action](Y.Array(o, 0, true), f, c);
+ default:
+ return Y.Object[action](o, f, c, proto);
+ }
+ }
+ };
+
+
+ /**
+ * Applies prototype properties from the supplier to the receiver.
+ * The receiver can be a constructor or an instance.
+ * @method augment
+ * @param {function} r the object to receive the augmentation.
+ * @param {function} s the object that supplies the properties to augment.
+ * @param {boolean} ov if true, properties already on the receiver
+ * will be overwritten if found on the supplier.
+ * @param {string[]} wl a whitelist. If supplied, only properties in
+ * this list will be applied to the receiver.
+ * @param {Array | Any} args arg or arguments to apply to the supplier
+ * constructor when initializing.
+ * @return {object} the augmented object.
+ *
+ * @todo constructor optional?
+ * @todo understanding what an instance is augmented with
+ * @todo best practices for overriding sequestered methods.
+ */
+ Y.augment = function(r, s, ov, wl, args) {
+ var sProto = s.prototype,
+ newProto = null,
+ construct = s,
+ a = (args) ? Y.Array(args) : [],
+ rProto = r.prototype,
+ target = rProto || r,
+ applyConstructor = false,
+ sequestered, replacements;
+
+ // working on a class, so apply constructor infrastructure
+ if (rProto && construct) {
+ sequestered = {};
+ replacements = {};
+ newProto = {};
+
+ // sequester all of the functions in the supplier and replace with
+ // one that will restore all of them.
+ Y.Object.each(sProto, function(v, k) {
+ replacements[k] = function() {
+
+ // Y.log('sequestered function "' + k +
+ // '" executed. Initializing EventTarget');
+ // overwrite the prototype with all of the sequestered functions,
+ // but only if it hasn't been overridden
+ for (var i in sequestered) {
+ if (sequestered.hasOwnProperty(i) &&
+ (this[i] === replacements[i])) {
+ // Y.log('... restoring ' + k);
+ this[i] = sequestered[i];
+ }
+ }
+
+ // apply the constructor
+ construct.apply(this, a);
+
+ // apply the original sequestered function
+ return sequestered[k].apply(this, arguments);
+ };
+
+ if ((!wl || (k in wl)) && (ov || !(k in this))) {
+ // Y.log('augment: ' + k);
+ if (L.isFunction(v)) {
+ // sequester the function
+ sequestered[k] = v;
+
+// replace the sequestered function with a function that will
+// restore all sequestered functions and exectue the constructor.
+ this[k] = replacements[k];
+ } else {
+ // Y.log('augment() applying non-function: ' + k);
+ this[k] = v;
+ }
+ }
+
+ }, newProto, true);
+
+ // augmenting an instance, so apply the constructor immediately
+ } else {
+ applyConstructor = true;
+ }
+
+ Y.mix(target, newProto || sProto, ov, wl);
+
+ if (applyConstructor) {
+ s.apply(target, a);
+ }
+
+ return r;
+ };
+
+ /**
+ * Applies object properties from the supplier to the receiver. If
+ * the target has the property, and the property is an object, the target
+ * object will be augmented with the supplier's value. If the property
+ * is an array, the suppliers value will be appended to the target.
+ * @method aggregate
+ * @param {function} r the object to receive the augmentation.
+ * @param {function} s the object that supplies the properties to augment.
+ * @param {boolean} ov if true, properties already on the receiver
+ * will be overwritten if found on the supplier.
+ * @param {string[]} wl a whitelist. If supplied, only properties in
+ * this list will be applied to the receiver.
+ * @return {object} the extended object.
+ */
+ Y.aggregate = function(r, s, ov, wl) {
+ return Y.mix(r, s, ov, wl, 0, true);
+ };
+
+ /**
+ * Utility to set up the prototype, constructor and superclass properties to
+ * support an inheritance strategy that can chain constructors and methods.
+ * Static members will not be inherited.
+ *
+ * @method extend
+ * @param {function} r the object to modify.
+ * @param {function} s the object to inherit.
+ * @param {object} px prototype properties to add/override.
+ * @param {object} sx static properties to add/override.
+ * @return {object} the extended object.
+ */
+ Y.extend = function(r, s, px, sx) {
+ if (!s || !r) {
+ Y.error('extend failed, verify dependencies');
+ }
+
+ var sp = s.prototype, rp = Y.Object(sp);
+ r.prototype = rp;
+
+ rp.constructor = r;
+ r.superclass = sp;
+
+ // assign constructor property
+ if (s != Object && sp.constructor == OP.constructor) {
+ sp.constructor = s;
+ }
+
+ // add prototype overrides
+ if (px) {
+ Y.mix(rp, px, true);
+ }
+
+ // add object overrides
+ if (sx) {
+ Y.mix(r, sx, true);
+ }
+
+ return r;
+ };
+
+ /**
+ * Executes the supplied function for each item in
+ * a collection. Supports arrays, objects, and
+ * Y.NodeLists
+ * @method each
+ * @param {object} o the object to iterate.
+ * @param {function} f the function to execute. This function
+ * receives the value, key, and object as parameters.
+ * @param {object} c the execution context for the function.
+ * @param {boolean} proto if true, prototype properties are
+ * iterated on objects.
+ * @return {YUI} the YUI instance.
+ */
+ Y.each = function(o, f, c, proto) {
+ return dispatch(o, f, c, proto, EACH);
+ };
+
+ /**
+ * Executes the supplied function for each item in
+ * a collection. The operation stops if the function
+ * returns true. Supports arrays, objects, and
+ * Y.NodeLists.
+ * @method some
+ * @param {object} o the object to iterate.
+ * @param {function} f the function to execute. This function
+ * receives the value, key, and object as parameters.
+ * @param {object} c the execution context for the function.
+ * @param {boolean} proto if true, prototype properties are
+ * iterated on objects.
+ * @return {boolean} true if the function ever returns true,
+ * false otherwise.
+ */
+ Y.some = function(o, f, c, proto) {
+ return dispatch(o, f, c, proto, SOME);
+ };
+
+ /**
+ * Deep obj/array copy. Function clones are actually
+ * wrappers around the original function.
+ * Array-like objects are treated as arrays.
+ * Primitives are returned untouched. Optionally, a
+ * function can be provided to handle other data types,
+ * filter keys, validate values, etc.
+ *
+ * @method clone
+ * @param {object} o what to clone.
+ * @param {boolean} safe if true, objects will not have prototype
+ * items from the source. If false, they will. In this case, the
+ * original is initially protected, but the clone is not completely
+ * immune from changes to the source object prototype. Also, cloned
+ * prototype items that are deleted from the clone will result
+ * in the value of the source prototype being exposed. If operating
+ * on a non-safe clone, items should be nulled out rather than deleted.
+ * @param {function} f optional function to apply to each item in a
+ * collection; it will be executed prior to applying the value to
+ * the new object. Return false to prevent the copy.
+ * @param {object} c optional execution context for f.
+ * @param {object} owner Owner object passed when clone is iterating
+ * an object. Used to set up context for cloned functions.
+ * @param {object} cloned hash of previously cloned objects to avoid
+ * multiple clones.
+ * @return {Array|Object} the cloned object.
+ */
+ Y.clone = function(o, safe, f, c, owner, cloned) {
+
+ if (!L.isObject(o)) {
+ return o;
+ }
+
+ // @todo cloning YUI instances doesn't currently work
+ if (Y.instanceOf(o, YUI)) {
+ return o;
+ }
+
+ var o2, marked = cloned || {}, stamp,
+ yeach = Y.each;
+
+ switch (L.type(o)) {
+ case 'date':
+ return new Date(o);
+ case 'regexp':
+ // if we do this we need to set the flags too
+ // return new RegExp(o.source);
+ return o;
+ case 'function':
+ // o2 = Y.bind(o, owner);
+ // break;
+ return o;
+ case 'array':
+ o2 = [];
+ break;
+ default:
+
+ // #2528250 only one clone of a given object should be created.
+ if (o[CLONE_MARKER]) {
+ return marked[o[CLONE_MARKER]];
+ }
+
+ stamp = Y.guid();
+
+ o2 = (safe) ? {} : Y.Object(o);
+
+ o[CLONE_MARKER] = stamp;
+ marked[stamp] = o;
+ }
+
+ // #2528250 don't try to clone element properties
+ if (!o.addEventListener && !o.attachEvent) {
+ yeach(o, function(v, k) {
+if ((k || k === 0) && (!f || (f.call(c || this, v, k, this, o) !== false))) {
+ if (k !== CLONE_MARKER) {
+ if (k == 'prototype') {
+ // skip the prototype
+ // } else if (o[k] === o) {
+ // this[k] = this;
+ } else {
+ this[k] =
+ Y.clone(v, safe, f, c, owner || o, marked);
+ }
+ }
+ }
+ }, o2);
+ }
+
+ if (!cloned) {
+ Y.Object.each(marked, function(v, k) {
+ delete v[CLONE_MARKER];
+ });
+ marked = null;
+ }
+
+ return o2;
+ };
+
+
+ /**
+ * Returns a function that will execute the supplied function in the
+ * supplied object's context, optionally adding any additional
+ * supplied parameters to the beginning of the arguments collection the
+ * supplied to the function.
+ *
+ * @method bind
+ * @param {Function|String} f the function to bind, or a function name
+ * to execute on the context object.
+ * @param {object} c the execution context.
+ * @param {any} args* 0..n arguments to include before the arguments the
+ * function is executed with.
+ * @return {function} the wrapped function.
+ */
+ Y.bind = function(f, c) {
+ var xargs = arguments.length > 2 ?
+ Y.Array(arguments, 2, true) : null;
+ return function() {
+ var fn = L.isString(f) ? c[f] : f,
+ args = (xargs) ?
+ xargs.concat(Y.Array(arguments, 0, true)) : arguments;
+ return fn.apply(c || fn, args);
+ };
+ };
+
+ /**
+ * Returns a function that will execute the supplied function in the
+ * supplied object's context, optionally adding any additional
+ * supplied parameters to the end of the arguments the function
+ * is executed with.
+ *
+ * @method rbind
+ * @param {Function|String} f the function to bind, or a function name
+ * to execute on the context object.
+ * @param {object} c the execution context.
+ * @param {any} args* 0..n arguments to append to the end of
+ * arguments collection supplied to the function.
+ * @return {function} the wrapped function.
+ */
+ Y.rbind = function(f, c) {
+ var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null;
+ return function() {
+ var fn = L.isString(f) ? c[f] : f,
+ args = (xargs) ?
+ Y.Array(arguments, 0, true).concat(xargs) : arguments;
+ return fn.apply(c || fn, args);
+ };
+ };
+

0 comments on commit 3ebaa95

Please sign in to comment.