1import { formatUrlTitle } from '../../client-utils.js'; 2import { 3 slugifyLegacyUrl, 4 slugifyLegacyArticleUrl, 5 normalizePath, 6 createFormatter, 7 getEnvLocale, 8 getCanonicalContext, 9} from '../../client-utils.js'; 10import { toText } from '../../value-utils.js'; 11 12const NEWS_CANONICAL_PATH = '/news'; 13const NEWS_CANONICAL_TEMPLATE = '/news/{slug}'; 14 15const createCanonicalNewsPathsGetter = ({ localeResolver = getEnvLocale } = {}) => { 16 let cached = null; 17 18 return () => { 19 const envLocale = toText(localeResolver()).trim(); 20 if (!envLocale) { 21 return null; 22 } 23 if (cached && cached.locale === envLocale) { 24 return cached; 25 } 26 27 const canonicalContext = getCanonicalContext(envLocale); 28 if (!canonicalContext?.context?.pathsData) { 29 return null; 30 } 31 32 const { i18nCore, context } = canonicalContext; 33 const urlLocale = context.urlLocaleRaw || envLocale; 34 const basePathRaw = i18nCore.resolvePath(context.pathsData, urlLocale, NEWS_CANONICAL_PATH, {}); 35 const basePath = normalizePath(basePathRaw || NEWS_CANONICAL_PATH); 36 const buildPath = (slug) => i18nCore.resolvePath(context.pathsData, urlLocale, NEWS_CANONICAL_TEMPLATE, { slug }); 37 38 cached = { locale: envLocale, basePath, buildPath }; 39 return cached; 40 }; 41}; 42 43const resolveNewsHref = (slug, paths) => { 44 if (!paths) { 45 return ''; 46 } 47 const normalizedSlug = toText(slug).trim(); 48 if (!normalizedSlug) { 49 return paths.basePath; 50 } 51 const resolved = paths.buildPath(normalizedSlug); 52 return resolved || `${paths.basePath}/${normalizedSlug}`; 53}; 54 55const getNewsForLocale = (serverData, locale) => { 56 const newsByLocale = serverData?.allNews; 57 if (!newsByLocale || typeof newsByLocale !== 'object') { 58 return null; 59 } 60 return Array.isArray(newsByLocale[locale]) ? newsByLocale[locale] : []; 61}; 62 63const getNewsTagsForLocale = (serverData, locale) => { 64 const tagsByLocale = serverData?.newsTags; 65 if (!tagsByLocale || typeof tagsByLocale !== 'object') { 66 return null; 67 } 68 return Array.isArray(tagsByLocale[locale]) ? tagsByLocale[locale] : []; 69}; 70 71const getNewsTagName = (tag) => { 72 if (!tag) { 73 return ''; 74 } 75 if (typeof tag === 'string') { 76 return tag; 77 } 78 if (typeof tag === 'object') { 79 if (typeof tag.name === 'string') { 80 return tag.name; 81 } 82 if (typeof tag.tag?.name === 'string') { 83 return tag.tag.name; 84 } 85 } 86 return String(tag || ''); 87}; 88 89const getNewsItemTagId = (item, { fallbackToItemId = false } = {}) => { 90 if (!item || typeof item !== 'object') { 91 return null; 92 } 93 const explicit = item.tagId ?? item.tag_id ?? item.news_tag_id ?? null; 94 if (explicit !== null && explicit !== undefined) { 95 return explicit; 96 } 97 return fallbackToItemId ? (item.id ?? null) : null; 98}; 99 100const buildNewsArticleSlug = (title, id) => { 101 if (id === undefined || id === null) { 102 return ''; 103 } 104 const base = slugifyLegacyArticleUrl(title || ''); 105 return `${base}-${id}`; 106}; 107 108const resolveNewsArticleHref = (item, articleSlugsById, paths) => { 109 if (!paths) { 110 return ''; 111 } 112 if (!item || item.id == null) { 113 return paths.basePath; 114 } 115 const slug = articleSlugsById?.get?.(String(item.id)); 116 return resolveNewsHref(slug || String(item.id), paths); 117}; 118 119const buildCanonicalArticleSlugs = (items, formatter) => { 120 const slugsById = new Map(); 121 (Array.isArray(items) ? items : []).forEach((item) => { 122 if (!item || item.id == null) { 123 return; 124 } 125 const titleText = formatUrlTitle(item.title, formatter); 126 const id = String(item.id); 127 slugsById.set(id, buildNewsArticleSlug(titleText, item.id) || id); 128 }); 129 return slugsById; 130}; 131 132const buildCanonicalTagSlugMaps = ({ 133 canonicalLocale, 134 canonicalFormatter, 135 newsByLocale, 136 tagsByLocale, 137 getTagName = getNewsTagName, 138 getItemTagId = getNewsItemTagId, 139} = {}) => { 140 const idToSlug = new Map(); 141 const nameToSlug = new Map(); 142 const articleIdToTagSlug = new Map(); 143 144 const registerName = (name, slug) => { 145 const key = slugifyLegacyUrl(name); 146 if (!key) { 147 return; 148 } 149 nameToSlug.set(key, slug); 150 }; 151 152 const registerCanonicalTag = (tag) => { 153 const name = formatUrlTitle(getTagName(tag), canonicalFormatter); 154 if (!name) { 155 return; 156 } 157 const slug = slugifyLegacyUrl(name); 158 if (!slug) { 159 return; 160 } 161 const id = tag.id; 162 if (id !== null && id !== undefined) { 163 idToSlug.set(String(id), slug); 164 } 165 registerName(name, slug); 166 }; 167 168 const canonicalTags = tagsByLocale?.[canonicalLocale];
169 (Array.isArray(canonicalTags) ? canonicalTags : []).forEach(registerCanonicalTag); 170 171 const canonicalNews = newsByLocale?.[canonicalLocale]; 172 (Array.isArray(canonicalNews) ? canonicalNews : []).forEach((item) => { 173 if (!item || item.id == null) { 174 return; 175 } 176 const name = formatUrlTitle(getTagName(item?.tag ?? ''), canonicalFormatter); 177 if (!name) { 178 return; 179 } 180 const slug = slugifyLegacyUrl(name); 181 if (!slug) { 182 return; 183 } 184 articleIdToTagSlug.set(String(item.id), slug); 185 const id = getItemTagId(item); 186 if (id !== null && id !== undefined && !idToSlug.has(String(id))) { 187 idToSlug.set(String(id), slug); 188 } 189 registerName(name, slug); 190 }); 191 192 if (newsByLocale && typeof newsByLocale === 'object') { 193 Object.entries(newsByLocale).forEach(([locale, news]) => { 194 const localeFormatter = createFormatter(locale); 195 (Array.isArray(news) ? news : []).forEach((item) => { 196 if (!item || item.id == null) { 197 return; 198 } 199 const slug = articleIdToTagSlug.get(String(item.id)); 200 if (!slug) { 201 return; 202 } 203 const tagName = String(localeFormatter.toText(getTagName(item?.tag ?? '') ?? '')).trim(); 204 if (tagName) { 205 registerName(tagName, slug); 206 } 207 const tagId = getItemTagId(item); 208 if (tagId !== null && tagId !== undefined && !idToSlug.has(String(tagId))) { 209 idToSlug.set(String(tagId), slug); 210 } 211 }); 212 }); 213 } 214 215 if (tagsByLocale && typeof tagsByLocale === 'object') { 216 Object.entries(tagsByLocale).forEach(([locale, tags]) => { 217 const localeFormatter = createFormatter(locale); 218 (Array.isArray(tags) ? tags : []).forEach((tag) => { 219 const name = String(localeFormatter.toText(getTagName(tag) ?? '')).trim(); 220 if (!name) { 221 return; 222 } 223 224 const id = tag.id; 225 let slug = ''; 226 if (id !== null && id !== undefined) { 227 slug = idToSlug.get(String(id)) || ''; 228 } 229 if (!slug) { 230 slug = nameToSlug.get(slugifyLegacyUrl(name)) || ''; 231 } 232 if (!slug) { 233 return; 234 } 235 if (id !== null && id !== undefined && !idToSlug.has(String(id))) { 236 idToSlug.set(String(id), slug); 237 } 238 registerName(name, slug); 239 }); 240 }); 241 } 242 243 return { idToSlug, nameToSlug }; 244}; 245 246const resolveTagSlug = (tagText, tagId, slugMaps) => { 247 if (tagId !== null && tagId !== undefined) { 248 const mapped = slugMaps.idToSlug.get(String(tagId)); 249 if (mapped) { 250 return mapped; 251 } 252 } 253 const nameKey = slugifyLegacyUrl(tagText); 254 if (nameKey) { 255 const mapped = slugMaps.nameToSlug.get(nameKey); 256 if (mapped) { 257 return mapped; 258 } 259 } 260 return ''; 261}; 262 263const resolveTagSlugFromPath = (pathname, basePath) => { 264 const normalizedBase = normalizePath(basePath); 265 if (!normalizedBase) { 266 return ''; 267 } 268 269 const normalizedPath = normalizePath(pathname || ''); 270 if (!normalizedPath || normalizedPath === normalizedBase) { 271 return ''; 272 } 273 if (!normalizedPath.startsWith(`${normalizedBase}/`)) { 274 return ''; 275 } 276 277 const suffix = normalizedPath.slice(normalizedBase.length + 1).replace(/\/+$/g, ''); 278 if (!suffix || suffix.includes('/')) { 279 return ''; 280 } 281 282 try { 283 return slugifyLegacyUrl(decodeURIComponent(suffix)); 284 } catch { 285 return slugifyLegacyUrl(suffix); 286 } 287}; 288 289const resolveNewsArticleId = ({ attrValue, pathname = '', basePath = '' } = {}) => { 290 const numeric = Number(attrValue); 291 if (Number.isFinite(numeric)) { 292 return numeric; 293 } 294 295 const normalizedBase = normalizePath(basePath); 296 const normalizedPath = normalizePath(pathname || ''); 297 if (!normalizedBase || !normalizedPath || !normalizedPath.startsWith(`${normalizedBase}/`)) { 298 return null; 299 } 300 301 const suffix = normalizedPath.slice(normalizedBase.length + 1).replace(/\/+$/g, ''); 302 if (!suffix || suffix.includes('/')) { 303 return null; 304 } 305 306 const normalizedSlug = slugifyLegacyArticleUrl(suffix); 307 const last = normalizedSlug.split('-').pop(); 308 const id = Number(last); 309 return Number.isFinite(id) ? id : null; 310}; 311 312export { 313 createCanonicalNewsPathsGetter, 314 resolveNewsHref, 315 getNewsForLocale, 316 getNewsTagsForLocale, 317 getNewsTagName, 318 getNewsItemTagId, 319 resolveNewsArticleHref, 320 buildCanonicalArticleSlugs, 321 buildCanonicalTagSlugMaps, 322 resolveTagSlug, 323 resolveTagSlugFromPath, 324 resolveNewsArticleId, 325};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.