mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-09 23:21:30 -07:00
Initial commit
This commit is contained in:
@@ -0,0 +1,328 @@
|
||||
import slugify from 'slugify'
|
||||
|
||||
export function slugifyText(text: string) {
|
||||
const slug = slugify(text, {
|
||||
lower: true,
|
||||
remove: /['.]/g,
|
||||
})
|
||||
return slug
|
||||
}
|
||||
|
||||
export function slugifyTextSingular(text: string) {
|
||||
const slug = slugify(text, {
|
||||
lower: true,
|
||||
remove: /['.]/g,
|
||||
})
|
||||
if (slug.endsWith('s')) {
|
||||
return slug.slice(0, slug.length - 1)
|
||||
}
|
||||
|
||||
return slug
|
||||
}
|
||||
|
||||
export function slugifyTextPlural(text: string) {
|
||||
const slug = slugify(text, {
|
||||
lower: true,
|
||||
remove: /['.]/g,
|
||||
})
|
||||
if (slug.endsWith('s')) {
|
||||
return slug
|
||||
}
|
||||
|
||||
return `${slug}s`
|
||||
}
|
||||
|
||||
export function pascalText(text: string) {
|
||||
return pascalCase(text)
|
||||
}
|
||||
|
||||
export function pascalTextSingular(text: string) {
|
||||
const slug = pascalCase(text)
|
||||
if (slug.endsWith('s')) {
|
||||
return slug.slice(0, slug.length - 1)
|
||||
}
|
||||
|
||||
return slug
|
||||
}
|
||||
|
||||
export function pascalTextPlural(text: string) {
|
||||
const slug = pascalCase(text)
|
||||
|
||||
if (slug.endsWith('s')) {
|
||||
return slug
|
||||
}
|
||||
|
||||
return `${slug}s`
|
||||
}
|
||||
|
||||
// =========================== //
|
||||
// ========= VENDOR ========== //
|
||||
// =========================== //
|
||||
|
||||
// https://github.com/blakeembrey/change-case/blob/main/packages/change-case/src/index.ts
|
||||
|
||||
// Regexps involved with splitting words in various case formats.
|
||||
const SPLIT_LOWER_UPPER_RE = /([\p{Ll}\d])(\p{Lu})/gu
|
||||
const SPLIT_UPPER_UPPER_RE = /(\p{Lu})([\p{Lu}][\p{Ll}])/gu
|
||||
|
||||
// Used to iterate over the initial split result and separate numbers.
|
||||
const SPLIT_SEPARATE_NUMBER_RE = /(\d)\p{Ll}|(\p{L})\d/u
|
||||
|
||||
// Regexp involved with stripping non-word characters from the result.
|
||||
const DEFAULT_STRIP_REGEXP = /[^\p{L}\d]+/giu
|
||||
|
||||
// The replacement value for splits.
|
||||
const SPLIT_REPLACE_VALUE = '$1\0$2'
|
||||
|
||||
// The default characters to keep after transforming case.
|
||||
const DEFAULT_PREFIX_SUFFIX_CHARACTERS = ''
|
||||
|
||||
/**
|
||||
* Supported locale values. Use `false` to ignore locale.
|
||||
* Defaults to `undefined`, which uses the host environment.
|
||||
*/
|
||||
export type Locale = string[] | string | false | undefined
|
||||
|
||||
/**
|
||||
* Options used for converting strings to pascal/camel case.
|
||||
*/
|
||||
export interface PascalCaseOptions extends Options {
|
||||
mergeAmbiguousCharacters?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Options used for converting strings to any case.
|
||||
*/
|
||||
export interface Options {
|
||||
locale?: Locale
|
||||
split?: (value: string) => string[]
|
||||
/** @deprecated Pass `split: splitSeparateNumbers` instead. */
|
||||
separateNumbers?: boolean
|
||||
delimiter?: string
|
||||
prefixCharacters?: string
|
||||
suffixCharacters?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Split any cased input strings into an array of words.
|
||||
*/
|
||||
export function split(value: string) {
|
||||
let result = value.trim()
|
||||
|
||||
result = result
|
||||
.replace(SPLIT_LOWER_UPPER_RE, SPLIT_REPLACE_VALUE)
|
||||
.replace(SPLIT_UPPER_UPPER_RE, SPLIT_REPLACE_VALUE)
|
||||
|
||||
result = result.replace(DEFAULT_STRIP_REGEXP, '\0')
|
||||
|
||||
let start = 0
|
||||
let end = result.length
|
||||
|
||||
// Trim the delimiter from around the output string.
|
||||
while (result.charAt(start) === '\0') start++
|
||||
if (start === end) return []
|
||||
while (result.charAt(end - 1) === '\0') end--
|
||||
|
||||
// oxlint-disable-next-line no-control-regex -- intentional null character split
|
||||
return result.slice(start, end).split(/\0/g)
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the input string into an array of words, separating numbers.
|
||||
*/
|
||||
export function splitSeparateNumbers(value: string) {
|
||||
const words = split(value)
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
const word = words[i]
|
||||
const match = SPLIT_SEPARATE_NUMBER_RE.exec(word)
|
||||
if (match) {
|
||||
const offset = match.index + (match[1] ?? match[2]).length
|
||||
words.splice(i, 1, word.slice(0, offset), word.slice(offset))
|
||||
}
|
||||
}
|
||||
return words
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to space separated lower case (`foo bar`).
|
||||
*/
|
||||
export function noCase(input: string, options?: Options) {
|
||||
const [prefix, words, suffix] = splitPrefixSuffix(input, options)
|
||||
return prefix + words.map(lowerFactory(options?.locale)).join(options?.delimiter ?? ' ') + suffix
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to camel case (`fooBar`).
|
||||
*/
|
||||
export function camelCase(input: string, options?: PascalCaseOptions) {
|
||||
const [prefix, words, suffix] = splitPrefixSuffix(input, options)
|
||||
const lower = lowerFactory(options?.locale)
|
||||
const upper = upperFactory(options?.locale)
|
||||
const transform = options?.mergeAmbiguousCharacters
|
||||
? capitalCaseTransformFactory(lower, upper)
|
||||
: pascalCaseTransformFactory(lower, upper)
|
||||
return (
|
||||
prefix +
|
||||
words
|
||||
.map((word, index) => {
|
||||
if (index === 0) return lower(word)
|
||||
return transform(word, index)
|
||||
})
|
||||
.join(options?.delimiter ?? '') +
|
||||
suffix
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to pascal case (`FooBar`).
|
||||
*/
|
||||
export function pascalCase(input: string, options?: PascalCaseOptions) {
|
||||
const [prefix, words, suffix] = splitPrefixSuffix(input, options)
|
||||
const lower = lowerFactory(options?.locale)
|
||||
const upper = upperFactory(options?.locale)
|
||||
const transform = options?.mergeAmbiguousCharacters
|
||||
? capitalCaseTransformFactory(lower, upper)
|
||||
: pascalCaseTransformFactory(lower, upper)
|
||||
return prefix + words.map(transform).join(options?.delimiter ?? '') + suffix
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to pascal snake case (`Foo_Bar`).
|
||||
*/
|
||||
export function pascalSnakeCase(input: string, options?: Options) {
|
||||
return capitalCase(input, { delimiter: '_', ...options })
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to capital case (`Foo Bar`).
|
||||
*/
|
||||
export function capitalCase(input: string, options?: Options) {
|
||||
const [prefix, words, suffix] = splitPrefixSuffix(input, options)
|
||||
const lower = lowerFactory(options?.locale)
|
||||
const upper = upperFactory(options?.locale)
|
||||
return (
|
||||
prefix +
|
||||
words.map(capitalCaseTransformFactory(lower, upper)).join(options?.delimiter ?? ' ') +
|
||||
suffix
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to constant case (`FOO_BAR`).
|
||||
*/
|
||||
export function constantCase(input: string, options?: Options) {
|
||||
const [prefix, words, suffix] = splitPrefixSuffix(input, options)
|
||||
return prefix + words.map(upperFactory(options?.locale)).join(options?.delimiter ?? '_') + suffix
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to dot case (`foo.bar`).
|
||||
*/
|
||||
export function dotCase(input: string, options?: Options) {
|
||||
return noCase(input, { delimiter: '.', ...options })
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to kebab case (`foo-bar`).
|
||||
*/
|
||||
export function kebabCase(input: string, options?: Options) {
|
||||
return noCase(input, { delimiter: '-', ...options })
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to path case (`foo/bar`).
|
||||
*/
|
||||
export function pathCase(input: string, options?: Options) {
|
||||
return noCase(input, { delimiter: '/', ...options })
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to path case (`Foo bar`).
|
||||
*/
|
||||
export function sentenceCase(input: string, options?: Options) {
|
||||
const [prefix, words, suffix] = splitPrefixSuffix(input, options)
|
||||
const lower = lowerFactory(options?.locale)
|
||||
const upper = upperFactory(options?.locale)
|
||||
const transform = capitalCaseTransformFactory(lower, upper)
|
||||
return (
|
||||
prefix +
|
||||
words
|
||||
.map((word, index) => {
|
||||
if (index === 0) return transform(word)
|
||||
return lower(word)
|
||||
})
|
||||
.join(options?.delimiter ?? ' ') +
|
||||
suffix
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to snake case (`foo_bar`).
|
||||
*/
|
||||
export function snakeCase(input: string, options?: Options) {
|
||||
return noCase(input, { delimiter: '_', ...options })
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to header case (`Foo-Bar`).
|
||||
*/
|
||||
export function trainCase(input: string, options?: Options) {
|
||||
return capitalCase(input, { delimiter: '-', ...options })
|
||||
}
|
||||
|
||||
function lowerFactory(locale: Locale): (input: string) => string {
|
||||
return locale === false
|
||||
? (input: string) => input.toLowerCase()
|
||||
: (input: string) => input.toLocaleLowerCase(locale)
|
||||
}
|
||||
|
||||
function upperFactory(locale: Locale): (input: string) => string {
|
||||
return locale === false
|
||||
? (input: string) => input.toUpperCase()
|
||||
: (input: string) => input.toLocaleUpperCase(locale)
|
||||
}
|
||||
|
||||
function capitalCaseTransformFactory(
|
||||
lower: (input: string) => string,
|
||||
upper: (input: string) => string
|
||||
) {
|
||||
return (word: string) => `${upper(word[0])}${lower(word.slice(1))}`
|
||||
}
|
||||
|
||||
function pascalCaseTransformFactory(
|
||||
lower: (input: string) => string,
|
||||
upper: (input: string) => string
|
||||
) {
|
||||
return (word: string, index: number) => {
|
||||
const char0 = word[0]
|
||||
const initial = index > 0 && char0 >= '0' && char0 <= '9' ? '_' + char0 : upper(char0)
|
||||
return initial + lower(word.slice(1))
|
||||
}
|
||||
}
|
||||
|
||||
function splitPrefixSuffix(input: string, options: Options = {}): [string, string[], string] {
|
||||
const splitFn = options.split ?? (options.separateNumbers ? splitSeparateNumbers : split)
|
||||
const prefixCharacters = options.prefixCharacters ?? DEFAULT_PREFIX_SUFFIX_CHARACTERS
|
||||
const suffixCharacters = options.suffixCharacters ?? DEFAULT_PREFIX_SUFFIX_CHARACTERS
|
||||
let prefixIndex = 0
|
||||
let suffixIndex = input.length
|
||||
|
||||
while (prefixIndex < input.length) {
|
||||
const char = input.charAt(prefixIndex)
|
||||
if (!prefixCharacters.includes(char)) break
|
||||
prefixIndex++
|
||||
}
|
||||
|
||||
while (suffixIndex > prefixIndex) {
|
||||
const index = suffixIndex - 1
|
||||
const char = input.charAt(index)
|
||||
if (!suffixCharacters.includes(char)) break
|
||||
suffixIndex = index
|
||||
}
|
||||
|
||||
return [
|
||||
input.slice(0, prefixIndex),
|
||||
splitFn(input.slice(prefixIndex, suffixIndex)),
|
||||
input.slice(suffixIndex),
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user