2020-05-20 10:37:30 -04:00
|
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
|
|
//
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
// copy of this software and associated documentation files (the
|
|
|
|
// "Software"), to deal in the Software without restriction, including
|
|
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
|
|
// persons to whom the Software is furnished to do so, subject to the
|
|
|
|
// following conditions:
|
|
|
|
//
|
|
|
|
// The above copyright notice and this permission notice shall be included
|
|
|
|
// in all copies or substantial portions of the Software.
|
|
|
|
//
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
import {
|
|
|
|
CHAR_LOWERCASE_A,
|
|
|
|
CHAR_LOWERCASE_Z,
|
|
|
|
CHAR_FORWARD_SLASH,
|
|
|
|
CHAR_BACKWARD_SLASH,
|
|
|
|
} from "../path/_constants.ts";
|
|
|
|
import * as path from "./path.ts";
|
|
|
|
|
|
|
|
const isWindows = Deno.build.os === "windows";
|
|
|
|
|
|
|
|
const forwardSlashRegEx = /\//g;
|
|
|
|
const percentRegEx = /%/g;
|
|
|
|
const backslashRegEx = /\\/g;
|
|
|
|
const newlineRegEx = /\n/g;
|
|
|
|
const carriageReturnRegEx = /\r/g;
|
|
|
|
const tabRegEx = /\t/g;
|
|
|
|
|
|
|
|
export function fileURLToPath(path: string | URL): string {
|
|
|
|
if (typeof path === "string") path = new URL(path);
|
2020-07-14 15:24:17 -04:00
|
|
|
else if (!(path instanceof URL)) {
|
2020-05-20 10:37:30 -04:00
|
|
|
throw new Deno.errors.InvalidData(
|
2020-07-14 15:24:17 -04:00
|
|
|
"invalid argument path , must be a string or URL",
|
2020-05-20 10:37:30 -04:00
|
|
|
);
|
2020-07-14 15:24:17 -04:00
|
|
|
}
|
|
|
|
if (path.protocol !== "file:") {
|
2020-05-20 10:37:30 -04:00
|
|
|
throw new Deno.errors.InvalidData("invalid url scheme");
|
2020-07-14 15:24:17 -04:00
|
|
|
}
|
2020-05-20 10:37:30 -04:00
|
|
|
return isWindows ? getPathFromURLWin(path) : getPathFromURLPosix(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getPathFromURLWin(url: URL): string {
|
|
|
|
const hostname = url.hostname;
|
|
|
|
let pathname = url.pathname;
|
|
|
|
for (let n = 0; n < pathname.length; n++) {
|
|
|
|
if (pathname[n] === "%") {
|
|
|
|
const third = pathname.codePointAt(n + 2) || 0x20;
|
|
|
|
if (
|
|
|
|
(pathname[n + 1] === "2" && third === 102) || // 2f 2F /
|
|
|
|
(pathname[n + 1] === "5" && third === 99)
|
|
|
|
) {
|
|
|
|
// 5c 5C \
|
|
|
|
throw new Deno.errors.InvalidData(
|
2020-07-14 15:24:17 -04:00
|
|
|
"must not include encoded \\ or / characters",
|
2020-05-20 10:37:30 -04:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pathname = pathname.replace(forwardSlashRegEx, "\\");
|
|
|
|
pathname = decodeURIComponent(pathname);
|
|
|
|
if (hostname !== "") {
|
|
|
|
//TODO add support for punycode encodings
|
|
|
|
return `\\\\${hostname}${pathname}`;
|
|
|
|
} else {
|
|
|
|
// Otherwise, it's a local path that requires a drive letter
|
2020-05-23 06:55:15 -04:00
|
|
|
const letter = pathname.codePointAt(1)! | 0x20;
|
2020-05-20 10:37:30 -04:00
|
|
|
const sep = pathname[2];
|
|
|
|
if (
|
|
|
|
letter < CHAR_LOWERCASE_A ||
|
|
|
|
letter > CHAR_LOWERCASE_Z || // a..z A..Z
|
|
|
|
sep !== ":"
|
|
|
|
) {
|
|
|
|
throw new Deno.errors.InvalidData("file url path must be absolute");
|
|
|
|
}
|
|
|
|
return pathname.slice(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getPathFromURLPosix(url: URL): string {
|
|
|
|
if (url.hostname !== "") {
|
|
|
|
throw new Deno.errors.InvalidData("invalid file url hostname");
|
|
|
|
}
|
|
|
|
const pathname = url.pathname;
|
|
|
|
for (let n = 0; n < pathname.length; n++) {
|
|
|
|
if (pathname[n] === "%") {
|
|
|
|
const third = pathname.codePointAt(n + 2) || 0x20;
|
|
|
|
if (pathname[n + 1] === "2" && third === 102) {
|
|
|
|
throw new Deno.errors.InvalidData(
|
2020-07-14 15:24:17 -04:00
|
|
|
"must not include encoded / characters",
|
2020-05-20 10:37:30 -04:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return decodeURIComponent(pathname);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function pathToFileURL(filepath: string): URL {
|
|
|
|
let resolved = path.resolve(filepath);
|
|
|
|
// path.resolve strips trailing slashes so we must add them back
|
|
|
|
const filePathLast = filepath.charCodeAt(filepath.length - 1);
|
|
|
|
if (
|
|
|
|
(filePathLast === CHAR_FORWARD_SLASH ||
|
|
|
|
(isWindows && filePathLast === CHAR_BACKWARD_SLASH)) &&
|
|
|
|
resolved[resolved.length - 1] !== path.sep
|
2020-07-14 15:24:17 -04:00
|
|
|
) {
|
2020-05-20 10:37:30 -04:00
|
|
|
resolved += "/";
|
2020-07-14 15:24:17 -04:00
|
|
|
}
|
2020-05-20 10:37:30 -04:00
|
|
|
const outURL = new URL("file://");
|
|
|
|
if (resolved.includes("%")) resolved = resolved.replace(percentRegEx, "%25");
|
|
|
|
// In posix, "/" is a valid character in paths
|
2020-07-14 15:24:17 -04:00
|
|
|
if (!isWindows && resolved.includes("\\")) {
|
2020-05-20 10:37:30 -04:00
|
|
|
resolved = resolved.replace(backslashRegEx, "%5C");
|
2020-07-14 15:24:17 -04:00
|
|
|
}
|
2020-05-20 10:37:30 -04:00
|
|
|
if (resolved.includes("\n")) resolved = resolved.replace(newlineRegEx, "%0A");
|
2020-07-14 15:24:17 -04:00
|
|
|
if (resolved.includes("\r")) {
|
2020-05-20 10:37:30 -04:00
|
|
|
resolved = resolved.replace(carriageReturnRegEx, "%0D");
|
2020-07-14 15:24:17 -04:00
|
|
|
}
|
2020-05-20 10:37:30 -04:00
|
|
|
if (resolved.includes("\t")) resolved = resolved.replace(tabRegEx, "%09");
|
|
|
|
outURL.pathname = resolved;
|
|
|
|
return outURL;
|
|
|
|
}
|