Comparing version 0.7.1 to 0.8.0
@@ -12,27 +12,45 @@ #!/usr/bin/env node | ||
var typeExpression = process.argv[2]; | ||
var jsdoc = process.argv[3] === '--jsdoc' ? true : false; | ||
var parsedType; | ||
var opts = { | ||
jsdoc: jsdoc | ||
describe: false, | ||
jsdoc: false | ||
}; | ||
var parsedType; | ||
function usage() { | ||
console.log(util.format('Usage:\n %s typeExpression [--jsdoc]', command)); | ||
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(); | ||
process.exit(1); | ||
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); | ||
process.exit(1); | ||
done(1); | ||
} | ||
console.log(JSON.stringify(parsedType, null, 2)); | ||
process.exit(0); | ||
done(); | ||
} |
@@ -11,2 +11,3 @@ /** | ||
var describe = require('./lib/describe'); | ||
var parse = require('./lib/parser').parse; | ||
@@ -25,2 +26,6 @@ var stringify = require('./lib/stringify'); | ||
var descriptionCache = { | ||
normal: {} | ||
}; | ||
function getTypeExpressionCache(options) { | ||
@@ -46,2 +51,10 @@ if (options.useCache === false) { | ||
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: | ||
@@ -58,2 +71,17 @@ // 1. restringification was requested | ||
// 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) { | ||
@@ -67,13 +95,4 @@ var cache = getTypeExpressionCache(options); | ||
parsedType = parse(expr, options); | ||
parsedType = prepareFrozenObject(parsedType, expr, options); | ||
Object.defineProperties(parsedType, { | ||
typeExpression: { | ||
value: expr | ||
}, | ||
jsdoc: { | ||
value: options.jsdoc === true ? true : false | ||
} | ||
}); | ||
parsedType = Object.freeze(parsedType); | ||
if (cache) { | ||
@@ -102,2 +121,19 @@ cache[expr] = parsedType; | ||
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() { | ||
@@ -118,5 +154,6 @@ this.Types = require('./lib/types'); | ||
Catharsis.prototype.stringify = function(parsedType, options) { | ||
options = options || {}; | ||
var result; | ||
options = options || {}; | ||
result = cachedStringify(parsedType, options); | ||
@@ -130,2 +167,8 @@ if (options.validate) { | ||
Catharsis.prototype.describe = function(parsedType, options) { | ||
options = options || {}; | ||
return cachedDescribe(parsedType, options); | ||
}; | ||
module.exports = new Catharsis(); |
@@ -7,2 +7,3 @@ 'use strict'; | ||
this._options = options || {}; | ||
this._options.linkClass = this._options.linkClass || this._options.cssClass; | ||
@@ -14,11 +15,11 @@ // in a list of function signature params, repeatable params are stringified differently | ||
Stringifier.prototype.applications = function(applications) { | ||
var result = ''; | ||
var strings = []; | ||
if (!applications) { | ||
return ''; | ||
return result; | ||
} | ||
var parsedApplications = []; | ||
var result = ''; | ||
for (var i = 0, l = applications.length; i < l; i++) { | ||
parsedApplications.push(this.type(applications[i])); | ||
strings.push(this.type(applications[i])); | ||
} | ||
@@ -32,3 +33,3 @@ | ||
result += parsedApplications.join(', ') + '>'; | ||
result += strings.join(', ') + '>'; | ||
@@ -39,13 +40,16 @@ return result; | ||
Stringifier.prototype.elements = function(elements) { | ||
var result = ''; | ||
var strings = []; | ||
if (!elements) { | ||
return ''; | ||
return result; | ||
} | ||
var result = []; | ||
for (var i = 0, l = elements.length; i < l; i++) { | ||
result.push(this.type(elements[i])); | ||
strings.push(this.type(elements[i])); | ||
} | ||
return '(' + result.join('|') + ')'; | ||
result = '(' + strings.join('|') + ')'; | ||
return result; | ||
}; | ||
@@ -81,27 +85,15 @@ | ||
Stringifier.prototype.params = function(params) { | ||
var result = ''; | ||
var strings = []; | ||
if (!params || params.length === 0) { | ||
return ''; | ||
return result; | ||
} | ||
var result = []; | ||
var param; | ||
for (var i = 0, l = params.length; i < l; i++) { | ||
result.push(this.type(params[i])); | ||
strings.push(this.type(params[i])); | ||
} | ||
return result.join(', '); | ||
}; | ||
result = strings.join(', '); | ||
Stringifier.prototype.properties = function(props) { | ||
if (!props) { | ||
return ''; | ||
} | ||
var result = []; | ||
for (var i = 0, l = props.length; i < l; i++) { | ||
result.push(this._formatNameAndType(props[i].name, props[i].type)); | ||
} | ||
return result; | ||
@@ -118,8 +110,7 @@ }; | ||
// TODO: refactor for clarity | ||
Stringifier.prototype.type = function(type) { | ||
var result = ''; | ||
var typeString = ''; | ||
if (!type) { | ||
return result; | ||
return typeString; | ||
} | ||
@@ -129,38 +120,35 @@ | ||
case Types.AllLiteral: | ||
result += this._formatRepeatableAndNullable(type, '', | ||
this._formatNameAndType(type, '*')); | ||
typeString = this._formatNameAndType(type, '*'); | ||
break; | ||
case Types.FunctionType: | ||
result += this._signature(type); | ||
typeString = this._signature(type); | ||
break; | ||
case Types.NullLiteral: | ||
result += this._formatRepeatableAndNullable(type, '', | ||
this._formatNameAndType(type, 'null')); | ||
typeString = this._formatNameAndType(type, 'null'); | ||
break; | ||
case Types.RecordType: | ||
result += this._formatRepeatableAndNullable(type, '', this._record(type)); | ||
typeString = this._record(type); | ||
break; | ||
case Types.TypeApplication: | ||
result += this._formatRepeatableAndNullable(type, '', this.type(type.expression)); | ||
result += this.applications(type.applications); | ||
typeString = this.type(type.expression) + this.applications(type.applications); | ||
break; | ||
case Types.UndefinedLiteral: | ||
result += this._formatRepeatableAndNullable(type, '', | ||
this._formatNameAndType(type, 'undefined')); | ||
typeString = this._formatNameAndType(type, 'undefined'); | ||
break; | ||
case Types.TypeUnion: | ||
result += this._formatRepeatableAndNullable(type, '', this.elements(type.elements)); | ||
typeString = this.elements(type.elements); | ||
break; | ||
case Types.UnknownLiteral: | ||
result += this._formatRepeatableAndNullable(type, '', | ||
this._formatNameAndType(type, '?')); | ||
typeString = this._formatNameAndType(type, '?'); | ||
break; | ||
default: | ||
result += this._formatRepeatableAndNullable(type, '', this._formatNameAndType(type)); | ||
typeString = this._formatNameAndType(type); | ||
} | ||
// finally, optionality | ||
result += this.optional(type.optional); | ||
// add optional/nullable/repeatable modifiers | ||
if (!this._options._ignoreModifiers) { | ||
typeString = this._addModifiers(type, typeString); | ||
} | ||
return result; | ||
return typeString; | ||
}; | ||
@@ -179,10 +167,10 @@ | ||
Stringifier.prototype._recordFields = function(fields) { | ||
if (!fields) { | ||
return ''; | ||
} | ||
var field; | ||
var keyAndValue; | ||
var result = []; | ||
var field; | ||
var keyAndValue; | ||
if (!fields) { | ||
return result; | ||
} | ||
@@ -207,6 +195,9 @@ for (var i = 0, l = fields.length; i < l; i++) { | ||
Stringifier.prototype._formatRepeatableAndNullable = function(type, nameString, typeString) { | ||
// Adds optional, nullable, and repeatable modifiers if necessary. | ||
Stringifier.prototype._addModifiers = function(type, typeString) { | ||
var combined; | ||
var open = ''; | ||
var close = ''; | ||
var combined; | ||
var optional = ''; | ||
@@ -218,22 +209,32 @@ if (type.repeatable) { | ||
combined = this.nullable(type.nullable) + combineNameAndType(nameString, typeString); | ||
combined = this.nullable(type.nullable) + combineNameAndType('', typeString); | ||
optional = this.optional(type.optional); | ||
return open + combined + close; | ||
return open + combined + close + optional; | ||
}; | ||
Stringifier.prototype._formatNameAndType = function(type, literal) { | ||
var nameString = type.name || literal || ''; | ||
var typeString = type.type ? this.type(type.type) : ''; | ||
var cssClass; | ||
Stringifier.prototype._addLinks = function(nameString) { | ||
var openTag; | ||
// replace the type with an HTML link if necessary | ||
if (this._options.links && Object.prototype.hasOwnProperty.call(this._options.links, | ||
nameString)) { | ||
cssClass = this._options.cssClass ? ' class="' + this._options.cssClass + '"' : ''; | ||
var linkClass = ''; | ||
var options = this._options; | ||
openTag = '<a href="' + this._options.links[nameString] + '"' + cssClass + '>'; | ||
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); | ||
@@ -243,7 +244,7 @@ }; | ||
Stringifier.prototype._signature = function(type) { | ||
var params = []; | ||
var param; | ||
var result; | ||
var signatureBase; | ||
var prop; | ||
var signature; | ||
var params = []; | ||
// these go within the signature's parens, in this order | ||
@@ -255,3 +256,2 @@ var props = [ | ||
]; | ||
var prop; | ||
@@ -268,7 +268,6 @@ this._inFunctionSignatureParams = true; | ||
signatureBase = 'function(' + params.join(', ') + ')'; | ||
result = this._formatRepeatableAndNullable(type, '', signatureBase); | ||
result += this.result(type.result); | ||
signature = 'function(' + params.join(', ') + ')'; | ||
signature += this.result(type.result); | ||
return result; | ||
return signature; | ||
}; | ||
@@ -275,0 +274,0 @@ |
{ | ||
"version": "0.7.1", | ||
"name": "catharsis", | ||
"description": "A JavaScript parser for Google Closure Compiler and JSDoc type expressions.", | ||
"author": "Jeff Williams <jeffrey.l.williams@gmail.com>", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/hegemonic/catharsis" | ||
}, | ||
"bugs": "https://github.com/hegemonic/catharsis/issues", | ||
"main": "catharsis.js", | ||
"devDependencies": { | ||
"mocha": "1.13.0", | ||
"pegjs": "git+ssh://git@github.com:dmajda/pegjs.git#76cc5d55", | ||
"should": "1.3.0", | ||
"uglify-js": "2.4.0", | ||
"underscore": "1.5.2" | ||
}, | ||
"engines": { | ||
"node": ">= 0.6" | ||
}, | ||
"scripts": { | ||
"build": "./node_modules/.bin/pegjs ./lib/parser.pegjs", | ||
"prepublish": "./node_modules/.bin/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" | ||
} | ||
] | ||
"version": "0.8.0", | ||
"name": "catharsis", | ||
"description": "A JavaScript parser for Google Closure Compiler and JSDoc type expressions.", | ||
"author": "Jeff Williams <jeffrey.l.williams@gmail.com>", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/hegemonic/catharsis" | ||
}, | ||
"bugs": "https://github.com/hegemonic/catharsis/issues", | ||
"main": "catharsis.js", | ||
"dependencies": { | ||
"underscore": "~1.6.0", | ||
"underscore-contrib": "~0.2.2" | ||
}, | ||
"devDependencies": { | ||
"mocha": "~1.18.2", | ||
"pegjs": "git+ssh://git@github.com:dmajda/pegjs.git#76cc5d55", | ||
"should": "~3.3.1", | ||
"uglify-js": "~2.4.13" | ||
}, | ||
"engines": { | ||
"node": ">= 0.8" | ||
}, | ||
"scripts": { | ||
"build": "./node_modules/.bin/pegjs ./lib/parser.pegjs", | ||
"prepublish": "./node_modules/.bin/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" | ||
} | ||
] | ||
} |
115
README.md
@@ -13,4 +13,5 @@ # Catharsis # | ||
+ **Fast**. Parse results are cached, so the parser is invoked only when necessary. | ||
+ **Flexible**. Catharsis can convert parse results back into type expressions. In addition, it can | ||
parse [JSDoc](https://github.com/jsdoc3/jsdoc)-style type expressions. | ||
+ **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. | ||
@@ -41,8 +42,14 @@ | ||
// 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 for more examples of Catharsis' parse results. | ||
See the [test/specs directory](tree/master/test/specs) for more examples of Catharsis' parse | ||
results. | ||
@@ -53,3 +60,3 @@ | ||
### parse(typeExpression, options) ### | ||
Parse `typeExpression`, and return the parse results. Throws an error if the type expression cannot | ||
Parse a type expression, and return the parse results. Throws an error if the type expression cannot | ||
be parsed. | ||
@@ -82,4 +89,4 @@ | ||
#### Returns #### | ||
An object containing the parse results. See the `test/specs/` directory for examples of the parse | ||
results for different type expressions. | ||
An object containing the parse results. See the [test/specs directory](tree/master/test/specs) for | ||
examples of the parse results for different type expressions. | ||
@@ -98,10 +105,12 @@ The object also includes two non-enumerable properties: | ||
+ `options`: Options for stringifying the parse results. | ||
+ `options.cssClass`: A CSS class to add to HTML links. Used only if `options.links` is | ||
provided. By default, no CSS class is added. | ||
+ `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 (`<`). **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.cssClass` is specified, the `<a>` tag will | ||
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 | ||
@@ -112,4 +121,4 @@ restringified, and the resulting string is not cached. | ||
return the `typeExpression` property without modification when possible. Defaults to `false`. | ||
+ `options.useCache`: Specifies whether to use the cache of stringified parse results. Defaults | ||
to `true`. | ||
+ `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 | ||
@@ -122,3 +131,80 @@ to parse them as a type expression. If the stringified results are not parsable by default, you | ||
### 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](tree/master/res/en.json). If you specify a value for `options.resources.en`, it | ||
will override the defaults in [res/en.json](tree/master/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 ## | ||
@@ -133,2 +219,4 @@ | ||
git clone git://github.com/hegemonic/catharsis.git | ||
cd catharsis | ||
npm install | ||
@@ -150,2 +238,7 @@ | ||
+ 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, | ||
@@ -152,0 +245,0 @@ `undefinedHTML`) are now parsed correctly when JSDoc-style type expressions are enabled. |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
215093
4
11
1438
329
2
+ Addedunderscore@~1.6.0
+ Addedunderscore-contrib@~0.2.2
+ Addedunderscore@1.6.0(transitive)
+ Addedunderscore-contrib@0.2.2(transitive)