railpack 1.2.7 → 1.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26a629014375c6614d4b3c2e4f9673ff7df37152cb504cc236913d817f147123
4
- data.tar.gz: ad53fdfe5e81b6cab82e71b423104979d20c6ed49df64fab037861af6a6a47ea
3
+ metadata.gz: 78f1e4155bf62341ff89f8740ae745ad8d661a4268d6770bd9c7c5117ec1c516
4
+ data.tar.gz: 9f940702b52d4b4d2d6e17a62292cad384cf1c3a37ef73b34d94271bdb160bbc
5
5
  SHA512:
6
- metadata.gz: 706d8c94a84dc23d1b3bd7bf88a6df3ef7f075770ef168efef448e4ac136ae82255c33007b4b8aee70ffcda1321fc26bdc1b8a79b447e74349745bdab097e839
7
- data.tar.gz: e3f6fb48000ad925127355df341824429091c19cb78299bcaa7caf3bcfd4c5a4a277d12a3c7c995dde3f5a8761d64303fef99a0586258e53e01839df96e4e2b3
6
+ metadata.gz: 73e1eb2404abc24a6ca389019012bb43aa26f37de80efc7db5f66bc56a41e3b9ca4c12f9b830957df9265baf31a915d52326690b844596e1fd269faba038be3a
7
+ data.tar.gz: c1eafb1385dc6eaeeacb0e589964cb1d2de997642e5cccba4e77ba72bb6ef11318e45a1d0d7e625100412da47ed96aca895ea90324c8bb80aa70f30f9ad05806
data/CHANGELOG.md CHANGED
@@ -1,14 +1,14 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [1.2.7] - 2026-01-26
4
-
5
- - Add dedicated test files for Manager and Bundler classes
6
- - manager_test.rb: 13 unit tests for Manager class functionality
7
- - bundler_test.rb: 17 unit tests for all bundler implementations
8
- - Test bundler initialization, commands, inheritance, error handling
9
- - Test manager bundler creation, bundle size calculation, asset manifest generation
10
- - Improved test organization with separate test files per major class
11
- - All 43 tests passing with 137 assertions
3
+ ## [1.2.8] - 2026-01-26
4
+
5
+ - Add Sprockets compatibility for older Rails applications
6
+ - Automatic asset pipeline detection (Propshaft vs Sprockets)
7
+ - Generate appropriate manifest format based on Rails version
8
+ - Propshaft manifest: .manifest.json (Rails 7+ default)
9
+ - Sprockets manifest: .sprockets-manifest-*.json (Rails < 7)
10
+ - Enhanced Rails integration for broader compatibility
11
+ - All 46 tests passing with 147 assertions
12
12
 
13
13
  ## [1.2.4] - 2026-01-26
14
14
 
@@ -128,6 +128,39 @@ module Railpack
128
128
  outdir = config['outdir']
129
129
  return unless outdir && Dir.exist?(outdir)
130
130
 
131
+ # Detect asset pipeline type
132
+ pipeline_type = detect_asset_pipeline
133
+
134
+ case pipeline_type
135
+ when :propshaft
136
+ generate_propshaft_manifest(config)
137
+ when :sprockets
138
+ generate_sprockets_manifest(config)
139
+ else
140
+ # Default to Propshaft for Rails 7+
141
+ generate_propshaft_manifest(config)
142
+ end
143
+ rescue => error
144
+ Railpack.logger.warn "⚠️ Failed to generate asset manifest: #{error.message}"
145
+ end
146
+
147
+ private
148
+
149
+ def detect_asset_pipeline
150
+ # Check for Propshaft (Rails 7+ default)
151
+ if defined?(Propshaft) || (defined?(Rails) && Rails.version.to_f >= 7.0)
152
+ :propshaft
153
+ # Check for Sprockets
154
+ elsif defined?(Sprockets)
155
+ :sprockets
156
+ else
157
+ # Default to Propshaft for modern Rails
158
+ :propshaft
159
+ end
160
+ end
161
+
162
+ def generate_propshaft_manifest(config)
163
+ outdir = config['outdir']
131
164
  manifest = {}
132
165
 
133
166
  # Find built assets - Propshaft format
@@ -155,8 +188,47 @@ module Railpack
155
188
  manifest_path = "#{outdir}/.manifest.json"
156
189
  File.write(manifest_path, JSON.pretty_generate(manifest))
157
190
  Railpack.logger.debug "📄 Generated Propshaft manifest: #{manifest_path}"
158
- rescue => error
159
- Railpack.logger.warn "⚠️ Failed to generate asset manifest: #{error.message}"
191
+ end
192
+
193
+ def generate_sprockets_manifest(config)
194
+ outdir = config['outdir']
195
+ manifest = {
196
+ 'files' => {},
197
+ 'assets' => {}
198
+ }
199
+
200
+ # Find built assets - Sprockets format
201
+ Dir.glob("#{outdir}/**/*.{js,css}").each do |file|
202
+ next unless File.file?(file)
203
+ relative_path = Pathname.new(file).relative_path_from(Pathname.new(outdir)).to_s
204
+
205
+ # Generate digest for Sprockets format
206
+ digest = Digest::MD5.file(file).hexdigest
207
+ logical_path = relative_path
208
+
209
+ # Map logical names (Sprockets style)
210
+ if relative_path.include?('application') && relative_path.end_with?('.js')
211
+ manifest['assets']['application.js'] = "#{digest}-#{File.basename(relative_path)}"
212
+ logical_path = 'application.js'
213
+ elsif relative_path.include?('application') && relative_path.end_with?('.css')
214
+ manifest['assets']['application.css'] = "#{digest}-#{File.basename(relative_path)}"
215
+ logical_path = 'application.css'
216
+ end
217
+
218
+ # Add file entry
219
+ manifest['files']["#{digest}-#{File.basename(relative_path)}"] = {
220
+ 'logical_path' => logical_path,
221
+ 'pathname' => relative_path,
222
+ 'digest' => digest,
223
+ 'size' => File.size(file),
224
+ 'mtime' => File.mtime(file).iso8601
225
+ }
226
+ end
227
+
228
+ # Write manifest for Sprockets (Rails < 7)
229
+ manifest_path = "#{outdir}/.sprockets-manifest-#{Digest::MD5.hexdigest(outdir)}.json"
230
+ File.write(manifest_path, JSON.pretty_generate(manifest))
231
+ Railpack.logger.debug "📄 Generated Sprockets manifest: #{manifest_path}"
160
232
  end
161
233
  end
162
234
  end
@@ -1,3 +1,3 @@
1
1
  module Railpack
2
- VERSION = "1.2.7"
2
+ VERSION = "1.2.8"
3
3
  end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require 'railpack'
5
+
6
+ class BundlerTest < Minitest::Test
7
+ def test_bundler_base_class
8
+ bundler = Railpack::Bundler.new({})
9
+
10
+ assert_respond_to bundler, :build!
11
+ assert_respond_to bundler, :watch
12
+ assert_respond_to bundler, :install!
13
+ assert_respond_to bundler, :name
14
+ assert_respond_to bundler, :commands
15
+ end
16
+
17
+ def test_bun_bundler_initialization
18
+ bundler = Railpack::BunBundler.new({})
19
+ assert_instance_of Railpack::BunBundler, bundler
20
+ assert_equal 'bun', bundler.name
21
+ end
22
+
23
+ def test_bun_bundler_commands
24
+ bundler = Railpack::BunBundler.new({})
25
+ commands = bundler.send(:commands)
26
+
27
+ assert_equal 'bun run build', commands[:build]
28
+ assert_equal 'bun run watch', commands[:watch]
29
+ assert_equal 'bun install', commands[:install]
30
+ end
31
+
32
+ def test_esbuild_bundler_initialization
33
+ bundler = Railpack::EsbuildBundler.new({})
34
+ assert_instance_of Railpack::EsbuildBundler, bundler
35
+ assert_equal 'esbuild', bundler.name
36
+ end
37
+
38
+ def test_esbuild_bundler_commands
39
+ bundler = Railpack::EsbuildBundler.new({})
40
+ commands = bundler.send(:commands)
41
+
42
+ assert_equal 'esbuild', commands[:build]
43
+ assert_equal 'esbuild --watch', commands[:watch]
44
+ assert_equal 'npm install', commands[:install]
45
+ end
46
+
47
+ def test_rollup_bundler_initialization
48
+ bundler = Railpack::RollupBundler.new({})
49
+ assert_instance_of Railpack::RollupBundler, bundler
50
+ assert_equal 'rollup', bundler.name
51
+ end
52
+
53
+ def test_rollup_bundler_commands
54
+ bundler = Railpack::RollupBundler.new({})
55
+ commands = bundler.send(:commands)
56
+
57
+ assert_equal 'rollup', commands[:build]
58
+ assert_equal 'rollup --watch', commands[:watch]
59
+ assert_equal 'npm install', commands[:install]
60
+ end
61
+
62
+ def test_webpack_bundler_initialization
63
+ bundler = Railpack::WebpackBundler.new({})
64
+ assert_instance_of Railpack::WebpackBundler, bundler
65
+ assert_equal 'webpack', bundler.name
66
+ end
67
+
68
+ def test_webpack_bundler_commands
69
+ bundler = Railpack::WebpackBundler.new({})
70
+ commands = bundler.send(:commands)
71
+
72
+ assert_equal 'webpack', commands[:build]
73
+ assert_equal 'webpack --watch', commands[:watch]
74
+ assert_equal 'npm install', commands[:install]
75
+ end
76
+
77
+ def test_bundler_name_uniqueness
78
+ bundlers = [
79
+ Railpack::BunBundler.new({}),
80
+ Railpack::EsbuildBundler.new({}),
81
+ Railpack::RollupBundler.new({}),
82
+ Railpack::WebpackBundler.new({})
83
+ ]
84
+
85
+ names = bundlers.map(&:name)
86
+ assert_equal names.uniq.size, names.size, "All bundler names should be unique"
87
+ end
88
+
89
+ def test_bundler_commands_structure
90
+ bundlers = [
91
+ Railpack::BunBundler.new({}),
92
+ Railpack::EsbuildBundler.new({}),
93
+ Railpack::RollupBundler.new({}),
94
+ Railpack::WebpackBundler.new({})
95
+ ]
96
+
97
+ bundlers.each do |bundler|
98
+ commands = bundler.send(:commands)
99
+ assert_respond_to commands, :[]
100
+ assert commands.key?(:build)
101
+ assert commands.key?(:watch)
102
+ assert commands.key?(:install)
103
+
104
+ # All commands should be strings
105
+ assert commands[:build].is_a?(String)
106
+ assert commands[:watch].is_a?(String)
107
+ assert commands[:install].is_a?(String)
108
+ end
109
+ end
110
+
111
+ def test_bundler_inheritance
112
+ bundlers = [
113
+ Railpack::BunBundler,
114
+ Railpack::EsbuildBundler,
115
+ Railpack::RollupBundler,
116
+ Railpack::WebpackBundler
117
+ ]
118
+
119
+ bundlers.each do |bundler_class|
120
+ assert bundler_class < Railpack::Bundler, "#{bundler_class} should inherit from Railpack::Bundler"
121
+ end
122
+ end
123
+
124
+ def test_bundler_config_passing
125
+ config = { 'target' => 'node', 'format' => 'cjs' }
126
+
127
+ bundler = Railpack::BunBundler.new(config)
128
+ assert_equal config, bundler.instance_variable_get(:@config)
129
+ end
130
+
131
+ def test_bundler_error_handling
132
+ bundler = Railpack::BunBundler.new({})
133
+
134
+ # Mock system to return false (command failure)
135
+ bundler.define_singleton_method(:execute!) do |*args|
136
+ raise Railpack::Error, "Command failed"
137
+ end
138
+
139
+ # This should raise an error
140
+ assert_raises(Railpack::Error) do
141
+ bundler.build!([])
142
+ end
143
+ end
144
+
145
+ def test_bundler_watch_method
146
+ bundler = Railpack::BunBundler.new({})
147
+
148
+ # Mock system to avoid actual execution
149
+ bundler.define_singleton_method(:system) do |*args|
150
+ true
151
+ end
152
+
153
+ # Watch should not raise an error
154
+ begin
155
+ bundler.watch([])
156
+ assert true # If we get here, no exception was raised
157
+ rescue
158
+ flunk "watch method should not raise an error"
159
+ end
160
+ end
161
+
162
+ def test_bundler_install_method
163
+ bundler = Railpack::BunBundler.new({})
164
+
165
+ # Mock system to return true (command success)
166
+ bundler.define_singleton_method(:system) do |*args|
167
+ true
168
+ end
169
+
170
+ result = bundler.install!
171
+ assert result
172
+ end
173
+ end
@@ -0,0 +1,254 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require 'tempfile'
5
+ require 'fileutils'
6
+ require 'pathname'
7
+ require 'railpack'
8
+
9
+ class ManagerTest < Minitest::Test
10
+ def setup
11
+ @temp_dir = Dir.mktmpdir
12
+ @original_rails_root = nil
13
+ if defined?(Rails) && Rails.respond_to?(:root)
14
+ @original_rails_root = Rails.singleton_class.instance_method(:root)
15
+ end
16
+ end
17
+
18
+ def teardown
19
+ FileUtils.remove_entry(@temp_dir) if @temp_dir && Dir.exist?(@temp_dir)
20
+ # Restore original Rails.root method if it was overridden
21
+ if @original_rails_root && defined?(Rails)
22
+ Rails.singleton_class.define_method(:root, @original_rails_root)
23
+ end
24
+ end
25
+
26
+ def test_manager_initialization
27
+ manager = Railpack::Manager.new
28
+ assert_instance_of Railpack::Manager, manager
29
+ assert_respond_to manager, :build!
30
+ assert_respond_to manager, :watch
31
+ assert_respond_to manager, :install!
32
+ end
33
+
34
+ def test_manager_bundler_creation
35
+ manager = Railpack::Manager.new
36
+ bundler = manager.send(:create_bundler)
37
+ assert_instance_of Railpack::BunBundler, bundler
38
+ end
39
+
40
+ def test_manager_bundle_size_calculation
41
+ # Create test output directory with fake assets
42
+ outdir = File.join(@temp_dir, 'builds')
43
+ FileUtils.mkdir_p(outdir)
44
+
45
+ # Create fake JS and CSS files
46
+ File.write(File.join(outdir, 'app.js'), 'console.log("test");')
47
+ File.write(File.join(outdir, 'app.css'), 'body { color: red; }')
48
+ File.write(File.join(outdir, 'app.js.map'), '{"version":3}')
49
+
50
+ config = { 'outdir' => outdir }
51
+ manager = Railpack::Manager.new
52
+
53
+ size = manager.send(:calculate_bundle_size, config)
54
+ assert size.is_a?(Float)
55
+ assert size > 0
56
+ end
57
+
58
+ def test_manager_bundle_size_calculation_empty_directory
59
+ # Test with empty directory
60
+ outdir = File.join(@temp_dir, 'empty_builds')
61
+ FileUtils.mkdir_p(outdir)
62
+
63
+ config = { 'outdir' => outdir }
64
+ manager = Railpack::Manager.new
65
+
66
+ size = manager.send(:calculate_bundle_size, config)
67
+ assert_equal 0.0, size
68
+ end
69
+
70
+ def test_manager_bundle_size_calculation_nonexistent_directory
71
+ manager = Railpack::Manager.new
72
+ config = { 'outdir' => '/nonexistent/directory' }
73
+
74
+ size = manager.send(:calculate_bundle_size, config)
75
+ assert_equal 'unknown', size
76
+ end
77
+
78
+ def test_manager_asset_manifest_generation
79
+ outdir = File.join(@temp_dir, 'builds')
80
+ FileUtils.mkdir_p(outdir)
81
+
82
+ # Create fake built assets
83
+ File.write(File.join(outdir, 'application.js'), 'console.log("app");')
84
+ File.write(File.join(outdir, 'application.css'), 'body { color: blue; }')
85
+
86
+ config = { 'outdir' => outdir }
87
+ manager = Railpack::Manager.new
88
+
89
+ manager.send(:generate_asset_manifest, config)
90
+
91
+ manifest_path = File.join(outdir, '.manifest.json')
92
+ assert File.exist?(manifest_path)
93
+
94
+ manifest = JSON.parse(File.read(manifest_path))
95
+ assert manifest.key?('application.js')
96
+ assert manifest.key?('application.css')
97
+ assert manifest['application.js']['logical_path'] == 'application.js'
98
+ assert manifest['application.js']['pathname'].end_with?('application.js')
99
+ assert manifest['application.js']['digest']
100
+ end
101
+
102
+ def test_manager_asset_manifest_generation_no_assets
103
+ outdir = File.join(@temp_dir, 'empty_builds')
104
+ FileUtils.mkdir_p(outdir)
105
+
106
+ config = { 'outdir' => outdir }
107
+ manager = Railpack::Manager.new
108
+
109
+ # Ensure Propshaft detection for this test
110
+ def manager.detect_asset_pipeline
111
+ :propshaft
112
+ end
113
+
114
+ manager.send(:generate_asset_manifest, config)
115
+
116
+ manifest_path = File.join(outdir, '.manifest.json')
117
+ assert File.exist?(manifest_path)
118
+
119
+ manifest = JSON.parse(File.read(manifest_path))
120
+ assert_empty manifest
121
+ end
122
+
123
+ def test_manager_build_with_config
124
+ manager = Railpack::Manager.new
125
+
126
+ # Mock the bundler to avoid actual command execution
127
+ mock_bundler = Minitest::Mock.new
128
+ mock_bundler.expect(:build!, true, [[]])
129
+ manager.instance_variable_set(:@bundler, mock_bundler)
130
+
131
+ # Mock Rails.env to avoid dependency
132
+ rails_module = if defined?(Rails)
133
+ Rails
134
+ else
135
+ Object.const_set(:Rails, Module.new)
136
+ end
137
+ rails_module.define_singleton_method(:env) { 'development' }
138
+
139
+ result = manager.build!
140
+ assert result
141
+
142
+ mock_bundler.verify
143
+ end
144
+
145
+ def test_manager_install_with_config
146
+ manager = Railpack::Manager.new
147
+
148
+ # Mock the bundler to avoid actual command execution
149
+ mock_bundler = Minitest::Mock.new
150
+ mock_bundler.expect(:install!, true, [[]])
151
+ manager.instance_variable_set(:@bundler, mock_bundler)
152
+
153
+ result = manager.install!
154
+ assert result
155
+
156
+ mock_bundler.verify
157
+ end
158
+
159
+ def test_manager_watch_with_config
160
+ manager = Railpack::Manager.new
161
+
162
+ # Mock the bundler to avoid actual command execution
163
+ mock_bundler = Minitest::Mock.new
164
+ mock_bundler.expect(:watch, nil, [[]])
165
+ manager.instance_variable_set(:@bundler, mock_bundler)
166
+
167
+ manager.watch
168
+
169
+ mock_bundler.verify
170
+ end
171
+
172
+ def test_manager_bundler_constants
173
+ assert_equal Railpack::BunBundler, Railpack::Manager::BUNDLERS['bun']
174
+ assert_equal Railpack::EsbuildBundler, Railpack::Manager::BUNDLERS['esbuild']
175
+ assert_equal Railpack::RollupBundler, Railpack::Manager::BUNDLERS['rollup']
176
+ assert_equal Railpack::WebpackBundler, Railpack::Manager::BUNDLERS['webpack']
177
+ end
178
+
179
+ def test_detect_asset_pipeline_propshaft
180
+ manager = Railpack::Manager.new
181
+
182
+ # Mock Rails version for Propshaft detection
183
+ rails_module = if defined?(Rails)
184
+ Rails
185
+ else
186
+ Object.const_set(:Rails, Module.new)
187
+ end
188
+ rails_module.define_singleton_method(:version) { '7.0.0' }
189
+
190
+ pipeline = manager.send(:detect_asset_pipeline)
191
+ assert_equal :propshaft, pipeline
192
+ end
193
+
194
+ def test_detect_asset_pipeline_sprockets
195
+ manager = Railpack::Manager.new
196
+
197
+ # Mock Sprockets being available
198
+ Object.const_set(:Sprockets, Module.new) unless defined?(Sprockets)
199
+
200
+ # Mock Rails version for Sprockets detection
201
+ rails_module = if defined?(Rails)
202
+ Rails
203
+ else
204
+ Object.const_set(:Rails, Module.new)
205
+ end
206
+ rails_module.define_singleton_method(:version) { '6.1.0' }
207
+
208
+ pipeline = manager.send(:detect_asset_pipeline)
209
+ assert_equal :sprockets, pipeline
210
+ ensure
211
+ Object.send(:remove_const, :Sprockets) if defined?(Sprockets) && Sprockets.name.nil?
212
+ end
213
+
214
+ def test_generate_sprockets_manifest
215
+ outdir = File.join(@temp_dir, 'builds')
216
+ FileUtils.mkdir_p(outdir)
217
+
218
+ # Create fake built assets
219
+ File.write(File.join(outdir, 'application.js'), 'console.log("app");')
220
+ File.write(File.join(outdir, 'application.css'), 'body { color: blue; }')
221
+
222
+ config = { 'outdir' => outdir }
223
+ manager = Railpack::Manager.new
224
+
225
+ manager.send(:generate_sprockets_manifest, config)
226
+
227
+ manifest_path = Dir.glob("#{outdir}/.sprockets-manifest-*.json").first
228
+ assert manifest_path
229
+ assert File.exist?(manifest_path)
230
+
231
+ manifest = JSON.parse(File.read(manifest_path))
232
+ assert manifest.key?('files')
233
+ assert manifest.key?('assets')
234
+ assert manifest['assets'].key?('application.js')
235
+ assert manifest['assets'].key?('application.css')
236
+
237
+ # Check file entries
238
+ js_digest = manifest['assets']['application.js']
239
+ assert manifest['files'].key?(js_digest)
240
+ assert_equal 'application.js', manifest['files'][js_digest]['logical_path']
241
+ end
242
+
243
+ private
244
+
245
+ def mock_rails_root(path)
246
+ rails_module = if defined?(Rails)
247
+ Rails
248
+ else
249
+ Object.const_set(:Rails, Module.new)
250
+ end
251
+
252
+ rails_module.define_singleton_method(:root) { Pathname.new(path) }
253
+ end
254
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: railpack
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.7
4
+ version: 1.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - 21tycoons LLC
@@ -48,7 +48,9 @@ files:
48
48
  - lib/railpack/version.rb
49
49
  - lib/tasks/railpack.rake
50
50
  - sig/railpack.rbs
51
+ - test/bundler_test.rb
51
52
  - test/config_test.rb
53
+ - test/manager_test.rb
52
54
  - test/railpack_test.rb
53
55
  homepage: https://github.com/21tycoons/railpack
54
56
  licenses: