feat(blog): add onUntruncatedBlogPosts blog options (#10375)

Co-authored-by: OzakIOne <OzakIOne@users.noreply.github.com>
Co-authored-by: Sébastien Lorber <slorber@users.noreply.github.com>
Co-authored-by: sebastien <lorber.sebastien@gmail.com>
This commit is contained in:
ozaki 2024-08-09 16:48:44 +02:00 committed by GitHub
parent f43be857d7
commit a096bbc0b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 189 additions and 0 deletions

View file

@ -5,12 +5,14 @@
* LICENSE file in the root directory of this source tree.
*/
import {jest} from '@jest/globals';
import {fromPartial} from '@total-typescript/shoehorn';
import {
truncate,
parseBlogFileName,
paginateBlogPosts,
applyProcessBlogPosts,
reportUntruncatedBlogPosts,
} from '../blogUtils';
import type {BlogPost} from '@docusaurus/plugin-content-blog';
@ -32,6 +34,109 @@ describe('truncate', () => {
});
});
describe('reportUntruncatedBlogPosts', () => {
function testPost({
source,
hasTruncateMarker,
}: {
source: string;
hasTruncateMarker: boolean;
}): BlogPost {
return fromPartial({
metadata: {
source,
hasTruncateMarker,
},
});
}
it('throw for untruncated blog posts', () => {
const blogPosts = [
testPost({source: '@site/blog/post1.md', hasTruncateMarker: false}),
testPost({source: '@site/blog/post2.md', hasTruncateMarker: true}),
testPost({
source: '@site/blog/subDir/post3.md',
hasTruncateMarker: false,
}),
];
expect(() =>
reportUntruncatedBlogPosts({blogPosts, onUntruncatedBlogPosts: 'throw'}),
).toThrowErrorMatchingInlineSnapshot(`
"Docusaurus found blog posts without truncation markers:
- "blog/post1.md"
- "blog/subDir/post3.md"
We recommend using truncation markers (\`<!-- truncate -->\` or \`{/* truncate */}\`) in blog posts to create shorter previews on blog paginated lists.
Tip: turn this security off with the \`onUntruncatedBlogPosts: 'ignore'\` blog plugin option."
`);
});
it('warn for untruncated blog posts', () => {
const consoleMock = jest.spyOn(console, 'warn');
const blogPosts = [
testPost({source: '@site/blog/post1.md', hasTruncateMarker: false}),
testPost({source: '@site/blog/post2.md', hasTruncateMarker: true}),
testPost({
source: '@site/blog/subDir/post3.md',
hasTruncateMarker: false,
}),
];
expect(() =>
reportUntruncatedBlogPosts({blogPosts, onUntruncatedBlogPosts: 'warn'}),
).not.toThrow();
expect(consoleMock.mock.calls).toMatchInlineSnapshot(`
[
[
"[WARNING] Docusaurus found blog posts without truncation markers:
- "blog/post1.md"
- "blog/subDir/post3.md"
We recommend using truncation markers (\`<!-- truncate -->\` or \`{/* truncate */}\`) in blog posts to create shorter previews on blog paginated lists.
Tip: turn this security off with the \`onUntruncatedBlogPosts: 'ignore'\` blog plugin option.",
],
]
`);
consoleMock.mockRestore();
});
it('ignore untruncated blog posts', () => {
const logMock = jest.spyOn(console, 'log');
const warnMock = jest.spyOn(console, 'warn');
const errorMock = jest.spyOn(console, 'error');
const blogPosts = [
testPost({source: '@site/blog/post1.md', hasTruncateMarker: false}),
testPost({source: '@site/blog/post2.md', hasTruncateMarker: true}),
testPost({
source: '@site/blog/subDir/post3.md',
hasTruncateMarker: false,
}),
];
expect(() =>
reportUntruncatedBlogPosts({blogPosts, onUntruncatedBlogPosts: 'ignore'}),
).not.toThrow();
expect(logMock).not.toHaveBeenCalled();
expect(warnMock).not.toHaveBeenCalled();
expect(errorMock).not.toHaveBeenCalled();
logMock.mockRestore();
warnMock.mockRestore();
errorMock.mockRestore();
});
it('does not throw for truncated posts', () => {
const blogPosts = [
testPost({source: '@site/blog/post1.md', hasTruncateMarker: true}),
testPost({source: '@site/blog/post2.md', hasTruncateMarker: true}),
];
expect(() =>
reportUntruncatedBlogPosts({blogPosts, onUntruncatedBlogPosts: 'throw'}),
).not.toThrow();
});
});
describe('paginateBlogPosts', () => {
const blogPosts = [
{id: 'post1', metadata: {}, content: 'Foo 1'},

View file

@ -374,4 +374,46 @@ describe('validateOptions', () => {
);
});
});
describe('onUntruncatedBlogPosts', () => {
it('accepts onUntruncatedBlogPosts - undefined', () => {
expect(
testValidate({onUntruncatedBlogPosts: undefined})
.onUntruncatedBlogPosts,
).toBe('warn');
});
it('accepts onUntruncatedBlogPosts - "throw"', () => {
expect(
testValidate({onUntruncatedBlogPosts: 'throw'}).onUntruncatedBlogPosts,
).toBe('throw');
});
it('rejects onUntruncatedBlogPosts - "trace"', () => {
expect(() =>
// @ts-expect-error: test
testValidate({onUntruncatedBlogPosts: 'trace'}),
).toThrowErrorMatchingInlineSnapshot(
`""onUntruncatedBlogPosts" must be one of [ignore, log, warn, throw]"`,
);
});
it('rejects onUntruncatedBlogPosts - null', () => {
expect(() =>
// @ts-expect-error: test
testValidate({onUntruncatedBlogPosts: 42}),
).toThrowErrorMatchingInlineSnapshot(
`""onUntruncatedBlogPosts" must be one of [ignore, log, warn, throw]"`,
);
});
it('rejects onUntruncatedBlogPosts - 42', () => {
expect(() =>
// @ts-expect-error: test
testValidate({onUntruncatedBlogPosts: 42}),
).toThrowErrorMatchingInlineSnapshot(
`""onUntruncatedBlogPosts" must be one of [ignore, log, warn, throw]"`,
);
});
});
});

View file

@ -26,6 +26,7 @@ import {
isDraft,
readLastUpdateData,
normalizeTags,
aliasedSitePathToRelativePath,
} from '@docusaurus/utils';
import {getTagsFile} from '@docusaurus/utils-validation';
import {validateBlogPostFrontMatter} from './frontMatter';
@ -47,6 +48,28 @@ export function truncate(fileString: string, truncateMarker: RegExp): string {
return fileString.split(truncateMarker, 1).shift()!;
}
export function reportUntruncatedBlogPosts({
blogPosts,
onUntruncatedBlogPosts,
}: {
blogPosts: BlogPost[];
onUntruncatedBlogPosts: PluginOptions['onUntruncatedBlogPosts'];
}): void {
const untruncatedBlogPosts = blogPosts.filter(
(p) => !p.metadata.hasTruncateMarker,
);
if (onUntruncatedBlogPosts !== 'ignore' && untruncatedBlogPosts.length > 0) {
const message = logger.interpolate`Docusaurus found blog posts without truncation markers:
- ${untruncatedBlogPosts
.map((p) => logger.path(aliasedSitePathToRelativePath(p.metadata.source)))
.join('\n- ')}
We recommend using truncation markers (code=${`<!-- truncate -->`} or code=${`{/* truncate */}`}) in blog posts to create shorter previews on blog paginated lists.
Tip: turn this security off with the code=${`onUntruncatedBlogPosts: 'ignore'`} blog plugin option.`;
logger.report(onUntruncatedBlogPosts)(message);
}
}
export function paginateBlogPosts({
blogPosts,
basePageUrl,

View file

@ -28,6 +28,7 @@ import {
shouldBeListed,
applyProcessBlogPosts,
generateBlogPosts,
reportUntruncatedBlogPosts,
} from './blogUtils';
import footnoteIDFixer from './remark/footnoteIDFixer';
import {translateContent, getTranslationFiles} from './translations';
@ -189,6 +190,10 @@ export default async function pluginContentBlog(
blogPosts,
processBlogPosts: options.processBlogPosts,
});
reportUntruncatedBlogPosts({
blogPosts,
onUntruncatedBlogPosts: options.onUntruncatedBlogPosts,
});
const listedBlogPosts = blogPosts.filter(shouldBeListed);
if (!blogPosts.length) {

View file

@ -72,6 +72,7 @@ export const DEFAULT_OPTIONS: PluginOptions = {
tags: undefined,
authorsBasePath: 'authors',
onInlineAuthors: 'warn',
onUntruncatedBlogPosts: 'warn',
};
export const XSLTBuiltInPaths = {
@ -240,6 +241,9 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
onInlineAuthors: Joi.string()
.equal('ignore', 'log', 'warn', 'throw')
.default(DEFAULT_OPTIONS.onInlineAuthors),
onUntruncatedBlogPosts: Joi.string()
.equal('ignore', 'log', 'warn', 'throw')
.default(DEFAULT_OPTIONS.onUntruncatedBlogPosts),
}).default(DEFAULT_OPTIONS);
export function validateOptions({

View file

@ -521,6 +521,8 @@ declare module '@docusaurus/plugin-content-blog' {
authorsBasePath: string;
/** The behavior of Docusaurus when it finds inline authors. */
onInlineAuthors: 'ignore' | 'log' | 'warn' | 'throw';
/** The behavior of Docusaurus when it finds untruncated blog posts. */
onUntruncatedBlogPosts: 'ignore' | 'log' | 'warn' | 'throw';
};
export type UserFeedXSLTOptions =