Skip to content

Commit

Permalink
feat: Improve client manifest path handling for dev server
Browse files Browse the repository at this point in the history
- Add `dev_server_url` helper to centralize dev server URL construction
- Add `public_output_uri_path` to get relative webpack output path
- Add `asset_uri_from_packer` to handle asset URIs consistently
- Update `react_client_manifest_file_path` to return dev server URLs when appropriate
- Add comprehensive specs for new asset URI handling

This change ensures client manifest paths are properly resolved to dev server
URLs during development, improving hot-reloading functionality.
  • Loading branch information
AbanoubGhadban committed Jan 12, 2025
1 parent 838a6e7 commit 4e89e93
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 2 deletions.
21 changes: 20 additions & 1 deletion lib/react_on_rails/packer_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ def self.dev_server_running?
packer.dev_server.running?
end

def self.dev_server_url
"#{packer.dev_server.protocol}://#{packer.dev_server.host_with_port}"
end

def self.shakapacker_version
return @shakapacker_version if defined?(@shakapacker_version)
return nil unless ReactOnRails::Utils.gem_available?("shakapacker")
Expand Down Expand Up @@ -79,12 +83,27 @@ def self.bundle_js_uri_from_packer(bundle_name)

if packer.dev_server.running? && (!is_bundle_running_on_server ||
ReactOnRails.configuration.same_bundle_for_client_and_server)
"#{packer.dev_server.protocol}://#{packer.dev_server.host_with_port}#{hashed_bundle_name}"
"#{dev_server_url}#{hashed_bundle_name}"
else
File.expand_path(File.join("public", hashed_bundle_name)).to_s
end
end

def self.public_output_uri_path
"#{packer.config.public_output_path.relative_path_from(packer.config.public_path)}/"
end

# The function doesn't ensure that the asset exists.
# - It just returns url to the asset if dev server is running
# - Otherwise it returns file path to the asset
def self.asset_uri_from_packer(asset_name)
if dev_server_running?
"#{dev_server_url}/#{public_output_uri_path}#{asset_name}"
else
File.join(packer_public_output_path, asset_name).to_s
end
end

def self.precompile?
return ::Webpacker.config.webpacker_precompile? if using_webpacker_const?
return ::Shakapacker.config.shakapacker_precompile? if using_shakapacker_const?
Expand Down
6 changes: 5 additions & 1 deletion lib/react_on_rails/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,11 @@ def self.react_client_manifest_file_path
return @react_client_manifest_path if @react_client_manifest_path && !Rails.env.development?

file_name = ReactOnRails.configuration.react_client_manifest_file
@react_client_manifest_path = bundle_js_file_path(file_name)
@react_client_manifest_path = if ReactOnRails::PackerUtils.using_packer?
ReactOnRails::PackerUtils.asset_uri_from_packer(file_name)
else
File.join(generated_assets_full_path, file_name)
end
end

def self.running_on_windows?
Expand Down
41 changes: 41 additions & 0 deletions spec/react_on_rails/packer_utils_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,46 @@ module ReactOnRails
expect(described_class.shakapacker_version_requirement_met?(minimum_version)).to be(true)
end
end

describe ".asset_uri_from_packer" do
let(:asset_name) { "test-asset.js" }
let(:public_output_path) { "/path/to/public/webpack/dev" }

context "when dev server is running" do
before do
allow(described_class.packer).to receive(:dev_server).and_return(
instance_double(
Shakapacker::DevServer,
running?: true,
protocol: "http",
host_with_port: "localhost:3035"
)
)

allow(described_class.packer).to receive_message_chain("config.public_output_path")
.and_return(Pathname.new(public_output_path))
allow(described_class.packer).to receive_message_chain("config.public_path")
.and_return(Pathname.new("/path/to/public"))
end

it "returns asset URL with dev server path" do
expected_url = "http://localhost:3035/webpack/dev/test-asset.js"
expect(described_class.asset_uri_from_packer(asset_name)).to eq(expected_url)
end
end

context "when dev server is not running" do
before do
allow(described_class.packer).to receive_message_chain("dev_server.running?").and_return(false)
allow(described_class.packer).to receive_message_chain("config.public_output_path")
.and_return(Pathname.new(public_output_path))
end

it "returns file path to the asset" do
expected_path = File.join(public_output_path, asset_name)
expect(described_class.asset_uri_from_packer(asset_name)).to eq(expected_path)
end
end
end
end
end
67 changes: 67 additions & 0 deletions spec/react_on_rails/utils_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,73 @@ def mock_dev_server_running
end
end
end

describe ".react_client_manifest_file_path" do
before do
described_class.instance_variable_set(:@react_client_manifest_path, nil)
allow(ReactOnRails.configuration).to receive(:react_client_manifest_file)
.and_return("react-client-manifest.json")
end

after do
described_class.instance_variable_set(:@react_client_manifest_path, nil)
end

context "when using packer" do
let(:public_output_path) { "/path/to/public/webpack/dev" }

before do
allow(ReactOnRails::PackerUtils).to receive(:using_packer?).and_return(true)
allow(ReactOnRails::PackerUtils.packer).to receive_message_chain("config.public_output_path")
.and_return(Pathname.new(public_output_path))
allow(ReactOnRails::PackerUtils.packer).to receive_message_chain("config.public_path")
.and_return(Pathname.new("/path/to/public"))
end

context "when dev server is running" do
before do
allow(ReactOnRails::PackerUtils.packer).to receive(:dev_server).and_return(
instance_double(
Object.const_get(ReactOnRails::PackerUtils.packer_type.capitalize)::DevServer,
running?: true,
protocol: "http",
host_with_port: "localhost:3035"
)
)
end

it "returns manifest URL with dev server path" do
expected_url = "http://localhost:3035/webpack/dev/react-client-manifest.json"
expect(described_class.react_client_manifest_file_path).to eq(expected_url)
end
end

context "when dev server is not running" do
before do
allow(ReactOnRails::PackerUtils.packer).to receive_message_chain("dev_server.running?")
.and_return(false)
end

it "returns file path to the manifest" do
expected_path = File.join(public_output_path, "react-client-manifest.json")
expect(described_class.react_client_manifest_file_path).to eq(expected_path)
end
end
end

context "when not using packer" do
before do
allow(ReactOnRails::PackerUtils).to receive(:using_packer?).and_return(false)
allow(described_class).to receive(:generated_assets_full_path)
.and_return("/path/to/generated/assets")
end

it "returns joined path with generated_assets_full_path" do
expect(described_class.react_client_manifest_file_path)
.to eq("/path/to/generated/assets/react-client-manifest.json")
end
end
end
end
end
# rubocop:enable Metrics/ModuleLength, Metrics/BlockLength

0 comments on commit 4e89e93

Please sign in to comment.