summaryrefslogtreecommitdiff
path: root/std
diff options
context:
space:
mode:
authorAli Hasani <a.hassssani@gmail.com>2020-05-20 19:07:30 +0430
committerGitHub <noreply@github.com>2020-05-20 10:37:30 -0400
commit22da75b8e554f69e552d6312c1ce412cbc6d05bd (patch)
tree9f50b2c6ea709f5d1fb36d29d29e69a6ae5fb7f6 /std
parent7630326b4c5ed32377f34a6668beac626d5156ac (diff)
feat(std/node): first pass at url module (#4700)
Diffstat (limited to 'std')
-rw-r--r--std/node/_utils.ts5
-rw-r--r--std/node/querystring.ts64
-rw-r--r--std/node/url.ts127
3 files changed, 196 insertions, 0 deletions
diff --git a/std/node/_utils.ts b/std/node/_utils.ts
index 352179f4a..b8c4fa00e 100644
--- a/std/node/_utils.ts
+++ b/std/node/_utils.ts
@@ -39,3 +39,8 @@ export function intoCallbackAPIWithIntercept<T1, T2>(
.then((value) => cb && cb(null, interceptor(value)))
.catch((err) => cb && cb(err, null));
}
+
+export function spliceOne(list: string[], index: number): void {
+ for (; index + 1 < list.length; index++) list[index] = list[index + 1];
+ list.pop();
+}
diff --git a/std/node/querystring.ts b/std/node/querystring.ts
index 427183bf0..bed327337 100644
--- a/std/node/querystring.ts
+++ b/std/node/querystring.ts
@@ -4,6 +4,9 @@ interface ParseOptions {
decodeURIComponent?: (string: string) => string;
maxKeys?: number;
}
+export const hexTable = new Array(256);
+for (let i = 0; i < 256; ++i)
+ hexTable[i] = "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase();
export function parse(
str: string,
@@ -43,6 +46,67 @@ interface StringifyOptions {
encodeURIComponent?: (string: string) => string;
}
+export function encodeStr(
+ str: string,
+ noEscapeTable: number[],
+ hexTable: string[]
+): string {
+ const len = str.length;
+ if (len === 0) return "";
+
+ let out = "";
+ let lastPos = 0;
+
+ for (let i = 0; i < len; i++) {
+ let c = str.charCodeAt(i);
+ // ASCII
+ if (c < 0x80) {
+ if (noEscapeTable[c] === 1) continue;
+ if (lastPos < i) out += str.slice(lastPos, i);
+ lastPos = i + 1;
+ out += hexTable[c];
+ continue;
+ }
+
+ if (lastPos < i) out += str.slice(lastPos, i);
+
+ // Multi-byte characters ...
+ if (c < 0x800) {
+ lastPos = i + 1;
+ out += hexTable[0xc0 | (c >> 6)] + hexTable[0x80 | (c & 0x3f)];
+ continue;
+ }
+ if (c < 0xd800 || c >= 0xe000) {
+ lastPos = i + 1;
+ out +=
+ hexTable[0xe0 | (c >> 12)] +
+ hexTable[0x80 | ((c >> 6) & 0x3f)] +
+ hexTable[0x80 | (c & 0x3f)];
+ continue;
+ }
+ // Surrogate pair
+ ++i;
+
+ // This branch should never happen because all URLSearchParams entries
+ // should already be converted to USVString. But, included for
+ // completion's sake anyway.
+ if (i >= len) throw new Deno.errors.InvalidData("invalid URI");
+
+ const c2 = str.charCodeAt(i) & 0x3ff;
+
+ lastPos = i + 1;
+ c = 0x10000 + (((c & 0x3ff) << 10) | c2);
+ out +=
+ hexTable[0xf0 | (c >> 18)] +
+ hexTable[0x80 | ((c >> 12) & 0x3f)] +
+ hexTable[0x80 | ((c >> 6) & 0x3f)] +
+ hexTable[0x80 | (c & 0x3f)];
+ }
+ if (lastPos === 0) return str;
+ if (lastPos < len) return out + str.slice(lastPos);
+ return out;
+}
+
export function stringify(
obj: object,
sep = "&",
diff --git a/std/node/url.ts b/std/node/url.ts
new file mode 100644
index 000000000..bbf0ff5d0
--- /dev/null
+++ b/std/node/url.ts
@@ -0,0 +1,127 @@
+// 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);
+ else if (!(path instanceof URL))
+ throw new Deno.errors.InvalidData(
+ "invalid argument path , must be a string or URL"
+ );
+ if (path.protocol !== "file:")
+ throw new Deno.errors.InvalidData("invalid url scheme");
+ 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(
+ "must not include encoded \\ or / characters"
+ );
+ }
+ }
+ }
+
+ 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
+ const letter = pathname.codePointAt(1) || 0x20;
+ 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(
+ "must not include encoded / characters"
+ );
+ }
+ }
+ }
+ 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
+ )
+ resolved += "/";
+ const outURL = new URL("file://");
+ if (resolved.includes("%")) resolved = resolved.replace(percentRegEx, "%25");
+ // In posix, "/" is a valid character in paths
+ if (!isWindows && resolved.includes("\\"))
+ resolved = resolved.replace(backslashRegEx, "%5C");
+ if (resolved.includes("\n")) resolved = resolved.replace(newlineRegEx, "%0A");
+ if (resolved.includes("\r"))
+ resolved = resolved.replace(carriageReturnRegEx, "%0D");
+ if (resolved.includes("\t")) resolved = resolved.replace(tabRegEx, "%09");
+ outURL.pathname = resolved;
+ return outURL;
+}