Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cuddly-pants-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bnidev/js-utils": minor
---

feat: Added new utility functions for strings: `capitalize`, `stripHtmlTags`, `truncate`.
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
* @categoryDescription Object
* Utility functions for working with objects, including deep cloning, merging, and property manipulation.
*
* @categoryDescription String
* Utility functions for string manipulation, including formatting, parsing, and validation.
*
* @categoryDescription Timing
* Utility functions for managing timing-related tasks, such as throttling, debouncing, and timeouts.
*
Expand All @@ -23,4 +26,6 @@ export * from './dom'

export * from './object'

export * from './string'

export * from './timing'
25 changes: 25 additions & 0 deletions src/string/__tests__/capitalize.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { describe, expect, it } from 'vitest'
import { capitalize } from '../capitalize'

describe('capitalize', () => {
it('capitalizes the first letter of a lowercase string', () => {
expect(capitalize('hello')).toBe('Hello')
})

it('leaves the first letter capitalized if already uppercase', () => {
expect(capitalize('World')).toBe('World')
})

it('returns an empty string if input is empty', () => {
expect(capitalize('')).toBe('')
})

it('does not modify the rest of the string', () => {
expect(capitalize('gOODBYE')).toBe('GOODBYE')
})

it('handles single-character strings', () => {
expect(capitalize('a')).toBe('A')
expect(capitalize('Z')).toBe('Z')
})
})
38 changes: 38 additions & 0 deletions src/string/__tests__/stripHtmlTags.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { describe, expect, it } from 'vitest'
import { stripHtmlTags } from '../stripHtmlTags'

describe('stripHtmlTags', () => {
it('removes simple HTML tags', () => {
expect(stripHtmlTags('<p>Hello <strong>World</strong></p>')).toBe(
'Hello World'
)
})

it('removes self-closing tags', () => {
expect(stripHtmlTags('Hello<br/>World')).toBe('HelloWorld')
})

it('returns empty string if input is empty', () => {
expect(stripHtmlTags('')).toBe('')
})

it('returns the same string if no HTML tags present', () => {
expect(stripHtmlTags('Just plain text')).toBe('Just plain text')
})

it('removes nested HTML tags', () => {
expect(stripHtmlTags('<div><p>Nested <em>tags</em></p></div>')).toBe(
'Nested tags'
)
})

it('handles attributes inside tags', () => {
expect(stripHtmlTags('<a href="https://example.com">Link</a>')).toBe('Link')
})

it('handles multiple tags with spaces', () => {
expect(stripHtmlTags('This is <b>bold</b> and <i>italic</i> text.')).toBe(
'This is bold and italic text.'
)
})
})
33 changes: 33 additions & 0 deletions src/string/__tests__/truncate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { describe, expect, it } from 'vitest'
import { truncate } from '../truncate'

describe('truncate', () => {
it('does not truncate if string is shorter than maxLength', () => {
expect(truncate('Hello', 10)).toBe('Hello')
})

it('truncates and appends default suffix', () => {
expect(truncate('Hello World', 8)).toBe('Hello...')
})

it('truncates and appends custom suffix', () => {
expect(truncate('Hello World', 5, '~~')).toBe('Hel~~')
})

it('returns full string if maxLength equals string length', () => {
expect(truncate('Hello', 5)).toBe('Hello')
})

it('handles maxLength less than or equal to suffix length', () => {
expect(truncate('Hello World', 2)).toBe('He')
expect(truncate('Hello World', 3, '>>>')).toBe('Hel')
})

it('handles empty string input', () => {
expect(truncate('', 5)).toBe('')
})

it('handles zero maxLength', () => {
expect(truncate('Hello', 0)).toBe('')
})
})
30 changes: 30 additions & 0 deletions src/string/capitalize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Capitalizes the first character of a string.
*
* @param str - The input string to capitalize.
* @returns The capitalized string.
*
* @remarks
* If the input is an empty string, it returns the empty string.
*
* @category String
*
* @example Imports
* ```ts
* // ES Module
* import { capitalize } from '@bnidev/js-utils'
*
* // CommonJS
* const { capitalize } = require('@bnidev/js-utils')
* ```
*
* @example Usage
* ```ts
* capitalize('hello') // → 'Hello'
* capitalize('Hello') // → 'Hello'
* ```
*/
export function capitalize(str: string): string {
if (!str) return ''
return str.charAt(0).toUpperCase() + str.slice(1)
}
5 changes: 5 additions & 0 deletions src/string/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Export all modules from the 'string' directory

export * from './capitalize'
export * from './stripHtmlTags'
export * from './truncate'
27 changes: 27 additions & 0 deletions src/string/stripHtmlTags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Removes all HTML tags from a string, returning plain text.
*
* @param html - The input string containing HTML.
* @returns The string without HTML tags.
*
* @category String
*
* @example Imports
* ```ts
* // ES Module
* import { stripHtmlTags } from '@bnidev/js-utils'
*
* // CommonJS
* const { stripHtmlTags } = require('@bnidev/js-utils')
* ```
*
* @example Usage
* ```ts
* stripHtml('<p>Hello <strong>World</strong></p>') // → 'Hello World'
* ```
*/
export function stripHtmlTags(html: string): string {
if (!html) return ''
// Simple regex to remove anything between < and >
return html.replace(/<[^>]*>/g, '')
}
40 changes: 40 additions & 0 deletions src/string/truncate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Truncates a string to a specified maximum length and appends a suffix if truncated.
*
* @param str - The string to truncate.
* @param maxLength - Maximum length of the truncated string including suffix.
* @param suffix - String to append after truncation (default: '...').
* @returns The truncated string with suffix if truncated, otherwise the original string.
*
* @category String
*
* @example Imports
* ```ts
* // ES Module
* import { truncate } from '@bnidev/js-utils'
*
* // CommonJS
* const { truncate } = require('@bnidev/js-utils')
* ```
*
* @example Usage
* ```ts
* truncate('Hello World', 8) // → 'Hello...'
* truncate('Hello', 10) // → 'Hello'
* truncate('Hello World', 5, '~~') // → 'Hel~~'
* ```
*/
export function truncate(
str: string,
maxLength: number,
suffix = '...'
): string {
if (str.length <= maxLength) return str

if (maxLength <= suffix.length) {
// If maxLength is less than suffix length, just slice the string
return str.slice(0, maxLength)
}

return str.slice(0, maxLength - suffix.length) + suffix
}