Skip to content

Commit

Permalink
Add node querystring polyfill (denoland#4370)
Browse files Browse the repository at this point in the history
  • Loading branch information
crowlKats committed Mar 14, 2020
1 parent 92bbce0 commit 9648d3d
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 1 deletion.
2 changes: 1 addition & 1 deletion std/node/README.md
Expand Up @@ -27,7 +27,7 @@ deno standard library as it's a compatiblity module.
- [x] path
- [ ] perf_hooks
- [x] process _partly_
- [ ] querystring
- [x] querystring
- [ ] readline
- [ ] repl
- [ ] stream
Expand Down
5 changes: 5 additions & 0 deletions std/node/module.ts
Expand Up @@ -27,6 +27,7 @@ import * as nodePath from "./path.ts";
import * as nodeTimers from "./timers.ts";
import * as nodeOs from "./os.ts";
import * as nodeEvents from "./events.ts";
import * as nodeQueryString from "./querystring.ts";

import * as path from "../path/mod.ts";
import { assert } from "../testing/asserts.ts";
Expand Down Expand Up @@ -593,6 +594,10 @@ nativeModulePolyfill.set("os", createNativeModule("os", nodeOs));
nativeModulePolyfill.set("path", createNativeModule("path", nodePath));
nativeModulePolyfill.set("timers", createNativeModule("timers", nodeTimers));
nativeModulePolyfill.set("util", createNativeModule("util", nodeUtil));
nativeModulePolyfill.set(
"querystring",
createNativeModule("querystring", nodeQueryString)
);

function loadNativeModule(
_filename: string,
Expand Down
70 changes: 70 additions & 0 deletions std/node/querystring.ts
@@ -0,0 +1,70 @@
interface ParseOptions {
decodeURIComponent?: (string: string) => string;
maxKeys?: number;
}

export function parse(
str: string,
sep = "&",
eq = "=",
{ decodeURIComponent = unescape, maxKeys = 1000 }: ParseOptions = {}
): { [key: string]: string[] | string } {
const entries = str
.split(sep)
.map(entry => entry.split(eq).map(decodeURIComponent));
const final: { [key: string]: string[] | string } = {};

let i = 0;
while (true) {
if ((Object.keys(final).length === maxKeys && !!maxKeys) || !entries[i]) {
break;
}

const [key, val] = entries[i];
if (final[key]) {
if (Array.isArray(final[key])) {
(final[key] as string[]).push(val);
} else {
final[key] = [final[key] as string, val];
}
} else {
final[key] = val;
}

i++;
}

return final;
}

interface StringifyOptions {
encodeURIComponent?: (string: string) => string;
}

export function stringify(
obj: object,
sep = "&",
eq = "=",
{ encodeURIComponent = escape }: StringifyOptions = {}
): string {
const final = [];

for (const entry of Object.entries(obj)) {
if (Array.isArray(entry[1])) {
for (const val of entry[1]) {
final.push(encodeURIComponent(entry[0]) + eq + encodeURIComponent(val));
}
} else if (typeof entry[1] !== "object" && entry[1] !== undefined) {
final.push(entry.map(encodeURIComponent).join(eq));
} else {
final.push(encodeURIComponent(entry[0]) + eq);
}
}

return final.join(sep);
}

export const decode = parse;
export const encode = stringify;
export const unescape = decodeURIComponent;
export const escape = encodeURIComponent;
30 changes: 30 additions & 0 deletions std/node/querystring_test.ts
@@ -0,0 +1,30 @@
const { test } = Deno;
import { assertEquals } from "../testing/asserts.ts";
import { stringify, parse } from "./querystring.ts";

test({
name: "stringify",
fn() {
assertEquals(
stringify({
a: "hello",
b: 5,
c: true,
d: ["foo", "bar"]
}),
"a=hello&b=5&c=true&d=foo&d=bar"
);
}
});

test({
name: "parse",
fn() {
assertEquals(parse("a=hello&b=5&c=true&d=foo&d=bar"), {
a: "hello",
b: "5",
c: "true",
d: ["foo", "bar"]
});
}
});

0 comments on commit 9648d3d

Please sign in to comment.