Skip to content

[v8.x backport] url: expose the WHATWG URL API globally #20306

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,6 @@ A Node.js API function was called with an incompatible `this` value.
Example:

```js
const { URLSearchParams } = require('url');
const urlSearchParams = new URLSearchParams('foo=bar&baz=new');

const buf = Buffer.alloc(1);
Expand Down
3 changes: 1 addition & 2 deletions doc/api/esm.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ rules with only JS file extension and Node builtin modules support could
be written:

```js
import url from 'url';
import path from 'path';
import process from 'process';
import Module from 'module';
Expand All @@ -165,7 +164,7 @@ export function resolve(specifier, parentModuleURL/*, defaultResolve */) {
throw new Error(
`imports must begin with '/', './', or '../'; '${specifier}' does not`);
}
const resolved = new url.URL(specifier, parentModuleURL);
const resolved = new URL(specifier, parentModuleURL);
const ext = path.extname(resolved.pathname);
if (!JS_EXTENSIONS.has(ext)) {
throw new Error(
Expand Down
1 change: 0 additions & 1 deletion doc/api/fs.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ are supported.

```js
const fs = require('fs');
const { URL } = require('url');
const fileUrl = new URL('file:///tmp/hello');

fs.readFileSync(fileUrl);
Expand Down
20 changes: 20 additions & 0 deletions doc/api/globals.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,24 @@ added: v0.0.1

[`setTimeout`] is described in the [timers][] section.

## URL
<!-- YAML
added: REPLACEME
-->

<!-- type=global -->

The WHATWG `URL` class. See the [`URL`][] section.

## URLSearchParams
<!-- YAML
added: REPLACEME
-->

<!-- type=global -->

The WHATWG `URLSearchParams` class. See the [`URLSearchParams`][] section.

[`__dirname`]: modules.html#modules_dirname
[`__filename`]: modules.html#modules_filename
[`clearImmediate`]: timers.html#timers_clearimmediate_immediate
Expand All @@ -151,6 +169,8 @@ added: v0.0.1
[`setImmediate`]: timers.html#timers_setimmediate_callback_args
[`setInterval`]: timers.html#timers_setinterval_callback_delay_args
[`setTimeout`]: timers.html#timers_settimeout_callback_delay_args
[`URL`]: url.html#url_class_url
[`URLSearchParams`]: url.html#url_class_urlsearchparams
[buffer section]: buffer.html
[built-in objects]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
[module system documentation]: modules.html
Expand Down
2 changes: 0 additions & 2 deletions doc/api/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -1871,8 +1871,6 @@ There are a few special headers that should be noted.
Example using a [`URL`][] as `options`:

```js
const { URL } = require('url');

const options = new URL('http://abc:xyz@example.com');

const req = http.request(options, (res) => {
Expand Down
1 change: 0 additions & 1 deletion doc/api/http2.md
Original file line number Diff line number Diff line change
Expand Up @@ -1958,7 +1958,6 @@ An HTTP/2 CONNECT proxy:
```js
const http2 = require('http2');
const net = require('net');
const { URL } = require('url');

const proxy = http2.createServer();
proxy.on('stream', (stream, headers) => {
Expand Down
2 changes: 0 additions & 2 deletions doc/api/https.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,6 @@ const req = https.request(options, (res) => {
Example using a [`URL`][] as `options`:

```js
const { URL } = require('url');

const options = new URL('https://abc:xyz@example.com');

const req = https.request(options, (res) => {
Expand Down
48 changes: 12 additions & 36 deletions doc/api/url.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,10 @@ properties of a WHATWG `URL` object.
Parsing the URL string using the WHATWG API:

```js
const { URL } = require('url');
const myURL =
new URL('https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash');
```

*Note*: In Web Browsers, the WHATWG `URL` class is a global that is always
available. In Node.js, however, the `URL` class must be accessed via
`require('url').URL`.

Parsing the URL string using the Legacy API:

```js
Expand All @@ -75,14 +70,19 @@ const myURL =
```

## The WHATWG URL API

### Class: URL
<!-- YAML
added: v7.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/18281
description: The class is now available on the global object.
-->

### Class: URL

Browser-compatible `URL` class, implemented by following the WHATWG URL
Standard. [Examples of parsed URLs][] may be found in the Standard itself.
The `URL` class is also available on the global object.

*Note*: In accordance with browser conventions, all properties of `URL` objects
are implemented as getters and setters on the class prototype, rather than as
Expand All @@ -101,7 +101,6 @@ Creates a new `URL` object by parsing the `input` relative to the `base`. If
`base` is passed as a string, it will be parsed equivalent to `new URL(base)`.

```js
const { URL } = require('url');
const myURL = new URL('/foo', 'https://example.org/');
// https://example.org/foo
```
Expand All @@ -111,7 +110,6 @@ that an effort will be made to coerce the given values into strings. For
instance:

```js
const { URL } = require('url');
const myURL = new URL({ toString: () => 'https://example.org/' });
// https://example.org/
```
Expand All @@ -120,7 +118,6 @@ Unicode characters appearing within the hostname of `input` will be
automatically converted to ASCII using the [Punycode][] algorithm.

```js
const { URL } = require('url');
const myURL = new URL('https://你好你好');
// https://xn--6qqa088eba/
```
Expand All @@ -135,7 +132,6 @@ with [ICU][] enabled. If not, the domain names are passed through unchanged.
Gets and sets the fragment portion of the URL.

```js
const { URL } = require('url');
const myURL = new URL('https://example.org/foo#bar');
console.log(myURL.hash);
// Prints #bar
Expand All @@ -157,7 +153,6 @@ percent-encode may vary somewhat from what the [`url.parse()`][] and
Gets and sets the host portion of the URL.

```js
const { URL } = require('url');
const myURL = new URL('https://example.org:81/foo');
console.log(myURL.host);
// Prints example.org:81
Expand All @@ -178,7 +173,6 @@ Gets and sets the hostname portion of the URL. The key difference between
port.

```js
const { URL } = require('url');
const myURL = new URL('https://example.org:81/foo');
console.log(myURL.hostname);
// Prints example.org
Expand All @@ -197,7 +191,6 @@ Invalid hostname values assigned to the `hostname` property are ignored.
Gets and sets the serialized URL.

```js
const { URL } = require('url');
const myURL = new URL('https://example.org/foo');
console.log(myURL.href);
// Prints https://example.org/foo
Expand All @@ -224,14 +217,12 @@ will be thrown.
Gets the read-only serialization of the URL's origin.

```js
const { URL } = require('url');
const myURL = new URL('https://example.org/foo/bar?baz');
console.log(myURL.origin);
// Prints https://example.org
```

```js
const { URL } = require('url');
const idnURL = new URL('https://你好你好');
console.log(idnURL.origin);
// Prints https://xn--6qqa088eba
Expand All @@ -247,7 +238,6 @@ console.log(idnURL.hostname);
Gets and sets the password portion of the URL.

```js
const { URL } = require('url');
const myURL = new URL('https://abc:xyz@example.com');
console.log(myURL.password);
// Prints xyz
Expand All @@ -269,7 +259,6 @@ percent-encode may vary somewhat from what the [`url.parse()`][] and
Gets and sets the path portion of the URL.

```js
const { URL } = require('url');
const myURL = new URL('https://example.org/abc/xyz?123');
console.log(myURL.pathname);
// Prints /abc/xyz
Expand All @@ -291,7 +280,6 @@ to percent-encode may vary somewhat from what the [`url.parse()`][] and
Gets and sets the port portion of the URL.

```js
const { URL } = require('url');
const myURL = new URL('https://example.org:8888');
console.log(myURL.port);
// Prints 8888
Expand Down Expand Up @@ -347,7 +335,6 @@ lies outside the range denoted above, it is ignored.
Gets and sets the protocol portion of the URL.

```js
const { URL } = require('url');
const myURL = new URL('https://example.org');
console.log(myURL.protocol);
// Prints https:
Expand All @@ -366,7 +353,6 @@ Invalid URL protocol values assigned to the `protocol` property are ignored.
Gets and sets the serialized query portion of the URL.

```js
const { URL } = require('url');
const myURL = new URL('https://example.org/abc?123');
console.log(myURL.search);
// Prints ?123
Expand Down Expand Up @@ -397,7 +383,6 @@ documentation for details.
Gets and sets the username portion of the URL.

```js
const { URL } = require('url');
const myURL = new URL('https://abc:xyz@example.com');
console.log(myURL.username);
// Prints abc
Expand Down Expand Up @@ -435,7 +420,6 @@ This method is automatically called when an `URL` object is serialized
with [`JSON.stringify()`][].

```js
const { URL } = require('url');
const myURLs = [
new URL('https://www.example.com'),
new URL('https://test.example.org')
Expand All @@ -447,20 +431,23 @@ console.log(JSON.stringify(myURLs));
### Class: URLSearchParams
<!-- YAML
added: v7.5.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/18281
description: The class is now available on the global object.
-->

The `URLSearchParams` API provides read and write access to the query of a
`URL`. The `URLSearchParams` class can also be used standalone with one of the
four following constructors.
The `URLSearchParams` class is also available on the global object.

The WHATWG `URLSearchParams` interface and the [`querystring`][] module have
similar purpose, but the purpose of the [`querystring`][] module is more
general, as it allows the customization of delimiter characters (`&` and `=`).
On the other hand, this API is designed purely for URL query strings.

```js
const { URL, URLSearchParams } = require('url');

const myURL = new URL('https://example.org/?abc=123');
console.log(myURL.searchParams.get('abc'));
// Prints 123
Expand Down Expand Up @@ -505,7 +492,6 @@ Parse the `string` as a query string, and use it to instantiate a new
`URLSearchParams` object. A leading `'?'`, if present, is ignored.

```js
const { URLSearchParams } = require('url');
let params;

params = new URLSearchParams('user=abc&query=xyz');
Expand Down Expand Up @@ -534,7 +520,6 @@ values are not allowed. Arrays are stringified using [`array.toString()`][],
which simply joins all array elements with commas.

```js
const { URLSearchParams } = require('url');
const params = new URLSearchParams({
user: 'abc',
query: ['first', 'second']
Expand Down Expand Up @@ -562,7 +547,6 @@ themselves be any iterable object.
Duplicate keys are allowed.

```js
const { URLSearchParams } = require('url');
let params;

// Using an array
Expand Down Expand Up @@ -631,7 +615,6 @@ Alias for [`urlSearchParams[@@iterator]()`][`urlSearchParams@@iterator()`].
Iterates over each name-value pair in the query and invokes the given function.

```js
const { URL } = require('url');
const myURL = new URL('https://example.org/?a=b&c=d');
myURL.searchParams.forEach((value, name, searchParams) => {
console.log(name, value, myURL.searchParams === searchParams);
Expand Down Expand Up @@ -672,7 +655,6 @@ Returns `true` if there is at least one name-value pair whose name is `name`.
Returns an ES6 Iterator over the names of each name-value pair.

```js
const { URLSearchParams } = require('url');
const params = new URLSearchParams('foo=bar&foo=baz');
for (const name of params.keys()) {
console.log(name);
Expand All @@ -693,8 +675,6 @@ set the first such pair's value to `value` and remove all others. If not,
append the name-value pair to the query string.

```js
const { URLSearchParams } = require('url');

const params = new URLSearchParams();
params.append('foo', 'bar');
params.append('foo', 'baz');
Expand All @@ -720,7 +700,6 @@ with the same name is preserved.
This method can be used, in particular, to increase cache hits.

```js
const { URLSearchParams } = require('url');
const params = new URLSearchParams('query[]=abc&type=search&query[]=123');
params.sort();
console.log(params.toString());
Expand Down Expand Up @@ -751,7 +730,6 @@ is the `name`, the second item of the Array is the `value`.
Alias for [`urlSearchParams.entries()`][].

```js
const { URLSearchParams } = require('url');
const params = new URLSearchParams('foo=bar&xyz=baz');
for (const [name, value] of params) {
console.log(name, value);
Expand Down Expand Up @@ -835,7 +813,6 @@ of the output.
For example:

```js
const { URL } = require('url');
const myURL = new URL('https://a:b@你好你好?abc#foo');

console.log(myURL.href);
Expand Down Expand Up @@ -1132,7 +1109,6 @@ using the [Punycode][] algorithm. Note, however, that a hostname *may* contain
*both* Punycode encoded and percent-encoded characters. For example:

```js
const { URL } = require('url');
const myURL = new URL('https://%CF%80.com/foo');
console.log(myURL.href);
// Prints https://xn--1xa.com/foo
Expand Down
19 changes: 19 additions & 0 deletions lib/internal/bootstrap_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
if (browserGlobals) {
setupGlobalTimeouts();
setupGlobalConsole();
setupGlobalURL();
}

// Ensure setURLConstructor() is called before the native
Expand Down Expand Up @@ -319,6 +320,24 @@
setupInspector(originalConsole, wrappedConsole, Module);
}

function setupGlobalURL() {
const { URL, URLSearchParams } = NativeModule.require('internal/url');
Object.defineProperties(global, {
URL: {
value: URL,
writable: true,
configurable: true,
enumerable: false
},
URLSearchParams: {
value: URLSearchParams,
writable: true,
configurable: true,
enumerable: false
}
});
}

function setupInspector(originalConsole, wrappedConsole, Module) {
if (!process.config.variables.v8_enable_inspector) {
return;
Expand Down
Loading