How to configure pdf_generator to use local installation of chrome-headless-render-pdf and puppeteer?

Hi all,

I managed to use pdf_generator with chrome-headless-render-pdf and puppeteer installed globally. Now I’d rather have both packages installed locally, however I couldn’t find a way to make it work.

I tried to install both packages within the /assets folder by running the npm install chrome-headless-render-pdf puppeteer command.

This did not work as pdf_generator expect chrome-headless-render-pdf to be in _build/dev/lib/pdf_generator/priv/node_modules/chrome-headless-render-pdf/dist/cli/chrome-headless-render-pdf.js

After reading the, and implementing the instructions for a local chrome install, I still get the same error.

Basically what I did is:

  • added {:pdf_generator, ">=0.6.0", compile: "make chrome"} to mix.exs
  • run make priv/node_modules which returns me this error make: *** No rule to make target 'priv/node_modules'. Stop.
  • run cd priv && npm install and rerun the above mentioned command, which still gets me the same make error.
  • run mix deps.get and mix phx.server which return me this error make: *** No rule to make target 'chrome'. Stop. ** (Mix) Could not compile dependency :pdf_generator, "make chrome" command failed. You can recompile this dependency with "mix deps.compile pdf_generator", update it with "mix deps.update pdf_generator" or clean it with "mix deps.clean pdf_generator" with none of the mentioned command working.

Any tipps?

I have GNU Make 4.1, nodejs v12.14.1, npm 6.13.4 and Elixir 1.10.0.

1 Like

The README explains relative to the pdf_generator sources.

Though I’m not sure how that would help you when using it as a dependency.

Perhaps it is enough to just add those npm packages to your asset pipeline, but I’m not sure…

Perhaps open a ticket at the repository and ask there.

1 Like

Thanks for the quick reply.
I opened up an issue here:

Let’s see.

Hi @ryanzidago. Thanks for opening the issue. I am the maintainer of the pfd_generator library: I will look into this.

Thanks a lot in advance for pointing out this bug. I just recently switched over to using make for managing the build process and I might have overlooked a thing. As you can see in the whole (new) make process isn’t yet being tested :frowning: , yet I have now a good opportunity to fix this.

Hey @gutschilla,

Thanks for the quick reply and taking a look into this issue!
I’m not sure if it’s a bug or me misunderstanding something.

I just realized that by priv you might mean creating a priv directory within the deps/pdf_generator folder. I tried with the priv folder.

Also, why can’t chrome-headless-render-pdf and puppeteer in the assets/node_modules be installed?

I’ve looked in details into the source code and realized that one can use the assets directory instead of the priv_dir like this in deps/pdf_generator/lib/pdf_generator.ex:

  @spec make_command(generator, opts, content, {html_path, pdf_path}) :: {path, list()}
  def make_command(:chrome, options, content, {html_path, pdf_path}) do
    chrome_executable  = PdfGenerator.PathAgent.get.chrome_path
    node_executable    = PdfGenerator.PathAgent.get.node_path
    disable_sandbox    = Application.get_env(:pdf_generator, :disable_chrome_sandbox) || options[:no_sandbox]

    # needs `make priv/node_modules` to be run when building
    # priv_dir = :code.priv_dir(:pdf_generator) |> to_string()

    path_to_assets_directory = Path.expand('assets')
    js_file = "#{path_to_assets_directory}/node_modules/chrome-headless-render-pdf/cli/chrome-headless-render-pdf.js"

Sofar, locally it worked for me. I’ll try to see how it goes on Heroku.

Thing with the <app>/assets directory is that this won’t get compiled into an erlang release using mix release. To get stuff in there you have to put it in the <app>/priv dir.

This is also what happens to your webpack builds (css and js). The assenbled/compiled result will end up in the /priv/static folder of your app. Take a look at _build/prod/lib/<your-app>_web/priv/static/ after running MIX_ENV=prod mix.compile && mix phx.digest - you’ll see js/app.js, csss/app.css and more.

The whole idea behind putting the chromium binary into priv as opposed to just assets is that the latter acts as the source for js/css compilation results. So it’s usually fine to have <app>/assets/node_modules in there as they will be compiled into priv/js/app.js after concatenation, tree-shaking, transpiling, scss-to-css conversion and whatnot.

But I need the binary as well.

And why all this?

I usually create releases with mix release and copy the resulting build from _build/prod/rel/<release-name>/ to the execution enviroinment, usually a docker container to be run in a Kubernetes cluster. Unfortunately/intendedly, stuff in <app>/assets won’t end up in there so on either has to install chromium there (the the CircleCI config) of package everything in the release (my idea).

But well, nothing stops you from just running MIX_ENV=prod iex -S mix phx.server on Heroku, or some VPS.

My current idea is to revert to a standard config and use Elixir’s release-assembling feature to copy the required binary over. Unfortunately this binary depends upon a gazillion of other thing ins node_modules which is why I ended up putting the whole node_modules dir there in the first place.

1 Like

…and meanwhile I fumble around with just using a wrapped-in-express.js and dockerized chromium container let Kubernetes handle the scaling.

Thanks again for taking the time to write a detailed answer.

What if I don’t use mix release?
Is there anything else preventing me or any other reason I should avoid to put the chromium binary in the assets directory?
Maybe we can implement an option where the user can chose either to save the packages to the priv or assets directory so that the user is not only dependent on global install of npm packages and can use local installation?

I’d be happy to open up a pull-request if there are no other reasons (besides mix release) to not have the chrome binary in /assets.