1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-26 16:09:27 -05:00

feat(std/node): first pass at url module (#4700)

This commit is contained in:
Ali Hasani 2020-05-20 19:07:30 +04:30 committed by GitHub
parent 7630326b4c
commit 22da75b8e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 196 additions and 0 deletions

View file

@ -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();
}

View file

@ -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 = "&",

127
std/node/url.ts Normal file
View file

@ -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;
}