rails: Rails 7.1 breaks assets compilation (Terser::Error: "extend" is redeclared)

Steps to reproduce

Rails 7.1 breaks assets compilation (Terser::Error: “extend” is redeclared)

I updated Rails 7.0.8 to Rails 7.1.1.

I use the terser 1.1.18 gem.

# config/environments/production.rb
config.assets.js_compressor = :terser

When I deploy the Rails app to Heroku I’m getting the following error for the assets compilation step.

https://eslint.org/docs/latest/rules/no-redeclare

...
remote:        rake aborted!
remote:        Terser::Error: "extend" is redeclared
remote:        --
remote:         44142   }
remote:         44143
remote:         44144 };
remote:         44145
remote:         44146 const ZERO_WIDTH_SPACE = "\uFEFF";
remote:         44147 const NON_BREAKING_SPACE = "\u00A0";
remote:         44148 const OBJECT_REPLACEMENT_CHARACTER = "\uFFFC";
remote:         44149
remote:            => const extend = function (properties) {
remote:         44151   for (const key in properties) {
remote:         44152     const value = properties[key];
remote:         44153     this[key] = value;
remote:         44154   }
remote:         44155
remote:         44156   return this;
remote:         44157 };
remote:         44158
remote:        ==
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/terser-1.1.18/lib/terser.rb:284:in `parse_result'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/terser-1.1.18/lib/terser.rb:228:in `run_terserjs'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/terser-1.1.18/lib/terser.rb:180:in `compile_with_map'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/terser-1.1.18/lib/terser/compressor.rb:36:in `call'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/terser-1.1.18/lib/terser/compressor.rb:23:in `call'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/sprockets-4.2.1/lib/sprockets/processor_utils.rb:84:in `call_processor'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/sprockets-4.2.1/lib/sprockets/processor_utils.rb:66:in `block in call_processors'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/sprockets-4.2.1/lib/sprockets/processor_utils.rb:65:in `reverse_each'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/sprockets-4.2.1/lib/sprockets/processor_utils.rb:65:in `call_processors'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/sprockets-4.2.1/lib/sprockets/loader.rb:184:in `load_from_unloaded'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/sprockets-4.2.1/lib/sprockets/loader.rb:59:in `block in load'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/sprockets-4.2.1/lib/sprockets/loader.rb:339:in `fetch_asset_from_dependency_cache'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/sprockets-4.2.1/lib/sprockets/loader.rb:43:in `load'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/sprockets-4.2.1/lib/sprockets/cached_environment.rb:44:in `block in load'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.2/lib/concurrent-ruby/concurrent/map.rb:207:in `block in fetch_or_store'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.2/lib/concurrent-ruby/concurrent/map.rb:187:in `fetch'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.2/lib/concurrent-ruby/concurrent/map.rb:206:in `fetch_or_store'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/sprockets-4.2.1/lib/sprockets/cached_environment.rb:44:in `load'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/sprockets-4.2.1/lib/sprockets/base.rb:81:in `find_asset'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/sprockets-4.2.1/lib/sprockets/base.rb:88:in `find_all_linked_assets'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/sprockets-4.2.1/lib/sprockets/manifest.rb:125:in `each'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/sprockets-4.2.1/lib/sprockets/manifest.rb:125:in `to_a'
remote:        /tmp/build_6cc64a5b/vendor/bundle/ruby/3.2.0/gems/sprockets-4.2.1/lib/sprockets/manifest.rb:125:in `block (2 levels) in find'
...

I suspect the issue is related to actioncable but I could not find the exact line of code that is failing. There are a few similar lines, for example rails/actioncable/app/assets/javascripts/actioncable.js: https://github.com/search?q=repo%3Arails%2Frails++const+extend+%3D+function+(properties)+{&type=code

When I run bundle exec rake assets:precompile in development then there is no error.

I’d appreciate your help. I’m not sure if this is the issue with Rails or maybe it’s our Heroku environment issue. Thank you.

Expected behavior

Assets compilation should be successful.

Actual behavior

Assets compilation is failing during Heroku deployment.

System configuration

Rails version: 7.1.1 (also the same issue on Rails 7.1.0)

Ruby version: 3.2.2

terser version: 1.1.18

About this issue

  • Original URL
  • State: closed
  • Created 9 months ago
  • Comments: 24 (10 by maintainers)

Commits related to this issue

Most upvoted comments

Do you think the error that I’m seeing in the rails_admin panel after using your fix for Rails is not related to Rails?

Sorry, no, I just haven’t had time to look into it yet. The linked PR fixed the original issue here, so let me open a new issue for the second problem

I managed to reproduce the issue on a new Rails 7 app. I installed the rails_admin gem with sprockets. You can see step by step in git commit messages what I did.

https://github.com/ArturT/rails7-issue-49599/commits/master

image

Amazing! Thanks. I think we need to fix trix. I’ll take a look on doing that.

Yes. I think you are doing the right thing to point to the commit, but the problem is that both trix included in Rails and the one included in rails_admin are defining the same constant. Trix should not be exposing a global constant. It works fine when se use imports, but not in the assets pipeline.

If you don’t add config.assets.js_compressor = :terser you can generate an unminified, but concatenated file which would need to have two or more const extend definitions, and we could use the code around them to know where they are defined.