Skip to content

Commit

Permalink
feat: support query params for trailingSlash utils (#24)
Browse files Browse the repository at this point in the history
Co-authored-by: Divine <48183131+divine@users.noreply.github.com>
  • Loading branch information
pi0 and divine authored Jun 30, 2021
1 parent 005bbf6 commit aa31481
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 10 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ Ensures url ends with a trailing slash
withTrailingSlash('/foo')
```

```ts
// Result: /path/?query=true
withTrailingSlash('/path?query=true', true)
```

### `withoutTrailingSlash`

Ensures url does not ends with a trailing slash
Expand All @@ -119,6 +124,11 @@ Ensures url does not ends with a trailing slash
withoutTrailingSlash('/foo/')
```

```ts
// Result: /path?query=true
withoutTrailingSlash('/path/?query=true', true)
```

### `cleanDoubleSlashes`

Ensures url does not have double slash (except for protocol)
Expand Down
31 changes: 25 additions & 6 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,35 @@ export function hasProtocol (inputStr: string, acceptProtocolRelative = false):
return /^\w+:\/\/.+/.test(inputStr) || (acceptProtocolRelative && /^\/\/[^/]+/.test(inputStr))
}

export function hasTrailingSlash (input: string = ''): boolean {
return input.endsWith('/')
const TRAILING_SLASH_RE = /\/$|\/\?/

export function hasTrailingSlash (input: string = '', queryParams: boolean = false): boolean {
if (!queryParams) {
return input.endsWith('/')
}
return TRAILING_SLASH_RE.test(input)
}

export function withoutTrailingSlash (input: string = ''): string {
return (hasTrailingSlash(input) ? input.slice(0, -1) : input) || '/'
export function withoutTrailingSlash (input: string = '', queryParams: boolean = false): string {
if (!queryParams) {
return (hasTrailingSlash(input) ? input.slice(0, -1) : input) || '/'
}
if (!hasTrailingSlash(input, true)) {
return input || '/'
}
const [s0, ...s] = input.split('?')
return (s0.slice(0, -1) || '/') + (s.length ? `?${s.join('?')}` : '')
}

export function withTrailingSlash (input: string = ''): string {
return input.endsWith('/') ? input : (input + '/')
export function withTrailingSlash (input: string = '', queryParams: boolean = false): string {
if (!queryParams) {
return input.endsWith('/') ? input : (input + '/')
}
if (hasTrailingSlash(input, true)) {
return input || '/'
}
const [s0, ...s] = input.split('?')
return s0 + '/' + (s.length ? `?${s.join('?')}` : '')
}

export function hasLeadingSlash (input: string = ''): boolean {
Expand Down
53 changes: 49 additions & 4 deletions test/trailing-slash.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// @ts-nocheck
import { withTrailingSlash, withoutTrailingSlash } from '../src'

describe('withTrailingSlash', () => {
describe('withTrailingSlash, queryParams: false', () => {
const tests = {
'': '/',
bar: 'bar/',
'bar/': 'bar/'
'bar/': 'bar/',
'foo?123': 'foo?123/',
'foo/?123': 'foo/?123/'
}

for (const input in tests) {
Expand All @@ -19,12 +21,34 @@ describe('withTrailingSlash', () => {
})
})

describe('withoutTrailingSlash', () => {
describe('withTrailingSlash, queryParams: true', () => {
const tests = {
'': '/',
bar: 'bar/',
'bar/': 'bar/',
'foo?123': 'foo/?123',
'foo/?123': 'foo/?123'
}

for (const input in tests) {
test(input, () => {
expect(withTrailingSlash(input, true)).toBe(tests[input])
})
}

test('falsy value', () => {
expect(withTrailingSlash()).toBe('/')
})
})

describe('withoutTrailingSlash, queryParams: false', () => {
const tests = {
'': '/',
'/': '/',
bar: 'bar',
'bar/': 'bar'
'bar/': 'bar',
'foo?123': 'foo?123',
'foo/?123': 'foo/?123'
}

for (const input in tests) {
Expand All @@ -37,3 +61,24 @@ describe('withoutTrailingSlash', () => {
expect(withoutTrailingSlash()).toBe('/')
})
})

describe('withoutTrailingSlash, queryParams: true', () => {
const tests = {
'': '/',
'/': '/',
bar: 'bar',
'bar/': 'bar',
'foo?123': 'foo?123',
'foo/?123': 'foo?123'
}

for (const input in tests) {
test(input, () => {
expect(withoutTrailingSlash(input, true)).toBe(tests[input])
})
}

test('falsy value', () => {
expect(withoutTrailingSlash()).toBe('/')
})
})

0 comments on commit aa31481

Please sign in to comment.