-
Notifications
You must be signed in to change notification settings - Fork 73
fix: patch @vercel/og usage to use the edge runtime version #283
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
31b0f9b
55f371b
32fcf6f
e5cdea4
89cd3a5
3c94150
28a043f
110fa56
972b4d7
58e3378
e286429
c30c619
7478813
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
"@opennextjs/cloudflare": patch | ||
--- | ||
|
||
fix: @vercel/og failing due to using the node version. | ||
|
||
Patches usage of the @vercel/og library to require the edge runtime version, and enables importing of the fallback font. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,6 @@ on: | |
push: | ||
branches: [main, experimental] | ||
pull_request: | ||
branches: [main, experimental] | ||
|
||
jobs: | ||
checks: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,6 @@ on: | |
push: | ||
branches: [main] | ||
pull_request: | ||
branches: [main] | ||
|
||
jobs: | ||
test: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,6 @@ on: | |
push: | ||
branches: [main, experimental] | ||
pull_request: | ||
branches: [main, experimental] | ||
|
||
jobs: | ||
release: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { ImageResponse } from "next/og"; | ||
|
||
export const dynamic = "force-dynamic"; | ||
|
||
export async function GET() { | ||
try { | ||
return new ImageResponse( | ||
( | ||
<div | ||
style={{ | ||
backgroundColor: "black", | ||
backgroundSize: "150px 150px", | ||
height: "100%", | ||
width: "100%", | ||
display: "flex", | ||
textAlign: "center", | ||
alignItems: "center", | ||
justifyContent: "center", | ||
flexDirection: "column", | ||
flexWrap: "nowrap", | ||
}} | ||
> | ||
<div | ||
style={{ | ||
display: "flex", | ||
alignItems: "center", | ||
justifyContent: "center", | ||
justifyItems: "center", | ||
}} | ||
> | ||
<img | ||
alt="Vercel" | ||
height={200} | ||
src="data:image/svg+xml,%3Csvg width='116' height='100' fill='white' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M57.5 0L115 100H0L57.5 0z' /%3E%3C/svg%3E" | ||
style={{ margin: "0 30px" }} | ||
width={232} | ||
/> | ||
</div> | ||
<div | ||
style={{ | ||
fontSize: 60, | ||
fontStyle: "normal", | ||
letterSpacing: "-0.025em", | ||
color: "white", | ||
marginTop: 30, | ||
padding: "0 120px", | ||
lineHeight: 1.4, | ||
whiteSpace: "pre-wrap", | ||
}} | ||
> | ||
'next/og' | ||
</div> | ||
</div> | ||
), | ||
{ | ||
width: 1200, | ||
height: 630, | ||
} | ||
); | ||
} catch (e: any) { | ||
return new Response("Failed to generate the image", { | ||
status: 500, | ||
}); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,7 +33,8 @@ export async function bundleServer(buildOpts: BuildOptions): Promise<void> { | |
console.log(`\x1b[35m⚙️ Bundling the OpenNext server...\n\x1b[0m`); | ||
|
||
patches.patchWranglerDeps(buildOpts); | ||
patches.updateWebpackChunksFile(buildOpts); | ||
await patches.updateWebpackChunksFile(buildOpts); | ||
await patches.patchVercelOgLibrary(buildOpts); | ||
|
||
|
||
const outputPath = path.join(outputDir, "server-functions", "default"); | ||
const packagePath = getPackagePath(buildOpts); | ||
|
@@ -176,7 +177,7 @@ async function updateWorkerBundledCode(workerOutputFile: string, buildOpts: Buil | |
|
||
const bundle = parse(Lang.TypeScript, patchedCode).root(); | ||
|
||
const edits = patchOptionalDependencies(bundle); | ||
const { edits } = patchOptionalDependencies(bundle); | ||
|
||
await writeFile(workerOutputFile, bundle.commitEdits(edits)); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { describe, expect, it } from "vitest"; | ||
|
||
import { patchCode } from "./util"; | ||
import { vercelOgFallbackFontRule, vercelOgImportRule } from "./vercel-og"; | ||
|
||
describe("vercelOgImportRule", () => { | ||
it("should rewrite a node import to an edge import", () => { | ||
const code = `e.exports=import("next/dist/compiled/@vercel/og/index.node.js")`; | ||
expect(patchCode(code, vercelOgImportRule)).toMatchInlineSnapshot( | ||
`"e.exports=import("next/dist/compiled/@vercel/og/index.edge.js")"` | ||
); | ||
}); | ||
}); | ||
|
||
describe("vercelOgFallbackFontRule", () => { | ||
it("should replace a fetch call for a font with an import", () => { | ||
const code = `var fallbackFont = fetch(new URL("./noto-sans-v27-latin-regular.ttf", import.meta.url)).then((res) => res.arrayBuffer());`; | ||
expect(patchCode(code, vercelOgFallbackFontRule)).toMatchInlineSnapshot(` | ||
"async function getFallbackFont() { | ||
return (await import("./noto-sans-v27-latin-regular.ttf.bin")).default | ||
} | ||
|
||
var fallbackFont = getFallbackFont()" | ||
`); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { SgNode } from "@ast-grep/napi"; | ||
|
||
import { applyRule } from "./util.js"; | ||
|
||
export const vercelOgImportRule = ` | ||
rule: | ||
pattern: $NODE | ||
kind: string | ||
regex: "next/dist/compiled/@vercel/og/index\\\\.node\\\\.js" | ||
inside: | ||
kind: arguments | ||
inside: | ||
kind: call_expression | ||
stopBy: end | ||
has: | ||
field: function | ||
regex: "import" | ||
fix: |- | ||
"next/dist/compiled/@vercel/og/index.edge.js" | ||
`; | ||
|
||
/** | ||
* Patches Node.js imports for the library to be Edge imports. | ||
* | ||
* @param root Root node. | ||
* @returns Results of applying the rule. | ||
*/ | ||
export function patchVercelOgImport(root: SgNode) { | ||
return applyRule(vercelOgImportRule, root); | ||
} | ||
|
||
export const vercelOgFallbackFontRule = ` | ||
rule: | ||
kind: variable_declaration | ||
all: | ||
- has: | ||
kind: variable_declarator | ||
has: | ||
kind: identifier | ||
regex: ^fallbackFont$ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: maybe it would be safer to detect the |
||
- has: | ||
kind: call_expression | ||
pattern: fetch(new URL("$PATH", $$$REST)) | ||
stopBy: end | ||
fix: |- | ||
async function getFallbackFont() { | ||
return (await import("$PATH.bin")).default | ||
james-elicx marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
james-elicx marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
} | ||
var fallbackFont = getFallbackFont() | ||
`; | ||
|
||
/** | ||
* Patches the default font fetching to use a .bin import. | ||
* | ||
* @param root Root node. | ||
* @returns Results of applying the rule. | ||
*/ | ||
export function patchVercelOgFallbackFont(root: SgNode) { | ||
return applyRule(vercelOgFallbackFontRule, root); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
export * from "./copy-package-cli-files.js"; | ||
export * from "./patch-cache.js"; | ||
export * from "./patch-require.js"; | ||
export * from "./patch-vercel-og-library.js"; | ||
export * from "./update-webpack-chunks-file/index.js"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; | ||
import path from "node:path"; | ||
|
||
import { BuildOptions } from "@opennextjs/aws/build/helper.js"; | ||
import mockFs from "mock-fs"; | ||
import { afterAll, beforeAll, describe, expect, it } from "vitest"; | ||
|
||
import { patchVercelOgLibrary } from "./patch-vercel-og-library"; | ||
|
||
const nodeModulesVercelOgDir = "node_modules/.pnpm/[email protected]/node_modules/next/dist/compiled/@vercel/og"; | ||
const nextServerOgNftPath = "examples/api/.next/server/app/og/route.js.nft.json"; | ||
const openNextFunctionDir = "examples/api/.open-next/server-functions/default/examples/api"; | ||
const openNextOgRoutePath = path.join(openNextFunctionDir, ".next/server/app/og/route.js"); | ||
const openNextVercelOgDir = path.join(openNextFunctionDir, "node_modules/next/dist/compiled/@vercel/og"); | ||
|
||
const buildOpts = { | ||
appBuildOutputPath: "examples/api", | ||
monorepoRoot: "", | ||
outputDir: "examples/api/.open-next", | ||
} as BuildOptions; | ||
|
||
describe("patchVercelOgLibrary", () => { | ||
beforeAll(() => { | ||
mockFs(); | ||
|
||
mkdirSync(nodeModulesVercelOgDir, { recursive: true }); | ||
mkdirSync(path.dirname(nextServerOgNftPath), { recursive: true }); | ||
mkdirSync(path.dirname(openNextOgRoutePath), { recursive: true }); | ||
mkdirSync(openNextVercelOgDir, { recursive: true }); | ||
|
||
writeFileSync( | ||
nextServerOgNftPath, | ||
JSON.stringify({ version: 1, files: [`../../../../../../${nodeModulesVercelOgDir}/index.node.js`] }) | ||
); | ||
writeFileSync( | ||
path.join(nodeModulesVercelOgDir, "index.edge.js"), | ||
`var fallbackFont = fetch(new URL("./noto-sans-v27-latin-regular.ttf", import.meta.url)).then((res) => res.arrayBuffer());` | ||
); | ||
writeFileSync(openNextOgRoutePath, `e.exports=import("next/dist/compiled/@vercel/og/index.node.js")`); | ||
writeFileSync(path.join(openNextVercelOgDir, "index.node.js"), ""); | ||
writeFileSync(path.join(openNextVercelOgDir, "noto-sans-v27-latin-regular.ttf"), ""); | ||
}); | ||
|
||
afterAll(() => mockFs.restore()); | ||
|
||
it("should patch the open-next files correctly", () => { | ||
patchVercelOgLibrary(buildOpts); | ||
|
||
expect(readdirSync(openNextVercelOgDir)).toMatchInlineSnapshot(` | ||
[ | ||
"index.edge.js", | ||
"index.node.js", | ||
"noto-sans-v27-latin-regular.ttf.bin", | ||
] | ||
`); | ||
|
||
expect(readFileSync(path.join(openNextVercelOgDir, "index.edge.js"), { encoding: "utf-8" })) | ||
.toMatchInlineSnapshot(` | ||
"async function getFallbackFont() { | ||
return (await import("./noto-sans-v27-latin-regular.ttf.bin")).default | ||
} | ||
|
||
var fallbackFont = getFallbackFont()" | ||
`); | ||
|
||
expect(readFileSync(openNextOgRoutePath, { encoding: "utf-8" })).toMatchInlineSnapshot( | ||
`"e.exports=import("next/dist/compiled/@vercel/og/index.edge.js")"` | ||
); | ||
}); | ||
}); |
Uh oh!
There was an error while loading. Please reload this page.