Skip to content

Commit

Permalink
feat(endo): Add URL relative
Browse files Browse the repository at this point in the history
  • Loading branch information
kriskowal committed Jun 19, 2020
1 parent cd10713 commit c74f79e
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 0 deletions.
78 changes: 78 additions & 0 deletions packages/endo/src/url.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Derrived from https://github.com/junosuarez/url-relative
//
// Copyright (c) MMXV jden jason@denizac.org
//
// ISC License
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

export const relative = (referrer, location) => {
referrer = String(referrer || "");
location = String(location || "");

if (referrer === location) {
return "";
}

const referrerURL = new URL(referrer);
const locationURL = new URL(location);

if (referrerURL.host !== locationURL.host) {
return location;
}

if (referrerURL.protocol !== locationURL.protocol) {
return location;
}

if (referrerURL.auth !== locationURL.auth) {
return location;
}

// left location right, look for closest common path segment
const referrerParts = referrerURL.pathname.substr(1).split("/");
const locationParts = locationURL.pathname.substr(1).split("/");

if (referrerURL.pathname === locationURL.pathname) {
if (locationURL.pathname[locationURL.pathname.length - 1] === "/") {
return ".";
}
return locationParts[locationParts.length - 1];
}

while (referrerParts[0] === locationParts[0]) {
referrerParts.shift();
locationParts.shift();
}

let length = referrerParts.length - locationParts.length;
if (length > 0) {
if (referrer.endsWith("/")) {
locationParts.unshift("..");
}
while (length > 0) {
length -= 1;
locationParts.unshift("..");
}
return locationParts.join("/");
}
if (length < 0) {
return locationParts.join("/");
}
length = locationParts.length - 1;
while (length > 0) {
length -= 1;
locationParts.unshift("..");
}
return locationParts.join("/");
};
117 changes: 117 additions & 0 deletions packages/endo/test/relative.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Derrived from https://github.com/junosuarez/url-relative
//
// Copyright (c) MMXV jden jason@denizac.org
//
// ISC License
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

import tape from "tape";
import { relative } from "../src/url.js";

const { test } = tape;

test("different protocol", t => {
t.plan(2);
t.equal(
relative("http://a.com:12/a", "https://a.com:12/a"),
"https://a.com:12/a"
);
t.equal(
relative("http://a.com:12/a/", "https://a.com:12/a/"),
"https://a.com:12/a/"
);
});

test("file protocol", t => {
t.plan(1);
t.equal(relative("file:///a", "file:///b"), "b");
});

test("different domain", t => {
t.plan(2);
t.equal(relative("http://a.com:12/a", "http://b.com/a"), "http://b.com/a");
t.equal(relative("http://a.com:12/a/", "http://b.com/a/"), "http://b.com/a/");
});

test("same domain", t => {
t.plan(2);
t.equal(relative("http://a.com/a", "http://a.com/b"), "b");
t.equal(relative("http://a.com/a/", "http://a.com/b/"), "../b/");
});

test("divergent paths, longer from", t => {
t.plan(4);
t.equal(
relative("http://example.com/a/b/c/d", "http://example.com/a/b/d"),
"../d"
);
t.equal(
relative("http://example.com/a/b/c/d/e", "http://example.com/a/d/e"),
"../../d/e"
);
t.equal(
relative("http://example.com/a/b/c/d/", "http://example.com/a/b/d/"),
"../../d/"
);
t.equal(
relative("http://example.com/a/b/c/d/e/", "http://example.com/a/d/e/"),
"../../../d/e/"
);
});

test("divergent paths, longer to", t => {
t.plan(6);
t.equal(
relative("http://example.com/a/b/c/d", "http://example.com/a/b/c/d/e"),
"e"
);
t.equal(
relative("http://example.com/a/b/c/d", "http://example.com/a/b/c/d/e/f"),
"e/f"
);
t.equal(relative("http://example.com/", "http://example.com/a/b"), "a/b");
t.equal(
relative("http://example.com/a/b/c/d/", "http://example.com/a/b/c/d/e/"),
"e/"
);
t.equal(
relative("http://example.com/a/b/c/d/", "http://example.com/a/b/c/d/e/f/"),
"e/f/"
);
t.equal(relative("http://example.com/", "http://example.com/a/b/"), "a/b/");
});

test("divergent paths, equal length", t => {
t.plan(2);
t.equal(
relative(
"http://example.com/a/b/c/d/e/f",
"http://example.com/a/b/c/g/h/j"
),
"../../g/h/j"
);
t.equal(
relative(
"http://example.com/a/b/c/d/e/f/",
"http://example.com/a/b/c/g/h/j/"
),
"../../../g/h/j/"
);
});

test("identical", t => {
t.plan(2);
t.equal(relative("https://a.com/a", "https://a.com/a"), "");
t.equal(relative("https://a.com/a/", "https://a.com/a/"), "");
});

0 comments on commit c74f79e

Please sign in to comment.