Merge bower-endpoint-parser repository

This commit is contained in:
Adam Stankiewicz
2016-06-10 14:24:55 +02:00
9 changed files with 619 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

View File

@@ -0,0 +1,2 @@
/node_modules
/npm-debug.*

View File

@@ -0,0 +1,61 @@
{
"predef": [
"console",
"describe",
"it",
"after",
"afterEach",
"before",
"beforeEach"
],
"indent": 4,
"node": true,
"devel": true,
"bitwise": false,
"curly": false,
"eqeqeq": true,
"forin": false,
"immed": true,
"latedef": false,
"newcap": true,
"noarg": true,
"noempty": false,
"nonew": true,
"plusplus": false,
"regexp": false,
"undef": true,
"unused": true,
"quotmark": "single",
"strict": false,
"trailing": true,
"camelcase": true,
"asi": false,
"boss": true,
"debug": false,
"eqnull": true,
"esnext": false,
"evil": false,
"expr": true,
"funcscope": false,
"globalstrict": false,
"iterator": false,
"lastsemic": false,
"laxbreak": true,
"laxcomma": false,
"loopfunc": true,
"multistr": false,
"onecase": true,
"regexdash": false,
"scripturl": false,
"smarttabs": false,
"shadow": false,
"sub": false,
"supernew": true,
"validthis": false,
"nomen": false,
"white": true
}

View File

@@ -0,0 +1,4 @@
language: node_js
node_js:
- "0.10"
- "0.8"

View File

@@ -0,0 +1,19 @@
Copyright (c) 2012 Twitter 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

@@ -0,0 +1,102 @@
# endpoint-parser [![Build Status](https://secure.travis-ci.org/bower/endpoint-parser.png?branch=master)](http://travis-ci.org/bower/endpoint-parser)
Little module that helps with endpoints parsing.
## API
### .decompose(endpoint)
Decomposes a endpoint into `name`, `source` and `target`.
```js
var endpointParser = require('bower-endpoint-parser');
endpointParser.decompose('jquery#~2.0.0');
// { name: '', source: 'jquery', target: '~2.0.0' }
endpointParser.decompose('backbone=backbone-amd#~1.0.0');
// { name: 'backbone', source: 'backbone-amd', target: '~1.0.0' }
endpointParser.decompose('http://twitter.github.io/bootstrap/assets/bootstrap.zip');
// { name: '', source: 'http://twitter.github.io/bootstrap/assets/bootstrap', target: '*' }
endpointParser.decompose('bootstrap=http://twitter.github.io/bootstrap/assets/bootstrap.zip');
// { name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap', target: '*' }
```
### .compose(decEndpoint)
Inverse of `decompose()`.
Takes a decomposed endpoint and composes it back into a string.
```js
var endpointParser = require('bower-endpoint-parser');
endpointParser.compose({ name: '', source: 'jquery', target: '~2.0.0' });
// jquery#~2.0.0
endpointParser.compose({ name: 'backbone', source: 'backbone-amd', target: '~1.0.0' });
// backbone=backbone-amd#~1.0.0
endpointParser.compose({ name: '', source: 'http://twitter.github.io/bootstrap/assets/bootstrap', target: '*' });
// http://twitter.github.io/bootstrap/assets/bootstrap.zip
endpointParser.compose({ name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap', target: '*' });
// bootstrap=http://twitter.github.io/bootstrap/assets/bootstrap.zip
```
### .json2decomposed(key, value)
Similar to `decompose()` but specially designed to be used when parsing `bower.json` dependencies.
For instance, in a `bower.json` like this:
```js
{
"name": "foo",
"version": "0.1.0",
"dependencies": {
"jquery": "~1.9.1",
"backbone": "backbone-amd#~1.0.0",
"bootstrap": "http://twitter.github.io/bootstrap/assets/bootstrap"
}
}
```
You would call `json2decomposed` like so:
```js
endpointParser.json2decomposed('jquery', '~1.9.1');
// { name: 'jquery', source: 'jquery', target: '~1.9.1' }
endpointParser.json2decomposed('backbone', 'backbone-amd#~1.0.0');
// { name: 'backbone', source: 'backbone-amd', target: '~1.0.0' }
endpointParser.json2decomposed('bootstrap', 'http://twitter.github.io/bootstrap/assets/bootstrap');
// { name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap', target: '*' }
```
### .decomposed2json(decEndpoint)
Inverse of `json2decomposed()`.
Takes a decomposed endpoint and composes it to be saved to `bower.json`.
```js
var endpointParser = require('bower-endpoint-parser');
endpointParser.decomposed2json({ name: 'jquery', source: 'jquery', target: '~2.0.0' });
// { jquery: '~2.0.0' }
endpointParser.decomposed2json({ name: 'backbone', source: 'backbone-amd', target: '~1.0.0' });
// { backbone: 'backbone-amd#~2.0.0' }
endpointParser.decomposed2json({ name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap', target: '*' });
// { bootstrap: 'http://twitter.github.io/bootstrap/assets/bootstrap' }
```
This function throws an exception if the `name` from the decomposed endpoint is empty.
## License
Released under the [MIT License](http://www.opensource.org/licenses/mit-license.php).

View File

@@ -0,0 +1,128 @@
function decompose(endpoint) {
// Note that we allow spaces in targets and sources but they are trimmed
var regExp = /^(?:([\w\-]|(?:[\w\.\-]+[\w\-])?)=)?([^\|#]+)(?:#(.*))?$/;
var matches = endpoint.match(regExp);
var target;
var error;
if (!matches) {
error = new Error('Invalid endpoint: ' + endpoint);
error.code = 'EINVEND';
throw error;
}
target = trim(matches[3]);
return {
name: trim(matches[1]),
source: trim(matches[2]),
target: isWildcard(target) ? '*' : target
};
}
function compose(decEndpoint) {
var name = trim(decEndpoint.name);
var source = trim(decEndpoint.source);
var target = trim(decEndpoint.target);
var composed = '';
if (name) {
composed += name + '=';
}
composed += source;
if (!isWildcard(target)) {
composed += '#' + target;
}
return composed;
}
function json2decomposed(key, value) {
var endpoint;
var split;
var error;
key = trim(key);
value = trim(value);
if (!key) {
error = new Error('The key must be specified');
error.code = 'EINVEND';
throw error;
}
endpoint = key + '=';
split = value.split('#').map(trim);
// If # was found, the source was specified
if (split.length > 1) {
endpoint += (split[0] || key) + '#' + split[1];
// Check if value looks like a source
} else if (isSource(value)) {
endpoint += value + '#*';
// Otherwise use the key as the source
} else {
endpoint += key + '#' + split[0];
}
return decompose(endpoint);
}
function decomposed2json(decEndpoint) {
var error;
var name = trim(decEndpoint.name);
var source = trim(decEndpoint.source);
var target = trim(decEndpoint.target);
var value = '';
var ret = {};
if (!name) {
error = new Error('Decomposed endpoint must have a name');
error.code = 'EINVEND';
throw error;
}
// Add source only if different than the name
if (source !== name) {
value += source;
}
// If value is empty, we append the target always
if (!value) {
if (isWildcard(target)) {
value += '*';
} else {
if (target.indexOf('/') !== -1) {
value += '#' + target;
} else {
value += target;
}
}
// Otherwise append only if not a wildcard or source does not look like a source
} else if (!isWildcard(target) || !isSource(source)) {
value += '#' + (target || '*');
}
ret[name] = value;
return ret;
}
function trim(str) {
return str ? str.trim() : '';
}
function isWildcard(target) {
return !target || target === '*' || target === 'latest';
}
function isSource(value) {
return (/[\/\\@]/).test(value);
}
module.exports.decompose = decompose;
module.exports.compose = compose;
module.exports.json2decomposed = json2decomposed;
module.exports.decomposed2json = decomposed2json;

View File

@@ -0,0 +1,28 @@
{
"name": "bower-endpoint-parser",
"version": "0.2.2",
"description": "Little module that helps with endpoints parsing.",
"author": "Twitter",
"licenses": [
{
"type": "MIT",
"url": "https://github.com/bower/endpoint-parser/blob/master/LICENSE"
}
],
"repository": {
"type": "git",
"url": "git://github.com/bower/endpoint-parser.git"
},
"main": "index.js",
"engines": {
"node": ">=0.8.0"
},
"devDependencies": {
"expect.js": "~0.2.0",
"mocha": "~1.12.0",
"mout": "~0.9.0"
},
"scripts": {
"test": "mocha -R spec"
}
}

View File

@@ -0,0 +1,263 @@
var expect = require('expect.js');
var lang = require('mout/lang');
var object = require('mout/object');
var endpointParser = require('../');
describe('endpoint-parser', function () {
describe('.decompose', function () {
it('should decompose endpoints correctly', function () {
var suite = {
'jquery#~2.0.0': { name: '', source: 'jquery', target: '~2.0.0' },
'jquery#*': { name: '', source: 'jquery', target: '*' },
'jquery#latest': { name: '', source: 'jquery', target: '*' },
'jquery#3dc50c62fe2d2d01afc58e7ad42236a35acff4d8': { name: '', source: 'jquery', target: '3dc50c62fe2d2d01afc58e7ad42236a35acff4d8' },
'jquery#master': { name: '', source: 'jquery', target: 'master' },
'backbone=backbone-amd#~1.0.0': { name: 'backbone', source: 'backbone-amd', target: '~1.0.0' },
'backbone=backbone-amd#latest': { name: 'backbone', source: 'backbone-amd', target: '*' },
'backbone=backbone-amd#*': { name: 'backbone', source: 'backbone-amd', target: '*' },
'http://twitter.github.io/bootstrap/assets/bootstrap.zip': { name: '', source: 'http://twitter.github.io/bootstrap/assets/bootstrap.zip', target: '*' },
'bootstrap=http://twitter.github.io/bootstrap/assets/bootstrap.zip': { name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap.zip', target: '*' },
'bootstrap=http://twitter.github.io/bootstrap/assets/bootstrap.zip#latest': { name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap.zip', target: '*' }
};
object.forOwn(suite, function (decEndpoint, endpoint) {
expect(endpointParser.decompose(endpoint)).to.eql(decEndpoint);
});
});
it('should trim sources and targets', function () {
var decEndpoint = endpointParser.decompose('foo= source # ~1.0.2 ');
expect(decEndpoint.source).to.equal('source');
expect(decEndpoint.target).to.equal('~1.0.2');
decEndpoint = endpointParser.decompose('foo= source # latest');
expect(decEndpoint.source).to.equal('source');
expect(decEndpoint.target).to.equal('*');
decEndpoint = endpointParser.decompose('foo= source # *');
expect(decEndpoint.source).to.equal('source');
expect(decEndpoint.target).to.equal('*');
});
});
describe('.compose', function () {
it('should compose endpoints correctly', function () {
var suite = {
'jquery#~2.0.0': { name: '', source: 'jquery', target: '~2.0.0' },
'jquery': [{ name: '', source: 'jquery', target: '*' }, { name: '', source: 'jquery', target: 'latest' }, { name: '', source: 'jquery', target: '' }],
'jquery#3dc50c62fe2d2d01afc58e7ad42236a35acff4d8': { name: '', source: 'jquery', target: '3dc50c62fe2d2d01afc58e7ad42236a35acff4d8' },
'jquery#master': { name: '', source: 'jquery', target: 'master' },
'backbone=backbone-amd#~1.0.0': { name: 'backbone', source: 'backbone-amd', target: '~1.0.0' },
'backbone=backbone-amd': [{ name: 'backbone', source: 'backbone-amd', target: '*' }, { name: 'backbone', source: 'backbone-amd', target: '*' }, { name: 'backbone', source: 'backbone-amd', target: '' }],
'http://twitter.github.io/bootstrap/assets/bootstrap.zip': { name: '', source: 'http://twitter.github.io/bootstrap/assets/bootstrap.zip', target: '*' },
'bootstrap=http://twitter.github.io/bootstrap/assets/bootstrap.zip': { name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap.zip', target: '*' }
};
object.forOwn(suite, function (decEndpoints, endpoint) {
decEndpoints = lang.toArray(decEndpoints);
decEndpoints.forEach(function (decEndpoint) {
expect(endpointParser.compose(decEndpoint)).to.equal(endpoint);
});
});
});
it('should trim values', function () {
expect(endpointParser.compose({
name: ' foo ',
source: ' bar ',
target: ' ~1.0.2 '
})).to.equal('foo=bar#~1.0.2');
expect(endpointParser.compose({
name: ' foo ',
source: ' foo ',
target: ' ~1.0.2 '
})).to.equal('foo=foo#~1.0.2');
expect(endpointParser.compose({
name: ' foo ',
source: ' foo ',
target: ' * '
})).to.equal('foo=foo');
expect(endpointParser.compose({
name: ' foo ',
source: ' foo ',
target: ' * '
})).to.equal('foo=foo');
expect(endpointParser.compose({
name: ' ',
source: ' foo ',
target: ''
})).to.equal('foo');
});
});
describe('.json2decomposed', function () {
var expected = [
{ name: 'jquery', source: 'jquery', target: '~1.9.1' },
{ name: 'foo', source: 'foo', target: '*' },
{ name: 'bar', source: 'bar', target: '*' },
{ name: 'baz', source: 'baz', target: '~0.2.0' },
{ name: 'backbone', source: 'backbone-amd', target: '~1.0.0' },
{ name: 'backbone2', source: 'backbone=backbone-amd', target: '~1.0.0' },
{ name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap', target: '*' },
{ name: 'bootstrap2', source: 'http://twitter.github.io/bootstrap/assets/bootstrap', target: '*' },
{ name: 'ssh', source: 'git@example.com', target: '*' },
{ name: 'git', source: 'git://example.com', target: '*' },
{ name: 'path', source: '/foo', target: '*' },
{ name: 'winpath', source: 'c:\\foo', target: '*' }
];
it('should decompose json endpoints correctly', function () {
var dependencies = {
jquery: '~1.9.1',
foo: 'latest',
bar: '*',
baz: '#~0.2.0',
backbone: 'backbone-amd#~1.0.0',
backbone2: 'backbone=backbone-amd#~1.0.0',
bootstrap: 'http://twitter.github.io/bootstrap/assets/bootstrap',
bootstrap2: 'http://twitter.github.io/bootstrap/assets/bootstrap#*',
ssh: 'git@example.com',
git: 'git://example.com',
path: '/foo',
winpath: 'c:\\foo'
};
var x = 0;
object.forOwn(dependencies, function (value, key) {
expect(endpointParser.json2decomposed(key, value)).to.eql(expected[x]);
x += 1;
});
});
it('should trim values', function () {
var dependencies = {
' jquery ': ' ~1.9.1 ',
' foo ': ' latest ',
' bar ': ' * ',
' baz ': '# ~0.2.0 ',
' backbone ': ' backbone-amd#~1.0.0 ',
' backbone2 ': ' backbone=backbone-amd # ~1.0.0 ',
' bootstrap ': ' http://twitter.github.io/bootstrap/assets/bootstrap',
' bootstrap2 ': ' http://twitter.github.io/bootstrap/assets/bootstrap # *',
' ssh ': ' git@example.com ',
' git ': ' git://example.com ',
' path ': ' /foo ',
' winpath ': ' c:\\foo '
};
var x = 0;
object.forOwn(dependencies, function (value, key) {
expect(endpointParser.json2decomposed(key, value)).to.eql(expected[x]);
x += 1;
});
});
it('should error out if key is not specified', function () {
try {
endpointParser.json2decomposed(null);
throw new Error('Should have failed');
} catch (e) {
expect(e.code).to.equal('EINVEND');
expect(e.message).to.contain('key must be specified');
}
try {
endpointParser.json2decomposed('');
throw new Error('Should have failed');
} catch (e) {
expect(e.code).to.equal('EINVEND');
expect(e.message).to.contain('key must be specified');
}
});
});
describe('.decomposed2json', function () {
var expected = [
{ jquery: '~1.9.1' },
{ foo: '*' },
{ bar: '*' },
{ baz: '*' },
{ jqueryx: 'jquery#~1.9.1' },
{ jqueryy: 'jquery-x#*' },
{ jqueryy: 'jquery-x#*' },
{ backbone: 'backbone-amd#~1.0.0' },
{ backbone : 'backbone=backbone-amd#~1.0.0' },
{ bootstrap: 'http://twitter.github.io/bootstrap/assets/bootstrap' },
{ bootstrap: 'http://twitter.github.io/bootstrap/assets/bootstrap' },
{ ssh: 'git@example.com' },
{ git: 'git://example.com' },
{ ckeditor: '#full/4.3.3' }
];
it('should compose endpoints to json correctly', function () {
var decEndpoints = [
{ name: 'jquery', source: 'jquery', target: '~1.9.1' },
{ name: 'foo', source: 'foo', target: 'latest' },
{ name: 'bar', source: 'bar', target: '*' },
{ name: 'baz', source: 'baz', target: '' },
{ name: 'jqueryx', source: 'jquery', target: '~1.9.1' },
{ name: 'jqueryy', source: 'jquery-x', target: '' },
{ name: 'jqueryy', source: 'jquery-x', target: '*' },
{ name: 'backbone', source: 'backbone-amd', target: '~1.0.0' },
{ name: 'backbone', source: 'backbone=backbone-amd', target: '~1.0.0' },
{ name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap', target: '' },
{ name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap', target: '*' },
{ name: 'ssh', source: 'git@example.com', target: '*' },
{ name: 'git', source: 'git://example.com', target: '*' },
{ name: 'ckeditor', source: 'ckeditor', target: 'full/4.3.3' }
];
var x = 0;
decEndpoints.forEach(function (decEndpoint) {
expect(endpointParser.decomposed2json(decEndpoint)).to.eql(expected[x]);
x += 1;
});
});
it('should trim values', function () {
var decEndpoints = [
{ name: ' jquery ', source: ' jquery ', target: ' ~1.9.1 ' },
{ name: 'foo', source: ' foo', target: ' latest ' },
{ name: 'bar', source: 'bar ', target: ' * ' },
{ name: 'baz ', source: 'baz', target: ' ' },
{ name: ' jqueryx ', source: ' jquery ', target: ' ~1.9.1 ' },
{ name: ' jqueryy ', source: ' jquery-x ', target: ' ' },
{ name: ' jqueryy ', source: ' jquery-x ', target: ' * ' },
{ name: ' backbone ', source: ' backbone-amd ', target: ' ~1.0.0 ' },
{ name: ' backbone ', source: ' backbone=backbone-amd ', target: ' ~1.0.0 ' },
{ name: ' bootstrap ', source: ' http://twitter.github.io/bootstrap/assets/bootstrap ', target: ' ' },
{ name: ' bootstrap ', source: ' http://twitter.github.io/bootstrap/assets/bootstrap ', target: ' * ' },
{ name: ' ssh ', source: ' git@example.com ', target: ' * ' },
{ name: ' git ', source: ' git://example.com ', target: ' * ' }
];
var x = 0;
decEndpoints.forEach(function (decEndpoint) {
expect(endpointParser.decomposed2json(decEndpoint)).to.eql(expected[x]);
x += 1;
});
});
it('should throw an error if name is empty', function () {
try {
endpointParser.decomposed2json({ name: '', source: 'jquery', target: '*' });
throw new Error('Should have failed');
} catch (e) {
expect(e.code).to.equal('EINVEND');
expect(e.message).to.contain('must have a name');
}
try {
endpointParser.decomposed2json({ name: ' ', source: 'jquery', target: '*' });
throw new Error('Should have failed');
} catch (e) {
expect(e.code).to.equal('EINVEND');
expect(e.message).to.contain('must have a name');
}
});
});
});