-
-
Save matthewp/81d2193cf6aa6f7208df447602b5357b to your computer and use it in GitHub Desktop.
url-relative
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { resolve } from 'https://deno.land/[email protected]/path/posix.ts'; | |
const CHAR_FORWARD_SLASH = 47; | |
/** | |
* Return the relative path from `from` to `to` based on current working directory. | |
* @param from path in current working directory | |
* @param to path in current working directory | |
*/ | |
export function relative(origFrom: string, origTo: string): string { | |
if (origFrom === origTo) return ""; | |
let from = resolve(origFrom); | |
let to = resolve(origTo); | |
if (from === to) return ""; | |
// Trim any leading backslashes | |
let fromStart = 1; | |
const fromEnd = from.length; | |
for (; fromStart < fromEnd; ++fromStart) { | |
if (from.charCodeAt(fromStart) !== CHAR_FORWARD_SLASH) break; | |
} | |
const fromLen = fromEnd - fromStart; | |
// Trim any leading backslashes | |
let toStart = 1; | |
const toEnd = to.length; | |
for (; toStart < toEnd; ++toStart) { | |
if (to.charCodeAt(toStart) !== CHAR_FORWARD_SLASH) break; | |
} | |
const toLen = toEnd - toStart; | |
// Compare paths to find the longest common path from root | |
const length = fromLen < toLen ? fromLen : toLen; | |
let lastCommonSep = -1; | |
let i = 0; | |
for (; i <= length; ++i) { | |
if (i === length) { | |
if (toLen > length) { | |
if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) { | |
// We get here if `from` is the exact base path for `to`. | |
// For example: from='/foo/bar'; to='/foo/bar/baz' | |
if(origFrom.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { | |
return to.slice(toStart + i + 1); | |
} else { | |
return to.slice(toStart + lastCommonSep + 1); | |
} | |
} else if (i === 0) { | |
// We get here if `from` is the root | |
// For example: from='/'; to='/foo' | |
return to.slice(toStart + i); | |
} | |
} else if (fromLen > length) { | |
if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { | |
// We get here if `to` is the exact base path for `from`. | |
// For example: from='/foo/bar/baz'; to='/foo/bar' | |
lastCommonSep = i; | |
} else if (i === 0) { | |
// We get here if `to` is the root. | |
// For example: from='/foo'; to='/' | |
lastCommonSep = 0; | |
} | |
} | |
break; | |
} | |
const fromCode = from.charCodeAt(fromStart + i); | |
const toCode = to.charCodeAt(toStart + i); | |
if (fromCode !== toCode) break; | |
else if (fromCode === CHAR_FORWARD_SLASH) lastCommonSep = i; | |
} | |
let out = ""; | |
// Generate the relative path based on the path difference between `to` | |
// and `from` | |
for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { | |
if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) { | |
if (out.length === 0) out += ".."; | |
else out += "/.."; | |
} | |
} | |
// Lastly, append the rest of the destination (`to`) path that comes after | |
// the common path parts | |
if (out.length > 0) return out + to.slice(toStart + lastCommonSep); | |
else { | |
toStart += lastCommonSep; | |
if (to.charCodeAt(toStart) === CHAR_FORWARD_SLASH) ++toStart; | |
return to.slice(toStart); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment