1/** 2 * Shared module for parsing docs paths to extract item name and type. 3 * Used by both server-side (TypeScript) and client-side (JavaScript) code. 4 * 5 * Requires: ES modules support, URL constructor (Node.js 10+, modern browsers) 6 */ 7 8const KNOWN_TYPES = { 9 patterns: 'pattern', 10 components: 'component', 11 utilities: 'utility' 12}; 13 14/** 15 * Extracts docs item name and type from a pathname. 16 * 17 * @param {string} pathname - URL pathname (may include query params or hash fragments) 18 * @returns {{name: string, type?: string} | null} Object with `name` and optionally `type`, or null 19 */ 20export function parseDocsPath(pathname) { 21 if (!pathname || typeof pathname !== 'string') { 22 return null; 23 } 24 25 // Validate URL constructor is available (Node.js 10+, all modern browsers) 26 if (typeof URL === 'undefined') { 27 if (typeof console !== 'undefined' && console.error) { 28 console.error('URL constructor not available. This module requires Node.js 10+ or a modern browser.'); 29 } 30 return null; 31 } 32 33 const extractFromPath = path => { 34 // Use URL constructor to handle query params and hash fragments 35 // Supply a base URL so it doesn't error on relative paths 36 const url = new URL(path, 'https://webawesome.com'); 37 38 // If an absolute URL was provided, validate it's from webawesome.com 39 // This prevents parsing paths from other domains that happen to have /docs/ in them 40 if (path.startsWith('http://') || path.startsWith('https://')) { 41 const inputUrl = new URL(path); 42 if (inputUrl.hostname !== 'webawesome.com' && inputUrl.hostname !== 'www.webawesome.com') { 43 return null; 44 } 45 } 46 47 const pathname = url.pathname; 48 49 if (!pathname.startsWith('/docs/')) { 50 return null; 51 } 52 53 const pathWithoutPrefix = pathname.slice('/docs/'.length).replace(/\/$/, ''); 54 const segments = pathWithoutPrefix.split('/').filter(Boolean); 55 56 if (segments.length === 0) { 57 return null; 58 } 59 60 const name = segments[segments.length - 1]; 61 62 // Only include type if there are multiple segments (category/item structure) 63 if (segments.length > 1) { 64 const firstSegment = segments[0]; 65 const type = KNOWN_TYPES[firstSegment] || firstSegment; 66 return { name, type }; 67 } 68 69 return { name }; 70 }; 71 72 const result = extractFromPath(pathname); 73 if (result) { 74 return result; 75 } 76 77 // Try decoding in case pathname is URL-encoded 78 try { 79 return extractFromPath(decodeURIComponent(pathname)); 80 } catch (error) { 81 // Invalid percent-encoding - return null. Log to help identify unexpected cases. 82 if (typeof console !== 'undefined' && console.warn) { 83 console.warn('Failed to decode URI component:', pathname, error); 84 } 85 return null; 86 } 87}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.