Socket
Socket
Sign inDemoInstall

catharsis

Package Overview
Dependencies
Maintainers
1
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

catharsis - npm Package Compare versions

Comparing version 0.2.0 to 0.3.0

bin/parseType.js

59

catharsis.js
/**
* catharsis 0.1.1
* catharsis 0.3.0
* A parser for Google Closure Compiler type expressions, powered by PEG.js.

@@ -13,3 +13,2 @@ *

var stringify = require('./lib/stringify');
var util = require('util');

@@ -36,63 +35,19 @@ var typeExpressionCache = {};

var nextTick = (function() {
if (process && process.nextTick) {
return process.nextTick;
} else if (setTimeout) {
return function(callback) {
setTimeout(callback, 0);
};
} else {
// better safe than sorry
return function(callback) {
callback('Your JavaScript environment does not support the parse() method. ' +
'Please call parseSync() instead.');
};
}
})();
function Catharsis() {
this.Types = require('./lib/types');
}
function Catharsis() {}
Catharsis.prototype.parse = function(type, opts, callback) {
Catharsis.prototype.parse = function(type, opts) {
opts = opts || {};
nextTick(function() {
try {
callback(null, opts.useCache !== false ? cachedParse(type) : parse(type));
}
catch(e) {
callback(util.format('unable to parse the type %s: %s', type, e.message));
}
});
};
var parseSync = Catharsis.prototype.parseSync = function(type, opts) {
opts = opts || {};
return opts.useCache !== false ? cachedParse(type) : parse(type);
};
Catharsis.prototype.stringify = function(parsedType, opts, callback) {
Catharsis.prototype.stringify = function(parsedType, opts) {
opts = opts || {};
var result;
nextTick(function() {
try {
result = opts.useCache !== false ? cachedStringify(parsedType) : stringify(parsedType);
if (opts.validate) {
parseSync(result);
}
callback(null, result);
} catch (e) {
callback(util.format('the following parsed type is not valid: %j', parsedType));
}
});
};
Catharsis.prototype.stringifySync = function(parsedType, opts) {
opts = opts || {};
var result;
result = opts.useCache !== false ? cachedStringify(parsedType) : stringify(parsedType);
if (opts.validate) {
parseSync(result);
this.parse(result);
}

@@ -99,0 +54,0 @@

'use strict';
var parse = require('./parser').parse;
var util = require('util');
var Types = require('./types');

@@ -11,4 +10,4 @@ function Stringifier() {

Stringifier.prototype.canContain = function(canContain) {
if (!canContain) {
Stringifier.prototype.applications = function(applications) {
if (!applications) {
return '';

@@ -19,4 +18,4 @@ }

for (var i = 0, l = canContain.length; i < l; i++) {
result.push(this.type(canContain[i]));
for (var i = 0, l = applications.length; i < l; i++) {
result.push(this.type(applications[i]));
}

@@ -27,12 +26,14 @@

Stringifier.prototype.funcNew = function(funcNew) {
return funcNew ? 'new:' + funcNew : '';
};
Stringifier.prototype.elements = function(elements) {
if (!elements) {
return '';
}
Stringifier.prototype.funcReturns = function(funcReturns) {
return funcReturns ? ': ' + this.type(funcReturns) : '';
};
var result = [];
Stringifier.prototype.funcThis = function(funcThis) {
return funcThis ? 'this:' + funcThis : '';
for (var i = 0, l = elements.length; i < l; i++) {
result.push(this.type(elements[i]));
}
return '(' + result.join('|') + ')';
};

@@ -44,2 +45,6 @@

Stringifier.prototype['new'] = function(funcNew) {
return funcNew ? 'new:' + this.type(funcNew) : '';
};
Stringifier.prototype.nullable = function(nullable) {

@@ -64,3 +69,3 @@ switch (nullable) {

Stringifier.prototype.parameters = function(params) {
Stringifier.prototype.params = function(params) {
if (!params || params.length === 0) {

@@ -70,14 +75,10 @@ return '';

var paramStrings = [];
var result = [];
var param;
var string;
for (var i = 0, l = params.length; i < l; i++) {
string = this._typeBase(params[i]);
if (string.length > 0) {
paramStrings.push(string);
}
result.push(this.type(params[i]));
}
return paramStrings.join(', ');
return result.join(', ');
};

@@ -92,12 +93,4 @@

var prop;
var typeAndName;
for (var i = 0, l = props.length; i < l; i++) {
prop = props[i];
typeAndName = prop.typeName ? util.format('%s: ', prop.typeName) : '';
typeAndName += this.name(prop.name);
result.push(typeAndName);
result.push(this._formatNameAndType(props[i].name, props[i].type));
}

@@ -108,29 +101,8 @@

Stringifier.prototype.signature = function(sig) {
var params = [];
var param;
var result = '';
Stringifier.prototype.result = function(result) {
return result ? ': ' + this.type(result) : '';
};
// these go within the signature's parens, in this order
var props = [
'funcNew',
'funcThis',
'parameters'
];
var prop;
this._inFunctionSignatureParams = true;
for (var i = 0, l = props.length; i < l; i++) {
prop = props[i];
param = this[prop](sig[prop]);
if (param.length > 0) {
params.push(param);
}
}
this._inFunctionSignatureParams = false;
result = util.format('(%s)', params.join(', '));
result += this.funcReturns(sig.funcReturns);
return result;
Stringifier.prototype['this'] = function(funcThis) {
return funcThis ? 'this:' + this.type(funcThis) : '';
};

@@ -143,12 +115,39 @@

var result = '';
// nullable comes first
var result = this.nullable(type.nullable);
if (util.isArray(type)) {
result += this._union(type);
} else if (type.properties) {
result += this._record(type);
} else {
result += this._typeBase(type);
// next portion varies by type
switch(type.type) {
case Types.AllLiteral:
result += this._formatNameAndType(type, '*');
break;
case Types.FunctionType:
result += this._signature(type);
break;
case Types.NullLiteral:
result += this._formatNameAndType(type, 'null');
break;
case Types.RecordType:
result += this._record(type);
break;
case Types.TypeApplication:
result += this.type(type.expression);
result += this.applications(type.applications);
break;
case Types.UndefinedLiteral:
result += this._formatNameAndType(type, 'undefined');
break;
case Types.TypeUnion:
result += this.elements(type.elements);
break;
case Types.UnknownLiteral:
result += this._formatNameAndType(type, '?');
break;
default:
result += this._formatNameAndType(type);
}
// finally, optionality
result += this.optional(type.optional);
return result;

@@ -159,14 +158,12 @@ };

Stringifier.prototype.typeName = function(typeName) {
return typeName || '';
};
Stringifier.prototype.key = Stringifier.prototype.type;
Stringifier.prototype._record = function(type) {
var props = this._recordProperties(type.properties);
var fields = this._recordFields(type.fields);
return util.format('{%s}', props.join(', '));
return '{' + fields.join(', ') + '}';
};
Stringifier.prototype._recordProperties = function(props) {
if (!props) {
Stringifier.prototype._recordFields = function(fields) {
if (!fields) {
return '';

@@ -177,12 +174,12 @@ }

var prop;
var nameAndType;
var field;
var keyAndValue;
for (var i = 0, l = props.length; i < l; i++) {
prop = props[i];
for (var i = 0, l = fields.length; i < l; i++) {
field = fields[i];
nameAndType = this.name(prop.name);
nameAndType += prop.typeName ? util.format(': %s', prop.typeName) : '';
keyAndValue = this.key(field.key);
keyAndValue += field.value ? ': ' + this.type(field.value) : '';
result.push(nameAndType);
result.push(keyAndValue);
}

@@ -193,78 +190,57 @@

function combineNameAndType(name, typeName) {
var format = (name.length > 0 && typeName.length > 0) ? '%s:%s' : '%s%s';
return util.format(format, name, typeName);
function combineNameAndType(nameString, typeString) {
var separator = (nameString && typeString) ? ':' : '';
return nameString + separator + typeString;
}
function formatRepeatable(name, typeName, formatter) {
var nameAndType = combineNameAndType(name, typeName);
return util.format(formatter, nameAndType);
}
Stringifier.prototype._formatRepeatable = function(nameString, typeString) {
var open = this._inFunctionSignatureParams ? '...[' : '...';
var close = this._inFunctionSignatureParams ? ']' : '';
Stringifier.prototype._repeatable = function(type) {
var name = this.name(type.name);
var typeName = this.typeName(type.typeName);
var format = type.repeatable === true ? '...%s' : '%s';
return formatRepeatable(name, typeName, format);
return open + combineNameAndType(nameString, typeString) + close;
};
Stringifier.prototype._repeatableInSignature = function(param) {
var name = this.name(param.name);
var typeName = this.typeName(param.typeName);
var format = param.repeatable === true ? '...[%s]' : '%s';
Stringifier.prototype._formatNameAndType = function(type, literal) {
var nameString = type.name || literal || '';
var typeString = type.type ? this.type(type.type) : '';
return formatRepeatable(name, typeName, format);
if (type.repeatable === true) {
return this._formatRepeatable(nameString, typeString);
} else {
return combineNameAndType(nameString, typeString);
}
};
Stringifier.prototype._typeBase = function(type) {
if (!type) {
return '';
}
Stringifier.prototype._signature = function(type) {
var params = [];
var param;
var result;
// nullable comes first
var result = this.nullable(type.nullable);
// then the type name, with appropriate decoration if it's repeatable
if (this._inFunctionSignatureParams) {
result += this._repeatableInSignature(type);
} else {
result += this._repeatable(type);
}
// then deal with the following properties, in order
// these go within the signature's parens, in this order
var props = [
'optional',
'canContain',
'signature'
'new',
'this',
'params'
];
var prop;
this._inFunctionSignatureParams = true;
for (var i = 0, l = props.length; i < l; i++) {
prop = props[i];
if (type[prop]) {
result += this[prop](type[prop]);
param = this[prop](type[prop]);
if (param.length > 0) {
params.push(param);
}
}
this._inFunctionSignatureParams = false;
result = 'function(' + params.join(', ') + ')';
result += this.result(type.result);
return result;
};
Stringifier.prototype._union = function(types) {
if (!types) {
return '';
}
var result = [];
for (var i = 0, l = types.length; i < l; i++) {
result.push(this.type(types[i]));
}
return util.format('(%s)', result.join('|'));
};
module.exports = function(type) {
return new Stringifier().stringify(type);
};
{
"version": "0.2.0",
"version": "0.3.0",
"name": "catharsis",

@@ -14,4 +14,5 @@ "description": "A JavaScript parser for Google Closure Compiler type expressions.",

"mocha": "1.6.0",
"pegjs": "0.7.0",
"should": "1.2.0"
"pegjs": "git+ssh://git@github.com:dmajda/pegjs.git#76cc5d55",
"should": "1.2.2",
"underscore": "1.4.4"
},

@@ -18,0 +19,0 @@ "engines": {

@@ -8,43 +8,20 @@ # Catharsis #

+ **Accurate**. Catharsis is based on a [PEG.js](http://pegjs.majda.cz/) grammar that can handle
any valid type expression, no matter how complex. It uses a
[Mocha](http://visionmedia.github.com/mocha/) test suite to verify the parser's accuracy.
+ **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 provides both asynchronous and synchronous interfaces. In addition, it can
convert parse results back into type expressions.
+ **Flexible**. Catharsis can also convert parse results back into type expressions.
## Examples ##
## Example ##
Asynchronous interface:
var catharsis = require('catharsis');
var type = '!Object';
catharsis.parse(type, {}, function(error, parsed) {
if (error) {
console.error('unable to parse %s: %s', type, error);
} else {
console.log('%j', parsed); // {"typeName":"Object","nullable":false}
catharsis.stringify(parsed, {}, function(error, expr) {
if (error) {
console.error('unable to stringify %j: %s', parsed, error);
} else {
console.log('%s', expr); // !Object
}
});
}
});
Synchronous interface:
var catharsis = require('catharsis');
var type = '!Object';
var parsed;
var parsedType;
var expr;
try {
parsed = catharsis.parseSync('!Object'); // {"typeName":"Object","nullable":false}
parsedType = catharsis.parse('!Object');
console.log('%j', parsedType); // {"type":"NameExpression,"name":"Object","nullable":false}
}

@@ -55,8 +32,4 @@ catch(e) {

try {
expr = catharsis.stringifySync(parsed); // !Object
}
catch(e) {
console.error('unable to parse %j: %s', parsed, e);
}
expr = catharsis.stringify(parsedType);
console.log(expr); // !Object

@@ -66,34 +39,5 @@ See the `test/specs/` directory for more examples of Catharsis' parse results.

## Asynchronous interface ##
## Methods ##
### parse(type, opts, callback) ###
Parse the Closure Compiler type expression `type`, and pass the parse results to the callback.
#### Parameters ####
+ `type`: A string containing a Closure Compiler type expression.
+ `opts`: Options for parsing the type expression.
+ `opts.useCache`: Specifies whether to use the cache of parsed types. Defaults to `true`.
+ `callback(error, parsedType)`: Parse results.
+ `error`: A description of the error, if any.
+ `parsedType`: An object containing the parse results.
### stringify(parsedType, opts, callback) ###
Stringify the parsed Closure Compiler type expression `parsedType`, and pass the type expression to
the callback.
#### Parameters ####
+ `parsedType`: An object containing a parsed Closure Compiler type expression.
+ `opts`: Options for stringifying the parse results.
+ `opts.useCache`: Specifies whether to use the cache of stringified parse results. Defaults to
`true`.
+ `opts.validate`: Specifies whether to validate the stringified parse results by attempting to
parse them as a type expression. Defaults to `false`.
+ `callback(error, typeExpr)`: Stringification results.
+ `error`: A description of the error, if any.
+ `typeExpr`: A string containing the type expression.
## Synchronous interface ##
### parseSync(type, opts) ###
### parse(type, opts) ###
Parse the Closure Compiler type `type`, and return the parse results. Throws an error if the type

@@ -110,3 +54,3 @@ cannot be parsed.

### stringifySync(parsedType, opts) ###
### stringify(parsedType, opts) ###
Stringify the parsed Closure Compiler type expression `parsedType`, and return the type expression.

@@ -152,2 +96,22 @@ If validation is enabled, throws an error if the stringified type expression cannot be parsed.

+ 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):

@@ -154,0 +118,0 @@ + Added `stringify()` and `stringifySync()` methods, which convert a parsed type to a type

@@ -6,19 +6,14 @@ /*global describe: true, it: true */

var should = require('should');
var Types = catharsis.Types;
var simpleType = 'foo';
var invalidType = '{*<?';
var simpleParsedType = {typeName: 'string'};
var simpleParsedType = {
type: Types.NameExpression,
name: 'string'
};
var invalidParsedType = {
typeName: 'bogus',
nullable: false,
optional: false,
repeatable: true,
canContain: [
{
typeName: 'bah'
}
],
signature: {
parameters: 'whatever'
}
type: Types.NameExpression,
applications: {},
params: 'whatever'
};

@@ -36,35 +31,4 @@

it('should return an object when given basic input', function(done) {
catharsis.parse(simpleType, {}, function(error, obj) {
should.not.exist(error);
should.exist(obj);
obj.should.be.a('object');
done();
});
});
it('should return an error when given an invalid type', function(done) {
catharsis.parse(invalidType, {}, function(error) {
should.exist(error);
// now make sure the cache isn't somehow causing the error to be suppressed
catharsis.parse(invalidType, {}, function(error) {
should.exist(error);
done();
});
});
});
});
describe('parseSync()', function() {
it('should exist', function() {
should.exist(catharsis.parseSync);
});
it('should be a function', function() {
catharsis.parseSync.should.be.a('function');
});
it('should return an object when given basic input', function() {
catharsis.parseSync(simpleType).should.be.a('object');
catharsis.parse(simpleType).should.be.a('object');
});

@@ -74,5 +38,5 @@

function invalid() {
catharsis.parseSync(invalidType);
catharsis.parse(invalidType);
}
invalid.should.throw();

@@ -91,30 +55,4 @@ });

it('should return a string when given basic input', function(done) {
catharsis.stringify(simpleParsedType, {}, function(error, typeExpr) {
should.not.exist(error);
typeExpr.should.be.a('string');
done();
});
});
it('should return an error when given invalid input if validation is enabled',
function(done) {
catharsis.stringify(invalidParsedType, {validate: true}, function(error) {
should.exist(error);
done();
});
});
});
describe('stringifySync()', function() {
it('should exist', function() {
should.exist(catharsis.stringify);
});
it('should be a function', function() {
catharsis.stringify.should.be.a('function');
});
it('should return a string when given basic input', function() {
catharsis.stringifySync(simpleParsedType).should.be.a('string');
catharsis.stringify(simpleParsedType).should.be.a('string');
});

@@ -124,3 +62,3 @@

function invalid() {
catharsis.stringifySync(invalidParsedType, {validate: true});
catharsis.stringify(invalidParsedType, {validate: true});
}

@@ -130,3 +68,12 @@

});
it('should not throw an error when given invalid input if validation is disabled',
function() {
function invalid() {
catharsis.stringify(invalidParsedType);
}
invalid.should.not.throw();
});
});
});
/*global describe: true, it: true */
'use strict';
var _ = require('underscore');
var fs = require('fs');
var helper = require('./helper');
var parse = require('../lib/parser').parse;
var path = require('path');
var should = require('should');
var util = require('util');
function parseIt(item) {
var parsed;
try {
parsed = parse(item[1]);
} catch(e) {
throw new Error(util.format('unable to parse type expression "%s": %s', item[1],
e.message));
}
if (!_.isEqual(parsed, item[2])) {
throw new Error(util.format('parse tree should be "%j", NOT "%j"', item[2], parsed));
}
}
// each item in the 'types' array looks like:

@@ -12,16 +32,16 @@ // item[0]: {string} description

// item[2]: {object} expected parsed type
function checkTypes(name) {
var types = require('./specs/' + name);
function checkTypes(filepath) {
var types = require(filepath);
types.map(function(item) {
var parsed;
var errors = [];
function parseIt() {
parsed = parse(item[1]);
types.forEach(function(type) {
try {
parseIt(type);
} catch(e) {
errors.push(e.message);
}
});
parseIt.should.not.throw();
parsed.should.eql(item[2]);
});
errors.should.eql([]);
}

@@ -31,27 +51,10 @@

describe('parse()', function() {
// TODO: instead of listing each spec, we should read the specs directory and do a foreach()
it('can parse basic types', function() {
checkTypes('basic');
});
it('can parse type applications', function() {
checkTypes('type-application');
});
it('can parse type unions', function() {
checkTypes('type-union');
});
it('can parse record types', function() {
checkTypes('record-type');
});
it('can parse nullable and non-nullable types', function() {
checkTypes('nullable');
});
it('can parse function types', function() {
checkTypes('function-type');
});
function tester(specPath, basename) {
it('can parse types in the "' + basename + '" spec', function() {
checkTypes(path.join(specPath, basename));
});
}
helper.testSpecs(tester);
});
});
'use strict';
var Types = require('../../lib/types');
module.exports = [

@@ -8,3 +10,4 @@ [

{
typeName: 'boolean'
type: Types.NameExpression,
name: 'boolean'
}

@@ -16,3 +19,4 @@ ],

{
typeName: 'Window'
type: Types.NameExpression,
name: 'Window'
}

@@ -24,3 +28,4 @@ ],

{
typeName: 'goog.ui.Menu'
type: Types.NameExpression,
name: 'goog.ui.Menu'
}

@@ -32,3 +37,4 @@ ],

{
typeName: 'number',
type: Types.NameExpression,
name: 'number',
repeatable: true

@@ -41,3 +47,4 @@ }

{
typeName: 'number',
type: Types.NameExpression,
name: 'number',
optional: true

@@ -50,6 +57,35 @@ }

{
typeName: 'Object',
type: Types.NameExpression,
name: 'Object',
optional: true
}
],
[
'null',
'null',
{
type: Types.NullLiteral
}
],
[
'undefined',
'undefined',
{
type: Types.UndefinedLiteral
}
],
[
'all',
'*',
{
type: Types.AllLiteral
}
],
[
'unknown',
'?',
{
type: Types.UnknownLiteral
}
]
];
'use strict';
var Types = require('../../lib/types');
module.exports = [

@@ -8,14 +10,33 @@ [

{
typeName: 'function',
signature: {
parameters: [
{
name: undefined,
typeName: 'string'
},
{
name: undefined,
typeName: 'boolean'
}
]
type: Types.FunctionType,
params: [
{
type: Types.NameExpression,
name: 'string'
},
{
type: Types.NameExpression,
name: 'boolean'
}
]
}
],
[
'function with two basic parameters and a return value',
'function(string, string): boolean',
{
type: Types.FunctionType,
params: [
{
type: Types.NameExpression,
name: 'string'
},
{
type: Types.NameExpression,
name: 'string'
}
],
result: {
type: Types.NameExpression,
name: 'boolean'
}

@@ -25,11 +46,24 @@ }

[
'optional function with one basic parameter',
'function(string)=',
{
type: Types.FunctionType,
params: [
{
type: Types.NameExpression,
name: 'string'
}
],
optional: true
}
],
[
'function with no parameters and a return value',
'function(): number',
{
typeName: 'function',
signature: {
parameters: [],
funcReturns: {
typeName: 'number'
}
type: Types.FunctionType,
params: [],
result: {
type: Types.NameExpression,
name: 'number'
}

@@ -42,11 +76,12 @@ }

{
typeName: 'function',
signature: {
parameters: [
{
name: undefined,
typeName: 'string'
}
],
funcThis: 'goog.ui.Menu'
type: Types.FunctionType,
params: [
{
type: Types.NameExpression,
name: 'string'
}
],
'this': {
type: Types.NameExpression,
name: 'goog.ui.Menu'
}

@@ -59,11 +94,12 @@ }

{
typeName: 'function',
signature: {
parameters: [
{
name: undefined,
typeName: 'string'
}
],
funcNew: 'goog.ui.Menu'
type: Types.FunctionType,
params: [
{
type: Types.NameExpression,
name: 'string'
}
],
'new': {
type: Types.NameExpression,
name: 'goog.ui.Menu'
}

@@ -73,21 +109,120 @@ }

[
'function with a variable number of parameters and a return value',
'function with a fixed parameter, followed by a variable number of parameters, as well ' +
'as a return value',
'function(string, ...[number]): number',
{
typeName: 'function',
signature: {
parameters: [
type: Types.FunctionType,
params: [
{
type: Types.NameExpression,
name: 'string'
},
{
type: Types.NameExpression,
name: 'number',
repeatable: true
}
],
result: {
type: Types.NameExpression,
name: 'number'
}
}
],
[
'function with a variable number of parameters containing the value `null`',
'function(...[null])',
{
type: Types.FunctionType,
params: [
{
type: Types.NullLiteral,
repeatable: true
}
]
}
],
[
'function with a variable number of parameters containing the value `undefined`',
'function(...[undefined])',
{
type: Types.FunctionType,
params: [
{
type: Types.UndefinedLiteral,
repeatable: true
}
]
}
],
[
'function with a variable number of parameters, a "new" type, a "this" type, and a ' +
'return value',
'function(new:Master, this:Everyone, string, goog.ui.Menu, Array.<Object>, ...[string]): ' +
'boolean',
{
type: Types.FunctionType,
params: [
{
type: Types.NameExpression,
name: 'string'
},
{
type: Types.NameExpression,
name: 'goog.ui.Menu'
},
{
type: Types.TypeApplication,
expression: {
type: Types.NameExpression,
name: 'Array'
},
applications: [
{
type: Types.NameExpression,
name: 'Object'
}
]
},
{
type: Types.NameExpression,
name: 'string',
repeatable: true
}
],
'new': {
type: Types.NameExpression,
name: 'Master'
},
'this': {
type: Types.NameExpression,
name: 'Everyone'
},
result: {
type: Types.NameExpression,
name: 'boolean'
}
}
],
// The following type expressions are adapted from the Closure Compiler test suite:
// http://goo.gl/rgKSk
[
'function that returns a type union',
'function(): (number|string)',
{
type: Types.FunctionType,
params: [],
result: {
type: Types.TypeUnion,
elements: [
{
name: undefined,
typeName: 'string'
type: Types.NameExpression,
name: 'number'
},
{
name: undefined,
typeName: 'number',
repeatable: true
type: Types.NameExpression,
name: 'string'
}
],
funcReturns: {
typeName: 'number'
}
]
}

@@ -97,41 +232,177 @@ }

[
'function with a variable number of named/unnamed parameters, a "new" type, ' +
'a "this" type, and a return value',
'function(new:Master, this:Everyone, string, foo:goog.ui.Menu, bar:Array.<Object>, ' +
'...[baz:string]): boolean',
'function with no parameters and no return value',
'function()',
{
typeName: 'function',
signature: {
parameters: [
type: Types.FunctionType,
params: []
}
],
[
'function with a variable number of parameters containing any values',
'function(...[*])',
{
type: Types.FunctionType,
params: [
{
type: Types.AllLiteral,
repeatable: true
}
]
}
],
[
'function with a "this" type that returns a type union',
'function(this:Object): (number|string)',
{
type: Types.FunctionType,
params: [],
'this': {
type: Types.NameExpression,
name: 'Object'
},
result: {
type: Types.TypeUnion,
elements: [
{
name: undefined,
typeName: 'string'
type: Types.NameExpression,
name: 'number'
},
{
name: 'foo',
typeName: 'goog.ui.Menu'
type: Types.NameExpression,
name: 'string'
}
]
}
}
],
[
'function with a "this" type that is a type union, and that returns a type union',
'function(this:(Array|Date)): (number|string)',
{
type: Types.FunctionType,
params: [],
'this': {
type: Types.TypeUnion,
elements: [
{
type: Types.NameExpression,
name: 'Array'
},
{
name: 'bar',
typeName: 'Array',
canContain: [
{
typeName: 'Object'
}
]
type: Types.NameExpression,
name: 'Date'
}
]
},
result: {
type: Types.TypeUnion,
elements: [
{
type: Types.NameExpression,
name: 'number'
},
{
name: 'baz',
typeName: 'string',
repeatable: true
type: Types.NameExpression,
name: 'string'
}
],
funcNew: 'Master',
funcThis: 'Everyone',
funcReturns: {
typeName: 'boolean'
]
}
}
],
[
'function with a "new" type and a variable number of params that accept all types, ' +
'returning a name expression',
'function(new:Array, ...[*]): Array',
{
type: Types.FunctionType,
params: [
{
type: Types.AllLiteral,
repeatable: true
}
],
'new': {
type: Types.NameExpression,
name: 'Array'
},
result: {
type: Types.NameExpression,
name: 'Array'
}
}
],
[
'function with a "new" type that accepts an optional parameter of any type, as well as a ' +
'return value',
'function(new:Boolean, *=): boolean',
{
type: Types.FunctionType,
params: [
{
type: Types.AllLiteral,
optional: true
}
],
'new': {
type: Types.NameExpression,
name: 'Boolean'
},
result: {
type: Types.NameExpression,
name: 'boolean'
}
}
],
[
'function with a variable number of parameters and a return value',
'function(...[number]): boolean',
{
type: Types.FunctionType,
params: [
{
type: Types.NameExpression,
name: 'number',
repeatable: true
}
],
result: {
type: Types.NameExpression,
name: 'boolean'
}
}
],
[
'function with a "this" type and a parameter that returns a type union',
'function(this:Date, number): (boolean|number|string)',
{
type: Types.FunctionType,
params: [
{
type: Types.NameExpression,
name: 'number'
}
],
'this': {
type: Types.NameExpression,
name: 'Date'
},
result: {
type: Types.TypeUnion,
elements: [
{
type: Types.NameExpression,
name: 'boolean'
},
{
type: Types.NameExpression,
name: 'number'
},
{
type: Types.NameExpression,
name: 'string'
}
]
}
}
]
];
'use strict';
var Types = require('../../lib/types');
module.exports = [

@@ -8,3 +10,4 @@ [

{
typeName: 'number',
type: Types.NameExpression,
name: 'number',
nullable: true

@@ -17,3 +20,4 @@ }

{
typeName: 'Object',
type: Types.NameExpression,
name: 'Object',
nullable: false

@@ -20,0 +24,0 @@ }

'use strict';
var Types = require('../../lib/types');
module.exports = [
[
'empty record type',
'{}',
{
type: Types.RecordType,
fields: []
}
],
[
'record type with 1 typed property',
'{myNum: number}',
{
typeName: 'object',
properties: [
type: Types.RecordType,
fields: [
{
name: 'myNum',
typeName: 'number'
type: Types.FieldType,
key: {
type: Types.NameExpression,
name: 'myNum'
},
value: {
type: Types.NameExpression,
name: 'number'
}
}

@@ -18,18 +35,280 @@ ]

[
'optional record type with 1 typed property',
'{myNum: number}=',
{
type: Types.RecordType,
fields: [
{
type: Types.FieldType,
key: {
type: Types.NameExpression,
name: 'myNum'
},
value: {
type: Types.NameExpression,
name: 'number'
}
}
],
optional: true
}
],
[
'nullable record type with 1 typed property',
'?{myNum: number}',
{
type: Types.RecordType,
fields: [
{
type: Types.FieldType,
key: {
type: Types.NameExpression,
name: 'myNum'
},
value: {
type: Types.NameExpression,
name: 'number'
}
}
],
nullable: true
}
],
[
'non-nullable record type with 1 typed property',
'!{myNum: number}',
{
type: Types.RecordType,
fields: [
{
type: Types.FieldType,
key: {
type: Types.NameExpression,
name: 'myNum'
},
value: {
type: Types.NameExpression,
name: 'number'
}
}
],
nullable: false
}
],
[
'record type with 1 typed property and 1 untyped property',
'{myNum: number, myObject}',
{
typeName: 'object',
properties: [
type: Types.RecordType,
fields: [
{
name: 'myNum',
typeName: 'number'
type: Types.FieldType,
key: {
type: Types.NameExpression,
name: 'myNum'
},
value: {
type: Types.NameExpression,
name: 'number'
}
},
{
name: 'myObject',
typeName: undefined
type: Types.FieldType,
key: {
type: Types.NameExpression,
name: 'myObject'
},
value: undefined
}
]
}
],
[
'record type with a property that uses a type application as a key',
'{Array.<string>: number}',
{
type: Types.RecordType,
fields: [
{
type: Types.FieldType,
key: {
type: Types.TypeApplication,
expression: {
type: Types.NameExpression,
name: 'Array'
},
applications: [
{
type: Types.NameExpression,
name: 'string'
}
]
},
value: {
type: Types.NameExpression,
name: 'number'
}
}
]
}
],
[
'record type with a property that uses a type application as a value',
'{myArray: Array.<string>}',
{
type: Types.RecordType,
fields: [
{
type: Types.FieldType,
key: {
type: Types.NameExpression,
name: 'myArray'
},
value: {
type: Types.TypeApplication,
expression: {
type: Types.NameExpression,
name: 'Array'
},
applications: [
{
type: Types.NameExpression,
name: 'string'
}
]
}
}
]
}
],
[
'record type with a property that uses a type union as a key',
'{(number|boolean|string): number}',
{
type: Types.RecordType,
fields: [
{
type: Types.FieldType,
key: {
type: Types.TypeUnion,
elements: [
{
type: Types.NameExpression,
name: 'number'
},
{
type: Types.NameExpression,
name: 'boolean'
},
{
type: Types.NameExpression,
name: 'string'
}
]
},
value: {
type: Types.NameExpression,
name: 'number'
}
}
]
}
],
[
'record type with a property that uses a type union as a value',
'{myKey: (number|boolean|string)}',
{
type: Types.RecordType,
fields: [
{
type: Types.FieldType,
key: {
type: Types.NameExpression,
name: 'myKey'
},
value: {
type: Types.TypeUnion,
elements: [
{
type: Types.NameExpression,
name: 'number'
},
{
type: Types.NameExpression,
name: 'boolean'
},
{
type: Types.NameExpression,
name: 'string'
}
]
}
}
]
}
],
[
'record type with a property that uses a JavaScript keyword as a key',
'{continue: string}',
{
type: Types.RecordType,
fields: [
{
type: Types.FieldType,
key: {
type: Types.NameExpression,
name: 'continue',
reservedWord: true
},
value: {
type: Types.NameExpression,
name: 'string'
}
}
]
}
],
[
'record type with a property that uses a JavaScript future reserved word as a key',
'{class: string}',
{
type: Types.RecordType,
fields: [
{
type: Types.FieldType,
key: {
type: Types.NameExpression,
name: 'class',
reservedWord: true
},
value: {
type: Types.NameExpression,
name: 'string'
}
}
]
}
],
[
'record type with a property that uses a string representation of a JavaScript boolean ' +
'literal as a key',
'{true: string}',
{
type: Types.RecordType,
fields: [
{
type: Types.FieldType,
key: {
type: Types.NameExpression,
name: 'true',
reservedWord: true
},
value: {
type: Types.NameExpression,
name: 'string'
}
}
]
}
]
];
'use strict';
var Types = require('../../lib/types');
module.exports = [

@@ -8,6 +10,11 @@ [

{
typeName: 'Array',
canContain: [
type: Types.TypeApplication,
expression: {
type: Types.NameExpression,
name: 'Array'
},
applications: [
{
typeName: 'string'
type: Types.NameExpression,
name: 'string'
}

@@ -21,9 +28,15 @@ ]

{
typeName: 'Object',
canContain: [
type: Types.TypeApplication,
expression: {
type: Types.NameExpression,
name: 'Object'
},
applications: [
{
typeName: 'string'
type: Types.NameExpression,
name: 'string'
},
{
typeName: 'number'
type: Types.NameExpression,
name: 'number'
}

@@ -34,13 +47,93 @@ ]

[
'object whose properties are a type application and property values are a type union',
'Object.<Array.<(boolean|{myKey: Error})>, (boolean|string|function(new:foo): string)>',
{
type: Types.TypeApplication,
expression: {
type: Types.NameExpression,
name: 'Object'
},
applications: [
{
type: Types.TypeApplication,
expression: {
type: Types.NameExpression,
name: 'Array'
},
applications: [
{
type: Types.TypeUnion,
elements: [
{
type: Types.NameExpression,
name: 'boolean'
},
{
type: Types.RecordType,
fields: [
{
type: Types.FieldType,
key: {
type: Types.NameExpression,
name: 'myKey'
},
value: {
type: Types.NameExpression,
name: 'Error'
}
}
]
}
]
}
]
},
{
type: Types.TypeUnion,
elements: [
{
type: Types.NameExpression,
name: 'boolean'
},
{
type: Types.NameExpression,
name: 'string'
},
{
type: Types.FunctionType,
params: [],
'new': {
type: Types.NameExpression,
name: 'foo'
},
result: {
type: Types.NameExpression,
name: 'string'
}
}
]
}
]
}
],
[
'array of objects that have a length property',
'Array.<{length}>',
{
typeName: 'Array',
canContain: [
type: Types.TypeApplication,
expression: {
type: Types.NameExpression,
name: 'Array'
},
applications: [
{
typeName: 'object',
properties: [
type: Types.RecordType,
fields: [
{
name: 'length',
typeName: undefined
type: Types.FieldType,
key: {
type: Types.NameExpression,
name: 'length'
},
value: undefined
}

@@ -51,3 +144,19 @@ ]

}
],
[
'array of unknown type',
'Array.<?>',
{
type: Types.TypeApplication,
expression: {
type: Types.NameExpression,
name: 'Array'
},
applications: [
{
type: Types.UnknownLiteral
}
]
}
]
];
'use strict';
var Types = require('../../lib/types');
module.exports = [

@@ -7,10 +9,15 @@ [

'(number|boolean)',
[
{
typeName: 'number'
},
{
typeName: 'boolean'
}
]
{
type: Types.TypeUnion,
elements: [
{
type: Types.NameExpression,
name: 'number'
},
{
type: Types.NameExpression,
name: 'boolean'
}
]
}
],

@@ -20,10 +27,14 @@ [

'(Object|undefined)',
[
{
typeName: 'Object'
},
{
typeName: 'undefined'
}
]
{
type: Types.TypeUnion,
elements: [
{
type: Types.NameExpression,
name: 'Object'
},
{
type: Types.UndefinedLiteral
}
]
}
],

@@ -33,14 +44,165 @@ [

'(number|Window|goog.ui.Menu)',
[
{
typeName: 'number'
},
{
typeName: 'Window'
},
{
typeName: 'goog.ui.Menu'
}
]
{
type: Types.TypeUnion,
elements: [
{
type: Types.NameExpression,
name: 'number'
},
{
type: Types.NameExpression,
name: 'Window'
},
{
type: Types.NameExpression,
name: 'goog.ui.Menu'
}
]
}
],
[
'nullable union with 2 types (number and boolean)',
'?(number|boolean)',
{
type: Types.TypeUnion,
elements: [
{
type: Types.NameExpression,
name: 'number'
},
{
type: Types.NameExpression,
name: 'boolean'
}
],
nullable: true
}
],
[
'non-nullable union with 2 types (number and boolean)',
'!(number|boolean)',
{
type: Types.TypeUnion,
elements: [
{
type: Types.NameExpression,
name: 'number'
},
{
type: Types.NameExpression,
name: 'boolean'
}
],
nullable: false
}
],
[
'optional union with 2 types (number and boolean)',
'(number|boolean)=',
{
type: Types.TypeUnion,
elements: [
{
type: Types.NameExpression,
name: 'number'
},
{
type: Types.NameExpression,
name: 'boolean'
}
],
optional: true
}
],
// The following type expressions are adapted from the Closure Compiler test suite:
// http://goo.gl/vpRTe, http://goo.gl/DVh3f
[
'union with 2 types (array and object with unknown value type)',
'(Array|Object.<string, ?>)',
{
type: Types.TypeUnion,
elements: [
{
type: Types.NameExpression,
name: 'Array'
},
{
type: Types.TypeApplication,
expression: {
type: Types.NameExpression,
name: 'Object'
},
applications: [
{
type: Types.NameExpression,
name: 'string'
},
{
type: Types.UnknownLiteral
}
]
}
]
}
],
[
'union with 2 type applications',
'(Array.<string>|Object.<string, ?>)',
{
type: Types.TypeUnion,
elements: [
{
type: Types.TypeApplication,
expression: {
type: Types.NameExpression,
name: 'Array'
},
applications: [
{
type: Types.NameExpression,
name: 'string'
}
]
},
{
type: Types.TypeApplication,
expression: {
type: Types.NameExpression,
name: 'Object'
},
applications: [
{
type: Types.NameExpression,
name: 'string'
},
{
type: Types.UnknownLiteral
}
]
}
]
}
],
[
'union with 2 types (an error, or a function that returns an error)',
'(Error|function(): Error)',
{
type: Types.TypeUnion,
elements: [
{
type: Types.NameExpression,
name: 'Error'
},
{
type: Types.FunctionType,
params: [],
result: {
type: Types.NameExpression,
name: 'Error'
}
}
]
}
]
];
/*global describe: true, it: true */
'use strict';
var helper = require('./helper');
var parse = require('../lib/parser').parse;
var path = require('path');
var should = require('should');
var stringify = require('../lib/stringify');
var util = require('util');
// each item in the 'types' array looks like:
// each item looks like:
// item[0]: {string} description
// item[1]: {string} type
// item[2]: {object} expected parsed type
function checkStringifiedTypes(name) {
var types = require('./specs/' + name);
function stringifyIt(item) {
var string = stringify(item[2]);
if (string !== item[1]) {
throw new Error(util.format('type expression "%s" was stringified as "%s"', item[1],
string));
}
types.map(function(item) {
var string;
function stringifyIt() {
string = stringify(item[2]);
parse(string);
}
stringifyIt.should.not.throw();
string.should.eql(item[1]);
});
try {
parse(string);
} catch(e) {
throw new Error(util.format('unable to parse string "%s", created from %j: %s', string,
item[2], e.message));
}
}
describe('stringify', function() {
// TODO: instead of listing each spec, we should read the specs directory and do a foreach()
it('can stringify basic types', function() {
checkStringifiedTypes('basic');
});
function checkStringifiedTypes(filepath) {
var types = require(filepath);
it('can stringify type applications', function() {
checkStringifiedTypes('type-application');
});
var errors = [];
it('can stringify type unions', function() {
checkStringifiedTypes('type-union');
types.forEach(function(type) {
try {
stringifyIt(type);
} catch(e) {
errors.push(e.message);
}
});
it('can stringify record types', function() {
checkStringifiedTypes('record-type');
});
errors.should.eql([]);
}
it('can stringify nullable and non-nullable types', function() {
checkStringifiedTypes('nullable');
});
it('can stringify function types', function() {
checkStringifiedTypes('function-type');
});
describe('stringify', function() {
function tester(specPath, basename) {
it('can stringify types in the "' + basename + '" spec', function() {
checkStringifiedTypes(path.join(specPath, basename));
});
}
helper.testSpecs(tester);
});

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc