spacedocs 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/dox/lib/dox.js ADDED
@@ -0,0 +1,286 @@
1
+ /*!
2
+ * Module dependencies.
3
+ */
4
+
5
+ var markdown = require('github-flavored-markdown').parse
6
+ , escape = require('./utils').escape;
7
+
8
+ /**
9
+ * Library version.
10
+ */
11
+
12
+ exports.version = '0.3.1';
13
+
14
+ /**
15
+ * Parse comments in the given string of `js`.
16
+ *
17
+ * @param {String} js
18
+ * @param {Object} options
19
+ * @return {Array}
20
+ * @see exports.parseComment
21
+ * @api public
22
+ */
23
+
24
+ exports.parseComments = function(js, options){
25
+ js = js.trim()
26
+
27
+ options = options || {};
28
+
29
+ var comments = []
30
+ , raw = options.raw
31
+ , comment
32
+ , buf = ''
33
+ , ignore
34
+ , within
35
+ , code
36
+ , indent;
37
+
38
+ for (var i = 0, len = js.length; i < len; ++i) {
39
+ // start comment
40
+ if (!within && '/' == js[i] && '*' == js[i+1]) {
41
+ // code following previous comment
42
+ if (buf.trim().length) {
43
+ comment = comments[comments.length - 1];
44
+ if(comment) {
45
+ comment.code = code = buf.trim();
46
+ comment.ctx = exports.parseCodeContext(code);
47
+ }
48
+ buf = '';
49
+ }
50
+ i += 2;
51
+ within = true;
52
+ ignore = '!' == js[i];
53
+ // end comment
54
+ } else if (within && '*' == js[i] && '/' == js[i+1]) {
55
+ i += 2;
56
+ lines = buf.split('\n');
57
+
58
+ indentLevel = lines[lines.length - 1].length;
59
+ indentRegex = new RegExp('^[ \\t]{' + indentLevel + '}', 'gm');
60
+ buf = buf.replace(indentRegex, '');
61
+
62
+ buf = buf.replace(/^[ \t]*\* ?/gm, '');
63
+ var comment = exports.parseComment(buf, options);
64
+ comment.ignore = ignore;
65
+ comments.push(comment);
66
+ within = ignore = false;
67
+ buf = '';
68
+ // buffer comment or code
69
+ } else {
70
+ buf += js[i];
71
+ }
72
+ }
73
+
74
+ // trailing code
75
+ if (buf.trim().length) {
76
+ comment = comments[comments.length - 1];
77
+ code = buf.trim();
78
+ comment.code = code;
79
+ comment.ctx = exports.parseCodeContext(code);
80
+ }
81
+
82
+ return comments;
83
+ };
84
+
85
+ /**
86
+ * Parse the given comment `str`.
87
+ *
88
+ * The comment object returned contains the following
89
+ *
90
+ * - `tags` array of tag objects
91
+ * - `description` the first line of the comment
92
+ * - `body` lines following the description
93
+ * - `content` both the description and the body
94
+ * - `isPrivate` true when "@api private" is used
95
+ *
96
+ * @param {String} str
97
+ * @param {Object} options
98
+ * @return {Object}
99
+ * @see exports.parseTag
100
+ * @api public
101
+ */
102
+
103
+ exports.parseComment = function(str, options) {
104
+ str = str.trim();
105
+ options = options || {};
106
+
107
+ var comment = { tags: [] }
108
+ , raw = options.raw
109
+ , description = {};
110
+
111
+ // parse comment body
112
+ description.full = str.split('\n@')[0].replace(/^([A-Z][\w ]+):$/gm, '## $1');
113
+ description.summary = description.full.split('\n\n')[0];
114
+ description.body = description.full.split('\n\n').slice(1).join('\n\n');
115
+ comment.description = description;
116
+
117
+ // parse tags
118
+ if (~str.indexOf('\n@')) {
119
+ var tags = '@' + str.split('\n@').slice(1).join('\n@');
120
+ comment.tags = tags.split('\n').map(exports.parseTag);
121
+ comment.isPrivate = comment.tags.some(function(tag){
122
+ return 'api' == tag.type && 'private' == tag.visibility;
123
+ })
124
+ }
125
+
126
+ // markdown
127
+ if (!raw) {
128
+ description.full = markdown(description.full);
129
+ description.summary = markdown(description.summary);
130
+ description.body = markdown(description.body);
131
+ }
132
+
133
+ return comment;
134
+ }
135
+
136
+ /**
137
+ * Parse tag string "@param {Array} name description" etc.
138
+ *
139
+ * @param {String}
140
+ * @return {Object}
141
+ * @api public
142
+ */
143
+
144
+ exports.parseTag = function(str) {
145
+ var tag = {}
146
+ , parts = str.split(/ +/)
147
+ , type = tag.type = parts.shift().replace('@', '');
148
+
149
+ switch (type) {
150
+ case 'param':
151
+ tag.types = exports.parseTagTypes(parts.shift());
152
+ tag.name = parts.shift() || '';
153
+ tag.description = parts.join(' ');
154
+ break;
155
+ case 'return':
156
+ tag.types = exports.parseTagTypes(parts.shift());
157
+ tag.description = parts.join(' ');
158
+ break;
159
+ case 'see':
160
+ if (~str.indexOf('http')) {
161
+ tag.title = parts.length > 1
162
+ ? parts.shift()
163
+ : '';
164
+ tag.url = parts.join(' ');
165
+ } else {
166
+ tag.local = parts.join(' ');
167
+ }
168
+ case 'api':
169
+ tag.visibility = parts.shift();
170
+ break;
171
+ case 'type':
172
+ tag.types = exports.parseTagTypes(parts.shift());
173
+ break;
174
+ case 'memberOf':
175
+ tag.parent = parts.shift();
176
+ break;
177
+ case 'augments':
178
+ tag.otherClass = parts.shift();
179
+ break;
180
+ case 'borrows':
181
+ tag.otherMemberName = parts.join(' ').split(' as ')[0];
182
+ tag.thisMemberName = parts.join(' ').split(' as ')[1];
183
+ break;
184
+ default:
185
+ tag.string = parts.join(' ');
186
+ break;
187
+ }
188
+
189
+ return tag;
190
+ }
191
+
192
+ /**
193
+ * Parse tag type string "{Array|Object}" etc.
194
+ *
195
+ * @param {String} str
196
+ * @return {Array}
197
+ * @api public
198
+ */
199
+
200
+ exports.parseTagTypes = function(str) {
201
+ return str
202
+ .replace(/[{}]/g, '')
203
+ .split(/ *[|,\/] */);
204
+ };
205
+
206
+ /**
207
+ * Parse the context from the given `str` of js.
208
+ *
209
+ * This method attempts to discover the context
210
+ * for the comment based on it's code. Currently
211
+ * supports:
212
+ *
213
+ * - function statements
214
+ * - function expressions
215
+ * - prototype methods
216
+ * - prototype properties
217
+ * - methods
218
+ * - properties
219
+ * - declarations
220
+ *
221
+ * @param {String} str
222
+ * @return {Object}
223
+ * @api public
224
+ */
225
+
226
+ exports.parseCodeContext = function(str){
227
+ var str = str.split('\n')[0];
228
+
229
+ // function statement
230
+ if (/^function (\w+) *\(/.exec(str)) {
231
+ return {
232
+ type: 'function'
233
+ , name: RegExp.$1
234
+ , string: RegExp.$1 + '()'
235
+ };
236
+ // function expression
237
+ } else if (/^var *(\w+) *= *function/.exec(str)) {
238
+ return {
239
+ type: 'function'
240
+ , name: RegExp.$1
241
+ , string: RegExp.$1 + '()'
242
+ };
243
+ // prototype method
244
+ } else if (/^(\w+)\.prototype\.(\w+) *= *function/.exec(str)) {
245
+ return {
246
+ type: 'method'
247
+ , constructor: RegExp.$1
248
+ , name: RegExp.$2
249
+ , string: RegExp.$1 + '.prototype.' + RegExp.$2 + '()'
250
+ };
251
+ // prototype property
252
+ } else if (/^(\w+)\.prototype\.(\w+) *= *([^\n;]+)/.exec(str)) {
253
+ return {
254
+ type: 'property'
255
+ , constructor: RegExp.$1
256
+ , name: RegExp.$2
257
+ , value: RegExp.$3
258
+ , string: RegExp.$1 + '.prototype' + RegExp.$2
259
+ };
260
+ // method
261
+ } else if (/^(\w+)\.(\w+) *= *function/.exec(str)) {
262
+ return {
263
+ type: 'method'
264
+ , receiver: RegExp.$1
265
+ , name: RegExp.$2
266
+ , string: RegExp.$1 + '.' + RegExp.$2 + '()'
267
+ };
268
+ // property
269
+ } else if (/^(\w+)\.(\w+) *= *([^\n;]+)/.exec(str)) {
270
+ return {
271
+ type: 'property'
272
+ , receiver: RegExp.$1
273
+ , name: RegExp.$2
274
+ , value: RegExp.$3
275
+ , string: RegExp.$1 + '.' + RegExp.$2
276
+ };
277
+ // declaration
278
+ } else if (/^var +(\w+) *= *([^\n;]+)/.exec(str)) {
279
+ return {
280
+ type: 'declaration'
281
+ , name: RegExp.$1
282
+ , value: RegExp.$2
283
+ , string: RegExp.$1
284
+ };
285
+ }
286
+ };
data/dox/lib/utils.js ADDED
@@ -0,0 +1,15 @@
1
+
2
+ /**
3
+ * Escape the given `html`.
4
+ *
5
+ * @param {String} html
6
+ * @return {String}
7
+ * @api private
8
+ */
9
+
10
+ exports.escape = function(html){
11
+ return String(html)
12
+ .replace(/&(?!\w+;)/g, '&amp;')
13
+ .replace(/</g, '&lt;')
14
+ .replace(/>/g, '&gt;');
15
+ };
data/dox/package.json ADDED
@@ -0,0 +1,16 @@
1
+ { "name": "dox"
2
+ , "description": "Markdown / JSdoc documentation generator"
3
+ , "version": "0.3.1"
4
+ , "author": "TJ Holowaychuk <tj@vision-media.ca>"
5
+ , "repository": { "type": "git", "url": "git://github.com/visionmedia/dox.git" }
6
+ , "keywords": ["documentation", "docs", "markdown", "jsdoc"]
7
+ , "bin": { "dox": "./bin/dox" }
8
+ , "dependencies": {
9
+ "github-flavored-markdown": ">= 0.0.1"
10
+ , "commander": "0.5.2"
11
+ }
12
+ , "devDependencies": {
13
+ "mocha": "*"
14
+ , "should": "*"
15
+ }
16
+ }
@@ -0,0 +1,287 @@
1
+
2
+ /**
3
+ * Module dependencies.
4
+ */
5
+
6
+ var dox = require('../')
7
+ , should = require('should')
8
+ , fs = require('fs');
9
+
10
+ function fixture(name, fn) {
11
+ fs.readFile(__dirname + '/fixtures/' + name, 'utf8', fn);
12
+ }
13
+
14
+ module.exports = {
15
+ 'test .version': function(){
16
+ dox.version.should.match(/^\d+\.\d+\.\d+$/);
17
+ },
18
+
19
+ 'test .parseComments() blocks': function(done){
20
+ fixture('a.js', function(err, str){
21
+ var comments = dox.parseComments(str)
22
+ , file = comments.shift()
23
+ , version = comments.shift();
24
+ file.should.have.property('ignore', true);
25
+ file.description.full.should.equal('<p>A<br />Copyright (c) 2010 Author Name <Author Email><br />MIT Licensed</p>');
26
+ file.description.summary.should.equal('<p>A<br />Copyright (c) 2010 Author Name <Author Email><br />MIT Licensed</p>');
27
+ file.description.body.should.equal('');
28
+ file.tags.should.be.empty;
29
+
30
+ version.should.have.property('ignore', false);
31
+ version.description.full.should.equal('<p>Library version.</p>');
32
+ version.description.summary.should.equal('<p>Library version.</p>');
33
+ version.description.body.should.equal('');
34
+ version.tags.should.be.empty;
35
+ done();
36
+ });
37
+ },
38
+
39
+ 'test .parseComments() tags': function(done){
40
+ fixture('b.js', function(err, str){
41
+ var comments = dox.parseComments(str);
42
+
43
+ var version = comments.shift();
44
+ version.description.summary.should.equal('<p>Library version.</p>');
45
+ version.description.full.should.equal('<p>Library version.</p>');
46
+ version.tags.should.have.length(2);
47
+ version.tags[0].type.should.equal('type');
48
+ version.tags[0].types.should.eql(['String']);
49
+ version.tags[1].type.should.equal('api');
50
+ version.tags[1].visibility.should.equal('public');
51
+ version.ctx.type.should.equal('property');
52
+ version.ctx.receiver.should.equal('exports');
53
+ version.ctx.name.should.equal('version');
54
+ version.ctx.value.should.equal("'0.0.1'");
55
+
56
+ var parse = comments.shift();
57
+ parse.description.summary.should.equal('<p>Parse the given <code>str</code>.</p>');
58
+ parse.description.body.should.equal('<h2>Examples</h2>\n\n<pre><code>parse(str)\n// =&amp;gt; "wahoo"\n</code></pre>');
59
+ parse.description.full.should.equal('<p>Parse the given <code>str</code>.</p>\n\n<h2>Examples</h2>\n\n<pre><code>parse(str)\n// =&amp;gt; "wahoo"\n</code></pre>');
60
+ parse.tags[0].type.should.equal('param');
61
+ parse.tags[0].name.should.equal('str');
62
+ parse.tags[0].description.should.equal('to parse');
63
+ parse.tags[0].types.should.eql(['String', 'Buffer']);
64
+ parse.tags[1].type.should.equal('return');
65
+ parse.tags[1].types.should.eql(['String']);
66
+ parse.tags[2].visibility.should.equal('public');
67
+ done();
68
+ });
69
+ },
70
+
71
+ 'test .parseComments() complex': function(done){
72
+ fixture('c.js', function(err, str){
73
+ var comments = dox.parseComments(str);
74
+
75
+ var file = comments.shift();
76
+
77
+ file.tags.should.be.empty;
78
+ // the following doesn't work as gh-md now obfuscates emails different on every pass
79
+ //file.description.full.should.equal('<p>Dox<br />Copyright (c) 2010 TJ Holowaychuk <a href=\'mailto:tj@vision-media.ca\'>tj@vision-media.ca</a><br />MIT Licensed</p>');
80
+ file.description.full.should.be.a('string');
81
+ file.ignore.should.be.true;
82
+
83
+ var mods = comments.shift();
84
+ mods.tags.should.be.empty;
85
+ mods.description.full.should.equal('<p>Module dependencies.</p>');
86
+ mods.description.summary.should.equal('<p>Module dependencies.</p>');
87
+ mods.description.body.should.equal('');
88
+ mods.ignore.should.be.false;
89
+ mods.code.should.equal('var markdown = require(\'github-flavored-markdown\').parse;');
90
+ mods.ctx.type.should.equal('declaration');
91
+ mods.ctx.name.should.equal('markdown');
92
+ mods.ctx.value.should.equal('require(\'github-flavored-markdown\').parse');
93
+
94
+ var version = comments.shift();
95
+ version.tags.should.be.empty;
96
+ version.description.full.should.equal('<p>Library version.</p>');
97
+
98
+ var parseComments = comments.shift();
99
+ parseComments.tags.should.have.length(4);
100
+ parseComments.ctx.type.should.equal('method');
101
+ parseComments.ctx.receiver.should.equal('exports');
102
+ parseComments.ctx.name.should.equal('parseComments');
103
+ parseComments.description.full.should.equal('<p>Parse comments in the given string of <code>js</code>.</p>');
104
+ parseComments.description.summary.should.equal('<p>Parse comments in the given string of <code>js</code>.</p>');
105
+ parseComments.description.body.should.equal('');
106
+
107
+ var parseComment = comments.shift();
108
+ parseComment.tags.should.have.length(4);
109
+ parseComment.description.summary.should.equal('<p>Parse the given comment <code>str</code>.</p>');
110
+ parseComment.description.full.should.equal('<p>Parse the given comment <code>str</code>.</p>\n\n<h2>The comment object returned contains the following</h2>\n\n<ul>\n<li><code>tags</code> array of tag objects</li>\n<li><code>description</code> the first line of the comment</li>\n<li><code>body</code> lines following the description</li>\n<li><code>content</code> both the description and the body</li>\n<li><code>isPrivate</code> true when "@api private" is used</li>\n</ul>');
111
+ parseComment.description.body.should.equal('<h2>The comment object returned contains the following</h2>\n\n<ul>\n<li><code>tags</code> array of tag objects</li>\n<li><code>description</code> the first line of the comment</li>\n<li><code>body</code> lines following the description</li>\n<li><code>content</code> both the description and the body</li>\n<li><code>isPrivate</code> true when "@api private" is used</li>\n</ul>');
112
+
113
+ var escape = comments.pop();
114
+ escape.tags.should.have.length(3);
115
+ escape.description.full.should.equal('<p>Escape the given <code>html</code>.</p>');
116
+ escape.ctx.type.should.equal('function');
117
+ escape.ctx.name.should.equal('escape');
118
+ done();
119
+ });
120
+ },
121
+
122
+ 'test .parseComments() tags': function (done){
123
+ fixture('d.js', function(err, str){
124
+ var comments = dox.parseComments(str);
125
+ var first = comments.shift();
126
+ first.tags.should.have.length(4);
127
+ first.description.full.should.equal('<p>Parse tag type string "{Array|Object}" etc.</p>');
128
+ first.description.summary.should.equal('<p>Parse tag type string "{Array|Object}" etc.</p>');
129
+ first.description.body.should.equal('');
130
+ first.ctx.type.should.equal('method');
131
+ first.ctx.receiver.should.equal('exports');
132
+ first.ctx.name.should.equal('parseTagTypes');
133
+ first.code.should.equal('exports.parseTagTypes = function(str) {\n return str\n .replace(/[{}]/g, \'\')\n .split(/ *[|,\\/] */);\n};');
134
+ done();
135
+ });
136
+ },
137
+
138
+ 'test .parseComments() code': function(done){
139
+ fixture('b.js', function(err, str){
140
+ var comments = dox.parseComments(str)
141
+ , version = comments.shift()
142
+ , parse = comments.shift();
143
+
144
+ version.code.should.equal("exports.version = '0.0.1';");
145
+ parse.code.should.equal('exports.parse = function(str) {\n return "wahoo";\n}');
146
+ done();
147
+ });
148
+ },
149
+
150
+ 'test .parseComments() titles': function(done){
151
+ fixture('titles.js', function(err, str){
152
+ var comments = dox.parseComments(str);
153
+ comments[0].description.body.should.include('<h2>Some examples</h2>');
154
+ comments[0].description.body.should.not.include('<h2>for example</h2>');
155
+ comments[0].description.body.should.include('<p>Some longer thing<br />for example:</p>');
156
+ done();
157
+ });
158
+ },
159
+
160
+ 'test .parseCodeContext() function statement': function(){
161
+ var ctx = dox.parseCodeContext('function foo(){\n\n}');
162
+ ctx.type.should.equal('function');
163
+ ctx.name.should.equal('foo');
164
+ },
165
+
166
+ 'test .parseCodeContext() function expression': function(){
167
+ var ctx = dox.parseCodeContext('var foo = function(){\n\n}');
168
+ ctx.type.should.equal('function');
169
+ ctx.name.should.equal('foo');
170
+ },
171
+
172
+ 'test .parseCodeContext() prototype method': function(){
173
+ var ctx = dox.parseCodeContext('User.prototype.save = function(){}');
174
+ ctx.type.should.equal('method');
175
+ ctx.constructor.should.equal('User');
176
+ ctx.name.should.equal('save');
177
+ },
178
+
179
+ 'test .parseCodeContext() prototype property': function(){
180
+ var ctx = dox.parseCodeContext('Database.prototype.enabled = true;\nasdf');
181
+ ctx.type.should.equal('property');
182
+ ctx.constructor.should.equal('Database');
183
+ ctx.name.should.equal('enabled');
184
+ ctx.value.should.equal('true');
185
+ },
186
+
187
+ 'test .parseCodeContext() method': function(){
188
+ var ctx = dox.parseCodeContext('user.save = function(){}');
189
+ ctx.type.should.equal('method');
190
+ ctx.receiver.should.equal('user');
191
+ ctx.name.should.equal('save');
192
+ },
193
+
194
+ 'test .parseCodeContext() property': function(){
195
+ var ctx = dox.parseCodeContext('user.name = "tj";\nasdf');
196
+ ctx.type.should.equal('property');
197
+ ctx.receiver.should.equal('user');
198
+ ctx.name.should.equal('name');
199
+ ctx.value.should.equal('"tj"');
200
+ },
201
+
202
+ 'test .parseCodeContext() declaration': function(){
203
+ var ctx = dox.parseCodeContext('var name = "tj";\nasdf');
204
+ ctx.type.should.equal('declaration');
205
+ ctx.name.should.equal('name');
206
+ ctx.value.should.equal('"tj"');
207
+ },
208
+
209
+ 'test .parseTag() @constructor': function(){
210
+ var tag = dox.parseTag('@constructor');
211
+ tag.type.should.equal('constructor');
212
+ },
213
+
214
+ 'test .parseTag() @see': function(){
215
+ var tag = dox.parseTag('@see http://google.com');
216
+ tag.type.should.equal('see');
217
+ tag.title.should.equal('');
218
+ tag.url.should.equal('http://google.com');
219
+
220
+ var tag = dox.parseTag('@see Google http://google.com');
221
+ tag.type.should.equal('see');
222
+ tag.title.should.equal('Google');
223
+ tag.url.should.equal('http://google.com');
224
+
225
+ var tag = dox.parseTag('@see exports.parseComment');
226
+ tag.type.should.equal('see');
227
+ tag.local.should.equal('exports.parseComment');
228
+ },
229
+
230
+ 'test .parseTag() @api': function(){
231
+ var tag = dox.parseTag('@api private');
232
+ tag.type.should.equal('api');
233
+ tag.visibility.should.equal('private');
234
+ },
235
+
236
+ 'test .parseTag() @type': function(){
237
+ var tag = dox.parseTag('@type {String}');
238
+ tag.type.should.equal('type');
239
+ tag.types.should.eql(['String']);
240
+ },
241
+
242
+ 'test .parseTag() @param': function(){
243
+ var tag = dox.parseTag('@param {String|Buffer}');
244
+ tag.type.should.equal('param');
245
+ tag.types.should.eql(['String', 'Buffer']);
246
+ tag.name.should.equal('');
247
+ tag.description.should.equal('');
248
+ },
249
+
250
+ 'test .parseTag() @return': function(){
251
+ var tag = dox.parseTag('@return {String} a normal string');
252
+ tag.type.should.equal('return');
253
+ tag.types.should.eql(['String']);
254
+ tag.description.should.equal('a normal string');
255
+ },
256
+
257
+ 'test .parseTag() @augments': function(){
258
+ var tag = dox.parseTag('@augments otherClass');
259
+ tag.type.should.equal('augments');
260
+ tag.otherClass.should.equal('otherClass')
261
+ },
262
+
263
+ 'test .parseTag() @author': function(){
264
+ var tag = dox.parseTag('@author Bob Bobson');
265
+ tag.type.should.equal('author');
266
+ tag.string.should.equal('Bob Bobson');
267
+ },
268
+
269
+ 'test .parseTag() @borrows': function(){
270
+ var tag = dox.parseTag('@borrows foo as bar');
271
+ tag.type.should.equal('borrows');
272
+ tag.otherMemberName.should.equal('foo');
273
+ tag.thisMemberName.should.equal('bar');
274
+ },
275
+
276
+ 'test .parseTag() @memberOf': function(){
277
+ var tag = dox.parseTag('@memberOf Foo.bar')
278
+ tag.type.should.equal('memberOf')
279
+ tag.parent.should.equal('Foo.bar')
280
+ },
281
+
282
+ 'test .parseTag() default': function(){
283
+ var tag = dox.parseTag('@hello universe is better than world');
284
+ tag.type.should.equal('hello');
285
+ tag.string.should.equal('universe is better than world');
286
+ }
287
+ };
@@ -0,0 +1,12 @@
1
+
2
+ /*!
3
+ * A
4
+ * Copyright (c) 2010 Author Name <Author Email>
5
+ * MIT Licensed
6
+ */
7
+
8
+ /**
9
+ * Library version.
10
+ */
11
+
12
+ exports.version = '0.0.1';
@@ -0,0 +1,26 @@
1
+
2
+ /**
3
+ * Library version.
4
+ *
5
+ * @type {String}
6
+ * @api public
7
+ */
8
+
9
+ exports.version = '0.0.1';
10
+
11
+ /**
12
+ * Parse the given `str`.
13
+ *
14
+ * Examples:
15
+ *
16
+ * parse(str)
17
+ * // => "wahoo"
18
+ *
19
+ * @param {String|Buffer} str to parse
20
+ * @return {String}
21
+ * @api public
22
+ */
23
+
24
+ exports.parse = function(str) {
25
+ return "wahoo";
26
+ }