simonhaenisch / md-to-pdf

Hackable CLI tool for converting Markdown files to PDF using Node.js and headless Chrome.
https://www.npmjs.com/md-to-pdf
MIT License
1.16k stars 111 forks source link

help: Vercel Deployment Error - 'Could Not Find Chrome' #309

Closed aleksa-codes closed 3 weeks ago

aleksa-codes commented 4 weeks ago

Hi, I’m having an issue with deploying a library to Vercel. Locally, everything works fine, but after deployment, I get this error when hitting an endpoint that uses md-to-pdf:

Error: Could not find Chrome (ver. 114.0.5735.133). This can occur if either
1. You did not run `npm install`, or
2. Your cache path is incorrect: /home/sbx_user1051/.cache/puppeteer.
Check https://pptr.dev/guides/configuration for help.
...

It seems related to the "Chromium binary exceeded the 50mb serverless function size" issue: https://github.com/orgs/vercel/discussions/3074#discussioncomment-6863110

I’ve read that using a minified Chrome version like @sparticuz/chromium-min might help, sources: https://gist.github.com/hajek-raven/d05204c948f773bbeff311b681a79df8 https://agilxp.no/blogg/running-puppeteer-on-vercel

However, since I’m not directly using Puppeteer, I’m not sure if this is an option.

Any help would be greatly appreciated.

aleksa-codes commented 4 weeks ago

To provide an update and help others who want to use this library on Vercel:

Install @sparticuz/chromium instead of @sparticuz/chromium-min that I mentioned earlier:

yarn add @sparticuz/chromium

or

npm install @sparticuz/chromium

Just like we edit next.config.mjs to make md-to-pdf work, we need to add this to the next.config.mjs file:

const nextConfig = {
  ...
  experimental: {
    serverComponentsExternalPackages: ['md-to-pdf', '@sparticuz/chromium'],
  },
};

Next, the code logic should look like this: use the default launch_options in development and the @sparticuz/chromium options in production because @sparticuz/chromium usually doesn't work locally.

import chromium from '@sparticuz/chromium';
import { mdToPdf } from 'md-to-pdf';

const isProduction = process.env.NODE_ENV === 'production';

if (isProduction) {
  // Set graphics mode
  chromium.setGraphicsMode = false;

  // Load custom fonts
  // await chromium.font('https://raw.githack.com/googlei18n/noto-emoji/master/fonts/NotoColorEmoji.ttf');
}

const launchOptions = isProduction
  ? {
      args: chromium.args,
      defaultViewport: chromium.defaultViewport,
      executablePath: await chromium.executablePath(),
      headless: chromium.headless,
    }
  : {};

const pdf = await mdToPdf(
  { content },
  {
    stylesheet: [process.cwd() + '/node_modules/md-to-pdf/markdown.css'],
    // css: '*, html, body { font-family: inherit !important; }',
    launch_options: launchOptions,
  }
).catch((error) => {
  console.error('Error generating PDF:', error);
  throw new Error('Failed to generate PDF');
});

Two things I haven't been able to figure out:

Thanks also to @duhoang00 for the code from this GitHub issue, which helped me load the library's CSS.

simonhaenisch commented 3 weeks ago

Good that you figured out how to use a different Chromium binary. You can actually prevent Puppeteer from downloading Chromium by setting the env var PUPPETEER_SKIP_DOWNLOAD=true, see https://pptr.dev/api/puppeteer.configuration (it might be PUPPETEER_SKIP_CHROMIUM_DOWNLOAD in older Puppeteer versions).

You can also set the skipDownload option via a config file instead which seems to be recommended over config via env vars now, see https://pptr.dev/guides/configuration, for example:

// puppeteer.config.js

export default {
  skipDownload: true
}

Regarding your other questions: