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 =

View file

@ -391,6 +391,8 @@ unlocalized
Unlocalized
unnormalized
unswizzle
untruncated
Untruncated
upvotes
urlset
Vannicatte

View file

@ -99,6 +99,7 @@ export const dogfoodingPluginInstances: PluginConfig[] = [
: defaultReadingTime({content, options: {wordsPerMinute: 5}}),
onInlineTags: 'warn',
onInlineAuthors: 'ignore',
onUntruncatedBlogPosts: 'ignore',
tags: 'tags.yml',
} satisfies BlogOptions,
],

View file

@ -85,6 +85,7 @@ Accepted fields:
| `showLastUpdateTime` | `boolean` | `false` | Whether to display the last date the blog post was updated. This requires access to git history during the build, so will not work correctly with shallow clones (a common default for CI systems). With GitHub `actions/checkout`, use`fetch-depth: 0`. |
| `tags` | `string \| false \| null \| undefined` | `tags.yml` | Path to the YAML tags file listing pre-defined tags. Relative to the blog content directory. |
| `onInlineTags` | `'ignore' \| 'log' \| 'warn' \| 'throw'` | `warn` | The plugin behavior when blog posts contain inline tags (not appearing in the list of pre-defined tags, usually `tags.yml`). |
| `onUntruncatedBlogPosts` | `'ignore' \| 'log' \| 'warn' \| 'throw'` | `warn` | The plugin behavior when blog posts do not contain a truncate marker. |
```mdx-code-block
</APITable>

View file

@ -496,6 +496,10 @@ export default async function createConfigAsync() {
blogDescription: 'Read blog posts about Docusaurus from the team',
blogSidebarCount: 'ALL',
blogSidebarTitle: 'All our posts',
onUntruncatedBlogPosts:
process.env.DOCUSAURUS_CURRENT_LOCALE !== defaultLocale
? 'warn'
: 'throw',
onInlineTags:
process.env.DOCUSAURUS_CURRENT_LOCALE !== defaultLocale
? 'warn'