|
1 | 1 | import { Buffer } from 'node:buffer' |
2 | | -import { existsSync } from 'node:fs' |
3 | | -import { fileURLToPath } from 'node:url' |
4 | 2 | import { decodeHTMLStrict } from 'entities' |
5 | 3 | import { getCanvasKit, fontManager, loadImage } from './assetLoaders' |
6 | 4 | import { SocialCardGenerationError } from './SocialCardGenerationError' |
@@ -35,8 +33,8 @@ export async function generateOpenGraphImage({ |
35 | 33 | bgGradient = defaultGradient, |
36 | 34 | avatarUrl, |
37 | 35 | }: SocialCardImageOptions): Promise<Buffer> { |
38 | | - const { titleFontPath, descriptionFontPath } = resolveFontPaths() |
39 | | - const fontMgr = await fontManager.get([titleFontPath, descriptionFontPath]) |
| 36 | + const { titleFontUrl, descriptionFontUrl } = resolveFontUrls(avatarUrl) |
| 37 | + const fontMgr = await fontManager.get([titleFontUrl, descriptionFontUrl]) |
40 | 38 | const avatarBuffer = await loadImage(avatarUrl) |
41 | 39 | const CanvasKit = await getCanvasKit() |
42 | 40 | const surface = CanvasKit.MakeSurface(width, height) |
@@ -185,40 +183,51 @@ const buildTextStyle = ( |
185 | 183 | heightMultiplier: style.lineHeight, |
186 | 184 | }) |
187 | 185 |
|
188 | | -const resolveFontPaths = (): { descriptionFontPath: string; titleFontPath: string } => ({ |
189 | | - titleFontPath: resolveLocalAssetPath( |
| 186 | +const resolveFontUrls = ( |
| 187 | + assetBaseUrl: string |
| 188 | +): { descriptionFontUrl: string; titleFontUrl: string } => ({ |
| 189 | + titleFontUrl: resolvePublicAssetUrl( |
190 | 190 | 'resolve-title-font', |
191 | | - [ |
192 | | - new URL('../../../../../public/fonts/Serif-Lora/Lora-Bold.ttf', import.meta.url), |
193 | | - new URL('../../../../../public/fonts/Lora-Bold.ttf', import.meta.url), |
194 | | - ], |
| 191 | + ['/fonts/Serif-Lora/Lora-Bold.ttf'], |
| 192 | + assetBaseUrl, |
195 | 193 | 'title font' |
196 | 194 | ), |
197 | | - descriptionFontPath: resolveLocalAssetPath( |
| 195 | + descriptionFontUrl: resolvePublicAssetUrl( |
198 | 196 | 'resolve-description-font', |
199 | | - [ |
200 | | - new URL('../../../../../public/fonts/Serif-Lora/Lora-Regular.ttf', import.meta.url), |
201 | | - new URL('../../../../../public/fonts/Lora-Regular.ttf', import.meta.url), |
202 | | - ], |
| 197 | + ['/fonts/Serif-Lora/Lora-Regular.ttf'], |
| 198 | + assetBaseUrl, |
203 | 199 | 'description font' |
204 | 200 | ), |
205 | 201 | }) |
206 | 202 |
|
207 | | -const resolveLocalAssetPath = ( |
| 203 | +const resolvePublicAssetUrl = ( |
208 | 204 | stage: 'resolve-description-font' | 'resolve-title-font', |
209 | | - candidateUrls: URL[], |
| 205 | + candidatePaths: string[], |
| 206 | + assetBaseUrl: string, |
210 | 207 | label: string |
211 | 208 | ): string => { |
212 | | - const candidatePaths = candidateUrls.map(candidateUrl => fileURLToPath(candidateUrl)) |
| 209 | + try { |
| 210 | + const baseUrl = new URL(assetBaseUrl) |
| 211 | + const candidateUrls = candidatePaths.map(candidatePath => new URL(candidatePath, baseUrl).toString()) |
| 212 | + const [resolvedUrl] = candidateUrls |
| 213 | + |
| 214 | + if (!resolvedUrl) { |
| 215 | + throw new SocialCardGenerationError(stage, `Social card ${label} could not be resolved`, { |
| 216 | + candidatePaths, |
| 217 | + label, |
| 218 | + }) |
| 219 | + } |
213 | 220 |
|
214 | | - for (const candidatePath of candidatePaths) { |
215 | | - if (existsSync(candidatePath)) { |
216 | | - return candidatePath |
| 221 | + return resolvedUrl |
| 222 | + } catch (cause) { |
| 223 | + if (cause instanceof SocialCardGenerationError) { |
| 224 | + throw cause |
217 | 225 | } |
218 | | - } |
219 | 226 |
|
220 | | - throw new SocialCardGenerationError(stage, `Social card ${label} could not be resolved`, { |
221 | | - candidatePaths, |
222 | | - label, |
223 | | - }) |
| 227 | + throw new SocialCardGenerationError(stage, `Social card ${label} could not be resolved`, { |
| 228 | + candidatePaths, |
| 229 | + label, |
| 230 | + assetBaseUrl, |
| 231 | + }, { cause }) |
| 232 | + } |
224 | 233 | } |
0 commit comments