Skip to content

Commit

Permalink
feat(plugin-seo): use excerpt content as description if present
Browse files Browse the repository at this point in the history
  • Loading branch information
Mister-Hope committed Mar 18, 2024
1 parent dd1aa1d commit 0aa59c8
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 51 deletions.
11 changes: 9 additions & 2 deletions plugins/plugin-seo/src/node/generateDescription.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getPageText } from '@vuepress/helper'
import { getPageExcerptContent, getText } from '@vuepress/helper'
import type { App } from 'vuepress/core'
import type { ExtendPage } from '../typings/index.js'

Expand All @@ -9,7 +9,14 @@ export const generateDescription = (
): void => {
// generate description
if (!page.frontmatter.description && autoDescription) {
const pageText = getPageText(app, page, { length: 180, singleLine: true })
const content = getPageExcerptContent(page.content)
? page.data.excerpt ?? page.contentRendered
: page.contentRendered

const pageText = getText(content, app.options.base, {
length: 180,
singleLine: true,
})

if (pageText.length) {
page.frontmatter.description =
Expand Down
9 changes: 9 additions & 0 deletions plugins/plugin-seo/tests/__fixtures__/src/excerpt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Page Excerpt

This is **excerpt** content.

<!-- more -->

## Heading 2

Page content.
9 changes: 9 additions & 0 deletions plugins/plugin-seo/tests/__fixtures__/src/zh/excerpt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# 页面摘要

这是页面**摘要**

<!-- more -->

## 标题 2

页面内容。
144 changes: 141 additions & 3 deletions plugins/plugin-seo/tests/node/__snapshots__/description.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,75 @@ exports[`Should generate seo information > Should contain basic properties 3`] =
`;

exports[`Should generate seo information > Should contain basic properties 4`] = `
[
[
"link",
{
"href": "https://exmaple.com/excerpt.html",
"rel": "canonical",
},
],
[
"link",
{
"href": "https://exmaple.com/zh/excerpt.html",
"hreflang": "zh-cn",
"rel": "alternate",
},
],
[
"meta",
{
"content": "https://exmaple.com/excerpt.html",
"property": "og:url",
},
],
[
"meta",
{
"content": "Page Excerpt",
"property": "og:title",
},
],
[
"meta",
{
"content": "This is excerpt content.",
"property": "og:description",
},
],
[
"meta",
{
"content": "article",
"property": "og:type",
},
],
[
"meta",
{
"content": "en-US",
"property": "og:locale",
},
],
[
"meta",
{
"content": "zh-CN",
"property": "og:locale:alternate",
},
],
[
"script",
{
"type": "application/ld+json",
},
"{"@context":"https://schema.org","@type":"Article","headline":"Page Excerpt","image":[""],"dateModified":null,"author":[]}",
],
]
`;

exports[`Should generate seo information > Should contain basic properties 5`] = `
[
[
"link",
Expand Down Expand Up @@ -269,7 +338,7 @@ exports[`Should generate seo information > Should contain basic properties 4`] =
]
`;

exports[`Should generate seo information > Should contain basic properties 5`] = `
exports[`Should generate seo information > Should contain basic properties 6`] = `
[
[
"link",
Expand Down Expand Up @@ -338,7 +407,7 @@ exports[`Should generate seo information > Should contain basic properties 5`] =
]
`;

exports[`Should generate seo information > Should contain basic properties 6`] = `
exports[`Should generate seo information > Should contain basic properties 7`] = `
[
[
"link",
Expand Down Expand Up @@ -414,7 +483,76 @@ exports[`Should generate seo information > Should contain basic properties 6`] =
]
`;

exports[`Should generate seo information > Should contain basic properties 7`] = `
exports[`Should generate seo information > Should contain basic properties 8`] = `
[
[
"link",
{
"href": "https://exmaple.com/zh/excerpt.html",
"rel": "canonical",
},
],
[
"link",
{
"href": "https://exmaple.com/excerpt.html",
"hreflang": "en-us",
"rel": "alternate",
},
],
[
"meta",
{
"content": "https://exmaple.com/zh/excerpt.html",
"property": "og:url",
},
],
[
"meta",
{
"content": "页面摘要",
"property": "og:title",
},
],
[
"meta",
{
"content": "这是页面摘要。",
"property": "og:description",
},
],
[
"meta",
{
"content": "article",
"property": "og:type",
},
],
[
"meta",
{
"content": "zh-CN",
"property": "og:locale",
},
],
[
"meta",
{
"content": "en-US",
"property": "og:locale:alternate",
},
],
[
"script",
{
"type": "application/ld+json",
},
"{"@context":"https://schema.org","@type":"Article","headline":"页面摘要","image":[""],"dateModified":null,"author":[]}",
],
]
`;

exports[`Should generate seo information > Should contain basic properties 9`] = `
[
[
"link",
Expand Down
2 changes: 2 additions & 0 deletions plugins/plugin-seo/tests/node/description.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { describe, expect, it } from 'vitest'
import { createBaseApp } from 'vuepress/core'
import { path } from 'vuepress/utils'
import { blogPlugin } from '../../../plugin-blog/src/node/blogPlugin.js'
import { seoPlugin } from '../../src/node/index.js'
import { emptyTheme } from '../__fixtures__/theme/empty.js'

Expand All @@ -18,6 +19,7 @@ const app = createBaseApp({
},
},
plugins: [
blogPlugin({}),
seoPlugin({
hostname: 'https://exmaple.com',
canonical: 'https://exmaple.com',
Expand Down
14 changes: 10 additions & 4 deletions tools/helper/src/node/page/excerpt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,15 @@ const $ = load('')
const isH1Tag = (node: AnyNode): boolean =>
node.type === 'tag' && node.tagName === 'h1'

export const getPageExcerptContent = (
content: string,
separator = '<!-- more -->',
): string | undefined =>
matter(content, {
excerpt: true,
excerpt_separator: separator,
}).excerpt

export const getPageExcerpt = (
{ markdown, options: { base } }: App,
{ content, contentRendered, filePath, filePathRelative, frontmatter }: Page,
Expand All @@ -208,10 +217,7 @@ export const getPageExcerpt = (
}: PageExcerptOptions = {},
): string => {
// get page content
const { excerpt } = matter(content, {
excerpt: true,
excerpt_separator: separator,
})
const excerpt = getPageExcerptContent(content, separator)

if (excerpt) {
const renderedContent = markdown.render(
Expand Down
89 changes: 47 additions & 42 deletions tools/helper/src/node/page/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,43 +47,6 @@ const REMOVED_TAGS = [
'datalist',
]

export interface PageTextOptions {
/**
* Whether convert text to single line content
*
* 是否将文字转换成单行内容
*
* @default false
*/
singleLine?: boolean

/**
* Length of text
*
* @description Text length will be the minimal possible length reaching this value
*
* 文字的长度
*
* @description 文字的长度会尽可能的接近这个值
*
* @default 300
*/
length?: number

/**
* Tags to be removed
*
* @description Table and code blocks are removed by default.
*
* 需要移除的标签
*
* @description 默认情况下表格和代码块会被移除
*
* @default ['table', 'pre']
*/
removedTags?: string[]
}

interface NodeOptions {
base: string
removedTags: string[]
Expand Down Expand Up @@ -134,17 +97,54 @@ const handleNodes = (

const $ = load('')

export const getPageText = (
{ options: { base } }: App,
{ contentRendered }: Page,
export interface PageTextOptions {
/**
* Whether convert text to single line content
*
* 是否将文字转换成单行内容
*
* @default false
*/
singleLine?: boolean

/**
* Length of text
*
* @description Text length will be the minimal possible length reaching this value
*
* 文字的长度
*
* @description 文字的长度会尽可能的接近这个值
*
* @default 300
*/
length?: number

/**
* Tags to be removed
*
* @description Table and code blocks are removed by default.
*
* 需要移除的标签
*
* @description 默认情况下表格和代码块会被移除
*
* @default ['table', 'pre']
*/
removedTags?: string[]
}

export const getText = (
content: string,
base: string,
{
length = 300,
singleLine,
removedTags = ['table', 'pre'],
}: PageTextOptions = {},
): string => {
let result = ''
const rootNodes = $.parseHTML(contentRendered) ?? []
const rootNodes = $.parseHTML(content) ?? []

for (const node of rootNodes) {
const text = handleNode(node, { base, removedTags })
Expand All @@ -154,8 +154,13 @@ export const getPageText = (
if (text.length >= length) break
}
}

return (
singleLine ? result.replace(/\n/g, ' ').replace(/\s+/g, ' ') : result
).trim()
}

export const getPageText = (
{ options: { base } }: App,
{ contentRendered }: Page,
options: PageTextOptions = {},
): string => getText(contentRendered, base, options)

0 comments on commit 0aa59c8

Please sign in to comment.