@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 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":[]}
@@ -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 };
@@ -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
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: ["src/index.ts"],
5
+ format: ["cjs", "esm"],
6
+ dts: true,
7
+ clean: true,
8
+ minify: false,
9
+ sourcemap: true,
10
+ splitting: false,
11
+ });