@dephub/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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright (c) 2025 Estarlin R
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,201 @@
1
+ # @dephub/slug 🔤
2
+
3
+ > URL-friendly slug generator with semantic comparison and validation. Convert strings to clean slugs with full Unicode support.
4
+
5
+ [![NPM version](https://img.shields.io/npm/v/@dephub/slug.svg?style=flat)](https://npmjs.org/package/@dephub/slug)
6
+ [![ESM-only](https://img.shields.io/badge/ESM-only-brightgreen?style=flat)](https://nodejs.org/)
7
+ [![Node.js version](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg)](https://nodejs.org/)
8
+
9
+ ---
10
+
11
+ ## Features ✨
12
+
13
+ - 🎯 **Simple API** - `slug()`, `isSlug()`, `compare()`
14
+ - 🔒 **Type Safe** - Full TypeScript support with zero configuration
15
+ - 🌍 **Unicode Support** - Handles accented characters and diacritics
16
+ - 🛠️ **Dual Interface** - Use as CLI tool or programmatic API
17
+ - ⚡ **Lightweight** - Zero dependencies, focused functionality
18
+ - 🔧 **Configurable** - Custom separators, case handling, and fallbacks
19
+
20
+ ---
21
+
22
+ ## Installation 📲
23
+
24
+ ```bash
25
+ npm install @dephub/slug
26
+ # or
27
+ pnpm add @dephub/slug
28
+ # or
29
+ yarn add @dephub/slug
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Usage 🎉
35
+
36
+ ### CLI Usage
37
+
38
+ ```bash
39
+ # Generate slugs
40
+ slug generate "Hello World!"
41
+ # Output: hello-world
42
+
43
+ slug generate "Café & Bar" --separator "_"
44
+ # Output: cafe_bar
45
+
46
+ # Validate slugs
47
+ slug validate "valid-slug-123"
48
+ # Output: ✅ Valid slug format
49
+
50
+ slug validate "Invalid Slug!"
51
+ # Output: ❌ Invalid slug format
52
+
53
+ # Compare strings
54
+ slug compare "Café au lait" "cafe-au-lait"
55
+ # Output: ✅ Strings are equivalent when slugified
56
+ ```
57
+
58
+ ### Programmatic Usage
59
+
60
+ ```typescript
61
+ import { slug, isSlug, compare } from '@dephub/slug';
62
+
63
+ // Basic slug generation
64
+ slug(' Héllo Wörld!!! '); // "hello-world"
65
+
66
+ // Custom options
67
+ slug('Hello World', { separator: '_' }); // "hello_world"
68
+ slug('Hello World', { lowercase: false }); // "Hello-World"
69
+ slug('###', { fallback: 'home' }); // "home"
70
+
71
+ // Validation
72
+ isSlug('valid-slug-name'); // true
73
+ isSlug('Invalid Slug!'); // false
74
+
75
+ // Semantic comparison
76
+ compare('Café au lait', 'cafe-au-lait'); // true
77
+ compare('user-profile', 'user_profile'); // false
78
+ ```
79
+
80
+ ### Advanced Examples
81
+
82
+ ```typescript
83
+ // SEO-friendly URLs
84
+ const title = 'My Awesome Blog Post!';
85
+ const urlSlug = slug(title); // "my-awesome-blog-post"
86
+
87
+ // Username normalization
88
+ const username = slug(userInput, { separator: '_' });
89
+ if (!isSlug(username)) {
90
+ throw new Error('Invalid username format');
91
+ }
92
+
93
+ // Database lookup with semantic comparison
94
+ function findProduct(name: string) {
95
+ return products.find((product) => compare(product.name, name));
96
+ }
97
+ ```
98
+
99
+ ---
100
+
101
+ ## API Reference 📚
102
+
103
+ ### `slug(str, options?)`
104
+
105
+ Convert string to URL-friendly slug.
106
+
107
+ **Parameters:**
108
+
109
+ - `str` (string) - Input string to convert
110
+ - `options` (SlugOptions) - Configuration options
111
+ - `separator` (string) - Character to replace spaces (default: `'-'`)
112
+ - `lowercase` (boolean) - Convert to lowercase (default: `true`)
113
+ - `fallback` (string) - Fallback if result empty (default: `'default-slug'`)
114
+
115
+ **Returns:** `string`
116
+
117
+ **Throws:** `TypeError` if input is not a string
118
+
119
+ ### `isSlug(str)`
120
+
121
+ Check if string matches valid slug format (lowercase, hyphens, alphanumeric).
122
+
123
+ **Parameters:**
124
+
125
+ - `str` (string) - String to validate
126
+
127
+ **Returns:** `boolean`
128
+
129
+ ### `compare(a, b, options?)`
130
+
131
+ Compare two strings by their slugified versions.
132
+
133
+ **Parameters:**
134
+
135
+ - `a` (string) - First string to compare
136
+ - `b` (string) - Second string to compare
137
+ - `options` (SlugOptions) - Slug configuration options
138
+
139
+ **Returns:** `boolean`
140
+
141
+ ---
142
+
143
+ ## CLI Commands
144
+
145
+ ### `slug generate <input>`
146
+
147
+ Convert string to URL-friendly slug.
148
+
149
+ **Options:**
150
+
151
+ - `--separator` - Separator character (default: "-")
152
+ - `--no-lowercase` - Keep original case
153
+ - `--fallback` - Fallback text if result is empty
154
+
155
+ ### `slug validate <input>`
156
+
157
+ Check if string is valid slug format.
158
+
159
+ ### `slug compare <first> <second>`
160
+
161
+ Compare two strings by slugified versions.
162
+
163
+ **Options:**
164
+
165
+ - `--separator` - Separator character
166
+ - `--no-lowercase` - Keep original case
167
+
168
+ ---
169
+
170
+ ## Integration Examples
171
+
172
+ ### With File System
173
+
174
+ ```typescript
175
+ import { slug } from '@dephub/slug';
176
+ import { writeFile } from '@dephub/write';
177
+
178
+ async function createSluggedFile(content: string, filename: string) {
179
+ const sluggedName = slug(filename);
180
+ await writeFile(`./${sluggedName}.txt`, content);
181
+ }
182
+ ```
183
+
184
+ ### With Configuration
185
+
186
+ ```typescript
187
+ import { slug, compare } from '@dephub/slug';
188
+ import { log } from '@dephub/log';
189
+
190
+ const categoryName = 'User Settings';
191
+ const normalized = slug(categoryName, { separator: '_' });
192
+ log(`Category: ${normalized}`); // "user_settings"
193
+ ```
194
+
195
+ ---
196
+
197
+ ## License 📄
198
+
199
+ MIT License – see [LICENSE](LICENSE) for details.
200
+
201
+ **Author:** Estarlin R ([estarlincito.com](https://estarlincito.com))
package/dist/cli.js ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+ import { cli as a } from "@dephub/cli";
3
+ import { log as l, error as n, success as c } from "@dephub/log";
4
+ import { compare as p } from "./core/compare.js";
5
+ import { slug as m } from "./core/slug.js";
6
+ import { isSlug as g } from "./core/valid.js";
7
+ import { name as u, version as f, description as d } from "./utils/pkg.js";
8
+ a.name(u.split("/")[1] ?? "slug").version(f).description(d);
9
+ a.command("generate", "Convert string to URL-friendly slug").argument("<input>", "Input string to convert").option("--separator", 'Separator character (default: "-")').flag("--no-lowercase", "Keep original case").option("--fallback", "Fallback text if result is empty").action(({ args: r, options: e, flags: o }) => {
10
+ const [s] = r;
11
+ try {
12
+ const t = {
13
+ fallback: e.fallback,
14
+ lowercase: o.lowercase,
15
+ separator: e.separator
16
+ }, i = m(s, t);
17
+ l(i);
18
+ } catch (t) {
19
+ n(t.message), process.exit(1);
20
+ }
21
+ });
22
+ a.command("validate", "Check if string is valid slug format").argument("<input>", "String to validate").action(({ args: r }) => {
23
+ const [e] = r;
24
+ g(e) ? c("Valid slug format") : (n("Invalid slug format"), process.exit(1));
25
+ });
26
+ a.command("compare", "Compare two strings by slugified versions").argument("<first>", "First string to compare").argument("<second>", "Second string to compare").option("--separator", "Separator character").flag("--no-lowercase", "Keep original case").action(({ args: r, options: e, flags: o }) => {
27
+ const [s, t] = r, i = {
28
+ lowercase: o.lowercase,
29
+ separator: e.separator
30
+ };
31
+ p(s, t, i) ? c("Strings are equivalent when slugified") : (n("Strings are different when slugified"), process.exit(1));
32
+ });
33
+ await a.run();
@@ -0,0 +1,9 @@
1
+ import { SlugOptions } from './slug.js';
2
+ /**
3
+ * Compare two strings by their slugified versions
4
+ * @param a - First string to compare
5
+ * @param b - Second string to compare
6
+ * @param options - Slug options for comparison
7
+ * @returns True if slugified strings are equal
8
+ */
9
+ export declare const compare: (a: string, b: string, options?: SlugOptions) => boolean;
@@ -0,0 +1,5 @@
1
+ import { slug as m } from "./slug.js";
2
+ const p = (o, c, r = {}) => m(o, r) === m(c, r);
3
+ export {
4
+ p as compare
5
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Convert string to URL-friendly slug
3
+ */
4
+ export interface SlugOptions {
5
+ separator?: string;
6
+ lowercase?: boolean;
7
+ fallback?: string;
8
+ }
9
+ /**
10
+ * Convert string to URL-friendly slug
11
+ * @param str - Input string to convert
12
+ * @param options - Slug configuration options
13
+ * @returns URL-friendly slug string
14
+ * @throws {TypeError} If input is not a string
15
+ */
16
+ export declare const slug: (str: string, options?: SlugOptions) => string;
@@ -0,0 +1,13 @@
1
+ const o = (r, t = {}) => {
2
+ if (typeof r != "string") throw new TypeError("Input must be string");
3
+ const {
4
+ separator: a = "-",
5
+ lowercase: l = !0,
6
+ fallback: s = "default-slug"
7
+ } = t;
8
+ let e = r.normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-zA-Z0-9\s]/g, "").trim().replace(/\s+/g, a);
9
+ return l && (e = e.toLowerCase()), e || s;
10
+ };
11
+ export {
12
+ o as slug
13
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Check if string matches valid slug format
3
+ * @param str - String to validate
4
+ * @returns True if string is valid slug format
5
+ */
6
+ export declare const isSlug: (str: string) => boolean;
@@ -0,0 +1,4 @@
1
+ const s = (t) => /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(t);
2
+ export {
3
+ s as isSlug
4
+ };
@@ -0,0 +1,4 @@
1
+ export { compare } from './core/compare.js';
2
+ export { slug, type SlugOptions } from './core/slug.js';
3
+ export { slug as default } from './core/slug.js';
4
+ export { isSlug } from './core/valid.js';
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ import { compare as e } from "./core/compare.js";
2
+ import { slug as l, slug as m } from "./core/slug.js";
3
+ import { isSlug as s } from "./core/valid.js";
4
+ export {
5
+ e as compare,
6
+ l as default,
7
+ s as isSlug,
8
+ m as slug
9
+ };
@@ -0,0 +1,11 @@
1
+ const n = "@dephub/slug", o = "1.0.0", s = "URL-friendly slug generator with semantic comparison and validation", t = {
2
+ name: n,
3
+ version: o,
4
+ description: s
5
+ };
6
+ export {
7
+ t as default,
8
+ s as description,
9
+ n as name,
10
+ o as version
11
+ };
@@ -0,0 +1 @@
1
+ export declare const version: string, name: string, description: string;
@@ -0,0 +1,7 @@
1
+ import o from "../package.json.js";
2
+ const { version: e, name: i, description: n } = o ?? {};
3
+ export {
4
+ n as description,
5
+ i as name,
6
+ e as version
7
+ };
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@dephub/slug",
3
+ "type": "module",
4
+ "version": "1.0.0",
5
+ "description": "URL-friendly slug generator with semantic comparison and validation",
6
+ "types": "./dist/index.d.ts",
7
+ "main": "./dist/index.js",
8
+ "bin": {
9
+ "slug": "./dist/cli.js"
10
+ },
11
+ "keywords": [
12
+ "slug",
13
+ "url",
14
+ "seo",
15
+ "string",
16
+ "normalize",
17
+ "comparison",
18
+ "validation",
19
+ "typescript",
20
+ "nodejs",
21
+ "dephub"
22
+ ],
23
+ "author": {
24
+ "name": "Estarlin R",
25
+ "email": "dev@estarlincito.com",
26
+ "url": "https://estarlincito.com"
27
+ },
28
+ "engines": {
29
+ "node": ">=18.0.0"
30
+ },
31
+ "files": [
32
+ "LICENSE",
33
+ "README.md",
34
+ "dist"
35
+ ],
36
+ "license": "MIT",
37
+ "homepage": "https://github.com/dephubjs/slug#readme",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/dephubjs/slug.git",
41
+ "directory": "packages/slug"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/dephubjs/slug/issues"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "dependencies": {
50
+ "@dephub/cli": "^1.0.0",
51
+ "@dephub/log": "^1.0.0"
52
+ },
53
+ "scripts": {
54
+ "release": "pnpm publish",
55
+ "check-types": "tsc --noEmit --skipLibCheck",
56
+ "lint": "lint . --max-warnings 0",
57
+ "lint:fix": "lint . --fix",
58
+ "clean": "rm -rf dist",
59
+ "build": "vite build",
60
+ "dev": "vite build -w --mode development"
61
+ }
62
+ }