From a9a9559740c030567cdf7a7e182d9bee2943571a Mon Sep 17 00:00:00 2001 From: wafuwafu13 Date: Sun, 7 Nov 2021 17:53:03 +0900 Subject: [PATCH 1/3] docs(node/querystring): Add docs --- node/querystring.ts | 265 ++++++++++++++++++++++----------------- node/querystring_test.ts | 26 ---- 2 files changed, 153 insertions(+), 138 deletions(-) diff --git a/node/querystring.ts b/node/querystring.ts index b56ccc5d87bb..4075b4e33753 100644 --- a/node/querystring.ts +++ b/node/querystring.ts @@ -2,7 +2,114 @@ import { Buffer } from "./buffer.ts"; import { ERR_INVALID_URI } from "./_errors.ts"; -export interface ParsedUrlQuery { +/******************** querystring.decode() ********************/ + +/** + * Alias of querystring.parse() + * @deprecated + */ +export const decode = parse; + +/******************** querystring.encode() ********************/ + +/** + * Alias of querystring.stringify() + * @deprecated + */ +export const encode = stringify; + +/******************** querystring.escape(str) ********************/ + +const hexTable = new Array(256); +for (let i = 0; i < 256; ++i) { + hexTable[i] = "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase(); +} + +function encodeStr( + str: string, + noEscapeTable: Int8Array, + 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 ERR_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; +} + +/** + * replaces encodeURIComponent() + * @see https://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.4 + */ +function qsEscape(str: unknown): string { + if (typeof str !== "string") { + if (typeof str === "object") { + str = String(str); + } else { + str += ""; + } + } + return encodeStr(str as string, noEscape, hexTable); +} + +/** + * Performs URL percent-encoding on the given `str` in a manner that is optimized for the specific requirements of URL query strings. + * Used by `querystring.stringify()` and is generally not expected to be used directly. + * It is exported primarily to allow application code to provide a replacement percent-encoding implementation if necessary by assigning `querystring.escape` to an alternative function. + * @deprecated + * @see Tested in `test-querystring-escape.js` + */ +export const escape = qsEscape; + +/******************** querystring.parse(str[, sep[, eq[, options]]]) ********************/ + +interface ParsedUrlQuery { [key: string]: string | string[] | undefined; } @@ -13,10 +120,25 @@ interface ParseOptions { maxKeys?: number; } -export const hexTable = new Array(256); -for (let i = 0; i < 256; ++i) { - hexTable[i] = "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase(); -} +// deno-fmt-ignore +const isHexTable = new Int8Array([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63 + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 64 - 79 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80 - 95 + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 96 - 111 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 112 - 127 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128 ... + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ... 256 +]); function charCodes(str: string): number[] { const ret = new Array(str.length); @@ -56,32 +178,16 @@ function addKeyVal( } } -// deno-fmt-ignore -const isHexTable = new Int8Array([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32 - 47 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63 - 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 64 - 79 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80 - 95 - 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 96 - 111 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 112 - 127 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128 ... - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ... 256 -]); - /** * Parses a URL query string into a collection of key and value pairs. * @param str The URL query string to parse * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'. * @param eq The substring used to delimit keys and values in the query string. Default: '='. * @param options The parse options + * @param options.decodeURIComponent The function to use when decoding percent-encoded characters in the query string. Default: `querystring.unescape()`. + * @param options.maxKeys Specifies the maximum number of keys to parse. Specify `0` to remove key counting limitations. Default: `1000`. + * @deprecated + * @see Tested in test-querystring.js */ export function parse( str: string, @@ -247,6 +353,8 @@ export function parse( return obj; } +/******************** querystring.stringify(obj[, sep[, eq[, options]]]) ********************/ + interface StringifyOptions { /** The function to use when converting URL-unsafe characters to percent-encoding in the query string. */ encodeURIComponent: (string: string) => string; @@ -272,79 +380,6 @@ const noEscape = new Int8Array([ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 112 - 127 ]); -/** - * replaces encodeURIComponent() - * @see https://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.4 - */ -function qsEscape(str: unknown): string { - if (typeof str !== "string") { - if (typeof str === "object") { - str = String(str); - } else { - str += ""; - } - } - return encodeStr(str as string, noEscape, hexTable); -} - -function encodeStr( - str: string, - noEscapeTable: Int8Array, - 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 ERR_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; -} - // deno-lint-ignore no-explicit-any function stringifyPrimitive(v: any): string { if (typeof v === "string") { @@ -395,6 +430,9 @@ function encodeStringified(v: any, encode: (string: string) => string): string { * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'. * @param eq The substring used to delimit keys and values in the query string. Default: '='. * @param options The stringify options + * @param options.encodeURIComponent The function to use when converting URL-unsafe characters to percent-encoding in the query string. Default: `querystring.escape()`. + * @deprecated + * @see Tested in `test-querystring.js` */ export function stringify( // deno-lint-ignore no-explicit-any @@ -444,6 +482,8 @@ export function stringify( return ""; } +/******************** querystring.unescape(str) ********************/ + // deno-fmt-ignore const unhexTable = new Int8Array([ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 - 15 @@ -464,14 +504,6 @@ const unhexTable = new Int8Array([ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 255 ]); -function qsUnescape(s: string): string { - try { - return decodeURIComponent(s); - } catch { - return unescapeBuffer(s).toString(); - } -} - /** * A safe fast alternative to decodeURIComponent */ @@ -517,17 +549,26 @@ export function unescapeBuffer(s: string, decodeSpaces = false): Buffer { return hasHex ? out.slice(0, outIndex) : out; } -/** Alias of querystring.parse() */ -export const decode = parse; -/** Alias of querystring.stringify() */ -export const encode = stringify; +function qsUnescape(s: string): string { + try { + return decodeURIComponent(s); + } catch { + return unescapeBuffer(s).toString(); + } +} + +/** + * Performs decoding of URL percent-encoded characters on the given `str`. + * Used by `querystring.parse()` and is generally not expected to be used directly. + * It is exported primarily to allow application code to provide a replacement decoding implementation if necessary by assigning `querystring.unescape` to an alternative function. + * @deprecated + * @see Tested in `test-querystring-escape.js` + */ export const unescape = qsUnescape; -export const escape = qsEscape; export default { parse, stringify, - hexTable, decode, encode, unescape, diff --git a/node/querystring_test.ts b/node/querystring_test.ts index 70b102b6de9f..d05a75eabd55 100644 --- a/node/querystring_test.ts +++ b/node/querystring_test.ts @@ -17,19 +17,6 @@ Deno.test({ }, }); -Deno.test({ - name: "stringify with escape", - fn() { - assertEquals( - stringify({ - a: "hello!", - b: "こんにちは", - }), - "a=hello%EF%BC%81&b=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF", - ); - }, -}); - Deno.test({ name: "parse", fn() { @@ -41,16 +28,3 @@ Deno.test({ }); }, }); - -Deno.test({ - name: "parse escaped string", - fn() { - assertEquals( - parse("a=hello%EF%BC%81&b=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF"), - { - a: "hello!", - b: "こんにちは", - }, - ); - }, -}); From 7d04d5fd179fc24a4056b9a0515639a6ad2d52a9 Mon Sep 17 00:00:00 2001 From: wafuwafu13 Date: Mon, 8 Nov 2021 17:54:51 +0900 Subject: [PATCH 2/3] docs(node/querystring): Do not group --- node/querystring.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/node/querystring.ts b/node/querystring.ts index 4075b4e33753..aa4edc7f6846 100644 --- a/node/querystring.ts +++ b/node/querystring.ts @@ -2,24 +2,18 @@ import { Buffer } from "./buffer.ts"; import { ERR_INVALID_URI } from "./_errors.ts"; -/******************** querystring.decode() ********************/ - /** * Alias of querystring.parse() * @deprecated */ export const decode = parse; -/******************** querystring.encode() ********************/ - /** * Alias of querystring.stringify() * @deprecated */ export const encode = stringify; -/******************** querystring.escape(str) ********************/ - const hexTable = new Array(256); for (let i = 0; i < 256; ++i) { hexTable[i] = "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase(); @@ -107,8 +101,6 @@ function qsEscape(str: unknown): string { */ export const escape = qsEscape; -/******************** querystring.parse(str[, sep[, eq[, options]]]) ********************/ - interface ParsedUrlQuery { [key: string]: string | string[] | undefined; } @@ -353,8 +345,6 @@ export function parse( return obj; } -/******************** querystring.stringify(obj[, sep[, eq[, options]]]) ********************/ - interface StringifyOptions { /** The function to use when converting URL-unsafe characters to percent-encoding in the query string. */ encodeURIComponent: (string: string) => string; @@ -482,8 +472,6 @@ export function stringify( return ""; } -/******************** querystring.unescape(str) ********************/ - // deno-fmt-ignore const unhexTable = new Int8Array([ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 - 15 From 349b5f10310055c3c205b7f11fb2d560efb6d5a9 Mon Sep 17 00:00:00 2001 From: wafuwafu13 Date: Mon, 8 Nov 2021 17:56:34 +0900 Subject: [PATCH 3/3] docs(node/querystring): Use legacy instead of deprecated --- node/querystring.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/node/querystring.ts b/node/querystring.ts index aa4edc7f6846..86cc4dedba49 100644 --- a/node/querystring.ts +++ b/node/querystring.ts @@ -4,13 +4,13 @@ import { ERR_INVALID_URI } from "./_errors.ts"; /** * Alias of querystring.parse() - * @deprecated + * @legacy */ export const decode = parse; /** * Alias of querystring.stringify() - * @deprecated + * @legacy */ export const encode = stringify; @@ -96,7 +96,7 @@ function qsEscape(str: unknown): string { * Performs URL percent-encoding on the given `str` in a manner that is optimized for the specific requirements of URL query strings. * Used by `querystring.stringify()` and is generally not expected to be used directly. * It is exported primarily to allow application code to provide a replacement percent-encoding implementation if necessary by assigning `querystring.escape` to an alternative function. - * @deprecated + * @legacy * @see Tested in `test-querystring-escape.js` */ export const escape = qsEscape; @@ -178,7 +178,7 @@ function addKeyVal( * @param options The parse options * @param options.decodeURIComponent The function to use when decoding percent-encoded characters in the query string. Default: `querystring.unescape()`. * @param options.maxKeys Specifies the maximum number of keys to parse. Specify `0` to remove key counting limitations. Default: `1000`. - * @deprecated + * @legacy * @see Tested in test-querystring.js */ export function parse( @@ -421,7 +421,7 @@ function encodeStringified(v: any, encode: (string: string) => string): string { * @param eq The substring used to delimit keys and values in the query string. Default: '='. * @param options The stringify options * @param options.encodeURIComponent The function to use when converting URL-unsafe characters to percent-encoding in the query string. Default: `querystring.escape()`. - * @deprecated + * @legacy * @see Tested in `test-querystring.js` */ export function stringify( @@ -549,7 +549,7 @@ function qsUnescape(s: string): string { * Performs decoding of URL percent-encoded characters on the given `str`. * Used by `querystring.parse()` and is generally not expected to be used directly. * It is exported primarily to allow application code to provide a replacement decoding implementation if necessary by assigning `querystring.unescape` to an alternative function. - * @deprecated + * @legacy * @see Tested in `test-querystring-escape.js` */ export const unescape = qsUnescape;