@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
128 lines • 4.16 kB
JavaScript
;
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