From 7b5752522f225aae7b92442bb60ec3d000a4f9d8 Mon Sep 17 00:00:00 2001 From: Vitaly Tomilov Date: Sat, 15 Jul 2017 03:27:18 +0100 Subject: [PATCH] major rewrite. --- README.md | 27 ++++++++++++++++--- package.json | 2 +- src/index.d.ts | 27 +++++++++---------- src/index.js | 69 +++++++++++++++++++++++++++++++++++++----------- test/main.ts | 7 ++--- test/mainSpec.js | 50 +++++++++++++++++++++++++++++++++-- 6 files changed, 142 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index fcf8c05..41a2b2a 100644 --- a/README.md +++ b/README.md @@ -60,26 +60,45 @@ var parse = require('connection-string'); var obj = parse('my-server:12345'); ``` +or as a class: + +```js +var ConnectionString = require('connection-string'); +var obj = new ConnectionString('my-server:12345'); +``` + * **Browsers** ```html ``` * **TypeScript** ```ts -import * as parse from 'connection-string' -import {ConnectionOptions} from 'connection-string' +import {ConnectionString} from 'connection-string' -var a: ConnectionOptions = parse('my-server:12345'); +var a = new ConnectionString('my-server:12345'); ``` For details and examples see the [WiKi Pages]. +## API + +After parsing you always get a class instance with all the properties, plus method `build`, +which can construct a new the connection string from the current properties. + +Example: + +```js +var a = new ConnectionString('abc://'); +a.host = a.host || 'localhost'; // set a default host +a.build(); //=> 'abc://localhost' +``` + [WiKi Pages]:https://github.com/vitaly-t/connection-string/wiki [Optional Format]:https://github.com/vitaly-t/connection-string/wiki#optional-format diff --git a/package.json b/package.json index a8d2a20..9031491 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "connection-string", - "version": "0.1.6", + "version": "0.2.0", "description": "URL Connection String Parser.", "main": "src/index.js", "typings": "src/index.d.ts", diff --git a/src/index.d.ts b/src/index.d.ts index 0b5277e..f3f86c0 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -1,17 +1,14 @@ -declare namespace ConnectionString { +export class ConnectionString { + constructor(cd: string) - interface ConnectionOptions { - protocol: string - user: string - password: string - host: string - hostname: string - port: number - segments: string[] - params: { [name: string]: string } - } -} - -declare function ConnectionString(cs: string): ConnectionString.ConnectionOptions; + protocol: string; + user: string; + password: string; + host: string; + hostname: string; + port: number; + segments: string[]; + params: { [name: string]: string }; -export = ConnectionString; + build(): string; +} diff --git a/src/index.js b/src/index.js index 261f234..5fc4486 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,11 @@ (function (window) { 'use strict'; - function parseConnectionString(cs) { + function ConnectionString(cs) { + + if (!(this instanceof ConnectionString)) { + return new ConnectionString(cs); + } if (typeof cs !== 'string') { throw new TypeError('Invalid connection string!'); @@ -16,14 +20,12 @@ throw new Error('Invalid URL character at position ' + idx); } - var result = {}; - // extract the protocol: var m = cs.match(/^[\w-_.+!*'()$%]*:\/\//); if (m) { var protocol = decodeURI(m[0].replace(/:\/\//, '')); if (protocol) { - result.protocol = protocol; + this.protocol = protocol; } cs = cs.substr(m[0].length); } @@ -32,10 +34,10 @@ m = cs.match(/^([\w-_.+!*'()$%]*):?([\w-_.+!*'()$%]*)@/); if (m) { if (m[1]) { - result.user = decodeURI(m[1]); + this.user = decodeURI(m[1]); } if (m[2]) { - result.password = decodeURI(m[2]); + this.password = decodeURI(m[2]); } cs = cs.substr(m[0].length); } @@ -52,13 +54,13 @@ } if (m) { if (m[1]) { - result.host = m[1]; + this.host = m[1]; } if (m[2]) { - result.hostname = m[2]; + this.hostname = m[2]; } if (m[3]) { - result.port = parseInt(m[3]); + this.port = parseInt(m[3]); } cs = cs.substr(m[0].length); } @@ -67,7 +69,7 @@ // extract segments: m = cs.match(/\/([\w-_.+!*'()$%]+)/g); if (m) { - result.segments = m.map(function (s) { + this.segments = m.map(function (s) { return decodeURI(s.substr(1)); }); } @@ -78,22 +80,59 @@ cs = cs.substr(idx + 1); m = cs.match(/([\w-_.+!*'()$%]+)=([\w-_.+!*'()$%]+)/g); if (m) { - result.params = {}; + var params = {}; m.forEach(function (s) { var a = s.split('='); - result.params[decodeURI(a[0])] = decodeURI(a[1]); + params[decodeURI(a[0])] = decodeURI(a[1]); }); + this.params = params; } } + } - return result; + function build() { + var s = ''; + if (this.protocol) { + s += encodeURI(this.protocol) + '://'; + } + if (this.user) { + s += encodeURI(this.user); + if (this.password) { + s += ':' + encodeURI(this.password); + } + s += '@'; + } else { + if (this.password) { + s += ':' + encodeURI(this.password) + '@'; + } + } + if (this.host) { + s += this.host; + } + if (Array.isArray(this.segments) && this.segments.length) { + this.segments.forEach(function (seg) { + s += '/' + encodeURI(seg); + }); + } + if (this.params) { + var params = []; + for (var a in this.params) { + params.push(encodeURI(a) + '=' + encodeURI(this.params[a])); + } + if (params.length) { + s += '?' + params.join('&'); + } + } + return s; } + Object.defineProperty(ConnectionString.prototype, 'build', {value: build}); + /* istanbul ignore else */ if (typeof module === 'object' && module && typeof module.exports === 'object') { - module.exports = parseConnectionString; + module.exports = ConnectionString; } else { - window.parseConnectionString = parseConnectionString; + window.ConnectionString = ConnectionString; } })(this); diff --git a/test/main.ts b/test/main.ts index 8776fcc..e76bcda 100644 --- a/test/main.ts +++ b/test/main.ts @@ -1,7 +1,6 @@ -import * as parse from '../src' -import {ConnectionOptions} from '../src' +import {ConnectionString} from '../src' -var a: ConnectionOptions = parse('protocol://'); +var a = new ConnectionString('protocol://'); if ('protocol' in a) { var protocol = a.protocol; @@ -9,3 +8,5 @@ if ('protocol' in a) { var segment1: string = a.segments[0]; var param1: string = a.params['first']; + +var cs = a.build(); diff --git a/test/mainSpec.js b/test/mainSpec.js index 603f028..684fcda 100644 --- a/test/mainSpec.js +++ b/test/mainSpec.js @@ -1,6 +1,10 @@ 'use strict'; -var parse = require('../src'); +var ConnectionString = require('../src'); + +function parse(cs) { + return new ConnectionString(cs); +} describe('init', function () { it('must throw on a non-string', function () { @@ -20,10 +24,13 @@ describe('init', function () { parse('ab\tc'); }).toThrow(new Error('Invalid URL character at position 2')); }); - it('must allow an empty string', function () { expect(parse('')).toEqual({}); }); + it('must support function-style calls', function () { + // eslint-disable-next-line + expect(ConnectionString('abc://')).toEqual({protocol: 'abc'}); + }); }); describe('protocol', function () { @@ -252,3 +259,42 @@ describe('complex', function () { expect(parse(':123/one')).toEqual({host: ':123', port: 123, segments: ['one']}); }); }); + +describe('build', function () { + it('must encode protocol', function () { + expect(parse('abc%20123://').build()).toBe('abc%20123://'); + }); + it('must encode user', function () { + expect(parse('user%20123@').build()).toBe('user%20123@'); + }); + it('must encode password', function () { + expect(parse(':pass%20123@').build()).toBe(':pass%20123@'); + }); + it('must support user + password', function () { + expect(parse('user:pass@').build()).toBe('user:pass@'); + }); + it('must support solo hostname', function () { + expect(parse('server').build()).toBe('server'); + }); + it('must support hostname + port', function () { + expect(parse('server:123').build()).toBe('server:123'); + }); + it('must encode segments', function () { + expect(parse('/a%20b').build()).toBe('/a%20b'); + expect(parse('/a/b%20/c').build()).toBe('/a/b%20/c'); + }); + it('must ignore empty segment list', function () { + var a = parse(''); + a.segments = []; + expect(a.build()).toBe(''); + }); + it('must encode params', function () { + expect(parse('?a%20b=1').build()).toBe('?a%20b=1'); + expect(parse('?a=1&b%20=2').build()).toBe('?a=1&b%20=2'); + }); + it('must ignore empty parameter list', function () { + var a = parse(''); + a.params = {}; + expect(a.build()).toBe(''); + }); +});