Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(blog): add onUntruncatedBlogPosts blog options #10375

Merged
merged 10 commits into from
Aug 9, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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'},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]"`,
);
});
});
});
23 changes: 23 additions & 0 deletions packages/docusaurus-plugin-content-blog/src/blogUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
isDraft,
readLastUpdateData,
normalizeTags,
aliasedSitePathToRelativePath,
} from '@docusaurus/utils';
import {getTagsFile} from '@docusaurus/utils-validation';
import {validateBlogPostFrontMatter} from './frontMatter';
Expand All @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions packages/docusaurus-plugin-content-blog/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
shouldBeListed,
applyProcessBlogPosts,
generateBlogPosts,
reportUntruncatedBlogPosts,
} from './blogUtils';
import footnoteIDFixer from './remark/footnoteIDFixer';
import {translateContent, getTranslationFiles} from './translations';
Expand Down Expand Up @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions packages/docusaurus-plugin-content-blog/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export const DEFAULT_OPTIONS: PluginOptions = {
tags: undefined,
authorsBasePath: 'authors',
onInlineAuthors: 'warn',
onUntruncatedBlogPosts: 'warn',
};

export const XSLTBuiltInPaths = {
Expand Down Expand Up @@ -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({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
2 changes: 2 additions & 0 deletions project-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,8 @@ unlocalized
Unlocalized
unnormalized
unswizzle
untruncated
Untruncated
upvotes
urlset
Vannicatte
Expand Down
1 change: 1 addition & 0 deletions website/_dogfooding/dogfooding.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export const dogfoodingPluginInstances: PluginConfig[] = [
: defaultReadingTime({content, options: {wordsPerMinute: 5}}),
onInlineTags: 'warn',
onInlineAuthors: 'ignore',
onUntruncatedBlogPosts: 'ignore',
tags: 'tags.yml',
} satisfies BlogOptions,
],
Expand Down
1 change: 1 addition & 0 deletions website/docs/api/plugins/plugin-content-blog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
4 changes: 4 additions & 0 deletions website/docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down