Skip to content

Commit

Permalink
Allow other module file extensions through config.importmap.accept
Browse files Browse the repository at this point in the history
  • Loading branch information
mtgrosser committed Mar 21, 2022
1 parent 3838dfc commit 19ca869
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 13 deletions.
1 change: 1 addition & 0 deletions lib/importmap/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Engine < ::Rails::Engine
config.importmap.sweep_cache = Rails.env.development? || Rails.env.test?
config.importmap.cache_sweepers = []
config.importmap.rescuable_asset_errors = []
config.importmap.accept = %w( js )

config.autoload_once_paths = %W( #{root}/app/helpers )

Expand Down
30 changes: 20 additions & 10 deletions lib/importmap/map.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def draw(path = nil, &block)

def pin(name, to: nil, preload: false)
clear_cache
@packages[name] = MappedFile.new(name: name, path: to || "#{name}.js", preload: preload)
@packages[name] = MappedFile.new(name: name, path: to || javascript_filename(name), preload: preload)
end

def pin_all_from(dir, under: nil, to: nil, preload: false)
Expand Down Expand Up @@ -73,10 +73,8 @@ def digest(resolver:)
# and test to ensure the map caches are reset when javascript files are changed.
def cache_sweeper(watches: nil)
if watches
@cache_sweeper =
Rails.application.config.file_watcher.new([], Array(watches).collect { |dir| [ dir.to_s, "js"] }.to_h) do
clear_cache
end
watches = Array(watches).collect { |dir| [ dir.to_s, accepted_extensions] }.to_h
@cache_sweeper = Rails.application.config.file_watcher.new([], watches) { clear_cache }
else
@cache_sweeper
end
Expand Down Expand Up @@ -137,7 +135,7 @@ def expanded_packages_and_directories
def expand_directories_into(paths)
@directories.values.each do |mapping|
if (absolute_path = absolute_root_of(mapping.dir)).exist?
find_javascript_files_in_tree(absolute_path).each do |filename|
find_accepted_files_in_tree(absolute_path).each do |filename|
module_filename = filename.relative_path_from(absolute_path)
module_name = module_name_from(module_filename, mapping)
module_path = module_path_from(module_filename, mapping)
Expand All @@ -149,18 +147,30 @@ def expand_directories_into(paths)
end

def module_name_from(filename, mapping)
[ mapping.under, filename.to_s.remove(filename.extname).remove(/\/?index$/).presence ].compact.join("/")
[ mapping.under, filename.to_s.remove(accepted_extensions_pattern).remove(/\/?index$/).presence ].compact.join("/")
end

def module_path_from(filename, mapping)
[ mapping.path || mapping.under, filename.to_s ].compact.join("/")
[ mapping.path || mapping.under, javascript_filename(filename.to_s) ].compact.join("/")
end

def find_javascript_files_in_tree(path)
Dir[path.join("**/*.js{,m}")].collect { |file| Pathname.new(file) }.select(&:file?)
def find_accepted_files_in_tree(path)
Dir[path.join("**/*.{#{accepted_extensions.join(',')}}")].map(&Pathname.method(:new)).select(&:file?)
end

def absolute_root_of(path)
(pathname = Pathname.new(path)).absolute? ? pathname : Rails.root.join(path)
end

def accepted_extensions
Rails.application.config.importmap.accept
end

def accepted_extensions_pattern
/\.(#{accepted_extensions.map(&Regexp.method(:escape)).join('|')})\z/
end

def javascript_filename(name)
"#{name.remove(accepted_extensions_pattern)}.js"
end
end
43 changes: 43 additions & 0 deletions test/cache_sweeper_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require "test_helper"

class CacheSweeperTest < ActiveSupport::TestCase

test "sweep is triggered when asset with extra extension changes" do
previous_accept = Rails.application.config.importmap.accept
Rails.application.config.importmap.accept += %w[jsx]

@importmap = Importmap::Map.new.tap do |map|
map.draw do
pin "application"
pin "components/Clock"
end
end

@importmap.cache_sweeper watches: %w[app/javascript vendor/javascript].map(&Rails.root.method(:join))

resolver = MockResolver.new(%r{components/Clock\.js\z})
imports = generate_imports(resolver: resolver)
touch_asset 'components/Clock.jsx'
new_imports = generate_imports(resolver: resolver)

assert_not_nil imports["components/Clock"]
assert_not_nil new_imports["components/Clock"]
assert_not_nil imports["application"]
assert_not_nil new_imports["application"]
assert_not_equal imports["components/Clock"], new_imports["components/Clock"]
assert_equal imports["application"], new_imports["application"]
ensure
Rails.application.config.importmap.accept = previous_accept
end

private
def touch_asset(name)
FileUtils.touch Rails.root.join('app', 'javascript', name)
sleep 3
@importmap.cache_sweeper.execute_if_updated
end

def generate_imports(resolver: ApplicationController.helpers)
JSON.parse(@importmap.to_json(resolver: resolver))["imports"]
end
end
12 changes: 12 additions & 0 deletions test/dummy/app/javascript/components/Clock.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Component } from "react";

export default class Clock extends Component {
render() {
return (
<div>
<h1>UNIX Clock</h1>
<p>The current UNIX date is {Date.now()}.</p>
</div>
);
}
}
7 changes: 7 additions & 0 deletions test/dummy/app/javascript/components/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { render } from "react-dom";
import Clock from "components/Clock";

render(
<Clock />,
document.getElementById('root')
);
27 changes: 25 additions & 2 deletions test/importmap_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def setup
pin_all_from "app/javascript/spina/controllers", under: "controllers/spina", preload: true
pin_all_from "app/javascript/spina/controllers", under: "controllers/spina", to: "spina/controllers", preload: true
pin_all_from "app/javascript/helpers", under: "helpers", preload: true
pin_all_from "app/javascript/components", under: "components"
pin_all_from "lib/assets/javascripts", preload: true
end
end
Expand Down Expand Up @@ -71,6 +72,27 @@ def setup
assert_no_match /application/, preloading_module_paths
end

test "jsx files are mapped to js when importmap accepts jsx" do
previous_accept = Rails.application.config.importmap.accept
Rails.application.config.importmap.accept += %w[jsx]
@importmap = Importmap::Map.new.tap do |map|
map.draw do
pin "application"
pin_all_from "app/javascript/controllers", under: "controllers", preload: true
pin_all_from "app/javascript/components", under: "components"
end
end
importmap_json = generate_importmap_json(resolver: MockResolver.new(%r{components/(Clock|index)\.js\z}))
assert_match %r|assets/components/index-.*\.js|, importmap_json["imports"]["components"]
assert_match %r|assets/components/Clock-.*\.js|, importmap_json["imports"]["components/Clock"]
ensure
Rails.application.config.importmap.accept = previous_accept
end

test "jsx files are not mapped when importmap doesn't accept jsx" do
assert_nil generate_importmap_json["imports"]["components/Clock"]
end

test "digest" do
assert_match /^\w{40}$/, @importmap.digest(resolver: ApplicationController.helpers)
end
Expand Down Expand Up @@ -98,7 +120,8 @@ def setup
end

private
def generate_importmap_json
JSON.parse @importmap.to_json(resolver: ApplicationController.helpers)

def generate_importmap_json(resolver: ApplicationController.helpers)
JSON.parse @importmap.to_json(resolver: resolver)
end
end
2 changes: 1 addition & 1 deletion test/reloader_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ class ReloaderTest < ActiveSupport::TestCase
private
def touch_config
FileUtils.touch(@config)
sleep 1
sleep 2
end
end
19 changes: 19 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,22 @@
ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files"
ActiveSupport::TestCase.fixtures :all
end

MockResolver = Struct.new(:pattern) do
def path_to_asset(path)
if path =~ pattern
digest = Digest::SHA256.hexdigest(source_file(path).mtime.to_s)
"/assets/" + path.sub(/\.js\z/, "-#{digest}.js")
else
ApplicationController.helpers.asset_path(path)
end
end

def root
Rails.root.join('app', 'javascript')
end

def source_file(path)
root.join("#{path}x")
end
end

0 comments on commit 19ca869

Please sign in to comment.