(rawUrls: string[])
| 20 | * @throws {TypeError} If any of the URL segment is not a string, this throws. |
| 21 | */ |
| 22 | export function normalizeUrl(rawUrls: string[]): string { |
| 23 | const urls = [...rawUrls]; |
| 24 | const resultArray = []; |
| 25 | |
| 26 | let hasStartingSlash = false; |
| 27 | let hasEndingSlash = false; |
| 28 | |
| 29 | const isNonEmptyArray = (arr: string[]): arr is [string, ...string[]] => |
| 30 | arr.length > 0; |
| 31 | |
| 32 | if (!isNonEmptyArray(urls)) { |
| 33 | return ''; |
| 34 | } |
| 35 | |
| 36 | // If the first part is a plain protocol, we combine it with the next part. |
| 37 | if (urls[0].match(/^[^/:]+:\/*$/) && urls.length > 1) { |
| 38 | const first = urls.shift()!; |
| 39 | if (first.startsWith('file:') && urls[0].startsWith('/')) { |
| 40 | // Force a double slash here, else we lose the information that the next |
| 41 | // segment is an absolute path |
| 42 | urls[0] = `${first}//${urls[0]}`; |
| 43 | } else { |
| 44 | urls[0] = first + urls[0]; |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | // There must be two or three slashes in the file protocol, |
| 49 | // two slashes in anything else. |
| 50 | const replacement = urls[0].match(/^file:\/\/\//) ? '$1:///' : '$1://'; |
| 51 | urls[0] = urls[0].replace(/^(?<protocol>[^/:]+):\/*/, replacement); |
| 52 | |
| 53 | for (let i = 0; i < urls.length; i += 1) { |
| 54 | let component = urls[i]; |
| 55 | |
| 56 | if (typeof component !== 'string') { |
| 57 | throw new TypeError(`Url must be a string. Received ${typeof component}`); |
| 58 | } |
| 59 | |
| 60 | if (component === '') { |
| 61 | if (i === urls.length - 1 && hasEndingSlash) { |
| 62 | resultArray.push('/'); |
| 63 | } |
| 64 | continue; |
| 65 | } |
| 66 | |
| 67 | if (component !== '/') { |
| 68 | if (i > 0) { |
| 69 | // Removing the starting slashes for each component but the first. |
| 70 | component = component.replace( |
| 71 | /^\/+/, |
| 72 | // Special case where the first element of rawUrls is empty |
| 73 | // ["", "/hello"] => /hello |
| 74 | component.startsWith('/') && !hasStartingSlash ? '/' : '', |
| 75 | ); |
| 76 | } |
| 77 | |
| 78 | hasEndingSlash = component.endsWith('/'); |
| 79 | // Removing the ending slashes for each component but the last. For the |
no test coverage detected
searching dependent graphs…