Skip to content

Commit 44820ab

Browse files
committed
Allow other module file extensions through config.importmap.accept
1 parent 4d02a20 commit 44820ab

File tree

8 files changed

+123
-12
lines changed

8 files changed

+123
-12
lines changed

lib/importmap/engine.rb

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class Engine < ::Rails::Engine
1010
config.importmap.sweep_cache = Rails.env.development? || Rails.env.test?
1111
config.importmap.cache_sweepers = []
1212
config.importmap.rescuable_asset_errors = []
13+
config.importmap.accept = %w( js mjs )
1314

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

lib/importmap/map.rb

+15-9
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,8 @@ def digest(resolver:)
7373
# and test to ensure the map caches are reset when javascript files are changed.
7474
def cache_sweeper(watches: nil)
7575
if watches
76-
@cache_sweeper =
77-
Rails.application.config.file_watcher.new([], Array(watches).collect { |dir| [ dir.to_s, "js"] }.to_h) do
78-
clear_cache
79-
end
76+
watches = Array(watches).collect { |dir| [ dir.to_s, accepted_extensions] }.to_h
77+
@cache_sweeper = Rails.application.config.file_watcher.new([], watches) { clear_cache }
8078
else
8179
@cache_sweeper
8280
end
@@ -137,7 +135,7 @@ def expanded_packages_and_directories
137135
def expand_directories_into(paths)
138136
@directories.values.each do |mapping|
139137
if (absolute_path = absolute_root_of(mapping.dir)).exist?
140-
find_javascript_files_in_tree(absolute_path).each do |filename|
138+
find_accepted_files_in_tree(absolute_path).each do |filename|
141139
module_filename = filename.relative_path_from(absolute_path)
142140
module_name = module_name_from(module_filename, mapping)
143141
module_path = module_path_from(module_filename, mapping)
@@ -149,18 +147,26 @@ def expand_directories_into(paths)
149147
end
150148

151149
def module_name_from(filename, mapping)
152-
[ mapping.under, filename.to_s.remove(filename.extname).remove(/\/?index$/).presence ].compact.join("/")
150+
[ mapping.under, remove_accepted_extensions(filename).remove(/\/?index$/).presence ].compact.join("/")
153151
end
154152

155153
def module_path_from(filename, mapping)
156-
[ mapping.path || mapping.under, filename.to_s ].compact.join("/")
154+
[ mapping.path || mapping.under, "#{remove_accepted_extensions(filename)}.js" ].compact.join("/")
157155
end
158156

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

163161
def absolute_root_of(path)
164162
(pathname = Pathname.new(path)).absolute? ? pathname : Rails.root.join(path)
165163
end
164+
165+
def accepted_extensions
166+
Rails.application.config.importmap.accept
167+
end
168+
169+
def remove_accepted_extensions(filename)
170+
filename.to_s.remove(/\.(#{accepted_extensions.map(&Regexp.method(:escape)).join('|')})\z/)
171+
end
166172
end

test/cache_sweeper_test.rb

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
require "test_helper"
2+
3+
class CacheSweeperTest < ActiveSupport::TestCase
4+
5+
test "sweep is triggered when asset with extra extension changes" do
6+
previous_accept = Rails.application.config.importmap.accept
7+
Rails.application.config.importmap.accept += %w[jsx]
8+
9+
@importmap = Importmap::Map.new.tap do |map|
10+
map.draw do
11+
pin "application"
12+
pin "components/Clock"
13+
end
14+
end
15+
16+
@importmap.cache_sweeper watches: %w[app/javascript vendor/javascript].map(&Rails.root.method(:join))
17+
18+
resolver = MockResolver.new(%w[jsx])
19+
imports = generate_imports(resolver: resolver)
20+
touch_asset 'components/Clock.jsx'
21+
new_imports = generate_imports(resolver: resolver)
22+
23+
assert_not_nil imports["components/Clock"]
24+
assert_not_nil new_imports["components/Clock"]
25+
assert_not_nil imports["application"]
26+
assert_not_nil new_imports["application"]
27+
assert_not_equal imports["components/Clock"], new_imports["components/Clock"]
28+
assert_equal imports["application"], new_imports["application"]
29+
ensure
30+
Rails.application.config.importmap.accept = previous_accept
31+
end
32+
33+
private
34+
def touch_asset(name)
35+
FileUtils.touch Rails.root.join('app', 'javascript', name)
36+
sleep 3
37+
@importmap.cache_sweeper.execute_if_updated
38+
end
39+
40+
def generate_imports(resolver: ApplicationController.helpers)
41+
JSON.parse(@importmap.to_json(resolver: resolver))["imports"]
42+
end
43+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Component } from "react";
2+
3+
export default class Clock extends Component {
4+
render() {
5+
return (
6+
<div>
7+
<h1>UNIX Clock</h1>
8+
<p>The current UNIX date is {Date.now()}.</p>
9+
</div>
10+
);
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { render } from "react-dom";
2+
import Clock from "components/Clock";
3+
4+
render(
5+
<Clock />,
6+
document.getElementById('root')
7+
);

test/importmap_test.rb

+25-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def setup
1313
pin_all_from "app/javascript/spina/controllers", under: "controllers/spina", preload: true
1414
pin_all_from "app/javascript/spina/controllers", under: "controllers/spina", to: "spina/controllers", preload: true
1515
pin_all_from "app/javascript/helpers", under: "helpers", preload: true
16+
pin_all_from "app/javascript/components", under: "components"
1617
pin_all_from "lib/assets/javascripts", preload: true
1718
end
1819
end
@@ -71,6 +72,27 @@ def setup
7172
assert_no_match /application/, preloading_module_paths
7273
end
7374

75+
test "jsx files are mapped to js when importmap accepts jsx" do
76+
previous_accept = Rails.application.config.importmap.accept
77+
Rails.application.config.importmap.accept += %w[jsx]
78+
@importmap = Importmap::Map.new.tap do |map|
79+
map.draw do
80+
pin "application"
81+
pin_all_from "app/javascript/controllers", under: "controllers", preload: true
82+
pin_all_from "app/javascript/components", under: "components"
83+
end
84+
end
85+
importmap_json = generate_importmap_json(resolver: MockResolver.new(%w[jsx]))
86+
assert_match %r|assets/components/index-.*\.js|, importmap_json["imports"]["components"]
87+
assert_match %r|assets/components/Clock-.*\.js|, importmap_json["imports"]["components/Clock"]
88+
ensure
89+
Rails.application.config.importmap.accept = previous_accept
90+
end
91+
92+
test "jsx files are not mapped when importmap doesn't accept jsx" do
93+
assert_nil generate_importmap_json["imports"]["components/Clock"]
94+
end
95+
7496
test "digest" do
7597
assert_match /^\w{40}$/, @importmap.digest(resolver: ApplicationController.helpers)
7698
end
@@ -98,7 +120,8 @@ def setup
98120
end
99121

100122
private
101-
def generate_importmap_json
102-
JSON.parse @importmap.to_json(resolver: ApplicationController.helpers)
123+
124+
def generate_importmap_json(resolver: ApplicationController.helpers)
125+
JSON.parse @importmap.to_json(resolver: resolver)
103126
end
104127
end

test/reloader_test.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ class ReloaderTest < ActiveSupport::TestCase
2525
private
2626
def touch_config
2727
FileUtils.touch(@config)
28-
sleep 1
28+
sleep 2
2929
end
3030
end

test/test_helper.rb

+19
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,22 @@
1414
ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files"
1515
ActiveSupport::TestCase.fixtures :all
1616
end
17+
18+
MockResolver = Struct.new(:accept) do
19+
def path_to_asset(path)
20+
if source = source_file(path)
21+
digest = Digest::SHA256.hexdigest(source.mtime.to_s)
22+
"/assets/" + path.sub(/\.js\z/, "-#{digest}.js")
23+
else
24+
ApplicationController.helpers.asset_path(path)
25+
end
26+
end
27+
28+
def root
29+
Rails.root.join('app', 'javascript')
30+
end
31+
32+
def source_file(path)
33+
accept.map { |ext| root.join("#{path.remove(/\.js\z/)}.#{ext}") }.detect(&:file?)
34+
end
35+
end

0 commit comments

Comments
 (0)