1 | (function (global, factory) {
|
---|
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
---|
3 | typeof define === 'function' && define.amd ? define(factory) :
|
---|
4 | (global = global || self, global.resolveURI = factory());
|
---|
5 | }(this, function () { 'use strict';
|
---|
6 |
|
---|
7 | /* istanbul ignore next */
|
---|
8 | const Url = (typeof URL !== 'undefined' ? URL : require('url').URL);
|
---|
9 | // Matches "..", which must be preceeded by "/" or the start of the string, and
|
---|
10 | // must be followed by a "/". We do not eat the following "/", so that the next
|
---|
11 | // iteration can match on it.
|
---|
12 | const parentRegex = /(^|\/)\.\.(?=\/|$)/g;
|
---|
13 | function isAbsoluteUrl(url) {
|
---|
14 | try {
|
---|
15 | return !!new Url(url);
|
---|
16 | }
|
---|
17 | catch (e) {
|
---|
18 | return false;
|
---|
19 | }
|
---|
20 | }
|
---|
21 | /**
|
---|
22 | * Creates a directory name that is guaranteed to not be in `str`.
|
---|
23 | */
|
---|
24 | function uniqInStr(str) {
|
---|
25 | let uniq = String(Math.random()).slice(2);
|
---|
26 | while (str.indexOf(uniq) > -1) {
|
---|
27 | /* istanbul ignore next */
|
---|
28 | uniq += uniq;
|
---|
29 | }
|
---|
30 | return uniq;
|
---|
31 | }
|
---|
32 | /**
|
---|
33 | * Removes the filename from the path (everything trailing the last "/"). This
|
---|
34 | * is only safe to call on a path, never call with an absolute or protocol
|
---|
35 | * relative URL.
|
---|
36 | */
|
---|
37 | function stripPathFilename(path) {
|
---|
38 | path = normalizePath(path);
|
---|
39 | const index = path.lastIndexOf('/');
|
---|
40 | return path.slice(0, index + 1);
|
---|
41 | }
|
---|
42 | /**
|
---|
43 | * Normalizes a protocol-relative URL, but keeps it protocol relative by
|
---|
44 | * stripping out the protocl before returning it.
|
---|
45 | */
|
---|
46 | function normalizeProtocolRelative(input, absoluteBase) {
|
---|
47 | const { href, protocol } = new Url(input, absoluteBase);
|
---|
48 | return href.slice(protocol.length);
|
---|
49 | }
|
---|
50 | /**
|
---|
51 | * Normalizes a simple path (one that has no ".."s, or is absolute so ".."s can
|
---|
52 | * be normalized absolutely).
|
---|
53 | */
|
---|
54 | function normalizeSimplePath(input) {
|
---|
55 | const { href } = new Url(input, 'https://foo.com/');
|
---|
56 | return href.slice('https://foo.com/'.length);
|
---|
57 | }
|
---|
58 | /**
|
---|
59 | * Normalizes a path, ensuring that excess ".."s are preserved for relative
|
---|
60 | * paths in the output.
|
---|
61 | *
|
---|
62 | * If the input is absolute, this will return an absolutey normalized path, but
|
---|
63 | * it will not have a leading "/".
|
---|
64 | *
|
---|
65 | * If the input has a leading "..", the output will have a leading "..".
|
---|
66 | *
|
---|
67 | * If the input has a leading ".", the output will not have a leading "."
|
---|
68 | * unless there are too many ".."s, in which case there will be a leading "..".
|
---|
69 | */
|
---|
70 | function normalizePath(input) {
|
---|
71 | // If there are no ".."s, we can treat this as if it were an absolute path.
|
---|
72 | // The return won't be an absolute path, so it's easy.
|
---|
73 | if (!parentRegex.test(input))
|
---|
74 | return normalizeSimplePath(input);
|
---|
75 | // We already found one "..". Let's see how many there are.
|
---|
76 | let total = 1;
|
---|
77 | while (parentRegex.test(input))
|
---|
78 | total++;
|
---|
79 | // If there are ".."s, we need to prefix the the path with the same number of
|
---|
80 | // unique directories. This is to ensure that we "remember" how many parent
|
---|
81 | // directories we are accessing. Eg, "../../.." must keep 3, and "foo/../.."
|
---|
82 | // must keep 1.
|
---|
83 | const uniqDirectory = `z${uniqInStr(input)}/`;
|
---|
84 | // uniqDirectory is just a "z", followed by numbers, followed by a "/". So
|
---|
85 | // generating a runtime regex from it is safe. We'll use this search regex to
|
---|
86 | // strip out our uniq directory names and insert any needed ".."s.
|
---|
87 | const search = new RegExp(`^(?:${uniqDirectory})*`);
|
---|
88 | // Now we can resolve the total path. If there are excess ".."s, they will
|
---|
89 | // eliminate one or more of the unique directories we prefix with.
|
---|
90 | const relative = normalizeSimplePath(uniqDirectory.repeat(total) + input);
|
---|
91 | // We can now count the number of unique directories that were eliminated. If
|
---|
92 | // there were 3, and 1 was eliminated, we know we only need to add 1 "..". If
|
---|
93 | // 2 were eliminated, we need to insert 2 ".."s. If all 3 were eliminated,
|
---|
94 | // then we need 3, etc. This replace is guranteed to match (it may match 0 or
|
---|
95 | // more times), and we can count the total match to see how many were eliminated.
|
---|
96 | return relative.replace(search, (all) => {
|
---|
97 | const leftover = all.length / uniqDirectory.length;
|
---|
98 | return '../'.repeat(total - leftover);
|
---|
99 | });
|
---|
100 | }
|
---|
101 | /**
|
---|
102 | * Attempts to resolve `input` URL relative to `base`.
|
---|
103 | */
|
---|
104 | function resolve(input, base) {
|
---|
105 | if (!base)
|
---|
106 | base = '';
|
---|
107 | // Absolute URLs are very easy to resolve right.
|
---|
108 | if (isAbsoluteUrl(input))
|
---|
109 | return new Url(input).href;
|
---|
110 | if (base) {
|
---|
111 | // Absolute URLs are easy...
|
---|
112 | if (isAbsoluteUrl(base))
|
---|
113 | return new Url(input, base).href;
|
---|
114 | // If base is protocol relative, we'll resolve with it but keep the result
|
---|
115 | // protocol relative.
|
---|
116 | if (base.startsWith('//'))
|
---|
117 | return normalizeProtocolRelative(input, `https:${base}`);
|
---|
118 | }
|
---|
119 | // Normalize input, but keep it protocol relative. We know base doesn't supply
|
---|
120 | // a protocol, because that would have been handled above.
|
---|
121 | if (input.startsWith('//'))
|
---|
122 | return normalizeProtocolRelative(input, 'https://foo.com/');
|
---|
123 | // We now know that base (if there is one) and input are paths. We've handled
|
---|
124 | // both absolute and protocol-relative variations above.
|
---|
125 | // Absolute paths don't need any special handling, because they cannot have
|
---|
126 | // extra "." or ".."s. That'll all be stripped away. Input takes priority here,
|
---|
127 | // because if input is an absolute path, base path won't affect it in any way.
|
---|
128 | if (input.startsWith('/'))
|
---|
129 | return '/' + normalizeSimplePath(input);
|
---|
130 | // Since input and base are paths, we need to join them to do any further
|
---|
131 | // processing. Paths are joined at the directory level, so we need to remove
|
---|
132 | // the base's filename before joining. We also know that input does not have a
|
---|
133 | // leading slash, and that the stripped base will have a trailing slash if
|
---|
134 | // there are any directories (or it'll be empty).
|
---|
135 | const joined = stripPathFilename(base) + input;
|
---|
136 | // If base is an absolute path, then input will be relative to it.
|
---|
137 | if (base.startsWith('/'))
|
---|
138 | return '/' + normalizeSimplePath(joined);
|
---|
139 | // We now know both base (if there is one) and input are relative paths.
|
---|
140 | const relative = normalizePath(joined);
|
---|
141 | // If base started with a leading ".", or there is no base and input started
|
---|
142 | // with a ".", then we need to ensure that the relative path starts with a
|
---|
143 | // ".". We don't know if relative starts with a "..", though, so check before
|
---|
144 | // prepending.
|
---|
145 | if ((base || input).startsWith('.') && !relative.startsWith('.')) {
|
---|
146 | return './' + relative;
|
---|
147 | }
|
---|
148 | return relative;
|
---|
149 | }
|
---|
150 |
|
---|
151 | return resolve;
|
---|
152 |
|
---|
153 | }));
|
---|
154 | //# sourceMappingURL=resolve-uri.umd.js.map
|
---|