Странно впечаталось в память: аккурат сразу после появления Webpacker - обертки ruby под webpack - в качестве компилятора JavaScript по умолчанию (Ruby on Rails 6, доступен начиная с RoR 5.2), в Сети появилось множество статей рубистов/рельсовиков (я читал, в основном, на английском), восторженно расхваливающих нововведение и разносящих на все корки Sprockets. Дескать, и тем были плохи звездочки, и этим; а вот Webpacker - да, реально вещь. Продержался webpacker недолго, на сегодняшний день реинкарнировав в Shakapacker: в седьмых рельсах на смену пришел JSBundling-rails, предоставляющий на выбор сразу несколько упаковщиков. А Sprockets, хм, по-прежнему неизменно активно присутствует в новых проектах Ruby on Rails.
Что интересно. Походу, те же самые (я образно) авторы, превозносившие в бытность Webpacker, сегодня дружно переключились в том же самом ключе на JSBundling-rails; довелось уже прочесть немало негатива и про Webpacker. Да, "ruby-разработчик не обязан быть фронтендером", npm и yarn не для него, веб-технологии развиваются несказанно быстро... все так. Но, возможно, сказывается недоверчивая ментальность жителя РФ, и именно всеобщие восторги вчера - вселяют лично в меня некоторые сомнения относительно прочности новых веяний сегодня. Впрочем, будем посмотреть. Что еще остается, особенно с учетом реалий дня? - только наблюдать.
Чуть забегая вперед - об одном небесполезном трюке. Итак, будь то Webpacker, Shakapacker или JSBundling-rails - ок; скажите, знаете ли вы, что даже в актуальном релизе Rails вы вполне можете по старинке использовать Sprockets в качестве компилятора js? Это не преподносится в качестве правильного "rails way", но в ряде случаев способно оказаться полезным: допустим, вы не умеете (или, может статься, лениво) переписать javascript таким образом, чтобы создание ES6-модуля на его основе оказалось возможным.
Ничто не может быть проще: создаем (название именно так, во множественном числе) каталог:
app/assets/javascripts
и добавляем
//= link_directory ../javascripts .js
в
app/assets/config/manifest.js
Остальное неизменно: javascript_include_tag, и вперед. Кратко и емко в контексте отличий Webpack от Sprockets - в статье by Younes Serraj. И какой нам, вообще говоря, right way после параллельного импорта! - гулять так гулять, стрелять так стрелять, один хрен счет будет неподъемным.
Это было, можно сказать, лирическое отступление, а теперь займемся собственно миграцией from Webpacker to JSBundling-rails (из духа противоречия я выбрал все тот же webpack). Рассказанное и показаное далее ни в коей мере не является чем-то эксклюзивным и инновационным, но содержит ряд практических деталей, способных в некоторой степени облегчить описываемый переход для новичков Ruby on Rails.
# Add to your Gemfile
+ gem 'jsbundling-rails'
# From the CLI, rebuild the bundle
./bin/bundle install
Раз уж webpack, стало быть, миграция начинается у меня (обновлял rails-blog) вот с таких команд в терминале:
./bin/rails javascript:install:webpack
mv webpack.config.js config/webpack
При необходимости и если что-то пойдет не так - не стесняйтесь взглянуть скрипты блога: репозиторий называется CRUD-Blog, это полностью рабочая демка, опубликованная на Heroku.
Сообразно конфигурации своего web-приложения отредактируйте application.html.erb, никаких javascript_pack_tag, stylesheet_pack_tag там (и не только там) остаться не должно. Можно поискать для верности::
grep -Fre 'pack_tag' app
У меня, например, вот так:
grep -Fre 'link_tag' app
app/views/layouts/application.html.erb: <%= stylesheet_link_tag 'application', media: 'all' %>
app/views/layouts/application.html.erb: <%= stylesheet_link_tag("clean-blog.min") %>
app/views/layouts/application.html.erb: <%= favicon_link_tag 'favicon.ico' %>
app/views/contacts/index.html.erb: <%= stylesheet_link_tag("contacts_only") %>
app/views/contacts/index.html.erb: <%= stylesheet_link_tag("mdb") %>
Вносим изменения в два файла:
# webpack.config.js:
- path: path.resolve(__dirname, "app/assets/builds"),
+ path: path.resolve(__dirname, '..', '..', 'app/assets/builds')
# package.json
- "build": "webpack --config ./webpack.config.js"
+ "build": "webpack --config ./config/webpack/webpack.config.js"
Удаляем файлы и директории, "убедившись, что перенесли все внесенные в них настройки":
./bin/webpack
./bin/webpack-dev-server
./config/initializers/webpacker.rb
./config/webpacker.yml
./config/webpack/development.js
./config/webpack/environment.js
./config/webpack/production.js
./config/webpack/test.js
А вот здесь кофе-брейк и небольшая рекламная пауза...
Необходимо, по-видимому, остановиться на контексте директивы о переносе настроек, что почерпнута из перевода официальной доки миграции. Уточню, речь идет сугубо о конфигах, отличных от дефолтных, т. е. привнесенных вами ранее, в процессе разработки rails-app, изменениях. Для меня это был конфиг jQuery, установленного посредством yarn.
Чтобы "перенести настройки", порекомендую прислушаться к совету, прозвучавшему в лекции How to use jQuery & jQueryUI with Esbuild знаменитого Chris from GoRails: создать файл, содержащий всего три строчки:
add_jquery.js
import $ from 'jquery';
window.jQuery = $;
window.$ = $;
, и импортировать в application.js:
import "./add_jquery"
Иначе в консоли непременно получите Uncaught ReferenceError: $ is not defined или что-нибудь в этом роде.
Кстати. Чтобы ошибка исчезла (а jQuery заработал), только лишь перезапуска rails-сервера может не хватить: используйте
rake assets:clobber
rake assets:precompile
Но все это чуть позже, после окончания миграции. Которое уже (да, вот так быстро) не за горами.
Удаляем из двух конфигов:
config/initializers/assets.rb
-# Add Yarn node_modules folder to the asset load path.
-Rails.application.config.assets.paths << Rails.root.join('node_modules')
config/initializers/content_security_policy.rb
-# # If you are using webpack-dev-server then specify webpack-dev-server host
-# policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
Удаляем из Gemfile gem "webpacker", бундлим. Еще удаляем:
yarn remove @rails/webpacker webpack-dev-server
Последний (во всяком случае, на это есть некоторая надежда) штрих. Смотрите, что в итоге получилось: у нас теперь есть новая точка входа app/javascript/application.js. Куда очень бы неплохо переместить содержание старой точки входа Webpacker, скопировав всё из app/javascript/packs - в app/javascript. Старый файл application.js смело копируйте с заменой нового, вновь созданного: он у вас пока что пустой. Озаботьтесь корректностью относительных путей на новом месте и, что немаловажно, полностью избавьтесь от require, заменив на import.
Пробуем:
yarn build --progress --color
Примечание. Если с этого места начинаются пляски с бубнами вокруг путей импорта - yarn снова и снова выдает ошибки - вы можете попытаться вернуть на место (временно) удаленную из config/initializers/assets.rb (см. выше) строчку. Либо же добавить аналогичную в config/application.rb:
config.assets.paths << Rails.root.join('node_modules')
Из потенциакльных проблем: Error message "DevTools failed to load SourceMap: Could not load content for chrome-extension://..." в консоли - не имеет отношения к вашему коду и элегантно отключается в настройках Chrome. ActionController::RoutingError (No route matches [GET] "/assets/application.js....) в Ruby on Rails 6 легко лечится: смените true на false в
config/environments/development.rb
config.assets.debug = true
Нужен ли вам babeljs - решайте сами. Я подумал... и забил на это дело. Если считаете иначе -
yarn add @babel/core @babel/preset-env babel-loader
, подробности на страничке официального дока jsbundling-rails.
Комментарии в блоге