normalize-url 6.1.0 → 7.0.3

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.
Files changed (5) hide show
  1. package/index.d.ts +212 -216
  2. package/index.js +71 -41
  3. package/license +1 -1
  4. package/package.json +10 -8
  5. package/readme.md +5 -4
package/index.d.ts CHANGED
@@ -1,249 +1,247 @@
1
- declare namespace normalizeUrl {
2
- interface Options {
3
- /**
4
- @default 'http:'
5
- */
6
- readonly defaultProtocol?: string;
1
+ export interface Options {
2
+ /**
3
+ @default 'http:'
4
+ */
5
+ readonly defaultProtocol?: string;
7
6
 
8
- /**
9
- Prepends `defaultProtocol` to the URL if it's protocol-relative.
7
+ /**
8
+ Prepends `defaultProtocol` to the URL if it's protocol-relative.
10
9
 
11
- @default true
10
+ @default true
12
11
 
13
- @example
14
- ```
15
- normalizeUrl('//sindresorhus.com:80/');
16
- //=> 'http://sindresorhus.com'
12
+ @example
13
+ ```
14
+ normalizeUrl('//sindresorhus.com:80/');
15
+ //=> 'http://sindresorhus.com'
17
16
 
18
- normalizeUrl('//sindresorhus.com:80/', {normalizeProtocol: false});
19
- //=> '//sindresorhus.com'
20
- ```
21
- */
22
- readonly normalizeProtocol?: boolean;
17
+ normalizeUrl('//sindresorhus.com:80/', {normalizeProtocol: false});
18
+ //=> '//sindresorhus.com'
19
+ ```
20
+ */
21
+ readonly normalizeProtocol?: boolean;
23
22
 
24
- /**
25
- Normalizes `https:` URLs to `http:`.
23
+ /**
24
+ Normalizes `https:` URLs to `http:`.
26
25
 
27
- @default false
26
+ @default false
28
27
 
29
- @example
30
- ```
31
- normalizeUrl('https://sindresorhus.com:80/');
32
- //=> 'https://sindresorhus.com'
28
+ @example
29
+ ```
30
+ normalizeUrl('https://sindresorhus.com:80/');
31
+ //=> 'https://sindresorhus.com'
33
32
 
34
- normalizeUrl('https://sindresorhus.com:80/', {forceHttp: true});
35
- //=> 'http://sindresorhus.com'
36
- ```
37
- */
38
- readonly forceHttp?: boolean;
33
+ normalizeUrl('https://sindresorhus.com:80/', {forceHttp: true});
34
+ //=> 'http://sindresorhus.com'
35
+ ```
36
+ */
37
+ readonly forceHttp?: boolean;
39
38
 
40
- /**
41
- Normalizes `http:` URLs to `https:`.
39
+ /**
40
+ Normalizes `http:` URLs to `https:`.
42
41
 
43
- This option can't be used with the `forceHttp` option at the same time.
42
+ This option can't be used with the `forceHttp` option at the same time.
44
43
 
45
- @default false
44
+ @default false
46
45
 
47
- @example
48
- ```
49
- normalizeUrl('https://sindresorhus.com:80/');
50
- //=> 'https://sindresorhus.com'
46
+ @example
47
+ ```
48
+ normalizeUrl('https://sindresorhus.com:80/');
49
+ //=> 'https://sindresorhus.com'
51
50
 
52
- normalizeUrl('http://sindresorhus.com:80/', {forceHttps: true});
53
- //=> 'https://sindresorhus.com'
54
- ```
55
- */
56
- readonly forceHttps?: boolean;
51
+ normalizeUrl('http://sindresorhus.com:80/', {forceHttps: true});
52
+ //=> 'https://sindresorhus.com'
53
+ ```
54
+ */
55
+ readonly forceHttps?: boolean;
57
56
 
58
- /**
59
- Strip the [authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) part of a URL.
57
+ /**
58
+ Strip the [authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) part of a URL.
60
59
 
61
- @default true
60
+ @default true
62
61
 
63
- @example
64
- ```
65
- normalizeUrl('user:password@sindresorhus.com');
66
- //=> 'https://sindresorhus.com'
62
+ @example
63
+ ```
64
+ normalizeUrl('user:password@sindresorhus.com');
65
+ //=> 'https://sindresorhus.com'
67
66
 
68
- normalizeUrl('user:password@sindresorhus.com', {stripAuthentication: false});
69
- //=> 'https://user:password@sindresorhus.com'
70
- ```
71
- */
72
- readonly stripAuthentication?: boolean;
67
+ normalizeUrl('user:password@sindresorhus.com', {stripAuthentication: false});
68
+ //=> 'https://user:password@sindresorhus.com'
69
+ ```
70
+ */
71
+ readonly stripAuthentication?: boolean;
73
72
 
74
- /**
75
- Removes hash from the URL.
76
-
77
- @default false
73
+ /**
74
+ Removes hash from the URL.
78
75
 
79
- @example
80
- ```
81
- normalizeUrl('sindresorhus.com/about.html#contact');
82
- //=> 'http://sindresorhus.com/about.html#contact'
76
+ @default false
83
77
 
84
- normalizeUrl('sindresorhus.com/about.html#contact', {stripHash: true});
85
- //=> 'http://sindresorhus.com/about.html'
86
- ```
87
- */
88
- readonly stripHash?: boolean;
78
+ @example
79
+ ```
80
+ normalizeUrl('sindresorhus.com/about.html#contact');
81
+ //=> 'http://sindresorhus.com/about.html#contact'
89
82
 
90
- /**
91
- Removes HTTP(S) protocol from an URL `http://sindresorhus.com` → `sindresorhus.com`.
83
+ normalizeUrl('sindresorhus.com/about.html#contact', {stripHash: true});
84
+ //=> 'http://sindresorhus.com/about.html'
85
+ ```
86
+ */
87
+ readonly stripHash?: boolean;
92
88
 
93
- @default false
89
+ /**
90
+ Removes HTTP(S) protocol from an URL `http://sindresorhus.com` → `sindresorhus.com`.
94
91
 
95
- @example
96
- ```
97
- normalizeUrl('https://sindresorhus.com');
98
- //=> 'https://sindresorhus.com'
92
+ @default false
99
93
 
100
- normalizeUrl('sindresorhus.com', {stripProtocol: true});
101
- //=> 'sindresorhus.com'
102
- ```
103
- */
104
- readonly stripProtocol?: boolean;
94
+ @example
95
+ ```
96
+ normalizeUrl('https://sindresorhus.com');
97
+ //=> 'https://sindresorhus.com'
105
98
 
106
- /**
107
- Strip the [text fragment](https://web.dev/text-fragments/) part of the URL
99
+ normalizeUrl('sindresorhus.com', {stripProtocol: true});
100
+ //=> 'sindresorhus.com'
101
+ ```
102
+ */
103
+ readonly stripProtocol?: boolean;
108
104
 
109
- __Note:__ The text fragment will always be removed if the `stripHash` option is set to `true`, as the hash contains the text fragment.
105
+ /**
106
+ Strip the [text fragment](https://web.dev/text-fragments/) part of the URL
110
107
 
111
- @default true
108
+ __Note:__ The text fragment will always be removed if the `stripHash` option is set to `true`, as the hash contains the text fragment.
112
109
 
113
- @example
114
- ```
115
- normalizeUrl('http://sindresorhus.com/about.html#:~:text=hello');
116
- //=> 'http://sindresorhus.com/about.html#'
110
+ @default true
117
111
 
118
- normalizeUrl('http://sindresorhus.com/about.html#section:~:text=hello');
119
- //=> 'http://sindresorhus.com/about.html#section'
112
+ @example
113
+ ```
114
+ normalizeUrl('http://sindresorhus.com/about.html#:~:text=hello');
115
+ //=> 'http://sindresorhus.com/about.html#'
120
116
 
121
- normalizeUrl('http://sindresorhus.com/about.html#:~:text=hello', {stripTextFragment: false});
122
- //=> 'http://sindresorhus.com/about.html#:~:text=hello'
117
+ normalizeUrl('http://sindresorhus.com/about.html#section:~:text=hello');
118
+ //=> 'http://sindresorhus.com/about.html#section'
123
119
 
124
- normalizeUrl('http://sindresorhus.com/about.html#section:~:text=hello', {stripTextFragment: false});
125
- //=> 'http://sindresorhus.com/about.html#section:~:text=hello'
126
- ```
127
- */
128
- readonly stripTextFragment?: boolean;
129
-
130
- /**
131
- Removes `www.` from the URL.
120
+ normalizeUrl('http://sindresorhus.com/about.html#:~:text=hello', {stripTextFragment: false});
121
+ //=> 'http://sindresorhus.com/about.html#:~:text=hello'
132
122
 
133
- @default true
123
+ normalizeUrl('http://sindresorhus.com/about.html#section:~:text=hello', {stripTextFragment: false});
124
+ //=> 'http://sindresorhus.com/about.html#section:~:text=hello'
125
+ ```
126
+ */
127
+ readonly stripTextFragment?: boolean;
128
+
129
+ /**
130
+ Removes `www.` from the URL.
134
131
 
135
- @example
136
- ```
137
- normalizeUrl('http://www.sindresorhus.com');
138
- //=> 'http://sindresorhus.com'
132
+ @default true
139
133
 
140
- normalizeUrl('http://www.sindresorhus.com', {stripWWW: false});
141
- //=> 'http://www.sindresorhus.com'
142
- ```
143
- */
144
- readonly stripWWW?: boolean;
145
-
146
- /**
147
- Removes query parameters that matches any of the provided strings or regexes.
148
-
149
- @default [/^utm_\w+/i]
150
-
151
- @example
152
- ```
153
- normalizeUrl('www.sindresorhus.com?foo=bar&ref=test_ref', {
154
- removeQueryParameters: ['ref']
155
- });
156
- //=> 'http://sindresorhus.com/?foo=bar'
157
- ```
158
-
159
- If a boolean is provided, `true` will remove all the query parameters.
160
-
161
- ```
162
- normalizeUrl('www.sindresorhus.com?foo=bar', {
163
- removeQueryParameters: true
164
- });
165
- //=> 'http://sindresorhus.com'
166
- ```
167
-
168
- `false` will not remove any query parameter.
169
-
170
- ```
171
- normalizeUrl('www.sindresorhus.com?foo=bar&utm_medium=test&ref=test_ref', {
172
- removeQueryParameters: false
173
- });
174
- //=> 'http://www.sindresorhus.com/?foo=bar&ref=test_ref&utm_medium=test'
175
- ```
176
- */
177
- readonly removeQueryParameters?: ReadonlyArray<RegExp | string> | boolean;
178
-
179
- /**
180
- Removes trailing slash.
181
-
182
- __Note__: Trailing slash is always removed if the URL doesn't have a pathname unless the `removeSingleSlash` option is set to `false`.
183
-
184
- @default true
185
-
186
- @example
187
- ```
188
- normalizeUrl('http://sindresorhus.com/redirect/');
189
- //=> 'http://sindresorhus.com/redirect'
190
-
191
- normalizeUrl('http://sindresorhus.com/redirect/', {removeTrailingSlash: false});
192
- //=> 'http://sindresorhus.com/redirect/'
193
-
194
- normalizeUrl('http://sindresorhus.com/', {removeTrailingSlash: false});
195
- //=> 'http://sindresorhus.com'
196
- ```
197
- */
198
- readonly removeTrailingSlash?: boolean;
199
-
200
- /**
201
- Remove a sole `/` pathname in the output. This option is independant of `removeTrailingSlash`.
202
-
203
- @default true
204
-
205
- @example
206
- ```
207
- normalizeUrl('https://sindresorhus.com/');
208
- //=> 'https://sindresorhus.com'
209
-
210
- normalizeUrl('https://sindresorhus.com/', {removeSingleSlash: false});
211
- //=> 'https://sindresorhus.com/'
212
- ```
213
- */
214
- readonly removeSingleSlash?: boolean;
215
-
216
- /**
217
- Removes the default directory index file from path that matches any of the provided strings or regexes.
218
- When `true`, the regex `/^index\.[a-z]+$/` is used.
219
-
220
- @default false
221
-
222
- @example
223
- ```
224
- normalizeUrl('www.sindresorhus.com/foo/default.php', {
225
- removeDirectoryIndex: [/^default\.[a-z]+$/]
226
- });
227
- //=> 'http://sindresorhus.com/foo'
228
- ```
229
- */
230
- readonly removeDirectoryIndex?: ReadonlyArray<RegExp | string>;
231
-
232
- /**
233
- Sorts the query parameters alphabetically by key.
234
-
235
- @default true
236
-
237
- @example
238
- ```
239
- normalizeUrl('www.sindresorhus.com?b=two&a=one&c=three', {
240
- sortQueryParameters: false
241
- });
242
- //=> 'http://sindresorhus.com/?b=two&a=one&c=three'
243
- ```
244
- */
245
- readonly sortQueryParameters?: boolean;
246
- }
134
+ @example
135
+ ```
136
+ normalizeUrl('http://www.sindresorhus.com');
137
+ //=> 'http://sindresorhus.com'
138
+
139
+ normalizeUrl('http://www.sindresorhus.com', {stripWWW: false});
140
+ //=> 'http://www.sindresorhus.com'
141
+ ```
142
+ */
143
+ readonly stripWWW?: boolean;
144
+
145
+ /**
146
+ Removes query parameters that matches any of the provided strings or regexes.
147
+
148
+ @default [/^utm_\w+/i]
149
+
150
+ @example
151
+ ```
152
+ normalizeUrl('www.sindresorhus.com?foo=bar&ref=test_ref', {
153
+ removeQueryParameters: ['ref']
154
+ });
155
+ //=> 'http://sindresorhus.com/?foo=bar'
156
+ ```
157
+
158
+ If a boolean is provided, `true` will remove all the query parameters.
159
+
160
+ ```
161
+ normalizeUrl('www.sindresorhus.com?foo=bar', {
162
+ removeQueryParameters: true
163
+ });
164
+ //=> 'http://sindresorhus.com'
165
+ ```
166
+
167
+ `false` will not remove any query parameter.
168
+
169
+ ```
170
+ normalizeUrl('www.sindresorhus.com?foo=bar&utm_medium=test&ref=test_ref', {
171
+ removeQueryParameters: false
172
+ });
173
+ //=> 'http://www.sindresorhus.com/?foo=bar&ref=test_ref&utm_medium=test'
174
+ ```
175
+ */
176
+ readonly removeQueryParameters?: ReadonlyArray<RegExp | string> | boolean;
177
+
178
+ /**
179
+ Removes trailing slash.
180
+
181
+ __Note__: Trailing slash is always removed if the URL doesn't have a pathname unless the `removeSingleSlash` option is set to `false`.
182
+
183
+ @default true
184
+
185
+ @example
186
+ ```
187
+ normalizeUrl('http://sindresorhus.com/redirect/');
188
+ //=> 'http://sindresorhus.com/redirect'
189
+
190
+ normalizeUrl('http://sindresorhus.com/redirect/', {removeTrailingSlash: false});
191
+ //=> 'http://sindresorhus.com/redirect/'
192
+
193
+ normalizeUrl('http://sindresorhus.com/', {removeTrailingSlash: false});
194
+ //=> 'http://sindresorhus.com'
195
+ ```
196
+ */
197
+ readonly removeTrailingSlash?: boolean;
198
+
199
+ /**
200
+ Remove a sole `/` pathname in the output. This option is independant of `removeTrailingSlash`.
201
+
202
+ @default true
203
+
204
+ @example
205
+ ```
206
+ normalizeUrl('https://sindresorhus.com/');
207
+ //=> 'https://sindresorhus.com'
208
+
209
+ normalizeUrl('https://sindresorhus.com/', {removeSingleSlash: false});
210
+ //=> 'https://sindresorhus.com/'
211
+ ```
212
+ */
213
+ readonly removeSingleSlash?: boolean;
214
+
215
+ /**
216
+ Removes the default directory index file from path that matches any of the provided strings or regexes.
217
+ When `true`, the regex `/^index\.[a-z]+$/` is used.
218
+
219
+ @default false
220
+
221
+ @example
222
+ ```
223
+ normalizeUrl('www.sindresorhus.com/foo/default.php', {
224
+ removeDirectoryIndex: [/^default\.[a-z]+$/]
225
+ });
226
+ //=> 'http://sindresorhus.com/foo'
227
+ ```
228
+ */
229
+ readonly removeDirectoryIndex?: boolean | ReadonlyArray<RegExp | string>;
230
+
231
+ /**
232
+ Sorts the query parameters alphabetically by key.
233
+
234
+ @default true
235
+
236
+ @example
237
+ ```
238
+ normalizeUrl('www.sindresorhus.com?b=two&a=one&c=three', {
239
+ sortQueryParameters: false
240
+ });
241
+ //=> 'http://sindresorhus.com/?b=two&a=one&c=three'
242
+ ```
243
+ */
244
+ readonly sortQueryParameters?: boolean;
247
245
  }
248
246
 
249
247
  /**
@@ -253,7 +251,7 @@ declare namespace normalizeUrl {
253
251
 
254
252
  @example
255
253
  ```
256
- import normalizeUrl = require('normalize-url');
254
+ import normalizeUrl from 'normalize-url';
257
255
 
258
256
  normalizeUrl('sindresorhus.com');
259
257
  //=> 'http://sindresorhus.com'
@@ -262,6 +260,4 @@ normalizeUrl('//www.sindresorhus.com:80/../baz?b=bar&a=foo');
262
260
  //=> 'http://sindresorhus.com/baz?a=foo&b=bar'
263
261
  ```
264
262
  */
265
- declare function normalizeUrl(url: string, options?: normalizeUrl.Options): string;
266
-
267
- export = normalizeUrl;
263
+ export default function normalizeUrl(url: string, options?: Options): string;
package/index.js CHANGED
@@ -1,12 +1,8 @@
1
- 'use strict';
2
-
3
1
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
4
2
  const DATA_URL_DEFAULT_MIME_TYPE = 'text/plain';
5
3
  const DATA_URL_DEFAULT_CHARSET = 'us-ascii';
6
4
 
7
- const testParameter = (name, filters) => {
8
- return filters.some(filter => filter instanceof RegExp ? filter.test(name) : filter === name);
9
- };
5
+ const testParameter = (name, filters) => filters.some(filter => filter instanceof RegExp ? filter.test(name) : filter === name);
10
6
 
11
7
  const normalizeDataURL = (urlString, {stripHash}) => {
12
8
  const match = /^data:(?<type>[^,]*?),(?<data>[^#]*?)(?:#(?<hash>.*))?$/.exec(urlString);
@@ -45,21 +41,21 @@ const normalizeDataURL = (urlString, {stripHash}) => {
45
41
  .filter(Boolean);
46
42
 
47
43
  const normalizedMediaType = [
48
- ...attributes
44
+ ...attributes,
49
45
  ];
50
46
 
51
47
  if (isBase64) {
52
48
  normalizedMediaType.push('base64');
53
49
  }
54
50
 
55
- if (normalizedMediaType.length !== 0 || (mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE)) {
51
+ if (normalizedMediaType.length > 0 || (mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE)) {
56
52
  normalizedMediaType.unshift(mimeType);
57
53
  }
58
54
 
59
55
  return `data:${normalizedMediaType.join(';')},${isBase64 ? data.trim() : data}${hash ? `#${hash}` : ''}`;
60
56
  };
61
57
 
62
- const normalizeUrl = (urlString, options) => {
58
+ export default function normalizeUrl(urlString, options) {
63
59
  options = {
64
60
  defaultProtocol: 'http:',
65
61
  normalizeProtocol: true,
@@ -74,7 +70,7 @@ const normalizeUrl = (urlString, options) => {
74
70
  removeSingleSlash: true,
75
71
  removeDirectoryIndex: false,
76
72
  sortQueryParameters: true,
77
- ...options
73
+ ...options,
78
74
  };
79
75
 
80
76
  urlString = urlString.trim();
@@ -96,43 +92,73 @@ const normalizeUrl = (urlString, options) => {
96
92
  urlString = urlString.replace(/^(?!(?:\w+:)?\/\/)|^\/\//, options.defaultProtocol);
97
93
  }
98
94
 
99
- const urlObj = new URL(urlString);
95
+ const urlObject = new URL(urlString);
100
96
 
101
97
  if (options.forceHttp && options.forceHttps) {
102
98
  throw new Error('The `forceHttp` and `forceHttps` options cannot be used together');
103
99
  }
104
100
 
105
- if (options.forceHttp && urlObj.protocol === 'https:') {
106
- urlObj.protocol = 'http:';
101
+ if (options.forceHttp && urlObject.protocol === 'https:') {
102
+ urlObject.protocol = 'http:';
107
103
  }
108
104
 
109
- if (options.forceHttps && urlObj.protocol === 'http:') {
110
- urlObj.protocol = 'https:';
105
+ if (options.forceHttps && urlObject.protocol === 'http:') {
106
+ urlObject.protocol = 'https:';
111
107
  }
112
108
 
113
109
  // Remove auth
114
110
  if (options.stripAuthentication) {
115
- urlObj.username = '';
116
- urlObj.password = '';
111
+ urlObject.username = '';
112
+ urlObject.password = '';
117
113
  }
118
114
 
119
115
  // Remove hash
120
116
  if (options.stripHash) {
121
- urlObj.hash = '';
117
+ urlObject.hash = '';
122
118
  } else if (options.stripTextFragment) {
123
- urlObj.hash = urlObj.hash.replace(/#?:~:text.*?$/i, '');
119
+ urlObject.hash = urlObject.hash.replace(/#?:~:text.*?$/i, '');
124
120
  }
125
121
 
126
122
  // Remove duplicate slashes if not preceded by a protocol
127
- if (urlObj.pathname) {
128
- urlObj.pathname = urlObj.pathname.replace(/(?<!\b(?:[a-z][a-z\d+\-.]{1,50}:))\/{2,}/g, '/');
123
+ // NOTE: This could be implemented using a single negative lookbehind
124
+ // regex, but we avoid that to maintain compatibility with older js engines
125
+ // which do not have support for that feature.
126
+ if (urlObject.pathname) {
127
+ // TODO: Replace everything below with `urlObject.pathname = urlObject.pathname.replace(/(?<!\b[a-z][a-z\d+\-.]{1,50}:)\/{2,}/g, '/');` when Safari supports negative lookbehind.
128
+
129
+ // Split the string by occurrences of this protocol regex, and perform
130
+ // duplicate-slash replacement on the strings between those occurrences
131
+ // (if any).
132
+ const protocolRegex = /\b[a-z][a-z\d+\-.]{1,50}:\/\//g;
133
+
134
+ let lastIndex = 0;
135
+ let result = '';
136
+ for (;;) {
137
+ const match = protocolRegex.exec(urlObject.pathname);
138
+ if (!match) {
139
+ break;
140
+ }
141
+
142
+ const protocol = match[0];
143
+ const protocolAtIndex = match.index;
144
+ const intermediate = urlObject.pathname.slice(lastIndex, protocolAtIndex);
145
+
146
+ result += intermediate.replace(/\/{2,}/g, '/');
147
+ result += protocol;
148
+ lastIndex = protocolAtIndex + protocol.length;
149
+ }
150
+
151
+ const remnant = urlObject.pathname.slice(lastIndex, urlObject.pathname.length);
152
+ result += remnant.replace(/\/{2,}/g, '/');
153
+
154
+ urlObject.pathname = result;
129
155
  }
130
156
 
131
157
  // Decode URI octets
132
- if (urlObj.pathname) {
158
+ if (urlObject.pathname) {
133
159
  try {
134
- urlObj.pathname = decodeURI(urlObj.pathname);
135
- } catch (_) {}
160
+ urlObject.pathname = decodeURI(urlObject.pathname);
161
+ } catch {}
136
162
  }
137
163
 
138
164
  // Remove directory index
@@ -141,62 +167,68 @@ const normalizeUrl = (urlString, options) => {
141
167
  }
142
168
 
143
169
  if (Array.isArray(options.removeDirectoryIndex) && options.removeDirectoryIndex.length > 0) {
144
- let pathComponents = urlObj.pathname.split('/');
170
+ let pathComponents = urlObject.pathname.split('/');
145
171
  const lastComponent = pathComponents[pathComponents.length - 1];
146
172
 
147
173
  if (testParameter(lastComponent, options.removeDirectoryIndex)) {
148
- pathComponents = pathComponents.slice(0, pathComponents.length - 1);
149
- urlObj.pathname = pathComponents.slice(1).join('/') + '/';
174
+ pathComponents = pathComponents.slice(0, -1);
175
+ urlObject.pathname = pathComponents.slice(1).join('/') + '/';
150
176
  }
151
177
  }
152
178
 
153
- if (urlObj.hostname) {
179
+ if (urlObject.hostname) {
154
180
  // Remove trailing dot
155
- urlObj.hostname = urlObj.hostname.replace(/\.$/, '');
181
+ urlObject.hostname = urlObject.hostname.replace(/\.$/, '');
156
182
 
157
183
  // Remove `www.`
158
- if (options.stripWWW && /^www\.(?!www\.)(?:[a-z\-\d]{1,63})\.(?:[a-z.\-\d]{2,63})$/.test(urlObj.hostname)) {
184
+ if (options.stripWWW && /^www\.(?!www\.)[a-z\-\d]{1,63}\.[a-z.\-\d]{2,63}$/.test(urlObject.hostname)) {
159
185
  // Each label should be max 63 at length (min: 1).
160
186
  // Source: https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names
161
187
  // Each TLD should be up to 63 characters long (min: 2).
162
188
  // It is technically possible to have a single character TLD, but none currently exist.
163
- urlObj.hostname = urlObj.hostname.replace(/^www\./, '');
189
+ urlObject.hostname = urlObject.hostname.replace(/^www\./, '');
164
190
  }
165
191
  }
166
192
 
167
193
  // Remove query unwanted parameters
168
194
  if (Array.isArray(options.removeQueryParameters)) {
169
- for (const key of [...urlObj.searchParams.keys()]) {
195
+ // eslint-disable-next-line unicorn/no-useless-spread -- We are intentionally spreading to get a copy.
196
+ for (const key of [...urlObject.searchParams.keys()]) {
170
197
  if (testParameter(key, options.removeQueryParameters)) {
171
- urlObj.searchParams.delete(key);
198
+ urlObject.searchParams.delete(key);
172
199
  }
173
200
  }
174
201
  }
175
202
 
176
203
  if (options.removeQueryParameters === true) {
177
- urlObj.search = '';
204
+ urlObject.search = '';
178
205
  }
179
206
 
180
207
  // Sort query parameters
181
208
  if (options.sortQueryParameters) {
182
- urlObj.searchParams.sort();
209
+ urlObject.searchParams.sort();
210
+
211
+ // Calling `.sort()` encodes the search parameters, so we need to decode them again.
212
+ try {
213
+ urlObject.search = decodeURIComponent(urlObject.search);
214
+ } catch {}
183
215
  }
184
216
 
185
217
  if (options.removeTrailingSlash) {
186
- urlObj.pathname = urlObj.pathname.replace(/\/$/, '');
218
+ urlObject.pathname = urlObject.pathname.replace(/\/$/, '');
187
219
  }
188
220
 
189
221
  const oldUrlString = urlString;
190
222
 
191
223
  // Take advantage of many of the Node `url` normalizations
192
- urlString = urlObj.toString();
224
+ urlString = urlObject.toString();
193
225
 
194
- if (!options.removeSingleSlash && urlObj.pathname === '/' && !oldUrlString.endsWith('/') && urlObj.hash === '') {
226
+ if (!options.removeSingleSlash && urlObject.pathname === '/' && !oldUrlString.endsWith('/') && urlObject.hash === '') {
195
227
  urlString = urlString.replace(/\/$/, '');
196
228
  }
197
229
 
198
230
  // Remove ending `/` unless removeSingleSlash is false
199
- if ((options.removeTrailingSlash || urlObj.pathname === '/') && urlObj.hash === '' && options.removeSingleSlash) {
231
+ if ((options.removeTrailingSlash || urlObject.pathname === '/') && urlObject.hash === '' && options.removeSingleSlash) {
200
232
  urlString = urlString.replace(/\/$/, '');
201
233
  }
202
234
 
@@ -211,6 +243,4 @@ const normalizeUrl = (urlString, options) => {
211
243
  }
212
244
 
213
245
  return urlString;
214
- };
215
-
216
- module.exports = normalizeUrl;
246
+ }
package/license CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
3
+ Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "normalize-url",
3
- "version": "6.1.0",
3
+ "version": "7.0.3",
4
4
  "description": "Normalize a URL",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/normalize-url",
@@ -10,11 +10,13 @@
10
10
  "email": "sindresorhus@gmail.com",
11
11
  "url": "https://sindresorhus.com"
12
12
  },
13
+ "type": "module",
14
+ "exports": "./index.js",
13
15
  "engines": {
14
- "node": ">=10"
16
+ "node": ">=12.20"
15
17
  },
16
18
  "scripts": {
17
- "test": "xo && nyc ava && tsd"
19
+ "test": "ava && tsd"
18
20
  },
19
21
  "files": [
20
22
  "index.js",
@@ -36,12 +38,12 @@
36
38
  "canonical"
37
39
  ],
38
40
  "devDependencies": {
39
- "ava": "^2.4.0",
40
- "nyc": "^15.0.0",
41
- "tsd": "^0.11.0",
42
- "xo": "^0.25.3"
41
+ "ava": "^4.0.1",
42
+ "c8": "^7.11.0",
43
+ "tsd": "^0.19.1",
44
+ "xo": "^0.47.0"
43
45
  },
44
- "nyc": {
46
+ "c8": {
45
47
  "reporter": [
46
48
  "text",
47
49
  "lcov"
package/readme.md CHANGED
@@ -4,10 +4,12 @@
4
4
 
5
5
  Useful when you need to display, store, deduplicate, sort, compare, etc, URLs.
6
6
 
7
+ **Note:** This package does **not** do URL sanitization. [Garbage in, garbage out.](https://en.wikipedia.org/wiki/Garbage_in,_garbage_out) If you use this in a server context and accept URLs as user input, it's up to you to protect against invalid URLs, [path traversal attacks](https://owasp.org/www-community/attacks/Path_Traversal), etc.
8
+
7
9
  ## Install
8
10
 
9
- ```
10
- $ npm install normalize-url
11
+ ```sh
12
+ npm install normalize-url
11
13
  ```
12
14
 
13
15
  *If you need to use this in the browser, use version 4: `npm i normalize-url@4`*
@@ -15,7 +17,7 @@ $ npm install normalize-url
15
17
  ## Usage
16
18
 
17
19
  ```js
18
- const normalizeUrl = require('normalize-url');
20
+ import normalizeUrl from 'normalize-url';
19
21
 
20
22
  normalizeUrl('sindresorhus.com');
21
23
  //=> 'http://sindresorhus.com'
@@ -240,7 +242,6 @@ normalizeUrl('https://sindresorhus.com/', {removeSingleSlash: false});
240
242
  //=> 'https://sindresorhus.com/'
241
243
  ```
242
244
 
243
-
244
245
  ##### removeDirectoryIndex
245
246
 
246
247
  Type: `boolean | Array<RegExp | string>`\