mirror of
https://github.com/dzeiocom/libs.git
synced 2025-06-06 08:19:53 +00:00
Added URLManager
Signed-off-by: Florian Bouillon <florian.bouillon@delta-wings.net>
This commit is contained in:
parent
066908cb4b
commit
f612d78545
@ -16,6 +16,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"prepublishOnly": "yarn build",
|
||||
"build": "tsc"
|
||||
"build": "tsc",
|
||||
"test": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
|
10
packages/url-manager/.babelrc
Normal file
10
packages/url-manager/.babelrc
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"presets": [
|
||||
["@babel/preset-env", {
|
||||
"targets": {
|
||||
"node": "current"
|
||||
}
|
||||
}],
|
||||
"@babel/preset-typescript"
|
||||
]
|
||||
}
|
2
packages/url-manager/.gitignore
vendored
Normal file
2
packages/url-manager/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/dist/
|
||||
coverage
|
13
packages/url-manager/.npmignore
Normal file
13
packages/url-manager/.npmignore
Normal file
@ -0,0 +1,13 @@
|
||||
node_modules
|
||||
src
|
||||
test
|
||||
.babelrc
|
||||
.eslintrc.js
|
||||
.gitignore
|
||||
.npmignore
|
||||
tsconfig.*
|
||||
webpack.config.js
|
||||
yarn-error.log
|
||||
coverage
|
||||
__tests__
|
||||
jest.config.js
|
102
packages/url-manager/__tests__/index.test.ts
Normal file
102
packages/url-manager/__tests__/index.test.ts
Normal file
@ -0,0 +1,102 @@
|
||||
/// <reference types="jest" />
|
||||
|
||||
import URLManager from '../src/URLManager'
|
||||
|
||||
|
||||
describe('Basic tests', () => {
|
||||
it('should be able to create some basics urls', () => {
|
||||
expect(new URLManager().domain('domain.com').toString()).toBe('domain.com');
|
||||
expect(new URLManager().path('path').toString()).toBe('/path');
|
||||
});
|
||||
it('should compile a full url', () => {
|
||||
const url = new URLManager()
|
||||
.protocols(['git', 'ssh'])
|
||||
.username('username')
|
||||
.password('password')
|
||||
.domain('domain.com')
|
||||
.port(65565)
|
||||
.path('/path')
|
||||
.query('test', 'true')
|
||||
.hash('hash')
|
||||
expect(url.toString())
|
||||
.toBe('git+ssh://username:password@domain.com:65565/path?test=true#hash')
|
||||
})
|
||||
it('should parse and give back the same url', () => {
|
||||
const url = 'git+ssh://username:password@domain.com:65565/path?test=true&test=false#hash'
|
||||
expect(new URLManager(url).toString()).toBe(url)
|
||||
})
|
||||
|
||||
it('should be able to add and delete query', () => {
|
||||
const url = new URLManager()
|
||||
// Test base url
|
||||
expect(url.toString()).toBe('')
|
||||
url.query('test', 'true')
|
||||
|
||||
// Test basic Query add
|
||||
expect(url.toString()).toBe('?test=true')
|
||||
url.query('test', ['a', 'b'])
|
||||
|
||||
// Test Query Array
|
||||
expect(url.toString()).toBe('?test=a&test=b')
|
||||
|
||||
// Test Query Array with Array Join
|
||||
expect(url.toString(undefined, {queryArrayJoin: ','})).toBe('?test=a,b')
|
||||
|
||||
url.query('test', null)
|
||||
// Test Query Deletion
|
||||
expect(url.toString()).toBe('')
|
||||
})
|
||||
});
|
||||
|
||||
describe('Protocol Tests', () => {
|
||||
it('should set the protocol', () => {
|
||||
const protocol = 'https'
|
||||
|
||||
const url = new URLManager('domain.com').protocol(protocol)
|
||||
expect(url.toString()).toBe('https://domain.com')
|
||||
})
|
||||
|
||||
it('should return the url protocol', () => {
|
||||
|
||||
const url = new URLManager('https://domain.com')
|
||||
expect(url.protocol()).toBe('https')
|
||||
})
|
||||
|
||||
it('should override the current protocol', () => {
|
||||
const url = new URLManager('ssh://domain.com').protocol('https')
|
||||
expect(url.toString()).toBe('https://domain.com')
|
||||
})
|
||||
it('should set multiple protocols', () => {
|
||||
const url = new URLManager('domain.com').protocols(['git', 'ssh'])
|
||||
expect(url.toString()).toBe('git+ssh://domain.com')
|
||||
})
|
||||
it('should override every protocols', () => {
|
||||
const url = new URLManager('https+sftp://domain.com').protocols(['git', 'ssh'])
|
||||
expect(url.toString()).toBe('git+ssh://domain.com')
|
||||
})
|
||||
it('should replace every protocols with only one', () => {
|
||||
const url = new URLManager('git+ssh://domain.com').protocol('https')
|
||||
expect(url.toString()).toBe('https://domain.com')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Special cases', () => {
|
||||
it('should generate a new url from URLSearchParams', () => {
|
||||
const search = new URLSearchParams('?test=true')
|
||||
expect(new URLManager(search).toString()).toBe('?test=true')
|
||||
})
|
||||
|
||||
it('should generate a new url from URL', () => {
|
||||
const url = new URL('https://domain.com/test')
|
||||
expect(new URLManager(url).toString()).toBe('https://domain.com/test')
|
||||
})
|
||||
|
||||
it('should format the template url', () => {
|
||||
const tmpl = '/test/[pouet]/home'
|
||||
expect(new URLManager(tmpl).toString({ pouet: 'true' })).toBe('/test/true/home')
|
||||
})
|
||||
it('should not format the template url if not in params', () => {
|
||||
const tmpl = '/test/[url-manager]/home'
|
||||
expect(new URLManager(tmpl).toString({ pouet: 'true' })).toBe('/test/[url-manager]/home')
|
||||
})
|
||||
})
|
18
packages/url-manager/jest.config.js
Normal file
18
packages/url-manager/jest.config.js
Normal file
@ -0,0 +1,18 @@
|
||||
module.exports = {
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsConfig: 'tsconfig.test.json'
|
||||
},
|
||||
"transform": {
|
||||
".(ts|tsx)": " ../../node_modules/ts-jest/preprocessor.js"
|
||||
},
|
||||
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
|
||||
"testResultsProcessor": "../../node_modules/ts-jest/coverageprocessor.js",
|
||||
"collectCoverageFrom": ["src/URLManager.ts"],
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js"
|
||||
],
|
||||
},
|
||||
}
|
31
packages/url-manager/package.json
Normal file
31
packages/url-manager/package.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "@dzeio/url-manager",
|
||||
"version": "0.0.1",
|
||||
"description": "Manage URL",
|
||||
"repository": "https://github.com/dzeiocom/libs.git",
|
||||
"author": "Aviortheking",
|
||||
"license": "MIT",
|
||||
"main": "./dist/URLManager.js",
|
||||
"browser": "./dist/browser.js",
|
||||
"types": "./dist/URLManager.d.ts",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.11.4",
|
||||
"@babel/preset-env": "^7.11.0",
|
||||
"@babel/preset-typescript": "^7.10.4",
|
||||
"@types/chai": "^4.2.12",
|
||||
"@types/jest": "^26.0.10",
|
||||
"babel-loader": "^8.1.0",
|
||||
"codecov": "^3.7.2",
|
||||
"jest": "^26.4.2",
|
||||
"ts-loader": "^8.0.3",
|
||||
"ts-node": "^9.0.0",
|
||||
"typescript": "^4.0.2",
|
||||
"webpack": "^4.44.1",
|
||||
"webpack-cli": "^3.3.12"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublishOnly": "yarn build",
|
||||
"build": "webpack --mode=\"production\" && tsc",
|
||||
"test": "jest --coverage && codecov -f coverage/coverage-final.json"
|
||||
}
|
||||
}
|
443
packages/url-manager/src/URLManager.ts
Normal file
443
packages/url-manager/src/URLManager.ts
Normal file
@ -0,0 +1,443 @@
|
||||
/**
|
||||
* Easy URLs manager
|
||||
*/
|
||||
export default class URLManager {
|
||||
|
||||
private _protocols: Array<string> = []
|
||||
private _username: string | undefined
|
||||
private _password: string | undefined
|
||||
private _domain = ''
|
||||
private _port: number | undefined
|
||||
private _path: string | undefined
|
||||
private _query: Record<string, string | Array<string>> = {}
|
||||
private _hash = ''
|
||||
|
||||
/**
|
||||
* Initialize the Manager
|
||||
*
|
||||
* @param { string | URLSearchParams | URL } url the url to start from
|
||||
*/
|
||||
public constructor(url?: string | URLSearchParams | URL | Location) {
|
||||
if (!url) {
|
||||
return
|
||||
}
|
||||
if (url instanceof URLSearchParams) {
|
||||
url.forEach((value, key) => {
|
||||
this.query(key, value)
|
||||
})
|
||||
return
|
||||
}
|
||||
this.fromURL(`${url}`)
|
||||
}
|
||||
|
||||
private fromURL(url: string) {
|
||||
const protocolIndex = url.indexOf('://')
|
||||
const indexOfPath = url.indexOf('/', protocolIndex !== -1 ? protocolIndex + 3 : undefined)
|
||||
const firstPart = url.substr(0, indexOfPath !== -1 ? indexOfPath : undefined)
|
||||
const path = url.substr(firstPart.length)
|
||||
|
||||
// PROTOCOL
|
||||
const procotolSplit = firstPart.split('://')
|
||||
if (procotolSplit.length === 2) {
|
||||
this.protocols(procotolSplit[0].split('+'))
|
||||
}
|
||||
|
||||
// USERNAME and PASSWORD
|
||||
const usrSplit = url.split('@')
|
||||
if (usrSplit.length === 2) {
|
||||
const usrPass = usrSplit[0].substr(protocolIndex !== -1 ? protocolIndex + 3 : 0)
|
||||
const data = usrPass.split(':')
|
||||
this.username(data.shift() as string)
|
||||
if (data.length >= 1) {
|
||||
this.password(data.join(':'))
|
||||
}
|
||||
}
|
||||
|
||||
// DOMAIN & PORT
|
||||
let splitted = firstPart.split('@')
|
||||
if (splitted.length === 1) {
|
||||
splitted = firstPart.split('://')
|
||||
}
|
||||
const post = splitted.length > 1 ? splitted[1] : splitted[0]
|
||||
const data = post.split(':')
|
||||
this.domain(data[0])
|
||||
if (data.length === 2) {
|
||||
this.port(parseInt(data[1]))
|
||||
}
|
||||
|
||||
const hashPos = path.indexOf('#')
|
||||
const queryStart = path.indexOf('?')
|
||||
|
||||
// PATH
|
||||
const pathEnd = queryStart !== -1 ? queryStart : hashPos
|
||||
this.path(path.substr(0, pathEnd !== -1 ? pathEnd : undefined))
|
||||
|
||||
// QUERY
|
||||
if (queryStart !== -1) {
|
||||
const queryString = path.substring(queryStart + 1, hashPos !== -1 ? hashPos : undefined)
|
||||
const queryArray = queryString.split('&')
|
||||
for (const queryItem of queryArray) {
|
||||
const item = queryItem.split('=')
|
||||
const key = item[0]
|
||||
const val = item.length === 2 ? item[1] : ''
|
||||
|
||||
let query = this.query(key)
|
||||
if (query) {
|
||||
if (typeof query === 'string') {
|
||||
query = [query, val]
|
||||
} else {
|
||||
query.push(val)
|
||||
}
|
||||
this.query(key, query)
|
||||
} else {
|
||||
this.query(key, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HASH
|
||||
if (hashPos !== -1) {
|
||||
this.hash(path.substr(hashPos + 1))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a new URLManager from the current location
|
||||
* @return { this }
|
||||
*/
|
||||
public static fromLocation() {
|
||||
return new URLManager(window.location)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the window
|
||||
*/
|
||||
public static reload() {
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
/**
|
||||
* return a `key: value` object of the query string
|
||||
*/
|
||||
public query(): Record<string, string | Array<string>>
|
||||
|
||||
/**
|
||||
* get a value from the query string
|
||||
* @param key the key to get the value from
|
||||
*/
|
||||
public query(key: string): string | Array<string>
|
||||
|
||||
/**
|
||||
* set a key to a value in the query string
|
||||
* @param key the key to set
|
||||
* @param value the value to set
|
||||
*/
|
||||
public query(key: string, value: string | Array<string>): this
|
||||
|
||||
/**
|
||||
* delete key from the query string
|
||||
* @param key the key to delete
|
||||
* @param value the `null` keyword
|
||||
*/
|
||||
public query(key: string, value: null): this
|
||||
|
||||
/**
|
||||
* Manipulate the query string
|
||||
* @param { string | undefined } key the key to manipulate (is not set return a list of key-value pair)
|
||||
* @param { string | Array<string> | null | undefined } value the value to set or action to run (if not set it returns the value)
|
||||
* @return { this | string | Array<string> }
|
||||
*/
|
||||
public query(key?: string, value?: string | Array<string> | null) {
|
||||
if (!key) {
|
||||
return this._query
|
||||
}
|
||||
if (typeof value === 'undefined') {
|
||||
return this._query[key]
|
||||
}
|
||||
if (value === null) {
|
||||
delete this._query[key]
|
||||
} else {
|
||||
this._query[key] = value
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url path
|
||||
*/
|
||||
public path(): string | undefined
|
||||
|
||||
/**
|
||||
* Set the url path
|
||||
* @param val the path to set
|
||||
*/
|
||||
public path(val: string): this
|
||||
|
||||
/**
|
||||
* Manipulate the url path
|
||||
* @param { string | undefined } val the path to set
|
||||
* @return { URLManager | string } erer
|
||||
*/
|
||||
public path(val?: string) {
|
||||
if (!val) {
|
||||
return this._path
|
||||
}
|
||||
this._path = val
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of protocols
|
||||
*/
|
||||
public protocols(): Array<string>
|
||||
|
||||
/**
|
||||
* set the list of protocols
|
||||
* @param val the list
|
||||
*/
|
||||
public protocols(val: Array<string>): this
|
||||
|
||||
/**
|
||||
* Manipulate the list of protocols
|
||||
* @param { Array<string> | undefined } val the list of protocols to set
|
||||
* @return { Array<string> }
|
||||
*/
|
||||
public protocols(val?: Array<string>) {
|
||||
if (!val) {
|
||||
return this._protocols
|
||||
}
|
||||
this._protocols = val
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url protocol
|
||||
*/
|
||||
public protocol(): string
|
||||
|
||||
/**
|
||||
* Set the url protocol
|
||||
* @param val the protocol to set
|
||||
*/
|
||||
public protocol(val: string): this
|
||||
|
||||
/**
|
||||
* Manipulate the url protocol
|
||||
* @param { string | undefined } val the protocol to set (Optionnal)
|
||||
* @return { string }
|
||||
*/
|
||||
public protocol(val?: string) {
|
||||
if (!val) {
|
||||
return this._protocols.length > 0 ? this._protocols[0] : undefined
|
||||
}
|
||||
this._protocols = [val]
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url Domain
|
||||
*/
|
||||
public domain(): string
|
||||
|
||||
/**
|
||||
* set the url domain name
|
||||
* @param val the domain name
|
||||
*/
|
||||
public domain(val: string): this
|
||||
|
||||
/**
|
||||
* Manipulate the url domain
|
||||
* @param { string | undefined } val the url domain (Optionnal)
|
||||
* @return { string | this }
|
||||
*/
|
||||
public domain(val?: string) {
|
||||
if (!val) {
|
||||
return this._domain
|
||||
}
|
||||
this._domain = val
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url username
|
||||
*/
|
||||
public username(): string | undefined
|
||||
|
||||
/**
|
||||
* Set the url username
|
||||
* @param val the url username
|
||||
*/
|
||||
public username(val: string): this
|
||||
|
||||
/**
|
||||
* Manipulate the url username
|
||||
* @param {string | undefined } val the username to set (Optionnal)
|
||||
* @return { string | undefined }
|
||||
*/
|
||||
public username(val?: string) {
|
||||
if (!val) {
|
||||
return this._username
|
||||
}
|
||||
this._username = val
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url password
|
||||
*/
|
||||
public password(): string | undefined
|
||||
|
||||
/**
|
||||
* Set the url password
|
||||
* @param val the password
|
||||
*/
|
||||
public password(val: string): this
|
||||
|
||||
/**
|
||||
* Manipulate the url password
|
||||
* @param { string | undefinel } val the password (Optionnal)
|
||||
* @return { string | this }
|
||||
*/
|
||||
public password(val?: string) {
|
||||
if (!val) {
|
||||
return this._password
|
||||
}
|
||||
this._password = val
|
||||
return this
|
||||
}
|
||||
|
||||
public port(): number | undefined
|
||||
public port(val: number): this
|
||||
public port(val?: number | undefined) {
|
||||
if (!val) {
|
||||
return this._port
|
||||
}
|
||||
this._port = val
|
||||
return this
|
||||
}
|
||||
|
||||
public hash(): string
|
||||
public hash(val: string): this
|
||||
public hash(val?: string) {
|
||||
if (!val) {
|
||||
return this._hash
|
||||
}
|
||||
this._hash = val
|
||||
return this
|
||||
}
|
||||
|
||||
private formatPath(format?: Record<string, string>) {
|
||||
let path = this.path()
|
||||
if (!path) {
|
||||
return undefined
|
||||
}
|
||||
if (format) {
|
||||
for (const key in format) {
|
||||
if (!(key in format)) {
|
||||
continue
|
||||
}
|
||||
const replacing = format[key]
|
||||
path = path.replace(`[${key}]`, replacing)
|
||||
}
|
||||
}
|
||||
return `${(path.startsWith('/') ? '' : '/')}${path}`
|
||||
}
|
||||
|
||||
private formatQuery(options?: { queryArrayJoin?: string }) {
|
||||
let result = ''
|
||||
const queryTmp = this.query()
|
||||
|
||||
for (const key in queryTmp) {
|
||||
if (!Object.prototype.hasOwnProperty.call(queryTmp, key)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const element = queryTmp[key]
|
||||
|
||||
result += result.length === 0 ? '?' : '&'
|
||||
|
||||
if (typeof element !== 'object') {
|
||||
result += `${key}=${element}`
|
||||
continue
|
||||
}
|
||||
|
||||
if (options?.queryArrayJoin) {
|
||||
result += `${key}=${element.join(options.queryArrayJoin)}`
|
||||
continue
|
||||
}
|
||||
|
||||
for (let i = 0; i < element.length; i++) {
|
||||
const val = element[i]
|
||||
if (i !== 0) {
|
||||
result += '&'
|
||||
}
|
||||
result += `${key}=${val}`
|
||||
}
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build the string back
|
||||
* @param { Record<string, string> | undefined } format Formatting options ex: if path contains `[test]` and format is `{test: 'working'}` `[test]` will be replaced by the value
|
||||
* @param { Record<string, string> | undefined } options options for formatting
|
||||
* @param { string | undefined } options.queryArrayJoin Query formatting
|
||||
* @return { string } return the builded string
|
||||
*/
|
||||
public toString(format?: Record<string, string>, options?: {queryArrayJoin?: string}): string {
|
||||
|
||||
let result = ''
|
||||
|
||||
const protocols = this.protocols()
|
||||
if (protocols.length > 0) {
|
||||
result += `${protocols.join('+')}://`
|
||||
}
|
||||
|
||||
const user = this.username()
|
||||
const pass = this.password()
|
||||
if (user) {
|
||||
result += user
|
||||
if (pass) {
|
||||
result += `:${pass}`
|
||||
}
|
||||
result += '@'
|
||||
}
|
||||
|
||||
result += this.domain()
|
||||
|
||||
const port = this.port()
|
||||
if (port) {
|
||||
result += `:${port}`
|
||||
}
|
||||
|
||||
result += this.formatPath(format) || ''
|
||||
|
||||
result += this.formatQuery(options) || ''
|
||||
|
||||
const hash = this.hash()
|
||||
if (hash) {
|
||||
result += `#${hash}`
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the page built
|
||||
* @param {boolean} reload is normal push or history only push
|
||||
*/
|
||||
public go(reload = true) {
|
||||
if (reload) {
|
||||
window.location.href = this.toString()
|
||||
return
|
||||
}
|
||||
window.history.pushState(undefined, document.head.title, this.toString())
|
||||
}
|
||||
|
||||
}
|
4
packages/url-manager/src/index.ts
Normal file
4
packages/url-manager/src/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import URLManager from './URLManager'
|
||||
|
||||
// @ts-expect-error
|
||||
window.URLManager = URLManager
|
9
packages/url-manager/tsconfig.json
Normal file
9
packages/url-manager/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist"
|
||||
},
|
||||
"files": [
|
||||
"src/URLManager.ts"
|
||||
]
|
||||
}
|
6
packages/url-manager/tsconfig.test.json
Normal file
6
packages/url-manager/tsconfig.test.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
17
packages/url-manager/webpack.config.js
Normal file
17
packages/url-manager/webpack.config.js
Normal file
@ -0,0 +1,17 @@
|
||||
module.exports = {
|
||||
entry: './src/index',
|
||||
output: {
|
||||
path: __dirname,
|
||||
filename: './dist/browser.js',
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.ts'],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/, use: ['babel-loader', 'ts-loader'], exclude: /node_modules/
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user