Files
documentation/node_modules/@docusaurus/utils/lib/urlUtils.js
2024-03-22 03:47:51 +05:30

256 lines
9.8 KiB
JavaScript

"use strict";
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.hasSSHProtocol = exports.buildHttpsUrl = exports.buildSshUrl = exports.removeTrailingSlash = exports.addTrailingSlash = exports.addLeadingSlash = exports.resolvePathname = exports.serializeURLPath = exports.parseURLPath = exports.isValidPathname = exports.encodePath = exports.fileToPath = exports.getEditUrl = exports.normalizeUrl = void 0;
const tslib_1 = require("tslib");
const resolve_pathname_1 = tslib_1.__importDefault(require("resolve-pathname"));
const jsUtils_1 = require("./jsUtils");
/**
* Much like `path.join`, but much better. Takes an array of URL segments, and
* joins them into a reasonable URL.
*
* - `["file:", "/home", "/user/", "website"]` => `file:///home/user/website`
* - `["file://", "home", "/user/", "website"]` => `file://home/user/website` (relative!)
* - Remove trailing slash before parameters or hash.
* - Replace `?` in query parameters with `&`.
* - Dedupe forward slashes in the entire path, avoiding protocol slashes.
*
* @throws {TypeError} If any of the URL segment is not a string, this throws.
*/
function normalizeUrl(rawUrls) {
const urls = [...rawUrls];
const resultArray = [];
let hasStartingSlash = false;
let hasEndingSlash = false;
const isNonEmptyArray = (arr) => arr.length > 0;
if (!isNonEmptyArray(urls)) {
return '';
}
// If the first part is a plain protocol, we combine it with the next part.
if (urls[0].match(/^[^/:]+:\/*$/) && urls.length > 1) {
const first = urls.shift();
if (first.startsWith('file:') && urls[0].startsWith('/')) {
// Force a double slash here, else we lose the information that the next
// segment is an absolute path
urls[0] = `${first}//${urls[0]}`;
}
else {
urls[0] = first + urls[0];
}
}
// There must be two or three slashes in the file protocol,
// two slashes in anything else.
const replacement = urls[0].match(/^file:\/\/\//) ? '$1:///' : '$1://';
urls[0] = urls[0].replace(/^(?<protocol>[^/:]+):\/*/, replacement);
for (let i = 0; i < urls.length; i += 1) {
let component = urls[i];
if (typeof component !== 'string') {
throw new TypeError(`Url must be a string. Received ${typeof component}`);
}
if (component === '') {
if (i === urls.length - 1 && hasEndingSlash) {
resultArray.push('/');
}
continue;
}
if (component !== '/') {
if (i > 0) {
// Removing the starting slashes for each component but the first.
component = component.replace(/^\/+/,
// Special case where the first element of rawUrls is empty
// ["", "/hello"] => /hello
component.startsWith('/') && !hasStartingSlash ? '/' : '');
}
hasEndingSlash = component.endsWith('/');
// Removing the ending slashes for each component but the last. For the
// last component we will combine multiple slashes to a single one.
component = component.replace(/\/+$/, i < urls.length - 1 ? '' : '/');
}
hasStartingSlash = true;
resultArray.push(component);
}
let str = resultArray.join('/');
// Each input component is now separated by a single slash except the possible
// first plain protocol part.
// Remove trailing slash before parameters or hash.
str = str.replace(/\/(?<search>\?|&|#[^!])/g, '$1');
// Replace ? in parameters with &.
const parts = str.split('?');
str = parts.shift() + (parts.length > 0 ? '?' : '') + parts.join('&');
// Dedupe forward slashes in the entire path, avoiding protocol slashes.
str = str.replace(/(?<textBefore>[^:/]\/)\/+/g, '$1');
// Dedupe forward slashes at the beginning of the path.
str = str.replace(/^\/+/g, '/');
return str;
}
exports.normalizeUrl = normalizeUrl;
/**
* Takes a file's path, relative to its content folder, and computes its edit
* URL. If `editUrl` is `undefined`, this returns `undefined`, as is the case
* when the user doesn't want an edit URL in her config.
*/
function getEditUrl(fileRelativePath, editUrl) {
return editUrl
? // Don't use posixPath for this: we need to force a forward slash path
normalizeUrl([editUrl, fileRelativePath.replace(/\\/g, '/')])
: undefined;
}
exports.getEditUrl = getEditUrl;
/**
* Converts file path to a reasonable URL path, e.g. `'index.md'` -> `'/'`,
* `'foo/bar.js'` -> `'/foo/bar'`
*/
function fileToPath(file) {
const indexRE = /(?<dirname>^|.*\/)index\.(?:mdx?|jsx?|tsx?)$/i;
const extRE = /\.(?:mdx?|jsx?|tsx?)$/;
if (indexRE.test(file)) {
return file.replace(indexRE, '/$1');
}
return `/${file.replace(extRE, '').replace(/\\/g, '/')}`;
}
exports.fileToPath = fileToPath;
/**
* Similar to `encodeURI`, but uses `encodeURIComponent` and assumes there's no
* query.
*
* `encodeURI("/question?/answer")` => `"/question?/answer#section"`;
* `encodePath("/question?/answer#section")` => `"/question%3F/answer%23foo"`
*/
function encodePath(userPath) {
return userPath
.split('/')
.map((item) => encodeURIComponent(item))
.join('/');
}
exports.encodePath = encodePath;
/**
* Whether `str` is a valid pathname. It must be absolute, and not contain
* special characters.
*/
function isValidPathname(str) {
if (!str.startsWith('/')) {
return false;
}
try {
const parsedPathname = new URL(str, 'https://domain.com').pathname;
return parsedPathname === str || parsedPathname === encodeURI(str);
}
catch {
return false;
}
}
exports.isValidPathname = isValidPathname;
// Let's name the concept of (pathname + search + hash) as URLPath
// See also https://twitter.com/kettanaito/status/1741768992866308120
// Note: this function also resolves relative pathnames while parsing!
function parseURLPath(urlPath, fromPath) {
function parseURL(url, base) {
try {
// A possible alternative? https://github.com/unjs/ufo#url
return new URL(url, base ?? 'https://example.com');
}
catch (e) {
throw new Error(`Can't parse URL ${url}${base ? ` with base ${base}` : ''}`, { cause: e });
}
}
const base = fromPath ? parseURL(fromPath) : undefined;
const url = parseURL(urlPath, base);
const { pathname } = url;
// Fixes annoying url.search behavior
// "" => undefined
// "?" => ""
// "?param => "param"
const search = url.search
? url.search.slice(1)
: urlPath.includes('?')
? ''
: undefined;
// Fixes annoying url.hash behavior
// "" => undefined
// "#" => ""
// "?param => "param"
const hash = url.hash
? url.hash.slice(1)
: urlPath.includes('#')
? ''
: undefined;
return {
pathname,
search,
hash,
};
}
exports.parseURLPath = parseURLPath;
function serializeURLPath(urlPath) {
const search = urlPath.search === undefined ? '' : `?${urlPath.search}`;
const hash = urlPath.hash === undefined ? '' : `#${urlPath.hash}`;
return `${urlPath.pathname}${search}${hash}`;
}
exports.serializeURLPath = serializeURLPath;
/**
* Resolve pathnames and fail-fast if resolution fails. Uses standard URL
* semantics (provided by `resolve-pathname` which is used internally by React
* router)
*/
function resolvePathname(to, from) {
// TODO do we really need resolve-pathname lib anymore?
// possible alternative: decodeURI(parseURLPath(to, from).pathname);
return (0, resolve_pathname_1.default)(to, from);
}
exports.resolvePathname = resolvePathname;
/** Appends a leading slash to `str`, if one doesn't exist. */
function addLeadingSlash(str) {
return (0, jsUtils_1.addPrefix)(str, '/');
}
exports.addLeadingSlash = addLeadingSlash;
// TODO deduplicate: also present in @docusaurus/utils-common
/** Appends a trailing slash to `str`, if one doesn't exist. */
function addTrailingSlash(str) {
return (0, jsUtils_1.addSuffix)(str, '/');
}
exports.addTrailingSlash = addTrailingSlash;
/** Removes the trailing slash from `str`. */
function removeTrailingSlash(str) {
return (0, jsUtils_1.removeSuffix)(str, '/');
}
exports.removeTrailingSlash = removeTrailingSlash;
/** Constructs an SSH URL that can be used to push to GitHub. */
function buildSshUrl(githubHost, organizationName, projectName, githubPort) {
if (githubPort) {
return `ssh://git@${githubHost}:${githubPort}/${organizationName}/${projectName}.git`;
}
return `git@${githubHost}:${organizationName}/${projectName}.git`;
}
exports.buildSshUrl = buildSshUrl;
/** Constructs an HTTP URL that can be used to push to GitHub. */
function buildHttpsUrl(gitCredentials, githubHost, organizationName, projectName, githubPort) {
if (githubPort) {
return `https://${gitCredentials}@${githubHost}:${githubPort}/${organizationName}/${projectName}.git`;
}
return `https://${gitCredentials}@${githubHost}/${organizationName}/${projectName}.git`;
}
exports.buildHttpsUrl = buildHttpsUrl;
/**
* Whether the current URL is an SSH protocol. In addition to looking for
* `ssh:`, it will also allow protocol-less URLs like
* `git@github.com:facebook/docusaurus.git`.
*/
function hasSSHProtocol(sourceRepoUrl) {
try {
if (new URL(sourceRepoUrl).protocol === 'ssh:') {
return true;
}
return false;
}
catch {
// Fails when there isn't a protocol
return /^(?:[\w-]+@)?[\w.-]+:[\w./-]+/.test(sourceRepoUrl);
}
}
exports.hasSSHProtocol = hasSSHProtocol;
//# sourceMappingURL=urlUtils.js.map