Skip to content

Commit

Permalink
init url module
Browse files Browse the repository at this point in the history
  • Loading branch information
imaitland committed Mar 25, 2024
1 parent a011bf6 commit eafa5b7
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 66 deletions.
72 changes: 72 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,78 @@ _Also available globally_

[clearTimeout](https://nodejs.org/api/timers.html#cleartimeouttimeout)

## url
```typescript
export class URL {
constructor(input: string, base?: string | URL);

hash: string;
host: string;
hostname: string;
href: string;
origin: string;
password: string;
pathname: string;
port: string;
protocol: string;
search: string;
searchParams: URLSearchParams;
username: string;

canParse(input: string, base?: string): boolean;
toString(): string;
}
```

### TODO, URL see tracking tickets:
```typescript
// Additional utilities in the URL module
export function domainToASCII(domain: string): string;

export function domainToUnicode(domain: string): string;

export function fileURLToPath(url: string | URL): string;

export function pathToFileURL(path: string): URL;

export function urlToHttpOptions(url: URL): {
protocol?: string;
hostname?: string;
port?: string;
path?: string;
};
```

## URLSearchParams
```typescript
export class URLSearchParams {
constructor(init?: string | string[][] | Record<string, string> | URLSearchParams);

// Methods
append(name: string, value: string): void;
delete(name: string): void;
get(name: string): string | null;
getAll(name: string): string[];
has(name: string): boolean;
set(name: string, value: string): void;
sort(): void;

[Symbol.iterator](): IterableIterator<[string, string]>;
entries(): IterableIterator<[string, string]>;
values(): IterableIterator<string>;

toString(): string;
}
```


### TODO, URLSearchParams see tracking tickets:

```typescript
URLSearchParams.sort(): void;
URLSearchParams.keys(): IterableIterator<string>;
```

## util

> [!IMPORTANT]
Expand Down
3 changes: 2 additions & 1 deletion build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const ES_BUILD_OPTIONS = {
"buffer",
"xml",
"net",
"url"
],
};

Expand Down Expand Up @@ -570,7 +571,7 @@ async function buildLibrary() {
await esbuild.build({
...defaultLibEsBuildOption,
entryPoints: testEntryPoints,
external: [...ES_BUILD_OPTIONS.external, "@aws-sdk", "@smithy", "uuid"],
external: [...ES_BUILD_OPTIONS.external, "@aws-sdk", "@smithy"],
});
}

Expand Down
Binary file added llrt
Binary file not shown.
4 changes: 2 additions & 2 deletions src/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ mod fetch;
mod headers;
mod request;
mod response;
mod url;
mod url_search_params;
pub mod url;
pub mod url_search_params;

use rquickjs::{Class, Ctx, Result};

Expand Down
34 changes: 28 additions & 6 deletions src/http/url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,36 @@ impl<'js> URL<'js> {
}

#[qjs(static)]
pub fn can_parse(input: Value<'js>) -> bool {
if input.is_string() {
match input.get::<String>() {
Ok(string_val) => Url::parse(&string_val).is_ok(),
Err(_) => false,
pub fn can_parse(ctx: Ctx<'js>, input: Value<'js>, base: Opt<Value<'js>>) -> bool {
if let Some(base) = base.0 {
let base_string = match get_string(&ctx, base) {
Ok(s) => s,
Err(_) => return false,
};
let path_string = match get_string(&ctx, input) {
Ok(s) => s,
Err(_) => return false,
};

match base_string.parse::<Url>() {
Ok(base_url) => {
match base_url.join(&path_string) {
Ok(_) => true, // Joining was successful
Err(_) => false, // There was a parsing error
}
}
Err(_) => false, // Base URL parsing failed
}
} else {
false
// Handle the case where base is not provided
if input.is_string() {
match input.get::<String>() {
Ok(string_val) => Url::parse(&string_val).is_ok(),
Err(_) => false,
}
} else {
false
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ mod security;
mod stream;
mod test_utils;
mod timers;
mod url;
mod utils;
mod uuid;
mod vm;
Expand Down
35 changes: 35 additions & 0 deletions src/url.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use rquickjs::{
module::{Declarations, Exports, ModuleDef},
Class, Ctx, Result,
};

use crate::{
http::{url::URL, url_search_params::URLSearchParams},
module::export_default,
};
pub struct UrlModule;

impl ModuleDef for UrlModule {
fn declare(declare: &mut Declarations) -> Result<()> {
declare.declare("URL")?;
declare.declare("URLSearchParams")?;
declare.declare("default")?;
Ok(())
}

fn evaluate<'js>(ctx: &Ctx<'js>, exports: &mut Exports<'js>) -> Result<()> {
let url_constructor =
Class::<URL>::create_constructor(ctx)?.expect("Could note create URL constructor");

let url_search_params_constructor = Class::<URLSearchParams>::create_constructor(ctx)?
.expect("Could note create URLSearchParams constructor");

export_default(ctx, exports, |default| {
default.set("URL", url_constructor.clone())?;
default.set("URLSearchParams", url_search_params_constructor.clone())?;
Ok(())
})
}
}
4 changes: 3 additions & 1 deletion src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ use crate::{
path::{dirname, join_path, resolve_path, PathModule},
process::ProcessModule,
timers::TimersModule,
url::UrlModule,
utils::{
class::get_class_name,
clone::structured_clone,
Expand Down Expand Up @@ -128,7 +129,8 @@ create_modules!(
"child_process" => ChildProcessModule,
"util" => UtilModule,
"uuid" => UuidModule,
"process" => ProcessModule
"process" => ProcessModule,
"url" => UrlModule
);

struct ModuleInfo<T: ModuleDef> {
Expand Down
57 changes: 56 additions & 1 deletion tests/unit/http.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,55 @@
import * as url from "url";

describe("url module import syntax works", () => {
it("global URL and imported URL are equal", () => {
const testUrl = "https://www.example.com";
const moduleUrl = new url.URL(testUrl);
const globalUrl = new URL(testUrl)
expect(moduleUrl).toEqual(globalUrl);
})
it("global URLSearchParams and imported URLSearchParams are equal", () => {
const testUrl = "https://www.example.com";
const moduleUrl = new url.URL(testUrl);
const globalUrl = new URL(testUrl)
expect(moduleUrl).toEqual(globalUrl);
})
describe("import { URL } from 'url';", () => {
it("should parse a url hostname", () => {
const test_url = new url.URL("https://www.example.com");
expect(test_url.protocol).toEqual("https:");
expect(test_url.host).toEqual("www.example.com");
expect(test_url.hostname).toEqual("www.example.com");
});
it("toString method works", () => {
const test_url = new url.URL("/base", "https://www.example.com");
expect(test_url.toString()).toEqual("https://www.example.com/base");
});
it("canParse method works", () => {
const valid_can_parse = url.URL.canParse("https://www.example.com");
const invalid_can_parse = url.URL.canParse("not_valid");
expect(valid_can_parse).toEqual(true);
expect(invalid_can_parse).toEqual(false);
expect(url.URL.canParse('/foo', 'https://example.org/')).toEqual(true)
});
});

describe("import { URLSearchParams } from 'url';", () => {
it("supports URLSearchParams basic API", () => {
const paramsString = "topic=api&a=1&a=2&a=3";
const searchParams = new url.URLSearchParams(paramsString);
searchParams.append("foo", "bar")
expect(searchParams.has("topic")).toBeTruthy();
expect(searchParams.has("foo")).toBeTruthy();
searchParams.delete("foo")
expect(searchParams.has("foo")).toBeFalsy();
expect(searchParams.get("topic")).toEqual("api");
expect(searchParams.getAll("a")).toEqual(["1", "2", "3"]);
searchParams.set("topic", "node");
expect(searchParams.get("topic")).toEqual("node");
});
});
});

describe("Headers", () => {
it("should construct a new Headers object with the provided headers", () => {
const headers = { "content-type": "application/json" };
Expand Down Expand Up @@ -247,12 +299,15 @@ describe("URL class", () => {
expect(url.username).toEqual("anonymous")
expect(url.password).toEqual("flabada")
});
it("should provide can_parse util", () => {
it("should provide canParse util", () => {
const valid_url = "https://www.example.com/";
const invalid_url = "not_a_valid_url";
expect(URL.canParse(valid_url)).toEqual(true)
expect(URL.canParse(invalid_url)).toEqual(false)
});
it("canParse works for relative urls", () => {
expect(URL.canParse('/foo', 'https://example.org/')).toEqual(true)
});
});

describe("URLSearchParams class", () => {
Expand Down
59 changes: 4 additions & 55 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2478,17 +2478,6 @@ arg@^4.1.0:
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==

assert@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd"
integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==
dependencies:
call-bind "^1.0.2"
is-nan "^1.3.2"
object-is "^1.1.5"
object.assign "^4.1.4"
util "^0.12.5"

assertion-error@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
Expand Down Expand Up @@ -2599,7 +2588,7 @@ cac@^6.7.14:
resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959"
integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==

call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5:
call-bind@^1.0.2, call-bind@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.6.tgz#6c46675fc7a5e9de82d75a233d586c8b7ac0d931"
integrity sha512-Mj50FLHtlsoVfRfnHaZvyrooHcrlceNZdL/QBvJJVd9Ta55qCQK0gs4ss2oZDeV9zFCs6ewzYgVE5yfVmfFpVg==
Expand Down Expand Up @@ -2723,7 +2712,7 @@ deep-eql@^4.1.3:
dependencies:
type-detect "^4.0.0"

define-data-property@^1.0.1, define-data-property@^1.1.2:
define-data-property@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.2.tgz#f3c33b4f0102360cd7c0f5f28700f5678510b63a"
integrity sha512-SRtsSqsDbgpJBbW3pABMCOt6rQyeM8s8RiyeSN8jYG8sYmt/kGJejbydttUsnDs1tadr19tvhT4ShwMyoqAm4g==
Expand All @@ -2733,15 +2722,6 @@ define-data-property@^1.0.1, define-data-property@^1.1.2:
gopd "^1.0.1"
has-property-descriptors "^1.0.1"

define-properties@^1.1.3, define-properties@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c"
integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==
dependencies:
define-data-property "^1.0.1"
has-property-descriptors "^1.0.0"
object-keys "^1.1.1"

detect-libc@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
Expand Down Expand Up @@ -3021,7 +3001,7 @@ has-flag@^4.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==

has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1:
has-property-descriptors@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340"
integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==
Expand Down Expand Up @@ -3107,14 +3087,6 @@ is-generator-function@^1.0.7:
dependencies:
has-tostringtag "^1.0.0"

is-nan@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d"
integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==
dependencies:
call-bind "^1.0.0"
define-properties "^1.1.3"

is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
Expand Down Expand Up @@ -3446,29 +3418,6 @@ npm-run-path@^5.1.0:
dependencies:
path-key "^4.0.0"

object-is@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac"
integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==
dependencies:
call-bind "^1.0.2"
define-properties "^1.1.3"

object-keys@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==

object.assign@^4.1.4:
version "4.1.5"
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0"
integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==
dependencies:
call-bind "^1.0.5"
define-properties "^1.2.1"
has-symbols "^1.0.3"
object-keys "^1.1.1"

obliterator@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-1.6.1.tgz#dea03e8ab821f6c4d96a299e17aef6a3af994ef3"
Expand Down Expand Up @@ -3974,7 +3923,7 @@ util-deprecate@^1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==

util@^0.12.4, util@^0.12.5:
util@^0.12.4:
version "0.12.5"
resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc"
integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==
Expand Down

0 comments on commit eafa5b7

Please sign in to comment.