-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: auto generate opengraph images
- Loading branch information
Showing
43 changed files
with
1,486 additions
and
152 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
{ | ||
"name": "@acid-info/docusaurus-og", | ||
"version": "1.0.0-alpha.44", | ||
"description": "Docusaurus local search plugin", | ||
"main": "lib/index.js", | ||
"types": "src/plugin.d.ts", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/acid-info/logos-docusaurus-plugins.git", | ||
"directory": "packages/docusaurus-search-local" | ||
}, | ||
"license": "MIT", | ||
"scripts": { | ||
"build": "tsc --build", | ||
"watch": "tsc --build --watch", | ||
"prepublishOnly": "yarn build" | ||
}, | ||
"dependencies": { | ||
"@docusaurus/core": "^2.4.1", | ||
"@docusaurus/module-type-aliases": "^2.4.1", | ||
"@docusaurus/types": "^2.4.1", | ||
"@docusaurus/utils": "^2.4.1", | ||
"@docusaurus/utils-common": "^2.4.1", | ||
"@docusaurus/utils-validation": "^2.4.1", | ||
"@easyops-cn/docusaurus-search-local": "^0.33.6", | ||
"lodash": "^4.17.21" | ||
}, | ||
"engines": { | ||
"node": ">=16.14" | ||
}, | ||
"devDependencies": { | ||
"@types/lodash": "^4.14.186" | ||
}, | ||
"gitHead": "d2ee08c6c0678f78ad70f5d04183f7f781d11563" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import type { LoadContext, Plugin } from '@docusaurus/types' | ||
import { postBuildFactory } from './server/index' | ||
import { PluginOptions } from './server/types/plugin.types' | ||
export { imageRendererFactory } from './server/imageRenderer.factory' | ||
export * from './server/types' | ||
export type { PluginOptions } | ||
|
||
export default function logosTheme( | ||
context: LoadContext, | ||
options: PluginOptions, | ||
): Plugin<any> { | ||
return { | ||
name: 'docusaurus-og', | ||
|
||
async postBuild(props) { | ||
await postBuildFactory(options)(props) | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export type * from './index' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import { | ||
BlogContent, | ||
PluginOptions as BlogPluginOptions, | ||
} from '@docusaurus/plugin-content-blog' | ||
import { LoadedPlugin, Props } from '@docusaurus/types' | ||
import * as fs from 'fs' | ||
import * as path from 'path' | ||
import { Document } from './document' | ||
import { ImageGenerator } from './imageGenerator' | ||
import { BlogPageData } from './types/blog.types' | ||
import { ImageRenderer } from './types/image.types' | ||
import { PluginOptions } from './types/plugin.types' | ||
|
||
export class BlogPlugin { | ||
static plugin = 'docusaurus-plugin-content-blog' | ||
|
||
pages: BlogPageData[] = [] | ||
|
||
constructor( | ||
private context: Props, | ||
private options: PluginOptions, | ||
private imageGenerator: ImageGenerator, | ||
private imageRenderer: ImageRenderer, | ||
) {} | ||
|
||
process = async () => { | ||
await this.loadData() | ||
await this.generate() | ||
} | ||
|
||
loadData = async () => { | ||
const plugins = this.context.plugins.filter( | ||
(plugin) => plugin.name === BlogPlugin.plugin, | ||
) | ||
|
||
for (const plugin of plugins) { | ||
await this.loadInstance(plugin) | ||
} | ||
} | ||
|
||
loadInstance = async (plugin: LoadedPlugin) => { | ||
const content = plugin.content as BlogContent | ||
const options = plugin.options as BlogPluginOptions | ||
|
||
content.blogListPaginated.forEach((value) => { | ||
this.pages.push({ | ||
data: value, | ||
plugin: options, | ||
pageType: 'list', | ||
permalink: value.metadata.permalink, | ||
}) | ||
}) | ||
|
||
content.blogPosts.forEach((post) => { | ||
this.pages.push({ | ||
data: post, | ||
plugin: options, | ||
pageType: 'post', | ||
permalink: post.metadata.permalink, | ||
}) | ||
}) | ||
|
||
if (content.blogTagsListPath) { | ||
const filePath = this.getHtmlPath(content.blogTagsListPath) | ||
fs.existsSync(filePath) && | ||
this.pages.push({ | ||
pageType: 'tags', | ||
plugin: options, | ||
data: { | ||
permalink: content.blogTagsListPath, | ||
}, | ||
permalink: content.blogTagsListPath, | ||
}) | ||
} | ||
|
||
if (options.archiveBasePath) { | ||
this.pages.push({ | ||
plugin: options, | ||
pageType: 'archive', | ||
data: { permalink: options.archiveBasePath }, | ||
permalink: options.archiveBasePath, | ||
}) | ||
} | ||
|
||
{ | ||
Object.entries(content.blogTags).map(([key, value]) => { | ||
value.pages.forEach((page) => { | ||
this.pages.push({ | ||
pageType: 'tag', | ||
plugin: options, | ||
data: { ...page.metadata, label: value.label }, | ||
permalink: page.metadata.permalink, | ||
}) | ||
}) | ||
}) | ||
} | ||
} | ||
|
||
generate = async () => { | ||
for (const page of this.pages) { | ||
const image = await this.imageRenderer( | ||
{ | ||
...page, | ||
websiteOutDir: this.context.outDir, | ||
}, | ||
this.context, | ||
) | ||
|
||
if (!image) continue | ||
|
||
const generated = await this.imageGenerator.generate(...image) | ||
const document = new Document(this.getHtmlPath(page.permalink)) | ||
|
||
await document.load() | ||
if (!document.loaded) continue | ||
|
||
await document.setImage(generated.url) | ||
|
||
await document.write() | ||
} | ||
} | ||
|
||
getHtmlPath = (permalink: string) => | ||
path.join(this.context.outDir, permalink, 'index.html') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { | ||
LoadedContent, | ||
LoadedVersion, | ||
PluginOptions as DocsPluginOptions, | ||
} from '@docusaurus/plugin-content-docs' | ||
import { LoadedPlugin, Props } from '@docusaurus/types' | ||
import * as path from 'path' | ||
import { Document } from './document' | ||
import { ImageGenerator } from './imageGenerator' | ||
import { DocsPageData } from './types/docs.types' | ||
import { ImageRenderer } from './types/image.types' | ||
import { PluginOptions } from './types/plugin.types' | ||
|
||
export class DocsPlugin { | ||
static plugin = 'docusaurus-plugin-content-docs' | ||
|
||
docs: DocsPageData[] = [] | ||
|
||
constructor( | ||
private context: Props, | ||
private options: PluginOptions, | ||
private imageGenerator: ImageGenerator, | ||
private imageRenderer: ImageRenderer, | ||
) {} | ||
|
||
process = async () => { | ||
await this.loadData() | ||
await this.generate() | ||
} | ||
|
||
loadData = async () => { | ||
const { plugins = [] } = this.context | ||
|
||
const docPlugins = plugins.filter( | ||
(plugin) => plugin.name === DocsPlugin.plugin, | ||
) | ||
|
||
for (const plugin of docPlugins) { | ||
await this.loadInstance(plugin) | ||
} | ||
} | ||
|
||
loadInstance = async (plugin: LoadedPlugin) => { | ||
const content = plugin.content as LoadedContent | ||
const options = plugin.options as DocsPluginOptions | ||
|
||
const { loadedVersions } = content | ||
|
||
for (const version of loadedVersions) { | ||
await this.loadVersion(options, version) | ||
} | ||
} | ||
|
||
loadVersion = async (options: DocsPluginOptions, version: LoadedVersion) => { | ||
this.docs.push( | ||
...version.docs.map((doc) => ({ | ||
version, | ||
metadata: doc, | ||
plugin: options, | ||
})), | ||
) | ||
} | ||
|
||
generate = async () => { | ||
for (const doc of this.docs) { | ||
const image = await this.imageRenderer( | ||
{ | ||
...doc, | ||
websiteOutDir: this.context.outDir, | ||
}, | ||
this.context, | ||
) | ||
|
||
if (!image) continue | ||
|
||
const generated = await this.imageGenerator.generate(...image) | ||
const document = new Document(this.getHtmlPath(doc)) | ||
await document.load() | ||
await document.setImage(generated.url) | ||
|
||
await document.write() | ||
} | ||
} | ||
|
||
getHtmlPath = (doc: DocsPageData) => | ||
path.join(this.context.outDir, doc.metadata.permalink, 'index.html') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import * as fsp from 'fs/promises' | ||
import { HTMLElement, parse as parseHTML } from 'node-html-parser' | ||
|
||
export class Document { | ||
root: HTMLElement | ||
loaded = false | ||
|
||
constructor(private path: string) {} | ||
|
||
load = async () => { | ||
const htmlString = await fsp.readFile(this.path, 'utf-8') | ||
this.root = parseHTML(htmlString) | ||
this.loaded = true | ||
} | ||
|
||
write = async () => { | ||
await fsp.writeFile(this.path, Buffer.from(this.root.outerHTML)) | ||
} | ||
|
||
setImage = async (url: string) => { | ||
this.updateMeta('property', 'og:image', { | ||
content: url, | ||
}) | ||
this.updateMeta('property', 'image', { | ||
content: url, | ||
}) | ||
} | ||
|
||
get head() { | ||
return this.root.querySelector('head')! | ||
} | ||
|
||
private getMeta(attr: string, value: string) { | ||
const { head } = this | ||
|
||
let meta = head.querySelector(`meta[${attr}=${value}]`) | ||
|
||
if (!meta) { | ||
meta = new HTMLElement('meta', {}, '', null, [0, 0]) | ||
meta.setAttribute(attr, value) | ||
head.appendChild(meta) | ||
} | ||
|
||
return meta | ||
} | ||
|
||
private updateMeta = ( | ||
attr: string, | ||
value: string, | ||
attrs: Record<string, any>, | ||
) => { | ||
const el = this.getMeta(attr, value) | ||
Object.entries(attrs).forEach(([key, value]) => el.setAttribute(key, value)) | ||
|
||
return el | ||
} | ||
} |
Oops, something went wrong.