UNPKG

anitimejs

Version:

Thư viện xử lý chuỗi số và thời gian trong JavaScript/Typescript

226 lines (225 loc) 6.75 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createSlug = createSlug; exports.isValidSlug = isValidSlug; exports.createUniqueSlug = createUniqueSlug; exports.getSlugPart = getSlugPart; /** * Tạo slug từ chuỗi với nhiều tùy chọn * * @param input Chuỗi đầu vào cần chuyển thành slug * @param options Tùy chọn xử lý slug * @returns Chuỗi slug đã được xử lý * * @example * // Tạo slug cơ bản * createSlug("Hello World"); // "hello-world" * * // Xử lý tiếng Việt * createSlug("Chào Thế Giới"); // "chao-the-gioi" * * // Với tùy chọn * createSlug("Product Name (Version 2.0)", { * separator: "_", * maxLength: 20, * customReplacements: { "2.0": "2-0" } * }); // "product_name_version" */ function createSlug(input, options = {}) { if (!input) return ""; const { separator = "-", lowercase = true, removeAccents = true, maxLength = 0, removeNonAlphanumeric = true, replaceWhitespace = true, customReplacements = {}, } = options; // Áp dụng các thay thế tùy chỉnh let slug = input; Object.entries(customReplacements).forEach(([from, to]) => { slug = slug.replace(new RegExp(from, "g"), to); }); // Xử lý chữ thường nếu cần if (lowercase) { slug = slug.toLowerCase(); } // Loại bỏ dấu tiếng Việt nếu cần if (removeAccents) { slug = removeVietnameseAccents(slug); } // Thay thế khoảng trắng bằng dấu phân cách if (replaceWhitespace) { slug = slug.replace(/\s+/g, separator); } // Loại bỏ các ký tự không phải chữ và số if (removeNonAlphanumeric) { slug = slug.replace(/[^\w\-]+/g, ""); slug = slug.replace(/\_+/g, separator); } // Loại bỏ các dấu phân cách trùng lặp const separatorRegex = new RegExp(`\\${separator}{2,}`, "g"); slug = slug.replace(separatorRegex, separator); // Loại bỏ dấu phân cách ở đầu và cuối slug = slug.replace(new RegExp(`^\\${separator}|\\${separator}$`, "g"), ""); // Giới hạn độ dài nếu cần if (maxLength > 0 && slug.length > maxLength) { // Cắt tại vị trí dấu phân cách gần nhất để tránh cắt giữa từ const lastPos = slug.substring(0, maxLength).lastIndexOf(separator); slug = slug.substring(0, lastPos > 0 ? lastPos : maxLength); } return slug; } /** * Kiểm tra xem một chuỗi có phải slug hợp lệ không * * @param slug Chuỗi slug cần kiểm tra * @param pattern Mẫu regex tùy chỉnh (tùy chọn) * @returns true nếu là slug hợp lệ, false nếu không * * @example * isValidSlug("hello-world"); // true * isValidSlug("hello world"); // false * isValidSlug("product_123", /^[a-z0-9_]+$/); // true */ function isValidSlug(slug, pattern) { if (!slug) return false; // Mẫu mặc định: chữ thường, số và dấu gạch ngang const defaultPattern = /^[a-z0-9\-]+$/; const regex = pattern || defaultPattern; return regex.test(slug); } /** * Tạo một slug duy nhất từ một chuỗi, thêm hậu tố số nếu cần * * @param input Chuỗi đầu vào * @param existingSlugs Mảng các slug đã tồn tại để kiểm tra trùng lặp * @param options Tùy chọn xử lý slug * @returns Slug duy nhất đã được xử lý * * @example * createUniqueSlug("Hello", ["hello", "hello-1"]); // "hello-2" */ function createUniqueSlug(input, existingSlugs = [], options = {}) { const baseSlug = createSlug(input, options); if (!existingSlugs.includes(baseSlug)) { return baseSlug; } // Tìm số hiện tại nếu slug có dạng base-1, base-2, ... const slugPattern = new RegExp(`^${baseSlug}(\\-\\d+)?$`); const { separator = "-" } = options; let maxNumber = 0; existingSlugs.forEach((slug) => { if (slugPattern.test(slug)) { const match = slug.match(new RegExp(`${baseSlug}${separator}(\\d+)$`)); if (match && match[1]) { const num = parseInt(match[1], 10); if (num > maxNumber) { maxNumber = num; } } } }); return `${baseSlug}${separator}${maxNumber + 1}`; } /** * Lấy một phần của slug, mặc định là phần cuối cùng * * @param slug Chuỗi slug cần xử lý * @param position Vị trí cần lấy (mặc định: 'last') * @param separator Ký tự phân cách (mặc định: "-") * @returns Phần của slug đã được xử lý * * @example * getSlugPart("blog/2023/post-title"); // "post-title" * getSlugPart("blog/2023/post-title", 'first', '/'); // "blog" * getSlugPart("blog/2023/post-title", 1, '/'); // "2023" */ function getSlugPart(slug, position = "last", separator = "-") { if (!slug) return ""; const parts = slug.split(separator); if (position === "first") { return parts[0] || ""; } else if (position === "last") { return parts[parts.length - 1] || ""; } else if (typeof position === "number") { return parts[position] || ""; } return ""; } /** * Hàm trợ giúp loại bỏ dấu tiếng Việt * * @param str Chuỗi cần xử lý * @returns Chuỗi đã loại bỏ dấu */ function removeVietnameseAccents(str) { const map = { à: "a", á: "a", ả: "a", ã: "a", ạ: "a", ă: "a", ằ: "a", ắ: "a", ẳ: "a", ẵ: "a", ặ: "a", â: "a", ầ: "a", ấ: "a", ẩ: "a", ẫ: "a", ậ: "a", đ: "d", è: "e", é: "e", ẻ: "e", ẽ: "e", ẹ: "e", ê: "e", ề: "e", ế: "e", ể: "e", ễ: "e", ệ: "e", ì: "i", í: "i", ỉ: "i", ĩ: "i", ị: "i", ò: "o", ó: "o", ỏ: "o", õ: "o", ọ: "o", ô: "o", ồ: "o", ố: "o", ổ: "o", ỗ: "o", ộ: "o", ơ: "o", ờ: "o", ớ: "o", ở: "o", ỡ: "o", ợ: "o", ù: "u", ú: "u", ủ: "u", ũ: "u", ụ: "u", ư: "u", ừ: "u", ứ: "u", ử: "u", ữ: "u", ự: "u", ỳ: "y", ý: "y", ỷ: "y", ỹ: "y", ỵ: "y", }; return str.replace(/[^A-Za-z0-9\s]/g, (a) => map[a] || a); }