Skip to content

Commit

Permalink
Updated for VuePress v2.0.0-beta.26
Browse files Browse the repository at this point in the history
  • Loading branch information
amerkay committed Oct 31, 2021
1 parent a2833e1 commit 0f74899
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 96 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.code-workspace
241 changes: 148 additions & 93 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,99 +1,154 @@
/**
* Frontmatter looks like
ogType: website # default is 'article'
ogTitle: OG/TWITTER TITLE HERE
title: PAGE TITLE HERE
description: A description of your page here.
date: 2021-10-30T04:20:42.00Z
image: /images/og_image_homepage.jpg
*/

module.exports = (options = {}, context) => {
options = Object.assign(defaultOptions, options)

return {
extendPageData ($page) {
const $site = context.siteConfig

// In Vuepress core, permalinks are built after enhancers.
const pageClone = Object.assign(Object.create(Object.getPrototypeOf($page)), $page)
pageClone.buildPermalink()

let meta = $page.frontmatter.meta || []
let addMeta = getAddMeta(meta)
let optionArgs = [ $page, $site, pageClone.path ]
let metaContext = {
$page, $site,
siteTitle: options.siteTitle(...optionArgs),
title: options.title(...optionArgs),
description: options.description(...optionArgs),
author: options.author(...optionArgs),
tags: options.tags(...optionArgs),
twitterCard: options.twitterCard(...optionArgs),
type: options.type(...optionArgs),
url: options.url(...optionArgs),
image: options.image(...optionArgs),
publishedAt: options.publishedAt(...optionArgs),
modifiedAt: options.modifiedAt(...optionArgs),
}

options.defaultMeta(addMeta, metaContext)
options.customMeta(addMeta, metaContext)
$page.frontmatter.meta = meta
}
}
}
options = Object.assign(defaultOptions, options);

function getAddMeta(meta) {
return (name, content, attribute = null) => {
if (! content) return
if (! attribute) attribute = ['article:', 'og:'].some(type => name.startsWith(type)) ? 'property' : 'name'
meta.push({ [attribute]: name, content })
}
return {
name: "vuepress-seo-plugin",

extendsPageData: ($page) => {
let $site = { ...context.siteData, ...context.options };
let head = $page.frontmatter.head || [];
let addMeta = getAddMeta(head);
let optionArgs = [$page, $site, $page.path];

// add cannonical
addCanonical(head, $page, $site);

let metaContext = {
$page,
$site,
siteTitle: options.siteTitle(...optionArgs),
title: options.title(...optionArgs),
description: options.description(...optionArgs),
author: options.author(...optionArgs),
tags: options.tags(...optionArgs),
twitterCard: options.twitterCard(...optionArgs),
type: options.type(...optionArgs),
url: options.url(...optionArgs),
image: options.image(...optionArgs),
publishedAt: options.publishedAt(...optionArgs),
modifiedAt: options.modifiedAt(...optionArgs),
};

options.defaultMeta(addMeta, metaContext);
options.customMeta(addMeta, metaContext);
$page.frontmatter.head = head;
},
};
};

function getAddMeta(head) {
return (name, content, attribute = null) => {
// check if meta tag already exists
const isExists =
head.findIndex((item) =>
// if any of hid, name or property fields are same as 'name'
[item[1].hid, item[1].name, item[1].property].includes(name)
) != -1;
// skip if it does
if (isExists) return;

if (!content) return;
if (!attribute)
attribute = ["article:", "og:"].some((type) => name.startsWith(type))
? "property"
: "name";

// push it to 'head' array
head.push([
"head",
{
hid: name,
[attribute]: name,
content: content,
},
]);
};
}

const defaultOptions = {
siteTitle: (_, $site) => $site.title,
title: $page => $page.title,
description: $page => $page.frontmatter.description,
author: (_, $site) => $site.themeConfig.author,
tags: $page => $page.frontmatter.tags,
twitterCard: _ => 'summary_large_image',
type: $page => ['articles', 'posts', 'blog'].some(folder => $page.regularPath.startsWith('/' + folder)) ? 'article' : 'website',
url: (_, $site, path) => ($site.themeConfig.domain || '') + path,
image: ($page, $site) => {
if ($page.frontmatter.image) {
return $site.themeConfig.domain &&
!$page.frontmatter.image.startsWith('http')
? $site.themeConfig.domain + $page.frontmatter.image
: $page.frontmatter.image;
}
return '';
// add cannonical tag
function addCanonical(head, $page, $site) {
// check if canonical tag exists already
let isExists =
head.findIndex((item) => {
return item[1].rel == "canonical";
}) != -1;

// if it does not, auto generate it
if (!isExists)
head.push([
"link",
{
rel: "canonical",
href: ($site.themeConfig.domain || "") + $page.path,
},
publishedAt: $page => $page.frontmatter.date && (new Date($page.frontmatter.date)).toISOString(),
modifiedAt: $page => $page.lastUpdated && (new Date($page.lastUpdated)).toISOString(),
customMeta: () => {},

defaultMeta(add, ctx) {
const { author, tags } = ctx

// Basics.
add('article:published_time', ctx.publishedAt)
add('article:modified_time', ctx.modifiedAt)
add('og:site_name', ctx.siteTitle)
add('og:title', ctx.title)
add('og:description', ctx.description)
add('og:type', ctx.type)
add('og:url', ctx.url)
add('og:image', ctx.image)
add('twitter:title', ctx.title)
add('twitter:description', ctx.description)
add('twitter:url', ctx.url)
add('twitter:card', ctx.twitterCard)
add('twitter:image', ctx.image)

// Author.
if (author) {
add('twitter:label1', 'Written by')
add('twitter:data1', author.name)
add('twitter:creator', author.twitter)
}

// Tags.
if (tags && Array.isArray(tags)) {
add('twitter:label2', 'Filed under')
add('twitter:data2', tags.join(', '))
tags.forEach(tag => add('article:tag', tag))
}
},
]);
}

const defaultOptions = {
siteTitle: (_, $site) => $site.title,
title: ($page) =>
$page.frontmatter.ogTitle || $page.frontmatter.title || $page.title,
description: ($page) => $page.frontmatter.description,
author: ($page) => $page.frontmatter.author,
tags: ($page) => $page.frontmatter.tags,
twitterCard: (_) => "summary_large_image",
type: ($page) => $page.frontmatter.ogType || "article",
url: (_, $site, path) => ($site.themeConfig.domain || "") + path,
image: ($page, $site) => {
if ($page.frontmatter.image) {
return $site.themeConfig.domain &&
!$page.frontmatter.image.startsWith("http")
? $site.themeConfig.domain + $page.frontmatter.image
: $page.frontmatter.image;
}
return "";
},
publishedAt: ($page) =>
$page.frontmatter.date && new Date($page.frontmatter.date).toISOString(),
modifiedAt: ($page) =>
$page.lastUpdated && new Date($page.lastUpdated).toISOString(),
customMeta: () => {},

defaultMeta(add, ctx) {
const { author, tags } = ctx;

// Basics.
add("article:published_time", ctx.publishedAt);
add("article:modified_time", ctx.modifiedAt);
add("og:site_name", ctx.siteTitle);
add("og:title", ctx.title);
add("og:description", ctx.description);
add("og:type", ctx.type);
add("og:url", ctx.url);
add("og:image", ctx.image);
add("twitter:title", ctx.title);
add("twitter:description", ctx.description);
add("twitter:url", ctx.url);
add("twitter:card", ctx.twitterCard);
add("twitter:image", ctx.image);

// Author.
if (author) {
add("twitter:label1", "Written by");
add("twitter:data1", author.name);
add("twitter:creator", author.twitter);
}

// Tags.
if (tags && Array.isArray(tags)) {
add("twitter:label2", "Filed under");
add("twitter:data2", tags.join(", "));
tags.forEach((tag) => add("article:tag", tag));
}
},
};
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
{
"name": "vuepress-plugin-seo",
"version": "0.1.4",
"description": "Generate SEO friendly meta header for every page",
"version": "0.2.0",
"description": "Generate SEO friendly meta header for every page, VuePress v2",
"main": "index.js",
"keywords": [
"vuepress",
"plugin",
"seo"
"seo",
"meta",
"headers"
],
"repository": {
"type": "git",
Expand Down

0 comments on commit 0f74899

Please sign in to comment.