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.
|
// Keep it outside of URL to avoid any attempts of access.
|
||||||
export const blobURLMap = new Map<string, domTypes.Blob>();
|
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 {
|
export class URL {
|
||||||
private _parts: URLParts;
|
private _parts: URLParts;
|
||||||
private _searchParams!: urlSearchParams.URLSearchParams;
|
private _searchParams!: urlSearchParams.URLSearchParams;
|
||||||
|
@ -254,7 +314,7 @@ export class URL {
|
||||||
let baseParts: URLParts | undefined;
|
let baseParts: URLParts | undefined;
|
||||||
if (base) {
|
if (base) {
|
||||||
baseParts = typeof base === "string" ? parse(base) : base._parts;
|
baseParts = typeof base === "string" ? parse(base) : base._parts;
|
||||||
if (!baseParts) {
|
if (!baseParts || baseParts.protocol == "") {
|
||||||
throw new TypeError("Invalid base URL.");
|
throw new TypeError("Invalid base URL.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,8 +333,8 @@ export class URL {
|
||||||
password: baseParts.password,
|
password: baseParts.password,
|
||||||
hostname: baseParts.hostname,
|
hostname: baseParts.hostname,
|
||||||
port: baseParts.port,
|
port: baseParts.port,
|
||||||
path: urlParts.path || baseParts.path,
|
path: resolvePathFromBase(urlParts.path, baseParts.path),
|
||||||
query: urlParts.query || baseParts.query,
|
query: urlParts.query,
|
||||||
hash: urlParts.hash
|
hash: urlParts.hash
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -142,6 +142,16 @@ test(function urlBaseString(): void {
|
||||||
assertEquals(url.href, "https://foo:bar@baz.qat:8000/foo/bar?baz=foo#qux");
|
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 {
|
test(function deletingAllParamsRemovesQuestionMarkFromURL(): void {
|
||||||
const url = new URL("http://example.com/?param1¶m2");
|
const url = new URL("http://example.com/?param1¶m2");
|
||||||
url.searchParams.delete("param1");
|
url.searchParams.delete("param1");
|
||||||
|
|
Loading…
Reference in a new issue