frontend-compiler-example/node_modules/slug/slug.js
2025-04-11 16:02:16 -04:00

887 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* global btoa */
let base64
// This function's sole purpose is to help us ignore lone surrogates so that
// malformed strings don't throw in the browser while being processed
// permissively in Node.js. If we didn't care about parity, we could get rid
// of it.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt
function getWholeCharAndI (str, i) {
const code = str.charCodeAt(i)
// This is a coherence check. `code` should never be `NaN`.
/* c8 ignore next 3 */
if (isNaN(code)) {
throw new RangeError('Index ' + i + ' out of range for string "' + str + '"; please open an issue at https://github.com/Trott/slug/issues/new')
}
if (code < 0xD800 || code > 0xDFFF) {
return [str.charAt(i), i] // Non-surrogate character, keeping 'i' the same
}
// High surrogate
if (code >= 0xD800 && code <= 0xDBFF) {
if (str.length <= (i + 1)) {
// High surrogate without following low surrogate
return [' ', i]
}
const next = str.charCodeAt(i + 1)
if (next < 0xDC00 || next > 0xDFFF) {
// High surrogate without following low surrogate
return [' ', i]
}
return [str.charAt(i) + str.charAt(i + 1), i + 1]
}
// Low surrogate (0xDC00 <= code && code <= 0xDFFF)
if (i === 0) {
// Low surrogate without preceding high surrogate
return [' ', i]
}
const prev = str.charCodeAt(i - 1)
/* c8 ignore next */
if (prev < 0xD800 || prev > 0xDBFF) {
// Low surrogate without preceding high surrogate
return [' ', i]
}
/* c8 ignore next */
throw new Error('String "' + str + '" reaches code believed to be unreachable; please open an issue at https://github.com/Trott/slug/issues/new')
}
if (typeof window !== 'undefined') {
// Browser environment. We don't yet merge coverage with CLI tests.
/* c8 ignore next 4 */
if (window.btoa) {
base64 = function (input) {
return btoa(unescape(encodeURIComponent(input)))
}
} else {
// Polyfill for environments that don't have btoa or Buffer class (notably, React Native).
// Based on https://github.com/davidchambers/Base64.js/blob/a121f75bb10c8dd5d557886c4b1069b31258d230/base64.js
base64 = function (input) {
const str = unescape(encodeURIComponent(input + ''))
let output = ''
for (
let block, charCode, idx = 0, map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
str.charAt(idx | 0) || (map = '=', idx % 1);
output += map.charAt(63 & block >> 8 - idx % 1 * 8)
) {
charCode = str.charCodeAt(idx += 3 / 4)
// This is a coherence check. The result of unescape(encodeURIComponent()) should always be
// characters with code points that fit into two bytes.
/* c8 ignore next 3 */
if (charCode > 0xFF) {
throw new Error("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.")
}
block = block << 8 | charCode
}
return output
}
}
} else {
base64 = function (input) {
return Buffer.from(input).toString('base64')
}
}
function slug (string, opts) {
let result = slugify(string, opts)
const fallback = opts && opts.fallback !== undefined ? opts.fallback : slug.defaults.fallback
// If output is an empty string, try slug for base64 of string.
if (fallback === true && result === '') {
// Get rid of lone surrogates.
let input = ''
for (let i = 0; i < string.length; i++) {
const charAndI = getWholeCharAndI(string, i)
i = charAndI[1]
input += charAndI[0]
}
result = slugify(base64(input), opts)
}
return result
}
const locales = {
// http://www.eki.ee/wgrs/rom1_bg.pdf
bg: { Й: 'Y', й: 'y', X: 'H', x: 'h', Ц: 'Ts', ц: 'ts', Щ: 'Sht', щ: 'sht', Ъ: 'A', ъ: 'a', Ь: 'Y', ь: 'y' },
// Need a reference URL for German, although this is pretty well-known.
de: { Ä: 'AE', ä: 'ae', Ö: 'OE', ö: 'oe', Ü: 'UE', ü: 'ue' },
// Need a reference URL for Serbian.
sr: { đ: 'dj', Đ: 'DJ' },
// https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/864314/ROMANIZATION_OF_UKRAINIAN.pdf
uk: { И: 'Y', и: 'y', Й: 'Y', й: 'y', Ц: 'Ts', ц: 'ts', Х: 'Kh', х: 'kh', Щ: 'Shch', щ: 'shch', Г: 'H', г: 'h' }
}
let defaultLocale = {}
function slugify (string, opts) {
if (typeof string !== 'string') {
throw new Error('slug() requires a string argument, received ' + typeof string)
}
if (typeof opts === 'string') { opts = { replacement: opts } }
opts = opts ? Object.assign({}, opts) : {}
opts.mode = opts.mode || slug.defaults.mode
const defaults = slug.defaults.modes[opts.mode]
const keys = ['replacement', 'multicharmap', 'charmap', 'remove', 'lower', 'trim']
for (let key, i = 0, l = keys.length; i < l; i++) {
key = keys[i]
opts[key] = (key in opts) ? opts[key] : defaults[key]
}
const localeMap = locales[opts.locale] || defaultLocale
let lengths = []
for (const key in opts.multicharmap) {
if (!Object.prototype.hasOwnProperty.call(opts.multicharmap, key)) { continue }
const len = key.length
if (lengths.indexOf(len) === -1) { lengths.push(len) }
}
// We want to match the longest string if there are multiple matches, so
// sort lengths in descending order.
lengths = lengths.sort(function (a, b) { return b - a })
const disallowedChars = opts.mode === 'rfc3986' ? /[^\w\s\-.~]/ : /[^A-Za-z0-9\s]/
let result = ''
for (let char, i = 0, l = string.length; i < l; i++) {
char = string[i]
let matchedMultichar = false
for (let j = 0; j < lengths.length; j++) {
const len = lengths[j]
const str = string.substr(i, len)
if (opts.multicharmap[str]) {
i += len - 1
char = opts.multicharmap[str]
matchedMultichar = true
break
}
}
if (!matchedMultichar) {
if (localeMap[char]) {
char = localeMap[char]
} else if (opts.charmap[char]) {
char = opts.charmap[char].replace(opts.replacement, ' ')
} else if (char.includes(opts.replacement)) {
// preserve the replacement character in case it is excluded by disallowedChars
char = char.replace(opts.replacement, ' ')
} else {
char = char.replace(disallowedChars, '')
}
}
result += char
}
if (opts.remove) {
result = result.replace(opts.remove, '')
}
if (opts.trim) {
result = result.trim()
}
result = result.replace(/\s+/g, opts.replacement) // convert spaces
if (opts.lower) {
result = result.toLowerCase()
}
return result
}
const initialMulticharmap = {
// multibyte devanagari characters (hindi, sanskrit, etc.)
: 'Fi',
: 'Ghi',
: 'Khi',
: 'Qi',
: 'ugDha',
: 'ugDhha',
: 'Yi',
: 'Za',
// hebrew
// Refs: http://www.eki.ee/wgrs/rom1_he.pdf
// Refs: https://en.wikipedia.org/wiki/Niqqud
בִי: 'i',
בֵ: 'e',
בֵי: 'e',
בֶ: 'e',
בַ: 'a',
בָ: 'a',
בֹ: 'o',
וֹ: 'o',
בֻ: 'u',
וּ: 'u',
בּ: 'b',
כּ: 'k',
ךּ: 'k',
פּ: 'p',
שׁ: 'sh',
שׂ: 's',
בְ: 'e',
חֱ: 'e',
חֲ: 'a',
חֳ: 'o',
בִ: 'i'
}
// https://github.com/django/django/blob/master/django/contrib/admin/static/admin/js/urlify.js
const initialCharmap = {
// latin
À: 'A',
Á: 'A',
Â: 'A',
Ã: 'A',
Ä: 'A',
Å: 'A',
Æ: 'AE',
Ç: 'C',
È: 'E',
É: 'E',
Ê: 'E',
Ë: 'E',
Ì: 'I',
Í: 'I',
Î: 'I',
Ï: 'I',
Ð: 'D',
Ñ: 'N',
Ò: 'O',
Ó: 'O',
Ô: 'O',
Õ: 'O',
Ö: 'O',
Ő: 'O',
Ø: 'O',
Ō: 'O',
Ù: 'U',
Ú: 'U',
Û: 'U',
Ü: 'U',
Ű: 'U',
Ý: 'Y',
Þ: 'TH',
ß: 'ss',
à: 'a',
á: 'a',
â: 'a',
ã: 'a',
ä: 'a',
å: 'a',
æ: 'ae',
ç: 'c',
è: 'e',
é: 'e',
ê: 'e',
ë: 'e',
ì: 'i',
í: 'i',
î: 'i',
ï: 'i',
ð: 'd',
ñ: 'n',
ò: 'o',
ó: 'o',
ô: 'o',
õ: 'o',
ö: 'o',
ő: 'o',
ø: 'o',
ō: 'o',
Œ: 'OE',
œ: 'oe',
ù: 'u',
ú: 'u',
û: 'u',
ü: 'u',
ű: 'u',
ý: 'y',
þ: 'th',
ÿ: 'y',
: 'SS',
// greek
α: 'a',
β: 'b',
γ: 'g',
δ: 'd',
ε: 'e',
ζ: 'z',
η: 'h',
θ: 'th',
ι: 'i',
κ: 'k',
λ: 'l',
μ: 'm',
ν: 'n',
ξ: '3',
ο: 'o',
π: 'p',
ρ: 'r',
σ: 's',
τ: 't',
υ: 'y',
φ: 'f',
χ: 'x',
ψ: 'ps',
ω: 'w',
ά: 'a',
έ: 'e',
ί: 'i',
ό: 'o',
ύ: 'y',
ή: 'h',
ώ: 'w',
ς: 's',
ϊ: 'i',
ΰ: 'y',
ϋ: 'y',
ΐ: 'i',
Α: 'A',
Β: 'B',
Γ: 'G',
Δ: 'D',
Ε: 'E',
Ζ: 'Z',
Η: 'H',
Θ: 'Th',
Ι: 'I',
Κ: 'K',
Λ: 'L',
Μ: 'M',
Ν: 'N',
Ξ: '3',
Ο: 'O',
Π: 'P',
Ρ: 'R',
Σ: 'S',
Τ: 'T',
Υ: 'Y',
Φ: 'F',
Χ: 'X',
Ψ: 'PS',
Ω: 'W',
Ά: 'A',
Έ: 'E',
Ί: 'I',
Ό: 'O',
Ύ: 'Y',
Ή: 'H',
Ώ: 'W',
Ϊ: 'I',
Ϋ: 'Y',
// turkish
ş: 's',
Ş: 'S',
ı: 'i',
İ: 'I',
ğ: 'g',
Ğ: 'G',
// russian
а: 'a',
б: 'b',
в: 'v',
г: 'g',
д: 'd',
е: 'e',
ё: 'yo',
ж: 'zh',
з: 'z',
и: 'i',
й: 'j',
к: 'k',
л: 'l',
м: 'm',
н: 'n',
о: 'o',
п: 'p',
р: 'r',
с: 's',
т: 't',
у: 'u',
ф: 'f',
х: 'h',
ц: 'c',
ч: 'ch',
ш: 'sh',
щ: 'sh',
ъ: 'u',
ы: 'y',
ь: '',
э: 'e',
ю: 'yu',
я: 'ya',
А: 'A',
Б: 'B',
В: 'V',
Г: 'G',
Д: 'D',
Е: 'E',
Ё: 'Yo',
Ж: 'Zh',
З: 'Z',
И: 'I',
Й: 'J',
К: 'K',
Л: 'L',
М: 'M',
Н: 'N',
О: 'O',
П: 'P',
Р: 'R',
С: 'S',
Т: 'T',
У: 'U',
Ф: 'F',
Х: 'H',
Ц: 'C',
Ч: 'Ch',
Ш: 'Sh',
Щ: 'Sh',
Ъ: 'U',
Ы: 'Y',
Ь: '',
Э: 'E',
Ю: 'Yu',
Я: 'Ya',
// ukranian
Є: 'Ye',
І: 'I',
Ї: 'Yi',
Ґ: 'G',
є: 'ye',
і: 'i',
ї: 'yi',
ґ: 'g',
// czech
č: 'c',
ď: 'd',
ě: 'e',
ň: 'n',
ř: 'r',
š: 's',
ť: 't',
ů: 'u',
ž: 'z',
Č: 'C',
Ď: 'D',
Ě: 'E',
Ň: 'N',
Ř: 'R',
Š: 'S',
Ť: 'T',
Ů: 'U',
Ž: 'Z',
// slovak
ľ: 'l',
ĺ: 'l',
ŕ: 'r',
Ľ: 'L',
Ĺ: 'L',
Ŕ: 'R',
// polish
ą: 'a',
ć: 'c',
ę: 'e',
ł: 'l',
ń: 'n',
ś: 's',
ź: 'z',
ż: 'z',
Ą: 'A',
Ć: 'C',
Ę: 'E',
Ł: 'L',
Ń: 'N',
Ś: 'S',
Ź: 'Z',
Ż: 'Z',
// latvian
ā: 'a',
ē: 'e',
ģ: 'g',
ī: 'i',
ķ: 'k',
ļ: 'l',
ņ: 'n',
ū: 'u',
Ā: 'A',
Ē: 'E',
Ģ: 'G',
Ī: 'I',
Ķ: 'K',
Ļ: 'L',
Ņ: 'N',
Ū: 'U',
// arabic
أ: 'a',
إ: 'i',
ب: 'b',
ت: 't',
ث: 'th',
ج: 'g',
ح: 'h',
خ: 'kh',
د: 'd',
ذ: 'th',
ر: 'r',
ز: 'z',
س: 's',
ش: 'sh',
ص: 's',
ض: 'd',
ط: 't',
ظ: 'th',
ع: 'aa',
غ: 'gh',
ف: 'f',
ق: 'k',
ك: 'k',
ل: 'l',
م: 'm',
ن: 'n',
ه: 'h',
و: 'o',
ي: 'y',
ء: 'aa',
ة: 'a',
// farsi
آ: 'a',
ا: 'a',
پ: 'p',
ژ: 'zh',
گ: 'g',
چ: 'ch',
ک: 'k',
ی: 'i',
// lithuanian
ė: 'e',
į: 'i',
ų: 'u',
Ė: 'E',
Į: 'I',
Ų: 'U',
// romanian
ț: 't',
Ț: 'T',
ţ: 't',
Ţ: 'T',
ș: 's',
Ș: 'S',
ă: 'a',
Ă: 'A',
// vietnamese
: 'A',
: 'A',
: 'A',
: 'A',
: 'A',
: 'A',
: 'A',
: 'A',
: 'A',
: 'A',
: 'A',
: 'A',
: 'E',
: 'E',
: 'E',
: 'E',
: 'E',
: 'E',
: 'E',
: 'E',
: 'I',
: 'I',
Ĩ: 'I',
: 'O',
: 'O',
: 'O',
: 'O',
: 'O',
: 'O',
: 'O',
Ơ: 'O',
: 'O',
: 'O',
: 'O',
: 'O',
: 'O',
: 'U',
: 'U',
Ũ: 'U',
Ư: 'U',
: 'U',
: 'U',
: 'U',
: 'U',
: 'U',
: 'Y',
: 'Y',
: 'Y',
: 'Y',
Đ: 'D',
: 'a',
: 'a',
: 'a',
: 'a',
: 'a',
: 'a',
: 'a',
: 'a',
: 'a',
: 'a',
: 'a',
: 'a',
: 'e',
: 'e',
: 'e',
: 'e',
ế: 'e',
: 'e',
: 'e',
: 'e',
: 'i',
: 'i',
ĩ: 'i',
: 'o',
: 'o',
: 'o',
: 'o',
: 'o',
: 'o',
: 'o',
ơ: 'o',
: 'o',
: 'o',
: 'o',
: 'o',
: 'o',
: 'u',
: 'u',
ũ: 'u',
ư: 'u',
: 'u',
: 'u',
: 'u',
: 'u',
: 'u',
: 'y',
: 'y',
: 'y',
: 'y',
đ: 'd',
// kazakh
Ә: 'AE',
ә: 'ae',
Ғ: 'GH',
ғ: 'gh',
Қ: 'KH',
қ: 'kh',
Ң: 'NG',
ң: 'ng',
Ү: 'UE',
ү: 'ue',
Ұ: 'U',
ұ: 'u',
Һ: 'H',
һ: 'h',
Ө: 'OE',
ө: 'oe',
// serbian
ђ: 'dj',
ј: 'j',
љ: 'lj',
њ: 'nj',
ћ: 'c',
џ: 'dz',
Ђ: 'Dj',
Ј: 'j',
Љ: 'Lj',
Њ: 'Nj',
Ћ: 'C',
Џ: 'Dz',
nj: 'nj',
lj: 'lj',
Nj: 'NJ',
Lj: 'LJ',
// hindi
: 'a',
: 'aa',
: 'e',
: 'ii',
: 'ei',
: 'ae',
: 'ai',
: 'i',
: 'o',
: 'oi',
: 'oii',
: 'uu',
: 'ou',
: 'u',
: 'B',
: 'Bha',
: 'Ca',
: 'Chha',
: 'Da',
: 'Dha',
: 'Fa',
: 'Ga',
: 'Gha',
: 'Ghi',
: 'Ha',
: 'Ja',
: 'Jha',
: 'Ka',
: 'Kha',
: 'Khi',
: 'L',
: 'Li',
: 'Li',
: 'Lii',
: 'Lii',
: 'Ma',
: 'Na',
: 'Na',
: 'Nia',
: 'Nae',
: 'Ni',
: 'oms',
: 'Pa',
: 'Qi',
: 'Ra',
: 'Ri',
: 'Ri',
: 'Ri',
: 'Sa',
: 'Sha',
: 'Shha',
: 'Ta',
: 'Ta',
: 'Tha',
: 'Tha',
: 'Tha',
: 'Thha',
: 'ugDha',
: 'ugDhha',
: 'Va',
: 'Ya',
: 'Yi',
: 'Za',
// azerbaijani
ə: 'e',
Ə: 'E',
// georgian
: 'a',
: 'b',
: 'g',
: 'd',
: 'e',
: 'v',
: 'z',
: 't',
: 'i',
: 'k',
: 'l',
: 'm',
: 'n',
: 'o',
: 'p',
: 'zh',
: 'r',
: 's',
: 't',
: 'u',
: 'p',
: 'k',
: 'gh',
: 'q',
: 'sh',
: 'ch',
: 'ts',
: 'dz',
: 'ts',
: 'ch',
: 'kh',
: 'j',
: 'h',
// hebrew
ב: 'v',
גּ: 'g',
ג: 'g',
ד: 'd',
דּ: 'd',
ה: 'h',
ו: 'v',
ז: 'z',
ח: 'h',
ט: 't',
י: 'y',
כ: 'kh',
ך: 'kh',
ל: 'l',
מ: 'm',
ם: 'm',
נ: 'n',
ן: 'n',
ס: 's',
פ: 'f',
ף: 'f',
ץ: 'ts',
צ: 'ts',
ק: 'k',
ר: 'r',
תּ: 't',
ת: 't'
}
slug.charmap = Object.assign({}, initialCharmap)
slug.multicharmap = Object.assign({}, initialMulticharmap)
slug.defaults = {
charmap: slug.charmap,
mode: 'pretty',
modes: {
rfc3986: {
replacement: '-',
remove: null,
lower: true,
charmap: slug.charmap,
multicharmap: slug.multicharmap,
trim: true
},
pretty: {
replacement: '-',
remove: null,
lower: true,
charmap: slug.charmap,
multicharmap: slug.multicharmap,
trim: true
}
},
multicharmap: slug.multicharmap,
fallback: true
}
slug.reset = function () {
slug.defaults.modes.rfc3986.charmap = slug.defaults.modes.pretty.charmap = slug.charmap = slug.defaults.charmap = Object.assign({}, initialCharmap)
slug.defaults.modes.rfc3986.multicharmap = slug.defaults.modes.pretty.multicharmap = slug.multicharmap = slug.defaults.multicharmap = Object.assign({}, initialMulticharmap)
defaultLocale = ''
}
slug.extend = function (customMap) {
const keys = Object.keys(customMap)
const multi = {}
const single = {}
for (let i = 0; i < keys.length; i++) {
if (keys[i].length > 1) {
multi[keys[i]] = customMap[keys[i]]
} else {
single[keys[i]] = customMap[keys[i]]
}
}
Object.assign(slug.charmap, single)
Object.assign(slug.multicharmap, multi)
}
slug.setLocale = function (locale) {
defaultLocale = locales[locale] || {}
}
export default slug