Adding nodejs package to my Phoenix project broke esbuild. How to fix it?

Hey guys!

I added a JS package to my Phoenix project, specifically TipTap so I could have a text editor. They did not have Phoenix Framework specific install instructions so I used the Vanilla JS instructions and it seems to have worked locally but whenever I try to run fly deploy I get build errors now.

I have checked the following:

I assume that my issue is some kind of relative path problem? Iā€™m just not pointing correctly to the node_modules folder? But this is my folder structure and I donā€™t see anything wrong with the relative paths I have.

If anyone could help point out what the trouble spot is here, Iā€™d greatly appreciate it!

P.S. I forgot to add, I also tried:

  • using a relative path to the node_modules folder in the app.js import. That didnā€™t work.
  • marking the path as external (as suggested by the error message) but I must not have done it right because that also didnā€™t fix anything.

have you checked if you have the dependency in package.json? the folder node_modules usually is added in gitignore.

1 Like

Yes, I have. This is what I have in package.json:

{
  "dependencies": {
    "@tiptap/core": "^2.3.1",
    "@tiptap/pm": "^2.3.1",
    "@tiptap/starter-kit": "^2.3.1"
  }
}

Iā€™m not sure I follow what you said about gitignore. I believe the fly deploy should only be looking at my local files not my github right?

Dependencies are usually not copied over in the build/deploy environment. Esbuild is also not responsible for installing your dependencies, you have to run npm install specifically, how you do that on fly io is beyond my knowledge.

Hmm, I had already run npm install. Ran it again just now, this is the result:

npm install

up to date, audited 53 packages in 270ms

23 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

So I feel like that part is ok?

Iā€˜m not sure what you do locally matters. What matters is that those commands are also run when you build the docker images for fly.

When I migrated to Fly.io, I had to tweak my project a bit to install NPM packages.

I think this is all I had to patch:

Add npm and nodejs here in Dockerfile:

# install build dependencies
RUN apt-get update -y && apt-get install -y build-essential git nodejs npm \
    && apt-get clean && rm -f /var/lib/apt/lists/*_*

Add an npm ci install step to mix.exs assets.deploy:

      "assets.deploy": [
        "cmd --cd assets npm ci",
        "tailwind default --minify",
        "esbuild default --minify",
        "phx.digest"
      ],

Let me know if this doesnā€™t work- I may have missed another step Iā€™m not remembering now.

1 Like

Hi!

I did as you suggested and updated my Dockerfile and the mix.exs. I suspect there might be another step to your tweaks because I still got an error (albeit a different one now haha)

=> ERROR [builder 13/17] RUN mix assets.deploy                   1.3s 
------                                                                 
 > [builder 13/17] RUN mix assets.deploy:                              
1.260 npm ERR! The `npm ci` command can only install with an existing package-lock.json or
1.261 npm ERR! npm-shrinkwrap.json with lockfileVersion >= 1. Run an install with npm@5 or
1.261 npm ERR! later to generate a package-lock.json file, then try again.
1.266 
1.267 npm ERR! A complete log of this run can be found in:
1.267 npm ERR!     /root/.npm/_logs/2024-05-06T14_03_53_690Z-debug.log
1.280 ** (exit) 1
1.280     (mix 1.16.1) lib/mix/tasks/cmd.ex:74: Mix.Tasks.Cmd.run/1
1.280     (mix 1.16.1) lib/mix/task.ex:478: anonymous fn/3 in Mix.Task.run_task/5
1.280     (mix 1.16.1) lib/mix/task.ex:544: Mix.Task.run_alias/6
1.280     (mix 1.16.1) lib/mix/cli.ex:96: Mix.CLI.run_task/2
1.280     /usr/local/bin/mix:2: (file)
------
Error: failed to fetch an image or build from source: error building: failed to solve: process "/bin/sh -c mix assets.deploy" did not complete successfully: exit code: 1

Iā€™m not sure why this error is being thrown. I have a package-lock.json file in the root directory of my app. and it definitely has a lockfileVersion > 1:

{
  "name": "wordsmith",
  "lockfileVersion": 3,
  "requires": true,

And the other suggestion from the error has already been done. I ran npm install with version 10.5.0. It seems like maybe fly deploy it is not detecting the existence of my package-lock.json at all?

Iā€™m including the above in case the error jogs more memory and you remember some other small facet I need to implement. Thanks for your previous suggestion!

OK. I got it working now. I also posted this question to r/elixir and got a suggestion there:

If you are using most other default configuration, youā€™ll want to have your node_modules directory (along with the package.json etc.), inside of the assets folder.

So I moved package.json, package-lock.json, and the node_modules folder into my assets folder. That seems to have fixed things. The fly deploy ran successfully.

That poster also pointed out the documentation which I had read, but not clearly understood. It looks like there is a line there in option 2 that says ā€œCall npm inside your assets directoryā€. I didnā€™t think that was the option I was using, but it looks like I should have used npm install from the get-go from inside that directory and it would have created the node_modules folder where it needed to go.

I also left in the changes that @dfalling suggested, so Iā€™m not sure if that is part of why things are working now, but Iā€™m glad they are. Thanks again everyone.

1 Like

If you want to keep that in the root, you can tweak the assets.deploy to not change the directory: ā€œcmd ā€“cd assets npm ciā€. Personally I prefer what youā€™ve done now with keeping the Javascript in the assets directory.

One other note: I just learned that npm ci actually installs dev dependencies as well. Iā€™ve updated my build to this:

npm ci --only=prod

This ensures dev dependencies (eg rollup, prettier, etc.) arenā€™t included in the prod build.

1 Like