Skip to content

Instantly share code, notes, and snippets.

@matthewp
Created September 1, 2021 11:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matthewp/81d2193cf6aa6f7208df447602b5357b to your computer and use it in GitHub Desktop.
Save matthewp/81d2193cf6aa6f7208df447602b5357b to your computer and use it in GitHub Desktop.
url-relative
import { resolve } from 'https://deno.land/std@0.105.0/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