Вопрос или проблема
Обновление chart.js с версии 2.9.4 до 4.4.4.
Локально (машина — MAC OS), приложение на Rails генерирует графики с обновленной версией.
При развертывании на AWS EC2 сборка Jenkins завершается с ошибкой на этапе предварительной компиляции ресурсов –
rails aborted!
ExecJS::RuntimeError: SyntaxError: Unexpected token: operator (=)
JS_Parse_Error.get ((execjs):3538:621)
(execjs):4077:53
(execjs):1:32
Object.<anonymous> ((execjs):1:50)
Module._compile (node:internal/modules/cjs/loader:1364:14)
Module._extensions..js (node:internal/modules/cjs/loader:1422:10)
Module.load (node:internal/modules/cjs/loader:1203:32)
Module._load (node:internal/modules/cjs/loader:1019:12)
Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:128:12)
node:internal/main/run_main_module:28:49
/usr/local/rvm/gems/ruby-3.1.0/gems/execjs-2.9.1/lib/execjs/external_runtime.rb:40:in `exec'
/usr/local/rvm/gems/ruby-3.1.0/gems/execjs-2.9.1/lib/execjs/external_runtime.rb:22:in `eval'
/usr/local/rvm/gems/ruby-3.1.0/gems/execjs-2.9.1/lib/execjs/external_runtime.rb:47:in `call'
/usr/local/rvm/gems/ruby-3.1.0/gems/uglifier-3.2.0/lib/uglifier.rb:195:in `run_uglifyjs'
/usr/local/rvm/gems/ruby-3.1.0/gems/uglifier-3.2.0/lib/uglifier.rb:157:in `compile'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/compressing.rb:84:in `block in js_compressor="
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/processor_utils.rb:84:in `call_processor"
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/processor_utils.rb:66:in `block in call_processors'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/processor_utils.rb:65:in `reverse_each'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/processor_utils.rb:65:in `call_processors'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/loader.rb:184:in `load_from_unloaded'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/loader.rb:59:in `block in load'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/loader.rb:339:in `fetch_asset_from_dependency_cache'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/loader.rb:43:in `load'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/cached_environment.rb:44:in `block in load'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/map.rb:207:in `block in fetch_or_store'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/map.rb:187:in `fetch'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/map.rb:206:in `fetch_or_store'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/cached_environment.rb:44:in `load'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/bundle.rb:32:in `block in call'
/usr/local/rvm/gems/ruby-3.1.0/gems/set-1.1.0/lib/set.rb:501:in `each_key'
/usr/local/rvm/gems/ruby-3.1.0/gems/set-1.1.0/lib/set.rb:501:in `each'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/bundle.rb:31:in `call'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/processor_utils.rb:84:in `call_processor'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/processor_utils.rb:66:in `block in call_processors'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/processor_utils.rb:65:in `reverse_each'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/processor_utils.rb:65:in `call_processors'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/loader.rb:184:in `load_from_unloaded'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/loader.rb:59:in `block in load'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/loader.rb:339:in `fetch_asset_from_dependency_cache'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/loader.rb:43:in `load'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/cached_environment.rb:44:in `block in load'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/map.rb:207:in `block in fetch_or_store'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/map.rb:187:in `fetch'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/map.rb:206:in `fetch_or_store'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/cached_environment.rb:44:in `load'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/base.rb:81:in `find_asset'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/base.rb:88:in `find_all_linked_assets'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/manifest.rb:125:in `each'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/manifest.rb:125:in `to_a'
/usr/local/rvm/gems/ruby-3.1.0/gems/sprockets-4.2.1/lib/sprockets/manifest.rb:125:in `block (2 levels) in find'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb:24:in `block in execute'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb:48:in `block in synchronize'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb:48:in `synchronize'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb:48:in `synchronize'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb:22:in `execute'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/promise.rb:564:in `block in realize'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:359:in `run_task'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:350:in `block (3 levels) in create_worker'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:341:in `loop'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:341:in `block (2 levels) in create_worker'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:340:in `catch'
/usr/local/rvm/gems/ruby-3.1.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:340:in `block in create_worker'
Задачи: TOP => assets:precompile
Кроме того, когда версия chart.js понижена до 2.x, она работает.
Я пробовал несколько решений:
- Установить переменную окружения Production
EXECJS_RUNTIME="Node"
- Обновить версии Node и Yarn, аналогично локальной машине
- js_compressor = Uglifier, поэтому кодовая база уже содержит
js_compressor = Uglifier.new(harmony: true, output: { ascii_only: false })
- Обновление версии Uglifier до 4.2
- Также проверил gem RubyRacer, но возникла проблема с памятью
Похоже, что зависимость времени выполнения для среды Linux нуждается в настройке.
Пожалуйста, помогите.
Ответ или решение
Решение проблемы ExecJS::RuntimeError при компиляции ассетов в Rails 7
При переходе с версии библиотеки Chart.js 2.9.4 на 4.4.4 у вас возникла ошибка ExecJS::RuntimeError: SyntaxError: Unexpected token: operator (=)
во время этапа предкомпиляции ассетов на AWS EC2. Данная проблема может указывать на различия в окружении между вашим локальным компьютером (MacOS) и сервером, где происходит сборка приложения. Рассмотрим причины и возможные решения данной ошибки.
Причины ошибки
-
Совместимость синтаксиса ES6 и окружение: Chart.js 4.4.4 использует более современный синтаксис JavaScript, включая элементы ES6, такие как стрелочные функции и деструктуризацию. Если версия Node.js на стороне сервера устарела, она может не поддерживать данные возможности.
-
ExecJS и используемый движок: Ruby gem
ExecJS
позволяет запускать JavaScript-код в Ruby. ОшибкаSyntaxError
говорит о том, что не удаётся правильно обработать JavaScript-код. Возможно, выбранный движок JS не поддерживает синтаксис, использованный в новых версиях Chart.js. -
Конфигурация Sprockets и Uglifier: Если
Uglifier
не обновлён или его параметры некорректны, это может привести к ошибкам во время сжатия скриптов.
Рекомендации по решению проблемы
-
Проверка версии Node.js: Убедитесь, что на вашем AWS EC2 установлена версия Node.js, совместимая с Chart.js 4.4.4. Рекомендуется использовать Node.js версии 14 или выше, так как более ранние версии могут исключать поддержку ES6. Вы можете обновить Node.js с помощью nvm (Node Version Manager):
nvm install 14 nvm use 14
-
Использование другого рендерера для JavaScript: Попробуйте использовать
Node
в качестве движка дляExecJS
. Это можно сделать через установку переменной окружения:export EXECJS_RUNTIME="Node"
-
Проверка конфигурации Uglifier: Убедитесь, что ваш
Uglifier
находится на последней версии. Если вы уже обновили его до версии 4.2, убедитесь, что конфигурация выглядит следующим образом:config.assets.js_compressor = Uglifier.new(harmony: true)
-
Тестирование локально с идентичной конфигурацией: Попробуйте воспроизвести окружение сервера локально. Настройте локальный тест, использующий такую же версию OS и такие же зависимости библиотек и версии, как на сервере.
-
Обновление
ruby
иexecjs
: Убедитесь, что ваша версия Ruby и соответствующие гems обновлены до последних стабильных версий. Это может помочь избежать совместимых проблем:gem update execjs gem update rails
-
Логи сборки: Внимательно просмотрите логи Jenkins для выявления других возможных подсказок о проблеме. Иногда ошибка может указывать на проблемы в других частях кода или конфигурации.
Заключение
Эти шаги должны помочь вам справиться с ошибкой ExecJS::RuntimeError
. Главное — обратить внимание на различия в окружениях и убедиться, что все сторонние зависимости актуальны и совместимы. Успех в предкомпиляции ассетов в Rails 7 во многом зависит от согласованности конфигурации как в локальном, так и в продакшен-среде. Если проблема останется нерешённой, возможно, стоит также рассмотреть варианты использования других инструментов для сборки JavaScript, таких как Webpacker или Rollup, которые могут предложить лучшие возможности по работе с современными JS библиотеками.