From e9b20ca990ccf00a6c38336781a4f68b2dfa1748 Mon Sep 17 00:00:00 2001 From: sebastienlorber Date: Thu, 4 Jan 2024 20:08:49 +0100 Subject: [PATCH] refactor, create getLocalizedSource() --- .../src/blogUtils.ts | 69 +++++++++++----- .../src/plugin-content-blog.d.ts | 1 + .../src/index.ts | 4 +- packages/docusaurus-utils/src/i18nUtils.ts | 82 +++++++++++++------ packages/docusaurus-utils/src/index.ts | 2 +- 5 files changed, 107 insertions(+), 51 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 3bbb5301bf..cd80af445b 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -26,6 +26,8 @@ import { getContentPathList, isUnlisted, isDraft, + filterFilesWithLocaleExtension, + getLocalizedSource, } from '@docusaurus/utils'; import {validateBlogPostFrontMatter} from './frontMatter'; import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors'; @@ -208,13 +210,19 @@ async function parseBlogPostMarkdownFile({ const defaultReadingTime: ReadingTimeFunction = ({content, options}) => readingTime(content, options).minutes; -async function processBlogSourceFile( - blogSourceRelative: string, - contentPaths: BlogContentPaths, - context: LoadContext, - options: PluginOptions, - authorsMap?: AuthorsMap, -): Promise { +async function processBlogSourceFile({ + blogSourceRelative, + contentPaths, + context, + options, + authorsMap, +}: { + blogSourceRelative: string; + contentPaths: BlogContentPaths; + context: LoadContext; + options: PluginOptions; + authorsMap?: AuthorsMap; +}): Promise { const { siteConfig: { baseUrl, @@ -231,21 +239,30 @@ async function processBlogSourceFile( editUrl, } = options; + // TODO remove this in favor of getLocalizedSource // Lookup in localized folder in priority const blogDirPath = await getFolderContainingFile( getContentPathList(contentPaths), blogSourceRelative, ); - const blogSourceAbsolute = path.join(blogDirPath, blogSourceRelative); + const { + source: blogSource, + + // contentPath: blogDirPath + } = await getLocalizedSource({ + relativeSource: blogSourceRelative, + contentPaths, + locale: context.i18n.currentLocale, + }); const {frontMatter, content, contentTitle, excerpt} = await parseBlogPostMarkdownFile({ - filePath: blogSourceAbsolute, + filePath: blogSource, parseFrontMatter, }); - const aliasedSource = aliasedSitePath(blogSourceAbsolute, siteDir); + const aliasedSource = aliasedSitePath(blogSource, siteDir); const draft = isDraft({frontMatter}); const unlisted = isUnlisted({frontMatter}); @@ -274,14 +291,14 @@ async function processBlogSourceFile( } try { - const result = getFileCommitDate(blogSourceAbsolute, { + const result = getFileCommitDate(blogSource, { age: 'oldest', includeAuthor: false, }); return result.date; } catch (err) { logger.warn(err); - return (await fs.stat(blogSourceAbsolute)).birthtime; + return (await fs.stat(blogSource)).birthtime; } } @@ -302,7 +319,7 @@ async function processBlogSourceFile( function getBlogEditUrl() { const blogPathRelative = path.relative( blogDirPath, - path.resolve(blogSourceAbsolute), + path.resolve(blogSource), ); if (typeof editUrl === 'function') { @@ -374,28 +391,36 @@ export async function generateBlogPosts( return []; } - const blogSourceFiles = await Globby(include, { - cwd: contentPaths.contentPath, - ignore: exclude, - }); + async function getBlogSourceFiles() { + const files = await Globby(include, { + cwd: contentPaths.contentPath, + ignore: exclude, + }); + return filterFilesWithLocaleExtension({ + files, + locales: context.i18n.locales, + }); + } + + const blogSourceFiles = await getBlogSourceFiles(); const authorsMap = await getAuthorsMap({ contentPaths, authorsMapPath: options.authorsMapPath, }); - async function doProcessBlogSourceFile(blogSourceFile: string) { + async function doProcessBlogSourceFile(blogSourceRelative: string) { try { - return await processBlogSourceFile( - blogSourceFile, + return await processBlogSourceFile({ + blogSourceRelative, contentPaths, context, options, authorsMap, - ); + }); } catch (err) { throw new Error( - `Processing of blog source file path=${blogSourceFile} failed.`, + `Processing of blog source file path=${blogSourceRelative} failed.`, {cause: err as Error}, ); } diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index b1915f7519..04ce1ba1d1 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -72,6 +72,7 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the * preserved as-is. Default values will be applied when generating metadata */ export type BlogPostFrontMatter = { + // TODO Docusaurus v4: remove /** * @deprecated Use `slug` instead. */ diff --git a/packages/docusaurus-plugin-content-pages/src/index.ts b/packages/docusaurus-plugin-content-pages/src/index.ts index 3b12b3c908..cd9913e891 100644 --- a/packages/docusaurus-plugin-content-pages/src/index.ts +++ b/packages/docusaurus-plugin-content-pages/src/index.ts @@ -21,7 +21,7 @@ import { parseMarkdownFile, isUnlisted, isDraft, - getLocalizedSourcePath, + getLocalizedSource, filterFilesWithLocaleExtension, } from '@docusaurus/utils'; import {validatePageFrontMatter} from './frontMatter'; @@ -95,7 +95,7 @@ export default function pluginContentPages( async function processPageSourceFile( relativeSource: string, ): Promise { - const source = await getLocalizedSourcePath({ + const {source} = await getLocalizedSource({ relativeSource, contentPaths, locale: context.i18n.currentLocale, diff --git a/packages/docusaurus-utils/src/i18nUtils.ts b/packages/docusaurus-utils/src/i18nUtils.ts index cac9e269ec..7b8c458b61 100644 --- a/packages/docusaurus-utils/src/i18nUtils.ts +++ b/packages/docusaurus-utils/src/i18nUtils.ts @@ -127,13 +127,13 @@ function addLocaleExtension(filePath: string, locale: string) { return path.join(dir, `${name}.${locale}${ext}`); } -/** - * Returns the first existing localized path of a content file - * @param relativeSource - * @param contentPaths - * @param locale - */ -export async function getLocalizedSourcePath({ +type LocalizedSource = { + contentPath: string; + source: string; + type: 'locale-extension' | 'locale-folder' | 'original'; +}; + +function getLocalizedSourceCandidates({ relativeSource, contentPaths, locale, @@ -141,38 +141,68 @@ export async function getLocalizedSourcePath({ relativeSource: string; contentPaths: ContentPaths; locale: string; -}): Promise { +}): LocalizedSource[] { // docs/myDoc.fr.md - const localeExtensionSource = path.join( - contentPaths.contentPath, - addLocaleExtension(relativeSource, locale), - ); + const localeExtensionSource: LocalizedSource = { + contentPath: contentPaths.contentPath, + source: path.join( + contentPaths.contentPath, + addLocaleExtension(relativeSource, locale), + ), + type: 'locale-extension', + }; // i18n/fr/docs/current/myDoc.md - const i18nFolderSource = path.join( - contentPaths.contentPathLocalized, - relativeSource, - ); + const i18nFolderSource: LocalizedSource = { + contentPath: contentPaths.contentPath, + source: path.join(contentPaths.contentPathLocalized, relativeSource), + type: 'locale-folder', + }; // docs/myDoc.md - const originalSource = path.join(contentPaths.contentPath, relativeSource); + const originalSource: LocalizedSource = { + contentPath: contentPaths.contentPath, + source: path.join(contentPaths.contentPath, relativeSource), + type: 'original', + }; // Order matters - const possibleSources = [ - localeExtensionSource, - i18nFolderSource, - originalSource, - ]; + return [localeExtensionSource, i18nFolderSource, originalSource]; +} + +/** + * Returns the first existing localized path of a content file + * @param relativeSource + * @param contentPaths + * @param locale + */ +export async function getLocalizedSource({ + relativeSource, + contentPaths, + locale, +}: { + relativeSource: string; + contentPaths: ContentPaths; + locale: string; +}): Promise { + // docs/myDoc.fr.md + const candidates = getLocalizedSourceCandidates({ + relativeSource, + contentPaths, + locale, + }); // TODO can we avoid/optimize this by passing all the files we know as param? - const localizedSource = await findAsyncSequential( - possibleSources, - fs.pathExists, + const localizedSource = await findAsyncSequential(candidates, (candidate) => + fs.pathExists(candidate.source), ); if (!localizedSource) { throw new Error( - `Unexpected error, couldn't find any existing file at ${originalSource}`, + `Unexpected error, couldn't find any localized source for file at ${path.join( + contentPaths.contentPath, + relativeSource, + )}`, ); } diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index ce4daefb77..9e772c6432 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -34,7 +34,7 @@ export { updateTranslationFileMessages, getPluginI18nPath, localizePath, - getLocalizedSourcePath, + getLocalizedSource, filterFilesWithLocaleExtension, } from './i18nUtils'; export {