UNPKG

@ledgerhq/live-common

Version:
128 lines 4.16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useHtmlLinkSegments = exports.buildHtmlDisplaySegments = exports.validateLedgerUrl = exports.splitHtmlLinkSegments = void 0; const react_1 = require("react"); const ANCHOR_OPEN_REGEX = /<a\b[^>]*>/gi; const HREF_ATTR_REGEX = /href\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s'">]+))/i; const CLOSING_ANCHOR = "</a>"; const splitHtmlLinkSegments = (input) => { const segments = []; if (!input) { return segments; } const lowerInput = input.toLowerCase(); let cursor = 0; ANCHOR_OPEN_REGEX.lastIndex = 0; const extractHrefValue = (tag) => { const match = HREF_ATTR_REGEX.exec(tag); if (!match) return null; return match[1] ?? match[2] ?? match[3] ?? null; }; let match; while ((match = ANCHOR_OPEN_REGEX.exec(input)) !== null) { const startIndex = match.index; const tag = match[0]; const contentStartIndex = startIndex + tag.length; const closingIndex = lowerInput.indexOf(CLOSING_ANCHOR, contentStartIndex); if (closingIndex === -1) { break; } if (startIndex > cursor) { const textBefore = input.slice(cursor, startIndex); if (textBefore) { segments.push({ type: "text", content: textBefore, }); } } const href = extractHrefValue(tag); if (href) { const label = input.slice(contentStartIndex, closingIndex); segments.push({ type: "link", href, label, }); } else { const fallbackText = input.slice(startIndex, closingIndex + CLOSING_ANCHOR.length); segments.push({ type: "text", content: fallbackText, }); } cursor = closingIndex + CLOSING_ANCHOR.length; ANCHOR_OPEN_REGEX.lastIndex = cursor; } if (cursor < input.length) { const textAfter = input.slice(cursor); if (textAfter) { segments.push({ type: "text", content: textAfter, }); } } return segments; }; exports.splitHtmlLinkSegments = splitHtmlLinkSegments; const validateLedgerUrl = (href) => { try { const url = new URL(href); const isHttp = url.protocol === "http:" || url.protocol === "https:"; if (!isHttp) { return { isHttp: false, isAllowedLedgerDomain: false, }; } const hostname = url.hostname.toLowerCase(); const isAllowedLedgerDomain = hostname === "ledger.com" || hostname.endsWith(".ledger.com"); return { isHttp, isAllowedLedgerDomain, }; } catch { return { isHttp: false, isAllowedLedgerDomain: false, }; } }; exports.validateLedgerUrl = validateLedgerUrl; const buildHtmlDisplaySegments = (input) => { return (0, exports.splitHtmlLinkSegments)(input).map(segment => { if (segment.type === "link") { const { isHttp, isAllowedLedgerDomain } = (0, exports.validateLedgerUrl)(segment.href); if (isHttp && isAllowedLedgerDomain) { return segment; } return { type: "text", content: segment.label, }; } return segment; }); }; exports.buildHtmlDisplaySegments = buildHtmlDisplaySegments; const useHtmlLinkSegments = (html) => { return (0, react_1.useMemo)(() => { if (!html) { return { segments: [], hasLinks: false, }; } const segments = (0, exports.buildHtmlDisplaySegments)(html); return { segments, hasLinks: segments.some(segment => segment.type === "link"), }; }, [html]); }; exports.useHtmlLinkSegments = useHtmlLinkSegments; //# sourceMappingURL=useHtmlLinkSegments.js.map