import type { ISeoRedirectSource } from '../../service/seo/seo.interface'; /** * What a source will catch that its author may not expect, or empty. * * @param {ISeoRedirectSource} source - The source as typed. * @returns {string} The hint. */ export const sourceHint = (source: ISeoRedirectSource): string => { const pattern = source.pattern.trim(); if (pattern === '') { return ''; } if (source.comparison === 'regex') { return /^\^?\//.test(pattern) ? 'The first slash is optional: the address is matched without it, so this reads the same as without the slash.' : ''; } if (source.comparison !== 'exact' && pattern.includes('?')) { return 'Only an exact source can hold a query string; this one is refused on save.'; } const word = pattern.replace(/^\/+|\/+$/g, ''); if ( source.comparison === 'contains' && !(pattern.startsWith('/') && pattern.endsWith('/')) ) { return `Catches “${word}” inside longer words too, such as /my-${word}s/. Type /${word}/ to catch that whole segment only.`; } if (source.comparison === 'starts' && !pattern.endsWith('/')) { return `Also catches addresses that only begin like it, such as /${word}s/. Type /${word}/ to catch that segment and what is under it only.`; } if (source.comparison === 'ends' && !pattern.startsWith('/')) { return `Also catches addresses that only end like it, such as /my-${word}/. Type /${word} to catch that last segment only.`; } return ''; }; /** * The sources of a saved rule as the form shows them: an exact address * with its first slash, the way it was typed, though the rule keeps it * without. * * @param {ISeoRedirectSource[]} sources - As the rule keeps them. * @returns {ISeoRedirectSource[]} */ export const editableSources = ( sources: ISeoRedirectSource[] ): ISeoRedirectSource[] => sources.map(source => source.comparison === 'exact' && !source.pattern.startsWith('/') && !/^https?:\/\//i.test(source.pattern) ? { ...source, pattern: `/${source.pattern}` } : source );