1 | (function (global, factory) {
|
---|
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
---|
3 | typeof define === 'function' && define.amd ? define(factory) :
|
---|
4 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.resolveURI = factory());
|
---|
5 | })(this, (function () { 'use strict';
|
---|
6 |
|
---|
7 | // Matches the scheme of a URL, eg "http://"
|
---|
8 | const schemeRegex = /^[\w+.-]+:\/\//;
|
---|
9 | /**
|
---|
10 | * Matches the parts of a URL:
|
---|
11 | * 1. Scheme, including ":", guaranteed.
|
---|
12 | * 2. User/password, including "@", optional.
|
---|
13 | * 3. Host, guaranteed.
|
---|
14 | * 4. Port, including ":", optional.
|
---|
15 | * 5. Path, including "/", optional.
|
---|
16 | * 6. Query, including "?", optional.
|
---|
17 | * 7. Hash, including "#", optional.
|
---|
18 | */
|
---|
19 | const urlRegex = /^([\w+.-]+:)\/\/([^@/#?]*@)?([^:/#?]*)(:\d+)?(\/[^#?]*)?(\?[^#]*)?(#.*)?/;
|
---|
20 | /**
|
---|
21 | * File URLs are weird. They dont' need the regular `//` in the scheme, they may or may not start
|
---|
22 | * with a leading `/`, they can have a domain (but only if they don't start with a Windows drive).
|
---|
23 | *
|
---|
24 | * 1. Host, optional.
|
---|
25 | * 2. Path, which may include "/", guaranteed.
|
---|
26 | * 3. Query, including "?", optional.
|
---|
27 | * 4. Hash, including "#", optional.
|
---|
28 | */
|
---|
29 | const fileRegex = /^file:(?:\/\/((?![a-z]:)[^/#?]*)?)?(\/?[^#?]*)(\?[^#]*)?(#.*)?/i;
|
---|
30 | function isAbsoluteUrl(input) {
|
---|
31 | return schemeRegex.test(input);
|
---|
32 | }
|
---|
33 | function isSchemeRelativeUrl(input) {
|
---|
34 | return input.startsWith('//');
|
---|
35 | }
|
---|
36 | function isAbsolutePath(input) {
|
---|
37 | return input.startsWith('/');
|
---|
38 | }
|
---|
39 | function isFileUrl(input) {
|
---|
40 | return input.startsWith('file:');
|
---|
41 | }
|
---|
42 | function isRelative(input) {
|
---|
43 | return /^[.?#]/.test(input);
|
---|
44 | }
|
---|
45 | function parseAbsoluteUrl(input) {
|
---|
46 | const match = urlRegex.exec(input);
|
---|
47 | return makeUrl(match[1], match[2] || '', match[3], match[4] || '', match[5] || '/', match[6] || '', match[7] || '');
|
---|
48 | }
|
---|
49 | function parseFileUrl(input) {
|
---|
50 | const match = fileRegex.exec(input);
|
---|
51 | const path = match[2];
|
---|
52 | return makeUrl('file:', '', match[1] || '', '', isAbsolutePath(path) ? path : '/' + path, match[3] || '', match[4] || '');
|
---|
53 | }
|
---|
54 | function makeUrl(scheme, user, host, port, path, query, hash) {
|
---|
55 | return {
|
---|
56 | scheme,
|
---|
57 | user,
|
---|
58 | host,
|
---|
59 | port,
|
---|
60 | path,
|
---|
61 | query,
|
---|
62 | hash,
|
---|
63 | type: 7 /* Absolute */,
|
---|
64 | };
|
---|
65 | }
|
---|
66 | function parseUrl(input) {
|
---|
67 | if (isSchemeRelativeUrl(input)) {
|
---|
68 | const url = parseAbsoluteUrl('http:' + input);
|
---|
69 | url.scheme = '';
|
---|
70 | url.type = 6 /* SchemeRelative */;
|
---|
71 | return url;
|
---|
72 | }
|
---|
73 | if (isAbsolutePath(input)) {
|
---|
74 | const url = parseAbsoluteUrl('http://foo.com' + input);
|
---|
75 | url.scheme = '';
|
---|
76 | url.host = '';
|
---|
77 | url.type = 5 /* AbsolutePath */;
|
---|
78 | return url;
|
---|
79 | }
|
---|
80 | if (isFileUrl(input))
|
---|
81 | return parseFileUrl(input);
|
---|
82 | if (isAbsoluteUrl(input))
|
---|
83 | return parseAbsoluteUrl(input);
|
---|
84 | const url = parseAbsoluteUrl('http://foo.com/' + input);
|
---|
85 | url.scheme = '';
|
---|
86 | url.host = '';
|
---|
87 | url.type = input
|
---|
88 | ? input.startsWith('?')
|
---|
89 | ? 3 /* Query */
|
---|
90 | : input.startsWith('#')
|
---|
91 | ? 2 /* Hash */
|
---|
92 | : 4 /* RelativePath */
|
---|
93 | : 1 /* Empty */;
|
---|
94 | return url;
|
---|
95 | }
|
---|
96 | function stripPathFilename(path) {
|
---|
97 | // If a path ends with a parent directory "..", then it's a relative path with excess parent
|
---|
98 | // paths. It's not a file, so we can't strip it.
|
---|
99 | if (path.endsWith('/..'))
|
---|
100 | return path;
|
---|
101 | const index = path.lastIndexOf('/');
|
---|
102 | return path.slice(0, index + 1);
|
---|
103 | }
|
---|
104 | function mergePaths(url, base) {
|
---|
105 | normalizePath(base, base.type);
|
---|
106 | // If the path is just a "/", then it was an empty path to begin with (remember, we're a relative
|
---|
107 | // path).
|
---|
108 | if (url.path === '/') {
|
---|
109 | url.path = base.path;
|
---|
110 | }
|
---|
111 | else {
|
---|
112 | // Resolution happens relative to the base path's directory, not the file.
|
---|
113 | url.path = stripPathFilename(base.path) + url.path;
|
---|
114 | }
|
---|
115 | }
|
---|
116 | /**
|
---|
117 | * The path can have empty directories "//", unneeded parents "foo/..", or current directory
|
---|
118 | * "foo/.". We need to normalize to a standard representation.
|
---|
119 | */
|
---|
120 | function normalizePath(url, type) {
|
---|
121 | const rel = type <= 4 /* RelativePath */;
|
---|
122 | const pieces = url.path.split('/');
|
---|
123 | // We need to preserve the first piece always, so that we output a leading slash. The item at
|
---|
124 | // pieces[0] is an empty string.
|
---|
125 | let pointer = 1;
|
---|
126 | // Positive is the number of real directories we've output, used for popping a parent directory.
|
---|
127 | // Eg, "foo/bar/.." will have a positive 2, and we can decrement to be left with just "foo".
|
---|
128 | let positive = 0;
|
---|
129 | // We need to keep a trailing slash if we encounter an empty directory (eg, splitting "foo/" will
|
---|
130 | // generate `["foo", ""]` pieces). And, if we pop a parent directory. But once we encounter a
|
---|
131 | // real directory, we won't need to append, unless the other conditions happen again.
|
---|
132 | let addTrailingSlash = false;
|
---|
133 | for (let i = 1; i < pieces.length; i++) {
|
---|
134 | const piece = pieces[i];
|
---|
135 | // An empty directory, could be a trailing slash, or just a double "//" in the path.
|
---|
136 | if (!piece) {
|
---|
137 | addTrailingSlash = true;
|
---|
138 | continue;
|
---|
139 | }
|
---|
140 | // If we encounter a real directory, then we don't need to append anymore.
|
---|
141 | addTrailingSlash = false;
|
---|
142 | // A current directory, which we can always drop.
|
---|
143 | if (piece === '.')
|
---|
144 | continue;
|
---|
145 | // A parent directory, we need to see if there are any real directories we can pop. Else, we
|
---|
146 | // have an excess of parents, and we'll need to keep the "..".
|
---|
147 | if (piece === '..') {
|
---|
148 | if (positive) {
|
---|
149 | addTrailingSlash = true;
|
---|
150 | positive--;
|
---|
151 | pointer--;
|
---|
152 | }
|
---|
153 | else if (rel) {
|
---|
154 | // If we're in a relativePath, then we need to keep the excess parents. Else, in an absolute
|
---|
155 | // URL, protocol relative URL, or an absolute path, we don't need to keep excess.
|
---|
156 | pieces[pointer++] = piece;
|
---|
157 | }
|
---|
158 | continue;
|
---|
159 | }
|
---|
160 | // We've encountered a real directory. Move it to the next insertion pointer, which accounts for
|
---|
161 | // any popped or dropped directories.
|
---|
162 | pieces[pointer++] = piece;
|
---|
163 | positive++;
|
---|
164 | }
|
---|
165 | let path = '';
|
---|
166 | for (let i = 1; i < pointer; i++) {
|
---|
167 | path += '/' + pieces[i];
|
---|
168 | }
|
---|
169 | if (!path || (addTrailingSlash && !path.endsWith('/..'))) {
|
---|
170 | path += '/';
|
---|
171 | }
|
---|
172 | url.path = path;
|
---|
173 | }
|
---|
174 | /**
|
---|
175 | * Attempts to resolve `input` URL/path relative to `base`.
|
---|
176 | */
|
---|
177 | function resolve(input, base) {
|
---|
178 | if (!input && !base)
|
---|
179 | return '';
|
---|
180 | const url = parseUrl(input);
|
---|
181 | let inputType = url.type;
|
---|
182 | if (base && inputType !== 7 /* Absolute */) {
|
---|
183 | const baseUrl = parseUrl(base);
|
---|
184 | const baseType = baseUrl.type;
|
---|
185 | switch (inputType) {
|
---|
186 | case 1 /* Empty */:
|
---|
187 | url.hash = baseUrl.hash;
|
---|
188 | // fall through
|
---|
189 | case 2 /* Hash */:
|
---|
190 | url.query = baseUrl.query;
|
---|
191 | // fall through
|
---|
192 | case 3 /* Query */:
|
---|
193 | case 4 /* RelativePath */:
|
---|
194 | mergePaths(url, baseUrl);
|
---|
195 | // fall through
|
---|
196 | case 5 /* AbsolutePath */:
|
---|
197 | // The host, user, and port are joined, you can't copy one without the others.
|
---|
198 | url.user = baseUrl.user;
|
---|
199 | url.host = baseUrl.host;
|
---|
200 | url.port = baseUrl.port;
|
---|
201 | // fall through
|
---|
202 | case 6 /* SchemeRelative */:
|
---|
203 | // The input doesn't have a schema at least, so we need to copy at least that over.
|
---|
204 | url.scheme = baseUrl.scheme;
|
---|
205 | }
|
---|
206 | if (baseType > inputType)
|
---|
207 | inputType = baseType;
|
---|
208 | }
|
---|
209 | normalizePath(url, inputType);
|
---|
210 | const queryHash = url.query + url.hash;
|
---|
211 | switch (inputType) {
|
---|
212 | // This is impossible, because of the empty checks at the start of the function.
|
---|
213 | // case UrlType.Empty:
|
---|
214 | case 2 /* Hash */:
|
---|
215 | case 3 /* Query */:
|
---|
216 | return queryHash;
|
---|
217 | case 4 /* RelativePath */: {
|
---|
218 | // The first char is always a "/", and we need it to be relative.
|
---|
219 | const path = url.path.slice(1);
|
---|
220 | if (!path)
|
---|
221 | return queryHash || '.';
|
---|
222 | if (isRelative(base || input) && !isRelative(path)) {
|
---|
223 | // If base started with a leading ".", or there is no base and input started with a ".",
|
---|
224 | // then we need to ensure that the relative path starts with a ".". We don't know if
|
---|
225 | // relative starts with a "..", though, so check before prepending.
|
---|
226 | return './' + path + queryHash;
|
---|
227 | }
|
---|
228 | return path + queryHash;
|
---|
229 | }
|
---|
230 | case 5 /* AbsolutePath */:
|
---|
231 | return url.path + queryHash;
|
---|
232 | default:
|
---|
233 | return url.scheme + '//' + url.user + url.host + url.port + url.path + queryHash;
|
---|
234 | }
|
---|
235 | }
|
---|
236 |
|
---|
237 | return resolve;
|
---|
238 |
|
---|
239 | }));
|
---|
240 | //# sourceMappingURL=resolve-uri.umd.js.map
|
---|