Files
website/node_modules/@astrojs/starlight/utils/slugs.ts
2024-05-06 17:15:30 -04:00

122 lines
4.1 KiB
TypeScript

import config from 'virtual:starlight/user-config';
export interface LocaleData {
/** Writing direction. */
dir: 'ltr' | 'rtl';
/** BCP-47 language tag. */
lang: string;
/** The base path at which a language is served. `undefined` for root locale slugs. */
locale: string | undefined;
}
/**
* Get the “locale” of a slug. This is the base path at which a language is served.
* For example, if French docs are in `src/content/docs/french/`, the locale is `french`.
* Root locale slugs will return `undefined`.
* @param slug A collection entry slug
*/
function slugToLocale(slug: string): string | undefined {
const locales = Object.keys(config.locales || {});
const baseSegment = slug.split('/')[0];
if (baseSegment && locales.includes(baseSegment)) return baseSegment;
return undefined;
}
/** Get locale information for a given slug. */
export function slugToLocaleData(slug: string): LocaleData {
const locale = slugToLocale(slug);
return { dir: localeToDir(locale), lang: localeToLang(locale), locale };
}
/**
* Get the BCP-47 language tag for the given locale.
* @param locale Locale string or `undefined` for the root locale.
*/
export function localeToLang(locale: string | undefined): string {
const lang = locale ? config.locales?.[locale]?.lang : config.locales?.root?.lang;
const defaultLang = config.defaultLocale?.lang || config.defaultLocale?.locale;
return lang || defaultLang || 'en';
}
/**
* Get the configured writing direction for the given locale.
* @param locale Locale string or `undefined` for the root locale.
*/
function localeToDir(locale: string | undefined): 'ltr' | 'rtl' {
const dir = locale ? config.locales?.[locale]?.dir : config.locales?.root?.dir;
return dir || config.defaultLocale.dir;
}
export function slugToParam(slug: string): string | undefined {
return slug === 'index' || slug === ''
? undefined
: slug.endsWith('/index')
? slug.replace(/\/index$/, '')
: slug;
}
export function slugToPathname(slug: string): string {
const param = slugToParam(slug);
return param ? '/' + param + '/' : '/';
}
/**
* Convert a slug to a different locale.
* For example, passing a slug of `en/home` and a locale of `fr` results in `fr/home`.
* An undefined locale is treated as the root locale, resulting in `home`
* @param slug A collection entry slug
* @param locale The target locale
* @example
* localizedSlug('en/home', 'fr') // => 'fr/home'
* localizedSlug('en/home', undefined) // => 'home'
*/
export function localizedSlug(slug: string, locale: string | undefined): string {
const slugLocale = slugToLocale(slug);
if (slugLocale === locale) return slug;
locale = locale || '';
if (slugLocale === slug) return locale;
if (slugLocale) {
return slug.replace(slugLocale + '/', locale ? locale + '/' : '').replace(/\/$/, '');
}
return slug ? locale + '/' + slug : locale;
}
/**
* Convert a collection entry ID to a different locale.
* For example, passing an ID of `en/home.md` and a locale of `fr` results in `fr/home.md`.
* An undefined locale is treated as the root locale, resulting in `home.md`.
* @param id A collection entry ID
* @param locale The target locale
* @example
* localizedSlug('en/home.md', 'fr') // => 'fr/home.md'
* localizedSlug('en/home.md', undefined) // => 'home.md'
*/
export function localizedId(id: string, locale: string | undefined): string {
const idLocale = slugToLocale(id);
if (idLocale) {
return id.replace(idLocale + '/', locale ? locale + '/' : '');
} else if (locale) {
return locale + '/' + id;
} else {
return id;
}
}
/** Extract the slug from a URL. */
export function urlToSlug(url: URL): string {
let pathname = url.pathname;
const base = import.meta.env.BASE_URL.replace(/\/$/, '');
if (pathname.startsWith(base)) pathname = pathname.replace(base, '');
const segments = pathname.split('/');
const htmlExt = '.html';
if (segments.at(-1) === 'index.html') {
// Remove trailing `index.html`.
segments.pop();
} else if (segments.at(-1)?.endsWith(htmlExt)) {
// Remove trailing `.html`.
const last = segments.pop();
if (last) segments.push(last.slice(0, -1 * htmlExt.length));
}
return segments.filter(Boolean).join('/');
}