summaryrefslogtreecommitdiff
path: root/node_modules/@sindresorhus/slugify/index.js
diff options
context:
space:
mode:
authorShipwreckt <me@shipwreckt.co.uk>2025-10-31 20:02:14 +0000
committerShipwreckt <me@shipwreckt.co.uk>2025-10-31 20:02:14 +0000
commit7a52ddeba2a68388b544f529d2d92104420f77b0 (patch)
tree15ddd47457a2cb4a96060747437d36474e4f6b4e /node_modules/@sindresorhus/slugify/index.js
parent53d6ae2b5568437afa5e4995580a3fb679b7b91b (diff)
Changed from static to 11ty!
Diffstat (limited to 'node_modules/@sindresorhus/slugify/index.js')
-rw-r--r--node_modules/@sindresorhus/slugify/index.js127
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;
+}