diff --git a/package.json b/package.json index 3a31b6b..2868402 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@ztimson/utils", - "version": "0.30.2", - "description": "Utility library", + "version": "0.30.3", + "description": "Utility library",S "author": "Zak Timson", "license": "MIT", "private": false, diff --git a/src/html.ts b/src/html.ts new file mode 100644 index 0000000..b6f7f53 --- /dev/null +++ b/src/html.ts @@ -0,0 +1,45 @@ +/** + * Decode HTML escaped characters + * @param html HTML to clean up + * @returns {any} + */ +export function decodeHtml(html: string) { + return html + .replace(/ /g, '\u00A0') + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/¢/g, '¢') + .replace(/£/g, '£') + .replace(/¥/g, '¥') + .replace(/€/g, '€') + .replace(/©/g, '©') + .replace(/®/g, '®') + .replace(/™/g, '™') + .replace(/×/g, '×') + .replace(/÷/g, '÷') + .replace(/&#(\d+);/g, (match, dec) => String.fromCharCode(dec)) + .replace(/&#x([0-9a-fA-F]+);/g, (match, hex) => String.fromCharCode(parseInt(hex, 16))) + .replace(/&/g, '&'); // Always last! +} + +/** + * Parse markdown headers + * @param {string} content + * @returns {{meta: any, content: string} | {meta: {}, content: string}} + */ +export function parseMarkdown(content: string) { + const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); + if(!match) return {meta: {}, content}; + + const meta: any = {}; + for (const line of match[1].split('\n')) { + const colonIdx = line.indexOf(':'); + if (colonIdx === -1) continue; + const key = line.slice(0, colonIdx).trim(); + const value = line.slice(colonIdx + 1).trim(); + try { meta[key] = JSON.parse(value); } catch { meta[key] = value; } + } + return {meta, content: match[2].trim()}; +} diff --git a/src/index.ts b/src/index.ts index 28e226f..c4e58d0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,9 +7,10 @@ export * from './cache'; export * from './color'; export * from './csv'; export * from './database'; -export * from './files'; export * from './emitter'; export * from './errors'; +export * from './files'; +export * from './html'; export * from './http'; export * from './json'; export * from './jwt'; diff --git a/src/string.ts b/src/string.ts index 7a2bba0..05fdf3a 100644 --- a/src/string.ts +++ b/src/string.ts @@ -27,32 +27,6 @@ export function camelCase(str?: string): string { return pascal.charAt(0).toLowerCase() + pascal.slice(1); } -/** - * Decode HTML escaped characters - * @param html HTML to clean up - * @returns {any} - */ -export function decodeHtml(html: string) { - return html - .replace(/ /g, '\u00A0') - .replace(/"/g, '"') - .replace(/'/g, "'") - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/¢/g, '¢') - .replace(/£/g, '£') - .replace(/¥/g, '¥') - .replace(/€/g, '€') - .replace(/©/g, '©') - .replace(/®/g, '®') - .replace(/™/g, '™') - .replace(/×/g, '×') - .replace(/÷/g, '÷') - .replace(/&#(\d+);/g, (match, dec) => String.fromCharCode(dec)) - .replace(/&#x([0-9a-fA-F]+);/g, (match, hex) => String.fromCharCode(parseInt(hex, 16))) - .replace(/&/g, '&'); // Always last! -} - /** * Convert number of bytes into a human-readable size * @@ -125,7 +99,6 @@ export function kebabCase(str?: string): string { return wordSegments(str).map(w => w.toLowerCase()).join("-"); } - /** * Add padding to string * @@ -256,18 +229,14 @@ export function strSplice(str: string, start: number, deleteCount: number, inser return before + insert + after; } -function titleCase(str: string) { - // Normalize separators: replace underscores and hyphens with spaces - let normalizedStr = str.replace(/(_|-)/g, ' '); - // Handle CamelCase/PascalCase boundaries: insert a space before capital letters - normalizedStr = normalizedStr.replace(/([a-z])([A-Z])/g, '$1 $2'); - // Lowercase the whole string, split by any whitespace, and capitalize each word - let words = normalizedStr.toLowerCase().split(/\s+/).filter(Boolean); - const titledWords = words.map(word => { - if (word.length === 0) return ''; - return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); - }); - return titledWords.join(' '); +/** + * Converts text to Title Case + */ +export function titleCase(str?: string): string { + if(!str) return ''; + return wordSegments(str) + .map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()) + .join(' '); } /**