whatwg-url 6.4.0 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  whatwg-url is a full implementation of the WHATWG [URL Standard](https://url.spec.whatwg.org/). It can be used standalone, but it also exposes a lot of the internal algorithms that are useful for integrating a URL parser into a project like [jsdom](https://github.com/tmpvar/jsdom).
4
4
 
5
- ## Current status
5
+ ## Specification conformance
6
6
 
7
- whatwg-url is currently up to date with the URL spec up to commit [7a3c69f](https://github.com/whatwg/url/commit/7a3c69f8a1583b33e730c3fea85141a618e7c697).
7
+ whatwg-url is currently up to date with the URL spec up to commit [7ae1c69](https://github.com/whatwg/url/commit/7ae1c691c96f0d82fafa24c33aa1e8df9ffbf2bc).
8
+
9
+ For `file:` URLs, whose [origin is left unspecified](https://url.spec.whatwg.org/#concept-url-origin), whatwg-url chooses to use a new opaque origin (which serializes to `"null"`).
8
10
 
9
11
  ## API
10
12
 
@@ -21,7 +23,7 @@ The following methods are exported for use by places like jsdom that need to imp
21
23
  - [URL serializer](https://url.spec.whatwg.org/#concept-url-serializer): `serializeURL(urlRecord, excludeFragment)`
22
24
  - [Host serializer](https://url.spec.whatwg.org/#concept-host-serializer): `serializeHost(hostFromURLRecord)`
23
25
  - [Serialize an integer](https://url.spec.whatwg.org/#serialize-an-integer): `serializeInteger(number)`
24
- - [Origin](https://url.spec.whatwg.org/#concept-url-origin) [serializer](https://html.spec.whatwg.org/multipage/browsers.html#serialization-of-an-origin): `serializeURLOrigin(urlRecord)`
26
+ - [Origin](https://url.spec.whatwg.org/#concept-url-origin) [serializer](https://html.spec.whatwg.org/multipage/origin.html#ascii-serialisation-of-an-origin): `serializeURLOrigin(urlRecord)`
25
27
  - [Set the username](https://url.spec.whatwg.org/#set-the-username): `setTheUsername(urlRecord, usernameString)`
26
28
  - [Set the password](https://url.spec.whatwg.org/#set-the-password): `setThePassword(urlRecord, passwordString)`
27
29
  - [Cannot have a username/password/port](https://url.spec.whatwg.org/#cannot-have-a-username-password-port): `cannotHaveAUsernamePasswordPort(urlRecord)`
@@ -66,3 +68,31 @@ The URL record type has the following API:
66
68
  These properties should be treated with care, as in general changing them will cause the URL record to be in an inconsistent state until the appropriate invocation of `basicURLParse` is used to fix it up. You can see examples of this in the URL Standard, where there are many step sequences like "4. Set context object’s url’s fragment to the empty string. 5. Basic URL parse _input_ with context object’s url as _url_ and fragment state as _state override_." In between those two steps, a URL record is in an unusable state.
67
69
 
68
70
  The return value of "failure" in the spec is represented by `null`. That is, functions like `parseURL` and `basicURLParse` can return _either_ a URL record _or_ `null`.
71
+
72
+ ## Development instructions
73
+
74
+ First, install [Node.js](https://nodejs.org/). Then, fetch the dependencies of whatwg-url, by running from this directory:
75
+
76
+ npm install
77
+
78
+ To run tests:
79
+
80
+ npm test
81
+
82
+ To generate a coverage report:
83
+
84
+ npm run coverage
85
+
86
+ To build and run the live viewer:
87
+
88
+ npm run build
89
+ npm run build-live-viewer
90
+
91
+ Serve the contents of the `live-viewer` directory using any web server.
92
+
93
+ ## Supporting whatwg-url
94
+
95
+ The jsdom project (including whatwg-url) is a community-driven project maintained by a team of [volunteers](https://github.com/orgs/jsdom/people). You could support us by:
96
+
97
+ - [Getting professional support for whatwg-url](https://tidelift.com/subscription/pkg/npm-whatwg-url?utm_source=npm-whatwg-url&utm_medium=referral&utm_campaign=readme) as part of a Tidelift subscription. Tidelift helps making open source sustainable for us while giving teams assurances for maintenance, licensing, and security.
98
+ - Contributing directly to the project.
package/lib/URL-impl.js CHANGED
@@ -12,13 +12,13 @@ exports.implementation = class URLImpl {
12
12
  if (base !== undefined) {
13
13
  parsedBase = usm.basicURLParse(base);
14
14
  if (parsedBase === null) {
15
- throw new TypeError("Invalid base URL");
15
+ throw new TypeError(`Invalid base URL: ${base}`);
16
16
  }
17
17
  }
18
18
 
19
19
  const parsedURL = usm.basicURLParse(url, { baseURL: parsedBase });
20
20
  if (parsedURL === null) {
21
- throw new TypeError("Invalid URL");
21
+ throw new TypeError(`Invalid URL: ${url}`);
22
22
  }
23
23
 
24
24
  const query = parsedURL.query !== null ? parsedURL.query : "";
@@ -38,10 +38,16 @@ exports.implementation = class URLImpl {
38
38
  set href(v) {
39
39
  const parsedURL = usm.basicURLParse(v);
40
40
  if (parsedURL === null) {
41
- throw new TypeError("Invalid URL");
41
+ throw new TypeError(`Invalid URL: ${v}`);
42
42
  }
43
43
 
44
44
  this._url = parsedURL;
45
+
46
+ this._query._list.splice(0);
47
+ const { query } = parsedURL;
48
+ if (query !== null) {
49
+ this._query._list = urlencoded.parseUrlencoded(query);
50
+ }
45
51
  }
46
52
 
47
53
  get origin() {
package/lib/URL.js CHANGED
@@ -2,197 +2,272 @@
2
2
 
3
3
  const conversions = require("webidl-conversions");
4
4
  const utils = require("./utils.js");
5
+
5
6
  const impl = utils.implSymbol;
6
7
 
7
- function URL(url) {
8
- if (!new.target) {
9
- throw new TypeError(
10
- "Failed to construct 'URL'. Please use the 'new' operator; this constructor cannot be called as a function."
11
- );
8
+ class URL {
9
+ constructor(url) {
10
+ if (arguments.length < 1) {
11
+ throw new TypeError("Failed to construct 'URL': 1 argument required, but only " + arguments.length + " present.");
12
+ }
13
+ const args = [];
14
+ {
15
+ let curArg = arguments[0];
16
+ curArg = conversions["USVString"](curArg, { context: "Failed to construct 'URL': parameter 1" });
17
+ args.push(curArg);
18
+ }
19
+ {
20
+ let curArg = arguments[1];
21
+ if (curArg !== undefined) {
22
+ curArg = conversions["USVString"](curArg, { context: "Failed to construct 'URL': parameter 2" });
23
+ }
24
+ args.push(curArg);
25
+ }
26
+ return iface.setup(Object.create(new.target.prototype), args);
12
27
  }
13
- if (arguments.length < 1) {
14
- throw new TypeError("Failed to construct 'URL': 1 argument required, but only " + arguments.length + " present.");
28
+
29
+ toJSON() {
30
+ if (!this || !module.exports.is(this)) {
31
+ throw new TypeError("Illegal invocation");
32
+ }
33
+
34
+ return this[impl].toJSON();
15
35
  }
16
36
 
17
- const args = [];
18
- for (let i = 0; i < arguments.length && i < 2; ++i) {
19
- args[i] = arguments[i];
37
+ get href() {
38
+ if (!this || !module.exports.is(this)) {
39
+ throw new TypeError("Illegal invocation");
40
+ }
41
+
42
+ return this[impl]["href"];
20
43
  }
21
- args[0] = conversions["USVString"](args[0], { context: "Failed to construct 'URL': parameter 1" });
22
- if (args[1] !== undefined) {
23
- args[1] = conversions["USVString"](args[1], { context: "Failed to construct 'URL': parameter 2" });
44
+
45
+ set href(V) {
46
+ if (!this || !module.exports.is(this)) {
47
+ throw new TypeError("Illegal invocation");
48
+ }
49
+
50
+ V = conversions["USVString"](V, { context: "Failed to set the 'href' property on 'URL': The provided value" });
51
+
52
+ this[impl]["href"] = V;
24
53
  }
25
54
 
26
- iface.setup(this, args);
27
- }
55
+ toString() {
56
+ if (!this || !module.exports.is(this)) {
57
+ throw new TypeError("Illegal invocation");
58
+ }
59
+ return this[impl]["href"];
60
+ }
61
+
62
+ get origin() {
63
+ if (!this || !module.exports.is(this)) {
64
+ throw new TypeError("Illegal invocation");
65
+ }
28
66
 
29
- URL.prototype.toJSON = function toJSON() {
30
- if (!this || !module.exports.is(this)) {
31
- throw new TypeError("Illegal invocation");
67
+ return this[impl]["origin"];
32
68
  }
33
- return this[impl].toJSON();
34
- };
35
- Object.defineProperty(URL.prototype, "href", {
36
- get() {
37
- return this[impl].href;
38
- },
39
- set(V) {
40
- V = conversions["USVString"](V, { context: "Failed to set the 'href' property on 'URL': The provided value" });
41
- this[impl].href = V;
42
- },
43
- enumerable: true,
44
- configurable: true
45
- });
46
69
 
47
- URL.prototype.toString = function toString() {
48
- if (!this || !module.exports.is(this)) {
49
- throw new TypeError("Illegal invocation");
70
+ get protocol() {
71
+ if (!this || !module.exports.is(this)) {
72
+ throw new TypeError("Illegal invocation");
73
+ }
74
+
75
+ return this[impl]["protocol"];
50
76
  }
51
- return this[impl].href;
52
- };
53
77
 
54
- Object.defineProperty(URL.prototype, "origin", {
55
- get() {
56
- return this[impl].origin;
57
- },
58
- enumerable: true,
59
- configurable: true
60
- });
78
+ set protocol(V) {
79
+ if (!this || !module.exports.is(this)) {
80
+ throw new TypeError("Illegal invocation");
81
+ }
61
82
 
62
- Object.defineProperty(URL.prototype, "protocol", {
63
- get() {
64
- return this[impl].protocol;
65
- },
66
- set(V) {
67
83
  V = conversions["USVString"](V, { context: "Failed to set the 'protocol' property on 'URL': The provided value" });
68
- this[impl].protocol = V;
69
- },
70
- enumerable: true,
71
- configurable: true
72
- });
73
84
 
74
- Object.defineProperty(URL.prototype, "username", {
75
- get() {
76
- return this[impl].username;
77
- },
78
- set(V) {
85
+ this[impl]["protocol"] = V;
86
+ }
87
+
88
+ get username() {
89
+ if (!this || !module.exports.is(this)) {
90
+ throw new TypeError("Illegal invocation");
91
+ }
92
+
93
+ return this[impl]["username"];
94
+ }
95
+
96
+ set username(V) {
97
+ if (!this || !module.exports.is(this)) {
98
+ throw new TypeError("Illegal invocation");
99
+ }
100
+
79
101
  V = conversions["USVString"](V, { context: "Failed to set the 'username' property on 'URL': The provided value" });
80
- this[impl].username = V;
81
- },
82
- enumerable: true,
83
- configurable: true
84
- });
85
102
 
86
- Object.defineProperty(URL.prototype, "password", {
87
- get() {
88
- return this[impl].password;
89
- },
90
- set(V) {
103
+ this[impl]["username"] = V;
104
+ }
105
+
106
+ get password() {
107
+ if (!this || !module.exports.is(this)) {
108
+ throw new TypeError("Illegal invocation");
109
+ }
110
+
111
+ return this[impl]["password"];
112
+ }
113
+
114
+ set password(V) {
115
+ if (!this || !module.exports.is(this)) {
116
+ throw new TypeError("Illegal invocation");
117
+ }
118
+
91
119
  V = conversions["USVString"](V, { context: "Failed to set the 'password' property on 'URL': The provided value" });
92
- this[impl].password = V;
93
- },
94
- enumerable: true,
95
- configurable: true
96
- });
97
120
 
98
- Object.defineProperty(URL.prototype, "host", {
99
- get() {
100
- return this[impl].host;
101
- },
102
- set(V) {
121
+ this[impl]["password"] = V;
122
+ }
123
+
124
+ get host() {
125
+ if (!this || !module.exports.is(this)) {
126
+ throw new TypeError("Illegal invocation");
127
+ }
128
+
129
+ return this[impl]["host"];
130
+ }
131
+
132
+ set host(V) {
133
+ if (!this || !module.exports.is(this)) {
134
+ throw new TypeError("Illegal invocation");
135
+ }
136
+
103
137
  V = conversions["USVString"](V, { context: "Failed to set the 'host' property on 'URL': The provided value" });
104
- this[impl].host = V;
105
- },
106
- enumerable: true,
107
- configurable: true
108
- });
109
138
 
110
- Object.defineProperty(URL.prototype, "hostname", {
111
- get() {
112
- return this[impl].hostname;
113
- },
114
- set(V) {
139
+ this[impl]["host"] = V;
140
+ }
141
+
142
+ get hostname() {
143
+ if (!this || !module.exports.is(this)) {
144
+ throw new TypeError("Illegal invocation");
145
+ }
146
+
147
+ return this[impl]["hostname"];
148
+ }
149
+
150
+ set hostname(V) {
151
+ if (!this || !module.exports.is(this)) {
152
+ throw new TypeError("Illegal invocation");
153
+ }
154
+
115
155
  V = conversions["USVString"](V, { context: "Failed to set the 'hostname' property on 'URL': The provided value" });
116
- this[impl].hostname = V;
117
- },
118
- enumerable: true,
119
- configurable: true
120
- });
121
156
 
122
- Object.defineProperty(URL.prototype, "port", {
123
- get() {
124
- return this[impl].port;
125
- },
126
- set(V) {
157
+ this[impl]["hostname"] = V;
158
+ }
159
+
160
+ get port() {
161
+ if (!this || !module.exports.is(this)) {
162
+ throw new TypeError("Illegal invocation");
163
+ }
164
+
165
+ return this[impl]["port"];
166
+ }
167
+
168
+ set port(V) {
169
+ if (!this || !module.exports.is(this)) {
170
+ throw new TypeError("Illegal invocation");
171
+ }
172
+
127
173
  V = conversions["USVString"](V, { context: "Failed to set the 'port' property on 'URL': The provided value" });
128
- this[impl].port = V;
129
- },
130
- enumerable: true,
131
- configurable: true
132
- });
133
174
 
134
- Object.defineProperty(URL.prototype, "pathname", {
135
- get() {
136
- return this[impl].pathname;
137
- },
138
- set(V) {
175
+ this[impl]["port"] = V;
176
+ }
177
+
178
+ get pathname() {
179
+ if (!this || !module.exports.is(this)) {
180
+ throw new TypeError("Illegal invocation");
181
+ }
182
+
183
+ return this[impl]["pathname"];
184
+ }
185
+
186
+ set pathname(V) {
187
+ if (!this || !module.exports.is(this)) {
188
+ throw new TypeError("Illegal invocation");
189
+ }
190
+
139
191
  V = conversions["USVString"](V, { context: "Failed to set the 'pathname' property on 'URL': The provided value" });
140
- this[impl].pathname = V;
141
- },
142
- enumerable: true,
143
- configurable: true
144
- });
145
192
 
146
- Object.defineProperty(URL.prototype, "search", {
147
- get() {
148
- return this[impl].search;
149
- },
150
- set(V) {
193
+ this[impl]["pathname"] = V;
194
+ }
195
+
196
+ get search() {
197
+ if (!this || !module.exports.is(this)) {
198
+ throw new TypeError("Illegal invocation");
199
+ }
200
+
201
+ return this[impl]["search"];
202
+ }
203
+
204
+ set search(V) {
205
+ if (!this || !module.exports.is(this)) {
206
+ throw new TypeError("Illegal invocation");
207
+ }
208
+
151
209
  V = conversions["USVString"](V, { context: "Failed to set the 'search' property on 'URL': The provided value" });
152
- this[impl].search = V;
153
- },
154
- enumerable: true,
155
- configurable: true
156
- });
157
210
 
158
- Object.defineProperty(URL.prototype, "searchParams", {
159
- get() {
211
+ this[impl]["search"] = V;
212
+ }
213
+
214
+ get searchParams() {
215
+ if (!this || !module.exports.is(this)) {
216
+ throw new TypeError("Illegal invocation");
217
+ }
218
+
160
219
  return utils.getSameObject(this, "searchParams", () => {
161
- return utils.tryWrapperForImpl(this[impl].searchParams);
220
+ return utils.tryWrapperForImpl(this[impl]["searchParams"]);
162
221
  });
163
- },
164
- enumerable: true,
165
- configurable: true
166
- });
222
+ }
223
+
224
+ get hash() {
225
+ if (!this || !module.exports.is(this)) {
226
+ throw new TypeError("Illegal invocation");
227
+ }
228
+
229
+ return this[impl]["hash"];
230
+ }
231
+
232
+ set hash(V) {
233
+ if (!this || !module.exports.is(this)) {
234
+ throw new TypeError("Illegal invocation");
235
+ }
167
236
 
168
- Object.defineProperty(URL.prototype, "hash", {
169
- get() {
170
- return this[impl].hash;
171
- },
172
- set(V) {
173
237
  V = conversions["USVString"](V, { context: "Failed to set the 'hash' property on 'URL': The provided value" });
174
- this[impl].hash = V;
175
- },
176
- enumerable: true,
177
- configurable: true
178
- });
179
238
 
180
- Object.defineProperty(URL.prototype, Symbol.toStringTag, {
181
- value: "URL",
182
- writable: false,
183
- enumerable: false,
184
- configurable: true
239
+ this[impl]["hash"] = V;
240
+ }
241
+ }
242
+ Object.defineProperties(URL.prototype, {
243
+ toJSON: { enumerable: true },
244
+ href: { enumerable: true },
245
+ toString: { enumerable: true },
246
+ origin: { enumerable: true },
247
+ protocol: { enumerable: true },
248
+ username: { enumerable: true },
249
+ password: { enumerable: true },
250
+ host: { enumerable: true },
251
+ hostname: { enumerable: true },
252
+ port: { enumerable: true },
253
+ pathname: { enumerable: true },
254
+ search: { enumerable: true },
255
+ searchParams: { enumerable: true },
256
+ hash: { enumerable: true },
257
+ [Symbol.toStringTag]: { value: "URL", configurable: true }
185
258
  });
186
-
187
259
  const iface = {
188
- mixedInto: [],
260
+ // When an interface-module that implements this interface as a mixin is loaded, it will append its own `.is()`
261
+ // method into this array. It allows objects that directly implements *those* interfaces to be recognized as
262
+ // implementing this mixin interface.
263
+ _mixedIntoPredicates: [],
189
264
  is(obj) {
190
265
  if (obj) {
191
- if (obj[impl] instanceof Impl.implementation) {
266
+ if (utils.hasOwn(obj, impl) && obj[impl] instanceof Impl.implementation) {
192
267
  return true;
193
268
  }
194
- for (let i = 0; i < module.exports.mixedInto.length; ++i) {
195
- if (obj instanceof module.exports.mixedInto[i]) {
269
+ for (const isMixedInto of module.exports._mixedIntoPredicates) {
270
+ if (isMixedInto(obj)) {
196
271
  return true;
197
272
  }
198
273
  }
@@ -206,8 +281,8 @@ const iface = {
206
281
  }
207
282
 
208
283
  const wrapper = utils.wrapperForImpl(obj);
209
- for (let i = 0; i < module.exports.mixedInto.length; ++i) {
210
- if (wrapper instanceof module.exports.mixedInto[i]) {
284
+ for (const isMixedInto of module.exports._mixedIntoPredicates) {
285
+ if (isMixedInto(wrapper)) {
211
286
  return true;
212
287
  }
213
288
  }
@@ -220,37 +295,41 @@ const iface = {
220
295
  }
221
296
  throw new TypeError(`${context} is not of type 'URL'.`);
222
297
  },
298
+
223
299
  create(constructorArgs, privateData) {
224
300
  let obj = Object.create(URL.prototype);
225
- this.setup(obj, constructorArgs, privateData);
301
+ obj = this.setup(obj, constructorArgs, privateData);
226
302
  return obj;
227
303
  },
228
304
  createImpl(constructorArgs, privateData) {
229
305
  let obj = Object.create(URL.prototype);
230
- this.setup(obj, constructorArgs, privateData);
306
+ obj = this.setup(obj, constructorArgs, privateData);
231
307
  return utils.implForWrapper(obj);
232
308
  },
233
309
  _internalSetup(obj) {},
234
310
  setup(obj, constructorArgs, privateData) {
235
311
  if (!privateData) privateData = {};
312
+
236
313
  privateData.wrapper = obj;
237
314
 
238
315
  this._internalSetup(obj);
239
-
240
316
  Object.defineProperty(obj, impl, {
241
317
  value: new Impl.implementation(constructorArgs, privateData),
242
- writable: false,
243
- enumerable: false,
244
318
  configurable: true
245
319
  });
320
+
246
321
  obj[impl][utils.wrapperSymbol] = obj;
322
+ if (Impl.init) {
323
+ Impl.init(obj[impl], privateData);
324
+ }
325
+ return obj;
247
326
  },
248
327
  interface: URL,
249
328
  expose: {
250
- Window: { URL: URL },
251
- Worker: { URL: URL }
329
+ Window: { URL },
330
+ Worker: { URL }
252
331
  }
253
- };
332
+ }; // iface
254
333
  module.exports = iface;
255
334
 
256
- const Impl = require(".//URL-impl.js");
335
+ const Impl = require("./URL-impl.js");