{"version":3,"file":"resolve-uri.mjs","sources":["../src/resolve-uri.ts"],"sourcesContent":["type WhatWgUrl = import('url').URL;\ninterface Url extends WhatWgUrl {\n new (input: string, base?: string): WhatWgUrl;\n}\ndeclare var URL: unknown;\n\n/* istanbul ignore next */\nconst Url = (typeof URL !== 'undefined' ? URL : require('url').URL) as Url;\n\n// Matches \"..\", which must be preceeded by \"/\" or the start of the string, and\n// must be followed by a \"/\". We do not eat the following \"/\", so that the next\n// iteration can match on it.\nconst parentRegex = /(^|\\/)\\.\\.(?=\\/|$)/g;\n\nfunction isAbsoluteUrl(url: string): boolean {\n try {\n return !!new Url(url);\n } catch (e) {\n return false;\n }\n}\n\n/**\n * Creates a directory name that is guaranteed to not be in `str`.\n */\nfunction uniqInStr(str: string): string {\n let uniq = String(Math.random()).slice(2);\n while (str.indexOf(uniq) > -1) {\n /* istanbul ignore next */\n uniq += uniq;\n }\n return uniq;\n}\n\n/**\n * Removes the filename from the path (everything trailing the last \"/\"). This\n * is only safe to call on a path, never call with an absolute or protocol\n * relative URL.\n */\nfunction stripPathFilename(path: string): string {\n path = normalizePath(path);\n const index = path.lastIndexOf('/');\n return path.slice(0, index + 1);\n}\n\n/**\n * Normalizes a protocol-relative URL, but keeps it protocol relative by\n * stripping out the protocl before returning it.\n */\nfunction normalizeProtocolRelative(input: string, absoluteBase: string): string {\n const { href, protocol } = new Url(input, absoluteBase);\n return href.slice(protocol.length);\n}\n\n/**\n * Normalizes a simple path (one that has no \"..\"s, or is absolute so \"..\"s can\n * be normalized absolutely).\n */\nfunction normalizeSimplePath(input: string): string {\n const { href } = new Url(input, 'https://foo.com/');\n return href.slice('https://foo.com/'.length);\n}\n\n/**\n * Normalizes a path, ensuring that excess \"..\"s are preserved for relative\n * paths in the output.\n *\n * If the input is absolute, this will return an absolutey normalized path, but\n * it will not have a leading \"/\".\n *\n * If the input has a leading \"..\", the output will have a leading \"..\".\n *\n * If the input has a leading \".\", the output will not have a leading \".\"\n * unless there are too many \"..\"s, in which case there will be a leading \"..\".\n */\nfunction normalizePath(input: string): string {\n // If there are no \"..\"s, we can treat this as if it were an absolute path.\n // The return won't be an absolute path, so it's easy.\n if (!parentRegex.test(input)) return normalizeSimplePath(input);\n\n // We already found one \"..\". Let's see how many there are.\n let total = 1;\n while (parentRegex.test(input)) total++;\n\n // If there are \"..\"s, we need to prefix the the path with the same number of\n // unique directories. This is to ensure that we \"remember\" how many parent\n // directories we are accessing. Eg, \"../../..\" must keep 3, and \"foo/../..\"\n // must keep 1.\n const uniqDirectory = `z${uniqInStr(input)}/`;\n\n // uniqDirectory is just a \"z\", followed by numbers, followed by a \"/\". So\n // generating a runtime regex from it is safe. We'll use this search regex to\n // strip out our uniq directory names and insert any needed \"..\"s.\n const search = new RegExp(`^(?:${uniqDirectory})*`);\n\n // Now we can resolve the total path. If there are excess \"..\"s, they will\n // eliminate one or more of the unique directories we prefix with.\n const relative = normalizeSimplePath(uniqDirectory.repeat(total) + input);\n\n // We can now count the number of unique directories that were eliminated. If\n // there were 3, and 1 was eliminated, we know we only need to add 1 \"..\". If\n // 2 were eliminated, we need to insert 2 \"..\"s. If all 3 were eliminated,\n // then we need 3, etc. This replace is guranteed to match (it may match 0 or\n // more times), and we can count the total match to see how many were eliminated.\n return relative.replace(search, (all: string) => {\n const leftover = all.length / uniqDirectory.length;\n return '../'.repeat(total - leftover);\n });\n}\n\n/**\n * Attempts to resolve `input` URL relative to `base`.\n */\nexport default function resolve(input: string, base: string | undefined): string {\n if (!base) base = '';\n\n // Absolute URLs are very easy to resolve right.\n if (isAbsoluteUrl(input)) return new Url(input).href;\n\n if (base) {\n // Absolute URLs are easy...\n if (isAbsoluteUrl(base)) return new Url(input, base).href;\n\n // If base is protocol relative, we'll resolve with it but keep the result\n // protocol relative.\n if (base.startsWith('//')) return normalizeProtocolRelative(input, `https:${base}`);\n }\n\n // Normalize input, but keep it protocol relative. We know base doesn't supply\n // a protocol, because that would have been handled above.\n if (input.startsWith('//')) return normalizeProtocolRelative(input, 'https://foo.com/');\n\n // We now know that base (if there is one) and input are paths. We've handled\n // both absolute and protocol-relative variations above.\n\n // Absolute paths don't need any special handling, because they cannot have\n // extra \".\" or \"..\"s. That'll all be stripped away. Input takes priority here,\n // because if input is an absolute path, base path won't affect it in any way.\n if (input.startsWith('/')) return '/' + normalizeSimplePath(input);\n\n // Since input and base are paths, we need to join them to do any further\n // processing. Paths are joined at the directory level, so we need to remove\n // the base's filename before joining. We also know that input does not have a\n // leading slash, and that the stripped base will have a trailing slash if\n // there are any directories (or it'll be empty).\n const joined = stripPathFilename(base) + input;\n\n // If base is an absolute path, then input will be relative to it.\n if (base.startsWith('/')) return '/' + normalizeSimplePath(joined);\n\n // We now know both base (if there is one) and input are relative paths.\n const relative = normalizePath(joined);\n\n // If base started with a leading \".\", or there is no base and input started\n // with a \".\", then we need to ensure that the relative path starts with a\n // \".\". We don't know if relative starts with a \"..\", though, so check before\n // prepending.\n if ((base || input).startsWith('.') && !relative.startsWith('.')) {\n return './' + relative;\n }\n\n return relative;\n}\n"],"names":[],"mappings":"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"}