mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-29 18:27:56 +02:00
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:
parent
f43be857d7
commit
a096bbc0b9
10 changed files with 189 additions and 0 deletions
|
@ -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'},
|
||||
|
|
|
@ -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]"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -391,6 +391,8 @@ unlocalized
|
|||
Unlocalized
|
||||
unnormalized
|
||||
unswizzle
|
||||
untruncated
|
||||
Untruncated
|
||||
upvotes
|
||||
urlset
|
||||
Vannicatte
|
||||
|
|
|
@ -99,6 +99,7 @@ export const dogfoodingPluginInstances: PluginConfig[] = [
|
|||
: defaultReadingTime({content, options: {wordsPerMinute: 5}}),
|
||||
onInlineTags: 'warn',
|
||||
onInlineAuthors: 'ignore',
|
||||
onUntruncatedBlogPosts: 'ignore',
|
||||
tags: 'tags.yml',
|
||||
} satisfies BlogOptions,
|
||||
],
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Add table
Reference in a new issue