mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 23:34:47 -05:00
Fix basing in URL constructor (#2867)
This commit is contained in:
parent
49ea932af8
commit
ca00039285
2 changed files with 73 additions and 3 deletions
66
js/url.ts
66
js/url.ts
|
@ -80,6 +80,66 @@ function generateUUID(): string {
|
|||
// Keep it outside of URL to avoid any attempts of access.
|
||||
export const blobURLMap = new Map<string, domTypes.Blob>();
|
||||
|
||||
function isAbsolutePath(path: string): boolean {
|
||||
return path.startsWith("/");
|
||||
}
|
||||
|
||||
// Resolves `.`s and `..`s where possible.
|
||||
// Preserves repeating and trailing `/`s by design.
|
||||
function normalizePath(path: string): string {
|
||||
const isAbsolute = isAbsolutePath(path);
|
||||
path = path.replace(/^\//, "");
|
||||
const pathSegments = path.split("/");
|
||||
|
||||
const newPathSegments: string[] = [];
|
||||
for (let i = 0; i < pathSegments.length; i++) {
|
||||
const previous = newPathSegments[newPathSegments.length - 1];
|
||||
if (
|
||||
pathSegments[i] == ".." &&
|
||||
previous != ".." &&
|
||||
(previous != undefined || isAbsolute)
|
||||
) {
|
||||
newPathSegments.pop();
|
||||
} else if (pathSegments[i] != ".") {
|
||||
newPathSegments.push(pathSegments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
let newPath = newPathSegments.join("/");
|
||||
if (!isAbsolute) {
|
||||
if (newPathSegments.length == 0) {
|
||||
newPath = ".";
|
||||
}
|
||||
} else {
|
||||
newPath = `/${newPath}`;
|
||||
}
|
||||
return newPath;
|
||||
}
|
||||
|
||||
// Standard URL basing logic, applied to paths.
|
||||
function resolvePathFromBase(path: string, basePath: string): string {
|
||||
const normalizedPath = normalizePath(path);
|
||||
if (isAbsolutePath(normalizedPath)) {
|
||||
return normalizedPath;
|
||||
}
|
||||
const normalizedBasePath = normalizePath(basePath);
|
||||
if (!isAbsolutePath(normalizedBasePath)) {
|
||||
throw new TypeError("Base path must be absolute.");
|
||||
}
|
||||
|
||||
// Special case.
|
||||
if (path == "") {
|
||||
return normalizedBasePath;
|
||||
}
|
||||
|
||||
// Remove everything after the last `/` in `normalizedBasePath`.
|
||||
const prefix = normalizedBasePath.replace(/[^\/]*$/, "");
|
||||
// If `normalizedPath` ends with `.` or `..`, add a trailing space.
|
||||
const suffix = normalizedPath.replace(/(?<=(^|\/)(\.|\.\.))$/, "/");
|
||||
|
||||
return normalizePath(prefix + suffix);
|
||||
}
|
||||
|
||||
export class URL {
|
||||
private _parts: URLParts;
|
||||
private _searchParams!: urlSearchParams.URLSearchParams;
|
||||
|
@ -254,7 +314,7 @@ export class URL {
|
|||
let baseParts: URLParts | undefined;
|
||||
if (base) {
|
||||
baseParts = typeof base === "string" ? parse(base) : base._parts;
|
||||
if (!baseParts) {
|
||||
if (!baseParts || baseParts.protocol == "") {
|
||||
throw new TypeError("Invalid base URL.");
|
||||
}
|
||||
}
|
||||
|
@ -273,8 +333,8 @@ export class URL {
|
|||
password: baseParts.password,
|
||||
hostname: baseParts.hostname,
|
||||
port: baseParts.port,
|
||||
path: urlParts.path || baseParts.path,
|
||||
query: urlParts.query || baseParts.query,
|
||||
path: resolvePathFromBase(urlParts.path, baseParts.path),
|
||||
query: urlParts.query,
|
||||
hash: urlParts.hash
|
||||
};
|
||||
} else {
|
||||
|
|
|
@ -142,6 +142,16 @@ test(function urlBaseString(): void {
|
|||
assertEquals(url.href, "https://foo:bar@baz.qat:8000/foo/bar?baz=foo#qux");
|
||||
});
|
||||
|
||||
test(function urlRelativeWithBase(): void {
|
||||
assertEquals(new URL("", "file:///a/a/a").href, "file:///a/a/a");
|
||||
assertEquals(new URL(".", "file:///a/a/a").href, "file:///a/a/");
|
||||
assertEquals(new URL("..", "file:///a/a/a").href, "file:///a/");
|
||||
assertEquals(new URL("b", "file:///a/a/a").href, "file:///a/a/b");
|
||||
assertEquals(new URL("b", "file:///a/a/a/").href, "file:///a/a/a/b");
|
||||
assertEquals(new URL("b/", "file:///a/a/a").href, "file:///a/a/b/");
|
||||
assertEquals(new URL("../b", "file:///a/a/a").href, "file:///a/b");
|
||||
});
|
||||
|
||||
test(function deletingAllParamsRemovesQuestionMarkFromURL(): void {
|
||||
const url = new URL("http://example.com/?param1¶m2");
|
||||
url.searchParams.delete("param1");
|
||||
|
|
Loading…
Reference in a new issue