Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate Pack if Missing Generated Pack for Registered Component #1506

Merged
merged 13 commits into from
Jan 28, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Changes since last non-beta release.
*Please add entries here for your pull requests that are not yet released.*
- Added `./bin/dev` and `./bin/dev-static` executables to ease and standardize running the dev server. [PR 1491](https://github.com/shakacode/react_on_rails/pull/1491) by [ahangarha](https://github.com/ahangarha)
- Fixed pack not found warning while using `react_component` and `react_component_hash` helpers, even when corresponding chunks are present. [PR 1511](https://github.com/shakacode/react_on_rails/pull/1511) by [pulkitkkr](https://github.com/pulkitkkr)
- Fixed FS-based packs generation functionality to trigger pack generation on the creation of a new react component inside `components_subdirectory`. [PR 1506](https://github.com/shakacode/react_on_rails/pull/1506) by [pulkitkkr](https://github.com/pulkitkkr)

### [13.2.0] - 2022-12-23

Expand Down
47 changes: 23 additions & 24 deletions lib/react_on_rails/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,27 +93,6 @@ def react_component(component_name, options = {})
end
end

def load_pack_for_component(component_name)
is_component_pack_present = File.exist?(generated_components_pack_path(component_name))
is_development = ENV["RAILS_ENV"] == "development"

if is_development && !is_component_pack_present
ReactOnRails::PacksGenerator.generate
raise_generated_missing_pack_warning(component_name)
end

ReactOnRails::PacksGenerator.raise_nested_entries_disabled unless ReactOnRails::WebpackerUtils.nested_entries?

append_javascript_pack_tag "generated/#{component_name}"
append_stylesheet_pack_tag "generated/#{component_name}"
end

def generated_components_pack_path(component_name)
extension = PacksGenerator::GENERATED_PACK_EXTENSION

"#{ReactOnRails::WebpackerUtils.webpacker_source_entry_path}/generated/#{component_name}.#{extension}"
end

# react_component_hash is used to return multiple HTML strings for server rendering, such as for
# adding meta-tags to a page.
# It is exactly like react_component except for the following:
Expand Down Expand Up @@ -340,6 +319,27 @@ def rails_context(server_side: true)

private

def load_pack_for_component(component_name)
is_component_pack_present = File.exist?(generated_components_pack_path(component_name))
is_development = ENV["RAILS_ENV"] == "development"

if is_development && !is_component_pack_present
ReactOnRails::PacksGenerator.generate
raise_missing_pack_error(component_name)
end

ReactOnRails::PacksGenerator.raise_nested_entries_disabled unless ReactOnRails::WebpackerUtils.nested_entries?

append_javascript_pack_tag "generated/#{component_name}"
append_stylesheet_pack_tag "generated/#{component_name}"
end

def generated_components_pack_path(component_name)
extension = PacksGenerator::GENERATED_PACK_EXTENSION

"#{ReactOnRails::WebpackerUtils.webpacker_source_entry_path}/generated/#{component_name}.#{extension}"
end

def build_react_component_result_for_server_rendered_string(
server_rendered_html: required("server_rendered_html"),
component_specification_tag: required("component_specification_tag"),
Expand Down Expand Up @@ -555,10 +555,9 @@ def initialize_redux_stores
result
end

def raise_generated_missing_pack_warning(component_name)
def raise_missing_pack_error(component_name)
msg = <<~MSG
**ERROR** ReactOnRails: Generated missing pack for Component: #{component_name}. Please refresh the webpage \
once webpack has finished generating the bundles. If the problem persists
**ERROR** ReactOnRails: Generated missing pack for Component: #{component_name}. Please restart the webpack dev server or webpack in watch mode, to ensure webpack generates the chunks corresponding to #{component_name} component. If the problem persists
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this error message be detected in some tests?

1. Verify `components_subdirectory` is configured in `config/initializers/react_on_rails`.
2. Component: #{component_name} is placed inside the configured `components_subdirectory`.
MSG
Expand Down
23 changes: 19 additions & 4 deletions lib/react_on_rails/packs_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,11 @@ def self.raise_nested_entries_disabled
def verify_setup_and_generate_packs
return unless components_subdirectory.present?

raise_webpacker_not_installed unless ReactOnRails::WebpackerUtils.using_webpacker?
raise_shakapacker_version_incompatible unless shackapacker_version_requirement_met?
raise_nested_entries_disabled unless ReactOnRails::WebpackerUtils.nested_entries?
verify_configuration

is_generated_directory_present = Dir.exist?(generated_packs_directory_path)

return if is_generated_directory_present && webpack_assets_status_checker.stale_generated_component_packs.empty?
return if is_generated_directory_present && !stale_or_missing_packs?

clean_generated_packs_directory
generate_packs
Expand Down Expand Up @@ -297,6 +295,23 @@ def prepend_to_file_if_not_present(file, text_to_prepend)
File.write(file, content_with_prepended_text)
puts "Prepended\n#{text_to_prepend}to #{file}."
end

def stale_or_missing_packs?
component_files = common_component_to_path.values + client_component_to_path.values
most_recent_mtime = Utils.find_most_recent_mtime(component_files)

component_files.each_with_object([]).any? do |file|
path = generated_pack_path(file)

!File.exist?(path) || File.mtime(path) < most_recent_mtime
end
end

def verify_configuration
raise_webpacker_not_installed unless ReactOnRails::WebpackerUtils.using_webpacker?
raise_shakapacker_version_incompatible unless shackapacker_version_requirement_met?
raise_nested_entries_disabled unless ReactOnRails::WebpackerUtils.nested_entries?
end
end
# rubocop:enable Metrics/ClassLength
end
21 changes: 1 addition & 20 deletions lib/react_on_rails/test_helper/webpack_assets_status_checker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,13 @@ def stale_generated_webpack_files
stale_generated_files(client_files)
end

def stale_generated_component_packs
stale_generated_files(component_pack_files)
end

def stale_generated_files(files)
manifest_needed = ReactOnRails::WebpackerUtils.using_webpacker? &&
!ReactOnRails::WebpackerUtils.manifest_exists?

return ["manifest.json"] if manifest_needed

most_recent_mtime = find_most_recent_mtime(files)
most_recent_mtime = Utils.find_most_recent_mtime(files)
all_compiled_assets.each_with_object([]) do |webpack_generated_file, stale_gen_list|
if !File.exist?(webpack_generated_file) ||
File.mtime(webpack_generated_file) < most_recent_mtime
Expand All @@ -51,13 +47,6 @@ def stale_generated_files(files)

private

def find_most_recent_mtime(files)
files.reduce(1.year.ago) do |newest_time, file|
mt = File.mtime(file)
mt > newest_time ? mt : newest_time
end
end

def all_compiled_assets
@all_compiled_assets ||= begin
webpack_generated_files = @webpack_generated_files.map do |bundle_name|
Expand Down Expand Up @@ -89,14 +78,6 @@ def client_files
@client_files ||= make_file_list(make_globs(source_path)).to_ary
end

def component_pack_files
make_file_list(make_globs(components_search_path)).to_ary
end

def components_search_path
"#{source_path}/**/#{ReactOnRails.configuration.components_subdirectory}"
end

def make_globs(dirs)
Array(dirs).map { |dir| File.join(dir, "**", "*") }
end
Expand Down
7 changes: 7 additions & 0 deletions lib/react_on_rails/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -197,5 +197,12 @@ def self.smart_trim(str, max_length = 1000)
rstrip = to_remove - lstrip
str[0..(midpoint - lstrip - 1)] + TRUNCATION_FILLER + str[(midpoint + rstrip)..-1]
end

def self.find_most_recent_mtime(files)
files.reduce(1.year.ago) do |newest_time, file|
mt = File.mtime(file)
mt > newest_time ? mt : newest_time
end
end
end
end
41 changes: 41 additions & 0 deletions spec/dummy/spec/packs_generator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

# rubocop:disable Metrics/ModuleLength
module ReactOnRails
GENERATED_PACKS_CONSOLE_OUTPUT_TEXT = "Generated Packs:"

# rubocop:disable Metrics/BlockLength
describe PacksGenerator do
let(:webpacker_source_path) { File.expand_path("fixtures/automated_packs_generation", __dir__) }
Expand Down Expand Up @@ -252,6 +254,45 @@ module ReactOnRails
end
end

context "when pack generator is called" do
let(:component_name) { "ComponentWithCommonOnly" }
let(:component_pack) { "#{generated_directory}/#{component_name}.js" }

before do
stub_webpacker_source_path(component_name: component_name,
webpacker_source_path: webpacker_source_path)
end

it "does not generate packs if there are no new components or stale files" do
expect { described_class.generate }.to output(GENERATED_PACKS_CONSOLE_OUTPUT_TEXT).to_stdout

expect { described_class.generate }.not_to output(GENERATED_PACKS_CONSOLE_OUTPUT_TEXT).to_stdout
end

it "generate packs if a new component is added" do
expect { described_class.generate }.to output(GENERATED_PACKS_CONSOLE_OUTPUT_TEXT).to_stdout

create_new_component("NewComponent")

expect { described_class.generate }.to output(GENERATED_PACKS_CONSOLE_OUTPUT_TEXT).to_stdout
end

it "generate packs if an old component is updated" do
expect { described_class.generate }.to output(GENERATED_PACKS_CONSOLE_OUTPUT_TEXT).to_stdout

create_new_component(component_name)

expect { described_class.generate }.to output(GENERATED_PACKS_CONSOLE_OUTPUT_TEXT).to_stdout
end

def create_new_component(name)
components_subdirectory = ReactOnRails.configuration.components_subdirectory
path = "#{webpacker_source_path}/components/#{component_name}/#{components_subdirectory}/#{name}.jsx"

File.write(path, "// Empty Test Component\n")
end
end

def generated_server_bundle_file_path
"#{webpacker_source_entry_path}/server-bundle-generated.js"
end
Expand Down