Rails 7 – ExecJS::RuntimeError: SyntaxError: Неожиданный токен: оператор (=) – ошибка компиляции производственных ресурсов:precompile

Вопрос или проблема

Обновление 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) и сервером, где происходит сборка приложения. Рассмотрим причины и возможные решения данной ошибки.

Причины ошибки

  1. Совместимость синтаксиса ES6 и окружение: Chart.js 4.4.4 использует более современный синтаксис JavaScript, включая элементы ES6, такие как стрелочные функции и деструктуризацию. Если версия Node.js на стороне сервера устарела, она может не поддерживать данные возможности.

  2. ExecJS и используемый движок: Ruby gem ExecJS позволяет запускать JavaScript-код в Ruby. Ошибка SyntaxError говорит о том, что не удаётся правильно обработать JavaScript-код. Возможно, выбранный движок JS не поддерживает синтаксис, использованный в новых версиях Chart.js.

  3. Конфигурация Sprockets и Uglifier: Если Uglifier не обновлён или его параметры некорректны, это может привести к ошибкам во время сжатия скриптов.

Рекомендации по решению проблемы

  1. Проверка версии 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
  2. Использование другого рендерера для JavaScript: Попробуйте использовать Node в качестве движка для ExecJS. Это можно сделать через установку переменной окружения:

    export EXECJS_RUNTIME="Node"
  3. Проверка конфигурации Uglifier: Убедитесь, что ваш Uglifier находится на последней версии. Если вы уже обновили его до версии 4.2, убедитесь, что конфигурация выглядит следующим образом:

    config.assets.js_compressor = Uglifier.new(harmony: true)
  4. Тестирование локально с идентичной конфигурацией: Попробуйте воспроизвести окружение сервера локально. Настройте локальный тест, использующий такую же версию OS и такие же зависимости библиотек и версии, как на сервере.

  5. Обновление ruby и execjs: Убедитесь, что ваша версия Ruby и соответствующие гems обновлены до последних стабильных версий. Это может помочь избежать совместимых проблем:

    gem update execjs
    gem update rails
  6. Логи сборки: Внимательно просмотрите логи Jenkins для выявления других возможных подсказок о проблеме. Иногда ошибка может указывать на проблемы в других частях кода или конфигурации.

Заключение

Эти шаги должны помочь вам справиться с ошибкой ExecJS::RuntimeError. Главное — обратить внимание на различия в окружениях и убедиться, что все сторонние зависимости актуальны и совместимы. Успех в предкомпиляции ассетов в Rails 7 во многом зависит от согласованности конфигурации как в локальном, так и в продакшен-среде. Если проблема останется нерешённой, возможно, стоит также рассмотреть варианты использования других инструментов для сборки JavaScript, таких как Webpacker или Rollup, которые могут предложить лучшие возможности по работе с современными JS библиотеками.

Оцените материал
Добавить комментарий

Капча загружается...