From 74d36cbafbff4e5b177512c3acc2061b1f0e0410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9renger?= Date: Sat, 4 Jan 2025 21:41:24 +0100 Subject: [PATCH 1/6] feat: add url/withLeadingSlash --- benchmarks/url/withLeadingSlash.bench.ts | 16 ++++ docs/url/withLeadingSlash.mdx | 22 ++++++ src/mod.ts | 2 + src/url/withLeadingSlash.ts | 98 ++++++++++++++++++++++++ tests/url/withLeadingSlash.test.ts | 25 ++++++ 5 files changed, 163 insertions(+) create mode 100644 benchmarks/url/withLeadingSlash.bench.ts create mode 100644 docs/url/withLeadingSlash.mdx create mode 100644 src/url/withLeadingSlash.ts create mode 100644 tests/url/withLeadingSlash.test.ts diff --git a/benchmarks/url/withLeadingSlash.bench.ts b/benchmarks/url/withLeadingSlash.bench.ts new file mode 100644 index 00000000..165535ff --- /dev/null +++ b/benchmarks/url/withLeadingSlash.bench.ts @@ -0,0 +1,16 @@ +import { withLeadingSlash } from 'radashi' + +describe('withLeadingSlash', () => { + bench('with no input', () => { + withLeadingSlash(undefined) + }) + bench('with empty string', () => { + withLeadingSlash('') + }) + bench('with missing leading slash', () => { + withLeadingSlash('some/path') + }) + bench('with leading slash', () => { + withLeadingSlash('/some/path') + }) +}) diff --git a/docs/url/withLeadingSlash.mdx b/docs/url/withLeadingSlash.mdx new file mode 100644 index 00000000..d83e37fb --- /dev/null +++ b/docs/url/withLeadingSlash.mdx @@ -0,0 +1,22 @@ +--- +title: withLeadingSlash +description: Adds a leading slash +--- + +### Usage + +Adds a leading slash `/` to the given URL if it is not already present. + +This function is useful for ensuring that URLs are properly formatted +with a leading slash, which is often required in web development for +consistency and to avoid issues with relative paths. + +```ts +import { withLeadingSlash } from 'radashi' + +withLeadingSlash('') // => '/' +withLeadingSlash('no/slash') // => '/no/slash' +withLeadingSlash('/already/has/slash') // => '/already/has/slash' +withLeadingSlash(undefined) // => undefined +withLeadingSlash(null) // => null +``` diff --git a/src/mod.ts b/src/mod.ts index 0a91c38d..fc3c05c8 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -142,4 +142,6 @@ export * from './typed/isUndefined.ts' export * from './typed/isWeakMap.ts' export * from './typed/isWeakSet.ts' +export * from './url/withLeadingSlash.ts' + export * from './types.ts' diff --git a/src/url/withLeadingSlash.ts b/src/url/withLeadingSlash.ts new file mode 100644 index 00000000..b7686491 --- /dev/null +++ b/src/url/withLeadingSlash.ts @@ -0,0 +1,98 @@ +/** + * Adds a leading slash `/` to the given URL if it is not already present. + * + * This function is useful for ensuring that URLs are properly formatted + * with a leading slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string with a leading slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withLeadingSlash + * + * @example + * ```ts + * withLeadingSlash('') // => '/' + * withLeadingSlash('no/slash') // => '/no/slash' + * withLeadingSlash('/already/has/slash') // => '/already/has/slash' + * withLeadingSlash(undefined) // => undefined + * withLeadingSlash(null) // => null + * ``` + */ +export function withLeadingSlash(url: string): string + +/** + * Adds a leading slash `/` to the given URL if it is not already present. + * + * This function is useful for ensuring that URLs are properly formatted + * with a leading slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string with a leading slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withLeadingSlash + * + * @example + * ```ts + * withLeadingSlash('') // => '/' + * withLeadingSlash('no/slash') // => '/no/slash' + * withLeadingSlash('/already/has/slash') // => '/already/has/slash' + * withLeadingSlash(undefined) // => undefined + * withLeadingSlash(null) // => null + * ``` + */ +export function withLeadingSlash(url: undefined): undefined + +/** + * Adds a leading slash `/` to the given URL if it is not already present. + * + * This function is useful for ensuring that URLs are properly formatted + * with a leading slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string with a leading slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withLeadingSlash + * + * @example + * ```ts + * withLeadingSlash('') // => '/' + * withLeadingSlash('no/slash') // => '/no/slash' + * withLeadingSlash('/already/has/slash') // => '/already/has/slash' + * withLeadingSlash(undefined) // => undefined + * withLeadingSlash(null) // => null + * ``` + */ +export function withLeadingSlash(url: null): null + +/** + * Adds a leading slash `/` to the given URL if it is not already present. + * + * This function is useful for ensuring that URLs are properly formatted + * with a leading slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string with a leading slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withLeadingSlash + * + * @example + * ```ts + * withLeadingSlash('') // => '/' + * withLeadingSlash('no/slash') // => '/no/slash' + * withLeadingSlash('/already/has/slash') // => '/already/has/slash' + * withLeadingSlash(undefined) // => undefined + * withLeadingSlash(null) // => null + * ``` + */ +export function withLeadingSlash( + url: string | undefined | null, +): string | undefined | null { + if (url === undefined || url === null) { + return url + } + return url[0] === '/' ? url : '/' + url +} diff --git a/tests/url/withLeadingSlash.test.ts b/tests/url/withLeadingSlash.test.ts new file mode 100644 index 00000000..4410c465 --- /dev/null +++ b/tests/url/withLeadingSlash.test.ts @@ -0,0 +1,25 @@ +import { withLeadingSlash } from 'radashi' + +describe('withLeadingSlash', () => { + test('should return undefined if input is undefined', () => { + expect(withLeadingSlash(undefined)).toBe(undefined) + }) + test('should return null if input is null', () => { + expect(withLeadingSlash(null)).toBe(null) + }) + test('should do nothing if leading slash is already present', () => { + expect(withLeadingSlash('/foo')).toBe('/foo') + }) + test('should add leading slash to an empty string', () => { + expect(withLeadingSlash('')).toBe('/') + }) + test('should add leading slash to a string without leading slash', () => { + expect(withLeadingSlash('text-without-slash')).toBe('/text-without-slash') + }) + test('should do nothing if input is a string of slashes', () => { + expect(withLeadingSlash('/////////')).toBe('/////////') + }) + test('should do nothing if input is a single slash', () => { + expect(withLeadingSlash('/')).toBe('/') + }) +}) From 273d278b8e6242e1fc2822ca82b0dd4fb225c4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9renger?= Date: Sat, 4 Jan 2025 21:41:24 +0100 Subject: [PATCH 2/6] feat: add url/withTrailingSlash --- benchmarks/url/withTrailingSlash.bench.ts | 16 ++++ docs/url/withTrailingSlash.mdx | 22 +++++ src/mod.ts | 1 + src/url/withTrailingSlash.ts | 98 +++++++++++++++++++++++ tests/url/withTrailingSlash.test.ts | 25 ++++++ 5 files changed, 162 insertions(+) create mode 100644 benchmarks/url/withTrailingSlash.bench.ts create mode 100644 docs/url/withTrailingSlash.mdx create mode 100644 src/url/withTrailingSlash.ts create mode 100644 tests/url/withTrailingSlash.test.ts diff --git a/benchmarks/url/withTrailingSlash.bench.ts b/benchmarks/url/withTrailingSlash.bench.ts new file mode 100644 index 00000000..256c8b96 --- /dev/null +++ b/benchmarks/url/withTrailingSlash.bench.ts @@ -0,0 +1,16 @@ +import { withTrailingSlash } from 'radashi' + +describe('withTrailingSlash', () => { + bench('with no input', () => { + withTrailingSlash(undefined) + }) + bench('with empty string', () => { + withTrailingSlash('') + }) + bench('with missing trailing slash', () => { + withTrailingSlash('some/path') + }) + bench('with trailing slash', () => { + withTrailingSlash('some/path/') + }) +}) diff --git a/docs/url/withTrailingSlash.mdx b/docs/url/withTrailingSlash.mdx new file mode 100644 index 00000000..0cb658ce --- /dev/null +++ b/docs/url/withTrailingSlash.mdx @@ -0,0 +1,22 @@ +--- +title: withTrailingSlash +description: Adds a trailing slash +--- + +### Usage + +Adds a trailing slash `/` to the given URL if it is not already present. + +This function is useful for ensuring that URLs are properly formatted +with a trailing slash, which is often required in web development for +consistency and to avoid issues with relative paths. + +```ts +import { withTrailingSlash } from 'radashi' + +withTrailingSlash('') // => '/' +withTrailingSlash('no/slash') // => 'no/slash/' +withTrailingSlash('already/has/slash/') // => 'already/has/slash/' +withTrailingSlash(undefined) // => undefined +withTrailingSlash(null) // => null +``` diff --git a/src/mod.ts b/src/mod.ts index fc3c05c8..86dde79c 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -143,5 +143,6 @@ export * from './typed/isWeakMap.ts' export * from './typed/isWeakSet.ts' export * from './url/withLeadingSlash.ts' +export * from './url/withTrailingSlash.ts' export * from './types.ts' diff --git a/src/url/withTrailingSlash.ts b/src/url/withTrailingSlash.ts new file mode 100644 index 00000000..a3952783 --- /dev/null +++ b/src/url/withTrailingSlash.ts @@ -0,0 +1,98 @@ +/** + * Adds a trailing slash `/` to the given URL if it is not already present. + * + * This function is useful for ensuring that URLs are properly formatted + * with a trailing slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string with a trailing slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withTrailingSlash + * + * @example + * ```ts + * withTrailingSlash('') // => '/' + * withTrailingSlash('no/slash') // => 'no/slash/' + * withTrailingSlash('already/has/slash/') // => 'already/has/slash/' + * withTrailingSlash(undefined) // => undefined + * withTrailingSlash(null) // => null + * ``` + */ +export function withTrailingSlash(url: string): string + +/** + * Adds a trailing slash `/` to the given URL if it is not already present. + * + * This function is useful for ensuring that URLs are properly formatted + * with a trailing slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string with a trailing slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withTrailingSlash + * + * @example + * ```ts + * withTrailingSlash('') // => '/' + * withTrailingSlash('no/slash') // => 'no/slash/' + * withTrailingSlash('already/has/slash/') // => 'already/has/slash/' + * withTrailingSlash(undefined) // => undefined + * withTrailingSlash(null) // => null + * ``` + */ +export function withTrailingSlash(url: undefined): undefined + +/** + * Adds a trailing slash `/` to the given URL if it is not already present. + * + * This function is useful for ensuring that URLs are properly formatted + * with a trailing slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string with a trailing slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withTrailingSlash + * + * @example + * ```ts + * withTrailingSlash('') // => '/' + * withTrailingSlash('no/slash') // => 'no/slash/' + * withTrailingSlash('already/has/slash/') // => 'already/has/slash/' + * withTrailingSlash(undefined) // => undefined + * withTrailingSlash(null) // => null + * ``` + */ +export function withTrailingSlash(url: null): null + +/** + * Adds a trailing slash `/` to the given URL if it is not already present. + * + * This function is useful for ensuring that URLs are properly formatted + * with a trailing slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string with a trailing slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withTrailingSlash + * + * @example + * ```ts + * withTrailingSlash('') // => '/' + * withTrailingSlash('no/slash') // => 'no/slash/' + * withTrailingSlash('already/has/slash/') // => 'already/has/slash/' + * withTrailingSlash(undefined) // => undefined + * withTrailingSlash(null) // => null + * ``` + */ +export function withTrailingSlash( + url: string | undefined | null, +): string | undefined | null { + if (url === undefined || url === null) { + return url + } + return url[url.length - 1] === '/' ? url : url + '/' +} diff --git a/tests/url/withTrailingSlash.test.ts b/tests/url/withTrailingSlash.test.ts new file mode 100644 index 00000000..df831af5 --- /dev/null +++ b/tests/url/withTrailingSlash.test.ts @@ -0,0 +1,25 @@ +import { withTrailingSlash } from 'radashi' + +describe('withTrailingSlash', () => { + test('should return undefined if input is undefined', () => { + expect(withTrailingSlash(undefined)).toBe(undefined) + }) + test('should return null if input is null', () => { + expect(withTrailingSlash(null)).toBe(null) + }) + test('should do nothing if trailing slash is already present', () => { + expect(withTrailingSlash('foo/')).toBe('foo/') + }) + test('should add trailing slash to an empty string', () => { + expect(withTrailingSlash('')).toBe('/') + }) + test('should add trailing slash to a string without trailing slash', () => { + expect(withTrailingSlash('text-without-slash')).toBe('text-without-slash/') + }) + test('should do nothing if input is a string of slashes', () => { + expect(withTrailingSlash('/////////')).toBe('/////////') + }) + test('should do nothing if input is a single slash', () => { + expect(withTrailingSlash('/')).toBe('/') + }) +}) From e9371732f74a565a06b638df8ca927b07e4e9851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9renger?= Date: Sat, 4 Jan 2025 21:41:24 +0100 Subject: [PATCH 3/6] feat: add url/withoutLeadingSlash --- benchmarks/url/withoutLeadingSlash.bench.ts | 16 +++ docs/url/withoutLeadingSlash.mdx | 22 +++++ src/mod.ts | 1 + src/url/withoutLeadingSlash.ts | 102 ++++++++++++++++++++ tests/url/withoutLeadingSlash.test.ts | 25 +++++ 5 files changed, 166 insertions(+) create mode 100644 benchmarks/url/withoutLeadingSlash.bench.ts create mode 100644 docs/url/withoutLeadingSlash.mdx create mode 100644 src/url/withoutLeadingSlash.ts create mode 100644 tests/url/withoutLeadingSlash.test.ts diff --git a/benchmarks/url/withoutLeadingSlash.bench.ts b/benchmarks/url/withoutLeadingSlash.bench.ts new file mode 100644 index 00000000..7218a8c5 --- /dev/null +++ b/benchmarks/url/withoutLeadingSlash.bench.ts @@ -0,0 +1,16 @@ +import { withoutLeadingSlash } from 'radashi' + +describe('withoutLeadingSlash', () => { + bench('with no input', () => { + withoutLeadingSlash(undefined) + }) + bench('with empty string', () => { + withoutLeadingSlash('') + }) + bench('with leading slash', () => { + withoutLeadingSlash('/some/path') + }) + bench('without leading slash', () => { + withoutLeadingSlash('some/path') + }) +}) diff --git a/docs/url/withoutLeadingSlash.mdx b/docs/url/withoutLeadingSlash.mdx new file mode 100644 index 00000000..9c5ed0ba --- /dev/null +++ b/docs/url/withoutLeadingSlash.mdx @@ -0,0 +1,22 @@ +--- +title: withoutLeadingSlash +description: Removes the leading slash +--- + +### Usage + +Removes the leading slash `/` from the given URL if it is present. + +This function is useful for ensuring that URLs are properly formatted +without a leading slash, which is often required in web development for +consistency and to avoid issues with relative paths. + +```ts +import { withoutLeadingSlash } from 'radashi' + +withoutLeadingSlash('') // => '' +withoutLeadingSlash('/no/slash') // => 'no/slash' +withoutLeadingSlash('already/has/slash') // => 'already/has/slash' +withoutLeadingSlash(undefined) // => undefined +withoutLeadingSlash(null) // => null +``` diff --git a/src/mod.ts b/src/mod.ts index 86dde79c..89976c07 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -143,6 +143,7 @@ export * from './typed/isWeakMap.ts' export * from './typed/isWeakSet.ts' export * from './url/withLeadingSlash.ts' +export * from './url/withoutLeadingSlash.ts' export * from './url/withTrailingSlash.ts' export * from './types.ts' diff --git a/src/url/withoutLeadingSlash.ts b/src/url/withoutLeadingSlash.ts new file mode 100644 index 00000000..55538720 --- /dev/null +++ b/src/url/withoutLeadingSlash.ts @@ -0,0 +1,102 @@ +/** + * Removes the leading slash `/` from the given URL if it is present. + * + * This function is useful for ensuring that URLs are properly formatted + * without a leading slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string without a leading slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withoutLeadingSlash + * + * @example + * ```ts + * withoutLeadingSlash('') // => '' + * withoutLeadingSlash('/') // => '' + * withoutLeadingSlash('/no/slash') // => 'no/slash' + * withoutLeadingSlash('already/has/slash') // => 'already/has/slash' + * withoutLeadingSlash(undefined) // => undefined + * withoutLeadingSlash(null) // => null + * ``` + */ +export function withoutLeadingSlash(url: string): string + +/** + * Removes the leading slash `/` from the given URL if it is present. + * + * This function is useful for ensuring that URLs are properly formatted + * without a leading slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string without a leading slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withoutLeadingSlash + * + * @example + * ```ts + * withoutLeadingSlash('') // => '' + * withoutLeadingSlash('/') // => '' + * withoutLeadingSlash('/no/slash') // => 'no/slash' + * withoutLeadingSlash('already/has/slash') // => 'already/has/slash' + * withoutLeadingSlash(undefined) // => undefined + * withoutLeadingSlash(null) // => null + * ``` + */ +export function withoutLeadingSlash(url: undefined): undefined + +/** + * Removes the leading slash `/` from the given URL if it is present. + * + * This function is useful for ensuring that URLs are properly formatted + * without a leading slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string without a leading slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withoutLeadingSlash + * + * @example + * ```ts + * withoutLeadingSlash('') // => '' + * withoutLeadingSlash('/') // => '' + * withoutLeadingSlash('/no/slash') // => 'no/slash' + * withoutLeadingSlash('already/has/slash') // => 'already/has/slash' + * withoutLeadingSlash(undefined) // => undefined + * withoutLeadingSlash(null) // => null + * ``` + */ +export function withoutLeadingSlash(url: null): null + +/** + * Removes the leading slash `/` from the given URL if it is present. + * + * This function is useful for ensuring that URLs are properly formatted + * without a leading slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string without a leading slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withoutLeadingSlash + * + * @example + * ```ts + * withoutLeadingSlash('') // => '' + * withoutLeadingSlash('/') // => '' + * withoutLeadingSlash('/no/slash') // => 'no/slash' + * withoutLeadingSlash('already/has/slash') // => 'already/has/slash' + * withoutLeadingSlash(undefined) // => undefined + * withoutLeadingSlash(null) // => null + * ``` + */ +export function withoutLeadingSlash( + url: string | undefined | null, +): string | undefined | null { + if (url === undefined || url === null) { + return url + } + return url[0] === '/' ? url.slice(1) : url +} diff --git a/tests/url/withoutLeadingSlash.test.ts b/tests/url/withoutLeadingSlash.test.ts new file mode 100644 index 00000000..d941134a --- /dev/null +++ b/tests/url/withoutLeadingSlash.test.ts @@ -0,0 +1,25 @@ +import { withoutLeadingSlash } from 'radashi' + +describe('withoutLeadingSlash', () => { + test('should return undefined if input is undefined', () => { + expect(withoutLeadingSlash(undefined)).toBe(undefined) + }) + test('should return null if input is null', () => { + expect(withoutLeadingSlash(null)).toBe(null) + }) + test('should remove leading slash if present', () => { + expect(withoutLeadingSlash('/foo')).toBe('foo') + }) + test('should do nothing if input is an empty string', () => { + expect(withoutLeadingSlash('')).toBe('') + }) + test('should do nothing if input does not have a leading slash', () => { + expect(withoutLeadingSlash('text-without-slash')).toBe('text-without-slash') + }) + test('should remove first slash if input is a string of slashes', () => { + expect(withoutLeadingSlash('/////////')).toBe('////////') + }) + test('should return an empty string if input is a single slash', () => { + expect(withoutLeadingSlash('/')).toBe('') + }) +}) From 3c3f4d33d0a05374dfd0bda5672e043efc45edb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9renger?= Date: Sat, 4 Jan 2025 21:41:24 +0100 Subject: [PATCH 4/6] feat: add url/withoutTrailingSlash --- benchmarks/url/withoutTrailingSlash.bench.ts | 16 +++ docs/url/withoutTrailingSlash.mdx | 22 ++++ src/mod.ts | 1 + src/url/withoutTrailingSlash.ts | 102 +++++++++++++++++++ tests/url/withoutTrailingSlash.test.ts | 27 +++++ 5 files changed, 168 insertions(+) create mode 100644 benchmarks/url/withoutTrailingSlash.bench.ts create mode 100644 docs/url/withoutTrailingSlash.mdx create mode 100644 src/url/withoutTrailingSlash.ts create mode 100644 tests/url/withoutTrailingSlash.test.ts diff --git a/benchmarks/url/withoutTrailingSlash.bench.ts b/benchmarks/url/withoutTrailingSlash.bench.ts new file mode 100644 index 00000000..610c848f --- /dev/null +++ b/benchmarks/url/withoutTrailingSlash.bench.ts @@ -0,0 +1,16 @@ +import { withoutTrailingSlash } from 'radashi' + +describe('withoutTrailingSlash', () => { + bench('with no input', () => { + withoutTrailingSlash(undefined) + }) + bench('with empty string', () => { + withoutTrailingSlash('') + }) + bench('with trailing slash', () => { + withoutTrailingSlash('some/path/') + }) + bench('without trailing slash', () => { + withoutTrailingSlash('some/path') + }) +}) diff --git a/docs/url/withoutTrailingSlash.mdx b/docs/url/withoutTrailingSlash.mdx new file mode 100644 index 00000000..a0eca49a --- /dev/null +++ b/docs/url/withoutTrailingSlash.mdx @@ -0,0 +1,22 @@ +--- +title: withoutTrailingSlash +description: Removes the trailing slash +--- + +### Usage + +Removes the trailing slash `/` from the given URL if it is present. + +This function is useful for ensuring that URLs are properly formatted +without a trailing slash, which is often required in web development for +consistency and to avoid issues with relative paths. + +```ts +import { withoutTrailingSlash } from 'radashi' + +withoutTrailingSlash('') // => '' +withoutTrailingSlash('no/slash/') // => 'no/slash' +withoutTrailingSlash('already/has/slash') // => 'already/has/slash' +withoutTrailingSlash(undefined) // => undefined +withoutTrailingSlash(null) // => null +``` diff --git a/src/mod.ts b/src/mod.ts index 89976c07..da121131 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -144,6 +144,7 @@ export * from './typed/isWeakSet.ts' export * from './url/withLeadingSlash.ts' export * from './url/withoutLeadingSlash.ts' +export * from './url/withoutTrailingSlash.ts' export * from './url/withTrailingSlash.ts' export * from './types.ts' diff --git a/src/url/withoutTrailingSlash.ts b/src/url/withoutTrailingSlash.ts new file mode 100644 index 00000000..c3278ad9 --- /dev/null +++ b/src/url/withoutTrailingSlash.ts @@ -0,0 +1,102 @@ +/** + * Removes the trailing slash `/` from the given URL if it is present. + * + * This function is useful for ensuring that URLs are properly formatted + * without a trailing slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string without a trailing slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withoutTrailingSlash + * + * @example + * ```ts + * withoutTrailingSlash('') // => '' + * withoutTrailingSlash('/') // => '' + * withoutTrailingSlash('no/slash/') // => 'no/slash' + * withoutTrailingSlash('already/has/slash') // => 'already/has/slash' + * withoutTrailingSlash(undefined) // => undefined + * withoutTrailingSlash(null) // => null + * ``` + */ +export function withoutTrailingSlash(url: string): string + +/** + * Removes the trailing slash `/` from the given URL if it is present. + * + * This function is useful for ensuring that URLs are properly formatted + * without a trailing slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string without a trailing slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withoutTrailingSlash + * + * @example + * ```ts + * withoutTrailingSlash('') // => '' + * withoutTrailingSlash('/') // => '' + * withoutTrailingSlash('no/slash/') // => 'no/slash' + * withoutTrailingSlash('already/has/slash') // => 'already/has/slash' + * withoutTrailingSlash(undefined) // => undefined + * withoutTrailingSlash(null) // => null + * ``` + */ +export function withoutTrailingSlash(url: undefined): undefined + +/** + * Removes the trailing slash `/` from the given URL if it is present. + * + * This function is useful for ensuring that URLs are properly formatted + * without a trailing slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string without a trailing slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withoutTrailingSlash + * + * @example + * ```ts + * withoutTrailingSlash('') // => '' + * withoutTrailingSlash('/') // => '' + * withoutTrailingSlash('no/slash/') // => 'no/slash' + * withoutTrailingSlash('already/has/slash') // => 'already/has/slash' + * withoutTrailingSlash(undefined) // => undefined + * withoutTrailingSlash(null) // => null + * ``` + */ +export function withoutTrailingSlash(url: null): null + +/** + * Removes the trailing slash `/` from the given URL if it is present. + * + * This function is useful for ensuring that URLs are properly formatted + * without a trailing slash, which is often required in web development for + * consistency and to avoid issues with relative paths. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string without a trailing slash, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @see https://radashi.js.org/reference/url/withoutTrailingSlash + * + * @example + * ```ts + * withoutTrailingSlash('') // => '' + * withoutTrailingSlash('/') // => '' + * withoutTrailingSlash('no/slash/') // => 'no/slash' + * withoutTrailingSlash('already/has/slash') // => 'already/has/slash' + * withoutTrailingSlash(undefined) // => undefined + * withoutTrailingSlash(null) // => null + * ``` + */ +export function withoutTrailingSlash( + url: string | undefined | null, +): string | undefined | null { + if (url === undefined || url === null) { + return url + } + return url[url.length - 1] === '/' ? url.slice(0, -1) : url +} diff --git a/tests/url/withoutTrailingSlash.test.ts b/tests/url/withoutTrailingSlash.test.ts new file mode 100644 index 00000000..8fb91bd0 --- /dev/null +++ b/tests/url/withoutTrailingSlash.test.ts @@ -0,0 +1,27 @@ +import { withoutTrailingSlash } from 'radashi' + +describe('withoutTrailingSlash', () => { + test('should return undefined if input is undefined', () => { + expect(withoutTrailingSlash(undefined)).toBe(undefined) + }) + test('should return null if input is null', () => { + expect(withoutTrailingSlash(null)).toBe(null) + }) + test('should remove trailing slash if present', () => { + expect(withoutTrailingSlash('foo/')).toBe('foo') + }) + test('should do nothing if input is an empty string', () => { + expect(withoutTrailingSlash('')).toBe('') + }) + test('should do nothing if input does not have a trailing slash', () => { + expect(withoutTrailingSlash('text-without-slash')).toBe( + 'text-without-slash', + ) + }) + test('should remove last slash if input is a string of slashes', () => { + expect(withoutTrailingSlash('/////////')).toBe('////////') + }) + test('should return an empty string if input is a single slash', () => { + expect(withoutTrailingSlash('/')).toBe('') + }) +}) From 372ca195b6dfffcdc10acdb8319a2cbd96e3d53c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9renger?= Date: Sat, 4 Jan 2025 21:41:24 +0100 Subject: [PATCH 5/6] feat: add url/onlyPath --- benchmarks/url/onlyPath.bench.ts | 22 +++++++ docs/url/onlyPath.mdx | 26 ++++++++ src/mod.ts | 1 + src/url/onlyPath.ts | 103 +++++++++++++++++++++++++++++++ tests/url/onlyPath.test.ts | 22 +++++++ 5 files changed, 174 insertions(+) create mode 100644 benchmarks/url/onlyPath.bench.ts create mode 100644 docs/url/onlyPath.mdx create mode 100644 src/url/onlyPath.ts create mode 100644 tests/url/onlyPath.test.ts diff --git a/benchmarks/url/onlyPath.bench.ts b/benchmarks/url/onlyPath.bench.ts new file mode 100644 index 00000000..79d5d9f1 --- /dev/null +++ b/benchmarks/url/onlyPath.bench.ts @@ -0,0 +1,22 @@ +import { onlyPath } from 'radashi' + +describe('onlyPath', () => { + bench('with no input', () => { + onlyPath(undefined) + }) + bench('with empty string', () => { + onlyPath('') + }) + bench('with path only', () => { + onlyPath('/some/path') + }) + bench('with path and query', () => { + onlyPath('/some/path?query=thing') + }) + bench('with path and fragment', () => { + onlyPath('/some/path#fragment') + }) + bench('with path, query, and fragment', () => { + onlyPath('/some/path?query=thing#fragment') + }) +}) diff --git a/docs/url/onlyPath.mdx b/docs/url/onlyPath.mdx new file mode 100644 index 00000000..93864cba --- /dev/null +++ b/docs/url/onlyPath.mdx @@ -0,0 +1,26 @@ +--- +title: onlyPath +description: Extract only the path +--- + +### Usage + +Extract only the path from an URI with optional query and fragments. + +For example, all these parameters will return `/path`: + +- `/path` +- `/path?query=thing` +- `/path#fragment` +- `/path?query=thing#fragment` + +```ts +import { onlyPath } from 'radashi' + +onlyPath('/path') // => '/path' +onlyPath('/path?query=thing') // => '/path' +onlyPath('/path#fragment') // => '/path' +onlyPath('/path?query=thing#fragment') // => '/path' +onlyPath(undefined) // => undefined +onlyPath(null) // => null +``` diff --git a/src/mod.ts b/src/mod.ts index da121131..95bc9218 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -142,6 +142,7 @@ export * from './typed/isUndefined.ts' export * from './typed/isWeakMap.ts' export * from './typed/isWeakSet.ts' +export * from './url/onlyPath.ts' export * from './url/withLeadingSlash.ts' export * from './url/withoutLeadingSlash.ts' export * from './url/withoutTrailingSlash.ts' diff --git a/src/url/onlyPath.ts b/src/url/onlyPath.ts new file mode 100644 index 00000000..56cdc54b --- /dev/null +++ b/src/url/onlyPath.ts @@ -0,0 +1,103 @@ +/** + * Extract only the path from an URI with optional query and fragments. + * + * For example, all these parameters will return `/path`: + * - `/path` + * - `/path?query=thing` + * - `/path#fragment` + * - `/path?query=thing#fragment` + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string without query and fragment, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @example + * ```ts + * onlyPath('/path') // => '/path' + * onlyPath('/path?query=thing') // => '/path' + * onlyPath('/path#fragment') // => '/path' + * onlyPath('/path?query=thing#fragment') // => '/path' + * onlyPath(undefined) // => undefined + * onlyPath(null) // => null + * ``` + */ +export function onlyPath(url: string): string + +/** + * Extract only the path from an URI with optional query and fragments. + * + * For example, all these parameters will return `/path`: + * - `/path` + * - `/path?query=thing` + * - `/path#fragment` + * - `/path?query=thing#fragment` + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string without query and fragment, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @example + * ```ts + * onlyPath('/path') // => '/path' + * onlyPath('/path?query=thing') // => '/path' + * onlyPath('/path#fragment') // => '/path' + * onlyPath('/path?query=thing#fragment') // => '/path' + * onlyPath(undefined) // => undefined + * onlyPath(null) // => null + * ``` + */ +export function onlyPath(url: null): null + +/** + * Extract only the path from an URI with optional query and fragments. + * + * For example, all these parameters will return `/path`: + * - `/path` + * - `/path?query=thing` + * - `/path#fragment` + * - `/path?query=thing#fragment` + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string without query and fragment, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @example + * ```ts + * onlyPath('/path') // => '/path' + * onlyPath('/path?query=thing') // => '/path' + * onlyPath('/path#fragment') // => '/path' + * onlyPath('/path?query=thing#fragment') // => '/path' + * onlyPath(undefined) // => undefined + * onlyPath(null) // => null + * ``` + */ +export function onlyPath(url: undefined): undefined + +/** + * Extract only the path from an URI with optional query and fragments. + * + * For example, all these parameters will return `/path`: + * - `/path` + * - `/path?query=thing` + * - `/path#fragment` + * - `/path?query=thing#fragment` + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The URL string without query and fragment, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @example + * ```ts + * onlyPath('/path') // => '/path' + * onlyPath('/path?query=thing') // => '/path' + * onlyPath('/path#fragment') // => '/path' + * onlyPath('/path?query=thing#fragment') // => '/path' + * onlyPath(undefined) // => undefined + * onlyPath(null) // => null + * ``` + */ +export function onlyPath( + url: string | undefined | null, +): string | undefined | null { + if (url === undefined || url === null) { + return url + } + const [path] = url.split(/[?#]/) + return path +} diff --git a/tests/url/onlyPath.test.ts b/tests/url/onlyPath.test.ts new file mode 100644 index 00000000..99bd6e70 --- /dev/null +++ b/tests/url/onlyPath.test.ts @@ -0,0 +1,22 @@ +import { onlyPath } from 'radashi' + +describe('onlyPath', () => { + test('should return the path without query and fragment', () => { + expect(onlyPath('/path')).toBe('/path') + }) + test('should return the path without query and fragment when query is present', () => { + expect(onlyPath('/path?query=thing')).toBe('/path') + }) + test('should return the path without query and fragment when fragment is present', () => { + expect(onlyPath('/path#fragment')).toBe('/path') + }) + test('should return the path without query and fragment when both query and fragment are present', () => { + expect(onlyPath('/path?query=thing#fragment')).toBe('/path') + }) + test('should return undefined if input is undefined', () => { + expect(onlyPath(undefined)).toBe(undefined) + }) + test('should return null if input is null', () => { + expect(onlyPath(null)).toBe(null) + }) +}) From 22586adeddddc868d16341543b1657488f5b5aef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9renger?= Date: Sat, 4 Jan 2025 21:41:24 +0100 Subject: [PATCH 6/6] feat: add url/cleanPath --- benchmarks/url/cleanPath.bench.ts | 19 +++++++++++++++++++ docs/url/cleanPath.mdx | 18 ++++++++++++++++++ src/mod.ts | 1 + src/url/cleanPath.ts | 23 +++++++++++++++++++++++ tests/url/cleanPath.test.ts | 29 +++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+) create mode 100644 benchmarks/url/cleanPath.bench.ts create mode 100644 docs/url/cleanPath.mdx create mode 100644 src/url/cleanPath.ts create mode 100644 tests/url/cleanPath.test.ts diff --git a/benchmarks/url/cleanPath.bench.ts b/benchmarks/url/cleanPath.bench.ts new file mode 100644 index 00000000..15f4022b --- /dev/null +++ b/benchmarks/url/cleanPath.bench.ts @@ -0,0 +1,19 @@ +import { cleanPath } from 'radashi' + +describe('cleanPath', () => { + bench('with no input', () => { + cleanPath(undefined) + }) + bench('with empty string', () => { + cleanPath('') + }) + bench('with correct path', () => { + cleanPath('/some/path') + }) + bench('with multiple slashes in path', () => { + cleanPath('/some//path') + }) + bench('with protocol, path, query, and fragment', () => { + cleanPath('https://server//some//path?query=thing#fragment') + }) +}) diff --git a/docs/url/cleanPath.mdx b/docs/url/cleanPath.mdx new file mode 100644 index 00000000..f4549d8d --- /dev/null +++ b/docs/url/cleanPath.mdx @@ -0,0 +1,18 @@ +--- +title: cleanPath +description: Clean a path +--- + +### Usage + +Clean a path by removing duplicate slashes. +The protocol part of the URL is not modified. + +```ts +import { cleanPath } from 'radashi' + +cleanPath('/path//to///resource') // => '/path/to/resource' +cleanPath('http://example.com//path//to///resource') // => 'http://example.com/path/to/resource' +cleanPath(undefined) // => undefined +cleanPath(null) // => null +``` diff --git a/src/mod.ts b/src/mod.ts index 95bc9218..c4275820 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -142,6 +142,7 @@ export * from './typed/isUndefined.ts' export * from './typed/isWeakMap.ts' export * from './typed/isWeakSet.ts' +export * from './url/cleanPath.ts' export * from './url/onlyPath.ts' export * from './url/withLeadingSlash.ts' export * from './url/withoutLeadingSlash.ts' diff --git a/src/url/cleanPath.ts b/src/url/cleanPath.ts new file mode 100644 index 00000000..192c57af --- /dev/null +++ b/src/url/cleanPath.ts @@ -0,0 +1,23 @@ +/** + * Clean an URL by removing duplicate slashes. + * The protocol part of the URL is not modified. + * + * @param url - The URL string to be processed. Can be `string`, `undefined`, or `null`. + * @returns The cleaned URL string, or `undefined` if the input is `undefined`, or `null` if the input is `null`. + * + * @example + * ```ts + * cleanPath('/path//to///resource') // => '/path/to/resource' + * cleanPath('http://example.com//path//to///resource') // => 'http://example.com/path/to/resource' + * cleanPath(undefined) // => undefined + * cleanPath(null) // => null + * ``` + */ +export function cleanPath( + url: string | undefined | null, +): string | undefined | null { + if (url === undefined || url === null) { + return url + } + return url.replace(/([^:]\/)\/+/g, '$1') +} diff --git a/tests/url/cleanPath.test.ts b/tests/url/cleanPath.test.ts new file mode 100644 index 00000000..61e720c8 --- /dev/null +++ b/tests/url/cleanPath.test.ts @@ -0,0 +1,29 @@ +import { cleanPath } from 'radashi' + +describe('cleanPath', () => { + test('should remove duplicate slashes', () => { + expect(cleanPath('/path//to///resource')).toBe('/path/to/resource') + }) + test('should handle URLs with protocol correctly', () => { + expect(cleanPath('http://example.com//path//to///resource')).toBe( + 'http://example.com/path/to/resource', + ) + }) + test('should return undefined if input is undefined', () => { + expect(cleanPath(undefined)).toBe(undefined) + }) + test('should return null if input is null', () => { + expect(cleanPath(null)).toBe(null) + }) + test('should handle path without duplicate slashes', () => { + expect(cleanPath('/path/to/resource')).toBe('/path/to/resource') + }) + test('should handle URLs with fragments and queries', () => { + expect(cleanPath('/path//to///resource?query=thing#fragment')).toBe( + '/path/to/resource?query=thing#fragment', + ) + expect( + cleanPath('http://example.com//path//to///resource?query=thing#fragment'), + ).toBe('http://example.com/path/to/resource?query=thing#fragment') + }) +})