Skip to content

Commit 0aa59c8

Browse files
committed
feat(plugin-seo): use excerpt content as description if present
1 parent dd1aa1d commit 0aa59c8

File tree

7 files changed

+227
-51
lines changed

7 files changed

+227
-51
lines changed

plugins/plugin-seo/src/node/generateDescription.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getPageText } from '@vuepress/helper'
1+
import { getPageExcerptContent, getText } from '@vuepress/helper'
22
import type { App } from 'vuepress/core'
33
import type { ExtendPage } from '../typings/index.js'
44

@@ -9,7 +9,14 @@ export const generateDescription = (
99
): void => {
1010
// generate description
1111
if (!page.frontmatter.description && autoDescription) {
12-
const pageText = getPageText(app, page, { length: 180, singleLine: true })
12+
const content = getPageExcerptContent(page.content)
13+
? page.data.excerpt ?? page.contentRendered
14+
: page.contentRendered
15+
16+
const pageText = getText(content, app.options.base, {
17+
length: 180,
18+
singleLine: true,
19+
})
1320

1421
if (pageText.length) {
1522
page.frontmatter.description =
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Page Excerpt
2+
3+
This is **excerpt** content.
4+
5+
<!-- more -->
6+
7+
## Heading 2
8+
9+
Page content.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# 页面摘要
2+
3+
这是页面**摘要**
4+
5+
<!-- more -->
6+
7+
## 标题 2
8+
9+
页面内容。

plugins/plugin-seo/tests/node/__snapshots__/description.spec.ts.snap

Lines changed: 141 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,75 @@ exports[`Should generate seo information > Should contain basic properties 3`] =
208208
`;
209209

210210
exports[`Should generate seo information > Should contain basic properties 4`] = `
211+
[
212+
[
213+
"link",
214+
{
215+
"href": "https://exmaple.com/excerpt.html",
216+
"rel": "canonical",
217+
},
218+
],
219+
[
220+
"link",
221+
{
222+
"href": "https://exmaple.com/zh/excerpt.html",
223+
"hreflang": "zh-cn",
224+
"rel": "alternate",
225+
},
226+
],
227+
[
228+
"meta",
229+
{
230+
"content": "https://exmaple.com/excerpt.html",
231+
"property": "og:url",
232+
},
233+
],
234+
[
235+
"meta",
236+
{
237+
"content": "Page Excerpt",
238+
"property": "og:title",
239+
},
240+
],
241+
[
242+
"meta",
243+
{
244+
"content": "This is excerpt content.",
245+
"property": "og:description",
246+
},
247+
],
248+
[
249+
"meta",
250+
{
251+
"content": "article",
252+
"property": "og:type",
253+
},
254+
],
255+
[
256+
"meta",
257+
{
258+
"content": "en-US",
259+
"property": "og:locale",
260+
},
261+
],
262+
[
263+
"meta",
264+
{
265+
"content": "zh-CN",
266+
"property": "og:locale:alternate",
267+
},
268+
],
269+
[
270+
"script",
271+
{
272+
"type": "application/ld+json",
273+
},
274+
"{"@context":"https://schema.org","@type":"Article","headline":"Page Excerpt","image":[""],"dateModified":null,"author":[]}",
275+
],
276+
]
277+
`;
278+
279+
exports[`Should generate seo information > Should contain basic properties 5`] = `
211280
[
212281
[
213282
"link",
@@ -269,7 +338,7 @@ exports[`Should generate seo information > Should contain basic properties 4`] =
269338
]
270339
`;
271340

272-
exports[`Should generate seo information > Should contain basic properties 5`] = `
341+
exports[`Should generate seo information > Should contain basic properties 6`] = `
273342
[
274343
[
275344
"link",
@@ -338,7 +407,7 @@ exports[`Should generate seo information > Should contain basic properties 5`] =
338407
]
339408
`;
340409

341-
exports[`Should generate seo information > Should contain basic properties 6`] = `
410+
exports[`Should generate seo information > Should contain basic properties 7`] = `
342411
[
343412
[
344413
"link",
@@ -414,7 +483,76 @@ exports[`Should generate seo information > Should contain basic properties 6`] =
414483
]
415484
`;
416485

417-
exports[`Should generate seo information > Should contain basic properties 7`] = `
486+
exports[`Should generate seo information > Should contain basic properties 8`] = `
487+
[
488+
[
489+
"link",
490+
{
491+
"href": "https://exmaple.com/zh/excerpt.html",
492+
"rel": "canonical",
493+
},
494+
],
495+
[
496+
"link",
497+
{
498+
"href": "https://exmaple.com/excerpt.html",
499+
"hreflang": "en-us",
500+
"rel": "alternate",
501+
},
502+
],
503+
[
504+
"meta",
505+
{
506+
"content": "https://exmaple.com/zh/excerpt.html",
507+
"property": "og:url",
508+
},
509+
],
510+
[
511+
"meta",
512+
{
513+
"content": "页面摘要",
514+
"property": "og:title",
515+
},
516+
],
517+
[
518+
"meta",
519+
{
520+
"content": "这是页面摘要。",
521+
"property": "og:description",
522+
},
523+
],
524+
[
525+
"meta",
526+
{
527+
"content": "article",
528+
"property": "og:type",
529+
},
530+
],
531+
[
532+
"meta",
533+
{
534+
"content": "zh-CN",
535+
"property": "og:locale",
536+
},
537+
],
538+
[
539+
"meta",
540+
{
541+
"content": "en-US",
542+
"property": "og:locale:alternate",
543+
},
544+
],
545+
[
546+
"script",
547+
{
548+
"type": "application/ld+json",
549+
},
550+
"{"@context":"https://schema.org","@type":"Article","headline":"页面摘要","image":[""],"dateModified":null,"author":[]}",
551+
],
552+
]
553+
`;
554+
555+
exports[`Should generate seo information > Should contain basic properties 9`] = `
418556
[
419557
[
420558
"link",

plugins/plugin-seo/tests/node/description.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { describe, expect, it } from 'vitest'
33
import { createBaseApp } from 'vuepress/core'
44
import { path } from 'vuepress/utils'
5+
import { blogPlugin } from '../../../plugin-blog/src/node/blogPlugin.js'
56
import { seoPlugin } from '../../src/node/index.js'
67
import { emptyTheme } from '../__fixtures__/theme/empty.js'
78

@@ -18,6 +19,7 @@ const app = createBaseApp({
1819
},
1920
},
2021
plugins: [
22+
blogPlugin({}),
2123
seoPlugin({
2224
hostname: 'https://exmaple.com',
2325
canonical: 'https://exmaple.com',

tools/helper/src/node/page/excerpt.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,15 @@ const $ = load('')
196196
const isH1Tag = (node: AnyNode): boolean =>
197197
node.type === 'tag' && node.tagName === 'h1'
198198

199+
export const getPageExcerptContent = (
200+
content: string,
201+
separator = '<!-- more -->',
202+
): string | undefined =>
203+
matter(content, {
204+
excerpt: true,
205+
excerpt_separator: separator,
206+
}).excerpt
207+
199208
export const getPageExcerpt = (
200209
{ markdown, options: { base } }: App,
201210
{ content, contentRendered, filePath, filePathRelative, frontmatter }: Page,
@@ -208,10 +217,7 @@ export const getPageExcerpt = (
208217
}: PageExcerptOptions = {},
209218
): string => {
210219
// get page content
211-
const { excerpt } = matter(content, {
212-
excerpt: true,
213-
excerpt_separator: separator,
214-
})
220+
const excerpt = getPageExcerptContent(content, separator)
215221

216222
if (excerpt) {
217223
const renderedContent = markdown.render(

tools/helper/src/node/page/text.ts

Lines changed: 47 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -47,43 +47,6 @@ const REMOVED_TAGS = [
4747
'datalist',
4848
]
4949

50-
export interface PageTextOptions {
51-
/**
52-
* Whether convert text to single line content
53-
*
54-
* 是否将文字转换成单行内容
55-
*
56-
* @default false
57-
*/
58-
singleLine?: boolean
59-
60-
/**
61-
* Length of text
62-
*
63-
* @description Text length will be the minimal possible length reaching this value
64-
*
65-
* 文字的长度
66-
*
67-
* @description 文字的长度会尽可能的接近这个值
68-
*
69-
* @default 300
70-
*/
71-
length?: number
72-
73-
/**
74-
* Tags to be removed
75-
*
76-
* @description Table and code blocks are removed by default.
77-
*
78-
* 需要移除的标签
79-
*
80-
* @description 默认情况下表格和代码块会被移除
81-
*
82-
* @default ['table', 'pre']
83-
*/
84-
removedTags?: string[]
85-
}
86-
8750
interface NodeOptions {
8851
base: string
8952
removedTags: string[]
@@ -134,17 +97,54 @@ const handleNodes = (
13497

13598
const $ = load('')
13699

137-
export const getPageText = (
138-
{ options: { base } }: App,
139-
{ contentRendered }: Page,
100+
export interface PageTextOptions {
101+
/**
102+
* Whether convert text to single line content
103+
*
104+
* 是否将文字转换成单行内容
105+
*
106+
* @default false
107+
*/
108+
singleLine?: boolean
109+
110+
/**
111+
* Length of text
112+
*
113+
* @description Text length will be the minimal possible length reaching this value
114+
*
115+
* 文字的长度
116+
*
117+
* @description 文字的长度会尽可能的接近这个值
118+
*
119+
* @default 300
120+
*/
121+
length?: number
122+
123+
/**
124+
* Tags to be removed
125+
*
126+
* @description Table and code blocks are removed by default.
127+
*
128+
* 需要移除的标签
129+
*
130+
* @description 默认情况下表格和代码块会被移除
131+
*
132+
* @default ['table', 'pre']
133+
*/
134+
removedTags?: string[]
135+
}
136+
137+
export const getText = (
138+
content: string,
139+
base: string,
140140
{
141141
length = 300,
142142
singleLine,
143143
removedTags = ['table', 'pre'],
144144
}: PageTextOptions = {},
145145
): string => {
146146
let result = ''
147-
const rootNodes = $.parseHTML(contentRendered) ?? []
147+
const rootNodes = $.parseHTML(content) ?? []
148148

149149
for (const node of rootNodes) {
150150
const text = handleNode(node, { base, removedTags })
@@ -154,8 +154,13 @@ export const getPageText = (
154154
if (text.length >= length) break
155155
}
156156
}
157-
158157
return (
159158
singleLine ? result.replace(/\n/g, ' ').replace(/\s+/g, ' ') : result
160159
).trim()
161160
}
161+
162+
export const getPageText = (
163+
{ options: { base } }: App,
164+
{ contentRendered }: Page,
165+
options: PageTextOptions = {},
166+
): string => getText(contentRendered, base, options)

0 commit comments

Comments
 (0)