Update npm modules

This commit is contained in:
Timothy Warren 2014-10-22 10:11:40 -04:00
parent 6542c8f674
commit d75bb728c2
1150 changed files with 24459 additions and 55404 deletions

2
node_modules/.bin/jsdoc generated vendored
View File

@ -1 +1 @@
../jsdoc/nodejs/bin/jsdoc ../jsdoc/jsdoc.js

1
node_modules/getargs/.npmignore generated vendored Normal file
View File

@ -0,0 +1 @@
/node_modules

78
node_modules/getargs/README.md generated vendored Normal file
View File

@ -0,0 +1,78 @@
getargs
=======
Simple utility for parsing/processing variable length argument lists for Javascript functions; also verifies argument types and argument list length.
## Install
Bower - `bower install getargs`; NPM - `npm install getargs`.
## Usage
var getArgs = require('getargs')
function ajax(/* url:string|array, [options]:object, callback:function */){
var args = getArgs('url:string|array, [options]:object, callback:function', arguments)
console.log('url is', args.url)
console.log('options is optionally', args.options)
console.log('callback', args.callback)
}
## Argument Spec Syntax
The argument spec is a comma delimited string of individual specs, which look like
argname:type
You can specify multiple types by using `|`
argname:type|type|type
* `argname` is the name of the argument, and can be called anything
* `type` is an optional basic Javascript type. Currently these are supported
* `string`
* `boolean`
* `number`
* `object`
* `function`
* `array`
### Optional Arguments
To denote optional arguments, you'd surround `argname` with square brackets `[]`.
### Type Verification
getArgs will throw if the arguments have the wrong types
var args = getArgs('url:string', [1])
// Error: Expected url(pos 0) to be a string
### Argument List Length Verification
getArgs will throw if there are too many or too few arguments
> getArgs('a,b', [1])
Error: Not enough arguments, expected 2, got 1
> getArgs('a,b', [1,1])
{ a: 1, b: 1 }
> getArgs('a,b', [1,1,1])
Error: Too many arguments, expected 2, got 3
### Spread Operator
You can mimick ES6's spread operator
var args = getArgs('first,...rest', [1,2,3,4])
console.log(args.first) // 1
console.log(args.rest) // [2,3,4]
### Set properties on an object
If you pass an object as its third argument, it will set the arguments as properties on that object.
getArgs('a,b,c', arguments, this)
// Now you can access the arguments by
// this.a, this.b, and this.c

6
node_modules/getargs/TODO.md generated vendored Normal file
View File

@ -0,0 +1,6 @@
TODO
====
1. spread operator in the middle.
2. one-character type aliases.
3. wider browser support.

15
node_modules/getargs/example.js generated vendored Normal file
View File

@ -0,0 +1,15 @@
var getArgs = require('./index')
function ajax(url, opts, callback){
var args = getArgs(
'url:string|array,[opts]:object,[callback]:function',
arguments)
console.log(JSON.stringify(args, null, ' '))
}
ajax()
ajax('/submit')
ajax(['/submit'])
ajax('/submit', {method: 'POST', params: {foo: 'bar'}}, function(dat){
})

100
node_modules/getargs/index.js generated vendored Normal file
View File

@ -0,0 +1,100 @@
var is = {
'string': function(s){ return typeof s === 'string' },
'function': function(f){ return typeof f === 'function' },
'number': function(f){ return typeof f === 'number' },
'array': Array.isArray || function(a){ return a instanceof Array },
'object': function(o){ return typeof o === 'object' && o != null },
'boolean': function(b){ return typeof b === 'boolean' }
}
function ArgSpec(str){
var ret
str = str.trim()
var parts = str.split(':')
if (parts.length > 1){
ret = {
name: parts[0],
type: parts[1].split('|')
}
}else if (parts.length === 1){
ret = {
name: str
}
}else{
throw new Error('Expected arg spec to be format name or name:type but was ' + str)
}
var m
if (m = ret.name.match(/^\[(.+)\]$/)){
ret.name = m[1]
ret.optional = true
}
if (m = ret.name.match(/^\.\.\.(.+)$/)){
ret.name = m[1]
ret.spread = true
}
return ret
}
function typeMatches(spec, arg) {
if (!spec.type) return true
var match = false;
var type = null;
for (var i = 0; i<spec.type.length; i++ ) {
type = spec.type[i];
var fun = is[type.toLowerCase()]
if (!fun) {
throw new Error('Unknown type: ' + spec.type)
}
match = fun(arg);
if (match) break;
}
return match;
}
module.exports = function getArgs(spec, args, target){
var ret = target || {}
spec = spec.split(',').map(function(s){
s = ArgSpec(s);
return s;
})
var minExpected = spec.filter(function(s){
return !s.optional
}).length
var maxExpected = spec.length
var argIdxOffset = 0
var length = Math.max(spec.length, args.length)
for (var i = 0; i < length; i++){
var sp = spec[i]
var argIdx = i + argIdxOffset
if (argIdx >= args.length){
if (argIdx < minExpected){
throw new Error(
'Not enough arguments, expected ' +
minExpected + ', got ' + argIdx)
}
break
}
if (argIdx >= maxExpected){
throw new Error('Too many arguments, expected ' +
maxExpected + ', got ' + (argIdx + 1))
}
var arg = args[argIdx]
if (typeMatches(sp, arg)){
if (sp.spread){
ret[sp.name] = Array.prototype.slice.call(args, argIdx)
break
}else{
ret[sp.name] = arg
}
}else if (sp.optional){
argIdxOffset--
}else{
throw new Error('Expected ' + sp.name +
'(pos ' + i + ') to be a ' + sp.type.join(' or '))
}
}
return ret
}

50
node_modules/getargs/package.json generated vendored Normal file
View File

@ -0,0 +1,50 @@
{
"name": "getargs",
"version": "0.0.8",
"description": "Utility to handle optional arguments and argument type checking.",
"main": "index.js",
"dependencies": {},
"devDependencies": {
"chai": "~1.7.2"
},
"scripts": {
"test": "mocha tests.js -u tdd"
},
"keywords": [
"arguments",
"utility"
],
"repository": {
"type": "git",
"url": "git@github.com:airportyh/getargs.git"
},
"author": {
"name": "Toby Ho"
},
"license": "MIT",
"readme": "getargs\n=======\n\nSimple utility for parsing/processing variable length argument lists for Javascript functions; also verifies argument types and argument list length.\n\n## Install\n\nBower - `bower install getargs`; NPM - `npm install getargs`.\n\n## Usage\n\n var getArgs = require('getargs')\n\n function ajax(/* url:string|array, [options]:object, callback:function */){\n var args = getArgs('url:string|array, [options]:object, callback:function', arguments)\n\n console.log('url is', args.url)\n console.log('options is optionally', args.options)\n console.log('callback', args.callback)\n }\n\n## Argument Spec Syntax\n\nThe argument spec is a comma delimited string of individual specs, which look like\n\n argname:type\n\nYou can specify multiple types by using `|`\n\n argname:type|type|type\n\n* `argname` is the name of the argument, and can be called anything\n* `type` is an optional basic Javascript type. Currently these are supported\n * `string`\n * `boolean`\n * `number`\n * `object`\n * `function`\n * `array`\n\n### Optional Arguments\n\nTo denote optional arguments, you'd surround `argname` with square brackets `[]`.\n\n### Type Verification\n\ngetArgs will throw if the arguments have the wrong types\n\n var args = getArgs('url:string', [1])\n // Error: Expected url(pos 0) to be a string\n\n### Argument List Length Verification\n\ngetArgs will throw if there are too many or too few arguments\n\n > getArgs('a,b', [1])\n Error: Not enough arguments, expected 2, got 1\n > getArgs('a,b', [1,1])\n { a: 1, b: 1 }\n > getArgs('a,b', [1,1,1])\n Error: Too many arguments, expected 2, got 3\n\n### Spread Operator\n\nYou can mimick ES6's spread operator\n\n var args = getArgs('first,...rest', [1,2,3,4])\n console.log(args.first) // 1\n console.log(args.rest) // [2,3,4]\n\n### Set properties on an object\n\nIf you pass an object as its third argument, it will set the arguments as properties on that object.\n\n getArgs('a,b,c', arguments, this)\n // Now you can access the arguments by\n // this.a, this.b, and this.c\n \n",
"readmeFilename": "README.md",
"bugs": {
"url": "https://github.com/airportyh/getargs/issues"
},
"_id": "getargs@0.0.8",
"dist": {
"shasum": "8516605872c980178e01ca70106fccd22521fc7c",
"tarball": "http://registry.npmjs.org/getargs/-/getargs-0.0.8.tgz"
},
"_from": "getargs@*",
"_npmVersion": "1.3.8",
"_npmUser": {
"name": "airportyh",
"email": "airportyh@gmail.com"
},
"maintainers": [
{
"name": "airportyh",
"email": "airportyh@gmail.com"
}
],
"directories": {},
"_shasum": "8516605872c980178e01ca70106fccd22521fc7c",
"_resolved": "https://registry.npmjs.org/getargs/-/getargs-0.0.8.tgz"
}

96
node_modules/getargs/tests.js generated vendored Normal file
View File

@ -0,0 +1,96 @@
var getArgs = require('./index')
var assert = require('chai').assert
test('basic', function(){
var args = [1, 2]
var result = getArgs('a,b', args)
assert(result.a === 1)
assert(result.b === 2)
assert.deepEqual(result, getArgs('a, b', args))
})
test('not enough throws', function(){
getArgs('a', [1])
assert.throws(function(){
getArgs('a', [])
}, 'Not enough arguments, expected 1, got 0')
})
test('too many throws', function(){
var args = [1, 2]
assert.throws(function(){
getArgs('a', args)
}, 'Too many arguments, expected 1, got 2')
assert.throws(function(){
getArgs('a,b', [1,2,3])
}, 'Too many arguments, expected 2, got 3')
})
test('checks type', function(){
var result = getArgs('a:string', ['abc'])
assert(result.a === 'abc')
assert.throws(function(){
getArgs('a:string', [1])
}, 'Expected a(pos 0) to be a string')
getArgs('a:Array', [[]])
assert.throws(function(){
getArgs('a:array', [1])
}, 'Expected a(pos 0) to be a array')
getArgs('a:number', [3])
assert.throws(function(){
getArgs('a:number', ['a'])
}, 'Expected a(pos 0) to be a number')
assert.throws(function(){
getArgs('a:boolean', ['a'])
}, 'Expected a(pos 0) to be a boolean')
})
test('supports multiple types', function(){
var result = getArgs('a:string|array', ['abc'])
assert.equal(result.a, 'abc')
var result = getArgs('a:string|array', [['abc']])
assert.deepEqual(result.a, ['abc'])
})
test('unknown type', function(){
assert.throws(function(){
getArgs('a:blarg', ['abc'])
}, 'Unknown type: blarg')
})
test('optional by type', function(){
var result = getArgs(
'[user]:object,callback:function',
[{name: 'bobby'}, function(){}])
assert(result.user.name === 'bobby')
result = getArgs(
'[user]:object,callback:function',
[function(){}])
assert(result.user === undefined)
assert(result.callback instanceof Function)
})
test('optional + spread', function(){
var result = getArgs('[user]:object,...rest',[1,2,3])
assert.deepEqual(result.rest, [1,2,3])
})
test('optional last', function(){
var result = getArgs('a,[b]', [1])
assert(result.a === 1)
assert(result.b === undefined)
})
test('spread operator', function(){
var result = getArgs('a,...b', [1, 2, 3, 4])
assert(result.b == '2,3,4')
})
test('sets properties on target if passed in', function(){
var target = {}
var args = [1, 2]
getArgs('a,b', args, target)
assert(target.a == 1)
assert(target.b == 2)
})

View File

@ -1 +0,0 @@
../jsdoc/jsdoc.js

View File

@ -114,5 +114,6 @@
], ],
"directories": {}, "directories": {},
"_shasum": "0765b72b841dd213fa91914c0f6765122719f061", "_shasum": "0765b72b841dd213fa91914c0f6765122719f061",
"_resolved": "https://registry.npmjs.org/moment/-/moment-2.6.0.tgz" "_resolved": "https://registry.npmjs.org/moment/-/moment-2.6.0.tgz",
"readme": "ERROR: No README data found!"
} }

View File

@ -58,5 +58,7 @@
], ],
"directories": {}, "directories": {},
"_shasum": "d41e376919debdad061288c11c8022b785e8acdf", "_shasum": "d41e376919debdad061288c11c8022b785e8acdf",
"_resolved": "https://registry.npmjs.org/ink-docstrap/-/ink-docstrap-0.4.12.tgz" "_resolved": "https://registry.npmjs.org/ink-docstrap/-/ink-docstrap-0.4.12.tgz",
"readme": "ERROR: No README data found!",
"homepage": "https://github.com/terryweiss/docstrap"
} }

View File

@ -1,14 +0,0 @@
# development-related files
.eslintignore
.eslintrc
.gitignore
.travis.yml
gulpfile.js
# scripts for launching JSDoc with Mozilla Rhino
/jsdoc*
!/jsdoc.js
# Rhino and test directories
rhino/
test/

View File

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,69 +0,0 @@
Pull Requests
-------------
If you're thinking about making some changes, maybe fixing a bug, or adding a
snazzy new feature, first, thank you. Contributions are very welcome. Things
need to be manageable for the maintainers, however. So below you'll find **The
fastest way to get your pull request merged in.** Some things, particularly how
you set up your branches and work with git, are just suggestions, but pretty good
ones.
1. **Create a remote to track the base jsdoc3/jsdoc repository**
This is just a convenience to make it easier to update your ```<tracking branch>```
(more on that shortly). You would execute something like:
git remote add base git://github.com/jsdoc3/jsdoc.git
Here 'base' is the name of the remote. Feel free to use whatever you want.
2. **Set up a tracking branch for the base repository**
We're gonna call this your ```<tracking branch>```. You will only ever update
this branch by pulling from the 'base' remote. (as opposed to 'origin')
git branch --track pullpost base/master
git checkout pullpost
Here 'pullpost' is the name of the branch. Fell free to use whatever you want.
3. **Create your change branch**
Once you are in ```<tracking branch>```, make sure it's up to date, then create
a branch for your changes off of that one.
git branch fix-for-issue-395
git checkout fix-for-issue-395
Here 'fix-for-issue-395' is the name of the branch. Feel free to use whatever
you want. We'll call this the ```<change branch>```. This is the branch that
you will eventually issue your pull request from.
The purpose of these first three steps is to make sure that your merge request
has a nice clean diff that only involves the changes related to your fix/feature.
4. **Make your changes**
On your ```<change branch>``` make any changes relevant to your fix/feature. Don't
group fixes for multiple unrelated issues or multiple unrelated features together.
Create a separate branch for each unrelated changeset. For instance, if you're
fixing a bug in the parser and adding some new UI to the default template, those
should be separate branches and merge requests.
5. **Add tests**
Add tests for your change. If you are submitting a bugfix, include a test that
verifies the existence of the bug along with your fix. If you are submitting
a new feature, include tests that verify proper feature function, if applicable.
See the readme in the 'test' directory for more information
6. **Commit and publish**
Commit your changes and publish your branch (or push it if it's already published)
7. **Issue your pull request**
On github.com, switch to your ```<change branch>``` and click the 'Pull Request'
button. Enter some meaningful information about the pull request. If it's a bugfix,
that doesn't already have an issue associated with it, provide some info on what
situations that bug occurs in and a sense of it's severity. If it does already have
an issue, make sure the include the hash and issue number (e.g. '#100') so github
links it.
If it's a feature, provide some context about the motivations behind the feature,
why it's important/useful/cool/necessary and what it does/how it works. Don't
worry about being too verbose. Folks will be much more amenable to reading through
your code if they know what its supposed to be about.

View File

@ -1,391 +0,0 @@
# License #
JSDoc 3 is free software, licensed under the Apache License, Version 2.0 (the
"License"). Commercial and non-commercial use are permitted in compliance with
the License.
Copyright (c) 2011-2014 Michael Mathews <micmath@gmail.com> and the
[contributors to JSDoc](https://github.com/jsdoc3/jsdoc/graphs/contributors).
All rights reserved.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
In addition, a copy of the License is included with this distribution.
As stated in Section 7, "Disclaimer of Warranty," of the License:
> Licensor provides the Work (and each Contributor provides its Contributions)
> on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
> express or implied, including, without limitation, any warranties or
> conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
> PARTICULAR PURPOSE. You are solely responsible for determining the
> appropriateness of using or redistributing the Work and assume any risks
> associated with Your exercise of permissions under this License.
The source code for JSDoc 3 is available at:
https://github.com/jsdoc3/jsdoc
# Third-Party Software #
JSDoc 3 includes or depends upon the following third-party software, either in
whole or in part. Each third-party software package is provided under its own
license.
## MIT License ##
Several of the following software packages are distributed under the MIT
license, which is reproduced below:
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
## Acorn ##
Portions of the Acorn source code are incorporated into the following files:
- `lib/jsdoc/src/walker.js`
Acorn is distributed under the MIT license, which is reproduced above.
Copyright (C) 2012 Marijn Haverbeke <marijnh@gmail.com>.
The source code for Acorn is available at:
https://github.com/marijnh/acorn
## Async.js ##
Async.js is distributed under the MIT license, which is reproduced above.
Copyright (c) 2010 Caolan McMahon.
The source code for Async.js is available at:
https://github.com/caolan/async
## Catharsis ##
Catharsis is distributed under the MIT license, which is reproduced above.
Copyright (c) 2012-2013 Jeff Williams.
The source code for Catharsis is available at:
https://github.com/hegemonic/catharsis
## crypto-browserify ##
crypto-browserify is distributed under the MIT license, which is reproduced
above.
Copyright (c) 2013 Dominic Tarr.
The source code for crypto-browserify is available at:
https://github.com/dominictarr/crypto-browserify
## Esprima ##
Esprima is distributed under the BSD 2-clause license:
> 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.
>
> 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 <COPYRIGHT HOLDER> 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.
Copyright (c) 2011-2013 Ariya Hidayat and other Esprima contributors.
The source code for Esprima is available at:
https://github.com/ariya/esprima
## github-flavored-markdown ##
github-flavored-markdown is distributed under the BSD 3-clause license:
> Copyright (c) 2007, John Fraser <http://www.attacklab.net/> All rights
> reserved.
>
> Original Markdown copyright (c) 2004, John Gruber <http://daringfireball.net/>
> 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 "Markdown" 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 the copyright owner or contributors 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.
The source code for github-flavored-markdown is available at:
https://github.com/hegemonic/github-flavored-markdown
## Google Code Prettify ##
Google Code Prettify is distributed under the Apache License 2.0, which is
included with this package.
Copyright (c) 2006 Google Inc.
The source code for Google Code Prettify is available at:
https://code.google.com/p/google-code-prettify/
## Jasmine ##
Jasmine is distributed under the MIT license, which is reproduced above.
Copyright (c) 2008-2011 Pivotal Labs.
The source code for Jasmine is available at:
https://github.com/pivotal/jasmine
## jasmine-node ##
jasmine-node is distributed under the MIT license, which is reproduced above.
Copyright (c) 2010 Adam Abrons and Misko Hevery (http://getangular.com).
The source code for jasmine-node is available at:
https://github.com/mhevery/jasmine-node
## js2xmlparser ##
js2xmlparser is distributed under the MIT license, which is reproduced above.
Copyright (c) 2012 Michael Kourlas.
The source code for js2xmlparser is available at:
https://github.com/michaelkourlas/node-js2xmlparser
## JSHint ##
JSHint is distributed under the MIT license, which is reproduced above.
Portions of JSHint are derived from JSLint, which is distributed under a
modified MIT license:
> Copyright (c) 2002 Douglas Crockford (www.JSLint.com)
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> The Software shall be used for Good, not Evil.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
The source code for JSHint is available at:
https://github.com/jshint/jshint
## Node.js ##
Portions of the Node.js source code are incorporated into the following files:
- `rhino/fs.js`
- `rhino/path.js`
- `rhino/querystring.js`
- `rhino/util.js`
Node.js is distributed under the MIT license, which is reproduced above.
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
The source code for Node.js is available at:
https://github.com/joyent/node
## node-browser-builtins ##
Portions of the node-browser-builtins source code are incorporated into the
following files:
- `rhino/assert.js`
- `rhino/rhino-shim.js`
node-browser-builtins is distributed under the MIT license, which is reproduced
above.
The source code for node-browser-builtins is available at:
https://github.com/alexgorbatchev/node-browser-builtins
## node-browserify ##
Portions of the node-browserify source code are incorporated into the following
files:
- `rhino/events.js`
node-browserify is distributed under the MIT license, which is reproduced above.
The source code for node-browserify is available at:
https://github.com/substack/node-browserify
## Requizzle ##
Requizzle is distributed under the MIT license, which is reproduced above.
Copyright (c) 2014 Google Inc. All rights reserved.
Copyright (c) 2012-2013 Johannes Ewald.
The source code for Requizzle is available at:
https://github.com/hegemonic/requizzle
## Rhino ##
Rhino is distributed under the following licenses:
### MPL 2.0 License ###
The majority of the source code for Rhino is available under the Mozilla Public
License (MPL) 2.0, which is included in this distribution.
### License for portions of the Rhino debugger ###
Additionally, some files are available under the BSD 3-clause license:
> Copyright 1997, 1998 Sun Microsystems, 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 Sun Microsystems 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.
### Source Code ###
The source code for Rhino is available at:
https://github.com/jsdoc3/rhino
## TaffyDB ##
TaffyDB is distributed under a modified BSD license:
> All rights reserved.
>
> Redistribution and use of this software in source and binary forms, with or
> without modification, are permitted provided that the following condition is
> met:
>
> Redistributions of source code must retain the above copyright notice, this
> list of conditions and the following disclaimer.
>
> 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.
The source code for TaffyDB is available at:
https://github.com/hegemonic/taffydb
## Tomorrow Theme for Google Code Prettify ##
License information for the Tomorrow Theme for Google Code Prettify is not
available. It is assumed that the package is distributed under an open source
license that is compatible with the Apache License 2.0.
Copyright (c) Yoshihide Jimbo.
The source code for the Tomorrow Theme is available at:
https://github.com/jmblog/color-themes-for-google-code-prettify
## tv4 ##
tv4 is in the public domain. It is also distributed under the MIT license, which
is reproduced above.
The source code for tv4 is available at:
https://github.com/geraintluff/tv4
## Underscore.js ##
Underscore.js is distributed under the MIT license, which is reproduced above.
Copyright (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative
Reporters & Editors.
The source code for Underscore.js is available at:
https://github.com/jashkenas/underscore
## wrench-js ##
wrench-js is distributed under the MIT license, which is reproduced above.
Copyright (c) 2010 Ryan McGrath.
The source code for wrench-js is available at:
https://github.com/ryanmcgrath/wrench-js

View File

@ -1,132 +0,0 @@
JSDoc 3
=======
[![Build Status](https://img.shields.io/travis/jsdoc3/jsdoc.svg)](http://travis-ci.org/jsdoc3/jsdoc)
An API documentation generator for JavaScript.
Want to contribute to JSDoc? Please read `CONTRIBUTING.md`.
Installation and Usage
----------------------
You can run JSDoc on either Node.js or Mozilla Rhino.
### Node.js
Native support for Node.js is available in JSDoc 3.3.0 and later. JSDoc
supports Node.js 0.10 and later.
#### Installing JSDoc for Node.js
You can install JSDoc in your project's `node_modules` folder, or you can
install it globally.
To install the latest alpha version:
npm install jsdoc@"<=3.3.0"
To install the latest development version:
npm install git+https://github.com/jsdoc3/jsdoc.git
**Note**: If you install JSDoc globally, do not use `sudo npm install`. This may
prevent you from running JSDoc as a normal user. If you cannot install global
packages without `sudo`, please
[fix your npm directory permissions](http://howtonode.org/introduction-to-npm).
#### Running JSDoc with Node.js
If you installed JSDoc locally, the JSDoc command-line tool is available in
`./node_modules/.bin`. To generate documentation for the file
`yourJavaScriptFile.js`:
./node_modules/.bin/jsdoc yourJavaScriptFile.js
Or if you installed JSDoc globally, simply run the `jsdoc` command:
jsdoc yourJavaScriptFile.js
By default, the generated documentation is saved in a directory named `out`. You
can use the `--destination` (`-d`) option to specify another directory.
Run `jsdoc --help` for a complete list of command-line options.
### Mozilla Rhino
All versions of JSDoc 3 run on a customized version of Mozilla Rhino, which
requires Java. You can run JSDoc 3 on Java 1.6 and later.
#### Installing JSDoc for Mozilla Rhino
To install JSDoc, download a .zip file for the
[latest development version](https://github.com/jsdoc3/jsdoc/archive/master.zip)
or a [previous release](https://github.com/jsdoc3/jsdoc/tags).
You can also use git to clone the
[JSDoc repository](https://github.com/jsdoc3/jsdoc):
git clone git+https://github.com/jsdoc3/jsdoc.git
The JSDoc repository includes a
[customized version of Mozilla Rhino](https://github.com/jsdoc3/rhino). Make
sure your Java classpath does not include any other versions of Rhino. (On OS X,
you may need to remove the file `~/Library/Java/Extensions/js.jar`.)
**Note**: In JSDoc 3.3.0 and later, if you need to run JSDoc on Mozilla Rhino,
do not install JSDoc with npm. Use one of the methods described above.
#### Running JSDoc with Mozilla Rhino
On OS X, Linux, and other POSIX systems, to generate documentation for the file
`yourJavaScriptFile.js`:
./jsdoc yourJavaScriptFile.js
Or on Windows:
jsdoc yourJavaScriptFile.js
By default, the generated documentation is saved in a directory named `out`. You
can use the `--destination` (`-d`) option to specify another directory.
Run `jsdoc --help` for a complete list of command-line options.
Templates and Build Tools
-------------------------
The JSDoc community has created numerous templates and other tools to help you
generate and customize your documentation. Here are just a few:
### Templates
+ [jaguarjs-jsdoc](https://github.com/davidshimjs/jaguarjs-jsdoc)
([example](http://davidshimjs.github.io/jaguarjs/doc))
+ [DocStrap](https://github.com/terryweiss/docstrap)
+ [jsdoc3Template](https://github.com/DBCDK/jsdoc3Template)
([example](https://github.com/danyg/jsdoc3Template/wiki#wiki-screenshots))
### Build Tools
+ [JSDoc Grunt plugin](https://github.com/krampstudio/grunt-jsdoc)
+ [JSDoc ant task](https://github.com/jannon/jsdoc3-ant-task)
For More Information
--------------------
+ Documentation is available at [Use JSDoc](http://usejsdoc.org).
+ Contribute to the docs at [jsdoc3/jsdoc3.github.com](https://github.com/jsdoc3/jsdoc3.github.com).
+ ~~Post questions to the [JSDoc Users mailing list](http://groups.google.com/group/jsdoc-users).~~
(temporarily unavailable)
+ Post questions tagged `jsdoc` to [Stack
Overflow](http://stackoverflow.com/questions/tagged/jsdoc).
License
-------
JSDoc 3 is copyright (c) 2011-2014 Michael Mathews <micmath@gmail.com> and the
[contributors to JSDoc](https://github.com/jsdoc3/jsdoc/graphs/contributors).
JSDoc 3 is free software, licensed under the Apache License, Version 2.0. See
the file `LICENSE.md` in this distribution for more details.

View File

@ -1,240 +0,0 @@
# JSDoc 3 change history
This file describes notable changes in each version of JSDoc 3. To download a specific version of JSDoc 3, see [GitHub's tags page](https://github.com/jsdoc3/jsdoc/tags).
## 3.2.2 (November 2013)
### Bug fixes
+ Addressed a regression in JSDoc 3.2.1 that could prevent a function declaration from shadowing a declaration with the same name in an outer scope. (#513)
+ If a child class overrides a method in a parent class without documenting the overridden method, the method's documentation is now copied from the parent class. (#503)
+ You can now use inline HTML tags in Markdown-formatted text. In addition, JSDoc now uses only the [marked Markdown parser](https://github.com/chjj/marked); the markdown-js parser has been removed. (#510)
+ Type expressions can now include a much broader range of repeatable types. In addition, you can now use Closure Compiler's nullable and non-nullable modifiers with repeatable types. For example, the type expression `...!string` (a repeatable, non-nullable string) is now parsed correctly. (#502)
+ If a function accepts a parameter named `prototype`, the parameter is no longer renamed during parsing. (#505)
+ If the list of input files includes relative paths, the paths are now resolved relative to the user's working directory. (a3d33842)
## 3.2.1 (October 2013)
### Enhancements
+ JSDoc's parser now fires a `processingComplete` event after JSDoc has completed all post-processing of the parse results. This event has a `doclets` property containing an array of doclets. (#421)
+ When JSDoc's parser fires a `parseComplete` event, the event now includes a `doclets` property containing an array of doclets. (#431)
+ You can now use relative paths in the JSDoc configuration file's `source.exclude` option. Relative paths will be resolved relative to the current working directory. (#405)
+ If a symbol uses the `@default` tag, and its default value is an object literal, this value is now stored as a string, and the doclet will have a `defaultvaluetype` property containing the string `object`. This change enables templates to show the default value with appropriate syntax highlighting. (#419)
+ Inline `{@link}` tags can now contain newlines. (#441)
### Bug fixes
+ Inherited symbols now indicate that they were inherited from the ancestor that defined the symbol, rather than the direct parent. (#422)
+ If the first line of a JavaScript file contains a hashbang (for example, `#!/usr/bin/env node`), the hashbang is now ignored when the file is parsed. (#499)
+ Resolved a crash when a JavaScript file contains a [JavaScript 1.8](https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/1.8) keyword, such as `let`. (#477)
+ The type expression `function[]` is now parsed correctly. (#493)
+ If a module is tagged incorrectly, the module's output file now has a valid filename. (#440, #458)
+ For tags that accept names, such as `@module` and `@param`, if a hyphen is used to separate the name and description, the hyphen must appear on the same line as the name. This change prevents a Markdown bullet on the followng line from being interpreted as a separator. (#459)
+ When lenient mode is enabled, a `@param` tag with an invalid type expression no longer causes a crash. (#448)
+ The `@requires` tag can now contain an inline tag in its tag text. (#486)
+ The `@returns` tag can now contain inline tags even if a type is not specified. (#444)
+ When lenient mode is enabled, a `@returns` tag with no value no longer causes a crash. (#451)
+ The `@type` tag now works correctly with type expressions that span multiple lines. (#427)
+ If a string contains inline `{@link}` tags preceded by bracketed link text (for example, `[test]{@link Test#test}`), HTML links are now generated correctly even if the string contains other bracketed text. (#470)
+ On POSIX systems, if you run JSDoc using a symlink to the startup script, JSDoc now works correctly. (#492)
### Default template
+ Pretty-printed source files are now generated by default. To disable this feature, add the property `templates.default.outputSourceFiles: false` to your `conf.json` file. (#454)
+ Links to a specific line in a source file now work correctly. (#475)
+ Pretty-printed source files are now generated using the encoding specified in the `-e/--encoding` option. (#496)
+ If a `@default` tag is added to a symbol whose default value is an object, the value is now displayed in the output file. (#419)
+ Output files now identify symbols as "abstract" rather than "virtual." (#432)
## 3.2.0 (May 2013)
### Major changes
+ JSDoc can now parse any valid [Google Closure Compiler type expression](https://developers.google.com/closure/compiler/docs/js-for-compiler#types). **Note**: As a result of this change, JSDoc quits if a file contains an invalid type expression. To prevent JSDoc from quitting, run JSDoc with the `--lenient` (`-l`) command-line option. (Multiple issues)
+ You can now use the new `@listens` tag to indicate that a symbol listens for an event. (#273)
### Enhancements
+ The parser now fires a `parseBegin` event before it starts parsing files, as well as a `parseComplete` event after all files have been parsed. Plugins can define event handlers for these events, and `parseBegin` handlers can modify the list of files to parse. (#299)
+ Event handlers for `jsdocCommentFound` events can now modify the JSDoc comment. (#228)
+ You can now exclude tags from Markdown processing using the new option `markdown.excludeTags` in the configuration file. (#337)
+ You can now use the [marked](https://github.com/chjj/marked) Markdown parser by setting the configuration property `markdown.parser` to `marked`. In addition, if `markdown.parser` is set to `gfm`, JSDoc will now use the "marked" parser instead. (#385)
+ The `@typedef` tag no longer requires a name when used with a Closure Compiler-style type definition. For example, the following type definition will automatically get the name `Foo.Bar`:
```javascript
/** @typedef {string} */
Foo.Bar;
```
(#391)
+ You can now use an inline `{@type}` tag in a parameter's description. If this tag is present, JSDoc will assume that the parameter uses the type specified in the inline `{@type}` tag. For example, the following `@param` tag would cause `myParam`'s type to be documented as `Foo`:
```
@param {(boolean|string)} myParam - My special parameter. {@type Foo}
```
(#152)
+ The `console.log` function now behaves the same way as on Node.js. In addition, the functions `console.info`, `console.error`, `console.warn`, and `console.trace` have been implemented. (#298)
+ You can now use npm to install JSDoc globally by running `npm install -g`. **Note**: JSDoc will still run under Mozilla Rhino, not Node.js. (#374)
+ The `jsVersion` configuration property has been removed. (#390)
### Bug fixes
+ JSDoc now quits if the configuration file cannot be loaded. (#407)
+ JSDoc's `--explain` (`-X`) option now runs much more quickly, and it outputs valid JSON to the console. (#298)
+ JSDoc's `--lenient` (`-l`) option now prints warnings on STDERR rather than STDOUT. (#298)
+ The parser now assigns the correct scope to object properties whose names include single quotes. (#386)
+ The parser now recognizes CommonJS modules that export a single function rather than an object. (#384)
+ The inline `{@link}` tag now works correctly when `@link` is followed by a tab. (#359)
+ On POSIX systems, quoted command-line arguments are no longer split on spaces. (#397)
### Plugins
+ The new `overloadHelper` plugin makes it easier to link to overloaded methods. (#179)
+ The `markdown` plugin now converts Markdown links in the `@see` tag. (#297)
### Default template enhancements
+ You can now use the configuration property `templates.default.staticFiles` to copy additional static files to the output directory. (#393)
+ All output files now use human-readable filenames. (#339)
+ The documentation for events now lists the symbols that listen to that event. (#273)
+ Links to source files now allow you to jump to the line where a symbol is defined. (#316)
+ The output files now link to individual types within a Closure Compiler type expression. (Multiple issues)
+ CommonJS modules that export a single function, rather than an object, are now documented more clearly. (#384)
+ Functions that can throw multiple types of errors are now documented more clearly. (#389)
+ If a `@property` tag does not identify the property's name, the template no longer throws an error. (#373)
+ The type of each `@typedef` is now displayed. (#391)
+ If a `@see` tag contains a URL (for example, `@see http://example.com` or `@see <http://example.com>`), the tag text is now converted to a link. (#371)
+ Repeatable parameters are now identified. (#381)
+ The "Classes" header is no longer repeated in the navigation bar. (#361)
+ When the only documented symbols in global scope are type definitions, you can now click the "Global" header to view their documentation. (#261)
## 3.1.1 (February 2013)
+ Resolved a crash when no input files contain JSDoc comments. (#329)
+ Resolved a crash when JSDoc cannot identify the common prefix of several paths. (#330)
+ Resolved a crash when the full path to JSDoc contained at least one space. (#347)
+ Files named `README.md` or `package.json` will now be processed when they are specified on the command line. (#350)
+ You can now use `@emits` as a synonym for `@fires`. (#324)
+ The module `jsdoc/util/templateHelper` now allows you to specify the CSS class for links that are generated by the following methods: (#331)
+ `getAncestorLinks`
+ `getSignatureReturns`
+ `getSignatureTypes`
+ `linkto`
## 3.1.0 (January 2013)
### Major changes
+ You can now use the new `@callback` tag to provide information about a callback function's signature. To document a callback function, create a standalone JSDoc comment, as shown in the following example:
```javascript
/**
* @class
*/
function MyClass() {}
/**
* Send a request.
*
* @param {MyClass~responseCb} cb - Called after a response is received.
*/
MyClass.prototype.sendRequest = function(cb) {
// code
};
/**
* Callback for sending a request.
*
* @callback MyClass~responseCb
* @param {?string} error - Information about the error.
* @param {?string} response - Body of the response.
*/
```
+ The inline link tag, `{@link}`, has been improved:
+ You can now use a space as the delimiter between the link target and link text.
+ In your `conf.json` file, you can now enable the option `templates.cleverLinks` to display code links in a monospace font and URL links in plain text. You can also enable the option `templates.monospaceLinks` to display all links in a monospace font. **Note**: JSDoc templates must be updated to respect these options.
+ You can now use the new inline tags `{@linkplain}`, which forces a plain-text link, and `{@linkcode}`, which forces a monospace link. These tags always override the settings in your `conf.json` file. (#250)
+ JSDoc now provides a `-l/--lenient` option that tells JSDoc to continue running if it encounters a non-fatal error. (Multiple issues)
+ A template's `publish.js` file should now assign its `publish` function to `exports.publish`, rather than defining a global `publish` function. The global `publish` function is deprecated and may not be supported in future versions. JSDoc's built-in templates reflect this change. (#166)
+ The template helper (`templateHelper.js`) exports a variety of new functions for finding information within a parse tree. These functions were previously contained within the default template. (#186)
+ Updated the `fs` and `path` modules to make their behavior more consistent with Node.js. In addition, created extended versions of these modules with additional functionality. (Multiple commits)
+ Updated or replaced numerous third-party modules. (Multiple commits)
+ Reorganized the JSDoc codebase in preparation for future enhancements. (Multiple commits)
+ JSDoc now embeds a version of Mozilla Rhino that recognizes Node.js packages, including `package.json` files. (Multiple commits)
+ Node.js' `npm` utility can now install JSDoc from its GitHub repository. **Note**: JSDoc is not currently compatible with Node.js. However, this change allows JSDoc to be installed as a dependency of a Node.js project. In this version, global installation with `npm` is not supported. (Multiple commits)
### Enhancements
+ If a `README.md` file is passed to JSDoc, its contents will be included on the `index.html` page of the generated documentation. (#128)
+ The `@augments` tag can now refer to an undocumented member, such as `window.XMLHTTPRequest`. (#160)
+ The `@extends` tag can now refer to an undocumented member, such as `window.XMLHttpRequest`. In addition, you can now use `@host` as a synonym for `@extends`. (#145)
+ The `@lends` tag is now supported in multiline JSDoc comments. (#163)
+ On Windows, `jsdoc.cmd` now provides the same options as the `jsdoc` shell script. (#127)
+ JSDoc now provides `setTimeout()`, `clearTimeout()`, `setInterval()`, and `clearInterval()` functions. (Multiple commits)
+ JSDoc no longer provides a global `exit()` function. Use `process.exit()` instead. (1228a8f7)
+ JSDoc now includes additional shims for Node.js' built-in modules. **Note**: Many of these shims implement only the functions that JSDoc uses, and they may not be consistent with Node.js' behavior in edge cases. (Multiple commits)
+ JSDoc now provides a `-v/--version` option to display information about the current version. (#303)
+ When running tests, you can now use the `--nocolor` option to disable colored output. On Windows, colored output is always disabled. (e17601fe, 8bc33541)
### Bug fixes
+ When using the `@event` tag to define an event within a class or namespace, the event's longname is now set correctly regardless of tag order. (#280)
+ The `@property` tag no longer results in malformed parse trees. (20f87094)
+ The `jsdoc` and `jsdoc.cmd` scripts now work correctly with paths that include spaces. (#127, #130)
+ The `jsdoc` script now works correctly on Cygwin and MinGW, and with the `dash` shell. (#182, #184, #187)
+ The `-d/--destination` option is no longer treated as a path relative to the JSDoc directory. Instead, it can contain an absolute path, or a path relative to the current working directory. (f5e3f0f3)
+ JSDoc now provides default options for the values in `conf.json`. (#129)
+ If the `conf.json` file does not exist, JSDoc no longer tries to create it, which prevents errors if the current user does not have write access to the JSDoc directory. (d2d05fcb)
+ Doclets for getters and setters are now parsed appropriately. (#150)
+ Only the first asterisk is removed from each line of a JSDoc comment. (#172)
+ If a child member overrides an ancestor member, the ancestor member is no longer documented. (#158)
+ If a member of a namespace has the same name as a namespace, the member is now documented correctly. (#214)
+ The parse tree now uses a single set of properties to track both JSDoc-style type information and Closure Compiler-style type information. (#118)
+ If a type has a leading `!`, indicating that it is non-nullable, the leading `!` is now removed from the type name. (#226)
+ When Markdown formatting is enabled, underscores in inline `{@link}` tags are no longer treated as Markdown formatting characters. (#259)
+ Markdown links now work correctly when a JavaScript reserved word, such as `constructor`, is used as the link text. (#249)
+ Markdown files for tutorials are now parsed based on the settings in `conf.json`, rather than using the "evilstreak" Markdown parser in all cases. (#220)
+ If a folder contains both tutorial source files and `.js` files, JSDoc no longer attempts to parse the `.js` files as JSON files. (#222)
+ The "evilstreak" Markdown parser now works correctly with files that use Windows-style line endings. (#223)
+ JSDoc no longer fails unit tests when the `conf.json` file is not present. (#206)
+ On Windows, JSDoc now passes all unit tests. (Multiple commits)
### Plugins
+ The new `partial` plugin adds support for a `@partial` tag, which links to an external file that contains JSDoc comments. (#156)
+ The new `commentsOnly` plugin removes everything in a file except JSDoc-style comments. You can use this plugin to document source files that are not valid JavaScript, including source files for other languages. (#304)
+ The new `eventDumper` plugin logs information about parser events to the console. (#242)
+ The new `verbose` plugin logs the name of each input file to the console. (#157)
### Template enhancements
#### Default template
+ The template output now includes pretty-printed versions of source files. This feature is enabled by default. To disable this feature, add the property `templates.default.outputSourceFiles: false` to your `conf.json` file. (#208)
+ You can now use the template if it is placed outside of the JSDoc directory. (#198)
+ The template no longer throws an error when a parameter does not have a name. (#175)
+ The navigation bar now includes an "Events" section if any events are documented. (#280)
+ Pages no longer include a "Classes" header when no classes are documented. (eb0186b9)
+ Member details now include "Inherited From" section when a member is inherited from another member. (#154)
+ If an `@author` tag contains text in the format "Jane Doe <jdoe@example.com>", the value is now converted to an HTML `mailto:` link. (#326)
+ Headings for functions now include the function's signature. (#253)
+ Type information is now displayed for events. (#192)
+ Functions now link to their return type when appropriate. (#192)
+ Type definitions that contain functions are now displayed correctly. (#292)
+ Tutorial output is now generated correctly. (#188)
+ Output files now use Google Code Prettify with the Tomorrow theme as a syntax highlighter. (#193)
+ The `index.html` output file is no longer overwritten if a namespace called `index` has been documented. (#244)
+ The current JSDoc version number is now displayed in the footer. (#321)
#### Haruki template
+ Members are now contained in arrays rather than objects, allowing overloaded members to be documented. (#153)
+ A clearer error message is now provided when the output destination is not specified correctly. (#174)
## 3.0.1 (June 2012)
### Enhancements
+ The `conf.json` file may now contain `source.include` and `source.exclude` properties. (#56)
+ `source.include` specifies files or directories that JSDoc should _always_ check for documentation.
+ `source.exclude` specifies files or directories that JSDoc should _never_ check for documentation.
These settings take precedence over the `source.includePattern` and `source.excludePattern` properties, which contain regular expressions that JSDoc uses to search for source files.
+ The `-t/--template` option may now specify the absolute path to a template. (#122)
### Bug fixes
+ JSDoc no longer throws exceptions when a symbol has a special name, such as `hasOwnProperty`. (1ef37251)
+ The `@alias` tag now works correctly when documenting inner classes as globals. (810dd7f7)
### Template improvements
+ The default template now sorts classes by name correctly when the classes come from several modules. (4ce17195)
+ The Haruki template now correctly supports `@example`, `@members`, and `@returns` tags. (6580e176, 59655252, 31c8554d)
## 3.0.0 (May 2012)
Initial release.

View File

@ -1,17 +0,0 @@
{
"tags": {
"allowUnknownTags": true
},
"source": {
"includePattern": ".+\\.js(doc)?$",
"excludePattern": "(^|\\/|\\\\)_"
},
"plugins": [],
"templates": {
"cleverLinks": false,
"monospaceLinks": false,
"default": {
"outputSourceFiles": true
}
}
}

View File

@ -1,184 +0,0 @@
#!/usr/bin/env node
/*global arguments, require: true */
/**
* @project jsdoc
* @author Michael Mathews <micmath@gmail.com>
* @license See LICENSE.md file included in this distribution.
*/
/**
* Data representing the environment in which this app is running.
*
* @namespace
* @name env
*/
global.env = {
/**
* Running start and finish times.
*
* @memberof env
*/
run: {
start: new Date(),
finish: null
},
/**
* The command-line arguments passed into JSDoc.
*
* @type Array
* @memberof env
*/
args: [],
/**
* The parsed JSON data from the configuration file.
*
* @type Object
* @memberof env
*/
conf: {},
/**
* The absolute path to the base directory of the JSDoc application.
*
* @private
* @type string
* @memberof env
*/
dirname: '.',
/**
* The user's working directory at the time that JSDoc was started.
*
* @private
* @type string
* @memberof env
*/
pwd: null,
/**
* The command-line options, parsed into a key/value hash.
*
* @type Object
* @memberof env
* @example if (global.env.opts.help) { console.log('Helpful message.'); }
*/
opts: {},
/**
* The source files that JSDoc will parse.
* @type Array
* @memberof env
*/
sourceFiles: [],
/**
* The JSDoc version number and revision date.
*
* @type Object
* @memberof env
*/
version: {}
};
// initialize the environment for the current JavaScript VM
(function(args) {
'use strict';
var path;
if (args[0] && typeof args[0] === 'object') {
// we should be on Node.js
args = [__dirname, process.cwd()];
path = require('path');
// Create a custom require method that adds `lib/jsdoc` and `node_modules` to the module
// lookup path. This makes it possible to `require('jsdoc/foo')` from external templates and
// plugins, and within JSDoc itself. It also allows external templates and plugins to
// require JSDoc's module dependencies without installing them locally.
require = require('requizzle')({
requirePaths: {
before: [path.join(__dirname, 'lib')],
after: [path.join(__dirname, 'node_modules')]
},
infect: true
});
}
require('./lib/jsdoc/util/runtime').initialize(args);
})( Array.prototype.slice.call(arguments, 0) );
/**
* Data that must be shared across the entire application.
*
* @namespace
* @name app
*/
global.app = {
jsdoc: {
name: require('./lib/jsdoc/name'),
parser: null,
scanner: new (require('./lib/jsdoc/src/scanner').Scanner)()
}
};
/**
* Recursively print an object's properties to stdout. This method is safe to use with objects that
* contain circular references. In addition, on Mozilla Rhino, this method is safe to use with
* native Java objects.
*
* @global
* @name dump
* @private
* @param {Object} obj - Object(s) to print to stdout.
*/
global.dump = function() {
'use strict';
var doop = require('./lib/jsdoc/util/doop').doop;
var _dump = require('./lib/jsdoc/util/dumper').dump;
for (var i = 0, l = arguments.length; i < l; i++) {
console.log( _dump(doop(arguments[i])) );
}
};
(function() {
'use strict';
var logger = require('./lib/jsdoc/util/logger');
var runtime = require('./lib/jsdoc/util/runtime');
var cli = require('./cli');
function cb(errorCode) {
cli.logFinish();
cli.exit(errorCode || 0);
}
cli.setVersionInfo()
.loadConfig();
if (!global.env.opts.test) {
cli.configureLogger();
}
cli.logStart();
// On Rhino, we use a try/catch block so we can log the Java exception (if available)
if ( runtime.isRhino() ) {
try {
cli.runCommand(cb);
}
catch(e) {
if (e.rhinoException) {
logger.fatal( e.rhinoException.printStackTrace() );
} else {
console.trace(e);
cli.exit(1);
}
}
}
else {
cli.runCommand(cb);
}
})();

View File

@ -1,144 +0,0 @@
'use strict';
var hasOwnProp = Object.prototype.hasOwnProperty;
function mapDependencies(index) {
var doclets, doc, len, dependencies = {};
Object.keys(index).forEach(function(name) {
doclets = index[name];
for (var i = 0, ii = doclets.length; i < ii; ++i) {
doc = doclets[i];
if (doc.kind === 'class' || doc.kind === 'external') {
dependencies[name] = {};
len = doc.augments && doc.augments.length || 0;
for (var j = 0; j < len; ++j) {
dependencies[name][doc.augments[j]] = true;
}
}
}
});
return dependencies;
}
function Sorter(dependencies) {
this.dependencies = dependencies;
this.visited = {};
this.sorted = [];
}
Sorter.prototype.visit = function(key) {
var self = this;
if (!(key in this.visited)) {
this.visited[key] = true;
if (this.dependencies[key]) {
Object.keys(this.dependencies[key]).forEach(function(path) {
self.visit(path);
});
}
this.sorted.push(key);
}
};
Sorter.prototype.sort = function() {
var self = this;
Object.keys(this.dependencies).forEach(function(key) {
self.visit(key);
});
return this.sorted;
};
function sort(dependencies) {
var sorter = new Sorter(dependencies);
return sorter.sort();
}
function getMembers(longname, docs) {
var candidate, members = [];
for (var i = 0, ii = docs.length; i < ii; ++i) {
candidate = docs[i];
if (candidate.memberof === longname && candidate.scope === 'instance') {
members.push(candidate);
}
}
return members;
}
function getAdditions(doclets, docs, longnames) {
var doop = require('jsdoc/util/doop');
var additions = [];
var doc;
var parents;
var members;
var member;
var parts;
// doclets will be undefined if the inherited symbol isn't documented
doclets = doclets || [];
for (var i = 0, ii = doclets.length; i < ii; i++) {
doc = doclets[i];
parents = doc.augments;
if (parents && doc.kind === 'class') {
for (var j = 0, jj = parents.length; j < jj; j++) {
members = getMembers(parents[j], docs);
for (var k = 0, kk = members.length; k < kk; k++) {
member = doop(members[k]);
if(!member.inherited)
{
member.inherits = member.longname;
}
member.inherited = true;
member.memberof = doc.longname;
parts = member.longname.split('#');
parts[0] = doc.longname;
member.longname = parts.join('#');
// if the child doesn't override the parent member, add the parent member
if (longnames.indexOf(member.longname) === -1) {
additions.push(member);
}
}
}
}
}
return additions;
}
exports.addInherited = function(docs) {
var dependencies = mapDependencies(docs.index);
var sorted = sort(dependencies);
var longnames = [];
// only build the list of longnames if we'll actually need it
if (sorted.length) {
longnames = docs.map(function(doc) {
// keep the ancestor's docs for a symbol if a local override is not documented
if (doc.longname && !doc.undocumented) {
return doc.longname;
}
});
}
sorted.forEach(function(name) {
var doclets = docs.index[name];
var additions = getAdditions(doclets, docs, longnames);
additions.forEach(function(doc) {
var name = doc.longname;
if ( !hasOwnProp.call(docs.index, name) ) {
docs.index[name] = [];
}
docs.index[name].push(doc);
docs.push(doc);
});
});
};

View File

@ -1,70 +0,0 @@
/**
A collection of functions relating to resolving @borrows tags in JSDoc symbols.
@module jsdoc/borrow
@author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
'use strict';
var doop = require('jsdoc/util/doop');
var logger = require('jsdoc/util/logger');
var hasOwnProp = Object.prototype.hasOwnProperty;
exports.indexAll = function(docs) {
var lookupTable = {};
docs.forEach(function(doc) {
if ( !hasOwnProp.call(lookupTable, doc.longname) ) {
lookupTable[doc.longname] = [];
}
lookupTable[doc.longname].push(doc);
});
docs.index = lookupTable;
};
// requires docs to have been indexed: docs.index must be defined here
/**
Take a copy of the docs for borrowed symbols and attach them to the
docs for the borrowing symbol. This process changes the symbols involved,
moving docs from the "borrowed" array and into the general docs, then
deleting the "borrowed" array.
*/
exports.resolveBorrows = function(docs) {
/*eslint max-nested-callbacks:[2, 3] */
if (!docs.index) {
logger.error('Unable to resolve borrowed symbols, because the docs have not been indexed.');
return;
}
docs.forEach(function(doc) {
if (doc.borrowed) {
doc.borrowed.forEach(function(b, i) {
var lent = docs.index[b.from], // lent is an array
asName = b.as || b.from;
if (lent) {
var cloned = doop(lent);
cloned.forEach(function(clone) {
asName = asName.replace(/^prototype\./, '#');
var parts = asName.split('#');
if (parts.length === 2) { clone.scope = 'instance'; }
else { clone.scope = 'static'; }
asName = parts.pop();
clone.name = asName;
clone.memberof = doc.longname;
clone.longname = clone.memberof + (clone.scope === 'instance' ? '#' : '.') +
clone.name;
docs.push(clone);
});
}
});
delete doc.borrowed;
}
});
};

View File

@ -1,59 +0,0 @@
/**
@overview
@author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
/**
@module jsdoc/config
*/
'use strict';
function mergeRecurse(target, source) {
Object.keys(source).forEach(function(p) {
if ( source[p].constructor === Object ) {
if ( !target[p] ) { target[p] = {}; }
mergeRecurse(target[p], source[p]);
}
else {
target[p] = source[p];
}
});
return target;
}
// required config values, override these defaults in your config.json if necessary
var defaults = {
tags: {
allowUnknownTags: true
},
templates: {
monospaceLinks: false,
cleverLinks: false
},
source: {
includePattern: '.+\\.js(doc)?$',
excludePattern: ''
},
plugins: []
};
/**
@class
@classdesc Represents a JSDoc application configuration.
@param {string} [json] - The contents of config.json.
*/
function Config(json) {
json = JSON.parse( (json || '{}') );
this._config = mergeRecurse(defaults, json);
}
module.exports = Config;
/**
Get the merged configuration values.
*/
Config.prototype.get = function() {
return this._config;
};

View File

@ -1,416 +0,0 @@
/**
* @overview
* @author Michael Mathews <micmath@gmail.com>
* @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
/**
* @module jsdoc/doclet
*/
'use strict';
var _ = require('underscore');
var jsdoc = {
name: require('jsdoc/name'),
src: {
astnode: require('jsdoc/src/astnode'),
Syntax: require('jsdoc/src/syntax').Syntax
},
tag: {
Tag: require('jsdoc/tag').Tag,
dictionary: require('jsdoc/tag/dictionary')
},
util: {
doop: require('jsdoc/util/doop')
}
};
var path = require('jsdoc/path');
var Syntax = jsdoc.src.Syntax;
var util = require('util');
function applyTag(doclet, tag) {
if (tag.title === 'name') {
doclet.name = tag.value;
}
if (tag.title === 'kind') {
doclet.kind = tag.value;
}
if (tag.title === 'description') {
doclet.description = tag.value;
}
}
// use the meta info about the source code to guess what the doclet kind should be
function codeToKind(code) {
var parent;
var isFunction = jsdoc.src.astnode.isFunction;
// default
var kind = 'member';
if (code.type === Syntax.FunctionDeclaration || code.type === Syntax.FunctionExpression) {
kind = 'function';
}
else if (code.node && code.node.parent) {
parent = code.node.parent;
if ( isFunction(parent) ) {
kind = 'param';
}
}
return kind;
}
function unwrap(docletSrc) {
if (!docletSrc) { return ''; }
// note: keep trailing whitespace for @examples
// extra opening/closing stars are ignored
// left margin is considered a star and a space
// use the /m flag on regex to avoid having to guess what this platform's newline is
docletSrc =
docletSrc.replace(/^\/\*\*+/, '') // remove opening slash+stars
.replace(/\**\*\/$/, '\\Z') // replace closing star slash with end-marker
.replace(/^\s*(\* ?|\\Z)/gm, '') // remove left margin like: spaces+star or spaces+end-marker
.replace(/\s*\\Z$/g, ''); // remove end-marker
return docletSrc;
}
function split(docletSrc) {
var tagSrcs = [];
// split out the basic tags, keep surrounding whitespace
// like: @tagTitle tagBody
docletSrc
.replace(/^(\s*)@(\S)/gm, '$1\\@$2') // replace splitter ats with an arbitrary sequence
.split('\\@') // then split on that arbitrary sequence
.forEach(function($) {
var parsedTag;
var tagText;
var tagTitle;
if ($) {
parsedTag = $.match(/^(\S+)(:?\s+(\S[\s\S]*))?/);
if (parsedTag) {
// we don't need parsedTag[0]
tagTitle = parsedTag[1];
tagText = parsedTag[2];
if (tagTitle) {
tagSrcs.push({
title: tagTitle,
text: tagText
});
}
}
}
});
return tagSrcs;
}
/**
* Convert the raw source of the doclet comment into an array of Tag objects.
* @private
*/
function toTags(docletSrc) {
var tags = [];
var tagSrcs = split(docletSrc);
for (var i = 0, l = tagSrcs.length; i < l; i++) {
tags.push({ title: tagSrcs[i].title, text: tagSrcs[i].text });
}
return tags;
}
function fixDescription(docletSrc) {
if (!/^\s*@/.test(docletSrc)) {
docletSrc = '@description ' + docletSrc;
}
return docletSrc;
}
/**
* @class
* @classdesc Represents a single JSDoc comment.
* @param {string} docletSrc - The raw source code of the jsdoc comment.
* @param {object=} meta - Properties describing the code related to this comment.
*/
var Doclet = exports.Doclet = function(docletSrc, meta) {
var newTags = [];
/** The original text of the comment from the source code. */
this.comment = docletSrc;
this.setMeta(meta);
docletSrc = unwrap(docletSrc);
docletSrc = fixDescription(docletSrc);
newTags = toTags.call(this, docletSrc);
for (var i = 0, l = newTags.length; i < l; i++) {
this.addTag(newTags[i].title, newTags[i].text);
}
this.postProcess();
};
/** Called once after all tags have been added. */
Doclet.prototype.postProcess = function() {
var i;
var l;
if (!this.preserveName) {
jsdoc.name.resolve(this);
}
if (this.name && !this.longname) {
this.setLongname(this.name);
}
if (this.memberof === '') {
delete this.memberof;
}
if (!this.kind && this.meta && this.meta.code) {
this.addTag( 'kind', codeToKind(this.meta.code) );
}
if (this.variation && this.longname && !/\)$/.test(this.longname) ) {
this.longname += '(' + this.variation + ')';
}
// add in any missing param names
if (this.params && this.meta && this.meta.code && this.meta.code.paramnames) {
for (i = 0, l = this.params.length; i < l; i++) {
if (!this.params[i].name) {
this.params[i].name = this.meta.code.paramnames[i] || '';
}
}
}
};
/**
* Add a tag to the doclet.
*
* @param {string} title - The title of the tag being added.
* @param {string} [text] - The text of the tag being added.
*/
Doclet.prototype.addTag = function(title, text) {
var tagDef = jsdoc.tag.dictionary.lookUp(title),
newTag = new jsdoc.tag.Tag(title, text, this.meta);
if (tagDef && tagDef.onTagged) {
tagDef.onTagged(this, newTag);
}
if (!tagDef) {
this.tags = this.tags || [];
this.tags.push(newTag);
}
applyTag(this, newTag);
};
function removeGlobal(longname) {
var globalRegexp = new RegExp('^' + jsdoc.name.GLOBAL_LONGNAME + '\\.?');
return longname.replace(globalRegexp, '');
}
/**
* Set the doclet's `memberof` property.
*
* @param {string} sid - The longname of the doclet's parent symbol.
*/
Doclet.prototype.setMemberof = function(sid) {
/**
* The longname of the symbol that contains this one, if any.
* @type string
*/
this.memberof = removeGlobal(sid)
.replace(/\.prototype/g, jsdoc.name.INSTANCE);
};
/**
* Set the doclet's `longname` property.
*
* @param {string} name - The longname for the doclet.
*/
Doclet.prototype.setLongname = function(name) {
/**
* The fully resolved symbol name.
* @type string
*/
this.longname = removeGlobal(name);
if (jsdoc.tag.dictionary.isNamespace(this.kind)) {
this.longname = jsdoc.name.applyNamespace(this.longname, this.kind);
}
};
/**
* Get the full path to the source file that is associated with a doclet.
*
* @private
* @param {module:jsdoc/doclet.Doclet} The doclet to check for a filepath.
* @return {string} The path to the doclet's source file, or an empty string if the path is not
* available.
*/
function getFilepath(doclet) {
if (!doclet || !doclet.meta || !doclet.meta.filename) {
return '';
}
return path.join(doclet.meta.path || '', doclet.meta.filename);
}
/**
* Set the doclet's `scope` property. Must correspond to a scope name that is defined in
* {@link module:jsdoc/name.SCOPE_NAMES}.
*
* @param {module:jsdoc/name.SCOPE_NAMES} scope - The scope for the doclet relative to the symbol's
* parent.
* @throws {Error} If the scope name is not recognized.
*/
Doclet.prototype.setScope = function(scope) {
var errorMessage;
var filepath;
var scopeNames = Object.keys(jsdoc.name.SCOPE_NAMES);
if (scopeNames.indexOf(scope) === -1) {
filepath = getFilepath(this);
errorMessage = util.format('The scope name "%s" is not recognized. Use one of the names ' +
'defined in module:jsdoc/name.SCOPE_NAMES.', scope);
if (filepath) {
errorMessage += util.format(' (Source file: %s)', filepath);
}
throw new Error(errorMessage);
}
this.scope = scope;
};
/**
* Add a symbol to this doclet's `borrowed` array.
*
* @param {string} source - The longname of the symbol that is the source.
* @param {string} target - The name the symbol is being assigned to.
*/
Doclet.prototype.borrow = function(source, target) {
var about = { from: source };
if (target) {
about.as = target;
}
if (!this.borrowed) {
/**
* A list of symbols that are borrowed by this one, if any.
* @type Array.<string>
*/
this.borrowed = [];
}
this.borrowed.push(about);
};
Doclet.prototype.mix = function(source) {
/**
* A list of symbols that are mixed into this one, if any.
* @type Array.<string>
*/
this.mixes = this.mixes || [];
this.mixes.push(source);
};
/**
* Add a symbol to the doclet's `augments` array.
*
* @param {string} base - The longname of the base symbol.
*/
Doclet.prototype.augment = function(base) {
/**
* A list of symbols that are augmented by this one, if any.
* @type Array.<string>
*/
this.augments = this.augments || [];
this.augments.push(base);
};
/**
* Set the `meta` property of this doclet.
*
* @param {object} meta
*/
Doclet.prototype.setMeta = function(meta) {
/**
* Information about the source code associated with this doclet.
* @namespace
*/
this.meta = this.meta || {};
if (meta.range) {
/**
* The positions of the first and last characters of the code associated with this doclet.
* @type Array.<number>
*/
this.meta.range = meta.range.slice(0);
}
if (meta.lineno) {
/**
* The name of the file containing the code associated with this doclet.
* @type string
*/
this.meta.filename = path.basename(meta.filename);
/**
* The line number of the code associated with this doclet.
* @type number
*/
this.meta.lineno = meta.lineno;
var pathname = path.dirname(meta.filename);
if (pathname && pathname !== '.') {
this.meta.path = pathname;
}
}
/**
* Information about the code symbol.
* @namespace
*/
this.meta.code = this.meta.code || {};
if (meta.id) { this.meta.code.id = meta.id; }
if (meta.code) {
if (meta.code.name) {
/** The name of the symbol in the source code. */
this.meta.code.name = meta.code.name;
}
if (meta.code.type) {
/** The type of the symbol in the source code. */
this.meta.code.type = meta.code.type;
}
// the AST node is only enumerable in debug mode, which reduces clutter for the
// --explain/-X option
if (meta.code.node) {
Object.defineProperty(this.meta.code, 'node', {
value: meta.code.node,
enumerable: global.env.opts.debug ? true : false
});
}
if (meta.code.funcscope) {
this.meta.code.funcscope = meta.code.funcscope;
}
if (meta.code.value) {
/** The value of the symbol in the source code. */
this.meta.code.value = meta.code.value;
}
if (meta.code.paramnames) {
this.meta.code.paramnames = meta.code.paramnames.slice(0);
}
}
};

View File

@ -1,74 +0,0 @@
/**
* Extended version of the standard `fs` module.
* @module jsdoc/fs
*/
'use strict';
var fs = require('fs');
var path = require('path');
var runtime = require('jsdoc/util/runtime');
var ls = exports.ls = function(dir, recurse, _allFiles, _path) {
var file;
var files;
var isFile;
// first pass
if (_path === undefined) {
_allFiles = [];
_path = [dir];
}
if (!_path.length) {
return _allFiles;
}
if (recurse === undefined) {
recurse = 1;
}
try {
isFile = fs.statSync(dir).isFile();
}
catch (e) {
isFile = false;
}
if (isFile) {
files = [dir];
}
else {
files = fs.readdirSync(dir);
}
for (var i = 0, l = files.length; i < l; i++) {
file = String(files[i]);
// skip dot files
if (file.match(/^\.[^\.\/\\]/)) {
continue;
}
if ( fs.statSync(path.join(_path.join('/'), file)).isDirectory() ) {
// it's a directory
_path.push(file);
if (_path.length - 1 < recurse) {
ls(_path.join('/'), recurse, _allFiles, _path);
}
_path.pop();
}
else {
// it's a file
_allFiles.push( path.normalize(path.join(_path.join('/'), file)) );
}
}
return _allFiles;
};
// export the VM-specific implementations of the extra methods
// TODO: document extra methods here
var extras = require( runtime.getModulePath('fs') );
Object.keys(extras).forEach(function(extra) {
exports[extra] = extras[extra];
});

View File

@ -1,259 +0,0 @@
/**
A collection of functions relating to JSDoc symbol name manipulation.
@module jsdoc/name
@requires jsdoc/tag/dictionary
@author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
'use strict';
var _ = require('underscore');
// Longname used for doclets whose actual longname cannot be identified.
var ANONYMOUS_LONGNAME = exports.ANONYMOUS_LONGNAME = '<anonymous>';
// Longname used for doclets in global scope.
var GLOBAL_LONGNAME = exports.GLOBAL_LONGNAME = '<global>';
var INNER = exports.INNER = '~';
var INSTANCE = exports.INSTANCE = '#';
var MODULE_PREFIX = exports.MODULE_PREFIX = 'module:';
// Scope identifiers.
var SCOPE_NAMES = exports.SCOPE_NAMES = {
global: 'global',
inner: 'inner',
instance: 'instance',
'static': 'static'
};
var STATIC = exports.STATIC = '.';
var scopeToPunc = exports.scopeToPunc = {
'inner': INNER,
'instance': INSTANCE,
'static': STATIC
};
var puncToScope = exports.puncToScope = _.invert(scopeToPunc);
var DEFAULT_SCOPE = SCOPE_NAMES.static;
var REGEXP_SCOPE_PUNC = '([' + INNER + INSTANCE + STATIC + '])';
function nameIsLongname(name, memberof) {
var regexp = new RegExp('^' + memberof + REGEXP_SCOPE_PUNC);
return regexp.test(name);
}
function prototypeToPunc(name) {
return name.replace(/(?:^|\.)prototype\.?/g, INSTANCE);
}
/**
Resolves the longname, memberof, variation and name values of the given doclet.
@param {module:jsdoc/doclet.Doclet} doclet
*/
exports.resolve = function(doclet) {
var about = {};
var leadingScope = new RegExp('^' + REGEXP_SCOPE_PUNC);
var memberof = doclet.memberof || '';
var name = doclet.name ? String(doclet.name) : '';
var trailingScope = new RegExp(REGEXP_SCOPE_PUNC + '$');
var parentDoc;
// change MyClass.prototype.instanceMethod to MyClass#instanceMethod
// (but not in function params, which lack doclet.kind)
// TODO: check for specific doclet.kind values (probably function, class, and module)
if (name && doclet.kind) {
name = prototypeToPunc(name);
}
doclet.name = name;
// member of a var in an outer scope?
if (name && !memberof && doclet.meta.code && doclet.meta.code.funcscope) {
name = doclet.longname = doclet.meta.code.funcscope + INNER + name;
}
if (memberof || doclet.forceMemberof) { // @memberof tag given
memberof = prototypeToPunc(memberof);
// the name is a complete longname, like @name foo.bar, @memberof foo
if (name && nameIsLongname(name, memberof) && name !== memberof) {
about = exports.shorten(name, (doclet.forceMemberof ? memberof : undefined));
}
// the name and memberof are identical and refer to a module,
// like @name module:foo, @memberof module:foo (probably a member like 'var exports')
else if (name && name === memberof && name.indexOf(MODULE_PREFIX) === 0) {
about = exports.shorten(name, (doclet.forceMemberof ? memberof : undefined));
}
// the name and memberof are identical, like @name foo, @memberof foo
else if (name && name === memberof) {
doclet.scope = doclet.scope || DEFAULT_SCOPE;
name = memberof + scopeToPunc[doclet.scope] + name;
about = exports.shorten(name, (doclet.forceMemberof ? memberof : undefined));
}
// like @memberof foo# or @memberof foo~
else if (name && trailingScope.test(memberof) ) {
about = exports.shorten(memberof + name, (doclet.forceMemberof ? memberof : undefined));
}
else if (name && doclet.scope) {
about = exports.shorten(memberof + (scopeToPunc[doclet.scope] || '') + name,
(doclet.forceMemberof ? memberof : undefined));
}
}
else { // no @memberof
about = exports.shorten(name);
}
if (about.name) {
doclet.name = about.name;
}
if (about.memberof) {
doclet.setMemberof(about.memberof);
}
if (about.longname && (!doclet.longname || doclet.longname === doclet.name)) {
doclet.setLongname(about.longname);
}
if (doclet.scope === 'global') { // via @global tag?
doclet.setLongname(doclet.name);
delete doclet.memberof;
}
else if (about.scope) {
if (about.memberof === GLOBAL_LONGNAME) { // via @memberof <global> ?
doclet.scope = 'global';
}
else {
doclet.scope = puncToScope[about.scope];
}
}
else {
if (doclet.name && doclet.memberof && !doclet.longname) {
if ( leadingScope.test(doclet.name) ) {
doclet.scope = puncToScope[RegExp.$1];
doclet.name = doclet.name.substr(1);
}
else {
doclet.scope = DEFAULT_SCOPE;
}
doclet.setLongname(doclet.memberof + scopeToPunc[doclet.scope] + doclet.name);
}
}
if (about.variation) {
doclet.variation = about.variation;
}
// if we never found a longname, just use an empty string
if (!doclet.longname) {
doclet.longname = '';
}
};
// TODO: make this a private method, or remove it if possible
RegExp.escape = RegExp.escape || function(str) {
var specials = new RegExp('[.*+?|()\\[\\]{}\\\\]', 'g'); // .*+?|()[]{}\
return str.replace(specials, '\\$&');
};
/**
@method module:jsdoc/name.applyNamespace
@param {string} longname The full longname of the symbol.
@param {string} ns The namespace to be applied.
@returns {string} The longname with the namespace applied.
*/
exports.applyNamespace = function(longname, ns) {
var nameParts = exports.shorten(longname),
name = nameParts.name;
longname = nameParts.longname;
if ( !/^[a-zA-Z]+?:.+$/i.test(name) ) {
longname = longname.replace( new RegExp(RegExp.escape(name) + '$'), ns + ':' + name );
}
return longname;
};
/**
Given a longname like "a.b#c(2)", slice it up into ["a.b", "#", 'c', '2'],
representing the memberof, the scope, the name, and variation.
@param {string} longname
@param {string} forcedMemberof
@returns {object} Representing the properties of the given name.
*/
exports.shorten = function(longname, forcedMemberof) {
// quoted strings in a longname are atomic, convert to tokens
var atoms = [], token;
// handle quoted names like foo["bar"] or foo['bar']
longname = longname.replace(/(\[?["'].+?["']\]?)/g, function($) {
var dot = '';
if ( /^\[/.test($) ) {
dot = '.';
$ = $.replace( /^\[/g, '' ).replace( /\]$/g, '' );
}
token = '@{' + atoms.length + '}@';
atoms.push($);
return dot + token; // foo["bar"] => foo.@{1}@
});
var name = '',
scope = '', // ., ~, or #
memberof = '',
parts,
variation;
longname = prototypeToPunc(longname);
if (typeof forcedMemberof !== 'undefined') {
name = longname.substr(forcedMemberof.length);
parts = forcedMemberof.match(/^(.*?)([#.~]?)$/);
if (parts[1]) { memberof = parts[1] || forcedMemberof; }
if (parts[2]) { scope = parts[2]; }
}
else {
parts = longname ?
(longname.match( /^(:?(.+)([#.~]))?(.+?)$/ ) || []).reverse() :
[''];
name = parts[0] || ''; // ensure name is always initialised to avoid error being thrown when calling replace on undefined [gh-24]
scope = parts[1] || ''; // ., ~, or #
memberof = parts[2] || '';
}
// like /** @name foo.bar(2) */
if ( /(.+)\(([^)]+)\)$/.test(name) ) {
name = RegExp.$1;
variation = RegExp.$2;
}
//// restore quoted strings back again
var i = atoms.length;
while (i--) {
longname = longname.replace('@{' + i + '}@', atoms[i]);
memberof = memberof.replace('@{' + i + '}@', atoms[i]);
scope = scope.replace('@{' + i + '}@', atoms[i]);
name = name.replace('@{' + i + '}@', atoms[i]);
}
////
return {longname: longname, memberof: memberof, scope: scope, name: name, variation: variation};
};
/**
Split a string that starts with a name and ends with a description into its parts.
@param {string} nameDesc
@returns {object} Hash with "name" and "description" properties.
*/
exports.splitName = function(nameDesc) {
// like: name, [name], name text, [name] text, name - text, or [name] - text
// the hyphen must be on the same line as the name; this prevents us from treating a Markdown
// dash as a separator
nameDesc.match(/^(\[[^\]]+\]|\S+)((?:[ \t]*\-\s*|\s+)(\S[\s\S]*))?$/);
return {
name: RegExp.$1,
description: RegExp.$3
};
};

View File

@ -1,301 +0,0 @@
/**
* Parse the command line arguments.
* @module jsdoc/opts/argparser
* @author Michael Mathews <micmath@gmail.com>
* @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
'use strict';
var _ = require('underscore');
var hasOwnProp = Object.prototype.hasOwnProperty;
/**
* Create an instance of the parser.
* @classdesc A parser to interpret the key-value pairs entered on the command line.
* @constructor
*/
var ArgParser = function() {
this._options = [];
this._shortNameIndex = {};
this._longNameIndex = {};
};
ArgParser.prototype._getOptionByShortName = function(name) {
if (hasOwnProp.call(this._shortNameIndex, name)) {
return this._options[this._shortNameIndex[name]];
}
return null;
};
ArgParser.prototype._getOptionByLongName = function(name) {
if (hasOwnProp.call(this._longNameIndex, name)) {
return this._options[this._longNameIndex[name]];
}
return null;
};
ArgParser.prototype._addOption = function(option) {
var currentIndex;
var longName = option.longName;
var shortName = option.shortName;
this._options.push(option);
currentIndex = this._options.length - 1;
if (shortName) {
this._shortNameIndex[shortName] = currentIndex;
}
if (longName) {
this._longNameIndex[longName] = currentIndex;
}
return this;
};
/**
* Provide information about a legal option.
* @param {character} shortName The short name of the option, entered like: -T.
* @param {string} longName The equivalent long name of the option, entered like: --test.
* @param {boolean} hasValue Does this option require a value? Like: -t templatename
* @param {string} helpText A brief description of the option.
* @param {boolean} [canHaveMultiple=false] Set to `true` if the option can be provided more than once.
* @param {function} [coercer] A function to coerce the given value to a specific type.
* @return {this}
* @example
* myParser.addOption('t', 'template', true, 'The path to the template.');
* myParser.addOption('h', 'help', false, 'Show the help message.');
*/
ArgParser.prototype.addOption = function(shortName, longName, hasValue, helpText, canHaveMultiple, coercer) {
var option = {
shortName: shortName,
longName: longName,
hasValue: hasValue,
helpText: helpText,
canHaveMultiple: (canHaveMultiple || false),
coercer: coercer
};
return this._addOption(option);
};
// TODO: refactor addOption to accept objects, then get rid of this method
/**
* Provide information about an option that should not cause an error if present, but that is always
* ignored (for example, an option that was used in previous versions but is no longer supported).
*
* @private
* @param {string} shortName - The short name of the option with a leading hyphen (for example,
* `-v`).
* @param {string} longName - The long name of the option with two leading hyphens (for example,
* `--version`).
*/
ArgParser.prototype.addIgnoredOption = function(shortName, longName) {
var option = {
shortName: shortName,
longName: longName,
ignore: true
};
return this._addOption(option);
};
function padding(length) {
return new Array(length + 1).join(' ');
}
function padLeft(str, length) {
return padding(length) + str;
}
function padRight(str, length) {
return str + padding(length);
}
function findMaxLength(arr) {
var max = 0;
arr.forEach(function(item) {
if (item.length > max) {
max = item.length;
}
});
return max;
}
function concatWithMaxLength(items, maxLength) {
var result = '';
// to prevent endless loops, always use the first item, regardless of length
result += items.shift();
while ( items.length && (result.length + items[0].length < maxLength) ) {
result += ' ' + items.shift();
}
return result;
}
// we want to format names and descriptions like this:
// | -f, --foo Very long description very long description very long |
// | description very long description. |
function formatHelpInfo(options) {
var MARGIN_LENGTH = 4;
var results = [];
var maxLength = process.stdout.columns;
var maxNameLength = findMaxLength(options.names);
var maxDescriptionLength = findMaxLength(options.descriptions);
var wrapDescriptionAt = maxLength - (MARGIN_LENGTH * 3) - maxNameLength;
// build the string for each option
options.names.forEach(function(name, i) {
var result;
var partialDescription;
var words;
// add a left margin to the name
result = padLeft(options.names[i], MARGIN_LENGTH);
// and a right margin, with extra padding so the descriptions line up with one another
result = padRight(result, maxNameLength - options.names[i].length + MARGIN_LENGTH);
// split the description on spaces
words = options.descriptions[i].split(' ');
// add as much of the description as we can fit on the first line
result += concatWithMaxLength(words, wrapDescriptionAt);
// if there's anything left, keep going until we've consumed the description
while (words.length) {
partialDescription = padding( maxNameLength + (MARGIN_LENGTH * 2) );
partialDescription += concatWithMaxLength(words, wrapDescriptionAt);
result += '\n' + partialDescription;
}
results.push(result);
});
return results;
}
/**
* Generate a summary of all the options with corresponding help text.
* @returns {string}
*/
ArgParser.prototype.help = function() {
var options = {
names: [],
descriptions: []
};
this._options.forEach(function(option) {
var name = '';
// don't show ignored options
if (option.ignore) {
return;
}
if (option.shortName) {
name += '-' + option.shortName + (option.longName ? ', ' : '');
}
if (option.longName) {
name += '--' + option.longName;
}
if (option.hasValue) {
name += ' <value>';
}
options.names.push(name);
options.descriptions.push(option.helpText);
});
return 'Options:\n' + formatHelpInfo(options).join('\n');
};
/**
* Get the options.
* @param {Array.<string>} args An array, like ['-x', 'hello']
* @param {Object} [defaults={}] An optional collection of default values.
* @returns {Object} The keys will be the longNames, or the shortName if no longName is defined for
* that option. The values will be the values provided, or `true` if the option accepts no value.
*/
ArgParser.prototype.parse = function(args, defaults) {
var result = defaults && _.defaults({}, defaults) || {};
result._ = [];
for (var i = 0, leni = args.length; i < leni; i++) {
var arg = '' + args[i],
next = (i < leni - 1) ? '' + args[i + 1] : null,
option,
shortName = null,
longName,
name,
value = null;
// like -t
if (arg.charAt(0) === '-') {
// like --template
if (arg.charAt(1) === '-') {
name = longName = arg.slice(2);
option = this._getOptionByLongName(longName);
}
else {
name = shortName = arg.slice(1);
option = this._getOptionByShortName(shortName);
}
if (option === null) {
throw new Error( 'Unknown command line option found: ' + name );
}
if (option.hasValue) {
value = next;
i++;
if (value === null || value.charAt(0) === '-') {
throw new Error( 'Command line option requires a value: ' + name );
}
}
else {
value = true;
}
// skip ignored options now that we've consumed the option text
if (option.ignore) {
continue;
}
if (option.longName && shortName) {
name = option.longName;
}
if (typeof option.coercer === 'function') {
value = option.coercer(value);
}
// Allow for multiple options of the same type to be present
if (option.canHaveMultiple && hasOwnProp.call(result, name)) {
var val = result[name];
if (val instanceof Array) {
val.push(value);
} else {
result[name] = [val, value];
}
}
else {
result[name] = value;
}
}
else {
result._.push(arg);
}
}
return result;
};
module.exports = ArgParser;

View File

@ -1,138 +0,0 @@
/**
* @module jsdoc/opts/args
* @requires jsdoc/opts/argparser
* @author Michael Mathews <micmath@gmail.com>
* @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
'use strict';
var ArgParser = require('jsdoc/opts/argparser');
var querystring = require('querystring');
var util = require('util');
var ourOptions;
var argParser = new ArgParser();
var hasOwnProp = Object.prototype.hasOwnProperty;
// cast strings to booleans or integers where appropriate
function castTypes(item) {
var integer;
var result = item;
switch (result) {
case 'true':
result = true;
break;
case 'false':
result = false;
break;
default:
// might be an integer
integer = parseInt(result, 10);
if (String(integer) === result && integer !== 'NaN') {
result = integer;
}
}
return result;
}
// check for strings that we need to cast to other types
function fixTypes(item) {
var result = item;
// recursively process arrays and objects
if ( util.isArray(result) ) {
for (var i = 0, l = result.length; i < l; i++) {
result[i] = fixTypes(result[i]);
}
}
else if (typeof result === 'object') {
Object.keys(result).forEach(function(prop) {
result[prop] = fixTypes(result[prop]);
});
}
else {
result = castTypes(result);
}
return result;
}
function parseQuery(str) {
var result = querystring.parse(str);
Object.keys(result).forEach(function(prop) {
result[prop] = fixTypes(result[prop]);
});
return result;
}
argParser.addOption('t', 'template', true, 'The path to the template to use. Default: path/to/jsdoc/templates/default');
argParser.addOption('c', 'configure', true, 'The path to the configuration file. Default: path/to/jsdoc/conf.json');
argParser.addOption('e', 'encoding', true, 'Assume this encoding when reading all source files. Default: utf8');
argParser.addOption('T', 'test', false, 'Run all tests and quit.');
argParser.addOption('d', 'destination', true, 'The path to the output folder. Use "console" to dump data to the console. Default: ./out/');
argParser.addOption('p', 'private', false, 'Display symbols marked with the @private tag. Default: false');
argParser.addOption('r', 'recurse', false, 'Recurse into subdirectories when scanning for source code files.');
argParser.addOption('h', 'help', false, 'Print this message and quit.');
argParser.addOption('X', 'explain', false, 'Dump all found doclet internals to console and quit.');
argParser.addOption('q', 'query', true, 'A query string to parse and store in env.opts.query. Example: foo=bar&baz=true', false, parseQuery);
argParser.addOption('u', 'tutorials', true, 'Directory in which JSDoc should search for tutorials.');
argParser.addOption('v', 'version', false, 'Display the version number and quit.');
argParser.addOption('', 'debug', false, 'Log information for debugging JSDoc. On Rhino, launches the debugger when passed as the first option.');
argParser.addOption('', 'verbose', false, 'Log detailed information to the console as JSDoc runs.');
argParser.addOption('', 'pedantic', false, 'Treat errors as fatal errors, and treat warnings as errors. Default: false');
// Options specific to tests
argParser.addOption(null, 'match', true, 'Only run tests containing <value>.', true);
argParser.addOption(null, 'nocolor', false, 'Do not use color in console output from tests.');
// Options that are no longer supported and should be ignored
argParser.addIgnoredOption('l', 'lenient'); // removed in JSDoc 3.3.0
/**
* Set the options for this app.
* @throws {Error} Illegal arguments will throw errors.
* @param {string|String[]} args The command line arguments for this app.
*/
exports.parse = function(args) {
args = args || [];
if (typeof args === 'string' || args.constructor === String) {
args = String(args).split(/\s+/g);
}
ourOptions = argParser.parse(args);
return ourOptions;
};
/**
* Retrieve help message for options.
*/
exports.help = function() {
return argParser.help();
};
/**
* Get a named option.
* @param {string} name The name of the option.
* @return {string} The value associated with the given name.
*//**
* Get all the options for this app.
* @return {Object} A collection of key/values representing all the options.
*/
exports.get = function(name) {
if (typeof name === 'undefined') {
return ourOptions;
}
else if ( hasOwnProp.call(ourOptions, name) ) {
return ourOptions[name];
}
};

View File

@ -1,70 +0,0 @@
/**
@overview
@author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
'use strict';
/**
@module jsdoc/package
@see http://wiki.commonjs.org/wiki/Packages/1.0
*/
/**
@class
@classdesc Represents a JavaScript package.
@param {string} json - The contents of package.json.
*/
exports.Package = function(json) {
json = json || '{}';
/** The source files associated with this package.
@type {Array<String>}
*/
this.files = [];
/** The kind of this package.
@readonly
@default
@type {string}
*/
this.kind = 'package';
json = JSON.parse(json);
/** The name of this package.
This value is found in the package.json file passed in as a command line option.
@type {string}
*/
this.name = json.name;
/** The longname of this package.
@type {string}
*/
this.longname = this.kind + ':' + this.name;
/** The description of this package.
@type {string}
*/
this.description = json.description;
/**
The hash summary of the source file.
@type {string}
@since 3.2.0
*/
this.version = json.version;
/**
* The licenses of this package.
* @type {Array<Object>}
* @example
* "licenses": [
* {
* "type": "GPLv2",
* "url": "http://www.example.com/licenses/gpl.html"
* }
* ]
*/
this.licenses = json.licenses;
};

View File

@ -1,134 +0,0 @@
/*global env: true */
/**
* Extended version of the standard `path` module.
* @module jsdoc/path
*/
'use strict';
var fs = require('fs');
var path = require('path');
var runtime = require('jsdoc/util/runtime');
function prefixReducer(previousPath, current) {
var currentPath = [];
// if previousPath is defined, but has zero length, there's no common prefix; move along
if (previousPath && !previousPath.length) {
return currentPath;
}
currentPath = path.resolve(global.env.pwd, current).split(path.sep) || [];
if (previousPath && currentPath.length) {
// remove chunks that exceed the previous path's length
currentPath = currentPath.slice(0, previousPath.length);
// if a chunk doesn't match the previous path, remove everything from that chunk on
for (var i = 0, l = currentPath.length; i < l; i++) {
if (currentPath[i] !== previousPath[i]) {
currentPath.splice(i, currentPath.length - i);
break;
}
}
}
return currentPath;
}
/**
* Find the common prefix for an array of paths. If there is a common prefix, a trailing separator
* is appended to the prefix. Relative paths are resolved relative to the current working directory.
*
* For example, assuming that the current working directory is `/Users/jsdoc`:
*
* + For the single path `foo/bar/baz/qux.js`, the common prefix is `foo/bar/baz/`.
* + For paths `foo/bar/baz/qux.js`, `foo/bar/baz/quux.js`, and `foo/bar/baz.js`, the common prefix
* is `/Users/jsdoc/foo/bar/`.
* + For paths `../jsdoc/foo/bar/baz/qux/quux/test.js`, `/Users/jsdoc/foo/bar/bazzy.js`, and
* `../../Users/jsdoc/foo/bar/foobar.js`, the common prefix is `/Users/jsdoc/foo/bar/`.
* + For paths `foo/bar/baz/qux.js` and `../../Library/foo/bar/baz.js`, there is no common prefix,
* and an empty string is returned.
*
* @param {Array.<string>} paths - The paths to search for a common prefix.
* @return {string} The common prefix, or an empty string if there is no common prefix.
*/
exports.commonPrefix = function(paths) {
var segments;
var prefix = '';
paths = paths || [];
// if there's only one path, its resolved dirname (plus a trailing slash) is the common prefix
if (paths.length === 1) {
prefix = path.resolve(global.env.pwd, paths[0]);
if ( path.extname(prefix) ) {
prefix = path.dirname(prefix);
}
prefix += path.sep;
}
else {
segments = paths.reduce(prefixReducer, undefined) || [];
// if there's anything left (other than a placeholder for a leading slash), add a
// placeholder for a trailing slash
if ( segments.length && (segments.length > 1 || segments[0] !== '') ) {
segments.push('');
}
prefix = segments.join(path.sep);
}
return prefix;
};
/**
* Retrieve the fully qualified path to the requested resource.
*
* If the resource path is specified as a relative path, JSDoc searches for the path in the
* directory where the JSDoc configuration file is located, then in the current working directory,
* and finally in the JSDoc directory.
*
* If the resource path is specified as a fully qualified path, JSDoc uses the path as-is.
*
* @param {string} filepath - The path to the requested resource. May be an absolute path; a path
* relative to the JSDoc directory; or a path relative to the current working directory.
* @param {string} [filename] - The filename of the requested resource.
* @return {string} The fully qualified path (or, on Rhino, a URI) to the requested resource.
* Includes the filename if one was provided.
*/
exports.getResourcePath = function(filepath, filename) {
var result = null;
function pathExists(_path) {
try {
fs.readdirSync(_path);
}
catch(e) {
return false;
}
return true;
}
// absolute paths are normalized by path.resolve on the first pass
[path.dirname(global.env.opts.configure || ''), env.pwd, env.dirname].forEach(function(_path) {
if (!result && _path) {
_path = path.resolve(_path, filepath);
if ( pathExists(_path) ) {
result = _path;
}
}
});
if (result) {
result = filename ? path.join(result, filename) : result;
}
return result;
};
Object.keys(path).forEach(function(member) {
exports[member] = path[member];
});

View File

@ -1,53 +0,0 @@
/*global app: true */
/**
* Utility functions to support the JSDoc plugin framework.
* @module jsdoc/plugins
*/
'use strict';
var logger = require('jsdoc/util/logger');
var path = require('jsdoc/path');
function addHandlers(handlers, parser) {
Object.keys(handlers).forEach(function(eventName) {
parser.on(eventName, handlers[eventName]);
});
}
exports.installPlugins = function(plugins, parser) {
var dictionary = require('jsdoc/tag/dictionary');
var eventName;
var plugin;
for (var i = 0, l = plugins.length; i < l; i++) {
plugin = require(plugins[i]);
// allow user-defined plugins to...
//...register event handlers
if (plugin.handlers) {
addHandlers(plugin.handlers, parser);
}
//...define tags
if (plugin.defineTags) {
plugin.defineTags(dictionary);
}
//...add a Rhino node visitor (deprecated in JSDoc 3.3)
if (plugin.nodeVisitor) {
if ( !parser.addNodeVisitor ) {
logger.error('Unable to add the Rhino node visitor from %s, because JSDoc ' +
'is not using the Rhino JavaScript parser.', plugins[i]);
}
else {
parser.addNodeVisitor(plugin.nodeVisitor);
}
}
//...add a Mozilla Parser API node visitor
if (plugin.astNodeVisitor) {
parser.addAstNodeVisitor(plugin.astNodeVisitor);
}
}
};

View File

@ -1,26 +0,0 @@
/*global env: true */
/**
* Make the contents of a README file available to include in the output.
* @module jsdoc/readme
* @author Michael Mathews <micmath@gmail.com>
* @author Ben Blank <ben.blank@gmail.com>
*/
'use strict';
var fs = require('jsdoc/fs'),
markdown = require('jsdoc/util/markdown');
/**
* @class
* @classdesc Represents a README file.
* @param {string} path - The filepath to the README.
*/
function ReadMe(path) {
var content = fs.readFileSync(path, env.opts.encoding),
parse = markdown.getParser();
this.html = parse(content);
}
module.exports = ReadMe;

View File

@ -1,624 +0,0 @@
/**
* @overview Schema for validating JSDoc doclets.
*
* @author Michael Mathews <micmath@gmail.com>
* @author Jeff Williams <jeffrey.l.williams@gmail.com>
* @license Apache License 2.0 - See file 'LICENSE.md' in this project.
* @see <http://tools.ietf.org/html/draft-zyp-json-schema-03>
*/
'use strict';
// JSON schema types
var ARRAY = 'array';
var BOOLEAN = 'boolean';
var INTEGER = 'integer';
var NULL = 'null';
var NUMBER = 'number';
var OBJECT = 'object';
var STRING = 'string';
var UNDEFINED = 'undefined';
var BOOLEAN_OPTIONAL = [BOOLEAN, NULL, UNDEFINED];
var STRING_OPTIONAL = [STRING, NULL, UNDEFINED];
var EVENT_REGEXP = /event\:[\S]+/;
var PACKAGE_REGEXP = /package\:[\S]+/;
// information about the code associated with a doclet
var META_SCHEMA = exports.META_SCHEMA = {
type: OBJECT,
optional: true,
additionalProperties: false,
properties: {
code: {
type: OBJECT,
additionalProperties: false,
properties: {
funcscope: {
type: STRING,
optional: true
},
id: {
type: STRING,
optional: true
},
name: {
type: STRING,
optional: true
},
node: {
type: OBJECT,
optional: true
},
paramnames: {
type: ARRAY,
optional: true,
uniqueItems: true,
items: {
type: STRING
}
},
type: {
type: STRING,
optional: true
},
value: {
optional: true
}
}
},
filename: {
title: 'The name of the file that contains the code associated with this doclet.',
type: STRING,
optional: true
},
lineno: {
title: 'The line number of the code associated with this doclet.',
type: NUMBER,
optional: true
},
path: {
title: 'The path in which the code associated with this doclet is located.',
type: STRING,
optional: true
},
range: {
title: 'The positions of the first and last characters of the code associated with ' +
'this doclet.',
type: ARRAY,
optional: true,
minItems: 2,
maxItems: 2,
items: {
type: NUMBER
}
},
vars: {
type: OBJECT
}
}
};
// type property containing type names
var TYPE_PROPERTY_SCHEMA = exports.TYPE_PROPERTY_SCHEMA = {
type: OBJECT,
additionalProperties: false,
properties: {
names: {
type: ARRAY,
minItems: 1,
items: {
type: STRING
}
}
}
};
// enumeration properties
var ENUM_PROPERTY_SCHEMA = exports.ENUM_PROPERTY_SCHEMA = {
type: OBJECT,
additionalProperties: false,
properties: {
comment: {
type: STRING
},
defaultvalue: {
type: STRING_OPTIONAL,
optional: true
},
description: {
type: STRING_OPTIONAL,
optional: true
},
kind: {
type: STRING,
// TODO: get this from a real enum somewhere
enum: ['member']
},
longname: {
type: STRING
},
memberof: {
type: STRING,
optional: true
},
meta: META_SCHEMA,
name: {
type: STRING
},
// is this member nullable? (derived from the type expression)
nullable: {
type: BOOLEAN_OPTIONAL
},
// is this member optional? (derived from the type expression)
optional: {
type: BOOLEAN_OPTIONAL
},
scope: {
type: STRING,
// TODO: get this from a real enum somewhere
enum: ['static']
},
type: TYPE_PROPERTY_SCHEMA,
// can this member be provided more than once? (derived from the type expression)
variable: {
type: BOOLEAN_OPTIONAL
}
}
};
// function parameter, or object property defined with @property tag
var PARAM_SCHEMA = exports.PARAM_SCHEMA = {
type: OBJECT,
additionalProperties: false,
properties: {
// what is the default value for this parameter?
defaultvalue: {
type: STRING_OPTIONAL,
optional: true
},
// a description of the parameter
description: {
type: STRING_OPTIONAL,
optional: true
},
// what name does this parameter have within the function?
name: {
type: STRING
},
// can the value for this parameter be null?
nullable: {
type: BOOLEAN_OPTIONAL,
optional: true
},
// is a value for this parameter optional?
optional: {
type: BOOLEAN_OPTIONAL,
optional: true
},
// what are the types of value expected for this parameter?
type: TYPE_PROPERTY_SCHEMA,
// can this parameter be repeated?
variable: {
type: BOOLEAN_OPTIONAL,
optional: true
}
}
};
var DOCLET_SCHEMA = exports.DOCLET_SCHEMA = {
type: OBJECT,
additionalProperties: false,
properties: {
// what access privileges are allowed
access: {
type: STRING,
optional: true,
// TODO: define this as an enumeration elsewhere
enum: [
'private',
'protected'
]
},
alias: {
type: STRING,
optional: true
},
augments: {
type: ARRAY,
optional: true,
uniqueItems: true,
items: {
type: STRING
}
},
author: {
type: ARRAY,
optional: true,
items: {
type: STRING
}
},
borrowed: {
type: ARRAY,
optional: true,
uniqueItems: true,
items: {
type: OBJECT,
additionalProperties: false,
properties: {
// name of the target
as: {
type: STRING,
optional: true
},
// name of the source
from: {
type: STRING
}
}
}
},
// a description of the class that this constructor belongs to
classdesc: {
type: STRING,
optional: true
},
comment: {
type: STRING
},
copyright: {
type: STRING,
optional: true
},
defaultvalue: {
optional: true
},
defaultvaluetype: {
type: STRING,
optional: true,
enum: [OBJECT, ARRAY]
},
// is usage of this symbol deprecated?
deprecated: {
type: [STRING, BOOLEAN],
optional: true
},
// a description
description: {
type: STRING_OPTIONAL,
optional: true
},
// something else to consider
examples: {
type: ARRAY,
optional: true,
items: {
type: STRING
}
},
exceptions: {
type: ARRAY,
optional: true,
items: PARAM_SCHEMA
},
// the path to another constructor
extends: {
type: ARRAY,
optional: true,
uniqueItems: true,
items: {
type: STRING
}
},
// the path to another doc object
fires: {
type: ARRAY,
optional: true,
uniqueItems: true,
items: {
type: STRING,
pattern: EVENT_REGEXP
}
},
forceMemberof: {
type: BOOLEAN_OPTIONAL,
optional: true
},
ignore: {
type: BOOLEAN,
optional: true
},
implements: {
type: ARRAY,
optional: true,
uniqueItems: true,
items: {
type: STRING
}
},
inherited: {
type: BOOLEAN,
optional: true
},
inherits: {
type: STRING,
optional: true,
dependency: {
inherited: true
}
},
isEnum: {
type: BOOLEAN,
optional: true
},
// what kind of symbol is this?
kind: {
type: STRING,
// TODO: define this as an enumeration elsewhere
enum: [
'class',
'constant',
'event',
'external',
'file',
'function',
'member',
'mixin',
'module',
'namespace',
'package',
'param',
'typedef'
]
},
license: {
type: STRING,
optional: true
},
listens: {
type: ARRAY,
optional: true,
uniqueItems: true,
items: {
type: STRING,
pattern: EVENT_REGEXP
}
},
longname: {
type: STRING
},
// probably a leading substring of the path
memberof: {
type: STRING,
optional: true
},
// information about this doc
meta: META_SCHEMA,
mixes: {
type: ARRAY,
optional: true,
uniqueItems: true,
items: {
type: STRING
}
},
// probably a trailing substring of the path
name: {
type: STRING
},
// is this member nullable? (derived from the type expression)
nullable: {
type: BOOLEAN_OPTIONAL
},
// is this member optional? (derived from the type expression)
optional: {
type: BOOLEAN_OPTIONAL
},
// are there function parameters associated with this doc?
params: {
type: ARRAY,
optional: true,
uniqueItems: true,
items: PARAM_SCHEMA
},
preserveName: {
type: BOOLEAN,
optional: true
},
properties: {
type: ARRAY,
optional: true,
uniqueItems: true,
minItems: 1,
items: {
anyOf: [ENUM_PROPERTY_SCHEMA, PARAM_SCHEMA]
}
},
readonly: {
type: BOOLEAN,
optional: true
},
// the symbol being documented requires another symbol
requires: {
type: ARRAY,
optional: true,
uniqueItems: true,
minItems: 1,
items: {
type: STRING
}
},
returns: {
type: ARRAY,
optional: true,
minItems: 1,
items: PARAM_SCHEMA
},
// what sort of parent scope does this symbol have?
scope: {
type: STRING,
enum: [
// TODO: make these an enumeration
'global',
'inner',
'instance',
'static'
]
},
// something else to consider
see: {
type: ARRAY,
optional: true,
minItems: 1,
items: {
type: STRING
}
},
// at what previous version was this doc added?
since: {
type: STRING,
optional: true
},
summary: {
type: STRING,
optional: true
},
// arbitrary tags associated with this doc
tags: {
type: ARRAY,
optional: true,
minItems: 1,
items: {
type: OBJECT,
additionalProperties: false,
properties: {
originalTitle: {
type: STRING
},
text: {
type: STRING,
optional: true
},
title: {
type: STRING
},
value: {
type: [STRING, OBJECT],
optional: true,
properties: PARAM_SCHEMA
}
}
}
},
'this': {
type: STRING,
optional: true
},
todo: {
type: ARRAY,
optional: true,
minItems: 1,
items: {
type: STRING
}
},
// extended tutorials
tutorials: {
type: ARRAY,
optional: true,
minItems: 1,
items: {
type: STRING
}
},
// what type is the value that this doc is associated with, like `number`
type: TYPE_PROPERTY_SCHEMA,
undocumented: {
type: BOOLEAN,
optional: true
},
// can this member be provided more than once? (derived from the type expression)
variable: {
type: BOOLEAN_OPTIONAL
},
variation: {
type: STRING,
optional: true
},
// what is the version of this doc
version: {
type: STRING,
optional: true
},
// is a member left to be implemented during inheritance?
virtual: {
type: BOOLEAN,
optional: true
}
}
};
var PACKAGE_SCHEMA = exports.PACKAGE_SCHEMA = {
type: OBJECT,
additionalProperties: false,
properties: {
description: {
type: STRING,
optional: true
},
files: {
type: ARRAY,
uniqueItems: true,
minItems: 1,
items: {
type: STRING
}
},
kind: {
type: STRING,
enum: ['package']
},
licenses: {
type: ARRAY,
optional: true,
minItems: 1,
items: {
type: OBJECT,
additionalProperties: false,
properties: {
type: {
type: STRING,
optional: true
},
url: {
type: STRING,
optional: true,
format: 'uri'
}
}
}
},
longname: {
type: STRING,
optional: true,
pattern: PACKAGE_REGEXP
},
name: {
type: STRING,
optional: true
},
version: {
type: STRING,
optional: true
}
}
};
var DOCLETS_SCHEMA = exports.DOCLETS_SCHEMA = {
type: ARRAY,
uniqueItems: true,
items: {
anyOf: [DOCLET_SCHEMA, PACKAGE_SCHEMA]
}
};

View File

@ -1,67 +0,0 @@
/*global env: true */
/**
@module jsdoc/src/filter
@author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
'use strict';
var path = require('jsdoc/path');
var pwd = env.pwd;
function makeRegExp(config) {
var regExp = null;
if (config) {
regExp = (typeof config === 'string') ? new RegExp(config) : config;
}
return regExp;
}
/**
@constructor
@param {object} opts
@param {string[]} opts.exclude - Specific files to exclude.
@param {string|RegExp} opts.includePattern
@param {string|RegExp} opts.excludePattern
*/
exports.Filter = function(opts) {
this.exclude = opts.exclude && Array.isArray(opts.exclude) ?
opts.exclude.map(function($) {
return path.resolve(pwd, $);
}) :
null;
this.includePattern = makeRegExp(opts.includePattern);
this.excludePattern = makeRegExp(opts.excludePattern);
};
/**
@param {string} filepath - The filepath to check.
@returns {boolean} Should the given file be included?
*/
exports.Filter.prototype.isIncluded = function(filepath) {
var included = true;
filepath = path.resolve(pwd, filepath);
if ( this.includePattern && !this.includePattern.test(filepath) ) {
included = false;
}
if ( this.excludePattern && this.excludePattern.test(filepath) ) {
included = false;
}
if (this.exclude) {
this.exclude.forEach(function(exclude) {
if ( filepath.indexOf(exclude) === 0 ) {
included = false;
}
});
}
return included;
};

View File

@ -1,231 +0,0 @@
/**
* @module jsdoc/src/handlers
*/
'use strict';
var jsdoc = {
doclet: require('jsdoc/doclet'),
name: require('jsdoc/name'),
util: {
logger: require('jsdoc/util/logger')
}
};
var util = require('util');
var currentModule = null;
var moduleRegExp = /^((?:module.)?exports|this)(\.|$)/;
function getNewDoclet(comment, e) {
var Doclet = jsdoc.doclet.Doclet;
var doclet;
var err;
try {
doclet = new Doclet(comment, e);
}
catch (error) {
err = new Error( util.format('cannot create a doclet for the comment "%s": %s',
comment.replace(/[\r\n]/g, ''), error.message) );
jsdoc.util.logger.error(err);
doclet = new Doclet('', e);
}
return doclet;
}
function setCurrentModule(doclet) {
if (doclet.kind === 'module') {
currentModule = doclet.longname;
}
}
function setDefaultScopeMemberOf(doclet) {
// add @inner and @memberof tags unless the current module exports only this symbol
if (currentModule && currentModule !== doclet.name) {
// add @inner unless the current module exports only this symbol
if (!doclet.scope) {
doclet.addTag('inner');
}
if (!doclet.memberof && doclet.scope !== 'global') {
doclet.addTag('memberof', currentModule);
}
}
}
/**
* Attach these event handlers to a particular instance of a parser.
* @param parser
*/
exports.attachTo = function(parser) {
function filter(doclet) {
// you can't document prototypes
if ( /#$/.test(doclet.longname) ) {
return true;
}
return false;
}
function addDoclet(newDoclet) {
var e;
if (newDoclet) {
setCurrentModule(newDoclet);
e = { doclet: newDoclet };
parser.emit('newDoclet', e);
if ( !e.defaultPrevented && !filter(e.doclet) ) {
parser.addResult(e.doclet);
}
}
}
// TODO: for clarity, decompose into smaller functions
function newSymbolDoclet(docletSrc, e) {
var memberofName = null,
newDoclet = getNewDoclet(docletSrc, e);
// A JSDoc comment can define a symbol name by including:
//
// + A `@name` tag
// + Another tag that accepts a name, such as `@function`
//
// When the JSDoc comment defines a symbol name, we treat it as a "virtual comment" for a
// symbol that isn't actually present in the code. And if a virtual comment is attached to
// a symbol, it's quite possible that the comment and symbol have nothing to do with one
// another.
//
// As a result, if we create a doclet for a `symbolFound` event, and we've already added a
// name attribute by parsing the JSDoc comment, we need to create a new doclet that ignores
// the attached JSDoc comment and only looks at the code.
if (newDoclet.name) {
// try again, without the comment
e.comment = '@undocumented';
newDoclet = getNewDoclet(e.comment, e);
}
if (newDoclet.alias) {
if (newDoclet.alias === '{@thisClass}') {
memberofName = parser.resolveThis(e.astnode);
// "class" refers to the owner of the prototype, not the prototype itself
if ( /^(.+?)(\.prototype|#)$/.test(memberofName) ) {
memberofName = RegExp.$1;
}
newDoclet.alias = memberofName;
}
newDoclet.addTag('name', newDoclet.alias);
newDoclet.postProcess();
}
else if (e.code && e.code.name) { // we need to get the symbol name from code
newDoclet.addTag('name', e.code.name);
if (!newDoclet.memberof && e.astnode) {
var basename = null,
scope = '';
if ( moduleRegExp.test(newDoclet.name) ) {
var nameStartsWith = RegExp.$1;
// remove stuff that indicates module membership (but don't touch the name
// `module.exports`, which identifies the module object itself)
if (newDoclet.name !== 'module.exports') {
newDoclet.name = newDoclet.name.replace(moduleRegExp, '');
}
// like /** @module foo */ exports.bar = 1;
// or /** @module foo */ module.exports.bar = 1;
// but not /** @module foo */ module.exports = 1;
if ( (nameStartsWith === 'exports' || nameStartsWith === 'module.exports') &&
newDoclet.name !== 'module.exports' && currentModule ) {
memberofName = currentModule;
scope = 'static';
}
else if (newDoclet.name === 'module.exports' && currentModule) {
newDoclet.addTag('name', currentModule);
newDoclet.postProcess();
}
else {
// like /** @module foo */ exports = {bar: 1};
// or /** blah */ this.foo = 1;
memberofName = parser.resolveThis(e.astnode);
scope = nameStartsWith === 'exports' ? 'static' : 'instance';
// like /** @module foo */ this.bar = 1;
if (nameStartsWith === 'this' && currentModule && !memberofName) {
memberofName = currentModule;
scope = 'static';
}
}
if (memberofName) {
if (newDoclet.name) {
newDoclet.name = memberofName + (scope === 'instance' ? '#' : '.') +
newDoclet.name;
}
else { newDoclet.name = memberofName; }
}
}
else {
memberofName = parser.astnodeToMemberof(e.astnode);
if( Array.isArray(memberofName) ) {
basename = memberofName[1];
memberofName = memberofName[0];
}
}
if (memberofName) {
newDoclet.addTag('memberof', memberofName);
if (basename) {
newDoclet.name = (newDoclet.name || '')
.replace(new RegExp('^' + RegExp.escape(basename) + '.'), '');
}
}
else {
setDefaultScopeMemberOf(newDoclet);
}
}
newDoclet.postProcess();
}
else {
return false;
}
// set the scope to global unless a) the doclet is a memberof something or b) the current
// module exports only this symbol
if (!newDoclet.memberof && currentModule !== newDoclet.name) {
newDoclet.scope = 'global';
}
addDoclet.call(parser, newDoclet);
e.doclet = newDoclet;
}
// handles JSDoc comments that include a @name tag -- the code is ignored in such a case
parser.on('jsdocCommentFound', function(e) {
var newDoclet = getNewDoclet(e.comment, e);
if (!newDoclet.name) {
return false; // only interested in virtual comments (with a @name) here
}
setDefaultScopeMemberOf(newDoclet);
newDoclet.postProcess();
addDoclet.call(parser, newDoclet);
e.doclet = newDoclet;
});
// handles named symbols in the code, may or may not have a JSDoc comment attached
parser.on('symbolFound', function(e) {
var subDoclets = e.comment.split(/@also\b/g);
for (var i = 0, l = subDoclets.length; i < l; i++) {
newSymbolDoclet.call(parser, subDoclets[i], e);
}
});
parser.on('fileComplete', function(e) {
currentModule = null;
});
};

View File

@ -1,500 +0,0 @@
/*global env, Packages */
/*eslint no-script-url:0 */
/**
* @module jsdoc/src/parser
*/
'use strict';
var jsdoc = {
doclet: require('jsdoc/doclet'),
name: require('jsdoc/name'),
src: {
astnode: require('jsdoc/src/astnode'),
syntax: require('jsdoc/src/syntax')
}
};
var logger = require('jsdoc/util/logger');
var util = require('util');
var hasOwnProp = Object.prototype.hasOwnProperty;
var Syntax = jsdoc.src.syntax.Syntax;
// Prefix for JavaScript strings that were provided in lieu of a filename.
var SCHEMA = 'javascript:';
// TODO: docs
var PARSERS = exports.PARSERS = {
esprima: 'jsdoc/src/parser',
rhino: 'rhino/jsdoc/src/parser'
};
// TODO: docs
// TODO: not currently used
function makeGlobalDoclet(globalScope) {
var doclet = new jsdoc.doclet.Doclet('/** Auto-generated doclet for global scope */', {});
if (globalScope) {
// TODO: handle global aliases
Object.keys(globalScope.ownedVariables).forEach(function(variable) {
doclet.meta.vars = doclet.meta.vars || {};
doclet.meta.vars[variable] = null;
});
}
return doclet;
}
// TODO: docs
exports.createParser = function(type) {
var path = require('jsdoc/path');
var runtime = require('jsdoc/util/runtime');
var modulePath;
if (!type) {
type = runtime.isRhino() ? 'rhino' : 'esprima';
}
if (PARSERS[type]) {
modulePath = PARSERS[type];
}
else {
modulePath = path.join( path.getResourcePath(path.dirname(type)), path.basename(type) );
}
try {
return new ( require(modulePath) ).Parser();
}
catch (e) {
logger.fatal('Unable to create the parser type "' + type + '": ' + e);
}
};
// TODO: docs
/**
* @class
* @mixes module:events.EventEmitter
*
* @example <caption>Create a new parser.</caption>
* var jsdocParser = new (require('jsdoc/src/parser').Parser)();
*/
var Parser = exports.Parser = function(builderInstance, visitorInstance, walkerInstance) {
this.clear();
this._astBuilder = builderInstance || new (require('jsdoc/src/astbuilder')).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);
// TODO: docs
Parser.prototype.clear = function() {
this._resultBuffer = [];
this.refs = {};
this.refs[jsdoc.src.astnode.GLOBAL_NODE_ID] = {};
this.refs[jsdoc.src.astnode.GLOBAL_NODE_ID].meta = {};
};
// TODO: update docs
/**
* Parse the given source files for JSDoc comments.
* @param {Array.<string>} sourceFiles An array of filepaths to the JavaScript sources.
* @param {string} [encoding=utf8]
*
* @fires module:jsdoc/src/parser.Parser.parseBegin
* @fires module:jsdoc/src/parser.Parser.fileBegin
* @fires module:jsdoc/src/parser.Parser.jsdocCommentFound
* @fires module:jsdoc/src/parser.Parser.symbolFound
* @fires module:jsdoc/src/parser.Parser.newDoclet
* @fires module:jsdoc/src/parser.Parser.fileComplete
* @fires module:jsdoc/src/parser.Parser.parseComplete
*
* @example <caption>Parse two source files.</caption>
* var myFiles = ['file1.js', 'file2.js'];
* var docs = jsdocParser.parse(myFiles);
*/
Parser.prototype.parse = function(sourceFiles, encoding) {
encoding = encoding || env.conf.encoding || 'utf8';
var filename = '';
var sourceCode = '';
var parsedFiles = [];
var e = {};
if (typeof sourceFiles === 'string') {
sourceFiles = [sourceFiles];
}
e.sourcefiles = sourceFiles;
logger.debug('Parsing source files: %j', sourceFiles);
this.emit('parseBegin', e);
for (var i = 0, l = sourceFiles.length; i < l; i++) {
sourceCode = '';
if (sourceFiles[i].indexOf(SCHEMA) === 0) {
sourceCode = sourceFiles[i].substr(SCHEMA.length);
filename = '[[string' + i + ']]';
}
else {
filename = sourceFiles[i];
try {
sourceCode = require('jsdoc/fs').readFileSync(filename, encoding);
}
catch(e) {
logger.error('Unable to read and parse the source file %s: %s', filename, e);
}
}
if (sourceCode.length) {
this._parseSourceCode(sourceCode, filename);
parsedFiles.push(filename);
}
}
this.emit('parseComplete', {
sourcefiles: parsedFiles,
doclets: this._resultBuffer
});
logger.debug('Finished parsing source files.');
return this._resultBuffer;
};
// TODO: docs
Parser.prototype.fireProcessingComplete = function(doclets) {
this.emit('processingComplete', { doclets: doclets });
};
// TODO: docs
Parser.prototype.results = function() {
return this._resultBuffer;
};
// TODO: update docs
/**
* @param {Object} o The parse result to add to the result buffer.
*/
Parser.prototype.addResult = function(o) {
this._resultBuffer.push(o);
};
// TODO: docs
Parser.prototype.addAstNodeVisitor = function(visitor) {
this._visitor.addAstNodeVisitor(visitor);
};
// TODO: docs
Parser.prototype.getAstNodeVisitors = function() {
return this._visitor.getAstNodeVisitors();
};
// TODO: docs
function pretreat(code) {
return code
// comment out hashbang at the top of the file, like: #!/usr/bin/env node
.replace(/^(\#\![\S \t]+\r?\n)/, '// $1')
// to support code minifiers that preserve /*! comments, treat /*!* as equivalent to /**
.replace(/\/\*\!\*/g, '/**')
// merge adjacent doclets
.replace(/\*\/\/\*\*+/g, '@also');
}
/** @private */
Parser.prototype._parseSourceCode = function(sourceCode, sourceName) {
var ast;
var globalScope;
var e = {
filename: sourceName
};
this.emit('fileBegin', e);
logger.printInfo('Parsing %s ...', sourceName);
if (!e.defaultPrevented) {
e = {
filename: sourceName,
source: sourceCode
};
this.emit('beforeParse', e);
sourceCode = e.source;
sourceName = e.filename;
sourceCode = pretreat(e.source);
ast = this._astBuilder.build(sourceCode, sourceName);
if (ast) {
this._walker.recurse(sourceName, ast, this._visitor);
}
}
this.emit('fileComplete', e);
logger.info('complete.');
};
// TODO: docs
Parser.prototype.addDocletRef = function(e) {
var node;
if (e && e.code && e.code.node) {
node = e.code.node;
// allow lookup from value => doclet
if (e.doclet) {
this.refs[node.nodeId] = e.doclet;
}
// keep references to undocumented anonymous functions, too, as they might have scoped vars
else if (
(node.type === Syntax.FunctionDeclaration || node.type === Syntax.FunctionExpression) &&
!this.refs[node.nodeId] ) {
this.refs[node.nodeId] = {
longname: jsdoc.name.ANONYMOUS_LONGNAME,
meta: {
code: e.code
}
};
}
}
};
// TODO: docs
Parser.prototype._getDoclet = function(id) {
if ( hasOwnProp.call(this.refs, id) ) {
return this.refs[id];
}
return null;
};
// TODO: docs
/**
* @param {string} name - The symbol's longname.
* @return {string} The symbol's basename.
*/
Parser.prototype.getBasename = function(name) {
if (name !== undefined) {
return name.replace(/^([$a-z_][$a-z_0-9]*).*?$/i, '$1');
}
};
// TODO: docs
function definedInScope(doclet, basename) {
return !!doclet && !!doclet.meta && !!doclet.meta.vars && !!basename &&
hasOwnProp.call(doclet.meta.vars, basename);
}
// TODO: docs
/**
* Given a node, determine what the node is a member of.
* @param {node} node
* @returns {string} The long name of the node that this is a member of.
*/
Parser.prototype.astnodeToMemberof = function(node) {
var basename;
var doclet;
var scope;
var result = '';
var type = node.type;
if ( (type === Syntax.FunctionDeclaration || type === Syntax.FunctionExpression ||
type === Syntax.VariableDeclarator) && node.enclosingScope ) {
doclet = this._getDoclet(node.enclosingScope.nodeId);
if (!doclet) {
result = jsdoc.name.ANONYMOUS_LONGNAME + jsdoc.name.INNER;
}
else {
result = (doclet.longname || doclet.name) + jsdoc.name.INNER;
}
}
else {
// check local references for aliases
scope = node;
basename = this.getBasename( jsdoc.src.astnode.nodeToString(node) );
// walk up the scope chain until we find the scope in which the node is defined
while (scope.enclosingScope) {
doclet = this._getDoclet(scope.enclosingScope.nodeId);
if ( doclet && definedInScope(doclet, basename) ) {
result = [doclet.meta.vars[basename], basename];
break;
}
else {
// move up
scope = scope.enclosingScope;
}
}
// do we know that it's a global?
doclet = this.refs[jsdoc.src.astnode.GLOBAL_NODE_ID];
if ( doclet && definedInScope(doclet, basename) ) {
result = [doclet.meta.vars[basename], basename];
}
// have we seen the node's parent? if so, use that
else if (node.parent) {
doclet = this._getDoclet(node.parent.nodeId);
// set the result if we found a doclet. (if we didn't, the AST node may describe a
// global symbol.)
if (doclet) {
result = doclet.longname || doclet.name;
}
}
}
return result;
};
// TODO: docs
/**
* Resolve what "this" refers to relative to a node.
* @param {node} node - The "this" node
* @returns {string} The longname of the enclosing node.
*/
Parser.prototype.resolveThis = function(node) {
var doclet;
var result;
// In general, if there's an enclosing scope, we use the enclosing scope to resolve `this`.
// For object properties, we use the node's parent (the object) instead. This is a consequence
// of the source-rewriting hackery that we use to support the `@lends` tag.
if (node.type !== Syntax.Property && node.enclosingScope) {
doclet = this._getDoclet(node.enclosingScope.nodeId);
if (!doclet) {
result = jsdoc.name.ANONYMOUS_LONGNAME; // TODO handle global this?
}
else if (doclet['this']) {
result = doclet['this'];
}
// like: Foo.constructor = function(n) { /** blah */ this.name = n; }
else if (doclet.kind === 'function' && doclet.memberof) {
result = doclet.memberof;
}
// like: var foo = function(n) { /** blah */ this.bar = n; }
else if ( doclet.kind === 'member' && jsdoc.src.astnode.isAssignment(node) ) {
result = doclet.longname || doclet.name;
}
// walk up to the closest class we can find
else if (doclet.kind === 'class' || doclet.kind === 'module') {
result = doclet.longname || doclet.name;
}
else if (node.enclosingScope) {
result = this.resolveThis(node.enclosingScope);
}
}
else if (node.parent) {
doclet = this.refs[node.parent.nodeId];
// TODO: is this behavior correct? when do we get here?
if (!doclet) {
result = ''; // global?
}
else {
result = doclet.longname || doclet.name;
}
}
// TODO: is this behavior correct? when do we get here?
else {
result = ''; // global?
}
return result;
};
// TODO: docs
/**
* Given 'var foo = { x: 1 }', find foo from x.
*/
Parser.prototype.resolvePropertyParent = function(node) {
var doclet;
if (node.parent) {
doclet = this._getDoclet(node.parent.nodeId);
}
return doclet;
};
// TODO docs
/**
* Resolve what function a var is limited to.
* @param {astnode} node
* @param {string} basename The leftmost name in the long name: in foo.bar.zip the basename is foo.
*/
Parser.prototype.resolveVar = function(node, basename) {
var doclet;
var result;
var scope = node.enclosingScope;
if (!scope) {
result = ''; // global
}
else {
doclet = this._getDoclet(scope.nodeId);
if ( definedInScope(doclet, basename) ) {
result = doclet.longname;
}
else {
result = this.resolveVar(scope, basename);
}
}
return result;
};
// TODO: docs
Parser.prototype.resolveEnum = function(e) {
var doclet = this.resolvePropertyParent(e.code.node.parent);
if (doclet && doclet.isEnum) {
if (!doclet.properties) {
doclet.properties = [];
}
// members of an enum inherit the enum's type
if (doclet.type && !e.doclet.type) {
e.doclet.type = doclet.type;
}
delete e.doclet.undocumented;
e.doclet.defaultvalue = e.doclet.meta.code.value;
// add a copy of the doclet to the parent's properties
doclet.properties.push( require('jsdoc/util/doop').doop(e.doclet) );
}
};
// TODO: document other events
/**
* Fired once for each JSDoc comment in the current source code.
* @event jsdocCommentFound
* @memberof module:jsdoc/src/parser.Parser
* @param {event} e
* @param {string} e.comment The text content of the JSDoc comment
* @param {number} e.lineno The line number associated with the found comment.
* @param {string} e.filename The file name associated with the found comment.
*/

View File

@ -1,70 +0,0 @@
/*global env: true */
/**
@module jsdoc/src/scanner
@requires module:fs
@author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
'use strict';
var fs = require('jsdoc/fs');
var logger = require('jsdoc/util/logger');
var path = require('jsdoc/path');
/**
@constructor
@mixes module:events
*/
exports.Scanner = function() {};
exports.Scanner.prototype = Object.create( require('events').EventEmitter.prototype );
/**
Recursively searches the given searchPaths for js files.
@param {Array.<string>} searchPaths
@param {number} [depth=1]
@fires sourceFileFound
*/
exports.Scanner.prototype.scan = function(searchPaths, depth, filter) {
var currentFile;
var isFile;
var filePaths = [];
var pwd = env.pwd;
var self = this;
searchPaths = searchPaths || [];
depth = depth || 1;
searchPaths.forEach(function($) {
var filepath = path.resolve( pwd, decodeURIComponent($) );
try {
currentFile = fs.statSync(filepath);
}
catch (e) {
logger.error('Unable to find the source file or directory %s', filepath);
return;
}
if ( currentFile.isFile() ) {
filePaths.push(filepath);
}
else {
filePaths = filePaths.concat( fs.ls(filepath, depth) );
}
});
filePaths = filePaths.filter(function($) {
return filter.isIncluded($);
});
filePaths = filePaths.filter(function($) {
var e = { fileName: $ };
self.emit('sourceFileFound', e);
return !e.defaultPrevented;
});
return filePaths;
};

View File

@ -1,140 +0,0 @@
/*global env: true */
/**
@overview
@author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
/**
Functionality related to JSDoc tags.
@module jsdoc/tag
@requires jsdoc/tag/dictionary
@requires jsdoc/tag/validator
@requires jsdoc/tag/type
*/
'use strict';
var jsdoc = {
tag: {
dictionary: require('jsdoc/tag/dictionary'),
validator: require('jsdoc/tag/validator'),
type: require('jsdoc/tag/type')
},
util: {
logger: require('jsdoc/util/logger')
}
};
var path = require('jsdoc/path');
function trim(text, opts) {
var indentMatcher;
var match;
opts = opts || {};
text = text || '';
if (opts.keepsWhitespace) {
text = text.replace(/^[\n\r\f]+|[\n\r\f]+$/g, '');
if (opts.removesIndent) {
match = text.match(/^([ \t]+)/);
if (match && match[1]) {
indentMatcher = new RegExp('^' + match[1], 'gm');
text = text.replace(indentMatcher, '');
}
}
}
else {
text = text.replace(/^\s+|\s+$/g, '');
}
return text;
}
function processTagText(tag, tagDef) {
var tagType;
if (tagDef.onTagText) {
tag.text = tagDef.onTagText(tag.text);
}
if (tagDef.canHaveType || tagDef.canHaveName) {
/** The value property represents the result of parsing the tag text. */
tag.value = {};
tagType = jsdoc.tag.type.parse(tag.text, tagDef.canHaveName, tagDef.canHaveType);
// It is possible for a tag to *not* have a type but still have
// optional or defaultvalue, e.g. '@param [foo]'.
// Although tagType.type.length == 0 we should still copy the other properties.
if (tagType.type) {
if (tagType.type.length) {
tag.value.type = {
names: tagType.type
};
}
tag.value.optional = tagType.optional;
tag.value.nullable = tagType.nullable;
tag.value.variable = tagType.variable;
tag.value.defaultvalue = tagType.defaultvalue;
}
if (tagType.text && tagType.text.length) {
tag.value.description = tagType.text;
}
if (tagDef.canHaveName) {
// note the dash is a special case: as a param name it means "no name"
if (tagType.name && tagType.name !== '-') { tag.value.name = tagType.name; }
}
}
else {
tag.value = tag.text;
}
}
/**
Constructs a new tag object. Calls the tag validator.
@class
@classdesc Represents a single doclet tag.
@param {string} tagTitle
@param {string=} tagBody
@param {object=} meta
*/
var Tag = exports.Tag = function(tagTitle, tagBody, meta) {
var tagDef;
var trimOpts;
meta = meta || {};
this.originalTitle = trim(tagTitle);
/** The title part of the tag: @title text */
this.title = jsdoc.tag.dictionary.normalise(this.originalTitle);
tagDef = jsdoc.tag.dictionary.lookUp(this.title);
trimOpts = {
keepsWhitespace: tagDef.keepsWhitespace,
removesIndent: tagDef.removesIndent
};
/** The text part of the tag: @title text */
this.text = trim(tagBody, trimOpts);
if (this.text) {
try {
processTagText(this, tagDef);
}
catch (e) {
// probably a type-parsing error
jsdoc.util.logger.error(
'Unable to create a Tag object%s with title "%s" and body "%s": %s',
meta.filename ? ( ' for source file ' + path.join(meta.path, meta.filename) ) : '',
tagTitle,
tagBody,
e.message
);
}
}
jsdoc.tag.validator.validate(this, tagDef, meta);
};

View File

@ -1,85 +0,0 @@
/**
@overview
@author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
'use strict';
var hasOwnProp = Object.prototype.hasOwnProperty;
var _tags = {};
var _tagSynonyms = {};
var _namespaces = [];
var dictionary;
/** @private */
function TagDefinition(title, etc) {
var self = this;
etc = etc || {};
this.title = dictionary.normalise(title);
Object.keys(etc).forEach(function(p) {
self[p] = etc[p];
});
}
/** @private */
TagDefinition.prototype.synonym = function(synonymName) {
_tagSynonyms[synonymName.toLowerCase()] = this.title;
return this; // chainable
};
/** @exports jsdoc/tag/dictionary */
dictionary = {
/** @function */
defineTag: function(title, opts) {
var def = new TagDefinition(title, opts);
// all the other dictionary functions use normalised names; we should too.
_tags[def.title] = def;
if (opts && opts.isNamespace) {
_namespaces.push(def.title);
}
return _tags[def.title];
},
/** @function */
lookUp: function(title) {
title = dictionary.normalise(title);
if ( hasOwnProp.call(_tags, title) ) {
return _tags[title];
}
return false;
},
/** @function */
isNamespace: function(kind) {
if (kind) {
kind = dictionary.normalise(kind);
if ( _namespaces.indexOf(kind) !== -1) {
return true;
}
}
return false;
},
/** @function */
normalise: function(title) {
var canonicalName = title.toLowerCase();
if ( hasOwnProp.call(_tagSynonyms, canonicalName) ) {
return _tagSynonyms[canonicalName];
}
return canonicalName;
}
};
require('jsdoc/tag/dictionary/definitions').defineTags(dictionary);
module.exports = dictionary;

View File

@ -1,797 +0,0 @@
/*global app, env */
/**
Define tags that are known in JSDoc.
@module jsdoc/tag/dictionary/definitions
@author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
'use strict';
var hasOwnProp = Object.prototype.hasOwnProperty;
var jsdoc = {
name: require('jsdoc/name'),
src: {
astnode: require('jsdoc/src/astnode')
},
tag: {
type: require('jsdoc/tag/type')
},
util: {
logger: require('jsdoc/util/logger')
}
};
var path = require('jsdoc/path');
var Syntax = require('jsdoc/src/syntax').Syntax;
var GLOBAL_LONGNAME = jsdoc.name.GLOBAL_LONGNAME;
var MODULE_PREFIX = jsdoc.name.MODULE_PREFIX;
function getSourcePaths() {
var sourcePaths = env.sourceFiles.slice(0) || [];
if (env.opts._) {
env.opts._.forEach(function(sourcePath) {
var resolved = path.resolve(env.pwd, sourcePath);
if (sourcePaths.indexOf(resolved) === -1) {
sourcePaths.push(resolved);
}
});
}
return sourcePaths;
}
function filepathMinusPrefix(filepath) {
var sourcePaths = getSourcePaths();
var commonPrefix = path.commonPrefix(sourcePaths);
var result = '';
if (filepath) {
// always use forward slashes
result = (filepath + path.sep).replace(commonPrefix, '')
.replace(/\\/g, '/');
}
if (result.length > 0 && result[result.length - 1] !== '/') {
result += '/';
}
return result;
}
/** @private */
function setDocletKindToTitle(doclet, tag) {
doclet.addTag( 'kind', tag.title );
}
function setDocletScopeToTitle(doclet, tag) {
try {
doclet.setScope(tag.title);
}
catch(e) {
jsdoc.util.logger.error(e.message);
}
}
function setDocletNameToValue(doclet, tag) {
if (tag.value && tag.value.description) { // as in a long tag
doclet.addTag( 'name', tag.value.description);
}
else if (tag.text) { // or a short tag
doclet.addTag('name', tag.text);
}
}
function setDocletNameToValueName(doclet, tag) {
if (tag.value && tag.value.name) {
doclet.addTag('name', tag.value.name);
}
}
function setDocletDescriptionToValue(doclet, tag) {
if (tag.value) {
doclet.addTag( 'description', tag.value );
}
}
function setDocletTypeToValueType(doclet, tag) {
if (tag.value && tag.value.type) {
// Add the type names and other type properties (such as `optional`).
// Don't overwrite existing properties.
Object.keys(tag.value).forEach(function(prop) {
if ( !hasOwnProp.call(doclet, prop) ) {
doclet[prop] = tag.value[prop];
}
});
}
}
function setNameToFile(doclet, tag) {
var name;
if (doclet.meta.filename) {
name = filepathMinusPrefix(doclet.meta.path) + doclet.meta.filename;
doclet.addTag('name', name);
}
}
function setDocletMemberof(doclet, tag) {
if (tag.value && tag.value !== '<global>') {
doclet.setMemberof(tag.value);
}
}
function applyNamespace(docletOrNs, tag) {
if (typeof docletOrNs === 'string') { // ns
tag.value = app.jsdoc.name.applyNamespace(tag.value, docletOrNs);
}
else { // doclet
if (!docletOrNs.name) {
return; // error?
}
//doclet.displayname = doclet.name;
docletOrNs.longname = app.jsdoc.name.applyNamespace(docletOrNs.name, tag.title);
}
}
function setDocletNameToFilename(doclet, tag) {
var name = '';
if (doclet.meta.path) {
name = filepathMinusPrefix(doclet.meta.path);
}
name += doclet.meta.filename.replace(/\.js$/i, '');
doclet.name = name;
}
function parseBorrows(doclet, tag) {
var m = /^(\S+)(?:\s+as\s+(\S+))?$/.exec(tag.text);
if (m) {
if (m[1] && m[2]) {
return { target: m[1], source: m[2] };
}
else if (m[1]) {
return { target: m[1] };
}
} else {
return {};
}
}
function firstWordOf(string) {
var m = /^(\S+)/.exec(string);
if (m) { return m[1]; }
else { return ''; }
}
/** Populate the given dictionary with all known JSDoc tag definitions.
@param {module:jsdoc/tag/dictionary} dictionary
*/
exports.defineTags = function(dictionary) {
dictionary.defineTag('abstract', {
mustNotHaveValue: true,
onTagged: function(doclet, tag) {
// since "abstract" is reserved word in JavaScript let's use "virtual" in code
doclet.virtual = true;
}
})
.synonym('virtual');
dictionary.defineTag('access', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
// only valid values are private and protected, public is default
if ( /^(private|protected)$/i.test(tag.value) ) {
doclet.access = tag.value.toLowerCase();
}
else {
delete doclet.access;
}
}
});
dictionary.defineTag('alias', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
doclet.alias = tag.value;
}
});
// Special separator tag indicating that multiple doclets should be generated for the same
// comment. Used internally (and by some JSDoc users, although it's not officially supported).
//
// In the following example, the parser will replace `//**` with an `@also` tag:
//
// /**
// * Foo.
// *//**
// * Foo with a param.
// * @param {string} bar
// */
// function foo(bar) {}
dictionary.defineTag('also', {
onTagged: function(doclet, tag) {
// let the parser handle it; we define the tag here to avoid "not a known tag" errors
}
});
// this symbol inherits from the specified symbol
dictionary.defineTag('augments', {
mustHaveValue: true,
// Allow augments value to be specified as a normal type, e.g. {Type}
onTagText: function(text) {
var tagType = jsdoc.tag.type.parse(text, false, true);
return tagType.typeExpression || text;
},
onTagged: function(doclet, tag) {
doclet.augment( firstWordOf(tag.value) );
}
})
.synonym('extends');
dictionary.defineTag('author', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
if (!doclet.author) { doclet.author = []; }
doclet.author.push(tag.value);
}
});
// this symbol has a member that should use the same docs as another symbol
dictionary.defineTag('borrows', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
var borrows = parseBorrows(doclet, tag);
doclet.borrow(borrows.target, borrows.source);
}
});
dictionary.defineTag('class', {
onTagged: function(doclet, tag) {
doclet.addTag('kind', 'class');
// handle special case where both @class and @constructor tags exist in same doclet
if (tag.originalTitle === 'class') {
var looksLikeDesc = (tag.value || '').match(/\S+\s+\S+/); // multiple words after @class?
if ( looksLikeDesc || /@construct(s|or)\b/i.test(doclet.comment) ) {
doclet.classdesc = tag.value; // treat the @class tag as a @classdesc tag instead
return;
}
}
setDocletNameToValue(doclet, tag);
}
})
.synonym('constructor');
dictionary.defineTag('classdesc', {
onTagged: function(doclet, tag) {
doclet.classdesc = tag.value;
}
});
dictionary.defineTag('constant', {
canHaveType: true,
canHaveName: true,
onTagged: function(doclet, tag) {
setDocletKindToTitle(doclet, tag);
setDocletNameToValueName(doclet, tag);
setDocletTypeToValueType(doclet, tag);
}
})
.synonym('const');
dictionary.defineTag('constructs', {
onTagged: function(doclet, tag) {
var ownerClassName;
if (!tag.value) {
ownerClassName = '{@thisClass}'; // this can be resolved later in the handlers
}
else {
ownerClassName = firstWordOf(tag.value);
}
doclet.addTag('alias', ownerClassName);
doclet.addTag('kind', 'class');
}
});
dictionary.defineTag('copyright', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
doclet.copyright = tag.value;
}
});
dictionary.defineTag('default', {
onTagged: function(doclet, tag) {
var type;
var value;
var nodeToString = jsdoc.src.astnode.nodeToString;
if (tag.value) {
doclet.defaultvalue = tag.value;
}
else if (doclet.meta && doclet.meta.code && doclet.meta.code.value) {
type = doclet.meta.code.type;
value = doclet.meta.code.value;
switch(type) {
case Syntax.ArrayExpression:
doclet.defaultvalue = nodeToString(doclet.meta.code.node);
doclet.defaultvaluetype = 'array';
break;
case Syntax.Literal:
doclet.defaultvalue = String(value);
break;
case Syntax.ObjectExpression:
doclet.defaultvalue = nodeToString(doclet.meta.code.node);
doclet.defaultvaluetype = 'object';
break;
default:
// do nothing
break;
}
}
}
})
.synonym('defaultvalue');
dictionary.defineTag('deprecated', {
// value is optional
onTagged: function(doclet, tag) {
doclet.deprecated = tag.value || true;
}
});
dictionary.defineTag('description', {
mustHaveValue: true
})
.synonym('desc');
dictionary.defineTag('enum', {
canHaveType: true,
onTagged: function(doclet, tag) {
doclet.kind = 'member';
doclet.isEnum = true;
setDocletTypeToValueType(doclet, tag);
}
});
dictionary.defineTag('event', {
isNamespace: true,
onTagged: function(doclet, tag) {
setDocletKindToTitle(doclet, tag);
setDocletNameToValue(doclet, tag);
}
});
dictionary.defineTag('example', {
keepsWhitespace: true,
removesIndent: true,
mustHaveValue: true,
onTagged: function(doclet, tag) {
if (!doclet.examples) { doclet.examples = []; }
doclet.examples.push(tag.value);
}
});
dictionary.defineTag('exports', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
var modName = firstWordOf(tag.value);
doclet.addTag('alias', modName);
doclet.addTag('kind', 'module');
}
});
dictionary.defineTag('external', {
canHaveType: true,
isNamespace: true,
onTagged: function(doclet, tag) {
setDocletKindToTitle(doclet, tag);
if (tag.value && tag.value.type) {
setDocletTypeToValueType(doclet, tag);
doclet.addTag('name', doclet.type.names[0]);
}
else {
setDocletNameToValue(doclet, tag);
}
}
})
.synonym('host');
dictionary.defineTag('file', {
onTagged: function(doclet, tag) {
setNameToFile(doclet, tag);
setDocletKindToTitle(doclet, tag);
setDocletDescriptionToValue(doclet, tag);
doclet.preserveName = true;
}
})
.synonym('fileoverview')
.synonym('overview');
dictionary.defineTag('fires', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
if (!doclet.fires) { doclet.fires = []; }
applyNamespace('event', tag);
doclet.fires.push(tag.value);
}
})
.synonym('emits');
dictionary.defineTag('function', {
onTagged: function(doclet, tag) {
setDocletKindToTitle(doclet, tag);
setDocletNameToValue(doclet, tag);
}
})
.synonym('func')
.synonym('method');
dictionary.defineTag('global', {
mustNotHaveValue: true,
onTagged: function(doclet, tag) {
doclet.scope = 'global';
delete doclet.memberof;
}
});
dictionary.defineTag('ignore', {
mustNotHaveValue: true,
onTagged: function(doclet, tag) {
doclet.ignore = true;
}
});
dictionary.defineTag('inner', {
onTagged: function(doclet, tag) {
setDocletScopeToTitle(doclet, tag);
}
});
dictionary.defineTag('instance', {
onTagged: function(doclet, tag) {
setDocletScopeToTitle(doclet, tag);
}
});
dictionary.defineTag('kind', {
mustHaveValue: true
});
dictionary.defineTag('lends', {
onTagged: function(doclet, tag) {
doclet.alias = tag.value || GLOBAL_LONGNAME;
doclet.addTag('undocumented');
}
});
dictionary.defineTag('license', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
doclet.license = tag.value;
}
});
dictionary.defineTag('listens', {
mustHaveValue: true,
onTagged: function (doclet, tag) {
if (!doclet.listens) { doclet.listens = []; }
applyNamespace('event', tag);
doclet.listens.push(tag.value);
// TODO: verify that parameters match the event parameters?
}
});
dictionary.defineTag('member', {
canHaveType: true,
canHaveName: true,
onTagged: function(doclet, tag) {
setDocletKindToTitle(doclet, tag);
setDocletNameToValueName(doclet, tag);
setDocletTypeToValueType(doclet, tag);
}
})
.synonym('var');
dictionary.defineTag('memberof', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
if (tag.originalTitle === 'memberof!') {
doclet.forceMemberof = true;
if (tag.value === GLOBAL_LONGNAME) {
doclet.addTag('global');
delete doclet.memberof;
}
}
setDocletMemberof(doclet, tag);
}
})
.synonym('memberof!');
// this symbol mixes in all of the specified object's members
dictionary.defineTag('mixes', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
var source = firstWordOf(tag.value);
doclet.mix(source);
}
});
dictionary.defineTag('mixin', {
onTagged: function(doclet, tag) {
setDocletKindToTitle(doclet, tag);
setDocletNameToValue(doclet, tag);
}
});
dictionary.defineTag('module', {
canHaveType: true,
isNamespace: true,
onTagged: function(doclet, tag) {
setDocletKindToTitle(doclet, tag);
setDocletNameToValue(doclet, tag);
if (!doclet.name) {
setDocletNameToFilename(doclet, tag);
}
setDocletTypeToValueType(doclet, tag);
}
});
dictionary.defineTag('name', {
mustHaveValue: true
});
dictionary.defineTag('namespace', {
canHaveType: true,
onTagged: function(doclet, tag) {
setDocletKindToTitle(doclet, tag);
setDocletNameToValue(doclet, tag);
setDocletTypeToValueType(doclet, tag);
}
});
dictionary.defineTag('param', {
//mustHaveValue: true, // param name can be found in the source code if not provided
canHaveType: true,
canHaveName: true,
onTagged: function(doclet, tag) {
if (!doclet.params) { doclet.params = []; }
doclet.params.push(tag.value || {});
}
})
.synonym('argument')
.synonym('arg');
dictionary.defineTag('private', {
mustNotHaveValue: true,
onTagged: function(doclet, tag) {
doclet.access = 'private';
}
});
dictionary.defineTag('property', {
mustHaveValue: true,
canHaveType: true,
canHaveName: true,
onTagged: function(doclet, tag) {
if (!doclet.properties) { doclet.properties = []; }
doclet.properties.push(tag.value);
}
})
.synonym('prop');
dictionary.defineTag('protected', {
mustNotHaveValue: true,
onTagged: function(doclet, tag) {
doclet.access = 'protected';
}
});
dictionary.defineTag('public', {
mustNotHaveValue: true,
onTagged: function(doclet, tag) {
delete doclet.access; // public is default
}
});
// use this instead of old deprecated @final tag
dictionary.defineTag('readonly', {
mustNotHaveValue: true,
onTagged: function(doclet, tag) {
doclet.readonly = true;
}
});
dictionary.defineTag('requires', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
var requiresName;
// inline link tags are passed through as-is so that `@requires {@link foo}` works
if ( require('jsdoc/tag/inline').isInlineTag(tag.value, 'link\\S*') ) {
requiresName = tag.value;
}
// otherwise, assume it's a module
else {
requiresName = firstWordOf(tag.value);
if (requiresName.indexOf(MODULE_PREFIX) !== 0) {
requiresName = MODULE_PREFIX + requiresName;
}
}
if (!doclet.requires) { doclet.requires = []; }
doclet.requires.push(requiresName);
}
});
dictionary.defineTag('returns', {
mustHaveValue: true,
canHaveType: true,
onTagged: function(doclet, tag) {
if (!doclet.returns) { doclet.returns = []; }
doclet.returns.push(tag.value);
}
})
.synonym('return');
dictionary.defineTag('see', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
if (!doclet.see) { doclet.see = []; }
doclet.see.push(tag.value);
}
});
dictionary.defineTag('since', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
doclet.since = tag.value;
}
});
dictionary.defineTag('static', {
onTagged: function(doclet, tag) {
setDocletScopeToTitle(doclet, tag);
}
});
dictionary.defineTag('summary', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
doclet.summary = tag.value;
}
});
dictionary.defineTag('this', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
doclet['this'] = firstWordOf(tag.value);
}
});
dictionary.defineTag('todo', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
if (!doclet.todo) { doclet.todo = []; }
doclet.todo.push(tag.value);
}
});
dictionary.defineTag('throws', {
mustHaveValue: true,
canHaveType: true,
onTagged: function(doclet, tag) {
if (!doclet.exceptions) { doclet.exceptions = []; }
doclet.exceptions.push(tag.value);
setDocletTypeToValueType(doclet, tag);
}
})
.synonym('exception');
dictionary.defineTag('tutorial', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
if (!doclet.tutorials) { doclet.tutorials = []; }
doclet.tutorials.push(tag.value);
}
});
dictionary.defineTag('type', {
mustHaveValue: true,
mustNotHaveDescription: true,
canHaveType: true,
onTagText: function(text) {
var closeIdx;
var openIdx;
var OPEN_BRACE = '{';
var CLOSE_BRACE = '}';
// remove line breaks
text = text.replace(/[\f\n\r]/g, '');
// Text must be a type expression; for backwards compatibility, we add braces if they're
// missing. But do NOT add braces to things like `@type {string} some pointless text`.
openIdx = text.indexOf(OPEN_BRACE);
closeIdx = text.indexOf(CLOSE_BRACE);
// a type expression is at least one character long
if ( openIdx !== 0 || closeIdx <= openIdx + 1) {
text = OPEN_BRACE + text + CLOSE_BRACE;
}
return text;
},
onTagged: function(doclet, tag) {
if (tag.value && tag.value.type) {
setDocletTypeToValueType(doclet, tag);
// for backwards compatibility, we allow @type for functions to imply return type
if (doclet.kind === 'function') {
doclet.addTag('returns', tag.text);
}
}
}
});
dictionary.defineTag('typedef', {
canHaveType: true,
canHaveName: true,
onTagged: function(doclet, tag) {
setDocletKindToTitle(doclet, tag);
if (tag.value) {
setDocletNameToValueName(doclet, tag);
// callbacks are always type {function}
if (tag.originalTitle === 'callback') {
doclet.type = {
names: [
'function'
]
};
}
else {
setDocletTypeToValueType(doclet, tag);
}
}
}
})
.synonym('callback');
dictionary.defineTag('undocumented', {
mustNotHaveValue: true,
onTagged: function(doclet, tag) {
doclet.undocumented = true;
doclet.comment = '';
}
});
dictionary.defineTag('variation', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
doclet.variation = tag.value;
}
});
dictionary.defineTag('version', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
doclet.version = tag.value;
}
});
};

View File

@ -1,141 +0,0 @@
/**
* @module jsdoc/tag/inline
*
* @author Jeff Williams <jeffrey.l.williams@gmail.com>
* @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
'use strict';
/**
* Information about an inline tag that was found within a string.
*
* @typedef {Object} InlineTagInfo
* @memberof module:jsdoc/tag/inline
* @property {?string} completeTag - The entire inline tag, including its enclosing braces.
* @property {?string} tag - The tag whose text was found.
* @property {?string} text - The tag text that was found.
*/
/**
* Information about the results of replacing inline tags within a string.
*
* @typedef {Object} InlineTagResult
* @memberof module:jsdoc/tag/inline
* @property {Array.<module:jsdoc/tag/inline.InlineTagInfo>} tags - The inline tags that were found.
* @property {string} newString - The updated text string after extracting or replacing the inline
* tags.
*/
/**
* Text-replacing function for strings that contain an inline tag.
*
* @callback InlineTagReplacer
* @memberof module:jsdoc/tag/inline
* @param {string} string - The complete string containing the inline tag.
* @param {module:jsdoc/tag/inline.InlineTagInfo} tagInfo - Information about the inline tag.
* @return {string} An updated version of the complete string.
*/
/**
* Create a regexp that matches a specific inline tag, or all inline tags.
*
* @private
* @memberof module:jsdoc/tag/inline
* @param {?string} tagName - The inline tag that the regexp will match. May contain regexp
* characters. If omitted, matches any string.
* @param {?string} prefix - A prefix for the regexp. Defaults to an empty string.
* @param {?string} suffix - A suffix for the regexp. Defaults to an empty string.
* @returns {RegExp} A regular expression that matches the requested inline tag.
*/
function regExpFactory(tagName, prefix, suffix) {
tagName = tagName || '\\S+';
prefix = prefix || '';
suffix = suffix || '';
return new RegExp(prefix + '\\{@' + tagName + '\\s+((?:.|\n)+?)\\}' + suffix, 'gi');
}
/**
* Check whether a string is an inline tag. You can check for a specific inline tag or for any valid
* inline tag.
*
* @param {string} string - The string to check.
* @param {?string} tagName - The inline tag to match. May contain regexp characters. If this
* parameter is omitted, this method returns `true` for any valid inline tag.
* @returns {boolean} Set to `true` if the string is a valid inline tag or `false` in all other
* cases.
*/
exports.isInlineTag = function(string, tagName) {
return regExpFactory(tagName, '^', '$').test(string);
};
/**
* Replace all instances of multiple inline tags with other text.
*
* @param {string} string - The string in which to replace the inline tags.
* @param {Object} replacers - The functions that are used to replace text in the string. The keys
* must contain tag names (for example, `link`), and the values must contain functions with the
* type {@link module:jsdoc/tag/inline.InlineTagReplacer}.
* @return {module:jsdoc/tag/inline.InlineTagResult} The updated string, as well as information
* about the inline tags that were found.
*/
exports.replaceInlineTags = function(string, replacers) {
var tagInfo = [];
function replaceMatch(replacer, tag, match, text) {
var matchedTag = {
completeTag: match,
tag: tag,
text: text
};
tagInfo.push(matchedTag);
return replacer(string, matchedTag);
}
string = string || '';
Object.keys(replacers).forEach(function(replacer) {
var tagRegExp = regExpFactory(replacer);
var matches;
// call the replacer once for each match
while ( (matches = tagRegExp.exec(string)) !== null ) {
string = replaceMatch(replacers[replacer], replacer, matches[0], matches[1]);
}
});
return {
tags: tagInfo,
newString: string.trim()
};
};
/**
* Replace all instances of an inline tag with other text.
*
* @param {string} string - The string in which to replace the inline tag.
* @param {string} tag - The name of the inline tag to replace.
* @param {module:jsdoc/tag/inline.InlineTagReplacer} replacer - The function that is used to
* replace text in the string.
* @return {module:jsdoc/tag/inline.InlineTagResult} The updated string, as well as information
* about the inline tags that were found.
*/
exports.replaceInlineTag = function(string, tag, replacer) {
var replacers = {};
replacers[tag] = replacer;
return exports.replaceInlineTags(string, replacers);
};
/**
* Extract inline tags from a string, replacing them with an empty string.
*
* @param {string} string - The string from which to extract text.
* @param {?string} tag - The inline tag to extract.
* @return {module:jsdoc/tag/inline.InlineTagResult} The updated string, as well as information
* about the inline tags that were found.
*/
exports.extractInlineTag = function(string, tag) {
return exports.replaceInlineTag(string, tag, function(str, tagInfo) {
return str.replace(tagInfo.completeTag, '');
});
};

View File

@ -1,303 +0,0 @@
/**
* @module jsdoc/tag/type
*
* @author Michael Mathews <micmath@gmail.com>
* @author Jeff Williams <jeffrey.l.williams@gmail.com>
* @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
'use strict';
var catharsis = require('catharsis');
var jsdoc = {
name: require('jsdoc/name'),
tag: {
inline: require('jsdoc/tag/inline')
}
};
var util = require('util');
/**
* Information about a type expression extracted from tag text.
*
* @typedef TypeExpressionInfo
* @memberof module:jsdoc/tag/type
* @property {string} expression - The type expression.
* @property {string} text - The updated tag text.
*/
/** @private */
function unescapeBraces(text) {
return text.replace(/\\\{/g, '{')
.replace(/\\\}/g, '}');
}
/**
* Extract a type expression from the tag text.
*
* @private
* @param {string} string - The tag text.
* @return {module:jsdoc/tag/type.TypeExpressionInfo} The type expression and updated tag text.
*/
function extractTypeExpression(string) {
var completeExpression;
var count = 0;
var position = 0;
var expression = '';
var startIndex = string.search(/\{[^@]/);
var textStartIndex;
if (startIndex !== -1) {
// advance to the first character in the type expression
position = textStartIndex = startIndex + 1;
count++;
while (position < string.length) {
switch (string[position]) {
case '\\':
// backslash is an escape character, so skip the next character
position++;
break;
case '{':
count++;
break;
case '}':
count--;
break;
default:
// do nothing
}
if (count === 0) {
completeExpression = string.slice(startIndex, position + 1);
expression = string.slice(textStartIndex, position).trim();
break;
}
position++;
}
}
string = completeExpression ? string.replace(completeExpression, '') : string;
return {
expression: unescapeBraces(expression),
newString: string.trim()
};
}
/** @private */
function getTagInfo(tagValue, canHaveName, canHaveType) {
var name = '';
var typeExpression = '';
var text = tagValue;
var expressionAndText;
var nameAndDescription;
var typeOverride;
if (canHaveType) {
expressionAndText = extractTypeExpression(text);
typeExpression = expressionAndText.expression;
text = expressionAndText.newString;
}
if (canHaveName) {
nameAndDescription = jsdoc.name.splitName(text);
name = nameAndDescription.name;
text = nameAndDescription.description;
}
// an inline @type tag, like {@type Foo}, overrides the type expression
if (canHaveType) {
typeOverride = jsdoc.tag.inline.extractInlineTag(text, 'type');
if (typeOverride.tags && typeOverride.tags[0]) {
typeExpression = typeOverride.tags[0].text;
}
text = typeOverride.newString;
}
return {
name: name,
typeExpression: typeExpression,
text: text
};
}
/**
* Information provided in a JSDoc tag.
*
* @typedef {Object} TagInfo
* @memberof module:jsdoc/tag/type
* @property {string} TagInfo.defaultvalue - The default value of the member.
* @property {string} TagInfo.name - The name of the member (for example, `myParamName`).
* @property {boolean} TagInfo.nullable - Indicates whether the member can be set to `null` or
* `undefined`.
* @property {boolean} TagInfo.optional - Indicates whether the member is optional.
* @property {string} TagInfo.text - Descriptive text for the member (for example, `The user's email
* address.`).
* @property {Array.<string>} TagInfo.type - The type or types that the member can contain (for
* example, `string` or `MyNamespace.MyClass`).
* @property {string} TagInfo.typeExpression - The type expression that was parsed to identify the
* types.
* @property {boolean} TagInfo.variable - Indicates whether the number of members that are provided
* can vary (for example, in a function that accepts any number of parameters).
*/
// TODO: move to module:jsdoc/name?
/**
* Extract JSDoc-style type information from the name specified in the tag info, including the
* member name; whether the member is optional; and the default value of the member.
*
* @private
* @param {module:jsdoc/tag/type.TagInfo} tagInfo - Information contained in the tag.
* @return {module:jsdoc/tag/type.TagInfo} Updated information from the tag.
*/
function parseName(tagInfo) {
// like '[foo]' or '[ foo ]' or '[foo=bar]' or '[ foo=bar ]' or '[ foo = bar ]'
if ( /^\[\s*(.+?)\s*\]$/.test(tagInfo.name) ) {
tagInfo.name = RegExp.$1;
tagInfo.optional = true;
// like 'foo=bar' or 'foo = bar'
if ( /^(.+?)\s*=\s*(.+)$/.test(tagInfo.name) ) {
tagInfo.name = RegExp.$1;
tagInfo.defaultvalue = RegExp.$2;
}
}
return tagInfo;
}
/** @private */
function getTypeStrings(parsedType, isOutermostType) {
var applications;
var typeString;
var types = [];
var TYPES = catharsis.Types;
switch(parsedType.type) {
case TYPES.AllLiteral:
types.push('*');
break;
case TYPES.FunctionType:
types.push('function');
break;
case TYPES.NameExpression:
types.push(parsedType.name);
break;
case TYPES.NullLiteral:
types.push('null');
break;
case TYPES.RecordType:
types.push('Object');
break;
case TYPES.TypeApplication:
// if this is the outermost type, we strip the modifiers; otherwise, we keep them
if (isOutermostType) {
applications = parsedType.applications.map(function(application) {
return getTypeStrings(application);
}).join(', ');
typeString = util.format( '%s.<%s>', getTypeStrings(parsedType.expression),
applications );
types.push(typeString);
}
else {
types.push( catharsis.stringify(parsedType) );
}
break;
case TYPES.TypeUnion:
parsedType.elements.forEach(function(element) {
types = types.concat( getTypeStrings(element) );
});
break;
case TYPES.UndefinedLiteral:
types.push('undefined');
break;
case TYPES.UnknownLiteral:
types.push('?');
break;
default:
// this shouldn't happen
throw new Error( util.format('unrecognized type %s in parsed type: %j', parsedType.type,
parsedType) );
}
return types;
}
/**
* Extract JSDoc-style and Closure Compiler-style type information from the type expression
* specified in the tag info.
*
* @private
* @param {module:jsdoc/tag/type.TagInfo} tagInfo - Information contained in the tag.
* @return {module:jsdoc/tag/type.TagInfo} Updated information from the tag.
*/
function parseTypeExpression(tagInfo) {
var errorMessage;
var parsedType;
// don't try to parse empty type expressions
if (!tagInfo.typeExpression) {
return tagInfo;
}
try {
parsedType = catharsis.parse(tagInfo.typeExpression, {jsdoc: true});
}
catch (e) {
// always re-throw so the caller has a chance to report which file was bad
throw new Error( util.format('Invalid type expression "%s": %s', tagInfo.typeExpression,
e.message) );
}
tagInfo.type = tagInfo.type.concat( getTypeStrings(parsedType, true) );
// Catharsis and JSDoc use the same names for 'optional' and 'nullable'...
['optional', 'nullable'].forEach(function(key) {
if (parsedType[key] !== null && parsedType[key] !== undefined) {
tagInfo[key] = parsedType[key];
}
});
// ...but not 'variable'.
if (parsedType.repeatable !== null && parsedType.repeatable !== undefined) {
tagInfo.variable = parsedType.repeatable;
}
return tagInfo;
}
// TODO: allow users to add/remove type parsers (perhaps via plugins)
var typeParsers = [parseName, parseTypeExpression];
/**
* Parse the value of a JSDoc tag.
*
* @param {string} tagValue - The value of the tag. For example, the tag `@param {string} name` has
* a value of `{string} name`.
* @param {boolean} canHaveName - Indicates whether the value can include a symbol name.
* @param {boolean} canHaveType - Indicates whether the value can include a type expression that
* describes the symbol.
* @return {module:jsdoc/tag/type.TagInfo} Information obtained from the tag.
* @throws {Error} Thrown if a type expression cannot be parsed.
*/
exports.parse = function(tagValue, canHaveName, canHaveType) {
if (typeof tagValue !== 'string') { tagValue = ''; }
var tagInfo = getTagInfo(tagValue, canHaveName, canHaveType);
tagInfo.type = tagInfo.type || [];
typeParsers.forEach(function(parser) {
tagInfo = parser.call(this, tagInfo);
});
// if we wanted a type, but the parsers didn't add any type names, use the type expression
if (canHaveType && !tagInfo.type.length && tagInfo.typeExpression) {
tagInfo.type = [tagInfo.typeExpression];
}
return tagInfo;
};

View File

@ -1,45 +0,0 @@
/*global env: true */
/**
@module jsdoc/tag/validator
@requires jsdoc/tag/dictionary
@author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
'use strict';
var dictionary = require('jsdoc/tag/dictionary');
var format = require('util').format;
var logger = require('jsdoc/util/logger');
function buildMessage(tagName, meta, desc) {
var result = format('The @%s tag %s. File: %s, line: %s', tagName, desc, meta.filename,
meta.lineno);
if (meta.comment) {
result += '\n' + meta.comment;
}
return result;
}
/**
* Validate the given tag.
*/
exports.validate = function(tag, tagDef, meta) {
// check for errors that make the tag useless
if (!tagDef && !env.conf.tags.allowUnknownTags) {
logger.error( buildMessage(tag.title, meta, 'is not a known tag') );
}
else if (!tag.text && tagDef.mustHaveValue) {
logger.error( buildMessage(tag.title, meta, 'requires a value') );
}
// check for minor issues that are usually harmless
else if (tag.text && tagDef.mustNotHaveValue) {
logger.warn( buildMessage(tag.title, meta,
'does not permit a value; the value will be ignored') );
}
else if (tag.value && tag.value.description && tagDef.mustNotHaveDescription) {
logger.warn( buildMessage(tag.title, meta,
'does not permit a description; the description will be ignored') );
}
};

View File

@ -1,84 +0,0 @@
/**
* @file Wrapper for underscore's template utility to allow loading templates from files.
* @author Rafał Wrzeszcz <rafal.wrzeszcz@wrzasq.pl>
* @author <a href="mailto:matthewkastor@gmail.com">Matthew Christopher Kastor-Inare III</a>
* @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
'use strict';
var _ = require('underscore'),
fs = require('jsdoc/fs'),
path = require('path');
/**
@module jsdoc/template
*/
/**
@class
@classdesc Underscore template helper.
@param {string} path - Templates directory.
*/
exports.Template = function(path) {
this.path = path;
this.layout = null;
this.cache = {};
// override default template tag settings
this.settings = {
evaluate : /<\?js([\s\S]+?)\?>/g,
interpolate: /<\?js=([\s\S]+?)\?>/g,
escape : /<\?js~([\s\S]+?)\?>/g
};
};
/** Loads template from given file.
@param {string} file - Template filename.
@return {function} Returns template closure.
*/
exports.Template.prototype.load = function(file) {
return _.template(fs.readFileSync(file, 'utf8'), null, this.settings);
};
/**
Renders template using given data.
This is low-level function, for rendering full templates use {@link Template.render()}.
@param {string} file - Template filename.
@param {object} data - Template variables (doesn't have to be object, but passing variables dictionary is best way and most common use).
@return {string} Rendered template.
*/
exports.Template.prototype.partial = function(file, data) {
file = path.resolve(this.path, file);
// load template into cache
if (!(file in this.cache)) {
this.cache[file] = this.load(file);
}
// keep template helper context
return this.cache[file].call(this, data);
};
/**
Renders template with given data.
This method automaticaly applies layout if set.
@param {string} file - Template filename.
@param {object} data - Template variables (doesn't have to be object, but passing variables dictionary is best way and most common use).
@return {string} Rendered template.
*/
exports.Template.prototype.render = function(file, data) {
// main content
var content = this.partial(file, data);
// apply layout
if (this.layout) {
data.content = content;
content = this.partial(this.layout, data);
}
return content;
};

View File

@ -1,111 +0,0 @@
/**
@overview
@author Rafał Wrzeszcz <rafal.wrzeszcz@wrzasq.pl>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
'use strict';
var markdown = require('jsdoc/util/markdown');
/** Removes child tutorial from the parent. Does *not* unset child.parent though.
@param {Tutorial} parent - parent tutorial.
@param {Tutorial} child - Old child.
@private
*/
function removeChild(parent, child) {
var index = parent.children.indexOf(child);
if (index !== -1) {
parent.children.splice(index, 1);
}
}
/** Adds a child to the parent tutorial. Does *not* set child.parent though.
@param {Tutorial} parent - parent tutorial.
@param {Tutorial} child - New child.
@private
*/
function addChild(parent, child) {
parent.children.push(child);
}
/**
@module jsdoc/tutorial
*/
/**
@class
@classdesc Represents a single JSDoc tutorial.
@param {string} name - Tutorial name.
@param {string} content - Text content.
@param {number} type - Source formating.
*/
exports.Tutorial = function(name, content, type) {
this.title = this.name = name;
this.content = content;
this.type = type;
// default values
this.parent = null;
this.children = [];
};
/** Moves children from current parent to different one.
@param {?Tutorial} parent - New parent. If null, the tutorial has no parent.
*/
exports.Tutorial.prototype.setParent = function(parent) {
// removes node from old parent
if (this.parent) {
removeChild(this.parent, this);
}
this.parent = parent;
if (parent) {
addChild(parent, this);
}
};
/** Removes children from current node.
@param {Tutorial} child - Old child.
*/
exports.Tutorial.prototype.removeChild = function(child) {
child.setParent(null);
};
/** Adds new children to current node.
@param {Tutorial} child - New child.
*/
exports.Tutorial.prototype.addChild = function(child) {
child.setParent(this);
};
/** Prepares source.
@return {string} HTML source.
*/
exports.Tutorial.prototype.parse = function() {
switch (this.type) {
// nothing to do
case exports.TYPES.HTML:
return this.content;
// markdown
case exports.TYPES.MARKDOWN:
var mdParse = markdown.getParser();
return mdParse(this.content)
.replace(/&amp;/g, '&') // because markdown escapes these
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>');
// uhm... should we react somehow?
// if not then this case can be merged with TYPES.HTML
default:
return this.content;
}
};
/** Tutorial source types.
@enum {number}
*/
exports.TYPES = {
HTML: 1,
MARKDOWN: 2
};

View File

@ -1,193 +0,0 @@
/*global env: true */
/**
@overview
@author Rafa&#322; Wrzeszcz <rafal.wrzeszcz@wrzasq.pl>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
/**
@module jsdoc/tutorial/resolver
*/
'use strict';
var logger = require('jsdoc/util/logger');
var fs = require('jsdoc/fs');
var path = require('path');
var tutorial = require('jsdoc/tutorial');
var hasOwnProp = Object.prototype.hasOwnProperty;
var conf = {};
var finder = /^(.*)\.(x(?:ht)?ml|html?|md|markdown|json)$/i;
var tutorials = {};
/** checks if `conf` is the metadata for a single tutorial.
* A tutorial's metadata has a property 'title' and/or a property 'children'.
* @param {object} json - the object we want to test (typically from JSON.parse)
* @returns {boolean} whether `json` could be the metadata for a tutorial.
*/
function isTutorialJSON(json) {
// if conf.title exists or conf.children exists, it is metadata for a tutorial
return (hasOwnProp.call(json, 'title') || hasOwnProp.call(json, 'children'));
}
/** Helper function that adds tutorial configuration to the `conf` variable.
* This helps when multiple tutorial configurations are specified in one object,
* or when a tutorial's children are specified as tutorial configurations as
* opposed to an array of tutorial names.
*
* Recurses as necessary to ensure all tutorials are added.
*
* @param {string} name - if `meta` is a configuration for a single tutorial,
* this is that tutorial's name.
* @param {object} meta - object that contains tutorial information.
* Can either be for a single tutorial, or for multiple
* (where each key in `meta` is the tutorial name and each
* value is the information for a single tutorial).
* Additionally, a tutorial's 'children' property may
* either be an array of strings (names of the child tutorials),
* OR an object giving the configuration for the child tutorials.
*/
function addTutorialConf(name, meta) {
var names, i;
if (isTutorialJSON(meta)) {
// if the children are themselves tutorial defintions as opposed to an
// array of strings, add each child.
if (hasOwnProp.call(meta, 'children') && !Array.isArray(meta.children)) {
names = Object.keys(meta.children);
for (i = 0; i < names.length; ++i) {
addTutorialConf(names[i], meta.children[names[i]]);
}
// replace with an array of names.
meta.children = names;
}
// check if the tutorial has already been defined...
if (hasOwnProp.call(conf, name)) {
logger.warn('Metadata for the tutorial %s is defined more than once. Only the first definition will be used.', name );
} else {
conf[name] = meta;
}
} else {
// it's an object of tutorials, the keys are th etutorial names.
names = Object.keys(meta);
for (i = 0; i < names.length; ++i) {
addTutorialConf(names[i], meta[names[i]]);
}
}
}
/** Adds new tutorial.
@param {tutorial.Tutorial} current - New tutorial.
*/
exports.addTutorial = function(current) {
if (hasOwnProp.call(tutorials, current.name)) {
logger.warn('The tutorial %s is defined more than once. Only the first definition will be used.', current.name);
} else {
tutorials[current.name] = current;
// default temporary parent
current.setParent(exports.root);
}
};
/** Root tutorial.
@type tutorial.Tutorial
*/
exports.root = new tutorial.Tutorial('', '');
/** Additional instance method for root node.
@param {string} name - Tutorial name.
@return {tutorial.Tutorial} Tutorial instance.
*/
exports.root.getByName = function(name) {
return hasOwnProp.call(tutorials, name) && tutorials[name];
};
/** Load tutorials from given path.
@param {string} _path - Tutorials directory.
*/
exports.load = function(_path) {
var match,
type,
name,
content,
current,
files = fs.ls(_path);
// tutorials handling
files.forEach(function(file) {
match = file.match(finder);
// any filetype that can apply to tutorials
if (match) {
name = path.basename(match[1]);
content = fs.readFileSync(file, env.opts.encoding);
switch (match[2].toLowerCase()) {
// HTML type
case 'xml':
case 'xhtml':
case 'html':
case 'htm':
type = tutorial.TYPES.HTML;
break;
// Markdown typs
case 'md':
case 'markdown':
type = tutorial.TYPES.MARKDOWN;
break;
// configuration file
case 'json':
var meta = JSON.parse(content);
addTutorialConf(name, meta);
// don't add this as a tutorial
return;
// how can it be? check `finder' regexp
default:
// not a file we want to work with
return;
}
current = new tutorial.Tutorial(name, content, type);
exports.addTutorial(current);
}
});
};
/** Resolves hierarchical structure.
*/
exports.resolve = function() {
var item,
current;
for (var name in conf) {
if ( hasOwnProp.call(conf, name) ) {
// TODO: should we complain about this?
if (!hasOwnProp.call(tutorials, name)) {
continue;
}
item = conf[name];
current = tutorials[name];
// set title
if (item.title) {
current.title = item.title;
}
// add children
if (item.children) {
item.children.forEach(function(child) {
if (!hasOwnProp.call(tutorials, child)) {
logger.error('Missing child tutorial: %s', child);
}
else {
tutorials[child].setParent(current);
}
});
}
}
}
};

View File

@ -1,38 +0,0 @@
/**
Deep clone a simple object.
@private
*/
'use strict';
function doop(o) {
var clone;
var props;
var i;
var l;
if (o instanceof Object && o.constructor !== Function) {
if ( Array.isArray(o) ) {
clone = [];
for (i = 0, l = o.length; i < l; i++) {
clone[i] = (o[i] instanceof Object) ? doop(o[i]) : o[i];
}
}
else {
clone = Object.create( Object.getPrototypeOf(o) );
props = Object.getOwnPropertyNames(o);
for (i = 0, l = props.length; i < l; i++) {
Object.defineProperty(clone, props[i],
Object.getOwnPropertyDescriptor(o, props[i]));
}
}
return clone;
}
return o;
}
// for backwards compatibility
doop.doop = doop;
module.exports = doop;

View File

@ -1,135 +0,0 @@
/*global Set */
/**
* Recursively print out all names and values in a data structure.
* @module jsdoc/util/dumper
* @author Michael Mathews <micmath@gmail.com>
* @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
'use strict';
var util = require('util');
var setDefined = typeof Set !== 'undefined';
function ObjectWalker() {
if (setDefined) {
this.seenItems = new Set();
} else {
this.seenItems = [];
}
}
ObjectWalker.prototype.seen = function(object) {
var result;
if (setDefined) {
result = this.seenItems.has(object);
} else {
result = object.hasBeenSeenByWalkerDumper;
}
return result;
};
ObjectWalker.prototype.markAsSeen = function(object) {
if (setDefined) {
this.seenItems.add(object);
} else {
object.hasBeenSeenByWalkerDumper = true;
this.seenItems.push(object);
}
};
ObjectWalker.prototype.cleanSeenFlag = function() {
if (setDefined) {
this.seenItems = new Set();
} else {
this.seenItems.forEach(function(object) {
delete object.hasBeenSeenByWalkerDumper;
});
}
};
// some objects are unwalkable, like Java native objects
ObjectWalker.prototype.isUnwalkable = function(o) {
return (o && typeof o === 'object' && typeof o.constructor === 'undefined');
};
ObjectWalker.prototype.isFunction = function(o) {
return (o && typeof o === 'function' || o instanceof Function);
};
ObjectWalker.prototype.isObject = function(o) {
return o && o instanceof Object ||
(o && typeof o.constructor !== 'undefined' && o.constructor.name === 'Object');
};
ObjectWalker.prototype.checkCircularRefs = function(o, func) {
if ( this.seen(o) ) {
return '<CircularRef>';
}
else {
this.markAsSeen(o);
return func(o);
}
};
ObjectWalker.prototype.walk = function(o) {
var result;
var self = this;
if ( this.isUnwalkable(o) ) {
result = '<Object>';
}
else if ( o === undefined ) {
result = null;
}
else if ( Array.isArray(o) ) {
result = this.checkCircularRefs(o, function(arr) {
var newArray = [];
arr.forEach(function(item) {
newArray.push( self.walk(item) );
});
return newArray;
});
}
else if ( util.isRegExp(o) ) {
result = '<RegExp ' + o + '>';
}
else if ( util.isDate(o) ) {
result = '<Date ' + o.toUTCString() + '>';
}
else if ( util.isError(o) ) {
result = { message: o.message };
}
else if ( this.isFunction(o) ) {
result = '<Function' + (o.name ? ' ' + o.name : '') + '>';
}
else if ( this.isObject(o) && o !== null ) {
result = this.checkCircularRefs(o, function(obj) {
var newObj = {};
Object.keys(obj).forEach(function(key) {
if (!setDefined && key === 'hasBeenSeenByWalkerDumper') { return; }
newObj[key] = self.walk(obj[key]);
});
return newObj;
});
}
// should be safe to JSON.stringify() everything else
else {
result = o;
}
return result;
};
/**
* @param {*} object
*/
exports.dump = function(object) {
var walker = new ObjectWalker();
var result = JSON.stringify(walker.walk(object), null, 4);
walker.cleanSeenFlag();
return result;
};

View File

@ -1,35 +0,0 @@
/*global env: true */
/**
* Helper functions for handling errors.
*
* @deprecated As of JSDoc 3.3.0. This module may be removed in a future release. Use the module
* {@link module:jsdoc/util/logger} to log warnings and errors.
* @module jsdoc/util/error
*/
'use strict';
/**
* Log an exception as an error.
*
* Prior to JSDoc 3.3.0, this method would either log the exception (if lenient mode was enabled) or
* re-throw the exception (default).
*
* In JSDoc 3.3.0 and later, lenient mode has been replaced with strict mode, which is disabled by
* default. If strict mode is enabled, calling the `handle` method causes JSDoc to exit immediately,
* just as if the exception had been re-thrown.
*
* @deprecated As of JSDoc 3.3.0. This module may be removed in a future release.
* @param {Error} e - The exception to log.
* @memberof module:jsdoc/util/error
*/
exports.handle = function(e) {
var logger = require('jsdoc/util/logger');
var msg = e ? ( e.message || JSON.stringify(e) ) : '';
// include the error type if it's an Error object
if (e instanceof Error) {
msg = e.name + ': ' + msg;
}
logger.error(msg);
};

View File

@ -1,156 +0,0 @@
/*global env */
/**
* Provides access to Markdown-related functions.
* @module jsdoc/util/markdown
* @author Michael Mathews <micmath@gmail.com>
* @author Ben Blank <ben.blank@gmail.com>
*/
'use strict';
var util = require('util');
/**
* Enumeration of Markdown parsers that are available.
* @enum {String}
*/
var parserNames = {
/**
* The "[markdown-js](https://github.com/evilstreak/markdown-js)" (aka "evilstreak") parser.
*
* @deprecated Replaced by "marked," as markdown-js does not support inline HTML.
*/
evilstreak: 'marked',
/**
* The "GitHub-flavored Markdown" parser.
* @deprecated Replaced by "marked."
*/
gfm: 'marked',
/**
* The "[Marked](https://github.com/chjj/marked)" parser.
*/
marked: 'marked'
};
/**
* Escape underscores that occur within {@ ... } in order to protect them
* from the markdown parser(s).
* @param {String} source the source text to sanitize.
* @returns {String} `source` where underscores within {@ ... } have been
* protected with a preceding backslash (i.e. \_) -- the markdown parsers
* will strip the backslash and protect the underscore.
*/
function escapeUnderscores(source) {
return source.replace(/\{@[^}\r\n]+\}/g, function (wholeMatch) {
return wholeMatch.replace(/(^|[^\\])_/g, '$1\\_');
});
}
/**
* Escape HTTP/HTTPS URLs so that they are not automatically converted to HTML links.
*
* @param {string} source - The source text to escape.
* @return {string} The source text with escape characters added to HTTP/HTTPS URLs.
*/
function escapeUrls(source) {
return source.replace(/(https?)\:\/\//g, '$1:\\/\\/');
}
/**
* Unescape HTTP/HTTPS URLs after Markdown parsing is complete.
*
* @param {string} source - The source text to unescape.
* @return {string} The source text with escape characters removed from HTTP/HTTPS URLs.
*/
function unescapeUrls(source) {
return source.replace(/(https?)\:\\\/\\\//g, '$1://');
}
/**
* Escape characters in text within a code block.
*
* @param {string} source - The source text to escape.
* @return {string} The escaped source text.
*/
function escapeCode(source) {
return source.replace(/</g, '&lt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
/**
* Retrieve a function that accepts a single parameter containing Markdown source. The function uses
* the specified parser to transform the Markdown source to HTML, then returns the HTML as a string.
*
* @private
* @param {String} parserName The name of the selected parser.
* @param {Object} [conf] Configuration for the selected parser, if any.
* @returns {Function} A function that accepts Markdown source, feeds it to the selected parser, and
* returns the resulting HTML.
*/
function getParseFunction(parserName, conf) {
var logger = require('jsdoc/util/logger');
var marked = require('marked');
var markedRenderer;
var parserFunction;
conf = conf || {};
if (parserName === parserNames.marked) {
// Marked generates an "id" attribute for headers; this custom renderer suppresses it
markedRenderer = new marked.Renderer();
markedRenderer.heading = function(text, level) {
return util.format('<h%s>%s</h%s>', level, text, level);
};
// Allow prettyprint to work on inline code samples
markedRenderer.code = function(code, language) {
var langClass = language ? ' lang-' + language : '';
return util.format( '<pre class="prettyprint source%s"><code>%s</code></pre>',
langClass, escapeCode(code) );
};
parserFunction = function(source) {
var result;
source = escapeUnderscores(source);
source = escapeUrls(source);
result = marked(source, { renderer: markedRenderer })
.replace(/\s+$/, '')
.replace(/&#39;/g, "'");
result = unescapeUrls(result);
return result;
};
parserFunction._parser = parserNames.marked;
return parserFunction;
}
else {
logger.error('Unrecognized Markdown parser "%s". Markdown support is disabled.',
parserName);
}
}
/**
* Retrieve a Markdown parsing function based on the value of the `conf.json` file's
* `env.conf.markdown` property. The parsing function accepts a single parameter containing Markdown
* source. The function uses the parser specified in `conf.json` to transform the Markdown source to
* HTML, then returns the HTML as a string.
*
* @returns {function} A function that accepts Markdown source, feeds it to the selected parser, and
* returns the resulting HTML.
*/
exports.getParser = function() {
var conf = env.conf.markdown;
if (conf && conf.parser) {
return getParseFunction(parserNames[conf.parser], conf);
}
else {
// marked is the default parser
return getParseFunction(parserNames.marked, conf);
}
};

View File

@ -1,767 +0,0 @@
/*global env: true */
/**
* @module jsdoc/util/templateHelper
*/
'use strict';
var dictionary = require('jsdoc/tag/dictionary');
var util = require('util');
var hasOwnProp = Object.prototype.hasOwnProperty;
var files = {};
// each container gets its own html file
var containers = ['class', 'module', 'external', 'namespace', 'mixin'];
var tutorials;
/** Sets tutorials map.
@param {jsdoc.tutorial.Tutorial} root - Root tutorial node.
*/
exports.setTutorials = function(root) {
tutorials = root;
};
exports.globalName = 'global';
exports.fileExtension = '.html';
exports.scopeToPunc = require('jsdoc/name').scopeToPunc;
function getNamespace(kind) {
if (dictionary.isNamespace(kind)) {
return kind + ':';
}
return '';
}
function makeFilenameUnique(filename, str) {
var key = filename.toLowerCase();
var nonUnique = true;
// append enough underscores to make the filename unique
while (nonUnique) {
if ( files[key] && hasOwnProp.call(files, key) ) {
filename += '_';
key = filename.toLowerCase();
} else {
nonUnique = false;
}
}
files[key] = str;
return filename;
}
function cleanseFilename(str) {
str = str || '';
// allow for namespace prefix
// TODO: use prefixes in jsdoc/doclet
return str.replace(/^(event|module|external|package):/, '$1-')
// use - instead of ~ to denote 'inner'
.replace(/~/g, '-')
// use _ instead of # to denote 'instance'
.replace(/\#/g, '_')
// remove the variation, if any
.replace(/\([\s\S]*\)$/, '');
}
var htmlsafe = exports.htmlsafe = function(str) {
return str.replace(/&/g, '&amp;')
.replace(/</g, '&lt;');
};
/**
* Convert a string to a unique filename, including an extension.
*
* Filenames are cached to ensure that they are used only once. For example, if the same string is
* passed in twice, two different filenames will be returned.
*
* Also, filenames are not considered unique if they are capitalized differently but are otherwise
* identical.
* @param {string} str The string to convert.
* @return {string} The filename to use for the string.
*/
var getUniqueFilename = exports.getUniqueFilename = function(str) {
// allow for namespace prefix
var basename = cleanseFilename(str);
// if the basename includes characters that we can't use in a filepath, remove everything up to
// and including the last bad character
var regexp = /[^$a-z0-9._\-](?=[$a-z0-9._\-]*$)/i;
var result = regexp.exec(basename);
if (result && result.index) {
basename = basename.substr(result.index + 1);
}
// make sure we don't create hidden files on POSIX systems
basename = basename.replace(/^\./, '');
// and in case we've now stripped the entire basename (uncommon, but possible):
basename = basename.length ? basename : '_';
return makeFilenameUnique(basename, str) + exports.fileExtension;
};
// two-way lookup
var linkMap = {
longnameToUrl: {},
urlToLongname: {}
};
var tutorialLinkMap = {
nameToUrl: {},
urlToName: {}
};
var longnameToUrl = exports.longnameToUrl = linkMap.longnameToUrl;
function parseType(longname) {
var catharsis = require('catharsis');
var err;
try {
return catharsis.parse(longname, {jsdoc: true});
}
catch (e) {
err = new Error('unable to parse ' + longname + ': ' + e.message);
require('jsdoc/util/logger').error(err);
return longname;
}
}
function stringifyType(parsedType, cssClass, linkMap) {
return require('catharsis').stringify(parsedType, {
cssClass: cssClass,
htmlSafe: true,
links: linkMap
});
}
function hasUrlPrefix(text) {
return (/^(http|ftp)s?:\/\//).test(text);
}
function isComplexTypeExpression(expr) {
// record types, type unions, and type applications all count as "complex"
return expr.search(/[{(|]/) !== -1 || expr.search(/</) > 0;
}
/**
* Build an HTML link to the symbol with the specified longname. If the longname is not
* associated with a URL, this method simply returns the link text, if provided, or the longname.
*
* The `longname` parameter can also contain a URL rather than a symbol's longname.
*
* This method supports type applications that can contain one or more types, such as
* `Array.<MyClass>` or `Array.<(MyClass|YourClass)>`. In these examples, the method attempts to
* replace `Array`, `MyClass`, and `YourClass` with links to the appropriate types. The link text
* is ignored for type applications.
*
* @param {string} longname - The longname (or URL) that is the target of the link.
* @param {string=} linkText - The text to display for the link, or `longname` if no text is
* provided.
* @param {Object} options - Options for building the link.
* @param {string=} options.cssClass - The CSS class (or classes) to include in the link's `<a>`
* tag.
* @param {string=} options.fragmentId - The fragment identifier (for example, `name` in
* `foo.html#name`) to append to the link target.
* @param {string=} options.linkMap - The link map in which to look up the longname.
* @param {boolean=} options.monospace - Indicates whether to display the link text in a monospace
* font.
* @return {string} The HTML link, or the link text if the link is not available.
*/
function buildLink(longname, linkText, options) {
var catharsis = require('catharsis');
var classString = options.cssClass ? util.format(' class="%s"', options.cssClass) : '';
var fragmentString = options.fragmentId ? '#' + options.fragmentId : '';
var stripped;
var text;
var url;
var parsedType;
// handle cases like:
// @see <http://example.org>
// @see http://example.org
stripped = longname ? longname.replace(/^<|>$/g, '') : '';
if ( hasUrlPrefix(stripped) ) {
url = stripped;
text = linkText || stripped;
}
// handle complex type expressions that may require multiple links
// (but skip anything that looks like an inline tag)
else if (longname && isComplexTypeExpression(longname) && /\{\@.+\}/.test(longname) === false) {
parsedType = parseType(longname);
return stringifyType(parsedType, options.cssClass, options.linkMap);
}
else {
url = hasOwnProp.call(options.linkMap, longname) ? options.linkMap[longname] : '';
text = linkText || longname;
}
text = options.monospace ? '<code>' + text + '</code>' : text;
if (!url) {
return text;
}
else {
return util.format('<a href="%s%s"%s>%s</a>', url, fragmentString, classString, text);
}
}
/**
* Retrieve an HTML link to the symbol with the specified longname. If the longname is not
* associated with a URL, this method simply returns the link text, if provided, or the longname.
*
* The `longname` parameter can also contain a URL rather than a symbol's longname.
*
* This method supports type applications that can contain one or more types, such as
* `Array.<MyClass>` or `Array.<(MyClass|YourClass)>`. In these examples, the method attempts to
* replace `Array`, `MyClass`, and `YourClass` with links to the appropriate types. The link text
* is ignored for type applications.
*
* @param {string} longname - The longname (or URL) that is the target of the link.
* @param {string=} linkText - The text to display for the link, or `longname` if no text is
* provided.
* @param {string=} cssClass - The CSS class (or classes) to include in the link's `<a>` tag.
* @param {string=} fragmentId - The fragment identifier (for example, `name` in `foo.html#name`) to
* append to the link target.
* @return {string} The HTML link, or a plain-text string if the link is not available.
*/
var linkto = exports.linkto = function(longname, linkText, cssClass, fragmentId) {
return buildLink(longname, linkText, {
cssClass: cssClass,
fragmentId: fragmentId,
linkMap: longnameToUrl
});
};
function useMonospace(tag, text) {
var cleverLinks;
var monospaceLinks;
var result;
if ( hasUrlPrefix(text) ) {
result = false;
}
else if (tag === 'linkplain') {
result = false;
}
else if (tag === 'linkcode') {
result = true;
}
else {
cleverLinks = env.conf.templates.cleverLinks;
monospaceLinks = env.conf.templates.monospaceLinks;
if (monospaceLinks || cleverLinks) {
result = true;
}
}
return result || false;
}
function splitLinkText(text) {
var linkText;
var target;
var splitIndex;
// if a pipe is not present, we split on the first space
splitIndex = text.indexOf('|');
if (splitIndex === -1) {
splitIndex = text.search(/\s/);
}
if (splitIndex !== -1) {
linkText = text.substr(splitIndex + 1);
// Normalize subsequent newlines to a single space.
linkText = linkText.replace(/\n+/, ' ');
target = text.substr(0, splitIndex);
}
return {
linkText: linkText,
target: target || text
};
}
var tutorialToUrl = exports.tutorialToUrl = function(tutorial) {
var node = tutorials.getByName(tutorial);
// no such tutorial
if (!node) {
require('jsdoc/util/logger').error( new Error('No such tutorial: ' + tutorial) );
return null;
}
var url;
// define the URL if necessary
if (!hasOwnProp.call(tutorialLinkMap.nameToUrl, node.name)) {
url = 'tutorial-' + getUniqueFilename(node.name);
tutorialLinkMap.nameToUrl[node.name] = url;
tutorialLinkMap.urlToName[url] = node.name;
}
return tutorialLinkMap.nameToUrl[node.name];
};
/**
* Retrieve a link to a tutorial, or the name of the tutorial if the tutorial is missing. If the
* `missingOpts` parameter is supplied, the names of missing tutorials will be prefixed by the
* specified text and wrapped in the specified HTML tag and CSS class.
*
* @todo Deprecate missingOpts once we have a better error-reporting mechanism.
* @param {string} tutorial The name of the tutorial.
* @param {string} content The link text to use.
* @param {object} [missingOpts] Options for displaying the name of a missing tutorial.
* @param {string} missingOpts.classname The CSS class to wrap around the tutorial name.
* @param {string} missingOpts.prefix The prefix to add to the tutorial name.
* @param {string} missingOpts.tag The tag to wrap around the tutorial name.
* @return {string} An HTML link to the tutorial, or the name of the tutorial with the specified
* options.
*/
var toTutorial = exports.toTutorial = function(tutorial, content, missingOpts) {
if (!tutorial) {
require('jsdoc/util/logger').error( new Error('Missing required parameter: tutorial') );
return null;
}
var node = tutorials.getByName(tutorial);
// no such tutorial
if (!node) {
missingOpts = missingOpts || {};
var tag = missingOpts.tag;
var classname = missingOpts.classname;
var link = tutorial;
if (missingOpts.prefix) {
link = missingOpts.prefix + link;
}
if (tag) {
link = '<' + tag + (classname ? (' class="' + classname + '">') : '>') + link;
link += '</' + tag + '>';
}
return link;
}
content = content || node.title;
return '<a href="' + tutorialToUrl(tutorial) + '">' + content + '</a>';
};
/** Find symbol {@link ...} and {@tutorial ...} strings in text and turn into html links */
exports.resolveLinks = function(str) {
var replaceInlineTags = require('jsdoc/tag/inline').replaceInlineTags;
function extractLeadingText(string, completeTag) {
var tagIndex = string.indexOf(completeTag);
var leadingText = null;
var leadingTextRegExp = /\[(.+?)\]/g;
var leadingTextInfo = leadingTextRegExp.exec(string);
// did we find leading text, and if so, does it immediately precede the tag?
while (leadingTextInfo && leadingTextInfo.length) {
if (leadingTextInfo.index + leadingTextInfo[0].length === tagIndex) {
string = string.replace(leadingTextInfo[0], '');
leadingText = leadingTextInfo[1];
break;
}
leadingTextInfo = leadingTextRegExp.exec(string);
}
return {
leadingText: leadingText,
string: string
};
}
function processLink(string, tagInfo) {
var leading = extractLeadingText(string, tagInfo.completeTag);
var linkText = leading.leadingText;
var monospace;
var split;
var target;
string = leading.string;
split = splitLinkText(tagInfo.text);
target = split.target;
linkText = linkText || split.linkText;
monospace = useMonospace(tagInfo.tag, tagInfo.text);
return string.replace( tagInfo.completeTag, buildLink(target, linkText, {
linkMap: longnameToUrl,
monospace: monospace
}) );
}
function processTutorial(string, tagInfo) {
var leading = extractLeadingText(string, tagInfo.completeTag);
string = leading.string;
return string.replace( tagInfo.completeTag, toTutorial(tagInfo.text, leading.leadingText) );
}
var replacers = {
link: processLink,
linkcode: processLink,
linkplain: processLink,
tutorial: processTutorial
};
return replaceInlineTags(str, replacers).newString;
};
/** Convert tag text like "Jane Doe <jdoe@example.org>" into a mailto link */
exports.resolveAuthorLinks = function(str) {
var author;
var matches = str.match(/^\s?([\s\S]+)\b\s+<(\S+@\S+)>\s?$/);
if (matches && matches.length === 3) {
author = '<a href="mailto:' + matches[2] + '">' + htmlsafe(matches[1]) + '</a>';
}
else {
author = htmlsafe(str);
}
return author;
};
/**
* Find items in a TaffyDB database that match the specified key-value pairs.
* @param {TAFFY} data The TaffyDB database to search.
* @param {object|function} spec Key-value pairs to match against (for example,
* `{ longname: 'foo' }`), or a function that returns `true` if a value matches or `false` if it
* does not match.
* @return {array<object>} The matching items.
*/
var find = exports.find = function(data, spec) {
return data(spec).get();
};
/**
* Check whether a symbol is the only symbol exported by a module (as in
* `module.exports = function() {};`).
*
* @private
* @param {module:jsdoc/doclet.Doclet} doclet - The doclet for the symbol.
* @return {boolean} `true` if the symbol is the only symbol exported by a module; otherwise,
* `false`.
*/
function isModuleExports(doclet) {
var MODULE_PREFIX = require('jsdoc/name').MODULE_PREFIX;
return doclet.longname && doclet.longname === doclet.name &&
doclet.longname.indexOf(MODULE_PREFIX) === 0 && doclet.kind !== 'module';
}
/**
* Retrieve all of the following types of members from a set of doclets:
*
* + Classes
* + Externals
* + Globals
* + Mixins
* + Modules
* + Namespaces
* + Events
* @param {TAFFY} data The TaffyDB database to search.
* @return {object} An object with `classes`, `externals`, `globals`, `mixins`, `modules`,
* `events`, and `namespaces` properties. Each property contains an array of objects.
*/
exports.getMembers = function(data) {
var members = {
classes: find( data, {kind: 'class'} ),
externals: find( data, {kind: 'external'} ),
events: find( data, {kind: 'event'} ),
globals: find(data, {
kind: ['member', 'function', 'constant', 'typedef'],
memberof: { isUndefined: true }
}),
mixins: find( data, {kind: 'mixin'} ),
modules: find( data, {kind: 'module'} ),
namespaces: find( data, {kind: 'namespace'} )
};
// functions that are also modules (as in "module.exports = function() {};") are not globals
members.globals = members.globals.filter(function(doclet) {
return !isModuleExports(doclet);
});
return members;
};
/**
* Retrieve the member attributes for a doclet (for example, `virtual`, `static`, and
* `readonly`).
* @param {object} d The doclet whose attributes will be retrieved.
* @return {array<string>} The member attributes for the doclet.
*/
exports.getAttribs = function(d) {
var attribs = [];
if (d.virtual) {
attribs.push('abstract');
}
if (d.access && d.access !== 'public') {
attribs.push(d.access);
}
if (d.scope && d.scope !== 'instance' && d.scope !== 'global') {
if (d.kind === 'function' || d.kind === 'member' || d.kind === 'constant') {
attribs.push(d.scope);
}
}
if (d.readonly === true) {
if (d.kind === 'member') {
attribs.push('readonly');
}
}
if (d.kind === 'constant') {
attribs.push('constant');
}
if (d.nullable === true) {
attribs.push('nullable');
}
else if (d.nullable === false) {
attribs.push('non-null');
}
return attribs;
};
/**
* Retrieve links to allowed types for the member.
*
* @param {Object} d - The doclet whose types will be retrieved.
* @param {string} [cssClass] - The CSS class to include in the `class` attribute for each link.
* @return {Array.<string>} HTML links to allowed types for the member.
*/
exports.getSignatureTypes = function(d, cssClass) {
var types = [];
if (d.type && d.type.names) {
types = d.type.names;
}
if (types && types.length) {
types = types.map(function(t) {
return linkto(t, htmlsafe(t), cssClass);
});
}
return types;
};
/**
* Retrieve names of the parameters that the member accepts. If a value is provided for `optClass`,
* the names of optional parameters will be wrapped in a `<span>` tag with that class.
* @param {object} d The doclet whose parameter names will be retrieved.
* @param {string} [optClass] The class to assign to the `<span>` tag that is wrapped around the
* names of optional parameters. If a value is not provided, optional parameter names will not be
* wrapped with a `<span>` tag. Must be a legal value for a CSS class name.
* @return {array<string>} An array of parameter names, with or without `<span>` tags wrapping the
* names of optional parameters.
*/
exports.getSignatureParams = function(d, optClass) {
var pnames = [];
if (d.params) {
d.params.forEach(function(p) {
if (p.name && p.name.indexOf('.') === -1) {
if (p.optional && optClass) {
pnames.push('<span class="' + optClass + '">' + p.name + '</span>');
}
else {
pnames.push(p.name);
}
}
});
}
return pnames;
};
/**
* Retrieve links to types that the member can return.
*
* @param {Object} d - The doclet whose types will be retrieved.
* @param {string} [cssClass] - The CSS class to include in the `class` attribute for each link.
* @return {Array.<string>} HTML links to types that the member can return.
*/
exports.getSignatureReturns = function(d, cssClass) {
var returnTypes = [];
if (d.returns) {
d.returns.forEach(function(r) {
if (r && r.type && r.type.names) {
if (!returnTypes.length) {
returnTypes = r.type.names;
}
}
});
}
if (returnTypes && returnTypes.length) {
returnTypes = returnTypes.map(function(r) {
return linkto(r, htmlsafe(r), cssClass);
});
}
return returnTypes;
};
/**
* Retrieve links to a member's ancestors.
*
* @param {TAFFY} data - The TaffyDB database to search.
* @param {Object} doclet - The doclet whose ancestors will be retrieved.
* @param {string} [cssClass] - The CSS class to include in the `class` attribute for each link.
* @return {Array.<string>} HTML links to a member's ancestors.
*/
exports.getAncestorLinks = function(data, doclet, cssClass) {
var ancestors = [],
doc = doclet.memberof;
while (doc) {
doc = find( data, {longname: doc}, false );
if (doc) { doc = doc[0]; }
if (!doc) { break; }
ancestors.unshift( linkto(doc.longname, (exports.scopeToPunc[doc.scope] || '') + doc.name,
cssClass) );
doc = doc.memberof;
}
if (ancestors.length) {
ancestors[ancestors.length - 1] += (exports.scopeToPunc[doclet.scope] || '');
}
return ancestors;
};
/**
* Iterates through all the doclets in `data`, ensuring that if a method
* @listens to an event, then that event has a 'listeners' array with the
* longname of the listener in it.
*
* @param {TAFFY} data - The TaffyDB database to search.
*/
exports.addEventListeners = function(data) {
// TODO: do this on the *pruned* data
// find all doclets that @listen to something.
var listeners = find(data, function () { return this.listens && this.listens.length; });
if (!listeners.length) {
return;
}
var doc,
l,
_events = {}; // just a cache to prevent me doing so many lookups
listeners.forEach(function (listener) {
l = listener.listens;
l.forEach(function (eventLongname) {
doc = _events[eventLongname] || find(data, {longname: eventLongname, kind: 'event'})[0];
if (doc) {
if (!doc.listeners) {
doc.listeners = [listener.longname];
} else {
doc.listeners.push(listener.longname);
}
_events[eventLongname] = _events[eventLongname] || doc;
}
});
});
};
/**
* Remove members that will not be included in the output, including:
*
* + Undocumented members.
* + Members tagged `@ignore`.
* + Members of anonymous classes.
* + Members tagged `@private`, unless the `private` option is enabled.
* @param {TAFFY} data The TaffyDB database to prune.
* @return {TAFFY} The pruned database.
*/
exports.prune = function(data) {
data({undocumented: true}).remove();
data({ignore: true}).remove();
if (!env.opts.private) { data({access: 'private'}).remove(); }
data({memberof: '<anonymous>'}).remove();
return data;
};
var registerLink = exports.registerLink = function(longname, url) {
linkMap.longnameToUrl[longname] = url;
linkMap.urlToLongname[url] = longname;
};
/**
* Get a longname's filename if one has been registered; otherwise, generate a unique filename, then
* register the filename.
* @private
*/
function getFilename(longname) {
var url;
if ( longnameToUrl[longname] && hasOwnProp.call(longnameToUrl, longname) ) {
url = longnameToUrl[longname];
} else {
url = getUniqueFilename(longname);
registerLink(longname, url);
}
return url;
}
/** Turn a doclet into a URL. */
exports.createLink = function(doclet) {
var filename;
var fragment;
var match;
var fakeContainer;
var url = '';
var INSTANCE = exports.scopeToPunc.instance;
var longname = doclet.longname;
// handle doclets in which doclet.longname implies that the doclet gets its own HTML file, but
// doclet.kind says otherwise. this happens due to mistagged JSDoc (for example, a module that
// somehow has doclet.kind set to `member`).
// TODO: generate a warning (ideally during parsing!)
if (containers.indexOf(doclet.kind) === -1) {
match = /(\S+):/.exec(longname);
if (match && containers.indexOf(match[1]) !== -1) {
fakeContainer = match[1];
}
}
// the doclet gets its own HTML file
if ( containers.indexOf(doclet.kind) !== -1 || isModuleExports(doclet) ) {
filename = getFilename(longname);
}
// mistagged version of a doclet that gets its own HTML file
else if ( containers.indexOf(doclet.kind) === -1 && fakeContainer ) {
filename = getFilename(doclet.memberof || longname);
if (doclet.name === doclet.longname) {
fragment = '';
}
else {
fragment = doclet.name || '';
}
}
// the doclet is within another HTML file
else {
filename = getFilename(doclet.memberof || exports.globalName);
fragment = getNamespace(doclet.kind) + (doclet.name || '');
}
url = fragment ? (filename + INSTANCE + fragment) : filename;
return url;
};

View File

@ -1 +0,0 @@
../marked/bin/marked

View File

@ -1,17 +0,0 @@
Copyright (c) 2014 Google Inc.
Copyright (c) 2012-2014 Jeff Williams
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,337 +0,0 @@
# Catharsis #
A JavaScript parser for
[Google Closure Compiler](https://developers.google.com/closure/compiler/docs/js-for-compiler#types)
and [JSDoc](https://github.com/jsdoc3/jsdoc) type expressions.
Catharsis is designed to be:
+ **Accurate**. Catharsis is based on a [PEG.js](http://pegjs.majda.cz/) grammar that's designed to
handle any valid type expression. It uses a [Mocha](http://visionmedia.github.com/mocha/) test suite
to verify the parser's accuracy.
+ **Fast**. Parse results are cached, so the parser is invoked only when necessary.
+ **Flexible**. Catharsis can convert a parse result back into a type expression, or into a
description of the type expression. In addition, Catharsis can parse
[JSDoc](https://github.com/jsdoc3/jsdoc)-style type expressions.
## Example ##
```js
var catharsis = require('catharsis');
// Google Closure Compiler parsing
var type = '!Object';
var parsedType;
try {
parsedType = catharsis.parse(type); // {"type":"NameExpression,"name":"Object","nullable":false}
} catch(e) {
console.error('unable to parse %s: %s', type, e);
}
// JSDoc-style type expressions enabled
var jsdocType = 'string[]'; // Closure Compiler expects Array.<string>
var parsedJsdocType;
try {
parsedJsdocType = catharsis.parse(jsdocType, {jsdoc: true});
} catch (e) {
console.error('unable to parse %s: %s', jsdocType, e);
}
// Converting parse results back to type expressions
catharsis.stringify(parsedType); // !Object
catharsis.stringify(parsedJsdocType); // string[]
catharsis.stringify(parsedJsdocType, {restringify: true}); // Array.<string>
// Converting parse results to descriptions of the type expression
catharsis.describe(parsedType).simple; // non-null Object
catharsis.describe(parsedJsdocType).simple; // Array of string
```
See the [test/specs directory](test/specs) for more examples of Catharsis' parse results.
## Methods ##
### parse(typeExpression, options) ###
Parse a type expression, and return the parse results. Throws an error if the type expression cannot
be parsed.
When called without options, Catharsis attempts to parse type expressions in the same way as
Closure Compiler. When the `jsdoc` option is enabled, Catharsis can also parse several kinds of
type expressions that are permitted in [JSDoc](https://github.com/jsdoc3/jsdoc):
+ The string `function` is treated as a function type with no parameters.
+ In a function type with repeatable parameters, the names of repeatable parameters are not required
to be enclosed in square brackets (for example, `function(...foo)` is allowed).
+ The period may be omitted from type applications. For example, `Array.<string>` and
`Array<string>` will be parsed in the same way.
+ You may append `[]` to a name expression (for example, `string[]`) to interpret it as a type
application with the expression `Array` (for example, `Array.<string>`).
+ Name expressions may contain the characters `#`, `~`, `:`, and `/`.
+ Name expressions may contain a suffix that is similar to a function signature (for example,
`MyClass(foo, bar)`).
+ Name expressions may contain a reserved word.
+ Record types may use types other than name expressions for keys.
#### Parameters ####
+ `type`: A string containing a Closure Compiler type expression.
+ `options`: Options for parsing the type expression.
+ `options.jsdoc`: Specifies whether to enable parsing of JSDoc-style type expressions. Defaults
to `false`.
+ `options.useCache`: Specifies whether to use the cache of parsed types. Defaults to `true`.
#### Returns ####
An object containing the parse results. See the [test/specs directory](test/specs) for examples of
the parse results for different type expressions.
The object also includes two non-enumerable properties:
+ `jsdoc`: A boolean indicating whether the type expression was parsed with JSDoc support enabled.
+ `typeExpression`: A string containing the type expression that was parsed.
### stringify(parsedType, options) ###
Stringify `parsedType`, and return the type expression. If validation is enabled, throws an error if
the stringified type expression cannot be parsed.
#### Parameters ####
+ `parsedType`: An object containing a parsed Closure Compiler type expression.
+ `options`: Options for stringifying the parse results.
+ `options.cssClass`: Synonym for `options.linkClass`. Deprecated in version 0.8.0; will be
removed in a future version.
+ `options.htmlSafe`: Specifies whether to return an HTML-safe string that replaces left angle
brackets (`<`) with the corresponding entity (`&lt;`). **Note**: Characters in name expressions
are not escaped.
+ `options.linkClass`: A CSS class to add to HTML links. Used only if `options.links` is
provided. By default, no CSS class is added.
+ `options.links`: An object whose keys are name expressions and whose values are URIs. If a
name expression matches a key in `options.links`, the name expression will be wrapped in an
HTML `<a>` tag that links to the URI. If `options.linkClass` is specified, the `<a>` tag will
include a `class` attribute. **Note**: When using this option, parsed types are always
restringified, and the resulting string is not cached.
+ `options.restringify`: Forces Catharsis to restringify the parsed type. If this option is not
specified, and the parsed type object includes a `typeExpression` property, Catharsis will
return the `typeExpression` property without modification when possible. Defaults to `false`.
+ `options.useCache`: Specifies whether to use the cache of stringified type expressions.
Defaults to `true`.
+ `options.validate`: Specifies whether to validate the stringified parse results by attempting
to parse them as a type expression. If the stringified results are not parsable by default, you
must also provide the appropriate options to pass to the `parse()` method. Defaults to `false`.
#### Returns ####
A string containing the type expression.
### describe(parsedType, options) ###
Convert a parsed type to a description of the type expression. This method is especially useful if
your users are not familiar with the syntax for type expressions.
The `describe()` method returns the description in two formats:
+ **Simple format**. A string that provides a complete description of the type expression.
+ **Extended format**. An object that separates out some of the details about the outermost type
expression, such as whether the type is optional, nullable, or repeatable.
For example, if you call `describe('?function(new:MyObject, string)=')`, it returns the following
object:
```js
{
simple: 'optional nullable function(constructs MyObject, string)',
extended: {
description: 'function(string)',
modifiers: {
functionNew: 'Returns MyObject when called with new.',
functionThis: '',
optional: 'Optional.',
nullable: 'May be null.',
repeatable: ''
},
returns: ''
}
}
```
#### Parameters ####
+ `parsedType`: An object containing a parsed Closure Compiler type expression.
+ `options`: Options for creating the description.
+ `options.codeClass`: A CSS class to add to the tag that is wrapped around type names. Used
only if `options.codeTag` is provided. By default, no CSS class is added.
+ `options.codeTag`: The name of an HTML tag (for example, `code`) to wrap around type names.
For example, if this option is set to `code`, the type expression `Array.<string>` would have
the simple description `<code>Array</code> of <code>string</code>`.
+ `options.language`: A string identifying the language in which to generate the description.
The identifier should be an
[ISO 639-1 language code](http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (for example,
`en`). It can optionally be followed by a hyphen and an
[ISO 3166-1 alpha-2 country code](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) (for example,
`en-US`). If you use values other than `en`, you must provide translation resources in
`options.resources`. Defaults to `en`.
+ `options.linkClass`: A CSS class to add to HTML links. Used only if `options.links` is
provided. By default, no CSS class is added.
+ `options.links`: An object whose keys are name expressions and whose values are URIs. If a
name expression matches a key in `options.links`, the name expression will be wrapped in an
HTML `<a>` tag that links to the URI. If `options.linkClass` is specified, the `<a>` tag will
include a `class` attribute. **Note**: When using this option, the description is not cached.
+ `options.resources`: An object that specifies how to describe type expressions for a given
language. The object's property names should use the same format as `options.language`. Each
property should contain an object in the same format as the translation resources in
[res/en.json](res/en.json). If you specify a value for `options.resources.en`, it will override
the defaults in [res/en.json](res/en.json).
+ `options.useCache`: Specifies whether to use the cache of descriptions. Defaults to `true`.
### Returns ###
An object with the following properties:
+ `simple`: A string that provides a complete description of the type expression.
+ `extended`: An object containing details about the outermost type expression.
+ `extended.description`: A string that provides a basic description of the type expression,
excluding the information contained in other properties.
+ `extended.modifiers`: Information about modifiers that apply to the type expression.
+ `extended.modifiers.functionNew`: A string describing what a function returns when called
with `new`. Used only for function types.
+ `extended.modifiers.functionThis`: A string describing what the keyword `this` refers to
within a function. Used only for function types.
+ `extended.modifiers.nullable`: A string indicating whether the type is nullable or
non-nullable.
+ `extended.modifiers.optional`: A string indicating whether the type is optional.
+ `extended.modifiers.repeatable`: A string indicating whether the type can be provided
+ `extended.returns`: A string describing the function's return value. Used only for function
types.
## Installation ##
With [npm](http://npmjs.org):
npm install catharsis
Or without:
git clone git://github.com/hegemonic/catharsis.git
cd catharsis
npm install
## Roadmap and known issues ##
Take a look at the [issue tracker](https://github.com/hegemonic/catharsis/issues) to see what's in
store for Catharsis.
Bug reports, feature requests, and pull requests are always welcome! If you're working on a large
pull request, please contact me in advance so I can help things go smoothly.
**Note**: The parse tree's format should not be considered final until Catharsis reaches version
1.0. I'll do my best to provide release notes for any changes.
## Changelog ##
+ 0.8.3 (October 2014):
+ Type applications are no longer required to include a period (`.`) as a separator, regardless
of whether JSDoc-style type expressions are enabled.
+ Type unions that are not enclosed in parentheses can now include the repeatable (`...`)
modifier when JSDoc-style type expressions are enabled.
+ Name expressions may now be enclosed in single or double quotation marks when JSDoc-style
type expressions are enabled.
+ 0.8.2 (June 2014): Fixed a compatibility issue with the JSDoc fork of Mozilla Rhino.
+ 0.8.1 (June 2014): Added support for type unions that are not enclosed in parentheses, and that
contain nullable or non-nullable modifiers (for example, `!string|!number`).
+ 0.8.0 (May 2014):
+ Added a `describe()` method, which converts a parsed type to a description of the type.
+ Added a `linkClass` option to the `stringify()` method, and deprecated the existing `cssClass`
option. The `cssClass` option will be removed in a future release.
+ Clarified and corrected several sections in the `README`.
+ 0.7.1 (April 2014): In record types, property names that begin with a keyword (for example,
`undefinedHTML`) are now parsed correctly when JSDoc-style type expressions are enabled.
+ 0.7.0 (October 2013):
+ Repeatable type expressions other than name expressions (for example, `...function()`) are now
parsed and stringified correctly.
+ Type expressions that are both repeatable and either nullable or non-nullable (for example,
`...!number`) are now parsed and stringified correctly.
+ Name expressions are now parsed correctly when they match a property name in an object
instance (for example, `constructor`).
+ 0.6.0 (September 2013): Added support for the type expression `function[]` when JSDoc-style type
expressions are enabled.
+ 0.5.6 (April 2013):
+ For consistency with Google Closure Library, parentheses are no longer required around type
unions. (In previous versions, the parentheses could be omitted when JSDoc support was enabled.)
+ For consistency with Google Closure Library, you can now use postfix notation for the `?`
(nullable) and `!` (non-nullable) modifiers. For example, `?string` and `string?` are now
treated as equivalent.
+ String literals and numeric literals are now allowed as property names within name
expressions. For example, the name expression `Foo."bar"` is now parsed correctly.
+ 0.5.5 (April 2013): Corrected a parsing issue with name expressions that end with a value enclosed
in parentheses.
+ 0.5.4 (April 2013):
+ Repeatable literals (for example, `...*`) are now parsed correctly.
+ When JSDoc-style type expressions are enabled, a name expression can now contain a value
enclosed in parentheses at the end of the name expression (for example, `MyClass(2)`).
+ 0.5.3 (March 2013): The `parse()` method now correctly parses name expressions that contain
hyphens.
+ 0.5.2 (March 2013): The `parse()` method now correctly parses function types when JSDoc-style type
expressions are enabled.
+ 0.5.1 (March 2013): Newlines and extra spaces are now removed from type expressions before they
are parsed.
+ 0.5.0 (March 2013):
+ The `parse()` method's `lenient` option has been renamed to `jsdoc`. **Note**: This change is
not backwards-compatible with previous versions.
+ The `stringify()` method now accepts `cssClass` and `links` options, which you can use to
add HTML links to a type expression.
+ 0.4.3 (March 2013):
+ The `stringify()` method no longer caches HTML-safe type expressions as if they were normal
type expressions.
+ The `stringify()` method's options parameter may now include an `options.restringify`
property, and the behavior of the `options.useCache` property has changed.
+ 0.4.2 (March 2013):
+ When lenient parsing is enabled, name expressions can now contain the characters `:` and `/`.
+ When lenient parsing is enabled, a name expression followed by `[]` (for example, `string[]`)
will be interpreted as a type application with the expression `Array` (for example,
`Array.<string>`).
+ 0.4.1 (March 2013):
+ The `parse()` and `stringify()` methods now honor all of the specified options.
+ When lenient parsing is enabled, name expressions can now contain a reserved word.
+ 0.4.0 (March 2013):
+ Catharsis now supports a lenient parsing option that can parse several kinds of malformed type
expressions. See the documentation for details.
+ The objects containing parse results are now frozen.
+ The objects containing parse results now have two non-enumerable properties:
+ `lenient`: A boolean indicating whether the type expression was parsed in lenient mode.
+ `typeExpression`: A string containing the original type expression.
+ The `stringify()` method now honors the `useCache` option. If a parsed type includes a
`typeExpression` property, and `useCache` is not set to `false`, the stringified type will be
identical to the original type expression.
+ 0.3.1 (March 2013): Type expressions that begin with a reserved word, such as `integer`, are now
parsed correctly.
+ 0.3.0 (March 2013):
+ The `parse()` and `stringify()` methods are now synchronous, and the `parseSync()` and
`stringifySync()` methods have been removed. **Note**: This change is not backwards-compatible
with previous versions.
+ The parse results now use a significantly different format from previous versions. The new
format is more expressive and is similar, but not identical, to the format used by the
[doctrine](https://github.com/Constellation/doctrine) parser. **Note**: This change is not
backwards-compatible with previous versions.
+ Name expressions that contain a reserved word now include a `reservedWord: true` property.
+ Union types that are optional or nullable, or that can be passed a variable number of times,
are now parsed and stringified correctly.
+ Optional function types and record types are now parsed and stringified correctly.
+ Function types now longer include `new` or `this` properties unless the properties are defined
in the type expression. In addition, the `new` and `this` properties can now use any type
expression.
+ In record types, the key for a field type can now use any type expression.
+ Standalone single-character literals, such as ALL (`*`), are now parsed and stringified
correctly.
+ `null` and `undefined` literals with additional properties, such as `repeatable`, are now
stringified correctly.
+ 0.2.0 (November 2012):
+ Added `stringify()` and `stringifySync()` methods, which convert a parsed type to a type
expression.
+ Simplified the parse results for function signatures. **Note**: This change is not
backwards-compatible with previous versions.
+ Corrected minor errors in README.md.
+ 0.1.1 (November 2012): Added `opts` argument to `parse()` and `parseSync()` methods. **Note**: The
change to `parse()` is not backwards-compatible with previous versions.
+ 0.1.0 (November 2012): Initial release.
## License ##
[MIT license](https://github.com/hegemonic/catharsis/blob/master/LICENSE).

View File

@ -1,55 +0,0 @@
#!/usr/bin/env node
'use strict';
// Command-line tool that parses a type expression and dumps a JSON version of the parse tree.
var catharsis = require('../catharsis');
var path = require('path');
var util = require('util');
var command = path.basename(process.argv[1]);
var typeExpression = process.argv[2];
var opts = {
describe: false,
jsdoc: false
};
var parsedType;
function usage() {
console.log(util.format('Usage:\n %s typeExpression [--jsdoc] [--describe]', command));
}
function done(err) {
/*eslint no-process-exit: 0 */
process.exit(err === undefined ? 0 : err);
}
process.argv.slice(3).forEach(function(arg) {
var parsedArg = arg.replace(/^\-{2}/, '');
if (opts[parsedArg] !== undefined) {
opts[parsedArg] = true;
} else {
console.error('Unknown option "%s"', arg);
usage();
done(1);
}
});
if (!typeExpression) {
usage();
done(1);
} else {
try {
parsedType = catharsis.parse(typeExpression, opts);
if (opts.describe) {
parsedType = catharsis.describe(parsedType);
}
} catch (e) {
console.error(util.format('Unable to parse "%s" (exception follows):', typeExpression));
console.error(e.stack || e.message);
done(1);
}
console.log(JSON.stringify(parsedType, null, 2));
done();
}

View File

@ -1,166 +0,0 @@
/**
* Catharsis
* A parser for Google Closure Compiler type expressions, powered by PEG.js.
*
* @author Jeff Williams <jeffrey.l.williams@gmail.com>
* @license MIT License <http://opensource.org/licenses/mit-license.php/>
*/
'use strict';
var describe = require('./lib/describe');
var parse = require('./lib/parser').parse;
var stringify = require('./lib/stringify');
var typeExpressionCache = {
normal: {},
jsdoc: {}
};
var parsedTypeCache = {
normal: {},
htmlSafe: {}
};
var descriptionCache = {
normal: {}
};
function getTypeExpressionCache(options) {
if (options.useCache === false) {
return null;
} else if (options.jsdoc === true) {
return typeExpressionCache.jsdoc;
} else {
return typeExpressionCache.normal;
}
}
function getParsedTypeCache(options) {
if (options.useCache === false || options.links !== null || options.links !== undefined) {
return null;
} else if (options.htmlSafe === true) {
return parsedTypeCache.htmlSafe;
} else {
return parsedTypeCache.normal;
}
}
function getDescriptionCache(options) {
if (options.useCache === false || options.links !== null || options.links !== undefined) {
return null;
} else {
return descriptionCache.normal;
}
}
// can't return the original if any of the following are true:
// 1. restringification was requested
// 2. htmlSafe option was requested
// 3. links option was provided
// 4. typeExpression property is missing
function canReturnOriginalExpression(parsedType, options) {
return options.restringify !== true && options.htmlSafe !== true &&
(options.links === null || options.links === undefined) &&
Object.prototype.hasOwnProperty.call(parsedType, 'typeExpression');
}
// Add non-enumerable properties to a result object, then freeze it.
function prepareFrozenObject(obj, expr, options) {
Object.defineProperty(obj, 'jsdoc', {
value: options.jsdoc === true ? true : false
});
if (expr) {
Object.defineProperty(obj, 'typeExpression', {
value: expr
});
}
return Object.freeze(obj);
}
function cachedParse(expr, options) {
var cache = getTypeExpressionCache(options);
var parsedType;
if (cache && Object.prototype.hasOwnProperty.call(cache, expr)) {
return cache[expr];
} else {
parsedType = parse(expr, options);
parsedType = prepareFrozenObject(parsedType, expr, options);
if (cache) {
cache[expr] = parsedType;
}
return parsedType;
}
}
function cachedStringify(parsedType, options) {
var cache = getParsedTypeCache(options);
var json;
if (canReturnOriginalExpression(parsedType, options)) {
return parsedType.typeExpression;
} else if (cache) {
json = JSON.stringify(parsedType);
cache[json] = cache[json] || stringify(parsedType, options);
return cache[json];
} else {
return stringify(parsedType, options);
}
}
function cachedDescribe(parsedType, options) {
var cache = getDescriptionCache(options);
var json;
var result;
if (cache) {
json = JSON.stringify(parsedType);
cache[json] = cache[json] || describe(parsedType, options);
return cache[json];
} else {
result = describe(parsedType, options);
result = prepareFrozenObject(result, null, options);
return result;
}
}
function Catharsis() {
this.Types = require('./lib/types');
}
Catharsis.prototype.parse = function(typeExpr, options) {
options = options || {};
typeExpr = typeExpr.replace(/[\r\n]/g, '')
.replace(/\s+/g, ' ')
.trim();
return cachedParse(typeExpr, options);
};
Catharsis.prototype.stringify = function(parsedType, options) {
var result;
options = options || {};
result = cachedStringify(parsedType, options);
if (options.validate) {
this.parse(result, options);
}
return result;
};
Catharsis.prototype.describe = function(parsedType, options) {
options = options || {};
return cachedDescribe(parsedType, options);
};
module.exports = new Catharsis();

File diff suppressed because one or more lines are too long

View File

@ -1,265 +0,0 @@
'use strict';
var Types = require('./types');
function Stringifier(options) {
this._options = options || {};
this._options.linkClass = this._options.linkClass || this._options.cssClass;
// in a list of function signature params, repeatable params are stringified differently
this._inFunctionSignatureParams = false;
}
Stringifier.prototype.applications = function(applications) {
var result = '';
var strings = [];
if (!applications) {
return result;
}
for (var i = 0, l = applications.length; i < l; i++) {
strings.push(this.type(applications[i]));
}
if (this._options.htmlSafe) {
result = '.&lt;';
} else {
result = '.<';
}
result += strings.join(', ') + '>';
return result;
};
Stringifier.prototype.elements = function(elements) {
var result = '';
var strings = [];
if (!elements) {
return result;
}
for (var i = 0, l = elements.length; i < l; i++) {
strings.push(this.type(elements[i]));
}
result = '(' + strings.join('|') + ')';
return result;
};
Stringifier.prototype.name = function(name) {
return name || '';
};
Stringifier.prototype['new'] = function(funcNew) {
return funcNew ? 'new:' + this.type(funcNew) : '';
};
Stringifier.prototype.nullable = function(nullable) {
switch (nullable) {
case true:
return '?';
case false:
return '!';
default:
return '';
}
};
Stringifier.prototype.optional = function(optional) {
if (optional === true) {
return '=';
} else {
return '';
}
};
Stringifier.prototype.params = function(params) {
var result = '';
var strings = [];
if (!params || params.length === 0) {
return result;
}
for (var i = 0, l = params.length; i < l; i++) {
strings.push(this.type(params[i]));
}
result = strings.join(', ');
return result;
};
Stringifier.prototype.result = function(result) {
return result ? ': ' + this.type(result) : '';
};
Stringifier.prototype['this'] = function(funcThis) {
return funcThis ? 'this:' + this.type(funcThis) : '';
};
Stringifier.prototype.type = function(type) {
var typeString = '';
if (!type) {
return typeString;
}
switch(type.type) {
case Types.AllLiteral:
typeString = this._formatNameAndType(type, '*');
break;
case Types.FunctionType:
typeString = this._signature(type);
break;
case Types.NullLiteral:
typeString = this._formatNameAndType(type, 'null');
break;
case Types.RecordType:
typeString = this._record(type);
break;
case Types.TypeApplication:
typeString = this.type(type.expression) + this.applications(type.applications);
break;
case Types.UndefinedLiteral:
typeString = this._formatNameAndType(type, 'undefined');
break;
case Types.TypeUnion:
typeString = this.elements(type.elements);
break;
case Types.UnknownLiteral:
typeString = this._formatNameAndType(type, '?');
break;
default:
typeString = this._formatNameAndType(type);
}
// add optional/nullable/repeatable modifiers
if (!this._options._ignoreModifiers) {
typeString = this._addModifiers(type, typeString);
}
return typeString;
};
Stringifier.prototype.stringify = Stringifier.prototype.type;
Stringifier.prototype.key = Stringifier.prototype.type;
Stringifier.prototype._record = function(type) {
var fields = this._recordFields(type.fields);
return '{' + fields.join(', ') + '}';
};
Stringifier.prototype._recordFields = function(fields) {
var field;
var keyAndValue;
var result = [];
if (!fields) {
return result;
}
for (var i = 0, l = fields.length; i < l; i++) {
field = fields[i];
keyAndValue = this.key(field.key);
keyAndValue += field.value ? ': ' + this.type(field.value) : '';
result.push(keyAndValue);
}
return result;
};
function combineNameAndType(nameString, typeString) {
var separator = (nameString && typeString) ? ':' : '';
return nameString + separator + typeString;
}
// Adds optional, nullable, and repeatable modifiers if necessary.
Stringifier.prototype._addModifiers = function(type, typeString) {
var combined;
var open = '';
var close = '';
var optional = '';
if (type.repeatable) {
open = this._inFunctionSignatureParams ? '...[' : '...';
close = this._inFunctionSignatureParams ? ']' : '';
}
combined = this.nullable(type.nullable) + combineNameAndType('', typeString);
optional = this.optional(type.optional);
return open + combined + close + optional;
};
Stringifier.prototype._addLinks = function(nameString) {
var openTag;
var linkClass = '';
var options = this._options;
if (options.links && Object.prototype.hasOwnProperty.call(options.links, nameString)) {
if (options.linkClass) {
linkClass = ' class="' + options.linkClass + '"';
}
openTag = '<a href="' + options.links[nameString] + '"' + linkClass + '>';
nameString = openTag + nameString + '</a>';
}
return nameString;
};
Stringifier.prototype._formatNameAndType = function(type, literal) {
var nameString = type.name || literal || '';
var typeString = type.type ? this.type(type.type) : '';
nameString = this._addLinks(nameString);
return combineNameAndType(nameString, typeString);
};
Stringifier.prototype._signature = function(type) {
var param;
var prop;
var signature;
var params = [];
// these go within the signature's parens, in this order
var props = [
'new',
'this',
'params'
];
this._inFunctionSignatureParams = true;
for (var i = 0, l = props.length; i < l; i++) {
prop = props[i];
param = this[prop](type[prop]);
if (param.length > 0) {
params.push(param);
}
}
this._inFunctionSignatureParams = false;
signature = 'function(' + params.join(', ') + ')';
signature += this.result(type.result);
return signature;
};
module.exports = function(type, options) {
return new Stringifier(options).stringify(type);
};

View File

@ -1,24 +0,0 @@
'use strict';
module.exports = Object.freeze({
// `*`
AllLiteral: 'AllLiteral',
// like `blah` in `{blah: string}`
FieldType: 'FieldType',
// like `function(string): string`
FunctionType: 'FunctionType',
// any string literal, such as `string` or `My.Namespace`
NameExpression: 'NameExpression',
// null
NullLiteral: 'NullLiteral',
// like `{foo: string}`
RecordType: 'RecordType',
// like `Array.<string>`
TypeApplication: 'TypeApplication',
// like `(number|string)`
TypeUnion: 'TypeUnion',
// undefined
UndefinedLiteral: 'UndefinedLiteral',
// `?`
UnknownLiteral: 'UnknownLiteral'
});

View File

@ -1,63 +0,0 @@
{
"version": "0.8.3",
"name": "catharsis",
"description": "A JavaScript parser for Google Closure Compiler and JSDoc type expressions.",
"author": {
"name": "Jeff Williams",
"email": "jeffrey.l.williams@gmail.com"
},
"repository": {
"type": "git",
"url": "https://github.com/hegemonic/catharsis"
},
"bugs": {
"url": "https://github.com/hegemonic/catharsis/issues"
},
"main": "catharsis.js",
"dependencies": {
"underscore-contrib": "~0.3.0"
},
"devDependencies": {
"mocha": "~1.21.3",
"pegjs": "https://github.com/dmajda/pegjs/tarball/76cc5d55b47ff3d5bbe1d435c6f843e2688cb729",
"should": "~4.0.4",
"tv4": "https://github.com/geraintluff/tv4/tarball/eb7561072d44943306e5fd88b55b4a4c98cb6c75",
"uglify-js": "~2.4.15"
},
"engines": {
"node": ">= 0.8"
},
"scripts": {
"build": "pegjs ./lib/parser.pegjs",
"prepublish": "pegjs ./lib/parser.pegjs; ./node_modules/.bin/uglifyjs ./lib/parser.js -o ./lib/parser.js",
"test": "mocha"
},
"licenses": [
{
"type": "MIT",
"url": "http://github.com/hegemonic/catharsis/raw/master/LICENSE"
}
],
"gitHead": "8795105b00acf02d0af464ad3432f47b53744934",
"homepage": "https://github.com/hegemonic/catharsis",
"_id": "catharsis@0.8.3",
"_shasum": "573ad3d285dcfc373221712bd382edda61b3a5d5",
"_from": "catharsis@>=0.8.2 <0.9.0",
"_npmVersion": "1.4.28",
"_npmUser": {
"name": "hegemonic",
"email": "jeffrey.l.williams@gmail.com"
},
"maintainers": [
{
"name": "hegemonic",
"email": "jeffrey.l.williams@gmail.com"
}
],
"dist": {
"shasum": "573ad3d285dcfc373221712bd382edda61b3a5d5",
"tarball": "http://registry.npmjs.org/catharsis/-/catharsis-0.8.3.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.3.tgz"
}

View File

@ -1,166 +0,0 @@
.idea/
node_modules
#################
## Eclipse
#################
*.pydevproject
.project
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
#################
## Visual Studio
#################
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.vspscc
.builds
*.dotCover
## TODO: If you have NuGet Package Restore enabled, uncomment this
#packages/
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
# Visual Studio profiler
*.psess
*.vsp
# ReSharper is a .NET coding add-in
_ReSharper*
# Installshield output folder
[Ee]xpress
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish
# Others
[Bb]in
[Oo]bj
sql
TestResults
*.Cache
ClientBin
stylecop.*
~$*
*.dbmdl
Generated_Code #added for RIA/Silverlight projects
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
############
## Windows
############
# Windows image file caches
Thumbs.db
# Folder config file
Desktop.ini
#############
## Python
#############
*.py[co]
# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
#Translations
*.mo
#Mr Developer
.mr.developer.cfg
# Mac crap
.DS_Store

View File

@ -1,16 +0,0 @@
js2xmlparser is licensed under the MIT license:
> Copyright © 2012 Michael Kourlas and other contributors
>
> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
> documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
> rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
> persons to whom the Software is furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
> Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
> WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
> COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
> OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,196 +0,0 @@
# js2xmlparser #
## Overview ##
js2xmlparser is a Node.js module that parses JavaScript objects into XML.
## Features ##
Since XML is a data-interchange format, js2xmlparser is designed primarily for JSON-type objects, arrays and primitive
data types, like many of the other JavaScript to XML parsers currently available for Node.js.
However, js2xmlparser is capable of parsing any object, including native JavaScript objects such as Date and RegExp, by
taking advantage of each object's toString function. Functions are a special case where the return value of the function
itself is used instead of the toString function, if available.
js2xmlparser also supports a number of constructs unique to XML:
* attributes (through a unique attribute property in objects)
* mixed content (through a unique value property in objects)
* multiple elements with the same name (through arrays)
js2xmlparser can also pretty-print the XML it outputs with the option of customizing the indent string.
## Installation ##
The easiest way to install js2xmlparser is to use npm: `npm install js2xmlparser`.
Alternatively, you may download the source from GitHub and copy it to a folder named "js2xmlparser" within your
"node_modules" directory.
## Usage ##
The js2xmlparser module contains one function which takes the following arguments:
* `root` - the XML root element's name (string, mandatory)
* `data` - the data to be converted to XML; while the data object can contain arrays, it cannot itself be an array
(object or JSON string, mandatory)
* `options` - module options (object, optional)
* `declaration` - XML declaration options (object, optional)
* `include` - specifies whether an XML declaration should be included (boolean, optional, default: true)
* `encoding` - value of XML encoding attribute in declaration; a value of null represents no encoding attribute
(string, optional, default: "UTF-8")
* `attributeString` - the name of the property representing an element's attributes; note that any property with a
name equal to the attribute string is ignored except in the context of XML attributes (string, optional, default:
"@")
* `valueString` - the name of the property representing an element's value; note that any property with a name equal
to the value string is ignored except in the context of supplying a value for a tag containing attributes (string,
optional, default: "#")
* `prettyPrinting` - pretty-printing options (object, optional)
* `enabled` - specifies whether pretty-printing is enabled (boolean, optional, default: true)
* `indentString` - indent string (string, optional, default: "\t")
* `convertMap` - maps object types (as given by the `Object.prototype.toString.call` method) to functions to convert
those objects to a particular string representation; `*` can be used as a wildcard for all types of objects
(object, optional, default: {})
* `useCDATA` - specifies whether strings should be enclosed in CDATA tags; otherwise, illegal XML characters will
be escaped (boolean, optional, default: false)
## Examples ##
The following example illustrates the basic usage of js2xmlparser:
var js2xmlparser = require("js2xmlparser");
var data = {
"firstName": "John",
"lastName": "Smith"
};
console.log(js2xmlparser("person", data));
> <?xml version="1.0" encoding="UTF-8"?>
> <person>
> <firstName>John</firstName>
> <lastName>Smith</lastName>
> </person>
Here's a more complex example that builds on the first:
var js2xmlparser = require("js2xmlparser");
var data = {
"@": {
"type": "individual"
},
"firstName": "John",
"lastName": "Smith",
"dateOfBirth": new Date(1964, 7, 26),
"address": {
"@": {
"type": "home"
},
"streetAddress": "3212 22nd St",
"city": "Chicago",
"state": "Illinois",
"zip": 10000
},
"phone": [
{
"@": {
"type": "home"
},
"#": "123-555-4567"
},
{
"@": {
"type": "cell"
},
"#": "456-555-7890"
}
],
"email": function() {return "john@smith.com";},
"notes": "John's profile is not complete."
};
console.log(js2xmlparser("person", data));
> <?xml version="1.0" encoding="UTF-8"?>
> <person type="individual">
> <firstName>John</firstName>
> <lastName>Smith</lastName>
> <dateOfBirth>Wed Aug 26 1964 00:00:00 GMT-0400 (Eastern Daylight Time)</dateOfBirth>
> <address type="home">
> <streetAddress>3212 22nd St</streetAddress>
> <city>Chicago</city>
> <state>Illinois</state>
> <zip>10000</zip>
> </address>
> <phone type="home">123-555-4567</phone>
> <phone type="cell">456-555-7890</phone>
> <email>john@smith.com</email>
> <notes>John&apos;s profile is not complete.</notes>
> </person>
Here's an example that uses the convert map feature:
var js2xmlparser = require("js2xmlparser");
var data = {
"email": function() {return "john@smith.com";},
"dateOfBirth": new Date(1964, 7, 26)
}
var options = {
convertMap: {
"[object Date]": function(date) {
return date.toISOString();
},
"[object Function]": function(func) {
return func.toString();
}
}
};
console.log(js2xmlparser("person", data, options));
> <?xml version="1.0" encoding="UTF-8"?>
> <person>
> <email>function () {return &quot;john@smith.com&quot;;}</email>
> <dateOfBirth>1964-08-26T04:00:00.000Z</dateOfBirth>
> </person>
Here's an example that wraps strings in CDATA tags instead of escaping invalid characters.
var js2xmlparser = require("js2xmlparser");
var data = {
"notes": {
"@": {
"type": "status"
},
"#": "John's profile is not complete."
}
};
var options = {
useCDATA: true
};
console.log(js2xmlparser("person", data, options));
> <?xml version="1.0" encoding="UTF-8"?>
> <person>
> <notes type="status"><![CDATA[John's profile is not complete.]]></notes>
> </person>
## Tests ##
js2xmlparser comes with a set of tests that evaluate and verify the package's core functionality. To run the tests:
* Install the test dependencies with `npm install`.
* Run the tests with `mocha`.
## License ##
js2xmlparser is licensed under the [MIT license](http://opensource.org/licenses/MIT). Please see the LICENSE.md file
for more information.

View File

@ -1,116 +0,0 @@
/* jshint node:true */
/**
* js2xmlparser
* Copyright © 2012 Michael Kourlas and other contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
(function() {
"use strict";
var js2xmlparser = require("../lib/js2xmlparser.js");
console.log("EXAMPLE 1");
console.log("=========");
var example1 = {
"firstName": "John",
"lastName": "Smith"
};
console.log(js2xmlparser("person", example1));
console.log();
console.log("EXAMPLE 2");
console.log("=========");
var example2 = {
"@": {
"type": "individual"
},
"firstName": "John",
"lastName": "Smith",
"dateOfBirth": new Date(1964, 7, 26),
"address": {
"@": {
"type": "home"
},
"streetAddress": "3212 22nd St",
"city": "Chicago",
"state": "Illinois",
"zip": 10000
},
"phone": [
{
"@": {
"type": "home"
},
"#": "123-555-4567"
},
{
"@": {
"type": "cell"
},
"#": "456-555-7890"
}
],
"email": function() {return "john@smith.com";},
"notes": "John's profile is not complete."
};
console.log(js2xmlparser("person", example2));
console.log();
console.log("EXAMPLE 3");
console.log("=========");
var example3 = {
"email": function() {return "john@smith.com";},
"dateOfBirth": new Date(1964, 7, 26)
};
var example3Options = {
convertMap: {
"[object Date]": function(date) {
return date.toISOString();
},
"[object Function]": function(func) {
return func.toString();
}
}
};
console.log(js2xmlparser("person", example3, example3Options));
console.log();
console.log("EXAMPLE 4");
console.log("=========");
var example4 = {
"notes": {
"@": {
"type": "status"
},
"#":"John's profile is not complete."
}
};
var example4Options = {
useCDATA: true
};
console.log(js2xmlparser("person", example4, example4Options));
})();

View File

@ -1,328 +0,0 @@
/* jshint node:true */
/**
* js2xmlparser
* Copyright © 2012 Michael Kourlas and other contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
(function() {
"use strict";
var xmlDeclaration = true;
var xmlVersion = "1.0";
var xmlEncoding = "UTF-8";
var attributeString = "@";
var valueString = "#";
var prettyPrinting = true;
var indentString = "\t";
var convertMap = {};
var useCDATA = false;
module.exports = function (root, data, options) {
return toXML(init(root, data, options));
};
// Initialization
var init = function(root, data, options) {
// Set option defaults
setOptionDefaults();
// Error checking for root element
if (typeof root !== "string") {
throw new Error("root element must be a string");
}
else if (root === "") {
throw new Error("root element cannot be empty");
}
// Error checking and variable initialization for options
if (typeof options === "object" && options !== null) {
if ("declaration" in options) {
if ("include" in options.declaration) {
if (typeof options.declaration.include === "boolean") {
xmlDeclaration = options.declaration.include;
}
else {
throw new Error("declaration.include option must be a boolean");
}
}
if ("encoding" in options.declaration) {
if (typeof options.declaration.encoding === "string" || options.declaration.encoding === null) {
xmlEncoding = options.declaration.encoding;
}
else {
throw new Error("declaration.encoding option must be a string or null");
}
}
}
if ("attributeString" in options) {
if (typeof options.attributeString === "string") {
attributeString = options.attributeString;
}
else {
throw new Error("attributeString option must be a string");
}
}
if ("valueString" in options) {
if (typeof options.valueString === "string") {
valueString = options.valueString;
}
else {
throw new Error("valueString option must be a string");
}
}
if ("prettyPrinting" in options) {
if ("enabled" in options.prettyPrinting) {
if (typeof options.prettyPrinting.enabled === "boolean") {
prettyPrinting = options.prettyPrinting.enabled;
}
else {
throw new Error("prettyPrinting.enabled option must be a boolean");
}
}
if ("indentString" in options.prettyPrinting) {
if (typeof options.prettyPrinting.indentString === "string") {
indentString = options.prettyPrinting.indentString;
}
else {
throw new Error("prettyPrinting.indentString option must be a string");
}
}
}
if ("convertMap" in options) {
if (Object.prototype.toString.call(options.convertMap) === "[object Object]") {
convertMap = options.convertMap;
}
else {
throw new Error("convertMap option must be an object");
}
}
if ("useCDATA" in options) {
if (typeof options.useCDATA === "boolean") {
useCDATA = options.useCDATA;
}
else {
throw new Error("useCDATA option must be a boolean");
}
}
}
// Error checking and variable initialization for data
if (typeof data !== "string" && typeof data !== "object" && typeof data !== "number" &&
typeof data !== "boolean" && data !== null) {
throw new Error("data must be an object (excluding arrays) or a JSON string");
}
if (data === null) {
throw new Error("data must be an object (excluding arrays) or a JSON string");
}
if (Object.prototype.toString.call(data) === "[object Array]") {
throw new Error("data must be an object (excluding arrays) or a JSON string");
}
if (typeof data === "string") {
data = JSON.parse(data);
}
var tempData = {};
tempData[root] = data; // Add root element to object
return tempData;
};
// Convert object to XML
var toXML = function(object) {
// Initialize arguments, if necessary
var xml = arguments[1] || "";
var level = arguments[2] || 0;
var i = null;
var tempObject = {};
for (var property in object) {
if (object.hasOwnProperty(property)) {
// Element name cannot start with a number
var elementName = property;
if (/^\d/.test(property)) {
elementName = "_" + property;
}
// Arrays
if (Object.prototype.toString.call(object[property]) === "[object Array]") {
// Create separate XML elements for each array element
for (i = 0; i < object[property].length; i++) {
tempObject = {};
tempObject[property] = object[property][i];
xml = toXML(tempObject, xml, level);
}
}
// JSON-type objects with properties
else if (Object.prototype.toString.call(object[property]) === "[object Object]") {
xml += addIndent("<" + elementName, level);
// Add attributes
var lengthExcludingAttributes = Object.keys(object[property]).length;
if (Object.prototype.toString.call(object[property][attributeString]) === "[object Object]") {
lengthExcludingAttributes -= 1;
for (var attribute in object[property][attributeString]) {
if (object[property][attributeString].hasOwnProperty(attribute)) {
xml += " " + attribute + "=\"" +
toString(object[property][attributeString][attribute], true) + "\"";
}
}
}
else if (typeof object[property][attributeString] !== "undefined") {
// Fix for the case where an object contains a single property with the attribute string as its
// name, but this property contains no attributes; in that case, lengthExcludingAttributes
// should be set to zero to ensure that the object is considered an empty object for the
// purposes of the following if statement.
lengthExcludingAttributes -= 1;
}
if (lengthExcludingAttributes === 0) { // Empty object
xml += addBreak("/>");
}
else if (lengthExcludingAttributes === 1 && valueString in object[property]) { // Value string only
xml += addBreak(">" + toString(object[property][valueString], false) + "</" + elementName + ">");
}
else { // Object with properties
xml += addBreak(">");
// Create separate object for each property and pass to this function
for (var subProperty in object[property]) {
if (object[property].hasOwnProperty(subProperty) && subProperty !== attributeString && subProperty !== valueString) {
tempObject = {};
tempObject[subProperty] = object[property][subProperty];
xml = toXML(tempObject, xml, level + 1);
}
}
xml += addBreak(addIndent("</" + elementName + ">", level));
}
}
// Everything else
else {
xml += addBreak(addIndent("<" + elementName + ">" + toString(object[property], false) + "</" +
elementName + ">", level));
}
}
}
// Finalize XML at end of process
if (level === 0) {
// Strip trailing whitespace
xml = xml.replace(/\s+$/g, "");
// Add XML declaration
if (xmlDeclaration) {
if (xmlEncoding === null) {
xml = addBreak("<?xml version=\"" + xmlVersion + "\"?>") + xml;
}
else {
xml = addBreak("<?xml version=\"" + xmlVersion + "\" encoding=\"" + xmlEncoding + "\"?>") + xml;
}
}
}
return xml;
};
// Add indenting to data for pretty printing
var addIndent = function(data, level) {
if (prettyPrinting) {
var indent = "";
for (var i = 0; i < level; i++) {
indent += indentString;
}
data = indent + data;
}
return data;
};
// Add line break to data for pretty printing
var addBreak = function(data) {
return prettyPrinting ? data + "\n" : data;
};
// Convert anything into a valid XML string representation
var toString = function(data, isAttribute) {
// Recursive function used to handle nested functions
var functionHelper = function(data) {
if (Object.prototype.toString.call(data) === "[object Function]") {
return functionHelper(data());
}
else {
return data;
}
};
// Convert map
if (Object.prototype.toString.call(data) in convertMap) {
data = convertMap[Object.prototype.toString.call(data)](data);
}
else if ("*" in convertMap) {
data = convertMap["*"](data);
}
// Functions
else if (Object.prototype.toString.call(data) === "[object Function]") {
data = functionHelper(data());
}
// Empty objects
else if (Object.prototype.toString.call(data) === "[object Object]" && Object.keys(data).length === 0) {
data = "";
}
// Cast data to string
if (typeof data !== "string") {
data = (data === null || typeof data === "undefined") ? "" : data.toString();
}
// Output as CDATA instead of escaping if option set (and only if not an attribute value)
if (useCDATA && !isAttribute) {
data = "<![CDATA[" + data.replace(/]]>/gm, "]]]]><![CDATA[>") + "]]>";
}
else {
// Escape illegal XML characters
data = data.replace(/&/gm, "&amp;")
.replace(/</gm, "&lt;")
.replace(/>/gm, "&gt;")
.replace(/"/gm, "&quot;")
.replace(/'/gm, "&apos;");
}
return data;
};
// Revert options back to their default settings
var setOptionDefaults = function() {
useCDATA = false;
convertMap = {};
xmlDeclaration = true;
xmlVersion = "1.0";
xmlEncoding = "UTF-8";
attributeString = "@";
valueString = "#";
prettyPrinting = true;
indentString = "\t";
};
})();

View File

@ -1,54 +0,0 @@
{
"name": "js2xmlparser",
"description": "Parses JavaScript objects into XML",
"keywords": [
"convert",
"converter",
"js",
"json",
"object",
"objects",
"parse",
"parser",
"xml"
],
"homepage": "http://www.kourlas.net",
"version": "0.1.5",
"author": {
"name": "Michael Kourlas",
"email": "michael@kourlas.net"
},
"main": "./lib/js2xmlparser.js",
"repository": {
"type": "git",
"url": "git://github.com/michaelkourlas/node-js2xmlparser.git"
},
"devDependencies": {
"mocha": "*",
"should": "*"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/michaelkourlas/node-js2xmlparser/issues"
},
"_id": "js2xmlparser@0.1.5",
"_shasum": "b42b3ac5a74bb282ff06cc93dfa67fb98a22959d",
"_from": "js2xmlparser@>=0.1.0 <0.2.0",
"_npmVersion": "1.4.9",
"_npmUser": {
"name": "michaelkourlas",
"email": "michael@kourlas.net"
},
"maintainers": [
{
"name": "michaelkourlas",
"email": "michaelkourlas@gmail.com"
}
],
"dist": {
"shasum": "b42b3ac5a74bb282ff06cc93dfa67fb98a22959d",
"tarball": "http://registry.npmjs.org/js2xmlparser/-/js2xmlparser-0.1.5.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-0.1.5.tgz"
}

View File

@ -1,2 +0,0 @@
.git*
test/

View File

@ -1,19 +0,0 @@
Copyright (c) 2011-2014, Christopher Jeffrey (https://github.com/chjj/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,12 +0,0 @@
all:
@cp lib/marked.js marked.js
@uglifyjs -o marked.min.js marked.js
clean:
@rm marked.js
@rm marked.min.js
bench:
@node test --bench
.PHONY: clean all

View File

@ -1,386 +0,0 @@
# marked
> A full-featured markdown parser and compiler, written in JavaScript. Built
> for speed.
[![NPM version](https://badge.fury.io/js/marked.png)][badge]
## Install
``` bash
npm install marked --save
```
## Usage
Minimal usage:
```js
var marked = require('marked');
console.log(marked('I am using __markdown__.'));
// Outputs: <p>I am using <strong>markdown</strong>.</p>
```
Example setting options with default values:
```js
var marked = require('marked');
marked.setOptions({
renderer: new marked.Renderer(),
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: true,
smartLists: true,
smartypants: false
});
console.log(marked('I am using __markdown__.'));
```
## marked(markdownString [,options] [,callback])
### markdownString
Type: `string`
String of markdown source to be compiled.
### options
Type: `object`
Hash of options. Can also be set using the `marked.setOptions` method as seen
above.
### callback
Type: `function`
Function called when the `markdownString` has been fully parsed when using
async highlighting. If the `options` argument is omitted, this can be used as
the second argument.
## Options
### highlight
Type: `function`
A function to highlight code blocks. The first example below uses async highlighting with
[node-pygmentize-bundled][pygmentize], and the second is a synchronous example using
[highlight.js][highlight]:
```js
var marked = require('marked');
var markdownString = '```js\n console.log("hello"); \n```';
// Async highlighting with pygmentize-bundled
marked.setOptions({
highlight: function (code, lang, callback) {
require('pygmentize-bundled')({ lang: lang, format: 'html' }, code, function (err, result) {
callback(err, result.toString());
});
}
});
// Using async version of marked
marked(markdownString, function (err, content) {
if (err) throw err;
console.log(content);
});
// Synchronous highlighting with highlight.js
marked.setOptions({
highlight: function (code) {
return require('highlight.js').highlightAuto(code).value;
}
});
console.log(marked(markdownString));
```
#### highlight arguments
`code`
Type: `string`
The section of code to pass to the highlighter.
`lang`
Type: `string`
The programming language specified in the code block.
`callback`
Type: `function`
The callback function to call when using an async highlighter.
### renderer
Type: `object`
Default: `new Renderer()`
An object containing functions to render tokens to HTML.
#### Overriding renderer methods
The renderer option allows you to render tokens in a custom manor. Here is an
example of overriding the default heading token rendering by adding an embedded anchor tag like on GitHub:
```javascript
var marked = require('marked');
var renderer = new marked.Renderer();
renderer.heading = function (text, level) {
var escapedText = text.toLowerCase().replace(/[^\w]+/g, '-');
return '<h' + level + '><a name="' +
escapedText +
'" class="anchor" href="#' +
escapedText +
'"><span class="header-link"></span></a>' +
text + '</h' + level + '>';
},
console.log(marked('# heading+', { renderer: renderer }));
```
This code will output the following HTML:
```html
<h1>
<a name="heading-" class="anchor" href="#heading-">
<span class="header-link"></span>
</a>
heading+
</h1>
```
#### Block level renderer methods
- code(*string* code, *string* language)
- blockquote(*string* quote)
- html(*string* html)
- heading(*string* text, *number* level)
- hr()
- list(*string* body, *boolean* ordered)
- listitem(*string* text)
- paragraph(*string* text)
- table(*string* header, *string* body)
- tablerow(*string* content)
- tablecell(*string* content, *object* flags)
`flags` has the following properties:
```js
{
header: true || false,
align: 'center' || 'left' || 'right'
}
```
#### Inline level renderer methods
- strong(*string* text)
- em(*string* text)
- codespan(*string* code)
- br()
- del(*string* text)
- link(*string* href, *string* title, *string* text)
- image(*string* href, *string* title, *string* text)
### gfm
Type: `boolean`
Default: `true`
Enable [GitHub flavored markdown][gfm].
### tables
Type: `boolean`
Default: `true`
Enable GFM [tables][tables].
This option requires the `gfm` option to be true.
### breaks
Type: `boolean`
Default: `false`
Enable GFM [line breaks][breaks].
This option requires the `gfm` option to be true.
### pedantic
Type: `boolean`
Default: `false`
Conform to obscure parts of `markdown.pl` as much as possible. Don't fix any of
the original markdown bugs or poor behavior.
### sanitize
Type: `boolean`
Default: `false`
Sanitize the output. Ignore any HTML that has been input.
### smartLists
Type: `boolean`
Default: `true`
Use smarter list behavior than the original markdown. May eventually be
default with the old behavior moved into `pedantic`.
### smartypants
Type: `boolean`
Default: `false`
Use "smart" typograhic punctuation for things like quotes and dashes.
## Access to lexer and parser
You also have direct access to the lexer and parser if you so desire.
``` js
var tokens = marked.lexer(text, options);
console.log(marked.parser(tokens));
```
``` js
var lexer = new marked.Lexer(options);
var tokens = lexer.lex(text);
console.log(tokens);
console.log(lexer.rules);
```
## CLI
``` bash
$ marked -o hello.html
hello world
^D
$ cat hello.html
<p>hello world</p>
```
## Philosophy behind marked
The point of marked was to create a markdown compiler where it was possible to
frequently parse huge chunks of markdown without having to worry about
caching the compiled output somehow...or blocking for an unnecesarily long time.
marked is very concise and still implements all markdown features. It is also
now fully compatible with the client-side.
marked more or less passes the official markdown test suite in its
entirety. This is important because a surprising number of markdown compilers
cannot pass more than a few tests. It was very difficult to get marked as
compliant as it is. It could have cut corners in several areas for the sake
of performance, but did not in order to be exactly what you expect in terms
of a markdown rendering. In fact, this is why marked could be considered at a
disadvantage in the benchmarks above.
Along with implementing every markdown feature, marked also implements [GFM
features][gfmf].
## Benchmarks
node v0.8.x
``` bash
$ node test --bench
marked completed in 3411ms.
marked (gfm) completed in 3727ms.
marked (pedantic) completed in 3201ms.
robotskirt completed in 808ms.
showdown (reuse converter) completed in 11954ms.
showdown (new converter) completed in 17774ms.
markdown-js completed in 17191ms.
```
__Marked is now faster than Discount, which is written in C.__
For those feeling skeptical: These benchmarks run the entire markdown test suite 1000 times. The test suite tests every feature. It doesn't cater to specific aspects.
### Pro level
You also have direct access to the lexer and parser if you so desire.
``` js
var tokens = marked.lexer(text, options);
console.log(marked.parser(tokens));
```
``` js
var lexer = new marked.Lexer(options);
var tokens = lexer.lex(text);
console.log(tokens);
console.log(lexer.rules);
```
``` bash
$ node
> require('marked').lexer('> i am using marked.')
[ { type: 'blockquote_start' },
{ type: 'paragraph',
text: 'i am using marked.' },
{ type: 'blockquote_end' },
links: {} ]
```
## Running Tests & Contributing
If you want to submit a pull request, make sure your changes pass the test
suite. If you're adding a new feature, be sure to add your own test.
The marked test suite is set up slightly strangely: `test/new` is for all tests
that are not part of the original markdown.pl test suite (this is where your
test should go if you make one). `test/original` is only for the original
markdown.pl tests. `test/tests` houses both types of tests after they have been
combined and moved/generated by running `node test --fix` or `marked --test
--fix`.
In other words, if you have a test to add, add it to `test/new/` and then
regenerate the tests with `node test --fix`. Commit the result. If your test
uses a certain feature, for example, maybe it assumes GFM is *not* enabled, you
can add `.nogfm` to the filename. So, `my-test.text` becomes
`my-test.nogfm.text`. You can do this with any marked option. Say you want
line breaks and smartypants enabled, your filename should be:
`my-test.breaks.smartypants.text`.
To run the tests:
``` bash
cd marked/
node test
```
### Contribution and License Agreement
If you contribute code to this project, you are implicitly allowing your code
to be distributed under the MIT license. You are also implicitly verifying that
all code is your original work. `</legalese>`
## License
Copyright (c) 2011-2014, Christopher Jeffrey. (MIT License)
See LICENSE for more info.
[gfm]: https://help.github.com/articles/github-flavored-markdown
[gfmf]: http://github.github.com/github-flavored-markdown/
[pygmentize]: https://github.com/rvagg/node-pygmentize-bundled
[highlight]: https://github.com/isagalaev/highlight.js
[badge]: http://badge.fury.io/js/marked
[tables]: https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#wiki-tables
[breaks]: https://help.github.com/articles/github-flavored-markdown#newlines

View File

@ -1,187 +0,0 @@
#!/usr/bin/env node
/**
* Marked CLI
* Copyright (c) 2011-2013, Christopher Jeffrey (MIT License)
*/
var fs = require('fs')
, util = require('util')
, marked = require('../');
/**
* Man Page
*/
function help() {
var spawn = require('child_process').spawn;
var options = {
cwd: process.cwd(),
env: process.env,
setsid: false,
customFds: [0, 1, 2]
};
spawn('man',
[__dirname + '/../man/marked.1'],
options);
}
/**
* Main
*/
function main(argv, callback) {
var files = []
, options = {}
, input
, output
, arg
, tokens
, opt;
function getarg() {
var arg = argv.shift();
if (arg.indexOf('--') === 0) {
// e.g. --opt
arg = arg.split('=');
if (arg.length > 1) {
// e.g. --opt=val
argv.unshift(arg.slice(1).join('='));
}
arg = arg[0];
} else if (arg[0] === '-') {
if (arg.length > 2) {
// e.g. -abc
argv = arg.substring(1).split('').map(function(ch) {
return '-' + ch;
}).concat(argv);
arg = argv.shift();
} else {
// e.g. -a
}
} else {
// e.g. foo
}
return arg;
}
while (argv.length) {
arg = getarg();
switch (arg) {
case '--test':
return require('../test').main(process.argv.slice());
case '-o':
case '--output':
output = argv.shift();
break;
case '-i':
case '--input':
input = argv.shift();
break;
case '-t':
case '--tokens':
tokens = true;
break;
case '-h':
case '--help':
return help();
default:
if (arg.indexOf('--') === 0) {
opt = camelize(arg.replace(/^--(no-)?/, ''));
if (!marked.defaults.hasOwnProperty(opt)) {
continue;
}
if (arg.indexOf('--no-') === 0) {
options[opt] = typeof marked.defaults[opt] !== 'boolean'
? null
: false;
} else {
options[opt] = typeof marked.defaults[opt] !== 'boolean'
? argv.shift()
: true;
}
} else {
files.push(arg);
}
break;
}
}
function getData(callback) {
if (!input) {
if (files.length <= 2) {
return getStdin(callback);
}
input = files.pop();
}
return fs.readFile(input, 'utf8', callback);
}
return getData(function(err, data) {
if (err) return callback(err);
data = tokens
? JSON.stringify(marked.lexer(data, options), null, 2)
: marked(data, options);
if (!output) {
process.stdout.write(data + '\n');
return callback();
}
return fs.writeFile(output, data, callback);
});
}
/**
* Helpers
*/
function getStdin(callback) {
var stdin = process.stdin
, buff = '';
stdin.setEncoding('utf8');
stdin.on('data', function(data) {
buff += data;
});
stdin.on('error', function(err) {
return callback(err);
});
stdin.on('end', function() {
return callback(null, buff);
});
try {
stdin.resume();
} catch (e) {
callback(e);
}
}
function camelize(text) {
return text.replace(/(\w)-(\w)/g, function(_, a, b) {
return a + b.toUpperCase();
});
}
/**
* Expose / Entry Point
*/
if (!module.parent) {
process.title = 'marked';
main(process.argv.slice(), function(err, code) {
if (err) throw err;
return process.exit(code || 0);
});
} else {
module.exports = main;
}

View File

@ -1 +0,0 @@
module.exports = require('./lib/marked');

File diff suppressed because it is too large Load Diff

View File

@ -1,88 +0,0 @@
.ds q \N'34'
.TH marked 1 "2014-01-31" "v0.3.1" "marked.js"
.SH NAME
marked \- a javascript markdown parser
.SH SYNOPSIS
.B marked
[\-o \fI<output>\fP] [\-i \fI<input>\fP] [\-\-help]
[\-\-tokens] [\-\-pedantic] [\-\-gfm]
[\-\-breaks] [\-\-tables] [\-\-sanitize]
[\-\-smart\-lists] [\-\-lang\-prefix \fI<prefix>\fP]
[\-\-no\-etc...] [\-\-silent] [\fIfilename\fP]
.SH DESCRIPTION
.B marked
is a full-featured javascript markdown parser, built for speed. It also includes
multiple GFM features.
.SH EXAMPLES
.TP
cat in.md | marked > out.html
.TP
echo "hello *world*" | marked
.TP
marked \-o out.html in.md \-\-gfm
.TP
marked \-\-output="hello world.html" \-i in.md \-\-no-breaks
.SH OPTIONS
.TP
.BI \-o,\ \-\-output\ [\fIoutput\fP]
Specify file output. If none is specified, write to stdout.
.TP
.BI \-i,\ \-\-input\ [\fIinput\fP]
Specify file input, otherwise use last argument as input file. If no input file
is specified, read from stdin.
.TP
.BI \-t,\ \-\-tokens
Output a token stream instead of html.
.TP
.BI \-\-pedantic
Conform to obscure parts of markdown.pl as much as possible. Don't fix original
markdown bugs.
.TP
.BI \-\-gfm
Enable github flavored markdown.
.TP
.BI \-\-breaks
Enable GFM line breaks. Only works with the gfm option.
.TP
.BI \-\-tables
Enable GFM tables. Only works with the gfm option.
.TP
.BI \-\-sanitize
Sanitize output. Ignore any HTML input.
.TP
.BI \-\-smart\-lists
Use smarter list behavior than the original markdown.
.TP
.BI \-\-lang\-prefix\ [\fIprefix\fP]
Set the prefix for code block classes.
.TP
.BI \-\-no\-sanitize,\ \-no-etc...
The inverse of any of the marked options above.
.TP
.BI \-\-silent
Silence error output.
.TP
.BI \-h,\ \-\-help
Display help information.
.SH CONFIGURATION
For configuring and running programmatically.
.B Example
require('marked')('*foo*', { gfm: true });
.SH BUGS
Please report any bugs to https://github.com/chjj/marked.
.SH LICENSE
Copyright (c) 2011-2014, Christopher Jeffrey (MIT License).
.SH "SEE ALSO"
.BR markdown(1),
.BR node.js(1)

View File

@ -1,65 +0,0 @@
{
"name": "marked",
"description": "A markdown parser built for speed",
"author": {
"name": "Christopher Jeffrey"
},
"version": "0.3.2",
"main": "./lib/marked.js",
"bin": {
"marked": "./bin/marked"
},
"man": [
"./man/marked.1"
],
"preferGlobal": true,
"repository": {
"type": "git",
"url": "git://github.com/chjj/marked.git"
},
"homepage": "https://github.com/chjj/marked",
"bugs": {
"url": "http://github.com/chjj/marked/issues"
},
"license": "MIT",
"keywords": [
"markdown",
"markup",
"html"
],
"tags": [
"markdown",
"markup",
"html"
],
"devDependencies": {
"markdown": "*",
"showdown": "*",
"robotskirt": "*"
},
"scripts": {
"test": "node test",
"bench": "node test --bench"
},
"_id": "marked@0.3.2",
"dist": {
"shasum": "015db158864438f24a64bdd61a0428b418706d09",
"tarball": "http://registry.npmjs.org/marked/-/marked-0.3.2.tgz"
},
"_from": "marked@>=0.3.1 <0.4.0",
"_npmVersion": "1.4.3",
"_npmUser": {
"name": "chjj",
"email": "chjjeffrey@gmail.com"
},
"maintainers": [
{
"name": "chjj",
"email": "chjjeffrey@gmail.com"
}
],
"directories": {},
"_shasum": "015db158864438f24a64bdd61a0428b418706d09",
"_resolved": "https://registry.npmjs.org/marked/-/marked-0.3.2.tgz",
"readme": "ERROR: No README data found!"
}

View File

@ -1 +0,0 @@
See [http://taffydb.com](http://taffydb.com).

View File

@ -1,21 +0,0 @@
{
"name": "taffydb",
"version": "2.6.2",
"description": "An open-source library that brings database features into your JavaScript applications.",
"main": "taffy.js",
"repository": {
"type": "git",
"url": "git://github.com/hegemonic/taffydb.git"
},
"license": "BSD",
"readme": "See [http://taffydb.com](http://taffydb.com).",
"readmeFilename": "README.md",
"bugs": {
"url": "https://github.com/hegemonic/taffydb/issues"
},
"homepage": "https://github.com/hegemonic/taffydb",
"_id": "taffydb@2.6.2",
"_shasum": "3c549d2f5712d7d1d109ad6bb1a4084f1b7add4e",
"_from": "https://github.com/hegemonic/taffydb/tarball/master",
"_resolved": "https://github.com/hegemonic/taffydb/tarball/master"
}

View File

@ -1,84 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>taffy test</title>
<script src="./taffy.js"></script>
<script>
// recursive object compare, for future use
Object.prototype.equals = function (x) {
var p;
for(p in this) {
if(typeof(x[p])=='undefined') {return false;}
}
for(p in this) {
if (this[p]) {
switch(typeof(this[p])) {
case 'object':
if (! this[p].equals(x[p])) { return false; }
break;
case 'function':
if (typeof(x[p])=='undefined' ||
(p != 'equals' && this[p].toString() != x[p].toString())
){ return false; }
break;
default:
if (this[p] != x[p]) { return false; }
}
}
else {
if (x[p]){ return false; }
}
}
for(p in x) {
if(typeof(this[p])=='undefined') {return false;}
}
return true;
};
var key_name, data_val, friends_table, taffy_map;
friends_table = TAFFY([
{"id":1,"gender":"M","first":"John","last":"Smith","city":"Seattle, WA","status":"Active"},
{"id":2,"gender":"F","first":"Kelly","last":"Ruth","city":"Dallas, TX","status":"Active"},
{"id":3,"gender":"M","first":"Jeff","last":"Stevenson","city":"Washington, D.C.","status":"Active"},
{"id":4,"gender":"F","first":"Jennifer","last":"Gill","city":"Seattle, WA","status":"Active"}
]);
taffy_map = {
t_by_city : friends_table({city:"Seattle, WA"}),
t_by_id : friends_table({id:1}),
t_by_id_f : friends_table({id:'1'}),
t_by_name : friends_table({first:'John',last:'Smith'}),
kelly_by_id : friends_table({id:2}).first(),
kelly_last_name : friends_table({id:2}).first().last,
id_list : friends_table().select('id'),
city_list : friends_table().distinct('city'),
};
for ( key_name in taffy_map ){
if ( taffy_map.hasOwnProperty(key_name) ){
data_val = taffy_map[key_name];
console.warn(key_name, data_val);
if ( data_val.hasOwnProperty('get') ){
console.warn(JSON.stringify(data_val.get()));
}
console.warn('----------------');
}
}
</script>
</head>
<body>
<div>
Please open your javascript console to see test results
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
node_modules
npm-debug.log

View File

@ -1,399 +0,0 @@
/* wrench.js
*
* A collection of various utility functions I've found myself in need of
* for use with Node.js (http://nodejs.org/). This includes things like:
*
* - Recursively deleting directories in Node.js (Sync, not Async)
* - Recursively copying directories in Node.js (Sync, not Async)
* - Recursively chmoding a directory structure from Node.js (Sync, not Async)
* - Other things that I'll add here as time goes on. Shhhh...
*
* ~ Ryan McGrath (ryan [at] venodesigns.net)
*/
var fs = require("fs"),
_path = require("path");
/* wrench.readdirSyncRecursive("directory_path");
*
* Recursively dives through directories and read the contents of all the
* children directories.
*/
exports.readdirSyncRecursive = function(baseDir) {
baseDir = baseDir.replace(/\/$/, '');
var readdirSyncRecursive = function(baseDir) {
var files = [],
curFiles,
nextDirs,
isDir = function(fname){
return fs.statSync( _path.join(baseDir, fname) ).isDirectory();
},
prependBaseDir = function(fname){
return _path.join(baseDir, fname);
};
curFiles = fs.readdirSync(baseDir);
nextDirs = curFiles.filter(isDir);
curFiles = curFiles.map(prependBaseDir);
files = files.concat( curFiles );
while (nextDirs.length) {
files = files.concat( readdirSyncRecursive( _path.join(baseDir, nextDirs.shift()) ) );
}
return files;
};
// convert absolute paths to relative
var fileList = readdirSyncRecursive(baseDir).map(function(val){
return _path.relative(baseDir, val);
});
return fileList;
};
/* wrench.readdirRecursive("directory_path", function(error, files) {});
*
* Recursively dives through directories and read the contents of all the
* children directories.
*
* Asynchronous, so returns results/error in callback.
* Callback receives the of files in currently recursed directory.
* When no more directories are left, callback is called with null for all arguments.
*
*/
exports.readdirRecursive = function(baseDir, fn) {
baseDir = baseDir.replace(/\/$/, '');
var waitCount = 0;
function readdirRecursive(curDir) {
var files = [],
curFiles,
nextDirs,
prependcurDir = function(fname){
return _path.join(curDir, fname);
};
waitCount++;
fs.readdir(curDir, function(e, curFiles) {
waitCount--;
curFiles = curFiles.map(prependcurDir);
curFiles.forEach(function(it) {
waitCount++;
fs.stat(it, function(e, stat) {
waitCount--;
if (e) {
fn(e);
} else {
if (stat.isDirectory()) {
readdirRecursive(it);
}
}
if (waitCount == 0) {
fn(null, null);
}
});
});
fn(null, curFiles.map(function(val) {
// convert absolute paths to relative
return _path.relative(baseDir, val);
}));
if (waitCount == 0) {
fn(null, null);
}
});
};
readdirRecursive(baseDir);
};
/* wrench.rmdirSyncRecursive("directory_path", forceDelete, failSilent);
*
* Recursively dives through directories and obliterates everything about it. This is a
* Sync-function, which blocks things until it's done. No idea why anybody would want an
* Asynchronous version. :\
*/
exports.rmdirSyncRecursive = function(path, failSilent) {
var files;
try {
files = fs.readdirSync(path);
} catch (err) {
if(failSilent) return;
throw new Error(err.message);
}
/* Loop through and delete everything in the sub-tree after checking it */
for(var i = 0; i < files.length; i++) {
var currFile = fs.lstatSync(path + "/" + files[i]);
if(currFile.isDirectory()) // Recursive function back to the beginning
exports.rmdirSyncRecursive(path + "/" + files[i]);
else if(currFile.isSymbolicLink()) // Unlink symlinks
fs.unlinkSync(path + "/" + files[i]);
else // Assume it's a file - perhaps a try/catch belongs here?
fs.unlinkSync(path + "/" + files[i]);
}
/* Now that we know everything in the sub-tree has been deleted, we can delete the main
directory. Huzzah for the shopkeep. */
return fs.rmdirSync(path);
};
/* wrench.copyDirSyncRecursive("directory_to_copy", "new_directory_location", opts);
*
* Recursively dives through a directory and moves all its files to a new location. This is a
* Synchronous function, which blocks things until it's done. If you need/want to do this in
* an Asynchronous manner, look at wrench.copyDirRecursively() below.
*
* Note: Directories should be passed to this function without a trailing slash.
*/
exports.copyDirSyncRecursive = function(sourceDir, newDirLocation, opts) {
if (!opts || !opts.preserve) {
try {
if(fs.statSync(newDirLocation).isDirectory()) exports.rmdirSyncRecursive(newDirLocation);
} catch(e) { }
}
/* Create the directory where all our junk is moving to; read the mode of the source directory and mirror it */
var checkDir = fs.statSync(sourceDir);
try {
fs.mkdirSync(newDirLocation, checkDir.mode);
} catch (e) {
//if the directory already exists, that's okay
if (e.code !== 'EEXIST') throw e;
}
var files = fs.readdirSync(sourceDir);
for(var i = 0; i < files.length; i++) {
var currFile = fs.lstatSync(sourceDir + "/" + files[i]);
if(currFile.isDirectory()) {
/* recursion this thing right on back. */
exports.copyDirSyncRecursive(sourceDir + "/" + files[i], newDirLocation + "/" + files[i], opts);
} else if(currFile.isSymbolicLink()) {
var symlinkFull = fs.readlinkSync(sourceDir + "/" + files[i]);
fs.symlinkSync(symlinkFull, newDirLocation + "/" + files[i]);
} else {
/* At this point, we've hit a file actually worth copying... so copy it on over. */
var contents = fs.readFileSync(sourceDir + "/" + files[i]);
fs.writeFileSync(newDirLocation + "/" + files[i], contents);
}
}
};
/* wrench.chmodSyncRecursive("directory", filemode);
*
* Recursively dives through a directory and chmods everything to the desired mode. This is a
* Synchronous function, which blocks things until it's done.
*
* Note: Directories should be passed to this function without a trailing slash.
*/
exports.chmodSyncRecursive = function(sourceDir, filemode) {
var files = fs.readdirSync(sourceDir);
for(var i = 0; i < files.length; i++) {
var currFile = fs.lstatSync(sourceDir + "/" + files[i]);
if(currFile.isDirectory()) {
/* ...and recursion this thing right on back. */
exports.chmodSyncRecursive(sourceDir + "/" + files[i], filemode);
} else {
/* At this point, we've hit a file actually worth copying... so copy it on over. */
fs.chmod(sourceDir + "/" + files[i], filemode);
}
}
/* Finally, chmod the parent directory */
fs.chmod(sourceDir, filemode);
};
/* wrench.chownSyncRecursive("directory", uid, gid);
*
* Recursively dives through a directory and chowns everything to the desired user and group. This is a
* Synchronous function, which blocks things until it's done.
*
* Note: Directories should be passed to this function without a trailing slash.
*/
exports.chownSyncRecursive = function(sourceDir, uid, gid) {
var files = fs.readdirSync(sourceDir);
for(var i = 0; i < files.length; i++) {
var currFile = fs.lstatSync(sourceDir + "/" + files[i]);
if(currFile.isDirectory()) {
/* ...and recursion this thing right on back. */
exports.chownSyncRecursive(sourceDir + "/" + files[i], uid, gid);
} else {
/* At this point, we've hit a file actually worth chowning... so own it. */
fs.chownSync(sourceDir + "/" + files[i], uid, gid);
}
}
/* Finally, chown the parent directory */
fs.chownSync(sourceDir, uid, gid);
};
/* wrench.rmdirRecursive("directory_path", callback);
*
* Recursively dives through directories and obliterates everything about it.
*/
exports.rmdirRecursive = function rmdirRecursive(dir, clbk){
fs.readdir(dir, function(err, files){
if (err) return clbk(err);
(function rmFile(err){
if (err) return clbk(err);
var filename = files.shift();
if (filename === null || typeof filename == 'undefined')
return fs.rmdir(dir, clbk);
var file = dir+'/'+filename;
fs.stat(file, function(err, stat){
if (err) return clbk(err);
if (stat.isDirectory())
rmdirRecursive(file, rmFile);
else
fs.unlink(file, rmFile);
});
})();
});
};
/* wrench.copyDirRecursive("directory_to_copy", "new_location", callback);
*
* Recursively dives through a directory and moves all its files to a new
* location.
*
* Note: Directories should be passed to this function without a trailing slash.
*/
exports.copyDirRecursive = function copyDirRecursive(srcDir, newDir, clbk) {
fs.stat(newDir, function(err, newDirStat){
if (!err) return exports.rmdirRecursive(newDir, function(err){
copyDirRecursive(srcDir, newDir, clbk);
});
fs.stat(srcDir, function(err, srcDirStat){
if (err) return clbk(err);
fs.mkdir(newDir, srcDirStat.mode, function(err){
if (err) return clbk(err);
fs.readdir(srcDir, function(err, files){
if (err) return clbk(err);
(function copyFiles(err){
if (err) return clbk(err);
var filename = files.shift();
if (filename === null || typeof filename == 'undefined')
return clbk();
var file = srcDir+'/'+filename,
newFile = newDir+'/'+filename;
fs.stat(file, function(err, fileStat){
if (fileStat.isDirectory())
copyDirRecursive(file, newFile, copyFiles);
else if (fileStat.isSymbolicLink())
fs.readlink(file, function(err, link){
fs.symlink(link, newFile, copyFiles);
});
else
fs.readFile(file, function(err, data){
fs.writeFile(newFile, data, copyFiles);
});
});
})();
});
});
});
});
};
var mkdirSyncRecursive = function(path, mode) {
var self = this;
try {
fs.mkdirSync(path, mode);
} catch(err) {
if(err.code == "ENOENT") {
var slashIdx = path.lastIndexOf("/");
if(slashIdx < 0) {
slashIdx = path.lastIndexOf("\\");
}
if(slashIdx > 0) {
var parentPath = path.substring(0, slashIdx);
mkdirSyncRecursive(parentPath, mode);
mkdirSyncRecursive(path, mode);
} else {
throw err;
}
} else if(err.code == "EEXIST") {
return;
} else {
throw err;
}
}
};
exports.mkdirSyncRecursive = mkdirSyncRecursive;
exports.LineReader = function(filename, bufferSize) {
this.bufferSize = bufferSize || 8192;
this.buffer = "";
this.fd = fs.openSync(filename, "r");
this.currentPosition = 0;
};
exports.LineReader.prototype = {
getBufferAndSetCurrentPosition: function(position) {
var res = fs.readSync(this.fd, this.bufferSize, position, "ascii");
this.buffer += res[0];
if(res[1] === 0) {
this.currentPosition = -1;
} else {
this.currentPosition = position + res[1];
}
return this.currentPosition;
},
hasNextLine: function() {
while(this.buffer.indexOf('\n') === -1) {
this.getBufferAndSetCurrentPosition(this.currentPosition);
if(this.currentPosition === -1) return false;
}
if(this.buffer.indexOf("\n") > -1) return true;
return false;
},
getNextLine: function() {
var lineEnd = this.buffer.indexOf("\n"),
result = this.buffer.substring(0, lineEnd);
this.buffer = this.buffer.substring(result.length + 1, this.buffer.length);
return result;
}
};
// vim: et ts=4 sw=4

View File

@ -1 +0,0 @@
require('./wrench').mkdirsSyncRecursive('x/lol/b', 777);

View File

@ -1,61 +0,0 @@
{
"name": "wrench",
"description": "Recursive filesystem (and other) operations that Node *should* have.",
"version": "1.3.9",
"author": {
"name": "Ryan McGrath",
"email": "ryan@venodesigns.net"
},
"repository": {
"type": "git",
"url": "git://github.com/ryanmcgrath/wrench-js.git"
},
"bugs": {
"url": "http://github.com/ryanmcgrath/wrench-js/issues"
},
"directories": {
"lib": "./lib/"
},
"dependencies": {},
"devDependencies": {
"nodeunit": ">= 0.6.4"
},
"main": "./lib/wrench",
"engines": {
"node": ">=0.1.97"
},
"scripts": {
"test": "nodeunit tests/runner.js"
},
"licenses": [
{
"type": "MIT",
"url": "http://github.com/ryanmcgrath/wrench-js/raw/master/LICENSE"
}
],
"_npmUser": {
"name": "ryanmcgrath",
"email": "ryan@venodesigns.net"
},
"_id": "wrench@1.3.9",
"optionalDependencies": {},
"_engineSupported": true,
"_npmVersion": "1.1.12",
"_nodeVersion": "v0.6.14",
"_defaultsLoaded": true,
"dist": {
"shasum": "6f13ec35145317eb292ca5f6531391b244111411",
"tarball": "http://registry.npmjs.org/wrench/-/wrench-1.3.9.tgz"
},
"maintainers": [
{
"name": "ryanmcgrath",
"email": "ryan@venodesigns.net"
}
],
"_shasum": "6f13ec35145317eb292ca5f6531391b244111411",
"_from": "wrench@>=1.3.9 <1.4.0",
"_resolved": "https://registry.npmjs.org/wrench/-/wrench-1.3.9.tgz",
"readme": "ERROR: No README data found!",
"homepage": "https://github.com/ryanmcgrath/wrench-js"
}

View File

@ -1,60 +0,0 @@
wrench.js - Recursive file operations in Node.js
----------------------------------------------------------------------------
While I love Node.js, I've found myself missing some functions. Things like
recursively deleting/chmodding a directory (or even deep copying a directory),
or even a basic line reader, shouldn't need to be re-invented time and time again.
That said, here's my attempt at a re-usable solution, at least until something
more formalized gets integrated into Node.js (*hint hint*). wrench.js is fairly simple
to use - check out the documentation/examples below:
Installation
-----------------------------------------------------------------------------
npm install wrench
Usage
-----------------------------------------------------------------------------
``` javascript
var wrench = require('wrench'),
util = require('util');
```
### Synchronous operations
``` javascript
// Recursively create directories, sub-trees and all.
wrench.mkdirSyncRecursive(dir, 0777);
// Recursively delete the entire sub-tree of a directory, then kill the directory
wrench.rmdirSyncRecursive('my_directory_name', failSilently);
// Recursively read directories contents.
wrench.readdirSyncRecursive('my_directory_name');
// Recursively chmod the entire sub-tree of a directory
wrench.chmodSyncRecursive('my_directory_name', 0755);
// Recursively chown the entire sub-tree of a directory
wrench.chownSyncRecursive("directory", uid, gid);
// Deep-copy an existing directory
wrench.copyDirSyncRecursive('directory_to_copy', 'location_where_copy_should_end_up');
// Read lines in from a file until you hit the end
var f = new wrench.LineReader('x.txt');
while(f.hasNextLine()) {
util.puts(x.getNextLine());
}
```
### Asynchronous operations
``` javascript
// Recursively read directories contents
var files = [];
wrench.readdirRecursive('my_directory_name', function(error, curFiles) {
// curFiles is what you want
});
```
Questions, comments? Hit me up. (ryan [at] venodesigns.net | http://twitter.com/ryanmcgrath)

View File

@ -1,26 +0,0 @@
var testCase = require('nodeunit').testCase;
var fs = require('fs');
var wrench = require('../lib/wrench');
var path = require('path');
module.exports = testCase({
test_mkdirSyncRecursive: function(test) {
var dir = __dirname + '/_tmp/foo/bar';
test.equals(path.existsSync(dir), false, 'Dir shouldn\'t exist - clean it up manually?');
wrench.mkdirSyncRecursive(dir, 0777);
test.equals(path.existsSync(dir), true, 'Dir should exist now');
// clean up
while (dir != __dirname) {
fs.rmdirSync(dir);
dir = path.dirname(dir);
}
test.done();
},
});
// vim: et ts=4 sw=4

View File

@ -1,52 +0,0 @@
var testCase = require('nodeunit').testCase;
var fs = require('fs');
var wrench = require('../lib/wrench');
var path = require('path');
function checkResult(test, files) {
var check = [
'bar.txt',
'foo',
path.join('foo', 'bar'),
path.join('foo', 'dolor.md'),
path.join('foo', 'lorem.txt'),
path.join('foo', 'bar', 'ipsum.js')
];
test.deepEqual(files, check);
test.done();
}
module.exports = testCase({
test_readdirSyncRecursive: function(test) {
var dir = path.join(__dirname, 'readdir');
test.ok(path.existsSync(dir), 'Folders should exist');
var files = wrench.readdirSyncRecursive(dir);
checkResult(test, files);
},
test_readdirRecursive: function(test) {
var dir = path.join(__dirname, 'readdir');
test.ok(path.existsSync(dir), 'Folders should exist');
var allFiles = [];
wrench.readdirRecursive(dir, function(e, files) {
if (e) throw e;
if (files) {
allFiles = allFiles.concat(files);
} else {
checkResult(test, allFiles);
}
});
}
});
// vim: et ts=4 sw=4

View File

@ -1,7 +0,0 @@
// `nodeunit tests/runner`
// will run all the tests
module.exports = {
group_mkdir: require('./mkdir'),
group_readdir: require('./readdir')
};

View File

@ -1,87 +0,0 @@
{
"name": "jsdoc",
"version": "3.3.0-alpha9",
"revision": "1403969163513",
"description": "An API documentation generator for JavaScript.",
"keywords": [
"documentation",
"javascript"
],
"licenses": [
{
"type": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0"
}
],
"repository": {
"type": "git",
"url": "https://github.com/jsdoc3/jsdoc"
},
"dependencies": {
"async": "~0.1.22",
"catharsis": "~0.8.2",
"esprima": "https://github.com/ariya/esprima/tarball/49a2eccb243f29bd653b11e9419241a9d726af7c",
"js2xmlparser": "~0.1.0",
"marked": "~0.3.1",
"requizzle": "~0.2.0",
"strip-json-comments": "~0.1.3",
"taffydb": "https://github.com/hegemonic/taffydb/tarball/master",
"underscore": "~1.6.0",
"wrench": "~1.3.9"
},
"devDependencies": {
"gulp": "~3.6.2",
"gulp-eslint": "~0.1.6",
"gulp-json-editor": "~2.0.2",
"istanbul": "~0.2.1",
"tv4": "https://github.com/hegemonic/tv4/tarball/own-properties"
},
"engines": {
"node": ">=0.10"
},
"scripts": {
"test": "gulp test"
},
"bin": {
"jsdoc": "./jsdoc.js"
},
"bugs": {
"url": "https://github.com/jsdoc3/jsdoc/issues"
},
"author": {
"name": "Michael Mathews",
"email": "micmath@gmail.com"
},
"contributors": [
{
"url": "https://github.com/jsdoc3/jsdoc/graphs/contributors"
}
],
"maintainers": [
{
"name": "kzh",
"email": "kaleb@hornsby.ws"
},
{
"name": "hegemonic",
"email": "jeffrey.l.williams@gmail.com"
}
],
"gitHead": "f5da3b7c9603bdcc80bff7c187d592f3d9fcf450",
"homepage": "https://github.com/jsdoc3/jsdoc",
"_id": "jsdoc@3.3.0-alpha9",
"_shasum": "8527b69bfcf5eaf2a6ae82ac01686151bba31660",
"_from": "jsdoc@>=3.3.0-alpha9 <3.4.0",
"_npmVersion": "1.4.16",
"_npmUser": {
"name": "hegemonic",
"email": "jeffrey.l.williams@gmail.com"
},
"dist": {
"shasum": "8527b69bfcf5eaf2a6ae82ac01686151bba31660",
"tarball": "http://registry.npmjs.org/jsdoc/-/jsdoc-3.3.0-alpha9.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.3.0-alpha9.tgz",
"readme": "ERROR: No README data found!"
}

View File

@ -1,21 +0,0 @@
/**
@overview Demonstrate how to modify the source code before the parser sees it.
@module plugins/commentConvert
@author Michael Mathews <micmath@gmail.com>
*/
'use strict';
exports.handlers = {
///
/// Convert ///-style comments into jsdoc comments.
/// @param e
/// @param e.filename
/// @param e.source
///
beforeParse: function(e) {
e.source = e.source.replace(/(\n[ \t]*\/\/\/[^\n]*)+/g, function($) {
var replacement = '\n/**' + $.replace(/^[ \t]*\/\/\//mg, '').replace(/(\n$|$)/, '*/$1');
return replacement;
});
}
};

View File

@ -1,18 +0,0 @@
/**
* @overview Remove everything in a file except JSDoc-style comments. By enabling this plugin, you
* can document source files that are not valid JavaScript (including source files for other
* languages).
* @module plugins/commentsOnly
* @author Jeff Williams <jeffrey.l.williams@gmail.com>
*/
'use strict';
exports.handlers = {
beforeParse: function(e) {
// a JSDoc comment looks like: /**[one or more chars]*/
var comments = e.source.match(/\/\*\*[\s\S]+?\*\//g);
if (comments) {
e.source = comments.join('\n\n');
}
}
};

View File

@ -1,21 +0,0 @@
/**
@overview Escape HTML tags in descriptions.
@module plugins/escapeHtml
@author Michael Mathews <micmath@gmail.com>
*/
'use strict';
exports.handlers = {
/**
Translate HTML tags in descriptions into safe entities.
Replaces <, & and newlines
*/
newDoclet: function(e) {
if (e.doclet.description) {
e.doclet.description = e.doclet.description
.replace(/&/g,'&amp;')
.replace(/</g,'&lt;')
.replace(/\r\n|\n|\r/g, '<br>');
}
}
};

View File

@ -1,115 +0,0 @@
/*global env: true */
/**
* @overview Dump information about parser events to the console.
* @module plugins/eventDumper
* @author Jeff Williams <jeffrey.l.williams@gmail.com>
*/
'use strict';
var _ = require('underscore');
var util = require('util');
var conf = env.conf.eventDumper || {};
var isRhino = require('jsdoc/util/runtime').isRhino();
// Dump the included parser events (defaults to all events)
var events = conf.include || [
'parseBegin',
'fileBegin',
'beforeParse',
'jsdocCommentFound',
'symbolFound',
'newDoclet',
'fileComplete',
'parseComplete',
'processingComplete'
];
// Don't dump the excluded parser events
if (conf.exclude) {
events = _.difference(events, conf.exclude);
}
/**
* Check whether a variable appears to be a Java native object.
*
* @param {*} o - The variable to check.
* @return {boolean} Set to `true` for Java native objects and `false` in all other cases.
*/
function isJavaNativeObject(o) {
if (!isRhino) {
return false;
}
return o && typeof o === 'object' && typeof o.getClass === 'function';
}
/**
* Replace AST node objects in events with a placeholder.
*
* @param {Object} o - An object whose properties may contain AST node objects.
* @return {Object} The modified object.
*/
function replaceNodeObjects(o) {
var doop = require('jsdoc/util/doop');
var OBJECT_PLACEHOLDER = '<Object>';
if (o.code && o.code.node) {
// don't break the original object!
o.code = doop(o.code);
o.code.node = OBJECT_PLACEHOLDER;
}
if (o.doclet && o.doclet.meta && o.doclet.meta.code && o.doclet.meta.code.node) {
// don't break the original object!
o.doclet.meta.code = doop(o.doclet.meta.code);
o.doclet.meta.code.node = OBJECT_PLACEHOLDER;
}
if (o.astnode) {
o.astnode = OBJECT_PLACEHOLDER;
}
return o;
}
/**
* Get rid of unwanted crud in an event object.
*
* @param {object} e The event object.
* @return {object} The fixed-up object.
*/
function cleanse(e) {
var result = {};
Object.keys(e).forEach(function(prop) {
// by default, don't stringify properties that contain an array of functions
if (!conf.includeFunctions && util.isArray(e[prop]) && e[prop][0] &&
String(typeof e[prop][0]) === 'function') {
result[prop] = 'function[' + e[prop].length + ']';
}
// never include functions that belong to the object
else if (typeof e[prop] !== 'function') {
// don't call JSON.stringify() on Java native objects--Rhino will throw an exception
result[prop] = isJavaNativeObject(e[prop]) ? String(e[prop]) : e[prop];
}
});
// allow users to omit node objects, which can be enormous
if (conf.omitNodes) {
result = replaceNodeObjects(result);
}
return result;
}
exports.handlers = {};
events.forEach(function(eventType) {
exports.handlers[eventType] = function(e) {
console.log( JSON.stringify({
type: eventType,
content: cleanse(e)
}, null, 4) );
};
});

View File

@ -1,82 +0,0 @@
/*global env: true */
/**
* @overview Translate doclet descriptions from MarkDown into HTML.
* @module plugins/markdown
* @author Michael Mathews <micmath@gmail.com>
* @author Ben Blank <ben.blank@gmail.com>
*/
'use strict';
var conf = env.conf.markdown;
var defaultTags = [ 'classdesc', 'description', 'params', 'properties', 'returns', 'see'];
var hasOwnProp = Object.prototype.hasOwnProperty;
var parse = require('jsdoc/util/markdown').getParser();
var tags = [];
var excludeTags = [];
function shouldProcessString(tagName, text) {
var shouldProcess = false;
if (tagName !== 'see') {
shouldProcess = true;
}
// we only want to process `@see` tags that contain Markdown links
else if (tagName === 'see' && text.indexOf('[') !== -1) {
shouldProcess = true;
}
return shouldProcess;
}
/**
* Process the markdown source in a doclet. The properties that should be
* processed are configurable, but always include "classdesc", "description",
* "params", "properties", and "returns". Handled properties can be bare
* strings, objects, or arrays of objects.
*/
function process(doclet) {
tags.forEach(function(tag) {
if ( !hasOwnProp.call(doclet, tag) ) {
return;
}
if (typeof doclet[tag] === 'string' && shouldProcessString(tag, doclet[tag]) ) {
doclet[tag] = parse(doclet[tag]);
}
else if ( Array.isArray(doclet[tag]) ) {
doclet[tag].forEach(function(value, index, original) {
var inner = {};
inner[tag] = value;
process(inner);
original[index] = inner[tag];
});
}
else if (doclet[tag]) {
process(doclet[tag]);
}
});
}
// set up the list of "tags" (properties) to process
if (conf && conf.tags) {
tags = conf.tags.slice();
}
// set up the list of default tags to exclude from processing
if (conf && conf.excludeTags) {
excludeTags = conf.excludeTags.slice();
}
defaultTags.forEach(function(tag) {
if (excludeTags.indexOf(tag) === -1 && tags.indexOf(tag) === -1) {
tags.push(tag);
}
});
exports.handlers = {
/**
* Translate markdown syntax in a new doclet's description into HTML. Is run
* by JSDoc 3 whenever a "newDoclet" event fires.
*/
newDoclet: function(e) {
process(e.doclet);
}
};

View File

@ -1,185 +0,0 @@
/**
* The Overload Helper plugin automatically adds a signature-like string to the longnames of
* overloaded functions and methods. In JSDoc, this string is known as a _variation_. (The longnames
* of overloaded constructor functions are _not_ updated, so that JSDoc can identify the class'
* members correctly.)
*
* Using this plugin allows you to link to overloaded functions without manually adding `@variation`
* tags to your documentation.
*
* For example, suppose your code includes a function named `foo` that you can call in the
* following ways:
*
* + `foo()`
* + `foo(bar)`
* + `foo(bar, baz)` (where `baz` is repeatable)
*
* This plugin assigns the following variations and longnames to each version of `foo`:
*
* + `foo()` gets the variation `()` and the longname `foo()`.
* + `foo(bar)` gets the variation `(bar)` and the longname `foo(bar)`.
* + `foo(bar, baz)` (where `baz` is repeatable) gets the variation `(bar, ...baz)` and the longname
* `foo(bar, ...baz)`.
*
* You can then link to these functions with `{@link foo()}`, `{@link foo(bar)}`, and
* `{@link foo(bar, ...baz)`. Note that the variation is based on the names of the function
* parameters, _not_ their types.
*
* If you prefer to manually assign variations to certain functions, you can still do so with the
* `@variation` tag. This plugin will not change these variations or add more variations for that
* function, as long as the variations you've defined result in unique longnames.
*
* If an overloaded function includes multiple signatures with the same parameter names, the plugin
* will assign numeric variations instead, starting at `(1)` and counting upwards.
*
* @module plugins/overloadHelper
* @author Jeff Williams <jeffrey.l.williams@gmail.com>
* @license Apache License 2.0
*/
'use strict';
// lookup table of function doclets by longname
var functionDoclets;
function hasUniqueValues(obj) {
var isUnique = true;
var seen = [];
Object.keys(obj).forEach(function(key) {
if (seen.indexOf(obj[key]) !== -1) {
isUnique = false;
}
seen.push(obj[key]);
});
return isUnique;
}
function getParamNames(params) {
var names = [];
params.forEach(function(param) {
var name = param.name || '';
if (param.variable) {
name = '...' + name;
}
if (name !== '') {
names.push(name);
}
});
return names.length ? names.join(', ') : '';
}
function getParamVariation(doclet) {
return getParamNames(doclet.params || []);
}
function getUniqueVariations(doclets) {
var counter = 0;
var variations = {};
var docletKeys = Object.keys(doclets);
function getUniqueNumbers() {
var format = require('util').format;
docletKeys.forEach(function(doclet) {
var newLongname;
while (true) {
counter++;
variations[doclet] = String(counter);
// is this longname + variation unique?
newLongname = format('%s(%s)', doclets[doclet].longname, variations[doclet]);
if ( !functionDoclets[newLongname] ) {
break;
}
}
});
}
function getUniqueNames() {
// start by trying to preserve existing variations
docletKeys.forEach(function(doclet) {
variations[doclet] = doclets[doclet].variation || getParamVariation(doclets[doclet]);
});
// if they're identical, try again, without preserving existing variations
if ( !hasUniqueValues(variations) ) {
docletKeys.forEach(function(doclet) {
variations[doclet] = getParamVariation(doclets[doclet]);
});
// if they're STILL identical, switch to numeric variations
if ( !hasUniqueValues(variations) ) {
getUniqueNumbers();
}
}
}
// are we already using numeric variations? if so, keep doing that
if (functionDoclets[doclets.newDoclet.longname + '(1)']) {
getUniqueNumbers();
}
else {
getUniqueNames();
}
return variations;
}
function ensureUniqueLongname(newDoclet) {
var doclets = {
oldDoclet: functionDoclets[newDoclet.longname],
newDoclet: newDoclet
};
var docletKeys = Object.keys(doclets);
var oldDocletLongname;
var variations = {};
if (doclets.oldDoclet) {
oldDocletLongname = doclets.oldDoclet.longname;
// if the shared longname has a variation, like MyClass#myLongname(variation),
// remove the variation
if (doclets.oldDoclet.variation || doclets.oldDoclet.variation === '') {
docletKeys.forEach(function(doclet) {
doclets[doclet].longname = doclets[doclet].longname.replace(/\([\s\S]*\)$/, '');
doclets[doclet].variation = null;
});
}
variations = getUniqueVariations(doclets);
// update the longnames/variations
docletKeys.forEach(function(doclet) {
doclets[doclet].longname += '(' + variations[doclet] + ')';
doclets[doclet].variation = variations[doclet];
});
// update the old doclet in the lookup table
functionDoclets[oldDocletLongname] = null;
functionDoclets[doclets.oldDoclet.longname] = doclets.oldDoclet;
}
// always store the new doclet in the lookup table
functionDoclets[doclets.newDoclet.longname] = doclets.newDoclet;
return doclets.newDoclet;
}
exports.handlers = {
parseBegin: function() {
functionDoclets = {};
},
newDoclet: function(e) {
if (e.doclet.kind === 'function') {
e.doclet = ensureUniqueLongname(e.doclet);
}
},
parseComplete: function() {
functionDoclets = null;
}
};

View File

@ -1,32 +0,0 @@
/*global env: true */
/**
@overview Adds support for reusable partial jsdoc files.
@module plugins/partial
@author Ludo Antonov <ludo@hulu.com>
*/
'use strict';
var fs = require('jsdoc/fs');
var path = require('path');
exports.handlers = {
///
/// Include a partial jsdoc
/// @param e
/// @param e.filename
/// @param e.source
///
/// @example
/// @partial "partial_doc.jsdoc"
///
beforeParse: function(e) {
e.source = e.source.replace(/(@partial \".*\")+/g, function($) {
var pathArg = $.match(/\".*\"/)[0].replace(/"/g,'');
var fullPath = path.join(e.filename , '..', pathArg);
var partialData = fs.readFileSync(fullPath, env.opts.encoding);
return partialData;
});
}
};

View File

@ -1,20 +0,0 @@
/**
@overview Strips the rails template tags from a js.erb file
@module plugins/railsTemplate
@author Jannon Frank <jannon@jannon.net>
*/
'use strict';
exports.handlers = {
/**
* Remove rails tags from the source input (e.g. <% foo bar %>)
* @param e
* @param e.filename
* @param e.source
*/
beforeParse: function(e) {
if (e.filename.match(/\.erb$/)) {
e.source = e.source.replace(/<%.*%>/g, '');
}
}
};

View File

@ -1,17 +0,0 @@
/**
@overview This is just an example.
@module plugins/shout
@author Michael Mathews <micmath@gmail.com>
*/
'use strict';
exports.handlers = {
/**
Make your descriptions more shoutier.
*/
newDoclet: function(e) {
if (typeof e.doclet.description === 'string') {
e.doclet.description = e.doclet.description.toUpperCase();
}
}
};

Some files were not shown because too many files have changed in this diff Show More