Rails importmap installs vendor scripts in vendor/javascript
and expects me to check that directory into version control:
The packages are downloaded to
vendor/javascript
, which you can check into your source control, and they'll be available through your application's own asset pipeline serving.
But I don’t want to check those files in. I want to gitignore them, as you normally would with something like NPM and the node_modules
directory, and instruct my production environment (Heroku) to install those dependencies itself. Ideally using something I can put in my Procfile.
How do I do that? There’s only one rake task, and that’s to install importmap itself, not the dependencies. I checked the commands too.
I could maybe cobble something together that iterates over the dependencies and downloads
them individually, but normally package managers offer a pre-existing public API for this, so I’m sure I’m just missing something.
You're not missing anything, there is no reinstall command. I took the code from this rejected pull request:
https://github.com/rails/importmap-rails/pull/226
I had to get creative to make it into a patch, because commands are executed right after the class definition:
# config/application.rb
def Importmap.const_added(const_name)
if const_name == :Commands
Importmap::Commands.class_eval do
desc "redownload", "Force download every package, even if the required versions are already downloaded"
def redownload
packages = npm.packages_with_versions.map { |package| package.join("@") }
pin packages
end
end
end
end
$ bin/importmap redownload
Pinning "@popperjs/core" to vendor/javascript/@popperjs/core.js via download from https://ga.jspm.io/npm:@popperjs/core@2.11.8/lib/index.js
Pinning "bootstrap" to vendor/javascript/bootstrap.js via download from https://ga.jspm.io/npm:bootstrap@5.3.3/dist/js/bootstrap.esm.js
Update
If you have packages pinned to urls, you could just start downloading them instead which is the default behavior in importmap-rails v2
, otherwise, you have to do some manual work:
def redownload
# packages = npm.packages_with_versions.map { |package| package.join("@") }
packages = npm.send(:importmap).scan(/^pin ["']([^["']]*)["'].* #.*@(\d+\.\d+\.\d+(?:[^\s]*)).*$/).map{_1*"@"}
pin packages
end
https://github.com/rails/importmap-rails/blob/v2.0.1/lib/importmap/npm.rb#L52
Update #2
Looks like the source of the download is not tracked anywhere. I've just checked bin/importmap update
command and that also ignores the original source and uses jspm
, which seems like a bug to me.
This one is a bit more involved. First, add ability to track the original source:
# config/application.rb
require "importmap/packager"
Importmap::Packager.class_eval do
# https://github.com/rails/importmap-rails/blob/v2.0.1/lib/importmap/packager.rb#L39
def vendored_pin_for(package, url)
filename = package_filename(package)
version = extract_package_version_from(url)
vendor = url.match(/npm|skypack|unpkg|jsdelivr/) # <= this
if "#{package}.js" == filename
%(pin "#{package}" # #{version} #{vendor}).strip
else
%(pin "#{package}", to: "#{filename}" # #{version} #{vendor}).strip
end
end
end
if you pin imask
:
bin/importmap pin imask --from unpkg
you get a pin like this:
pin "imask" # @7.6.1 unpkg
Now parse this importmap and use unpkg
as a --from
option:
# config/application.rb
def Importmap.const_added(const_name)
if const_name == :Commands
Importmap::Commands.class_eval do
desc "redownload", "Force download every package, even if the required versions are already downloaded"
def redownload
# adjusted here vvvvvvv
npm.send(:importmap).scan(/^pin ["']([^["']]*)["'].* #.*@(\d+\.\d+\.\d+(?:[^\s]*)).*?(\w+)?$/).group_by { _3 }.each do |from, packages|
packages_with_version = packages.map do |package|
package.pop
package * "@"
end
@options = {from:}
pin packages_with_version
end
end
end
end
end
Test:
# config/importmap.rb
pin "bootstrap" # @5.3.3
pin "@popperjs/core", to: "@popperjs--core.js" # @2.11.8
pin "jquery" # @3.7.1 jsdelivr
pin "imask" # @7.6.1 unpkg
$ bin/importmap redownload
Pinning "@popperjs/core" to vendor/javascript/@popperjs/core.js via download from https://ga.jspm.io/npm:@popperjs/core@2.11.8/lib/index.js
Pinning "bootstrap" to vendor/javascript/bootstrap.js via download from https://ga.jspm.io/npm:bootstrap@5.3.3/dist/js/bootstrap.esm.js
Pinning "jquery" to vendor/javascript/jquery.js via download from https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.js
Pinning "imask" to vendor/javascript/imask.js via download from https://unpkg.com/imask@7.6.1/esm/index.js