importmap-rails v2 is forever broken. Chart.js has a relative import which only works relative to cdn and it is not downloaded by bin/importmap:
import {...} from "../_/MwoWUuIu.js"
Using a cdn is probably the best option:
pin "chart.js", to: "https://ga.jspm.io/npm:[email protected]/dist/chart.js"
pin "@kurkle/color", to: "https://ga.jspm.io/npm:@kurkle/[email protected]/dist/color.esm.js"
Relative imports resolve correctly from a cdn:
https://ga.jspm.io/npm:[email protected]/dist/chart.js
`-> import {...} from "../_/MwoWUuIu.js
`-> https://ga.jspm.io/npm:[email protected]/_/MwoWUuIu.js
when you download chart.js file the url and the whole path is different:
http://localhost:3000/assets/chart.js
`-> import {...} from "../_/MwoWUuIu.js
`-> http://localhost:3000/_/MwoWUuIu.js 404
One way to solve this is to make a relative pin:
wget -P vendor/javascript/_/ https://ga.jspm.io/npm:[email protected]/_/MwoWUuIu.js
pin "./_/MwoWUuIu.js", to: "_/MwoWUuIu.js"
You can patch download option back, like in v1 where it was false by default so you don't have to pin manually:
# bin/importmap
#!/usr/bin/env ruby
require_relative "../config/application"
# Add back --download option, make it false by default
if ENV["IMPORTMAP_PATCH"] == "1"
puts "Download option patch enabled"
module ImportmapCommandsDownloadOptionPatch
def self.prepended base
base.option :download, for: :pin, type: :boolean, aliases: :d, default: false
base.option :download, for: :unpin, type: :boolean, aliases: :d, default: false
end
private def pin_package(package, url, preload)
if options[:download]
super
else
puts %(Pinning "#{package}" to #{url})
pin = packager.pin_for(package, url)
update_importmap_with_pin(package, pin)
end
end
end
require "thor"
class Importmap::Commands < Thor
def self.start(argv)
prepend ImportmapCommandsDownloadOptionPatch
super
end
end
end
require "importmap/commands"
$ IMPORTMAP_PATCH=1 bin/importmap pin chart.js
Download option patch enabled
Pinning "chart.js" to https://ga.jspm.io/npm:[email protected]/dist/chart.js
Pinning "@kurkle/color" to https://ga.jspm.io/npm:@kurkle/[email protected]/dist/color.esm.js
Alternatively, you could try downloading all the dependencies. This is a bit more involved to configure relative pins correctly, no warranty on this one:
# bin/importmap
#!/usr/bin/env ruby
require_relative "../config/application"
if ENV["IMPORTMAP_PATCH"] == "1"
# ...
end
# Try to download all the dependecies
if ENV["IMPORTMAP_PATCH"] == "2"
puts "Dependecy patch enabled"
module ImportmapCommandsDependencyPatch
def pin(*packages)
super
packager.extract_dependencies.each do |dep, opts|
opts => {to:, url:}
puts %( Pinning dependency "#{dep}" to #{packager.vendor_path}/#{to}.js via download from #{url})
packager.download(to, url)
pin = packager.pin_for(dep, "#{to}.js", preloads: options[:preload])
update_importmap_with_pin(dep, pin)
end
end
end
require "thor"
class Importmap::Commands < Thor
def self.start(argv)
prepend ImportmapCommandsDependencyPatch
super
end
end
module ImportmapPackagerDependencyPatch
private def extract_parsed_response(response)
parsed = JSON.parse(response.body)
imports = parsed.dig("map", "imports")
@parsed = parsed # need to grab this
{imports: imports}
end
# Take @parsed:
#
# {
# "staticDeps" => [ "https://ga.jspm.io/npm:@kurkle/[email protected]/dist/color.esm.js", "https://ga.jspm.io/npm:[email protected]/_/MwoWUuIu.js", "https://ga.jspm.io/npm:[email protected]/dist/chart.js" ],
# "dynamicDeps" => [],
# "map" => { "imports" => { "chart.js" => "https://ga.jspm.io/npm:[email protected]/dist/chart.js", "@kurkle/color" => "https://ga.jspm.io/npm:@kurkle/[email protected]/dist/color.esm.js" } }
# }
#
# and extract staticDeps to make a pin relative to its package so we
# can remap it to be realtive to our url:
#
# {
# "./_/MwoWUuIu.js" => { to: "chart.js--_--MwoWUuIu", url: "https://ga.jspm.io/npm:[email protected]/_/MwoWUuIu.js" }
# }
#
# isn't it obvious :)
def extract_dependencies
@parsed.dig("map", "imports").each_with_object({}) do |(name, url), deps|
relative_dir = url.match(/.+@.+?\//).to_s
@parsed["staticDeps"].each do |dep_url|
if dep_url.start_with?(relative_dir) && dep_url != url
deps["./#{dep_url.delete_prefix(relative_dir)}"] = {
to: "#{name}/#{dep_url.delete_prefix(relative_dir)}".gsub("/", "--").chomp(".js"),
url: dep_url,
}
end
end
end
end
end
require "importmap/packager"
Importmap::Packager.prepend ImportmapPackagerDependencyPatch
end
require "importmap/commands"
$ IMPORTMAP_PATCH=2 bin/importmap pin chart.js
Dependecy patch enabled
Pinning "chart.js" to vendor/javascript/chart.js.js via download from https://ga.jspm.io/npm:[email protected]/dist/chart.js
Pinning "@kurkle/color" to vendor/javascript/@kurkle/color.js via download from https://ga.jspm.io/npm:@kurkle/[email protected]/dist/color.esm.js
Pinning dependency "./_/MwoWUuIu.js" to vendor/javascript/chart.js--_--MwoWUuIu.js via download from https://ga.jspm.io/npm:[email protected]/_/MwoWUuIu.js
This will add the missing pin:
pin "chart.js" # @4.5.1
pin "@kurkle/color", to: "@kurkle--color.js" # @0.3.4
pin "./_/MwoWUuIu.js", to: "chart.js--_--MwoWUuIu.js"
Keep in mind, this might download a lot and is super slow. Relative pins are not unique, if it happens to be ./utils.js from two packages it would clash, the fix would be to nest all packages into their own directories instead of the current flat structure:
$ IMPORTMAP_PATCH=2 bin/importmap pin date-fns
Dependecy patch enabled
Pinning "date-fns" to vendor/javascript/date-fns.js via download from https://ga.jspm.io/npm:[email protected]/index.js
Pinning dependency "./_/BDJn6Y19.js" to vendor/javascript/date-fns--_--BDJn6Y19.js via download from https://ga.jspm.io/npm:[email protected]/_/BDJn6Y19.js
# ... 257 LINES SKIPPED !
Pinning dependency "./yearsToQuarters.js" to vendor/javascript/date-fns--yearsToQuarters.js via download from https://ga.jspm.io/npm:[email protected]/yearsToQuarters.js