Compare commits
8 Commits
main
...
slorber/i1
| Author | SHA1 | Date |
|---|---|---|
|
|
e9b20ca990 | |
|
|
ecf6888840 | |
|
|
f27bedb76f | |
|
|
b49b462389 | |
|
|
90b1895156 | |
|
|
87b9df54c8 | |
|
|
b4f95929cf | |
|
|
fc72669528 |
|
|
@ -26,6 +26,8 @@ import {
|
||||||
getContentPathList,
|
getContentPathList,
|
||||||
isUnlisted,
|
isUnlisted,
|
||||||
isDraft,
|
isDraft,
|
||||||
|
filterFilesWithLocaleExtension,
|
||||||
|
getLocalizedSource,
|
||||||
} from '@docusaurus/utils';
|
} from '@docusaurus/utils';
|
||||||
import {validateBlogPostFrontMatter} from './frontMatter';
|
import {validateBlogPostFrontMatter} from './frontMatter';
|
||||||
import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors';
|
import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors';
|
||||||
|
|
@ -208,13 +210,19 @@ async function parseBlogPostMarkdownFile({
|
||||||
const defaultReadingTime: ReadingTimeFunction = ({content, options}) =>
|
const defaultReadingTime: ReadingTimeFunction = ({content, options}) =>
|
||||||
readingTime(content, options).minutes;
|
readingTime(content, options).minutes;
|
||||||
|
|
||||||
async function processBlogSourceFile(
|
async function processBlogSourceFile({
|
||||||
blogSourceRelative: string,
|
blogSourceRelative,
|
||||||
contentPaths: BlogContentPaths,
|
contentPaths,
|
||||||
context: LoadContext,
|
context,
|
||||||
options: PluginOptions,
|
options,
|
||||||
authorsMap?: AuthorsMap,
|
authorsMap,
|
||||||
): Promise<BlogPost | undefined> {
|
}: {
|
||||||
|
blogSourceRelative: string;
|
||||||
|
contentPaths: BlogContentPaths;
|
||||||
|
context: LoadContext;
|
||||||
|
options: PluginOptions;
|
||||||
|
authorsMap?: AuthorsMap;
|
||||||
|
}): Promise<BlogPost | undefined> {
|
||||||
const {
|
const {
|
||||||
siteConfig: {
|
siteConfig: {
|
||||||
baseUrl,
|
baseUrl,
|
||||||
|
|
@ -231,21 +239,30 @@ async function processBlogSourceFile(
|
||||||
editUrl,
|
editUrl,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
|
// TODO remove this in favor of getLocalizedSource
|
||||||
// Lookup in localized folder in priority
|
// Lookup in localized folder in priority
|
||||||
const blogDirPath = await getFolderContainingFile(
|
const blogDirPath = await getFolderContainingFile(
|
||||||
getContentPathList(contentPaths),
|
getContentPathList(contentPaths),
|
||||||
blogSourceRelative,
|
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} =
|
const {frontMatter, content, contentTitle, excerpt} =
|
||||||
await parseBlogPostMarkdownFile({
|
await parseBlogPostMarkdownFile({
|
||||||
filePath: blogSourceAbsolute,
|
filePath: blogSource,
|
||||||
parseFrontMatter,
|
parseFrontMatter,
|
||||||
});
|
});
|
||||||
|
|
||||||
const aliasedSource = aliasedSitePath(blogSourceAbsolute, siteDir);
|
const aliasedSource = aliasedSitePath(blogSource, siteDir);
|
||||||
|
|
||||||
const draft = isDraft({frontMatter});
|
const draft = isDraft({frontMatter});
|
||||||
const unlisted = isUnlisted({frontMatter});
|
const unlisted = isUnlisted({frontMatter});
|
||||||
|
|
@ -274,14 +291,14 @@ async function processBlogSourceFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = getFileCommitDate(blogSourceAbsolute, {
|
const result = getFileCommitDate(blogSource, {
|
||||||
age: 'oldest',
|
age: 'oldest',
|
||||||
includeAuthor: false,
|
includeAuthor: false,
|
||||||
});
|
});
|
||||||
return result.date;
|
return result.date;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.warn(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() {
|
function getBlogEditUrl() {
|
||||||
const blogPathRelative = path.relative(
|
const blogPathRelative = path.relative(
|
||||||
blogDirPath,
|
blogDirPath,
|
||||||
path.resolve(blogSourceAbsolute),
|
path.resolve(blogSource),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (typeof editUrl === 'function') {
|
if (typeof editUrl === 'function') {
|
||||||
|
|
@ -374,28 +391,36 @@ export async function generateBlogPosts(
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const blogSourceFiles = await Globby(include, {
|
async function getBlogSourceFiles() {
|
||||||
cwd: contentPaths.contentPath,
|
const files = await Globby(include, {
|
||||||
ignore: exclude,
|
cwd: contentPaths.contentPath,
|
||||||
});
|
ignore: exclude,
|
||||||
|
});
|
||||||
|
return filterFilesWithLocaleExtension({
|
||||||
|
files,
|
||||||
|
locales: context.i18n.locales,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const blogSourceFiles = await getBlogSourceFiles();
|
||||||
|
|
||||||
const authorsMap = await getAuthorsMap({
|
const authorsMap = await getAuthorsMap({
|
||||||
contentPaths,
|
contentPaths,
|
||||||
authorsMapPath: options.authorsMapPath,
|
authorsMapPath: options.authorsMapPath,
|
||||||
});
|
});
|
||||||
|
|
||||||
async function doProcessBlogSourceFile(blogSourceFile: string) {
|
async function doProcessBlogSourceFile(blogSourceRelative: string) {
|
||||||
try {
|
try {
|
||||||
return await processBlogSourceFile(
|
return await processBlogSourceFile({
|
||||||
blogSourceFile,
|
blogSourceRelative,
|
||||||
contentPaths,
|
contentPaths,
|
||||||
context,
|
context,
|
||||||
options,
|
options,
|
||||||
authorsMap,
|
authorsMap,
|
||||||
);
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Processing of blog source file path=${blogSourceFile} failed.`,
|
`Processing of blog source file path=${blogSourceRelative} failed.`,
|
||||||
{cause: err as Error},
|
{cause: err as Error},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
* preserved as-is. Default values will be applied when generating metadata
|
||||||
*/
|
*/
|
||||||
export type BlogPostFrontMatter = {
|
export type BlogPostFrontMatter = {
|
||||||
|
// TODO Docusaurus v4: remove
|
||||||
/**
|
/**
|
||||||
* @deprecated Use `slug` instead.
|
* @deprecated Use `slug` instead.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,10 @@ module.exports = {
|
||||||
url: 'https://your-docusaurus-site.example.com',
|
url: 'https://your-docusaurus-site.example.com',
|
||||||
baseUrl: '/',
|
baseUrl: '/',
|
||||||
favicon: 'img/favicon.ico',
|
favicon: 'img/favicon.ico',
|
||||||
|
i18n: {
|
||||||
|
defaultLocale: 'en',
|
||||||
|
locales: ['en', 'fr'],
|
||||||
|
},
|
||||||
markdown: {
|
markdown: {
|
||||||
parseFrontMatter: async (params) => {
|
parseFrontMatter: async (params) => {
|
||||||
const result = await params.defaultParseFrontMatter(params);
|
const result = await params.defaultParseFrontMatter(params);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import Head from '@docusaurus/Head';
|
||||||
|
|
||||||
|
export default class Home extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Head>
|
||||||
|
<title>translated Hello</title>
|
||||||
|
</Head>
|
||||||
|
<div>translated TypeScript...</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -69,7 +69,7 @@ exports[`docusaurus-plugin-content-pages loads simple pages with french translat
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"permalink": "/fr/typescript",
|
"permalink": "/fr/typescript",
|
||||||
"source": "@site/src/pages/typescript.tsx",
|
"source": "@site/src/pages/typescript.fr.tsx",
|
||||||
"type": "jsx",
|
"type": "jsx",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import {
|
||||||
aliasedSitePath,
|
aliasedSitePath,
|
||||||
docuHash,
|
docuHash,
|
||||||
getPluginI18nPath,
|
getPluginI18nPath,
|
||||||
getFolderContainingFile,
|
|
||||||
addTrailingPathSeparator,
|
addTrailingPathSeparator,
|
||||||
Globby,
|
Globby,
|
||||||
createAbsoluteFilePathMatcher,
|
createAbsoluteFilePathMatcher,
|
||||||
|
|
@ -22,6 +21,8 @@ import {
|
||||||
parseMarkdownFile,
|
parseMarkdownFile,
|
||||||
isUnlisted,
|
isUnlisted,
|
||||||
isDraft,
|
isDraft,
|
||||||
|
getLocalizedSource,
|
||||||
|
filterFilesWithLocaleExtension,
|
||||||
} from '@docusaurus/utils';
|
} from '@docusaurus/utils';
|
||||||
import {validatePageFrontMatter} from './frontMatter';
|
import {validatePageFrontMatter} from './frontMatter';
|
||||||
|
|
||||||
|
|
@ -62,6 +63,17 @@ export default function pluginContentPages(
|
||||||
);
|
);
|
||||||
const dataDir = path.join(pluginDataDirRoot, options.id ?? DEFAULT_PLUGIN_ID);
|
const dataDir = path.join(pluginDataDirRoot, options.id ?? DEFAULT_PLUGIN_ID);
|
||||||
|
|
||||||
|
async function getPageFiles() {
|
||||||
|
const files = await Globby(options.include, {
|
||||||
|
cwd: contentPaths.contentPath,
|
||||||
|
ignore: options.exclude,
|
||||||
|
});
|
||||||
|
return filterFilesWithLocaleExtension({
|
||||||
|
files,
|
||||||
|
locales: context.i18n.locales,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'docusaurus-plugin-content-pages',
|
name: 'docusaurus-plugin-content-pages',
|
||||||
|
|
||||||
|
|
@ -73,28 +85,21 @@ export default function pluginContentPages(
|
||||||
},
|
},
|
||||||
|
|
||||||
async loadContent() {
|
async loadContent() {
|
||||||
const {include} = options;
|
|
||||||
|
|
||||||
if (!(await fs.pathExists(contentPaths.contentPath))) {
|
if (!(await fs.pathExists(contentPaths.contentPath))) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {baseUrl} = siteConfig;
|
const {baseUrl} = siteConfig;
|
||||||
const pagesFiles = await Globby(include, {
|
const pagesFiles = await getPageFiles();
|
||||||
cwd: contentPaths.contentPath,
|
|
||||||
ignore: options.exclude,
|
|
||||||
});
|
|
||||||
|
|
||||||
async function processPageSourceFile(
|
async function processPageSourceFile(
|
||||||
relativeSource: string,
|
relativeSource: string,
|
||||||
): Promise<Metadata | undefined> {
|
): Promise<Metadata | undefined> {
|
||||||
// Lookup in localized folder in priority
|
const {source} = await getLocalizedSource({
|
||||||
const contentPath = await getFolderContainingFile(
|
|
||||||
getContentPathList(contentPaths),
|
|
||||||
relativeSource,
|
relativeSource,
|
||||||
);
|
contentPaths,
|
||||||
|
locale: context.i18n.currentLocale,
|
||||||
const source = path.join(contentPath, relativeSource);
|
});
|
||||||
const aliasedSourcePath = aliasedSitePath(source, siteDir);
|
const aliasedSourcePath = aliasedSitePath(source, siteDir);
|
||||||
const permalink = normalizeUrl([
|
const permalink = normalizeUrl([
|
||||||
baseUrl,
|
baseUrl,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type PagesContentPaths = {
|
import type {ContentPaths} from '@docusaurus/utils';
|
||||||
contentPath: string;
|
|
||||||
contentPathLocalized: string;
|
export type PagesContentPaths = ContentPaths;
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import fs from 'fs-extra';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import {DEFAULT_PLUGIN_ID} from './constants';
|
import {DEFAULT_PLUGIN_ID} from './constants';
|
||||||
import {normalizeUrl} from './urlUtils';
|
import {normalizeUrl} from './urlUtils';
|
||||||
|
import {findAsyncSequential} from './jsUtils';
|
||||||
import type {
|
import type {
|
||||||
TranslationFileContent,
|
TranslationFileContent,
|
||||||
TranslationFile,
|
TranslationFile,
|
||||||
I18n,
|
I18n,
|
||||||
} from '@docusaurus/types';
|
} from '@docusaurus/types';
|
||||||
|
import type {ContentPaths} from './markdownLinks';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a list of translation file contents, and shallow-merges them into one.
|
* Takes a list of translation file contents, and shallow-merges them into one.
|
||||||
|
|
@ -112,3 +115,112 @@ export function localizePath({
|
||||||
// Url paths; add a trailing slash so it's a valid base URL
|
// Url paths; add a trailing slash so it's a valid base URL
|
||||||
return normalizeUrl([originalPath, i18n.currentLocale, '/']);
|
return normalizeUrl([originalPath, i18n.currentLocale, '/']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localize a content file path
|
||||||
|
* ./dir/myDoc.md => ./dir/myDoc.fr.md
|
||||||
|
* @param filePath
|
||||||
|
* @param locale
|
||||||
|
*/
|
||||||
|
function addLocaleExtension(filePath: string, locale: string) {
|
||||||
|
const {name, dir, ext} = path.parse(filePath);
|
||||||
|
return path.join(dir, `${name}.${locale}${ext}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
type LocalizedSource = {
|
||||||
|
contentPath: string;
|
||||||
|
source: string;
|
||||||
|
type: 'locale-extension' | 'locale-folder' | 'original';
|
||||||
|
};
|
||||||
|
|
||||||
|
function getLocalizedSourceCandidates({
|
||||||
|
relativeSource,
|
||||||
|
contentPaths,
|
||||||
|
locale,
|
||||||
|
}: {
|
||||||
|
relativeSource: string;
|
||||||
|
contentPaths: ContentPaths;
|
||||||
|
locale: string;
|
||||||
|
}): LocalizedSource[] {
|
||||||
|
// docs/myDoc.fr.md
|
||||||
|
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: LocalizedSource = {
|
||||||
|
contentPath: contentPaths.contentPath,
|
||||||
|
source: path.join(contentPaths.contentPathLocalized, relativeSource),
|
||||||
|
type: 'locale-folder',
|
||||||
|
};
|
||||||
|
|
||||||
|
// docs/myDoc.md
|
||||||
|
const originalSource: LocalizedSource = {
|
||||||
|
contentPath: contentPaths.contentPath,
|
||||||
|
source: path.join(contentPaths.contentPath, relativeSource),
|
||||||
|
type: 'original',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Order matters
|
||||||
|
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<LocalizedSource> {
|
||||||
|
// 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(candidates, (candidate) =>
|
||||||
|
fs.pathExists(candidate.source),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!localizedSource) {
|
||||||
|
throw new Error(
|
||||||
|
`Unexpected error, couldn't find any localized source for file at ${path.join(
|
||||||
|
contentPaths.contentPath,
|
||||||
|
relativeSource,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return localizedSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function filterFilesWithLocaleExtension({
|
||||||
|
files,
|
||||||
|
locales,
|
||||||
|
}: {
|
||||||
|
files: string[];
|
||||||
|
locales: string[];
|
||||||
|
}): string[] {
|
||||||
|
const possibleLocaleExtensions = new Set(
|
||||||
|
locales.map((locale) => `.${locale}`),
|
||||||
|
);
|
||||||
|
return files.filter((file) => {
|
||||||
|
const {name} = path.parse(file);
|
||||||
|
return !possibleLocaleExtensions.has(path.extname(name));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@ export {
|
||||||
updateTranslationFileMessages,
|
updateTranslationFileMessages,
|
||||||
getPluginI18nPath,
|
getPluginI18nPath,
|
||||||
localizePath,
|
localizePath,
|
||||||
|
getLocalizedSource,
|
||||||
|
filterFilesWithLocaleExtension,
|
||||||
} from './i18nUtils';
|
} from './i18nUtils';
|
||||||
export {
|
export {
|
||||||
removeSuffix,
|
removeSuffix,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
date: 2024-01-03
|
||||||
|
---
|
||||||
|
|
||||||
|
# Blog i18n test
|
||||||
|
|
||||||
|
French version
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
date: 2024-01-03
|
||||||
|
---
|
||||||
|
|
||||||
|
# Blog i18n test
|
||||||
|
|
||||||
|
English version
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Docs i18n test
|
||||||
|
|
||||||
|
French version
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Docs i18n test
|
||||||
|
|
||||||
|
English version
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Page i18n test
|
||||||
|
|
||||||
|
French version
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Page i18n test
|
||||||
|
|
||||||
|
English version
|
||||||
|
|
@ -26,6 +26,7 @@ import Readme from "../README.mdx"
|
||||||
### Other tests
|
### Other tests
|
||||||
|
|
||||||
- [React 18](/tests/pages/react-18)
|
- [React 18](/tests/pages/react-18)
|
||||||
|
- [i18n](/tests/pages/i18n)
|
||||||
- [Crash test](/tests/pages/crashTest)
|
- [Crash test](/tests/pages/crashTest)
|
||||||
- [Code block tests](/tests/pages/code-block-tests)
|
- [Code block tests](/tests/pages/code-block-tests)
|
||||||
- [Link tests](/tests/pages/link-tests)
|
- [Link tests](/tests/pages/link-tests)
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ export const dogfoodingPluginInstances: PluginConfig[] = [
|
||||||
{
|
{
|
||||||
id: 'docs-tests',
|
id: 'docs-tests',
|
||||||
routeBasePath: '/tests/docs',
|
routeBasePath: '/tests/docs',
|
||||||
|
editUrl: 'https://github.com/facebook/docusaurus/edit/main/website',
|
||||||
sidebarPath: '_dogfooding/docs-tests-sidebars.js',
|
sidebarPath: '_dogfooding/docs-tests-sidebars.js',
|
||||||
versions: {
|
versions: {
|
||||||
current: {
|
current: {
|
||||||
|
|
@ -69,8 +70,7 @@ export const dogfoodingPluginInstances: PluginConfig[] = [
|
||||||
id: 'blog-tests',
|
id: 'blog-tests',
|
||||||
path: '_dogfooding/_blog tests',
|
path: '_dogfooding/_blog tests',
|
||||||
routeBasePath: '/tests/blog',
|
routeBasePath: '/tests/blog',
|
||||||
editUrl:
|
editUrl: 'https://github.com/facebook/docusaurus/edit/main/website',
|
||||||
'https://github.com/facebook/docusaurus/edit/main/website/_dogfooding/_blog-tests',
|
|
||||||
postsPerPage: 3,
|
postsPerPage: 3,
|
||||||
feedOptions: {
|
feedOptions: {
|
||||||
type: 'all',
|
type: 'all',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue