@sopkit/slug 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -0
- package/dist/index.cjs +66 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +30 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/package.json +30 -0
- package/src/index.ts +78 -0
- package/tsconfig.json +14 -0
- package/tsup.config.ts +11 -0
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# `@sopkit/slug`
|
|
2
|
+
|
|
3
|
+
Premium, lightweight URL slug generator and text clean-up utility with full Unicode/diacritics support. Part of the SopKit utility ecosystem.
|
|
4
|
+
|
|
5
|
+
## Online Interactive Tool
|
|
6
|
+
You can use the browser-based interactive version of this tool at [SopKit Slug Generator](https://sopkit.github.io/slug-generator/).
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
- Unicode normalization (decomposes accents and diacritics like `é` ➜ `e`)
|
|
10
|
+
- Custom separators (e.g. `-`, `_`, or custom characters)
|
|
11
|
+
- Case control (force lowercase/preserve case)
|
|
12
|
+
- Strict clean-up filters out special URL-breaking symbols
|
|
13
|
+
- Zero dependencies
|
|
14
|
+
- ESM and CommonJS support
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
```bash
|
|
18
|
+
npm install @sopkit/slug
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### ESM
|
|
24
|
+
```typescript
|
|
25
|
+
import { slugify, isValid } from "@sopkit/slug";
|
|
26
|
+
|
|
27
|
+
// Standard slugification
|
|
28
|
+
slugify("Hello World & Universe!"); // "hello-world-universe"
|
|
29
|
+
|
|
30
|
+
// Accent normalization
|
|
31
|
+
slugify("Café & résumé"); // "cafe-resume"
|
|
32
|
+
|
|
33
|
+
// Custom options
|
|
34
|
+
slugify("User Profile ID", { separator: "_", lowercase: false }); // "User_Profile_ID"
|
|
35
|
+
|
|
36
|
+
// Validation
|
|
37
|
+
isValid("hello-world"); // true
|
|
38
|
+
isValid("hello world!"); // false
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### CommonJS
|
|
42
|
+
```javascript
|
|
43
|
+
const { slugify } = require("@sopkit/slug");
|
|
44
|
+
|
|
45
|
+
const slug = slugify("SopKit Slugify"); // "sopkit-slugify"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
MIT © [SopKit](https://sopkit.github.io/)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
isValid: () => isValid,
|
|
24
|
+
slugify: () => slugify
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
function slugify(text, options = {}) {
|
|
28
|
+
if (typeof text !== "string") {
|
|
29
|
+
throw new TypeError("Input must be a string");
|
|
30
|
+
}
|
|
31
|
+
const {
|
|
32
|
+
separator = "-",
|
|
33
|
+
lowercase = true,
|
|
34
|
+
strict = true
|
|
35
|
+
} = options;
|
|
36
|
+
let str = text;
|
|
37
|
+
str = str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
38
|
+
if (lowercase) {
|
|
39
|
+
str = str.toLowerCase();
|
|
40
|
+
}
|
|
41
|
+
if (strict) {
|
|
42
|
+
str = str.replace(/[^a-z0-9\s-_]/gi, "");
|
|
43
|
+
}
|
|
44
|
+
str = str.trim().replace(/\s+/g, separator).replace(new RegExp(`\\${separator}+`, "g"), separator);
|
|
45
|
+
if (str.startsWith(separator)) {
|
|
46
|
+
str = str.slice(separator.length);
|
|
47
|
+
}
|
|
48
|
+
if (str.endsWith(separator)) {
|
|
49
|
+
str = str.slice(0, -separator.length);
|
|
50
|
+
}
|
|
51
|
+
return str;
|
|
52
|
+
}
|
|
53
|
+
function isValid(slug, separator = "-") {
|
|
54
|
+
if (typeof slug !== "string" || slug.trim() === "") {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const escapedSeparator = separator.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
58
|
+
const regex = new RegExp(`^[a-z0-9]+(${escapedSeparator}[a-z0-9]+)*$`, "i");
|
|
59
|
+
return regex.test(slug);
|
|
60
|
+
}
|
|
61
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
62
|
+
0 && (module.exports = {
|
|
63
|
+
isValid,
|
|
64
|
+
slugify
|
|
65
|
+
});
|
|
66
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * SopKit Slug Utilities\n * Premium, zero-dependency multilingual URL slug generator.\n * Link: https://sopkit.github.io/slug-generator/\n */\n\nexport interface SlugOptions {\n /**\n * Character separator. Defaults to \"-\".\n */\n separator?: string;\n /**\n * Automatically convert slug to lowercase. Defaults to true.\n */\n lowercase?: boolean;\n /**\n * Strip non-alphanumeric characters. Defaults to true.\n */\n strict?: boolean;\n}\n\n/**\n * Generates a clean URL slug from the given text input.\n * Supports accent removal and basic character normalization.\n */\nexport function slugify(text: string, options: SlugOptions = {}): string {\n if (typeof text !== \"string\") {\n throw new TypeError(\"Input must be a string\");\n }\n\n const {\n separator = \"-\",\n lowercase = true,\n strict = true\n } = options;\n\n let str = text;\n\n // 1. Normalize unicode characters (remove diacritics/accents)\n str = str.normalize(\"NFD\").replace(/[\\u0300-\\u036f]/g, \"\");\n\n // 2. Convert to lowercase if flag set\n if (lowercase) {\n str = str.toLowerCase();\n }\n\n // 3. Apply separator and remove invalid characters\n if (strict) {\n str = str.replace(/[^a-z0-9\\s-_]/gi, \"\");\n }\n\n // 4. Replace whitespace and multiple separators\n str = str.trim()\n .replace(/\\s+/g, separator)\n .replace(new RegExp(`\\\\${separator}+`, \"g\"), separator);\n\n // 5. Clean up leading or trailing separators\n if (str.startsWith(separator)) {\n str = str.slice(separator.length);\n }\n if (str.endsWith(separator)) {\n str = str.slice(0, -separator.length);\n }\n\n return str;\n}\n\n/**\n * Validates if a string is a valid URL slug (alphanumeric and dashes/underscores).\n */\nexport function isValid(slug: string, separator: string = \"-\"): boolean {\n if (typeof slug !== \"string\" || slug.trim() === \"\") {\n return false;\n }\n const escapedSeparator = separator.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\");\n const regex = new RegExp(`^[a-z0-9]+(${escapedSeparator}[a-z0-9]+)*$`, \"i\");\n return regex.test(slug);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyBO,SAAS,QAAQ,MAAc,UAAuB,CAAC,GAAW;AACvE,MAAI,OAAO,SAAS,UAAU;AAC5B,UAAM,IAAI,UAAU,wBAAwB;AAAA,EAC9C;AAEA,QAAM;AAAA,IACJ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,EACX,IAAI;AAEJ,MAAI,MAAM;AAGV,QAAM,IAAI,UAAU,KAAK,EAAE,QAAQ,oBAAoB,EAAE;AAGzD,MAAI,WAAW;AACb,UAAM,IAAI,YAAY;AAAA,EACxB;AAGA,MAAI,QAAQ;AACV,UAAM,IAAI,QAAQ,mBAAmB,EAAE;AAAA,EACzC;AAGA,QAAM,IAAI,KAAK,EACZ,QAAQ,QAAQ,SAAS,EACzB,QAAQ,IAAI,OAAO,KAAK,SAAS,KAAK,GAAG,GAAG,SAAS;AAGxD,MAAI,IAAI,WAAW,SAAS,GAAG;AAC7B,UAAM,IAAI,MAAM,UAAU,MAAM;AAAA,EAClC;AACA,MAAI,IAAI,SAAS,SAAS,GAAG;AAC3B,UAAM,IAAI,MAAM,GAAG,CAAC,UAAU,MAAM;AAAA,EACtC;AAEA,SAAO;AACT;AAKO,SAAS,QAAQ,MAAc,YAAoB,KAAc;AACtE,MAAI,OAAO,SAAS,YAAY,KAAK,KAAK,MAAM,IAAI;AAClD,WAAO;AAAA,EACT;AACA,QAAM,mBAAmB,UAAU,QAAQ,0BAA0B,MAAM;AAC3E,QAAM,QAAQ,IAAI,OAAO,cAAc,gBAAgB,gBAAgB,GAAG;AAC1E,SAAO,MAAM,KAAK,IAAI;AACxB;","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SopKit Slug Utilities
|
|
3
|
+
* Premium, zero-dependency multilingual URL slug generator.
|
|
4
|
+
* Link: https://sopkit.github.io/slug-generator/
|
|
5
|
+
*/
|
|
6
|
+
interface SlugOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Character separator. Defaults to "-".
|
|
9
|
+
*/
|
|
10
|
+
separator?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Automatically convert slug to lowercase. Defaults to true.
|
|
13
|
+
*/
|
|
14
|
+
lowercase?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Strip non-alphanumeric characters. Defaults to true.
|
|
17
|
+
*/
|
|
18
|
+
strict?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Generates a clean URL slug from the given text input.
|
|
22
|
+
* Supports accent removal and basic character normalization.
|
|
23
|
+
*/
|
|
24
|
+
declare function slugify(text: string, options?: SlugOptions): string;
|
|
25
|
+
/**
|
|
26
|
+
* Validates if a string is a valid URL slug (alphanumeric and dashes/underscores).
|
|
27
|
+
*/
|
|
28
|
+
declare function isValid(slug: string, separator?: string): boolean;
|
|
29
|
+
|
|
30
|
+
export { type SlugOptions, isValid, slugify };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SopKit Slug Utilities
|
|
3
|
+
* Premium, zero-dependency multilingual URL slug generator.
|
|
4
|
+
* Link: https://sopkit.github.io/slug-generator/
|
|
5
|
+
*/
|
|
6
|
+
interface SlugOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Character separator. Defaults to "-".
|
|
9
|
+
*/
|
|
10
|
+
separator?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Automatically convert slug to lowercase. Defaults to true.
|
|
13
|
+
*/
|
|
14
|
+
lowercase?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Strip non-alphanumeric characters. Defaults to true.
|
|
17
|
+
*/
|
|
18
|
+
strict?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Generates a clean URL slug from the given text input.
|
|
22
|
+
* Supports accent removal and basic character normalization.
|
|
23
|
+
*/
|
|
24
|
+
declare function slugify(text: string, options?: SlugOptions): string;
|
|
25
|
+
/**
|
|
26
|
+
* Validates if a string is a valid URL slug (alphanumeric and dashes/underscores).
|
|
27
|
+
*/
|
|
28
|
+
declare function isValid(slug: string, separator?: string): boolean;
|
|
29
|
+
|
|
30
|
+
export { type SlugOptions, isValid, slugify };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
function slugify(text, options = {}) {
|
|
3
|
+
if (typeof text !== "string") {
|
|
4
|
+
throw new TypeError("Input must be a string");
|
|
5
|
+
}
|
|
6
|
+
const {
|
|
7
|
+
separator = "-",
|
|
8
|
+
lowercase = true,
|
|
9
|
+
strict = true
|
|
10
|
+
} = options;
|
|
11
|
+
let str = text;
|
|
12
|
+
str = str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
13
|
+
if (lowercase) {
|
|
14
|
+
str = str.toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
if (strict) {
|
|
17
|
+
str = str.replace(/[^a-z0-9\s-_]/gi, "");
|
|
18
|
+
}
|
|
19
|
+
str = str.trim().replace(/\s+/g, separator).replace(new RegExp(`\\${separator}+`, "g"), separator);
|
|
20
|
+
if (str.startsWith(separator)) {
|
|
21
|
+
str = str.slice(separator.length);
|
|
22
|
+
}
|
|
23
|
+
if (str.endsWith(separator)) {
|
|
24
|
+
str = str.slice(0, -separator.length);
|
|
25
|
+
}
|
|
26
|
+
return str;
|
|
27
|
+
}
|
|
28
|
+
function isValid(slug, separator = "-") {
|
|
29
|
+
if (typeof slug !== "string" || slug.trim() === "") {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const escapedSeparator = separator.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
33
|
+
const regex = new RegExp(`^[a-z0-9]+(${escapedSeparator}[a-z0-9]+)*$`, "i");
|
|
34
|
+
return regex.test(slug);
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
isValid,
|
|
38
|
+
slugify
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * SopKit Slug Utilities\n * Premium, zero-dependency multilingual URL slug generator.\n * Link: https://sopkit.github.io/slug-generator/\n */\n\nexport interface SlugOptions {\n /**\n * Character separator. Defaults to \"-\".\n */\n separator?: string;\n /**\n * Automatically convert slug to lowercase. Defaults to true.\n */\n lowercase?: boolean;\n /**\n * Strip non-alphanumeric characters. Defaults to true.\n */\n strict?: boolean;\n}\n\n/**\n * Generates a clean URL slug from the given text input.\n * Supports accent removal and basic character normalization.\n */\nexport function slugify(text: string, options: SlugOptions = {}): string {\n if (typeof text !== \"string\") {\n throw new TypeError(\"Input must be a string\");\n }\n\n const {\n separator = \"-\",\n lowercase = true,\n strict = true\n } = options;\n\n let str = text;\n\n // 1. Normalize unicode characters (remove diacritics/accents)\n str = str.normalize(\"NFD\").replace(/[\\u0300-\\u036f]/g, \"\");\n\n // 2. Convert to lowercase if flag set\n if (lowercase) {\n str = str.toLowerCase();\n }\n\n // 3. Apply separator and remove invalid characters\n if (strict) {\n str = str.replace(/[^a-z0-9\\s-_]/gi, \"\");\n }\n\n // 4. Replace whitespace and multiple separators\n str = str.trim()\n .replace(/\\s+/g, separator)\n .replace(new RegExp(`\\\\${separator}+`, \"g\"), separator);\n\n // 5. Clean up leading or trailing separators\n if (str.startsWith(separator)) {\n str = str.slice(separator.length);\n }\n if (str.endsWith(separator)) {\n str = str.slice(0, -separator.length);\n }\n\n return str;\n}\n\n/**\n * Validates if a string is a valid URL slug (alphanumeric and dashes/underscores).\n */\nexport function isValid(slug: string, separator: string = \"-\"): boolean {\n if (typeof slug !== \"string\" || slug.trim() === \"\") {\n return false;\n }\n const escapedSeparator = separator.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\");\n const regex = new RegExp(`^[a-z0-9]+(${escapedSeparator}[a-z0-9]+)*$`, \"i\");\n return regex.test(slug);\n}\n"],"mappings":";AAyBO,SAAS,QAAQ,MAAc,UAAuB,CAAC,GAAW;AACvE,MAAI,OAAO,SAAS,UAAU;AAC5B,UAAM,IAAI,UAAU,wBAAwB;AAAA,EAC9C;AAEA,QAAM;AAAA,IACJ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,EACX,IAAI;AAEJ,MAAI,MAAM;AAGV,QAAM,IAAI,UAAU,KAAK,EAAE,QAAQ,oBAAoB,EAAE;AAGzD,MAAI,WAAW;AACb,UAAM,IAAI,YAAY;AAAA,EACxB;AAGA,MAAI,QAAQ;AACV,UAAM,IAAI,QAAQ,mBAAmB,EAAE;AAAA,EACzC;AAGA,QAAM,IAAI,KAAK,EACZ,QAAQ,QAAQ,SAAS,EACzB,QAAQ,IAAI,OAAO,KAAK,SAAS,KAAK,GAAG,GAAG,SAAS;AAGxD,MAAI,IAAI,WAAW,SAAS,GAAG;AAC7B,UAAM,IAAI,MAAM,UAAU,MAAM;AAAA,EAClC;AACA,MAAI,IAAI,SAAS,SAAS,GAAG;AAC3B,UAAM,IAAI,MAAM,GAAG,CAAC,UAAU,MAAM;AAAA,EACtC;AAEA,SAAO;AACT;AAKO,SAAS,QAAQ,MAAc,YAAoB,KAAc;AACtE,MAAI,OAAO,SAAS,YAAY,KAAK,KAAK,MAAM,IAAI;AAClD,WAAO;AAAA,EACT;AACA,QAAM,mBAAmB,UAAU,QAAQ,0BAA0B,MAAM;AAC3E,QAAM,QAAQ,IAAI,OAAO,cAAc,gBAAgB,gBAAgB,GAAG;AAC1E,SAAO,MAAM,KAAK,IAAI;AACxB;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sopkit/slug",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Premium, lightweight URL slug generator and text clean-up utility with full Unicode/diacritics support.",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"sopkit",
|
|
21
|
+
"slug",
|
|
22
|
+
"slugify",
|
|
23
|
+
"url",
|
|
24
|
+
"clean-url",
|
|
25
|
+
"seo"
|
|
26
|
+
],
|
|
27
|
+
"author": "SopKit",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"homepage": "https://sopkit.github.io/slug-generator/"
|
|
30
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SopKit Slug Utilities
|
|
3
|
+
* Premium, zero-dependency multilingual URL slug generator.
|
|
4
|
+
* Link: https://sopkit.github.io/slug-generator/
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface SlugOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Character separator. Defaults to "-".
|
|
10
|
+
*/
|
|
11
|
+
separator?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Automatically convert slug to lowercase. Defaults to true.
|
|
14
|
+
*/
|
|
15
|
+
lowercase?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Strip non-alphanumeric characters. Defaults to true.
|
|
18
|
+
*/
|
|
19
|
+
strict?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Generates a clean URL slug from the given text input.
|
|
24
|
+
* Supports accent removal and basic character normalization.
|
|
25
|
+
*/
|
|
26
|
+
export function slugify(text: string, options: SlugOptions = {}): string {
|
|
27
|
+
if (typeof text !== "string") {
|
|
28
|
+
throw new TypeError("Input must be a string");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const {
|
|
32
|
+
separator = "-",
|
|
33
|
+
lowercase = true,
|
|
34
|
+
strict = true
|
|
35
|
+
} = options;
|
|
36
|
+
|
|
37
|
+
let str = text;
|
|
38
|
+
|
|
39
|
+
// 1. Normalize unicode characters (remove diacritics/accents)
|
|
40
|
+
str = str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
41
|
+
|
|
42
|
+
// 2. Convert to lowercase if flag set
|
|
43
|
+
if (lowercase) {
|
|
44
|
+
str = str.toLowerCase();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 3. Apply separator and remove invalid characters
|
|
48
|
+
if (strict) {
|
|
49
|
+
str = str.replace(/[^a-z0-9\s-_]/gi, "");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 4. Replace whitespace and multiple separators
|
|
53
|
+
str = str.trim()
|
|
54
|
+
.replace(/\s+/g, separator)
|
|
55
|
+
.replace(new RegExp(`\\${separator}+`, "g"), separator);
|
|
56
|
+
|
|
57
|
+
// 5. Clean up leading or trailing separators
|
|
58
|
+
if (str.startsWith(separator)) {
|
|
59
|
+
str = str.slice(separator.length);
|
|
60
|
+
}
|
|
61
|
+
if (str.endsWith(separator)) {
|
|
62
|
+
str = str.slice(0, -separator.length);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return str;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Validates if a string is a valid URL slug (alphanumeric and dashes/underscores).
|
|
70
|
+
*/
|
|
71
|
+
export function isValid(slug: string, separator: string = "-"): boolean {
|
|
72
|
+
if (typeof slug !== "string" || slug.trim() === "") {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const escapedSeparator = separator.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
76
|
+
const regex = new RegExp(`^[a-z0-9]+(${escapedSeparator}[a-z0-9]+)*$`, "i");
|
|
77
|
+
return regex.test(slug);
|
|
78
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"]
|
|
14
|
+
}
|