Studiosity / grover

A Ruby gem to transform HTML into PDFs, PNGs or JPEGs using Google Puppeteer/Chromium
MIT License
945 stars 109 forks source link

SyntaxError: Unexpected token '?' #220

Closed sbounmy closed 10 months ago

sbounmy commented 10 months ago
Grover.new("https://google.com").to_pdf

raises SyntaxError: Unexpected token '?'

Backtrace

[grover (1.1.6) lib/grover/processor.rb:54:in `parse_package_error'](http://localhost:3000/hq.pdf#)
[grover (1.1.6) lib/grover/processor.rb:49:in `ensure_packages_are_initiated'](http://localhost:3000/hq.pdf#)
[grover (1.1.6) lib/grover/processor.rb:19:in `convert'](http://localhost:3000/hq.pdf#)
[grover (1.1.6) lib/grover.rb:51:in `to_pdf'](http://localhost:3000/hq.pdf#)

Node version 20.11.0

Ruby 3.2.2

puppeteer@21.7.0

abrom commented 10 months ago

I am unable to replicate the error you reported with the versions you indicated and the test you suggested.

Although the stack trace would suggest that the issue is with your puppeteer installation. I would suggest deleting/renaming the node_modules folder and trying to re-install. Something isn't right.

You would be able to confirm this by trying to run node and require puppeteer manually:

$ node
Welcome to Node.js v20.11.0.
> require('puppeteer')
......

If you get back an object then puppeteer loaded ok.. if you get the error you mentioned, your puppeteer install is broken, and it will likely display a different stack trace that might explain the issue.

sbounmy commented 10 months ago

thanks for getting back to me so quickly @abrom

I forgot to mention I am using a docker image (ruby:3.2.2-bullseye)

# uname -a
Linux beab81388613 6.4.16-linuxkit #1 SMP PREEMPT Fri Nov 10 14:49:23 UTC 2023 aarch64 GNU/Linux

puppeteer seems to load fine

# node
Welcome to Node.js v20.11.0.
Type ".help" for more information.
> require('puppeteer')
{
  connect: [Function: bound bound connect],
  defaultArgs: [Function: bound defaultArgs],
  executablePath: [Function: bound executablePath],
  launch: [Function: bound launch],
  trimCache: [Function: bound trimCache] AsyncFunction,
    ...
    launch: [Function: bound launch],
    executablePath: [Function: bound executablePath],
    defaultArgs: [Function: bound defaultArgs],
    trimCache: [Function: bound trimCache] AsyncFunction
  }
}
abrom commented 10 months ago

Interesting.. doesn't necessarily help us get to the bottom of this.. As I mentioned, I'm unable to replicate so it's going to need some leg work from you to dig into this..

With the error that is being raised the issue has to be either something to do with the Node install.. or with the node packages install (puppeteer or its dependencies). Did you try nuking the node_modules folder as suggested above?

Have you tried installing a different (older) version of puppeteer? Just for testings sake, try go for something really old (say v11.0.0 circa 2021) to try rule out any recent shenanigans. If that works, try split the difference in versions until you figure out which version broke things (that's assuming that it's a puppeteer version issue).

To double check you haven't got some sort of ENV NodeJS version issue going on, try check the node version from your Ruby terminal: system 'node --version'.

If that works as expected, try load puppeteer inline: system 'node -e "require(\'puppeteer1\')"'

sbounmy commented 10 months ago

Interesting. In Rails console

CleanShot 2024-01-22 at 15 26 10

But on the Rails web console I have...

14:19:21 web.1  | v12.22.12

CleanShot 2024-01-22 at 15 23 39

Maybe something related tonvm checking it out...

sbounmy commented 10 months ago

It was caused by my Dockerfile not properly setting up nvm vs bash

RUN mkdir /usr/local/nvm
ENV NVM_DIR /usr/local/nvm
ENV NODE_VERSION 20.11.0
RUN curl https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash \
    && . $NVM_DIR/nvm.sh \
    && nvm install $NODE_VERSION \
    && nvm alias default $NODE_VERSION \
    && nvm use default

ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH

Now having the right version but running into Failed to launch the browser process! qemu-x86_64: Could not open '/lib64/ld-linux-x86-64.so.2': No such file or directory

abrom commented 10 months ago

Sounds like maybe you're trying to run your docker container on an ARM processor? If so, try add --platform=linux/amd64 to the Docker FROM statement

sbounmy commented 10 months ago

Yes but after that I had initialize': Function not implemented - Failed to initialize inotify (Errno::ENOSYS) that couldnt be solved.

I ended up installing chromium.

Here's my code in case anyone needs it

#Dockerfile
FROM ruby:3.2.2-bullseye
RUN apt-get update -qq && apt-get install -y --fix-missing \
    postgresql-client vim libvips \
    **chromium**

RUN mkdir /usr/local/nvm

ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium

ENV NVM_DIR /usr/local/nvm
ENV NODE_VERSION 20.11.0
RUN curl https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash \
    && . $NVM_DIR/nvm.sh \
    && nvm install $NODE_VERSION \
    && nvm alias default $NODE_VERSION \
    && nvm use default

ENV NODE_PATH $NVM_DIR/versions/node/v$NODE_VERSION/lib/node_modules
ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH

RUN npm -g install puppeteer

RUN ls -la $NODE_PATH
WORKDIR /app

COPY Gemfile* ./
RUN bundle install
ADD . /app
RUN npm install

#COPY entrypoint.sh /usr/bin/
#RUN chmod +x /usr/bin/entrypoint.sh
#ENTRYPOINT ["entrypoint.sh"]
ARG DEFAULT_PORT 3000
EXPOSE 3000

CMD ["bin/dev"]

# config/initializers/grover.rb
Grover.configure do |config|
  config.options = {
    executable_path: ENV['PUPPETEER_EXECUTABLE_PATH'],
    launch_args: ['--no-sandbox', '--disable-dev-shm-usage']
  }
end

# houses_controller.rb
  def show
    @house = House.find_by(slug: params[:slug])
    respond_to do |format|
      format.html
      format.pdf do
        send_data PDFBuilders::House.call(@house), filename: "test.pdf", type: :pdf, disposition: :inline
      end
    end
  end

# pdf_builders/house.rb

module PDFBuilders
  class House < Base
    def call
      relative_html = ApplicationController.render({
                                                     template: "houses/show",
                                                     layout: 'application',
                                                     formats: :pdf,
                                                     assigns: { house: house }
                                                   })
      absolute_html = Grover::HTMLPreprocessor.process(relative_html, 'http://localhost:3000/', 'http') #replace localhost by app host
      Grover.new(absolute_html).to_pdf
    end
  end
sbounmy commented 10 months ago

Had some issue with images not being displayed.

I endedup sending an url instead of html via a custom type / format.print. The advantage : it's easier to debug since we can preview :)

you can view live: n28.fr/hq.pdf n28.fr/hq.print



# initializers/mime_types
Mime::Type.register "text/html", :print

# house_controller
  def show
    @house = House.find_by(slug: params[:slug])
    respond_to do |format|
      format.html
      format.pdf do
         send_data Grover.new(house_url(@house.id, format: :print)).to_pdf, filename: "test.pdf", type: :pdf, disposition: :inline
      end
    end
  end

# show.print.erb
# Some html template....