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.
- package/index.d.ts +212 -216
- package/index.js +71 -41
- package/license +1 -1
- package/package.json +10 -8
- package/readme.md +5 -4
package/index.d.ts
CHANGED
|
@@ -1,249 +1,247 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
readonly defaultProtocol?: string;
|
|
1
|
+
export interface Options {
|
|
2
|
+
/**
|
|
3
|
+
@default 'http:'
|
|
4
|
+
*/
|
|
5
|
+
readonly defaultProtocol?: string;
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
/**
|
|
8
|
+
Prepends `defaultProtocol` to the URL if it's protocol-relative.
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
@default true
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
@example
|
|
13
|
+
```
|
|
14
|
+
normalizeUrl('//sindresorhus.com:80/');
|
|
15
|
+
//=> 'http://sindresorhus.com'
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
normalizeUrl('//sindresorhus.com:80/', {normalizeProtocol: false});
|
|
18
|
+
//=> '//sindresorhus.com'
|
|
19
|
+
```
|
|
20
|
+
*/
|
|
21
|
+
readonly normalizeProtocol?: boolean;
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
/**
|
|
24
|
+
Normalizes `https:` URLs to `http:`.
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
@default false
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
@example
|
|
29
|
+
```
|
|
30
|
+
normalizeUrl('https://sindresorhus.com:80/');
|
|
31
|
+
//=> 'https://sindresorhus.com'
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
normalizeUrl('https://sindresorhus.com:80/', {forceHttp: true});
|
|
34
|
+
//=> 'http://sindresorhus.com'
|
|
35
|
+
```
|
|
36
|
+
*/
|
|
37
|
+
readonly forceHttp?: boolean;
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
/**
|
|
40
|
+
Normalizes `http:` URLs to `https:`.
|
|
42
41
|
|
|
43
|
-
|
|
42
|
+
This option can't be used with the `forceHttp` option at the same time.
|
|
44
43
|
|
|
45
|
-
|
|
44
|
+
@default false
|
|
46
45
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
@example
|
|
47
|
+
```
|
|
48
|
+
normalizeUrl('https://sindresorhus.com:80/');
|
|
49
|
+
//=> 'https://sindresorhus.com'
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
normalizeUrl('http://sindresorhus.com:80/', {forceHttps: true});
|
|
52
|
+
//=> 'https://sindresorhus.com'
|
|
53
|
+
```
|
|
54
|
+
*/
|
|
55
|
+
readonly forceHttps?: boolean;
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
/**
|
|
58
|
+
Strip the [authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) part of a URL.
|
|
60
59
|
|
|
61
|
-
|
|
60
|
+
@default true
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
@example
|
|
63
|
+
```
|
|
64
|
+
normalizeUrl('user:password@sindresorhus.com');
|
|
65
|
+
//=> 'https://sindresorhus.com'
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
normalizeUrl('user:password@sindresorhus.com', {stripAuthentication: false});
|
|
68
|
+
//=> 'https://user:password@sindresorhus.com'
|
|
69
|
+
```
|
|
70
|
+
*/
|
|
71
|
+
readonly stripAuthentication?: boolean;
|
|
73
72
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
@default false
|
|
73
|
+
/**
|
|
74
|
+
Removes hash from the URL.
|
|
78
75
|
|
|
79
|
-
|
|
80
|
-
```
|
|
81
|
-
normalizeUrl('sindresorhus.com/about.html#contact');
|
|
82
|
-
//=> 'http://sindresorhus.com/about.html#contact'
|
|
76
|
+
@default false
|
|
83
77
|
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
83
|
+
normalizeUrl('sindresorhus.com/about.html#contact', {stripHash: true});
|
|
84
|
+
//=> 'http://sindresorhus.com/about.html'
|
|
85
|
+
```
|
|
86
|
+
*/
|
|
87
|
+
readonly stripHash?: boolean;
|
|
92
88
|
|
|
93
|
-
|
|
89
|
+
/**
|
|
90
|
+
Removes HTTP(S) protocol from an URL `http://sindresorhus.com` → `sindresorhus.com`.
|
|
94
91
|
|
|
95
|
-
|
|
96
|
-
```
|
|
97
|
-
normalizeUrl('https://sindresorhus.com');
|
|
98
|
-
//=> 'https://sindresorhus.com'
|
|
92
|
+
@default false
|
|
99
93
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
readonly stripProtocol?: boolean;
|
|
94
|
+
@example
|
|
95
|
+
```
|
|
96
|
+
normalizeUrl('https://sindresorhus.com');
|
|
97
|
+
//=> 'https://sindresorhus.com'
|
|
105
98
|
|
|
106
|
-
|
|
107
|
-
|
|
99
|
+
normalizeUrl('sindresorhus.com', {stripProtocol: true});
|
|
100
|
+
//=> 'sindresorhus.com'
|
|
101
|
+
```
|
|
102
|
+
*/
|
|
103
|
+
readonly stripProtocol?: boolean;
|
|
108
104
|
|
|
109
|
-
|
|
105
|
+
/**
|
|
106
|
+
Strip the [text fragment](https://web.dev/text-fragments/) part of the URL
|
|
110
107
|
|
|
111
|
-
|
|
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
|
-
|
|
114
|
-
```
|
|
115
|
-
normalizeUrl('http://sindresorhus.com/about.html#:~:text=hello');
|
|
116
|
-
//=> 'http://sindresorhus.com/about.html#'
|
|
110
|
+
@default true
|
|
117
111
|
|
|
118
|
-
|
|
119
|
-
|
|
112
|
+
@example
|
|
113
|
+
```
|
|
114
|
+
normalizeUrl('http://sindresorhus.com/about.html#:~:text=hello');
|
|
115
|
+
//=> 'http://sindresorhus.com/about.html#'
|
|
120
116
|
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
normalizeUrl('http://sindresorhus.com/about.html#section:~:text=hello');
|
|
118
|
+
//=> 'http://sindresorhus.com/about.html#section'
|
|
123
119
|
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
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
|
-
|
|
136
|
-
```
|
|
137
|
-
normalizeUrl('http://www.sindresorhus.com');
|
|
138
|
-
//=> 'http://sindresorhus.com'
|
|
132
|
+
@default true
|
|
139
133
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 &&
|
|
106
|
-
|
|
101
|
+
if (options.forceHttp && urlObject.protocol === 'https:') {
|
|
102
|
+
urlObject.protocol = 'http:';
|
|
107
103
|
}
|
|
108
104
|
|
|
109
|
-
if (options.forceHttps &&
|
|
110
|
-
|
|
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
|
-
|
|
116
|
-
|
|
111
|
+
urlObject.username = '';
|
|
112
|
+
urlObject.password = '';
|
|
117
113
|
}
|
|
118
114
|
|
|
119
115
|
// Remove hash
|
|
120
116
|
if (options.stripHash) {
|
|
121
|
-
|
|
117
|
+
urlObject.hash = '';
|
|
122
118
|
} else if (options.stripTextFragment) {
|
|
123
|
-
|
|
119
|
+
urlObject.hash = urlObject.hash.replace(/#?:~:text.*?$/i, '');
|
|
124
120
|
}
|
|
125
121
|
|
|
126
122
|
// Remove duplicate slashes if not preceded by a protocol
|
|
127
|
-
|
|
128
|
-
|
|
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 (
|
|
158
|
+
if (urlObject.pathname) {
|
|
133
159
|
try {
|
|
134
|
-
|
|
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 =
|
|
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,
|
|
149
|
-
|
|
174
|
+
pathComponents = pathComponents.slice(0, -1);
|
|
175
|
+
urlObject.pathname = pathComponents.slice(1).join('/') + '/';
|
|
150
176
|
}
|
|
151
177
|
}
|
|
152
178
|
|
|
153
|
-
if (
|
|
179
|
+
if (urlObject.hostname) {
|
|
154
180
|
// Remove trailing dot
|
|
155
|
-
|
|
181
|
+
urlObject.hostname = urlObject.hostname.replace(/\.$/, '');
|
|
156
182
|
|
|
157
183
|
// Remove `www.`
|
|
158
|
-
if (options.stripWWW && /^www\.(?!www\.)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
198
|
+
urlObject.searchParams.delete(key);
|
|
172
199
|
}
|
|
173
200
|
}
|
|
174
201
|
}
|
|
175
202
|
|
|
176
203
|
if (options.removeQueryParameters === true) {
|
|
177
|
-
|
|
204
|
+
urlObject.search = '';
|
|
178
205
|
}
|
|
179
206
|
|
|
180
207
|
// Sort query parameters
|
|
181
208
|
if (options.sortQueryParameters) {
|
|
182
|
-
|
|
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
|
-
|
|
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 =
|
|
224
|
+
urlString = urlObject.toString();
|
|
193
225
|
|
|
194
|
-
if (!options.removeSingleSlash &&
|
|
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 ||
|
|
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": "
|
|
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": ">=
|
|
16
|
+
"node": ">=12.20"
|
|
15
17
|
},
|
|
16
18
|
"scripts": {
|
|
17
|
-
"test": "
|
|
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": "^
|
|
40
|
-
"
|
|
41
|
-
"tsd": "^0.
|
|
42
|
-
"xo": "^0.
|
|
41
|
+
"ava": "^4.0.1",
|
|
42
|
+
"c8": "^7.11.0",
|
|
43
|
+
"tsd": "^0.19.1",
|
|
44
|
+
"xo": "^0.47.0"
|
|
43
45
|
},
|
|
44
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
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>`\
|