Skip to content

Release 1.9

Compare
Choose a tag to compare
@the-teacher the-teacher released this 06 May 12:47
· 99 commits to master since this release
d3025e5

Rails 7. Start Kit. Assets and Asset Pipelines

Screenshot 2023-03-18 at 21 31 27

Install and Run Ruby on Rails now!

Copy & Paste in your terminal

git clone https://github.com/the-teacher/rails7-startkit.git && \
  cd rails7-startkit && \
  bin/setup

I want to use Bootstrap. What next?

I just want to install Bootstrap. What should I do?

Option 1

Oldish "rails way" with installing a gem like "bootstrap-rails". Right? Maybe it still even works. Who knows.

Bootstrap is a front-end solution. I'm sure It has to be managed with a typical front-end approach. I mean node, npm or yarn.

Wrapping a front-end solution with a ruby code in 2023 is a very weird approach. Not my way!

Option 2

I heard something about ImportMap. Ok. Maybe it may help somehow. For sure, partially, because it is about JS code only. I should investigate what I can do with that.

Option 3

I can (even I have to) use a typical Front-end way with using yarn and compiling Bootstrap assets with a processor or processors. I'm going to use this way.

A Direction to Go

Before I continue, I should clearly understand what asset management approaches Rails provides out of the box (or recommends) and how to use them for my goals.

Let's do a quick overview first.

Sprockets

What is that? A way to deliver CSS/JS assets of a web page.
What does it do? It merges all assets described in a manifest in the only bundle-file.
What types of assets? It can work with CSS / JS.
Something else? It can minify a js/css bundle file and provide for a bundle a hashed name to provide caching.
Best Match For precompiled assets that you want to merge in the only bundle file and deliver in a typical Rails way.

ImportMap

What is that? A way to deliver JS assets of a web page.
What does it do? It emulates a modern way to import JS modules in an entryPoint.js file
What types of assets? Only JS files without any compilation or processing.
Something else? It can minify a js/css bundle file and provide for a bundle a hashed name to provide caching.
Best Match For small JS scripts and modules to make alive some of your elements on a page.

NPM/YARN and node_modules

What is that? A typical way to work with any Front-end assets and tools.
What does it do? It does everything what any FE developer needs, because it is FE eco system.
What types of assets? Any types.
Something else? Can be difficult to setup and use with Rails.
Best Match For front-end developers who are going to work with Rails and for heavy and big FE solutions for Rails apps.

What a Plan?

I want to use in my Application all possible approaches. I will use.

  • yarn + ESBuild + SASS to build Bootstrap Assets.
  • importMap to deliver on a page my own small JS scripts that I do not need to compile.
  • Sprockets to deliver precompiled assets on a page in a typical Rails way.

Yes, my friend! The party is going to be hard.

Naming is everything!

To avoid mixing different things the first thing that I'm going to do is renaming.

Having only application.js or application.css in the project may confuse when you use different approaches at the same time.

Let's isolate importmap and sprockets and place them in different folders (read scopes)

    <%= javascript_importmap_tags("importmap/application") %>

    <%= stylesheet_link_tag "sprockets/application", "data-turbo-track": "reload" %>
    <%= javascript_include_tag "sprockets/application", "data-turbo-track": "reload", defer: true %>

Screenshot 2023-03-18 at 23 36 56

app/javascript/
├── importmap
   ├── application.js
   ├── articlesIndex.js
   └── theSearchHighlight.js
└── sprockets
    └── application.js
app/assets/stylesheets/
├── demo
   ├── articles.css
   ├── demo.css
   ├── devise.css
   ├── main.css
   └── users.css
└── sprockets
    └── application.css

ImportMap

In my project I'm going to use ImportMap for delivering on the page my own simple JS scripts. It is a cheap and interesting way which I never used before. Why not? It just works and does not intersect with other things in the project.

app/javascript/importmap/
├── application.js
├── articlesIndex.js
└── theSearchHighlight.js

app/javascript/importmap/application.js

import ArticlesIndex from 'importmap/articlesIndex'
import TheSearchHighlight from 'importmap/theSearchHighlight'

console.log("Hello World! This is `importmap` entry point")

ArticlesIndex.init()
TheSearchHighlight.init('.the-search-highlight')

My config/importmap.rb looks so:

# Pin npm packages by running ./bin/importmap

pin "importmap/articlesIndex",      preload: true
pin "importmap/theSearchHighlight", preload: true
pin "importmap/application", preload: true

And together with

javascript_importmap_tags("importmap/application")

It produces the following code:

<script type="importmap" data-turbo-track="reload">
  { "imports": {
  "importmap/articlesIndex": "/assets/importmap/articlesIndex-69e4089.js",
  "importmap/theSearchHighlight": "/assets/importmap/theSearchHighlight-c58f503.js",
  "importmap/application": "/assets/importmap/application-acea523.js"
  }}
</script>

<link rel="modulepreload" href="/assets/importmap/articlesIndex-69e4089.js">
<link rel="modulepreload" href="/assets/importmap/theSearchHighlight-c58f503.js">
<link rel="modulepreload" href="/assets/importmap/application-acea525.js">

<script src="/assets/es-module-shims.min-d89e732.js" async="async" data-turbo-track="reload"></script>

<script type="module">import "importmap/application"</script>
Screenshot 2023-03-18 at 23 44 27

⚠️ ⚠️ ⚠️ Note 1

Potentially you can even to install and use additional packages with using something like ./bin/importmap pin react react-dom or even ./bin/importmap pin react react-dom --download.

I would like to recommend you to avoid it. Because installing any front-end packages with this way will contradict with a regular way to install packages with using node.

⚠️ ⚠️ ⚠️ Note 2

As you can see -- all ImportMap assets have hashed tails to provide a correct caching. It is because ImportMap is integrated with sprockets and rails asset pipeline. Not sure if I name things correctly. But you can see it here

Rails.application.config.assets.paths

ImportMap. Conclusion

ImportMap is a simple way to use simple modular JS scripts that do not need any compilation. You can use this approach even if you use something more difficult. No need to remove it if you are going to use something else, like esbuild or typical sprockets.

Node + Bootstrap + ESBuild + SASS

  • I'm going to install Bootstrap in my project.
  • Bootstrap is a pure Front-end solution
  • I'm going to use only FE ecosystem solutions. No rails gems or something like that!

Node and Yarn

$ node -v
v18.12.1

$ yarn -v
3.4.1
{
  "name": "rails7startkit",

  "scripts": {
    "start": "yarn build:js:watch & yarn build:sass:watch",
    "build": "yarn build:js & yarn build:sass",

    "build:sass": "sass ./app/assets/stylesheets/sass/application.scss:./app/assets/builds/sass/application.css --verbose --no-source-map --load-path=node_modules",
    "build:sass:watch": "yarn build:sass --watch",

    "build:js": "esbuild app/javascript/esbuild/application.js --outdir=app/assets/builds/esbuild --bundle --sourcemap --public-path=assets",
    "build:js:watch": "yarn build:js --watch"
  },

  "dependencies": {
    "@popperjs/core": "2.11.6",
    "bootstrap": "5.2.3",
    "bootstrap-icons": "1.10.3",
    "esbuild": "0.17.10",
    "sass": "^1.58.3"
  },
  "packageManager": "yarn@3.4.1"
}

Now let's install required things

yan install

Let's add some things in our JS, SASS entry points.

app/javascript/esbuild/application.js

import 'bootstrap'
import * as Popper from '@popperjs/core'

app/assets/stylesheets/sass/application.scss

@import 'bootstrap/scss/bootstrap';
@import 'bootstrap-icons/font/bootstrap-icons';
yarn build
lucky@bb2cd8bf2c41:~/app$ yarn build
[1]
[1]   app/assets/builds/esbuild/application.js      186.8kb
[1]   app/assets/builds/esbuild/application.js.map  356.9kb
[1]
[1]  Done in 66ms
Job [1], 'yarn build:js' has ended

Now we have some precompiled files

app/assets/builds/
├── esbuild
   ├── application.js
   └── application.js.map
└── sass
    └── application.css

image

Node and Yarn. Conclusion

Installation front-end packages with using Node and Yarn and compiling them is a typical way in front-end ecosystem. Use it for your front-end solutions. It may improve your development experience and will work correctly in a chain with Rails' Sprockets.

Sprockets

Now, when we have Bootstrap and precompiled bootstrap assets, we can use them as a typical local assets for sprockets

We will just use existed precompiled sass and js assets and use them in a sprocket's manifests.

image

app/javascript/sprockets/application.js

//= require esbuild/application.js
console.log("Hello from `sprockets/application.js`")

app/assets/stylesheets/sprockets/application.css

/*
 *= require "demo/main"
 *= require "demo/demo"
 *= require "demo/articles"
 *= require "demo/devise"
 *= require "demo/users"

 *= require "sass/application"
 */

To make things work property do not forget to edit manifest.js

app/assets/config/manifest.js

//= link_tree ../builds
//= link_tree ../images
//
//= link_directory ../stylesheets/sprockets
//
//= link_directory ../../../vendor/javascript
//= link_directory ../../javascript/importmap
//
//= link sprockets/application.js

Now, in development mode sprockets will merge all possible JS and CSS assets in bundles like that.

Screenshot 2023-03-19 at 00 30 38

In production mode everything will be managed during assets:precompile

Conclusion

  1. I've successfully and simultaneously used in my project 3 different types to work with assets
  2. Careful naming and placing files in the system opens comfortable ways to use approaches together
  3. I better understood advantages and boundaries different approaches to work with assets in Rails
  4. Sprockets can be easily used with tools like eslint and sass and be a good addition for them
  5. To work with FE tools in Rails you do not need and additional gems like jsbundling-rails

Personal recommendations

  • Use ImportMap to manage simple modular scripts for your page.
  • Avoid installing any assets or packages with ImportMap. It is not a best way that you can use. Avoid a mess in a project.
  • Use node, npm, yarn as a main way to install and compile any FE solutions. With FE solutions better to work in FE way.
  • You precompiled assets can be used by Sprockets and extend a typical Rails approach to manage assets.

That is it!

👉 Subscribe to the project to know about most recent updates.

Happy coding with Rails 7. Start Kit