normalize-url 4.4.0 → 5.0.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/index.d.ts +17 -24
- package/index.js +33 -49
- package/package.json +6 -5
- package/readme.md +13 -17
package/index.d.ts
CHANGED
|
@@ -188,29 +188,22 @@ declare namespace normalizeUrl {
|
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
(url: string, options?: normalizeUrl.Options): string;
|
|
209
|
-
|
|
210
|
-
// TODO: Remove this for the next major release, refactor the whole definition to:
|
|
211
|
-
// declare function normalizeUrl(url: string, options?: normalizeUrl.Options): string;
|
|
212
|
-
// export = normalizeUrl;
|
|
213
|
-
default: typeof normalizeUrl;
|
|
214
|
-
};
|
|
191
|
+
/**
|
|
192
|
+
[Normalize](https://en.wikipedia.org/wiki/URL_normalization) a URL.
|
|
193
|
+
|
|
194
|
+
@param url - URL to normalize, including [data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs).
|
|
195
|
+
|
|
196
|
+
@example
|
|
197
|
+
```
|
|
198
|
+
import normalizeUrl = require('normalize-url');
|
|
199
|
+
|
|
200
|
+
normalizeUrl('sindresorhus.com');
|
|
201
|
+
//=> 'http://sindresorhus.com'
|
|
202
|
+
|
|
203
|
+
normalizeUrl('HTTP://xn--xample-hva.com:80/?b=bar&a=foo');
|
|
204
|
+
//=> 'http://êxample.com/?a=foo&b=bar'
|
|
205
|
+
```
|
|
206
|
+
*/
|
|
207
|
+
declare function normalizeUrl(url: string, options?: normalizeUrl.Options): string;
|
|
215
208
|
|
|
216
209
|
export = normalizeUrl;
|
package/index.js
CHANGED
|
@@ -1,56 +1,62 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
|
|
4
|
+
const DATA_URL_DEFAULT_MIME_TYPE = 'text/plain';
|
|
5
|
+
const DATA_URL_DEFAULT_CHARSET = 'us-ascii';
|
|
4
6
|
|
|
5
7
|
const testParameter = (name, filters) => {
|
|
6
8
|
return filters.some(filter => filter instanceof RegExp ? filter.test(name) : filter === name);
|
|
7
9
|
};
|
|
8
10
|
|
|
9
|
-
const normalizeDataURL = urlString => {
|
|
10
|
-
const
|
|
11
|
+
const normalizeDataURL = (urlString, {stripHash}) => {
|
|
12
|
+
const match = /^data:(?<type>.*?),(?<data>.*?)(?:#(?<hash>.*))?$/.exec(urlString);
|
|
11
13
|
|
|
12
|
-
if (!
|
|
14
|
+
if (!match) {
|
|
13
15
|
throw new Error(`Invalid URL: ${urlString}`);
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
let base64 = false;
|
|
18
|
+
let {type, data, hash} = match.groups;
|
|
19
|
+
const mediaType = type.split(';');
|
|
20
|
+
hash = stripHash ? '' : hash;
|
|
20
21
|
|
|
22
|
+
let isBase64 = false;
|
|
21
23
|
if (mediaType[mediaType.length - 1] === 'base64') {
|
|
22
24
|
mediaType.pop();
|
|
23
|
-
|
|
25
|
+
isBase64 = true;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
// Lowercase MIME type
|
|
27
29
|
const mimeType = (mediaType.shift() || '').toLowerCase();
|
|
28
30
|
const attributes = mediaType
|
|
29
|
-
.filter(Boolean)
|
|
30
31
|
.map(attribute => {
|
|
31
32
|
let [key, value = ''] = attribute.split('=').map(string => string.trim());
|
|
32
33
|
|
|
33
34
|
// Lowercase `charset`
|
|
34
35
|
if (key === 'charset') {
|
|
35
36
|
value = value.toLowerCase();
|
|
37
|
+
|
|
38
|
+
if (value === DATA_URL_DEFAULT_CHARSET) {
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
return `${key}
|
|
39
|
-
})
|
|
43
|
+
return `${key}${value ? `=${value}` : ''}`;
|
|
44
|
+
})
|
|
45
|
+
.filter(Boolean);
|
|
40
46
|
|
|
41
47
|
const normalizedMediaType = [
|
|
42
48
|
...attributes
|
|
43
49
|
];
|
|
44
50
|
|
|
45
|
-
if (
|
|
51
|
+
if (isBase64) {
|
|
46
52
|
normalizedMediaType.push('base64');
|
|
47
53
|
}
|
|
48
54
|
|
|
49
|
-
if (normalizedMediaType.length !== 0 || mimeType) {
|
|
55
|
+
if (normalizedMediaType.length !== 0 || (mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE)) {
|
|
50
56
|
normalizedMediaType.unshift(mimeType);
|
|
51
57
|
}
|
|
52
58
|
|
|
53
|
-
return `data:${normalizedMediaType.join(';')},${
|
|
59
|
+
return `data:${normalizedMediaType.join(';')},${isBase64 ? data.trim() : data}${hash ? `#${hash}` : ''}`;
|
|
54
60
|
};
|
|
55
61
|
|
|
56
62
|
const normalizeUrl = (urlString, options) => {
|
|
@@ -69,30 +75,22 @@ const normalizeUrl = (urlString, options) => {
|
|
|
69
75
|
...options
|
|
70
76
|
};
|
|
71
77
|
|
|
72
|
-
|
|
73
|
-
if (Reflect.has(options, 'normalizeHttps')) {
|
|
74
|
-
throw new Error('options.normalizeHttps is renamed to options.forceHttp');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (Reflect.has(options, 'normalizeHttp')) {
|
|
78
|
-
throw new Error('options.normalizeHttp is renamed to options.forceHttps');
|
|
79
|
-
}
|
|
78
|
+
urlString = urlString.trim();
|
|
80
79
|
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
// Data URL
|
|
81
|
+
if (/^data:/i.test(urlString)) {
|
|
82
|
+
return normalizeDataURL(urlString, options);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
urlString = urlString.trim();
|
|
86
|
-
|
|
87
85
|
const hasRelativeProtocol = urlString.startsWith('//');
|
|
88
86
|
const isRelativeUrl = !hasRelativeProtocol && /^\.*\//.test(urlString);
|
|
89
87
|
|
|
90
88
|
// Prepend protocol
|
|
91
|
-
if (!isRelativeUrl
|
|
89
|
+
if (!isRelativeUrl) {
|
|
92
90
|
urlString = urlString.replace(/^(?!(?:\w+:)?\/\/)|^\/\//, options.defaultProtocol);
|
|
93
91
|
}
|
|
94
92
|
|
|
95
|
-
const urlObj = new
|
|
93
|
+
const urlObj = new URL(urlString);
|
|
96
94
|
|
|
97
95
|
if (options.forceHttp && options.forceHttps) {
|
|
98
96
|
throw new Error('The `forceHttp` and `forceHttps` options cannot be used together');
|
|
@@ -119,20 +117,14 @@ const normalizeUrl = (urlString, options) => {
|
|
|
119
117
|
|
|
120
118
|
// Remove duplicate slashes if not preceded by a protocol
|
|
121
119
|
if (urlObj.pathname) {
|
|
122
|
-
|
|
123
|
-
// `urlObj.pathname = urlObj.pathname.replace(/(?<!https?:)\/{2,}/g, '/');`
|
|
124
|
-
urlObj.pathname = urlObj.pathname.replace(/((?!:).|^)\/{2,}/g, (_, p1) => {
|
|
125
|
-
if (/^(?!\/)/g.test(p1)) {
|
|
126
|
-
return `${p1}/`;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return '/';
|
|
130
|
-
});
|
|
120
|
+
urlObj.pathname = urlObj.pathname.replace(/(?<!https?:)\/{2,}/g, '/');
|
|
131
121
|
}
|
|
132
122
|
|
|
133
123
|
// Decode URI octets
|
|
134
124
|
if (urlObj.pathname) {
|
|
135
|
-
|
|
125
|
+
try {
|
|
126
|
+
urlObj.pathname = decodeURI(urlObj.pathname);
|
|
127
|
+
} catch (_) {}
|
|
136
128
|
}
|
|
137
129
|
|
|
138
130
|
// Remove directory index
|
|
@@ -155,7 +147,7 @@ const normalizeUrl = (urlString, options) => {
|
|
|
155
147
|
urlObj.hostname = urlObj.hostname.replace(/\.$/, '');
|
|
156
148
|
|
|
157
149
|
// Remove `www.`
|
|
158
|
-
if (options.stripWWW && /^www\.([a-z\-\d]{2,63})\.([a-z.]{2,5})$/.test(urlObj.hostname)) {
|
|
150
|
+
if (options.stripWWW && /^www\.(?:[a-z\-\d]{2,63})\.(?:[a-z.]{2,5})$/.test(urlObj.hostname)) {
|
|
159
151
|
// Each label should be max 63 at length (min: 2).
|
|
160
152
|
// The extension should be max 5 at length (min: 2).
|
|
161
153
|
// Source: https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names
|
|
@@ -177,12 +169,6 @@ const normalizeUrl = (urlString, options) => {
|
|
|
177
169
|
urlObj.searchParams.sort();
|
|
178
170
|
}
|
|
179
171
|
|
|
180
|
-
// Data URL
|
|
181
|
-
if (urlObj.protocol === 'data:') {
|
|
182
|
-
const url = normalizeDataURL(`${urlObj.protocol}${urlObj.pathname}`);
|
|
183
|
-
return `${url}${urlObj.search}${urlObj.hash}`;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
172
|
if (options.removeTrailingSlash) {
|
|
187
173
|
urlObj.pathname = urlObj.pathname.replace(/\/$/, '');
|
|
188
174
|
}
|
|
@@ -209,5 +195,3 @@ const normalizeUrl = (urlString, options) => {
|
|
|
209
195
|
};
|
|
210
196
|
|
|
211
197
|
module.exports = normalizeUrl;
|
|
212
|
-
// TODO: Remove this for the next major release
|
|
213
|
-
module.exports.default = normalizeUrl;
|
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "normalize-url",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "Normalize a URL",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "sindresorhus/normalize-url",
|
|
7
|
+
"funding": "https://github.com/sponsors/sindresorhus",
|
|
7
8
|
"author": {
|
|
8
9
|
"name": "Sindre Sorhus",
|
|
9
10
|
"email": "sindresorhus@gmail.com",
|
|
10
11
|
"url": "sindresorhus.com"
|
|
11
12
|
},
|
|
12
13
|
"engines": {
|
|
13
|
-
"node": ">=
|
|
14
|
+
"node": ">=10"
|
|
14
15
|
},
|
|
15
16
|
"scripts": {
|
|
16
17
|
"test": "xo && nyc ava && tsd"
|
|
@@ -37,8 +38,8 @@
|
|
|
37
38
|
"devDependencies": {
|
|
38
39
|
"ava": "^2.4.0",
|
|
39
40
|
"coveralls": "^3.0.6",
|
|
40
|
-
"nyc": "^
|
|
41
|
-
"tsd": "^0.
|
|
42
|
-
"xo": "^0.
|
|
41
|
+
"nyc": "^15.0.0",
|
|
42
|
+
"tsd": "^0.11.0",
|
|
43
|
+
"xo": "^0.25.3"
|
|
43
44
|
}
|
|
44
45
|
}
|
package/readme.md
CHANGED
|
@@ -4,14 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
Useful when you need to display, store, deduplicate, sort, compare, etc, URLs.
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
## Install
|
|
9
8
|
|
|
10
9
|
```
|
|
11
10
|
$ npm install normalize-url
|
|
12
11
|
```
|
|
13
12
|
|
|
14
|
-
|
|
15
13
|
## Usage
|
|
16
14
|
|
|
17
15
|
```js
|
|
@@ -24,7 +22,6 @@ normalizeUrl('HTTP://xn--xample-hva.com:80/?b=bar&a=foo');
|
|
|
24
22
|
//=> 'http://êxample.com/?a=foo&b=bar'
|
|
25
23
|
```
|
|
26
24
|
|
|
27
|
-
|
|
28
25
|
## API
|
|
29
26
|
|
|
30
27
|
### normalizeUrl(url, options?)
|
|
@@ -41,12 +38,12 @@ Type: `object`
|
|
|
41
38
|
|
|
42
39
|
##### defaultProtocol
|
|
43
40
|
|
|
44
|
-
Type: `string
|
|
41
|
+
Type: `string`\
|
|
45
42
|
Default: `http:`
|
|
46
43
|
|
|
47
44
|
##### normalizeProtocol
|
|
48
45
|
|
|
49
|
-
Type: `boolean
|
|
46
|
+
Type: `boolean`\
|
|
50
47
|
Default: `true`
|
|
51
48
|
|
|
52
49
|
Prepend `defaultProtocol` to the URL if it's protocol-relative.
|
|
@@ -61,7 +58,7 @@ normalizeUrl('//sindresorhus.com:80/', {normalizeProtocol: false});
|
|
|
61
58
|
|
|
62
59
|
##### forceHttp
|
|
63
60
|
|
|
64
|
-
Type: `boolean
|
|
61
|
+
Type: `boolean`\
|
|
65
62
|
Default: `false`
|
|
66
63
|
|
|
67
64
|
Normalize `https:` to `http:`.
|
|
@@ -76,7 +73,7 @@ normalizeUrl('https://sindresorhus.com:80/', {forceHttp: true});
|
|
|
76
73
|
|
|
77
74
|
##### forceHttps
|
|
78
75
|
|
|
79
|
-
Type: `boolean
|
|
76
|
+
Type: `boolean`\
|
|
80
77
|
Default: `false`
|
|
81
78
|
|
|
82
79
|
Normalize `http:` to `https:`.
|
|
@@ -93,7 +90,7 @@ This option can't be used with the `forceHttp` option at the same time.
|
|
|
93
90
|
|
|
94
91
|
##### stripAuthentication
|
|
95
92
|
|
|
96
|
-
Type: `boolean
|
|
93
|
+
Type: `boolean`\
|
|
97
94
|
Default: `true`
|
|
98
95
|
|
|
99
96
|
Strip the [authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) part of the URL.
|
|
@@ -108,7 +105,7 @@ normalizeUrl('user:password@sindresorhus.com', {stripAuthentication: false});
|
|
|
108
105
|
|
|
109
106
|
##### stripHash
|
|
110
107
|
|
|
111
|
-
Type: `boolean
|
|
108
|
+
Type: `boolean`\
|
|
112
109
|
Default: `false`
|
|
113
110
|
|
|
114
111
|
Strip the hash part of the URL.
|
|
@@ -123,7 +120,7 @@ normalizeUrl('sindresorhus.com/about.html#contact', {stripHash: true});
|
|
|
123
120
|
|
|
124
121
|
##### stripProtocol
|
|
125
122
|
|
|
126
|
-
Type: `boolean
|
|
123
|
+
Type: `boolean`\
|
|
127
124
|
Default: `false`
|
|
128
125
|
|
|
129
126
|
Remove HTTP(S) protocol from the URL: `http://sindresorhus.com` → `sindresorhus.com`.
|
|
@@ -132,13 +129,13 @@ Remove HTTP(S) protocol from the URL: `http://sindresorhus.com` → `sindresorhu
|
|
|
132
129
|
normalizeUrl('https://sindresorhus.com');
|
|
133
130
|
//=> 'https://sindresorhus.com'
|
|
134
131
|
|
|
135
|
-
normalizeUrl('sindresorhus.com', {stripProtocol: true});
|
|
132
|
+
normalizeUrl('https://sindresorhus.com', {stripProtocol: true});
|
|
136
133
|
//=> 'sindresorhus.com'
|
|
137
134
|
```
|
|
138
135
|
|
|
139
136
|
##### stripWWW
|
|
140
137
|
|
|
141
|
-
Type: `boolean
|
|
138
|
+
Type: `boolean`\
|
|
142
139
|
Default: `true`
|
|
143
140
|
|
|
144
141
|
Remove `www.` from the URL.
|
|
@@ -153,7 +150,7 @@ normalizeUrl('http://www.sindresorhus.com', {stripWWW: false});
|
|
|
153
150
|
|
|
154
151
|
##### removeQueryParameters
|
|
155
152
|
|
|
156
|
-
Type: `Array<RegExp | string
|
|
153
|
+
Type: `Array<RegExp | string>`\
|
|
157
154
|
Default: `[/^utm_\w+/i]`
|
|
158
155
|
|
|
159
156
|
Remove query parameters that matches any of the provided strings or regexes.
|
|
@@ -167,7 +164,7 @@ normalizeUrl('www.sindresorhus.com?foo=bar&ref=test_ref', {
|
|
|
167
164
|
|
|
168
165
|
##### removeTrailingSlash
|
|
169
166
|
|
|
170
|
-
Type: `boolean
|
|
167
|
+
Type: `boolean`\
|
|
171
168
|
Default: `true`
|
|
172
169
|
|
|
173
170
|
Remove trailing slash.
|
|
@@ -187,7 +184,7 @@ normalizeUrl('http://sindresorhus.com/', {removeTrailingSlash: false});
|
|
|
187
184
|
|
|
188
185
|
##### removeDirectoryIndex
|
|
189
186
|
|
|
190
|
-
Type: `boolean | Array<RegExp | string
|
|
187
|
+
Type: `boolean | Array<RegExp | string>`\
|
|
191
188
|
Default: `false`
|
|
192
189
|
|
|
193
190
|
Removes the default directory index file from path that matches any of the provided strings or regexes. When `true`, the regex `/^index\.[a-z]+$/` is used.
|
|
@@ -201,7 +198,7 @@ normalizeUrl('www.sindresorhus.com/foo/default.php', {
|
|
|
201
198
|
|
|
202
199
|
##### sortQueryParameters
|
|
203
200
|
|
|
204
|
-
Type: `boolean
|
|
201
|
+
Type: `boolean`\
|
|
205
202
|
Default: `true`
|
|
206
203
|
|
|
207
204
|
Sorts the query parameters alphabetically by key.
|
|
@@ -213,7 +210,6 @@ normalizeUrl('www.sindresorhus.com?b=two&a=one&c=three', {
|
|
|
213
210
|
//=> 'http://sindresorhus.com/?b=two&a=one&c=three'
|
|
214
211
|
```
|
|
215
212
|
|
|
216
|
-
|
|
217
213
|
## Related
|
|
218
214
|
|
|
219
215
|
- [compare-urls](https://github.com/sindresorhus/compare-urls) - Compare URLs by first normalizing them
|