docs: fix search character escaping

This commit is contained in:
Daniel Lautzenheiser 2023-11-08 14:42:18 -05:00
parent bb0b709b48
commit 27b6112cba

View File

@ -72,6 +72,10 @@ const StyledSuggestionSection = styled('div')`
gap: 4px; gap: 4px;
`; `;
function escapeRegExp(str: string) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
interface SearchModalProps { interface SearchModalProps {
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
@ -102,9 +106,10 @@ const SearchModal: FC<SearchModalProps> = ({ open, onClose, searchablePages }) =
const searchResults = useSearchScores(search, searchablePages); const searchResults = useSearchScores(search, searchablePages);
const renderedResults = useMemo( const renderedResults = useMemo(() => {
() => const escapedSearch = escapeRegExp(search);
searchResults?.length > 0 ? (
return searchResults?.length > 0 ? (
[...Array<unknown>(SEARCH_RESULTS_TO_SHOW)].map((_, index) => { [...Array<unknown>(SEARCH_RESULTS_TO_SHOW)].map((_, index) => {
if (searchResults.length <= index) { if (searchResults.length <= index) {
return; return;
@ -116,14 +121,16 @@ const SearchModal: FC<SearchModalProps> = ({ open, onClose, searchablePages }) =
if (!result.isExactTitleMatch) { if (!result.isExactTitleMatch) {
const match = new RegExp( const match = new RegExp(
`(?:[\\s]+[^\\s]+){0,10}[\\s]*${search}(?![^<>]*(([/"']|]]|\\b)>))[\\s]*(?:[^\\s]+\\s){0,25}`, `(?:[\\s]+[^\\s]+){0,10}[\\s]*${escapeRegExp(
escapedSearch,
)}(?![^<>]*(([/"']|]]|\\b)>))[\\s]*(?:[^\\s]+\\s){0,25}`,
'ig', 'ig',
).exec(entry.textContent); ).exec(entry.textContent);
if (match && match.length >= 1) { if (match && match.length >= 1) {
summary = `...${match[0].trim()}...`; summary = `...${match[0].trim()}...`;
} else { } else {
const match = new RegExp( const match = new RegExp(
`(?:[\\s]+[^\\s]+){0,10}[\\s]*(${search `(?:[\\s]+[^\\s]+){0,10}[\\s]*(${escapedSearch
.split(' ') .split(' ')
.join('|')})(?![^<>]*(([/"']|]]|\\b)>))[\\s]*(?:[^\\s]+\\s){0,25}`, .join('|')})(?![^<>]*(([/"']|]]|\\b)>))[\\s]*(?:[^\\s]+\\s){0,25}`,
'ig', 'ig',
@ -135,7 +142,7 @@ const SearchModal: FC<SearchModalProps> = ({ open, onClose, searchablePages }) =
} }
summary = summary?.replace( summary = summary?.replace(
new RegExp(`(${search.split(' ').join('|')})(?![^<>]*(([/"']|]]|\\b)>))`, 'ig'), new RegExp(`(${escapedSearch.split(' ').join('|')})(?![^<>]*(([/"']|]]|\\b)>))`, 'ig'),
`<strong style="color: ${theme.palette.primary.main}">$1</strong>`, `<strong style="color: ${theme.palette.primary.main}">$1</strong>`,
); );
@ -148,7 +155,7 @@ const SearchModal: FC<SearchModalProps> = ({ open, onClose, searchablePages }) =
/> />
); );
}) })
) : isNotEmpty(search) ? ( ) : isNotEmpty(escapedSearch) ? (
<Typography <Typography
variant="h3" variant="h3"
component="div" component="div"
@ -163,13 +170,9 @@ const SearchModal: FC<SearchModalProps> = ({ open, onClose, searchablePages }) =
<Typography variant="h3" sx={{ marginBottom: '4px' }}> <Typography variant="h3" sx={{ marginBottom: '4px' }}>
Getting Started Getting Started
</Typography> </Typography>
<SuggestionLink href="/docs/start-with-a-template"> <SuggestionLink href="/docs/start-with-a-template">Start With a Template</SuggestionLink>
Start With a Template
</SuggestionLink>
<SuggestionLink href="/docs/add-to-your-site">Add to Your Site</SuggestionLink> <SuggestionLink href="/docs/add-to-your-site">Add to Your Site</SuggestionLink>
<SuggestionLink href="/docs/configuration-options"> <SuggestionLink href="/docs/configuration-options">Configuration Options</SuggestionLink>
Configuration Options
</SuggestionLink>
<SuggestionLink href="/docs/collection-overview">Collections</SuggestionLink> <SuggestionLink href="/docs/collection-overview">Collections</SuggestionLink>
</StyledSuggestionSection> </StyledSuggestionSection>
<StyledSuggestionSection> <StyledSuggestionSection>
@ -199,9 +202,8 @@ const SearchModal: FC<SearchModalProps> = ({ open, onClose, searchablePages }) =
<SuggestionLink href="/docs/widget-markdown">Markdown</SuggestionLink> <SuggestionLink href="/docs/widget-markdown">Markdown</SuggestionLink>
</StyledSuggestionSection> </StyledSuggestionSection>
</StyledSuggestions> </StyledSuggestions>
),
[handleClose, search, searchResults, theme.palette.primary.main],
); );
}, [handleClose, search, searchResults, theme.palette.primary.main]);
return ( return (
<StyledDialog open={open} onClose={handleClose} fullScreen={fullScreen} fullWidth> <StyledDialog open={open} onClose={handleClose} fullScreen={fullScreen} fullWidth>