Skip to content
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

[BUG] Unable use @vercel/og with OpenNext #133

Closed
gregavola opened this issue Nov 13, 2024 · 7 comments · Fixed by #283
Closed

[BUG] Unable use @vercel/og with OpenNext #133

gregavola opened this issue Nov 13, 2024 · 7 comments · Fixed by #283
Assignees
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@gregavola
Copy link

gregavola commented Nov 13, 2024

Describe the bug

When I try to add @vercel/og to the project, it compiles, but provides this as an error:

stack:

" [ERR_INVALID_ARG_TYPE]" argument must be of type string. Received undefined at validateString (node-internal:validators:101:15) at join (node-internal:internal_path:1004:13) at .worker-next/.next/standalone/node_modules/next/dist/compiled/@vercel/og/index.node.js (index.js:146973:54) at __init (index.js:12:59) at index.js:147033:52",
name:
 
"TypeError",
message:
 
"The "path" argument must be of type string. Received undefined",

I've also tried worker-og and I get:

 ⨯ ./node_modules/@cloudflare/pages-plugin-vercel-og/dist/src/api/noto-sans-v27-latin-regular.ttf.bin
Module parse failed: Unexpected character '' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)

Import trace for requested module:
./node_modules/@cloudflare/pages-plugin-vercel-og/dist/src/api/noto-sans-v27-latin-regular.ttf.bin
./node_modules/@cloudflare/pages-plugin-vercel-og/dist/src/api/index.js
./app/api/avatar/[uuid]/route.tsx
 ⨯ ./node_modules/@cloudflare/pages-plugin-vercel-og/dist/src/api/noto-sans-v27-latin-regular.ttf.bin
Module parse failed: Unexpected character '' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)

Even if I try to webAssembly:

const nextConfig = {
  webpack: (config) => {
    config.experiments = {
      asyncWebAssembly: true,
      syncWebAssembly: true,
      layers: true,
    };
    return config;
  },
};

I get: Attempted import error: './resvg-LFIOYO65.wasm' does not contain a default export (imported as 'fy').

Steps to reproduce

  1. Render an ImageResponse on the page
    browser

Expected behavior

I would expect the image to render on the apge.

@opennextjs/cloudflare version

0.2.1

Node.js version

18.x

Wrangler version

3.80

next info output

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.6.0: Wed Jul 31 20:49:39 PDT 2024; root:xnu-10063.141.1.700.5~1/RELEASE_ARM64_T6000
  Available memory (MB): 65536
  Available CPU cores: 10
Binaries:
  Node: 18.17.0
  npm: 9.6.7
  Yarn: 1.22.22
  pnpm: 8.11.0
Relevant Packages:
  next: 14.2.14 // An outdated version detected (latest is 15.0.3), upgrade is highly recommended!
  eslint-config-next: 14.2.14
  react: 18.3.1
  react-dom: 18.3.1
  typescript: 5.6.2

Additional context

No response

@gregavola gregavola added bug Something isn't working triage labels Nov 13, 2024
@kpyszkowski
Copy link

Hi @gregavola
Did you manage to fix it?

@gregavola
Copy link
Author

No I didn't sadly.

@kpyszkowski
Copy link

Prepared reproduction repo as requested: https://github.com/kpyszkowski/opennextjs-cloudflare-vercel-og-bug-repro

@vicb
Copy link
Contributor

vicb commented Jan 10, 2025

The error

[ERR_INVALID_ARG_TYPE]" argument must be of type string. Received undefined at validateString (node-internal:validators:101:15) at join (node-internal:internal_path:1004:13) at .worker-next/.next/standalone/node_modules/next/dist/compiled/@vercel/og/index.node.js (index.js:146973:54) at __init (index.js:12:59) at index.js:147033:52

is at

var fontData = fs.readFileSync(fileURLToPath(join(import.meta.url, "../noto-sans-v27-latin-regular.ttf")));
var yoga_wasm = fs.readFileSync(fileURLToPath(join(import.meta.url, "../yoga.wasm")));
var resvg_wasm = fs.readFileSync(fileURLToPath(join(import.meta.url, "../resvg.wasm")));

Because import.meta.url is undefined - but we should not be loading the node bundle in the first place.
I'll keep investigating.

@vicb
Copy link
Contributor

vicb commented Jan 10, 2025

Loading the edge bundle + another bit of hacking:

image

We'll see next week if it makes sense to use that bundle or if an API compatible bundle is better

@vicb vicb moved this from Todo to Help Wanted in opennext-cloudflare - aws "merge" Jan 13, 2025
@vicb
Copy link
Contributor

vicb commented Jan 13, 2025

I will not have time to work on this in the next few days so adding the steps to fix if someone has time to look at this before.

Use the edge version rather than the node version of og.

This should be done before bundling (after that the required file is imported).

We should replace import("next/dist/compiled/@vercel/og/index.node.js") with require("next/dist/compiled/@vercel/og/index.edge.js")

To avoid having to search all the source code, we can first look for next/dist/compiled/@vercel/og/index.node.js in the traced files (*.nft.json). Note that we should also copy the edge file alongside the node as it is not traced.

We can use ast-grep for that, i.e.

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\""

Patch the edge code

The edge code looks like:

var initializedResvg = initWasm(resvg_wasm);
var initializedYoga = initYoga(yoga_wasm).then((yoga2) => Rl(yoga2));
var fallbackFont = fetch(new URL("./noto-sans-v27-latin-regular.ttf", import.meta.url)).then((res) => res.arrayBuffer());
var ImageResponse = class extends Response {
  constructor(element, options = {}) {
    const result = new ReadableStream({
      async start(controller) {
        await initializedYoga;
        await initializedResvg;
        const fontData = await fallbackFont;

Using ast-grep, we should init the fallbackFont with something like:

var fallbackFont = getOgFallbackFont();

where getOgFallbackFont is a global function returning the content of noto-sans-v27-latin-regular.ttf - it is traced and present alongside the code.
getOgFallbackFont should not be generated (or at least tree-shaken) when not called.
We might need to rename noto-sans-v27-latin-regular.ttf to noto-sans-v27-latin-regular.ttf.bin so that we can import it without adding a rule in the wrangler config.

Add a test

Add a route in i.e. examples/api/app/api/og/route.tsx

import { ImageResponse } from "next/og";

export async function GET(request: Request) {
  try {
    const { searchParams } = new URL(request.url);

    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,
    });
  }
}

@vicb
Copy link
Contributor

vicb commented Jan 28, 2025

This was merged into main, thanks @james-elicx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Development

Successfully merging a pull request may close this issue.

4 participants