mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-12 16:47:26 +02:00
refactor(v2): blog/docs: add more context in error messages (#4989)
* refactor(v2): blog/docs: add more context in error messages * more error handling
This commit is contained in:
parent
b54ec72389
commit
1b0acc5547
3 changed files with 178 additions and 116 deletions
|
@ -55,6 +55,19 @@ function toUrl({date, link}: DateLink) {
|
||||||
.replace(/-/g, '/')}/${link}`;
|
.replace(/-/g, '/')}/${link}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatBlogPostDate(locale: string, date: Date): string {
|
||||||
|
try {
|
||||||
|
return new Intl.DateTimeFormat(locale, {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
timeZone: 'UTC',
|
||||||
|
}).format(date);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Can't format blog post date "${date}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function generateBlogFeed(
|
export async function generateBlogFeed(
|
||||||
contentPaths: BlogContentPaths,
|
contentPaths: BlogContentPaths,
|
||||||
context: LoadContext,
|
context: LoadContext,
|
||||||
|
@ -131,123 +144,126 @@ export async function generateBlogPosts(
|
||||||
|
|
||||||
const blogPosts: BlogPost[] = [];
|
const blogPosts: BlogPost[] = [];
|
||||||
|
|
||||||
|
async function processBlogSourceFile(blogSourceFile: string) {
|
||||||
|
// Lookup in localized folder in priority
|
||||||
|
const blogDirPath = await getFolderContainingFile(
|
||||||
|
getContentPathList(contentPaths),
|
||||||
|
blogSourceFile,
|
||||||
|
);
|
||||||
|
|
||||||
|
const source = path.join(blogDirPath, blogSourceFile);
|
||||||
|
|
||||||
|
const {
|
||||||
|
frontMatter: unsafeFrontMatter,
|
||||||
|
content,
|
||||||
|
contentTitle,
|
||||||
|
excerpt,
|
||||||
|
} = await parseMarkdownFile(source, {removeContentTitle: true});
|
||||||
|
const frontMatter = validateBlogPostFrontMatter(unsafeFrontMatter);
|
||||||
|
|
||||||
|
const aliasedSource = aliasedSitePath(source, siteDir);
|
||||||
|
|
||||||
|
const blogFileName = path.basename(blogSourceFile);
|
||||||
|
|
||||||
|
if (frontMatter.draft && process.env.NODE_ENV === 'production') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frontMatter.id) {
|
||||||
|
console.warn(
|
||||||
|
chalk.yellow(
|
||||||
|
`"id" header option is deprecated in ${blogFileName} file. Please use "slug" option instead.`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let date: Date | undefined;
|
||||||
|
// Extract date and title from filename.
|
||||||
|
const dateFilenameMatch = blogFileName.match(DATE_FILENAME_PATTERN);
|
||||||
|
let linkName = blogFileName.replace(/\.mdx?$/, '');
|
||||||
|
|
||||||
|
if (dateFilenameMatch) {
|
||||||
|
const [, dateString, name] = dateFilenameMatch;
|
||||||
|
// Always treat dates as UTC by adding the `Z`
|
||||||
|
date = new Date(`${dateString}Z`);
|
||||||
|
linkName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer user-defined date.
|
||||||
|
if (frontMatter.date) {
|
||||||
|
date = frontMatter.date;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use file create time for blog.
|
||||||
|
date = date ?? (await fs.stat(source)).birthtime;
|
||||||
|
const formattedDate = formatBlogPostDate(i18n.currentLocale, date);
|
||||||
|
|
||||||
|
const title = frontMatter.title ?? contentTitle ?? linkName;
|
||||||
|
const description = frontMatter.description ?? excerpt ?? '';
|
||||||
|
|
||||||
|
const slug =
|
||||||
|
frontMatter.slug ||
|
||||||
|
(dateFilenameMatch ? toUrl({date, link: linkName}) : linkName);
|
||||||
|
|
||||||
|
const permalink = normalizeUrl([baseUrl, routeBasePath, slug]);
|
||||||
|
|
||||||
|
function getBlogEditUrl() {
|
||||||
|
const blogPathRelative = path.relative(blogDirPath, path.resolve(source));
|
||||||
|
|
||||||
|
if (typeof editUrl === 'function') {
|
||||||
|
return editUrl({
|
||||||
|
blogDirPath: posixPath(path.relative(siteDir, blogDirPath)),
|
||||||
|
blogPath: posixPath(blogPathRelative),
|
||||||
|
permalink,
|
||||||
|
locale: i18n.currentLocale,
|
||||||
|
});
|
||||||
|
} else if (typeof editUrl === 'string') {
|
||||||
|
const isLocalized = blogDirPath === contentPaths.contentPathLocalized;
|
||||||
|
const fileContentPath =
|
||||||
|
isLocalized && options.editLocalizedFiles
|
||||||
|
? contentPaths.contentPathLocalized
|
||||||
|
: contentPaths.contentPath;
|
||||||
|
|
||||||
|
const contentPathEditUrl = normalizeUrl([
|
||||||
|
editUrl,
|
||||||
|
posixPath(path.relative(siteDir, fileContentPath)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return getEditUrl(blogPathRelative, contentPathEditUrl);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blogPosts.push({
|
||||||
|
id: frontMatter.slug ?? title,
|
||||||
|
metadata: {
|
||||||
|
permalink,
|
||||||
|
editUrl: getBlogEditUrl(),
|
||||||
|
source: aliasedSource,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
date,
|
||||||
|
formattedDate,
|
||||||
|
tags: frontMatter.tags ?? [],
|
||||||
|
readingTime: showReadingTime ? readingTime(content).minutes : undefined,
|
||||||
|
truncated: truncateMarker?.test(content) || false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
blogSourceFiles.map(async (blogSourceFile: string) => {
|
blogSourceFiles.map(async (blogSourceFile: string) => {
|
||||||
// Lookup in localized folder in priority
|
try {
|
||||||
const blogDirPath = await getFolderContainingFile(
|
return await processBlogSourceFile(blogSourceFile);
|
||||||
getContentPathList(contentPaths),
|
} catch (e) {
|
||||||
blogSourceFile,
|
console.error(
|
||||||
);
|
chalk.red(
|
||||||
|
`Processing of blog source file failed for path "${blogSourceFile}"`,
|
||||||
const source = path.join(blogDirPath, blogSourceFile);
|
|
||||||
|
|
||||||
const {
|
|
||||||
frontMatter: unsafeFrontMatter,
|
|
||||||
content,
|
|
||||||
contentTitle,
|
|
||||||
excerpt,
|
|
||||||
} = await parseMarkdownFile(source, {removeContentTitle: true});
|
|
||||||
const frontMatter = validateBlogPostFrontMatter(unsafeFrontMatter);
|
|
||||||
|
|
||||||
const aliasedSource = aliasedSitePath(source, siteDir);
|
|
||||||
|
|
||||||
const blogFileName = path.basename(blogSourceFile);
|
|
||||||
|
|
||||||
if (frontMatter.draft && process.env.NODE_ENV === 'production') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frontMatter.id) {
|
|
||||||
console.warn(
|
|
||||||
chalk.yellow(
|
|
||||||
`"id" header option is deprecated in ${blogFileName} file. Please use "slug" option instead.`,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
let date: Date | undefined;
|
|
||||||
// Extract date and title from filename.
|
|
||||||
const dateFilenameMatch = blogFileName.match(DATE_FILENAME_PATTERN);
|
|
||||||
let linkName = blogFileName.replace(/\.mdx?$/, '');
|
|
||||||
|
|
||||||
if (dateFilenameMatch) {
|
|
||||||
const [, dateString, name] = dateFilenameMatch;
|
|
||||||
// Always treat dates as UTC by adding the `Z`
|
|
||||||
date = new Date(`${dateString}Z`);
|
|
||||||
linkName = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer user-defined date.
|
|
||||||
if (frontMatter.date) {
|
|
||||||
date = frontMatter.date;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use file create time for blog.
|
|
||||||
date = date ?? (await fs.stat(source)).birthtime;
|
|
||||||
const formattedDate = new Intl.DateTimeFormat(i18n.currentLocale, {
|
|
||||||
day: 'numeric',
|
|
||||||
month: 'long',
|
|
||||||
year: 'numeric',
|
|
||||||
timeZone: 'UTC',
|
|
||||||
}).format(date);
|
|
||||||
|
|
||||||
const title = frontMatter.title ?? contentTitle ?? linkName;
|
|
||||||
const description = frontMatter.description ?? excerpt ?? '';
|
|
||||||
|
|
||||||
const slug =
|
|
||||||
frontMatter.slug ||
|
|
||||||
(dateFilenameMatch ? toUrl({date, link: linkName}) : linkName);
|
|
||||||
|
|
||||||
const permalink = normalizeUrl([baseUrl, routeBasePath, slug]);
|
|
||||||
|
|
||||||
function getBlogEditUrl() {
|
|
||||||
const blogPathRelative = path.relative(
|
|
||||||
blogDirPath,
|
|
||||||
path.resolve(source),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (typeof editUrl === 'function') {
|
|
||||||
return editUrl({
|
|
||||||
blogDirPath: posixPath(path.relative(siteDir, blogDirPath)),
|
|
||||||
blogPath: posixPath(blogPathRelative),
|
|
||||||
permalink,
|
|
||||||
locale: i18n.currentLocale,
|
|
||||||
});
|
|
||||||
} else if (typeof editUrl === 'string') {
|
|
||||||
const isLocalized = blogDirPath === contentPaths.contentPathLocalized;
|
|
||||||
const fileContentPath =
|
|
||||||
isLocalized && options.editLocalizedFiles
|
|
||||||
? contentPaths.contentPathLocalized
|
|
||||||
: contentPaths.contentPath;
|
|
||||||
|
|
||||||
const contentPathEditUrl = normalizeUrl([
|
|
||||||
editUrl,
|
|
||||||
posixPath(path.relative(siteDir, fileContentPath)),
|
|
||||||
]);
|
|
||||||
|
|
||||||
return getEditUrl(blogPathRelative, contentPathEditUrl);
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blogPosts.push({
|
|
||||||
id: frontMatter.slug ?? title,
|
|
||||||
metadata: {
|
|
||||||
permalink,
|
|
||||||
editUrl: getBlogEditUrl(),
|
|
||||||
source: aliasedSource,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
date,
|
|
||||||
formattedDate,
|
|
||||||
tags: frontMatter.tags ?? [],
|
|
||||||
readingTime: showReadingTime
|
|
||||||
? readingTime(content).minutes
|
|
||||||
: undefined,
|
|
||||||
truncated: truncateMarker?.test(content) || false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ import globby from 'globby';
|
||||||
import {getDocsDirPaths} from './versions';
|
import {getDocsDirPaths} from './versions';
|
||||||
import {stripPathNumberPrefixes} from './numberPrefix';
|
import {stripPathNumberPrefixes} from './numberPrefix';
|
||||||
import {validateDocFrontMatter} from './docFrontMatter';
|
import {validateDocFrontMatter} from './docFrontMatter';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
|
||||||
type LastUpdateOptions = Pick<
|
type LastUpdateOptions = Pick<
|
||||||
PluginOptions,
|
PluginOptions,
|
||||||
|
@ -102,7 +103,7 @@ export async function readVersionDocs(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function processDocMetadata({
|
function doProcessDocMetadata({
|
||||||
docFile,
|
docFile,
|
||||||
versionMetadata,
|
versionMetadata,
|
||||||
context,
|
context,
|
||||||
|
@ -262,3 +263,21 @@ export function processDocMetadata({
|
||||||
frontMatter,
|
frontMatter,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function processDocMetadata(args: {
|
||||||
|
docFile: DocFile;
|
||||||
|
versionMetadata: VersionMetadata;
|
||||||
|
context: LoadContext;
|
||||||
|
options: MetadataOptions;
|
||||||
|
}): DocMetadataBase {
|
||||||
|
try {
|
||||||
|
return doProcessDocMetadata(args);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
chalk.red(
|
||||||
|
`Can't process doc metadatas for doc at path "${args.docFile.filePath}" in version "${args.versionMetadata.versionName}"`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ import {
|
||||||
getLoadedContentTranslationFiles,
|
getLoadedContentTranslationFiles,
|
||||||
} from './translations';
|
} from './translations';
|
||||||
import {CategoryMetadataFilenamePattern} from './sidebarItemsGenerator';
|
import {CategoryMetadataFilenamePattern} from './sidebarItemsGenerator';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
|
||||||
export default function pluginContentDocs(
|
export default function pluginContentDocs(
|
||||||
context: LoadContext,
|
context: LoadContext,
|
||||||
|
@ -163,7 +164,7 @@ export default function pluginContentDocs(
|
||||||
return Promise.all(docFiles.map(processVersionDoc));
|
return Promise.all(docFiles.map(processVersionDoc));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadVersion(
|
async function doLoadVersion(
|
||||||
versionMetadata: VersionMetadata,
|
versionMetadata: VersionMetadata,
|
||||||
): Promise<LoadedVersion> {
|
): Promise<LoadedVersion> {
|
||||||
const unprocessedSidebars = loadSidebars(
|
const unprocessedSidebars = loadSidebars(
|
||||||
|
@ -267,6 +268,19 @@ export default function pluginContentDocs(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadVersion(versionMetadata: VersionMetadata) {
|
||||||
|
try {
|
||||||
|
return await doLoadVersion(versionMetadata);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
chalk.red(
|
||||||
|
`Loading of version failed for version "${versionMetadata.versionName}"`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
loadedVersions: await Promise.all(versionsMetadata.map(loadVersion)),
|
loadedVersions: await Promise.all(versionsMetadata.map(loadVersion)),
|
||||||
};
|
};
|
||||||
|
@ -307,7 +321,7 @@ export default function pluginContentDocs(
|
||||||
return routes.sort((a, b) => a.path.localeCompare(b.path));
|
return routes.sort((a, b) => a.path.localeCompare(b.path));
|
||||||
};
|
};
|
||||||
|
|
||||||
async function handleVersion(loadedVersion: LoadedVersion) {
|
async function doCreateVersionRoutes(loadedVersion: LoadedVersion) {
|
||||||
const versionMetadataPropPath = await createData(
|
const versionMetadataPropPath = await createData(
|
||||||
`${docuHash(
|
`${docuHash(
|
||||||
`version-${loadedVersion.versionName}-metadata-prop`,
|
`version-${loadedVersion.versionName}-metadata-prop`,
|
||||||
|
@ -334,7 +348,20 @@ export default function pluginContentDocs(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(loadedVersions.map(handleVersion));
|
async function createVersionRoutes(loadedVersion: LoadedVersion) {
|
||||||
|
try {
|
||||||
|
return await doCreateVersionRoutes(loadedVersion);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
chalk.red(
|
||||||
|
`Can't create version routes for version "${loadedVersion.versionName}"`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(loadedVersions.map(createVersionRoutes));
|
||||||
|
|
||||||
setGlobalData<GlobalPluginData>({
|
setGlobalData<GlobalPluginData>({
|
||||||
path: normalizeUrl([baseUrl, options.routeBasePath]),
|
path: normalizeUrl([baseUrl, options.routeBasePath]),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue