rails / jsbundling-rails

Bundle and transpile JavaScript in Rails with esbuild, rollup.js, or Webpack.
MIT License
831 stars 143 forks source link

Assets:precomipile task has to be run twice #162

Closed Strnadj closed 1 month ago

Strnadj commented 1 year ago

We are facing an interesting issue...

$ app/assets/builds # Empty
$ public/assets # Empty
$ bin/rails assets:precompile
yarn install
yarn build
and
I, [2023-07-03T10:50:06.731882 #1]  INFO -- : Writing /web/public/assets/manifest-75a11da44c802486bc6f65640aa48a730f0f684c5c07a42ba3cd1735eb3fb070.js
I, [2023-07-03T10:50:06.732456 #1]  INFO -- : Writing /web/public/assets/manifest-75a11da44c802486bc6f65640aa48a730f0f684c5c07a42ba3cd1735eb3fb070.js.gz
I, [2023-07-03T10:50:06.732856 #1]  INFO -- : Writing /web/public/assets/nothing-uploaded-060da73a715f4a9d56da08aa5ce51d4e845eea67c11c911c6fb830726653ed2a.png

So the output is:

$ app/assets/builds
- application.js ... and more files
$ public/assets
- manifests.js
- manifest.js.gz
- nothing-uploaded.png

If we run the task for the 2nd time:

$ bin/rails assets:precompile
yarn install
yarn build
and
I, [2023-07-03T10:50:06.731882 #1]  INFO -- : Writing /web/public/assets/manifest-75a11da44c802486bc6f65640aa48a730f0f684c5c07a42ba3cd1735eb3fb070.js
I, [2023-07-03T10:50:06.732456 #1]  INFO -- : Writing /web/public/assets/manifest-75a11da44c802486bc6f65640aa48a730f0f684c5c07a42ba3cd1735eb3fb070.js.gz
I, [2023-07-03T10:50:06.732856 #1]  INFO -- : Writing /web/public/assets/nothing-uploaded-060da73a715f4a9d56da08aa5ce51d4e845eea67c11c911c6fb830726653ed2a.png
I, [2023-07-03T10:50:06.732856 #1]  INFO -- : Writing /web/public/assets/applications.js
... and the rest of the files

It will do what it should do, it will use the files from app/assets/builds to the public/assets and create a proper manifest.js. The first pass just creates an empty manifest. This is problematic, especially with empty docker builds.

p-romain commented 1 year ago

Hi, I have the same behavior. I'm new to RoR so I was thinking it was my fault, and posted a stackoverflow request here: https://stackoverflow.com/questions/76639570/forced-to-run-assetsprecompile-two-times-to-get-the-webpack-builds-copied-to-pu

brenogazzola commented 1 year ago

You mentioned app/assets/builds is empty. But does it exist? I've only seem this specific behavior when the folder itself had been deleted.

p-romain commented 1 year ago

Hi @brenogazzola For my tests, the first thing I do is to delete this folder. In the assets:precompile task, the first step is yarn install, the second one is yarn run webpack (or something similar), so at this time the directory is created and populated by webpack. Finally, these files are (not) copied by the assets:precompile task to the public folder.

When I run the task the second time, without deleting theapp/assets/builds folder this time, the same process is executed but the builded (by webpack) files are correctly copied to the public folder.

By this facts, we can suppose that the copy occurs before webpack compile, but this is not the case as we see in the console output 🤷‍♂️ A parallel execution or a docker filesystem latency could be the cause?

brenogazzola commented 1 year ago

I'm not sure, as I don't use Docker myself. But best way to handle this is to delete the files inside the folder, but keep the folder (and make sure you have a .keep file there so it gets commit to git and not removed by accident).

Strnadj commented 1 year ago

I'm not sure, as I don't use Docker myself. But best way to handle this is to delete the files inside the folder, but keep the folder (and make sure you have a .keep file there so it gets commit to git and not removed by accident).

The behavior is Docker-independent, it is doing the same thing locally, even on clean installation of rails. Looks like manifest.js is executed and cached before the yarn build ? 🤔

karthikishorejs commented 1 year ago

@brenogazzola Yeah if I leave .keep file, I did not have to precompile twice. Thanks.

p-romain commented 1 year ago

Thank you, same thing for me. I've understood why the .keep file was removed:

output: {
  path: path.resolve(__dirname, "app/assets/builds/"),
  clean: true,
}

So webpack remove the whole content of the directory on build... 😏

Fs00 commented 1 year ago

I've noticed something interesting that might be the cause of this issue. In the Rails console:

> Rails.application.load_tasks
> Rake::Task['assets:precompile'].prereqs
=> ["environment", "javascript:build"]

The interesting thing to notice here is that the assets:precompile task depends first on environment and then on javascript:build task. The environment task spins up the entire Rails environment, including Sprockets. And look at what Sprockets does when it's loaded:

https://github.com/rails/sprockets-rails/blob/065cbe83989c44019eca7161782ed4fdb6473517/lib/sprockets/railtie.rb#L48-L54

I feel that the caching on the Sprockets side might be the reason Sprockets cannot find the builds folder if it has been created after running the environment task. So we should try to change the order of the dependencies of the assets:precompile task (javascript:build before environment) and see what happens...

Update: I've tried to make the javascript:build task run before environment by changing the following line https://github.com/rails/jsbundling-rails/blob/7faf59f85303d44f39e78cfce21db09dbe48d60a/lib/tasks/jsbundling/build.rake#L66 to Rake::Task["assets:precompile"].prereqs.prepend("javascript:build"), but it didn't fix the issue as I hoped.