@docusaurus/plugin-content-blog 3.7.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/atom.xsl +1 -1
- package/lib/blogUtils.js +3 -2
- package/lib/options.js +1 -1
- package/lib/readingTime.d.ts +17 -0
- package/lib/readingTime.js +45 -0
- package/package.json +11 -11
- package/src/blogUtils.ts +4 -3
- package/src/options.ts +2 -1
- package/src/plugin-content-blog.d.ts +17 -15
- package/src/readingTime.ts +49 -0
- package/src/remark/footnoteIDFixer.ts +0 -1
package/assets/atom.xsl
CHANGED
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
<div class="blog-posts">
|
|
72
72
|
<xsl:for-each select="atom:feed/atom:entry">
|
|
73
73
|
<div class="blog-post">
|
|
74
|
-
<h3><a href="{atom:link
|
|
74
|
+
<h3><a href="{atom:link/@href}"><xsl:value-of
|
|
75
75
|
select="atom:title"
|
|
76
76
|
/></a></h3>
|
|
77
77
|
<div class="blog-post-date">
|
package/lib/blogUtils.js
CHANGED
|
@@ -19,12 +19,12 @@ const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
|
19
19
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
20
20
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
21
21
|
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
|
|
22
|
-
const reading_time_1 = tslib_1.__importDefault(require("reading-time"));
|
|
23
22
|
const utils_1 = require("@docusaurus/utils");
|
|
24
23
|
const utils_validation_1 = require("@docusaurus/utils-validation");
|
|
25
24
|
const frontMatter_1 = require("./frontMatter");
|
|
26
25
|
const authors_1 = require("./authors");
|
|
27
26
|
const authorsProblems_1 = require("./authorsProblems");
|
|
27
|
+
const readingTime_1 = require("./readingTime");
|
|
28
28
|
function truncate(fileString, truncateMarker) {
|
|
29
29
|
return fileString.split(truncateMarker, 1).shift();
|
|
30
30
|
}
|
|
@@ -130,7 +130,7 @@ async function parseBlogPostMarkdownFile({ filePath, parseFrontMatter, }) {
|
|
|
130
130
|
throw err;
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
-
const defaultReadingTime = ({ content, options }) => (0,
|
|
133
|
+
const defaultReadingTime = ({ content, locale, options }) => (0, readingTime_1.calculateReadingTime)(content, locale, options);
|
|
134
134
|
async function processBlogSourceFile(blogSourceRelative, contentPaths, context, options, tagsFile, authorsMap) {
|
|
135
135
|
const { siteConfig: { baseUrl, markdown: { parseFrontMatter }, }, siteDir, i18n, } = context;
|
|
136
136
|
const { routeBasePath, tagsBasePath: tagsRouteBasePath, truncateMarker, showReadingTime, editUrl, } = options;
|
|
@@ -238,6 +238,7 @@ async function processBlogSourceFile(blogSourceRelative, contentPaths, context,
|
|
|
238
238
|
content,
|
|
239
239
|
frontMatter,
|
|
240
240
|
defaultReadingTime,
|
|
241
|
+
locale: i18n.currentLocale,
|
|
241
242
|
})
|
|
242
243
|
: undefined,
|
|
243
244
|
hasTruncateMarker: truncateMarker.test(content),
|
package/lib/options.js
CHANGED
|
@@ -51,7 +51,7 @@ exports.DEFAULT_OPTIONS = {
|
|
|
51
51
|
path: 'blog',
|
|
52
52
|
editLocalizedFiles: false,
|
|
53
53
|
authorsMapPath: 'authors.yml',
|
|
54
|
-
readingTime: ({ content, defaultReadingTime }) => defaultReadingTime({ content }),
|
|
54
|
+
readingTime: ({ content, defaultReadingTime, locale }) => defaultReadingTime({ content, locale }),
|
|
55
55
|
sortPosts: 'descending',
|
|
56
56
|
showLastUpdateTime: false,
|
|
57
57
|
showLastUpdateAuthor: false,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Calculates the reading time for a given content string using Intl.Segmenter.
|
|
9
|
+
* @param content The text content to calculate reading time for.
|
|
10
|
+
* @param locale Required locale string for Intl.Segmenter
|
|
11
|
+
* @param options Options for reading time calculation.
|
|
12
|
+
* - wordsPerMinute: number of words per minute (default 200)
|
|
13
|
+
* @returns Estimated reading time in minutes (float, rounded to 2 decimals)
|
|
14
|
+
*/
|
|
15
|
+
export declare function calculateReadingTime(content: string, locale: string, options?: {
|
|
16
|
+
wordsPerMinute?: number;
|
|
17
|
+
}): number;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.calculateReadingTime = calculateReadingTime;
|
|
10
|
+
const DEFAULT_WORDS_PER_MINUTE = 200;
|
|
11
|
+
/**
|
|
12
|
+
* Counts the number of words in a string using Intl.Segmenter.
|
|
13
|
+
* @param content The text content to count words in.
|
|
14
|
+
* @param locale The locale to use for segmentation.
|
|
15
|
+
*/
|
|
16
|
+
function countWords(content, locale) {
|
|
17
|
+
if (!content) {
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
const segmenter = new Intl.Segmenter(locale, { granularity: 'word' });
|
|
21
|
+
let wordCount = 0;
|
|
22
|
+
for (const { isWordLike } of segmenter.segment(content)) {
|
|
23
|
+
if (isWordLike) {
|
|
24
|
+
wordCount += 1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return wordCount;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Calculates the reading time for a given content string using Intl.Segmenter.
|
|
31
|
+
* @param content The text content to calculate reading time for.
|
|
32
|
+
* @param locale Required locale string for Intl.Segmenter
|
|
33
|
+
* @param options Options for reading time calculation.
|
|
34
|
+
* - wordsPerMinute: number of words per minute (default 200)
|
|
35
|
+
* @returns Estimated reading time in minutes (float, rounded to 2 decimals)
|
|
36
|
+
*/
|
|
37
|
+
function calculateReadingTime(content, locale, options) {
|
|
38
|
+
const wordsPerMinute = options?.wordsPerMinute ?? DEFAULT_WORDS_PER_MINUTE;
|
|
39
|
+
const words = countWords(content, locale);
|
|
40
|
+
if (words === 0) {
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
// Calculate reading time in minutes and round to 2 decimal places
|
|
44
|
+
return Math.round((words / wordsPerMinute) * 100) / 100;
|
|
45
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docusaurus/plugin-content-blog",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"description": "Blog plugin for Docusaurus.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "src/plugin-content-blog.d.ts",
|
|
@@ -31,19 +31,19 @@
|
|
|
31
31
|
},
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@docusaurus/core": "3.
|
|
35
|
-
"@docusaurus/logger": "3.
|
|
36
|
-
"@docusaurus/mdx-loader": "3.
|
|
37
|
-
"@docusaurus/theme-common": "3.
|
|
38
|
-
"@docusaurus/types": "3.
|
|
39
|
-
"@docusaurus/utils": "3.
|
|
40
|
-
"@docusaurus/utils-common": "3.
|
|
41
|
-
"@docusaurus/utils-validation": "3.
|
|
34
|
+
"@docusaurus/core": "3.8.0",
|
|
35
|
+
"@docusaurus/logger": "3.8.0",
|
|
36
|
+
"@docusaurus/mdx-loader": "3.8.0",
|
|
37
|
+
"@docusaurus/theme-common": "3.8.0",
|
|
38
|
+
"@docusaurus/types": "3.8.0",
|
|
39
|
+
"@docusaurus/utils": "3.8.0",
|
|
40
|
+
"@docusaurus/utils-common": "3.8.0",
|
|
41
|
+
"@docusaurus/utils-validation": "3.8.0",
|
|
42
42
|
"cheerio": "1.0.0-rc.12",
|
|
43
43
|
"feed": "^4.2.2",
|
|
44
44
|
"fs-extra": "^11.1.1",
|
|
45
45
|
"lodash": "^4.17.21",
|
|
46
|
-
"
|
|
46
|
+
"schema-dts": "^1.1.2",
|
|
47
47
|
"srcset": "^4.0.0",
|
|
48
48
|
"tslib": "^2.6.0",
|
|
49
49
|
"unist-util-visit": "^5.0.0",
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"@total-typescript/shoehorn": "^0.1.2",
|
|
63
63
|
"tree-node-cli": "^1.6.0"
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "948d63c42fad0ba24b7b531a9deb6167e64dc845"
|
|
66
66
|
}
|
package/src/blogUtils.ts
CHANGED
|
@@ -9,7 +9,6 @@ import fs from 'fs-extra';
|
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import _ from 'lodash';
|
|
11
11
|
import logger from '@docusaurus/logger';
|
|
12
|
-
import readingTime from 'reading-time';
|
|
13
12
|
import {
|
|
14
13
|
parseMarkdownFile,
|
|
15
14
|
normalizeUrl,
|
|
@@ -32,6 +31,7 @@ import {getTagsFile} from '@docusaurus/utils-validation';
|
|
|
32
31
|
import {validateBlogPostFrontMatter} from './frontMatter';
|
|
33
32
|
import {getBlogPostAuthors} from './authors';
|
|
34
33
|
import {reportAuthorsProblems} from './authorsProblems';
|
|
34
|
+
import {calculateReadingTime} from './readingTime';
|
|
35
35
|
import type {TagsFile} from '@docusaurus/utils';
|
|
36
36
|
import type {LoadContext, ParseFrontMatter} from '@docusaurus/types';
|
|
37
37
|
import type {
|
|
@@ -210,8 +210,8 @@ async function parseBlogPostMarkdownFile({
|
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
const defaultReadingTime: ReadingTimeFunction = ({content, options}) =>
|
|
214
|
-
|
|
213
|
+
const defaultReadingTime: ReadingTimeFunction = ({content, locale, options}) =>
|
|
214
|
+
calculateReadingTime(content, locale, options);
|
|
215
215
|
|
|
216
216
|
async function processBlogSourceFile(
|
|
217
217
|
blogSourceRelative: string,
|
|
@@ -373,6 +373,7 @@ async function processBlogSourceFile(
|
|
|
373
373
|
content,
|
|
374
374
|
frontMatter,
|
|
375
375
|
defaultReadingTime,
|
|
376
|
+
locale: i18n.currentLocale,
|
|
376
377
|
})
|
|
377
378
|
: undefined,
|
|
378
379
|
hasTruncateMarker: truncateMarker.test(content),
|
package/src/options.ts
CHANGED
|
@@ -63,7 +63,8 @@ export const DEFAULT_OPTIONS: PluginOptions = {
|
|
|
63
63
|
path: 'blog',
|
|
64
64
|
editLocalizedFiles: false,
|
|
65
65
|
authorsMapPath: 'authors.yml',
|
|
66
|
-
readingTime: ({content, defaultReadingTime}) =>
|
|
66
|
+
readingTime: ({content, defaultReadingTime, locale}) =>
|
|
67
|
+
defaultReadingTime({content, locale}),
|
|
67
68
|
sortPosts: 'descending',
|
|
68
69
|
showLastUpdateTime: false,
|
|
69
70
|
showLastUpdateAuthor: false,
|
|
@@ -16,7 +16,12 @@ declare module '@docusaurus/plugin-content-blog' {
|
|
|
16
16
|
FrontMatterLastUpdate,
|
|
17
17
|
TagsPluginOptions,
|
|
18
18
|
} from '@docusaurus/utils';
|
|
19
|
-
import type {
|
|
19
|
+
import type {
|
|
20
|
+
DocusaurusConfig,
|
|
21
|
+
Plugin,
|
|
22
|
+
LoadContext,
|
|
23
|
+
OptionValidationContext,
|
|
24
|
+
} from '@docusaurus/types';
|
|
20
25
|
import type {Item as FeedItem} from 'feed';
|
|
21
26
|
import type {Overwrite} from 'utility-types';
|
|
22
27
|
|
|
@@ -382,15 +387,10 @@ declare module '@docusaurus/plugin-content-blog' {
|
|
|
382
387
|
};
|
|
383
388
|
|
|
384
389
|
/**
|
|
385
|
-
*
|
|
390
|
+
* Options for reading time calculation using Intl.Segmenter.
|
|
386
391
|
*/
|
|
387
392
|
type ReadingTimeOptions = {
|
|
388
393
|
wordsPerMinute?: number;
|
|
389
|
-
/**
|
|
390
|
-
* @param char The character to be matched.
|
|
391
|
-
* @returns `true` if this character is a word bound.
|
|
392
|
-
*/
|
|
393
|
-
wordBound?: (char: string) => boolean;
|
|
394
394
|
};
|
|
395
395
|
|
|
396
396
|
/**
|
|
@@ -400,24 +400,22 @@ declare module '@docusaurus/plugin-content-blog' {
|
|
|
400
400
|
export type ReadingTimeFunction = (params: {
|
|
401
401
|
/** Markdown content. */
|
|
402
402
|
content: string;
|
|
403
|
+
/** Locale for word segmentation. */
|
|
404
|
+
locale: string;
|
|
403
405
|
/** Front matter. */
|
|
404
406
|
frontMatter?: BlogPostFrontMatter & {[key: string]: unknown};
|
|
405
|
-
/** Options
|
|
407
|
+
/** Options for reading time calculation. */
|
|
406
408
|
options?: ReadingTimeOptions;
|
|
407
409
|
}) => number;
|
|
408
410
|
|
|
409
411
|
/**
|
|
410
|
-
* @returns The reading time directly plugged into metadata.
|
|
411
|
-
* hide reading time for a specific post.
|
|
412
|
+
* @returns The reading time directly plugged into metadata.
|
|
413
|
+
* `undefined` to hide reading time for a specific post.
|
|
412
414
|
*/
|
|
413
415
|
export type ReadingTimeFunctionOption = (
|
|
414
|
-
/**
|
|
415
|
-
* The `options` is not provided by the caller; the user can inject their
|
|
416
|
-
* own option values into `defaultReadingTime`
|
|
417
|
-
*/
|
|
418
416
|
params: Required<Omit<Parameters<ReadingTimeFunction>[0], 'options'>> & {
|
|
419
417
|
/**
|
|
420
|
-
* The default reading time implementation
|
|
418
|
+
* The default reading time implementation.
|
|
421
419
|
*/
|
|
422
420
|
defaultReadingTime: ReadingTimeFunction;
|
|
423
421
|
},
|
|
@@ -666,6 +664,10 @@ declare module '@docusaurus/plugin-content-blog' {
|
|
|
666
664
|
context: LoadContext,
|
|
667
665
|
options: PluginOptions,
|
|
668
666
|
): Promise<Plugin<BlogContent>>;
|
|
667
|
+
|
|
668
|
+
export function validateOptions(
|
|
669
|
+
args: OptionValidationContext<Options | undefined, PluginOptions>,
|
|
670
|
+
): PluginOptions;
|
|
669
671
|
}
|
|
670
672
|
|
|
671
673
|
declare module '@theme/BlogPostPage' {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const DEFAULT_WORDS_PER_MINUTE = 200;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Counts the number of words in a string using Intl.Segmenter.
|
|
12
|
+
* @param content The text content to count words in.
|
|
13
|
+
* @param locale The locale to use for segmentation.
|
|
14
|
+
*/
|
|
15
|
+
function countWords(content: string, locale: string): number {
|
|
16
|
+
if (!content) {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
const segmenter = new Intl.Segmenter(locale, {granularity: 'word'});
|
|
20
|
+
let wordCount = 0;
|
|
21
|
+
for (const {isWordLike} of segmenter.segment(content)) {
|
|
22
|
+
if (isWordLike) {
|
|
23
|
+
wordCount += 1;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return wordCount;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Calculates the reading time for a given content string using Intl.Segmenter.
|
|
31
|
+
* @param content The text content to calculate reading time for.
|
|
32
|
+
* @param locale Required locale string for Intl.Segmenter
|
|
33
|
+
* @param options Options for reading time calculation.
|
|
34
|
+
* - wordsPerMinute: number of words per minute (default 200)
|
|
35
|
+
* @returns Estimated reading time in minutes (float, rounded to 2 decimals)
|
|
36
|
+
*/
|
|
37
|
+
export function calculateReadingTime(
|
|
38
|
+
content: string,
|
|
39
|
+
locale: string,
|
|
40
|
+
options?: {wordsPerMinute?: number},
|
|
41
|
+
): number {
|
|
42
|
+
const wordsPerMinute = options?.wordsPerMinute ?? DEFAULT_WORDS_PER_MINUTE;
|
|
43
|
+
const words = countWords(content, locale);
|
|
44
|
+
if (words === 0) {
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
// Calculate reading time in minutes and round to 2 decimal places
|
|
48
|
+
return Math.round((words / wordsPerMinute) * 100) / 100;
|
|
49
|
+
}
|