diff options
| author | Shipwreckt <me@shipwreckt.co.uk> | 2025-10-31 20:02:14 +0000 |
|---|---|---|
| committer | Shipwreckt <me@shipwreckt.co.uk> | 2025-10-31 20:02:14 +0000 |
| commit | 7a52ddeba2a68388b544f529d2d92104420f77b0 (patch) | |
| tree | 15ddd47457a2cb4a96060747437d36474e4f6b4e /node_modules/@sindresorhus/slugify/index.js | |
| parent | 53d6ae2b5568437afa5e4995580a3fb679b7b91b (diff) | |
Changed from static to 11ty!
Diffstat (limited to 'node_modules/@sindresorhus/slugify/index.js')
| -rw-r--r-- | node_modules/@sindresorhus/slugify/index.js | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/node_modules/@sindresorhus/slugify/index.js b/node_modules/@sindresorhus/slugify/index.js new file mode 100644 index 0000000..836efaa --- /dev/null +++ b/node_modules/@sindresorhus/slugify/index.js @@ -0,0 +1,127 @@ +import escapeStringRegexp from 'escape-string-regexp'; +import transliterate from '@sindresorhus/transliterate'; +import builtinOverridableReplacements from './overridable-replacements.js'; + +const decamelize = string => { + return string + // Separate capitalized words. + .replace(/([A-Z]{2,})(\d+)/g, '$1 $2') + .replace(/([a-z\d]+)([A-Z]{2,})/g, '$1 $2') + + .replace(/([a-z\d])([A-Z])/g, '$1 $2') + // `[a-rt-z]` matches all lowercase characters except `s`. + // This avoids matching plural acronyms like `APIs`. + .replace(/([A-Z]+)([A-Z][a-rt-z\d]+)/g, '$1 $2'); +}; + +const removeMootSeparators = (string, separator) => { + const escapedSeparator = escapeStringRegexp(separator); + + return string + .replace(new RegExp(`${escapedSeparator}{2,}`, 'g'), separator) + .replace(new RegExp(`^${escapedSeparator}|${escapedSeparator}$`, 'g'), ''); +}; + +const buildPatternSlug = options => { + let negationSetPattern = 'a-z\\d'; + negationSetPattern += options.lowercase ? '' : 'A-Z'; + + if (options.preserveCharacters.length > 0) { + for (const character of options.preserveCharacters) { + if (character === options.separator) { + throw new Error(`The separator character \`${options.separator}\` cannot be included in preserved characters: ${options.preserveCharacters}`); + } + + negationSetPattern += escapeStringRegexp(character); + } + } + + return new RegExp(`[^${negationSetPattern}]+`, 'g'); +}; + +export default function slugify(string, options) { + if (typeof string !== 'string') { + throw new TypeError(`Expected a string, got \`${typeof string}\``); + } + + options = { + separator: '-', + lowercase: true, + decamelize: true, + customReplacements: [], + preserveLeadingUnderscore: false, + preserveTrailingDash: false, + preserveCharacters: [], + ...options + }; + + const shouldPrependUnderscore = options.preserveLeadingUnderscore && string.startsWith('_'); + const shouldAppendDash = options.preserveTrailingDash && string.endsWith('-'); + + const customReplacements = new Map([ + ...builtinOverridableReplacements, + ...options.customReplacements + ]); + + string = transliterate(string, {customReplacements}); + + if (options.decamelize) { + string = decamelize(string); + } + + const patternSlug = buildPatternSlug(options); + + if (options.lowercase) { + string = string.toLowerCase(); + } + + // Detect contractions/possessives by looking for any word followed by a `'t` + // or `'s` in isolation and then remove it. + string = string.replace(/([a-zA-Z\d]+)'([ts])(\s|$)/g, '$1$2$3'); + + string = string.replace(patternSlug, options.separator); + string = string.replace(/\\/g, ''); + + if (options.separator) { + string = removeMootSeparators(string, options.separator); + } + + if (shouldPrependUnderscore) { + string = `_${string}`; + } + + if (shouldAppendDash) { + string = `${string}-`; + } + + return string; +} + +export function slugifyWithCounter() { + const occurrences = new Map(); + + const countable = (string, options) => { + string = slugify(string, options); + + if (!string) { + return ''; + } + + const stringLower = string.toLowerCase(); + const numberless = occurrences.get(stringLower.replace(/(?:-\d+?)+?$/, '')) || 0; + const counter = occurrences.get(stringLower); + occurrences.set(stringLower, typeof counter === 'number' ? counter + 1 : 1); + const newCounter = occurrences.get(stringLower) || 2; + if (newCounter >= 2 || numberless > 2) { + string = `${string}-${newCounter}`; + } + + return string; + }; + + countable.reset = () => { + occurrences.clear(); + }; + + return countable; +} |
