Using Hanami with Webpack

Vladimir Dralo - 01.06.2017
hanami, webpack


Here, at Elixirator, we had a major rework for one of our projects. We were moving project’s backend from Rails to Hanami as part of this task. The Rails application had a mix of the javascript code, including vanilla JS, Coffeescript, JQuery and React. The final goal was to get rid of all extra dependencies, and use only one approach for writing frontend code - React.js.

We have decided to use Webpack as the main source of all frontend code, including Javascript and CSS. One of the first steps of the migration was making Hanami and Webpack work together.

After some initial testing, the best approach was found for our needs. Here is a short overview how it can be done.

Webpack configuration

First what is needed to be done, is Webpack. Hopefully, there is nothing difficult here. Just some basic Webpack stuff. Dev server needs to be started at some address, serving assets from the specific location.

An example of simple configuration could look like this:

// webpack.config.js
{
  output: {
    path: path.join(__dirname, '..', 'public', 'assets'),
    filename: '[name]-bundle.js',
    publicPath: 'http://localhost:8080/assets/'
  }
}

Hanami configuration

Hanami configuration is easy thanks to built-in helpers, which allow consuming assets from CDN. It is needed to specify content security policy to accept Javascript, CSS, images, etc. from location different than a website address. Providing config with the same host and port (as in the Webpack config) allows achieving this.

# application.rb
configure :development do
  security.content_security_policy %(
    script-src 'unsafe-eval' *;
    connect-src 'self' *;
    img-src 'self' data: *;
    style-src 'unsafe-inline' *;
    font-src 'self' *;
  )

  assets do
    compile false
    cdn true
    host '0.0.0.0'
    port 8080
  end
end

Going live

Despite using React.js as our main frontend library, most of the views are still written in plain HTML. These views are using Hanami asset helpers, such as image and javascript. We wanted to keep them. And we wanted to keep digested assets provided to us by Hanami.

When I was looking for the solution, I have tried to solve manifest file generation from the Webpack side initially. But existing plugins were producing it with the format that was not understood by Hanami. After spending some time, trying to export manifest with JS, I realized that I can achieve same easily using plain Ruby.

Webpack already outputs filenames with digest hash inside to the specified folder. So all I need is just get all files in assets folder, and save them to the JSON in the format that could be understood by Hanami.

The final solution is really straightforward:

# assets_tasks.rake
namespace :assets do
  desc 'Generate assets from webpack'
  task :generate do
    pty 'npm run build:development'

    assets_hash = {}

    Dir.glob('./public/assets/**/*.*').each do |f|
      file       = Pathname(f.sub('./public', ''))
      folder     = file.dirname
      asset_name = file.basename.to_s.split('_').first
      extension  = file.extname
      assets_hash["#{folder}/#{asset_name}#{extension}"] = { target: file, sri: '' }
    end

    File.open('./public/assets.json', 'w') { |f| f.write(assets_hash.to_json) }
  end
end

Summary

As the result, we started to use the combination of Hanami and React.js as our main tools. Adding minor features like auto mounting of React components, using mini SPAs inside the application, made our experience really smooth and pleasant. Webpack integration was just one of the first steps of our journey from Rails to Hanami. But this is a really long story. Stay tuned.


comments powered by Disqus