mirror of
https://github.com/d0zingcat/nextjs-notion-starter-kit.git
synced 2026-05-18 23:16:44 +00:00
feat: remove puppeteer dependency
This commit is contained in:
@@ -1,129 +0,0 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import chromium from 'chrome-aws-lambda'
|
||||
import renderSocialImage from 'puppeteer-social-image-transitive-bs'
|
||||
import { getBlockTitle, parsePageId } from 'notion-utils'
|
||||
|
||||
import { mapNotionImageUrl } from '../../lib/map-image-url'
|
||||
// import { getPageDescription } from '../../lib/get-page-description'
|
||||
import { getPage } from '../../lib/notion'
|
||||
import * as types from '../../lib/types'
|
||||
import {
|
||||
socialImageTitle,
|
||||
// socialImageSubtitle,
|
||||
defaultPageCover,
|
||||
defaultPageIcon,
|
||||
rootNotionPageId,
|
||||
socialImageSubtitle
|
||||
} from '../../lib/config'
|
||||
|
||||
export interface SocialImageConfig {
|
||||
title: string
|
||||
subtitle?: string
|
||||
eyebrow?: string
|
||||
logo?: string
|
||||
imageUrl?: string
|
||||
unsplashId?: string
|
||||
unsplashKeywords?: string
|
||||
backgroundImageAnchor?: string
|
||||
backgroundImageOverlay?: boolean
|
||||
background?: string
|
||||
color?: string
|
||||
googleFont?: string
|
||||
fontFamily?: string
|
||||
watermark?: string
|
||||
size?:
|
||||
| 'facebook'
|
||||
| 'twitter'
|
||||
| 'ig-landscape'
|
||||
| 'ig-portrait'
|
||||
| 'ig-square'
|
||||
| 'ig-story'
|
||||
}
|
||||
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
if (req.method !== 'GET') {
|
||||
return res.status(405).send({ error: 'method not allowed' })
|
||||
}
|
||||
|
||||
const pageId = req.query.pageId as string
|
||||
|
||||
if (!pageId) {
|
||||
return res.status(400).send({ error: 'missing required parameter pageId' })
|
||||
}
|
||||
|
||||
let recordMap: types.ExtendedRecordMap
|
||||
let block: types.PageBlock
|
||||
|
||||
try {
|
||||
recordMap = await getPage(pageId)
|
||||
|
||||
const pageBlockId = Object.keys(recordMap.block)[0]
|
||||
block = recordMap.block[pageBlockId]?.value as types.PageBlock
|
||||
|
||||
if (!block) {
|
||||
return res.status(404).send({
|
||||
error: `unable to resolve root block for notion page "${pageId}"`
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ error: `unable to load notion page "${pageId}"` })
|
||||
}
|
||||
|
||||
const isRootPage = parsePageId(block.id) === parsePageId(rootNotionPageId)
|
||||
|
||||
const image = await createSocialImage({
|
||||
imageUrl: mapNotionImageUrl(
|
||||
block.format?.page_cover || defaultPageCover,
|
||||
block
|
||||
),
|
||||
logo: mapNotionImageUrl(defaultPageIcon, block),
|
||||
title: isRootPage
|
||||
? socialImageTitle
|
||||
: getBlockTitle(block, recordMap) || socialImageTitle,
|
||||
subtitle: isRootPage ? socialImageSubtitle : undefined
|
||||
// subtitle: getPageDescription(block, recordMap) || socialImageSubtitle
|
||||
})
|
||||
|
||||
res.setHeader(
|
||||
'Cache-Control',
|
||||
'public, immutable, s-maxage=31536000, max-age=31536000, stale-while-revalidate=60'
|
||||
)
|
||||
res.setHeader('Content-Type', 'image/jpeg')
|
||||
res.status(200).send(image)
|
||||
}
|
||||
|
||||
async function createSocialImage(params: SocialImageConfig) {
|
||||
let browser
|
||||
|
||||
try {
|
||||
// add font support for emojis
|
||||
// @see https://github.com/alixaxel/chrome-aws-lambda#fonts
|
||||
await chromium.font(
|
||||
'https://raw.githack.com/googlei18n/noto-emoji/master/fonts/NotoColorEmoji.ttf'
|
||||
)
|
||||
|
||||
browser = await chromium.puppeteer.launch({
|
||||
args: chromium.args,
|
||||
defaultViewport: chromium.defaultViewport,
|
||||
executablePath: await chromium.executablePath,
|
||||
headless: true, // chromium.headless,
|
||||
ignoreHTTPSErrors: true
|
||||
})
|
||||
|
||||
const res = await renderSocialImage({
|
||||
template: 'article',
|
||||
templateParams: params,
|
||||
templateStyles: `h1 { font-size: 96px; text-align: center; } h2 { margin-top: 48px; font-size: 48px; text-align: center; }`,
|
||||
size: params.size,
|
||||
browser
|
||||
})
|
||||
|
||||
return res
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,11 +126,10 @@ export const NotionPage: React.FC<types.PageProps> = ({
|
||||
const showTableOfContents = !!isBlogPost
|
||||
const minTableOfContentsItems = 3
|
||||
|
||||
const socialImage =
|
||||
mapNotionImageUrl(
|
||||
(block as PageBlock).format?.page_cover || config.defaultPageCover,
|
||||
block
|
||||
) || config.api.renderSocialImage(pageId)
|
||||
const socialImage = mapNotionImageUrl(
|
||||
(block as PageBlock).format?.page_cover || config.defaultPageCover,
|
||||
block
|
||||
)
|
||||
|
||||
const socialDescription =
|
||||
getPageDescription(block, recordMap) ?? config.description
|
||||
|
||||
@@ -106,8 +106,7 @@ export const apiBaseUrl = `${host}/api`
|
||||
|
||||
export const api = {
|
||||
createPreviewImage: `${apiBaseUrl}/create-preview-image`,
|
||||
searchNotion: `${apiBaseUrl}/search-notion`,
|
||||
renderSocialImage: (pageId) => `${apiBaseUrl}/render-social-image/${pageId}`
|
||||
searchNotion: `${apiBaseUrl}/search-notion`
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -41,8 +41,6 @@
|
||||
"notion-utils": "^4.4.0",
|
||||
"p-map": "^4.0.0",
|
||||
"p-memoize": "^4.0.0",
|
||||
"puppeteer-core": "^5.5.0",
|
||||
"puppeteer-social-image-transitive-bs": "^0.8.2",
|
||||
"react": "^17.0.2",
|
||||
"react-body-classname": "^1.3.1",
|
||||
"react-dom": "^17.0.2",
|
||||
@@ -74,7 +72,6 @@
|
||||
"eslint-plugin-react": "^7.23.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.0.5",
|
||||
"puppeteer": "^5.5.0",
|
||||
"typescript": "^4.1.3"
|
||||
}
|
||||
}
|
||||
|
||||
11
readme.md
11
readme.md
@@ -143,17 +143,6 @@ The actual work happens in the [create-preview-image](./api/create-preview-image
|
||||
|
||||
Open Graph images like this one will be generated for each page of your site automatically based each page's content.
|
||||
|
||||
By default, it takes into account:
|
||||
|
||||
- cover image (falling back to a default site-wide cover image)
|
||||
- page icon (falling back to a default site-wide icon)
|
||||
- page title
|
||||
- page subtitle (optional; pulled from the "Description" property of collection pages)
|
||||
|
||||
This feature works by rendering some custom HTML to a [Puppeteer](https://pptr.dev) instance in this [serverless function](./api/render-social-image/[pageId].ts) that takes in the page ID as input.
|
||||
|
||||
Here's an example of a social image URL in production: [/api/render-social-image/71201624b204481f862630ea25ce62fe](https://transitivebullsh.it/api/render-social-image/71201624b204481f862630ea25ce62fe)
|
||||
|
||||
Note that you shouldn't have to do anything extra to enable this feature as long as you're deploying to Vercel.
|
||||
|
||||
### Automatic Table of Contents
|
||||
|
||||
Reference in New Issue
Block a user