Commit 00dc1c9
Changed files (6)
src
components
misc
pages
posts
plugins
utils
src/components/misc/PostInfo.astro
@@ -3,22 +3,26 @@ import { articleConfig } from '@/config';
import MetaIcon from '@components/widgets/MetaIcon.astro';
import I18nKey from '@i18n/I18nKey';
import { i18n } from '@i18n/translation';
-import { countWords } from '@utils/content-utils';
interface Props {
title: string;
publishedAt: Date;
category: string;
tags: string[];
- wordCount: ReturnType<typeof countWords>;
+ wordsCount: number;
+ readingTime: number;
class?: string;
}
-const { title, publishedAt, category, tags, wordCount, class: className } = Astro.props;
-
-const readTime =
- Math.ceil(wordCount.cjk / articleConfig.readingTime.wordsPerMinute.cjk) +
- Math.ceil(wordCount.nonCjk / articleConfig.readingTime.wordsPerMinute.nonCjk);
+const {
+ title,
+ publishedAt,
+ category,
+ tags,
+ wordsCount: wordCount,
+ readingTime,
+ class: className,
+} = Astro.props;
const metas: ({ icon: string; text: string; link?: string } | undefined)[] = [
{
@@ -28,13 +32,13 @@ const metas: ({ icon: string; text: string; link?: string } | undefined)[] = [
articleConfig.wordCount
? {
icon: 'material-symbols:docs-rounded',
- text: `${wordCount.total} ${wordCount.total === 1 ? i18n(I18nKey.wordCount) : i18n(I18nKey.wordsCount)}`,
+ text: `${wordCount} ${wordCount === 1 ? i18n(I18nKey.wordCount) : i18n(I18nKey.wordsCount)}`,
}
: undefined,
articleConfig.readingTime
? {
icon: 'material-symbols:nest-clock-farsight-analog-rounded',
- text: `${readTime} ${readTime === 1 ? i18n(I18nKey.minuteCount) : i18n(I18nKey.minutesCount)}`,
+ text: `${readingTime} ${readingTime === 1 ? i18n(I18nKey.minuteCount) : i18n(I18nKey.minutesCount)}`,
}
: undefined,
category
src/pages/posts/[article].astro
@@ -7,7 +7,6 @@ import Markdown from '@components/utils/Markdown.astro';
import ProfileCard from '@components/widgets/ProfileCard.astro';
import TOC from '@components/widgets/TOC.astro';
import GridLayout from '@layouts/GridLayout.astro';
-import { countWords } from '@utils/content-utils';
import { getCollection, render } from 'astro:content';
export async function getStaticPaths() {
@@ -19,9 +18,7 @@ export async function getStaticPaths() {
}
const { article } = Astro.props;
-const { Content, headings } = await render(article);
-
-const wordCount = countWords(article.body || '');
+const { Content, headings, remarkPluginFrontmatter } = await render(article);
---
<GridLayout title={article.data.title} description={article.data.description}>
@@ -31,7 +28,8 @@ const wordCount = countWords(article.body || '');
publishedAt={article.data.published}
category={article.data.category}
tags={article.data.tags}
- wordCount={wordCount}
+ wordsCount={remarkPluginFrontmatter.words}
+ readingTime={remarkPluginFrontmatter.minutes}
class="mx-2 mt-4"
/>
</Fragment>
src/plugins/remark-reading-time.mjs
@@ -0,0 +1,11 @@
+import { toString } from 'mdast-util-to-string';
+import getReadingTime from 'reading-time';
+
+export function remarkReadingTime() {
+ return (tree, { data }) => {
+ const textOnPage = toString(tree);
+ const readingTime = getReadingTime(textOnPage);
+ data.astro.frontmatter.minutes = Math.max(1, Math.round(readingTime.minutes));
+ data.astro.frontmatter.words = readingTime.words;
+ };
+}
src/utils/content-utils.ts
@@ -64,19 +64,3 @@ export async function getTimeArchives() {
}))
.sort((a, b) => b.year - a.year);
}
-
-export function countWords(text: string): { cjk: number; nonCjk: number; total: number } {
- const cjkRegex =
- /[\u4E00-\u9FFF\u3400-\u4DBF\u20000-\u2A6DF\u2A700-\u2B73F\u2B740-\u2B81F\u2B820-\u2CEAF\uF900-\uFAFF\u3040-\u309F\u30A0-\u30FF\uAC00-\uD7AF]/g;
- const cjkCount = (text.match(cjkRegex) || []).length;
- const nonCjkText = text.replace(cjkRegex, '');
- const wordCount = nonCjkText
- .trim()
- .split(/\s+/)
- .filter((word) => word.length > 0).length;
- return {
- cjk: cjkCount,
- nonCjk: wordCount,
- total: cjkCount + wordCount,
- };
-}
astro.config.mjs
@@ -1,4 +1,5 @@
// @ts-check
+import { remarkReadingTime } from './src/plugins/remark-reading-time.mjs';
import sitemap from '@astrojs/sitemap';
import tailwind from '@astrojs/tailwind';
import icon from 'astro-icon';
@@ -17,4 +18,7 @@ export default defineConfig({
sitemap({ filter: (page) => !page.includes('/archives/') && !page.includes('/about/') }),
pagefind(),
],
+ markdown: {
+ remarkPlugins: [remarkReadingTime],
+ },
});
package.json
@@ -24,7 +24,9 @@
"astro-pagefind": "^1.8.0",
"autoprefixer": "^10.4.20",
"daisyui": "^4.12.23",
+ "mdast-util-to-string": "^4.0.0",
"postcss-load-config": "^6.0.1",
+ "reading-time": "^1.5.0",
"sass": "^1.84.0",
"sharp": "^0.33.5",
"tailwindcss": "^3.4.17",