UNPKG

@elastic/eui

Version:

Elastic UI Component Library

114 lines (110 loc) 4.63 kB
/* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ import visit from 'unist-util-visit'; export var DEFAULT_OPTIONS = { allowRelative: true, allowDocumentRelative: false, allowProtocols: ['https:', 'http:', 'mailto:'] }; export function euiMarkdownLinkValidator(_ref) { var _ref$allowRelative = _ref.allowRelative, allowRelative = _ref$allowRelative === void 0 ? DEFAULT_OPTIONS.allowRelative : _ref$allowRelative, _ref$allowDocumentRel = _ref.allowDocumentRelative, allowDocumentRelative = _ref$allowDocumentRel === void 0 ? DEFAULT_OPTIONS.allowDocumentRelative : _ref$allowDocumentRel, _ref$allowProtocols = _ref.allowProtocols, allowProtocols = _ref$allowProtocols === void 0 ? DEFAULT_OPTIONS.allowProtocols : _ref$allowProtocols, baseUrl = _ref.baseUrl; return function (ast) { visit(ast, 'link', function (_node) { var node = _node; if (allowRelative && allowDocumentRelative && ( // Prevent throwing in non-browser environments if baseUrl isn't configured typeof window !== 'undefined' || baseUrl) && node.url && isDocumentRelativeUrl(node.url)) { var newUrl = resolveDocumentRelativeUrl(node.url, baseUrl !== null && baseUrl !== void 0 ? baseUrl : window.location.href); if (newUrl) { node.url = newUrl; } else { mutateLinkToText(node); } } else if (!validateUrl(node.url, { allowRelative: allowRelative, allowProtocols: allowProtocols })) { mutateLinkToText(node); } }); }; } export function mutateLinkToText(node) { var _node$children, _node$url; // this is an upsupported url, convert to a text node node.type = 'text'; // and, if the link text matches the url there's only one value to show // otherwise render as the markdown syntax so both text & url remain, unlinked var linkText = ((_node$children = node.children) === null || _node$children === void 0 || (_node$children = _node$children[0]) === null || _node$children === void 0 ? void 0 : _node$children.value) || ''; var linkUrl = (_node$url = node.url) !== null && _node$url !== void 0 ? _node$url : ''; if (linkText === linkUrl) { node.value = linkText; } else { node.value = "[".concat(linkText, "](").concat(node.url, ")"); } delete node.children; delete node.title; delete node.url; return node; } export function validateUrl(url, _ref2) { var _ref2$allowRelative = _ref2.allowRelative, allowRelative = _ref2$allowRelative === void 0 ? DEFAULT_OPTIONS.allowRelative : _ref2$allowRelative, _ref2$allowProtocols = _ref2.allowProtocols, allowProtocols = _ref2$allowProtocols === void 0 ? DEFAULT_OPTIONS.allowProtocols : _ref2$allowProtocols; // relative captures both relative paths `/` and protocols `//` var isRelative = url.startsWith('/'); if (isRelative) { return allowRelative; } try { var parsedUrl = new URL(url); return allowProtocols.indexOf(parsedUrl.protocol) !== -1; } catch (e) { // failed to parse input as url return false; } } /** * Tests whether a URL is a document relative URL (e.g. "discover", "dashboards#/view/123") * that has no scheme, no leading slash, and is not an anchor or query-only link. */ export function isDocumentRelativeUrl(url) { if (url.startsWith('/') || url.startsWith('#') || url.startsWith('?')) { return false; } // Return false if the url starts with a protocol/scheme. Catches http:, https:, mailto:, etc. if (/^[a-z][a-z0-9+.-]*:/i.test(url)) { return false; } return true; } /** * Resolves a document relative URL against a base URL. This mostly replicates * native browser resolution of e.g. `<a href="discover">`, except that * trailing slashes are always removed to ensure consistent resolution, * regardless of whether the current page URL ends with a trailing slash or not. * Without this, "baz" on "/foo/bar/" resolves to "/foo/bar/baz" instead of the * expected "/foo/baz". */ function resolveDocumentRelativeUrl(url, baseUrl) { var normalizedBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl; try { var resolved = new URL(url, normalizedBase); return "".concat(resolved.pathname).concat(resolved.search).concat(resolved.hash); } catch (e) { // failed to parse URL return null; } }