To follow up/end this discussion, I want to explain what I finally achieved and for now that’s a fine solution. First of all, I’ve dropped the whole importmaps idea. To me, it seems too immature at the moment. The most important blocking issue I faced - after implementing @eirvandelden’s solution - is that I can’t (easily) hide the importmaps definition in a HTML template used for a webshop containing information of files being used in an employee portal (which is a single page JavaScript app).
The gem I now have, is very lean. It contains a file/directory structure like this:
Root |
Sub 1 |
Sub 2 |
Sub 3 |
app |
|
|
|
|
javascript |
|
|
|
|
my_gem |
|
|
|
|
file1.js |
|
|
|
file2.js |
|
|
|
file3.js |
lib |
|
|
|
|
my_gem |
|
|
|
|
engine.rb |
|
|
my_gem.rb |
|
|
index.js |
|
|
|
my_gem.gemspec |
|
|
|
package.json |
|
|
|
The index.js
file contains these lines:
import 'app/javascript/my_gem/file1';
import 'app/javascript/my_gem/file2';
import 'app/javascript/my_gem/file3';
Nothing special needs to be done in the lib/my_gem.rb
and lib/my_gem/engine.rb
files!
The crucial part is to create a package.json
file so that the gem basically becomes a Node package as well. Not sure if this is common practice, but I like it. A hybrid solution!
Make sure the package.json
file contains the required parts. Something like this:
{
"author": "...",
"description": "...",
"name": "my_gem",
"repository": "...",
"version": "1.0.0"
}
The gem is ready to be used in an application! That application should at least include the jsbundling-rails
and propshaft
gems (not importmaps-rails
and sprocket-rails
). Besides that, it needs a package.json
file, containing my_gem
and esbuild
as dependencies. You don’t need to register your Ruby/NodeJS hybrid to rubygems.org and/or npmjs.org: both Gemfile
and package.json
can download from a GIT-repository!
Then, create app\javascript\application.js
with the line import 'my_gem';
at the top and you’re good to go!
As a bonus I’ve set up a watcher in Procfile.dev
that compiles that application.js
JavaScript:
js: yarn build --watch
web: bin/rails server -p 3000
This works, because package.json
also contains:
"scripts": {
"build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds"
}
As a final bonus, I’ve managed to trick the watcher to notice changes in my gem as well. I use this during development of the gem. After yarn install
or yarn upgrade
, the my_gem
directory is added to the node_modules
directory. By replacing that directory with a symbolic link to the local gem directory, Yarn is able to notice file differences!
$ cd node_modules
$ rm -r my_gem
$ ln -s /path/to/my_gem
I had this set-up quite some time ago, but I used to build the JavaScript inside the gem to create a single output file. That is no longer needed, so the gem no longer needs a dependency of esbuild and NodeJS is not needed for the gem. Still for the application (unfortunately), but for me this is the best solution at the moment!
As a side node to the above, I want to say I make use of the bootstrap
gem and since that took quite some time to configure correctly, I want to give you a direction on how to use it in JavaScript and CSS files.
// Example: Show Bootstrap Modal via a Stimulus controller
import { Controller } from '@hotwired/stimulus';
import { Modal } from 'bootstrap';
export default class extends Controller {
connect() {
this.modal = new Modal(this.element);
this.modal.show();
}
}
// Use full Bootstrap styling in SASS
@import 'bootstrap/scss/bootstrap';
// Use partial Bootstrap in SASS (saves output size)
@import 'bootstrap/scss/bootstrap-utilities';
@import 'bootstrap/scss/bootstrap-reboot';
@import 'bootstrap/scss/buttons';
Hope this is helpful to somebody. It took me quite some time to figure this out!