| name | stacks-strings |
| description | Use when working with string utilities in Stacks — case conversion (camelCase, PascalCase, snake_case, kebab-case, CONSTANT_CASE, Train-Case, etc.), pluralization, string validation (email, URL, UUID, credit card, etc.), slug generation, random strings, template interpolation, or the Str facade. Covers @stacksjs/strings. |
| license | MIT |
| compatibility | Bun >= 1.3.0, TypeScript |
| allowed-tools | Read Edit Write Bash Grep Glob |
Stacks Strings
Key Paths
- Core package:
storage/framework/core/strings/src/
- Package:
@stacksjs/strings
- Entry:
storage/framework/core/strings/src/string.ts (re-exports all submodules)
Architecture
The package re-exports from these submodules:
case.ts — case conversion functions (camelCase, pascalCase, snakeCase, etc.)
pluralize.ts — English pluralization/singularization with rule engine
slug.ts — URL slug generation with extensive character map
utils.ts — random strings, template interpolation, slash, prefix/suffix, truncate, detect-indent, detect-newline
macro.ts — Str and str facade objects
validators.ts — re-exports from is.ts (native string validators)
sponge-case.ts — random upper/lower case
swap-case.ts — swap upper/lower case
title-case.ts — intelligent title case with small word handling
The top-level index.ts does:
export * from './string'
export * as string from './string'
And string.ts does:
export * from './case'
export * from './helpers'
export * from './macro'
export * from './pluralize'
export * from './slug'
export * from './utils'
export * from './validators'
Case Conversion Functions
All accept (input: string, options?: CaseOptions) and use Unicode-aware word boundary detection via regex patterns (\p{Ll}, \p{Lu}).
interface CaseOptions {
locale?: string[] | string | false | undefined
split?: (value: string) => string[]
delimiter?: string
prefixCharacters?: string
suffixCharacters?: string
}
interface PascalCaseOptions extends CaseOptions {
mergeAmbiguousCharacters?: boolean
}
Functions
camelCase('hello world')
pascalCase('hello world')
snakeCase('hello world')
kebabCase('hello world')
constantCase('hello world')
trainCase('hello world')
dotCase('hello world')
pathCase('hello world')
sentenceCase('hello world')
capitalCase('hello world')
noCase('helloWorld')
pascalSnakeCase('hello world')
paramCase('hello world')
Special Case Functions
spongeCase('hello world')
spongeCase('hello', 'en')
swapCase('Hello')
swapCase('Hello', 'en')
Title Case
titleCase('hello world')
titleCase('the quick brown fox')
titleCase('the quick brown fox', { sentenceCase: true })
interface TitleCaseOptions {
locale?: string | string[]
sentenceCase?: boolean
sentenceTerminators?: Set<string>
titleTerminators?: Set<string>
smallWords?: Set<string>
wordSeparators?: Set<string>
}
Handles special cases: URLs, email addresses, acronyms (e.g., "U.S.A."), camelCase words (e.g., "iPhone"), and hyphenated compound words.
Word Splitting
split('camelCase')
split('hello_world')
split('HTTPSRequest')
splitSeparateNumbers('test123')
Uses Unicode-aware regex for word boundary detection. Handles camelCase, PascalCase, snake_case, UPPER_CASE, and mixed formats.
Pluralization
Full English pluralization engine with irregular words, custom rules, and uncountable nouns.
import { plural, singular, pluralize } from '@stacksjs/strings'
plural('person')
plural('person', 1)
singular('people')
pluralize('mouse')
pluralize('mouse', { count: 1 })
pluralize('mouse', { count: 2 })
pluralize('mouse', { count: 5, inclusive: true })
pluralize.isPlural('dogs')
pluralize.isSingular('dog')
pluralize.addPluralRule(/gex$/i, 'gices')
pluralize.addSingularRule(/gices$/i, 'gex')
pluralize.addIrregularRule(, )
pluralize.()
Built-in Irregular Words
The engine includes 48 irregular word pairs (e.g., ox/oxen, foot/feet, goose/geese, child/children, person/people, die/dice, tooth/teeth, quiz/quizzes, etc.).
Built-in Uncountable Words
Includes 74 uncountable words (e.g., sheep, fish, moose, aircraft, software, equipment, information, etc.) plus regex patterns for Pokemon, words ending in non-ASCII ese, deer, fish, measles, pox, sheep.
Case Preservation
The pluralization engine preserves the case pattern of the input word:
'PERSON' -> 'PEOPLE'
'Person' -> 'People'
'person' -> 'people'
Slug Generation
Two slug functions with different defaults:
slug(str, options?) — Simple slug (strict + lowercase by default)
slug('Hello World')
slug('Hello World', { lower: false })
slugify(str, options?) — Full-featured slug
slugify('Hello World!')
slugify('Hello World!', { lower: true, strict: true })
interface SlugifyOptions {
replacement?: string
remove?: RegExp
lower?: boolean
strict?: boolean
locale?: string
trim?: boolean
}
Character Map
Extensive built-in character map covering:
- Latin extended (accented chars): A-Z with diacritics
- Greek alphabet
- Cyrillic alphabet
- Armenian alphabet
- Arabic alphabet and numerals
- Georgian alphabet
- Vietnamese diacritics
- Currency symbols ($, EUR, GBP, YEN, etc.)
- Special characters (trademark, copyright, etc.)
Locale Support
Locale-specific character mappings for: bg, de, es, fr, pt, uk, vi, da, nb, it, nl, sv.
slugify('AE', { locale: 'de' })
slugify('AE')
Extending the Character Map
extendCharMap({ 'EUR': 'EUR', 'GBP': 'GBP' })
String Utilities
capitalize('hello world')
lowercase('HELLO')
slash('path\\to\\file')
ensurePrefix('https://', 'google.com')
ensurePrefix('https://', 'https://x.com')
ensureSuffix('.ts', 'file')
ensureSuffix('.ts', 'file.ts')
template('Hello {0}! My name is {1}.', 'Buddy', 'Chris')
truncate('long text here', 10, '...')
truncate('short', 10)
random()
random(21)
random(10, )
urlAlphabet Constant
const urlAlphabet = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
String Validators
Native TypeScript implementations (no external validator dependency). All accept a string and return boolean.
Core Validators
isEmail('user@example.com')
isStrongPassword('Abc123!@#')
isAlphanumeric('abc123')
isAlpha('abcdef')
isNumeric('123')
isURL('https://example.com')
Format Validators
isHexColor('#ff0000')
isHexadecimal('deadbeef')
isBase64('SGVsbG8=')
isBase32('MFZWI===')
isAscii('hello')
isJSON('{"key":"value"}')
isHSL('hsl(0, 100%, 50%)')
isMimeType('text/html')
isDataURI('data:text/plain;base64,...')
isJWT('eyJhbGciOi...')
Network Validators
isIP('192.168.1.1')
isIPRange('192.168.1.0/24')
isMACAddress('00:11:22:33:44:55')
isFQDN('example.com')
Financial Validators
isCreditCard('4111111111111111')
isISBN('978-3-16-148410-0')
isCurrency('$1,234.56')
isIBAN('DE89370400440532013000')
isISIN('US0378331005')
isISSN('0378-5955')
Geographic Validators
isLatLong('40.7128,-74.0060')
isLatitude('40.7128')
isLongitude('-74.0060')
isPostalCode('12345')
Phone, Identity, Date Validators
isMobilePhone('+1234567890')
isUUID('550e8400-e29b-41d4-a716...')
isISO8601('2024-01-01T00:00:00Z')
isIdentityCard('AB123456')
isISRC('USRC17607839')
isISO31661Alpha2('US')
isISO31661Alpha3('USA')
validateUsername('user123')
isHash('abc123...', 'md5')
isByteLength('hello', { min: 1, max: 10 })
isFullWidth('\uFF21')
isHalfWidth('a')
Newline & Indentation Detection
Newline Detection (from detect-newline.ts)
detectNewline('line1\r\nline2')
detectNewline('line1\nline2')
detectNewline('no newline')
detectNewlineGraceful(text)
The detection counts CRLF vs LF occurrences and returns the dominant type.
Indentation Detection (from detect-indent.ts)
detectIndent(' hello\n world')
detectIndent('\thello\n\t\tworld')
detectIndent('no indent')
Algorithm: builds a frequency map of indent changes (type + size), handles edge cases like single-space indentation (skipped by default to avoid false positives from code comments), then selects the most common indent pattern.
Str Facade Object
Both Str and str are exported as equivalent facade objects providing all functions as methods.
import { Str, str } from '@stacksjs/strings'
Str.camelCase('hello world')
Str.pascalCase('hello world')
Str.snakeCase('hello world')
Str.kebabCase('hello world')
Str.constantCase('hello world')
Str.dotCase('hello world')
Str.noCase('helloWorld')
Str.paramCase('hello world')
Str.pathCase('hello world')
Str.sentenceCase('hello world')
Str.capitalCase('hello world')
Str.()
.()
.()
.()
.(, )
.(, , )
.(, )
.()
.(, )
.(, )
.(text)
.(text)
.()
.()
.()
.()
.(, )
.(, )
.(, )
.()
Exported Constants
WORD_SEPARATORS: Set<string>
SENTENCE_TERMINATORS: Set<string>
TITLE_TERMINATORS: Set<string>
SMALL_WORDS: Set<string>
urlAlphabet: string
Exported Types
type Locale = string[] | string | false | undefined
interface CaseOptions { locale?, split?, delimiter?, prefixCharacters?, suffixCharacters? }
interface PascalCaseOptions extends CaseOptions { mergeAmbiguousCharacters? }
interface TitleCaseOptions { locale?, sentenceCase?, sentenceTerminators?, smallWords?, titleTerminators?, wordSeparators? }
interface SlugifyOptions { replacement?, remove?, lower?, strict?, locale?, trim? }
interface PluralizeOptions { count?, inclusive? }
Gotchas
- Case conversion uses Unicode-aware word boundary detection (
\p{Ll}, \p{Lu}) -- handles international characters
slug() calls slugify() under the hood but defaults to { lower: true, strict: true }; slugify() defaults to { lower: false, strict: false }
pascalCase and camelCase accept PascalCaseOptions with mergeAmbiguousCharacters to control number-prefixed word handling
- All case functions support
prefixCharacters/suffixCharacters to preserve leading/trailing non-word characters
pluralize() is both a function AND an object with methods -- pluralize.plural(), pluralize.singular(), etc.
- The
random() function uses Math.random(), not crypto -- fine for IDs but not for security tokens
isNumeric() only matches whole numbers (/^\d+$/), not floats -- use parseFloat for float checking
isURL() requires http: or https: protocol -- rejects other protocols and bare domains
- The
Str facade does NOT include validators -- only case, utility, and pluralization methods
helpers.ts only exports toString() which returns Object.prototype.toString.call(v)