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

Add Automated Files System Based Pack Generation #1455

Merged
merged 42 commits into from Aug 21, 2022
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
229c406
Add Pack Generation Rake Task
pulkitkkr May 30, 2022
bab780e
Bump Up Package Version
pulkitkkr May 30, 2022
6be28da
Update Initializer
pulkitkkr May 30, 2022
05758b1
Add Support for Client Entry and Common FIles
pulkitkkr May 31, 2022
e79cf03
Add Support for Server bundle
pulkitkkr Jun 1, 2022
18d10fe
Add Support for Server Bundle
pulkitkkr Jun 3, 2022
18da79d
Remove `react_component_with_bundle` helper
pulkitkkr Jun 17, 2022
41b04ad
Disable eslint for generate packs code
pulkitkkr Jun 17, 2022
7820784
Misc Code Refactoring
pulkitkkr Jun 17, 2022
16a48f3
Update configs for `load_bundle` option for `Rreact_component` view h…
pulkitkkr Jun 17, 2022
6cc1a5c
Fix CI/CD Pipeline
pulkitkkr Jul 7, 2022
40a21f9
Refactor Code
pulkitkkr Jul 7, 2022
bd22714
Add tests for packs generation
pulkitkkr Jul 11, 2022
1878475
Update 'load_bundle' option to 'auto_load_bundle'
pulkitkkr Jul 15, 2022
fbd160d
Update asset:precompile and generate_packs if component pack not present
pulkitkkr Jul 15, 2022
ee76245
Update Test Files
pulkitkkr Jul 15, 2022
017f1e5
Updat Default for `components_dir`, desc for rake task.
pulkitkkr Jul 15, 2022
4859b13
Fix bracket anomalies
pulkitkkr Jul 15, 2022
79f5407
Fix rspecs
pulkitkkr Jul 18, 2022
5e37114
Do not capitalize "file" in test names
alexeyr Jul 20, 2022
e1e6fbf
Fix common_component_to_path
alexeyr Jul 20, 2022
dd90fef
Reuse the regex in common_component_to_path
alexeyr Jul 20, 2022
1470da8
Add Docs
pulkitkkr Jul 20, 2022
c156b01
Address Docs related comments
pulkitkkr Jul 21, 2022
d6c1131
Add Config options
pulkitkkr Jul 22, 2022
45a02f5
Address doc related comments
pulkitkkr Jul 22, 2022
89d0b03
Refactor Test Components
pulkitkkr Jul 22, 2022
5776ad4
Handle Edge Cases for Specificity of Files
pulkitkkr Jul 23, 2022
e53d69b
Update Doc for specificity
pulkitkkr Jul 23, 2022
b9ed7c7
Update docs as per comments
pulkitkkr Jul 23, 2022
4fbe542
Handle Shakapacker Requirements Unmet Issue
pulkitkkr Jul 25, 2022
14917db
Address Grammar issues
pulkitkkr Jul 25, 2022
8e2a9d8
Raise on nested_enteries false
pulkitkkr Jul 26, 2022
d3574d8
Update example component names
pulkitkkr Jul 26, 2022
b0a92ed
Add Misc Error messages for Better Developer Experience
pulkitkkr Jul 27, 2022
31ad0b4
Address Review Comments
pulkitkkr Aug 16, 2022
656ec63
Address Docs related comments
pulkitkkr Aug 17, 2022
66a11bc
Rename `components_directory` to `components_subdirectory`
pulkitkkr Aug 17, 2022
d6fb855
Address Comments
pulkitkkr Aug 17, 2022
fd324b8
Remove static methods from Pack Generator
pulkitkkr Aug 17, 2022
8c755d0
Address Review Comments
pulkitkkr Aug 18, 2022
31eea93
Update Docs
pulkitkkr Aug 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -26,6 +26,8 @@ Changes since last non-beta release.

- Upgraded the example test app in `spec/dummy` to React 18. [PR 1463](https://github.com/shakacode/react_on_rails/pull/1463) by [alexeyr](https://github.com/alexeyr).

- Added file-system-based automatic bundle generation feature. [PR 1455](https://github.com/shakacode/react_on_rails/pull/1455) by [pulkitkkr](https://github.com/pulkitkkr).

#### Fixed
- Correctly unmount roots under React 18. [PR 1466](https://github.com/shakacode/react_on_rails/pull/1466) by [alexeyr](https://github.com/alexeyr).

Expand Down
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -49,8 +49,9 @@ To provide a high performance framework for integrating Ruby on Rails with React
Given that `rails/webpacker` gem already provides basic React integration, why would you use "React on Rails"?

1. Easy passing of props directly from your Rails view to your React components rather than having your Rails view load and then make a separate request to your API.
1. Tight integration with [shakapacker](https://github.com/shakacode/shakapacker) (or it's predecessor [rails/webpacker](https://github.com/rails/webpacker)).
Tight integration with [shakapacker](https://github.com/shakacode/shakapacker) (or it's predecessor [rails/webpacker](https://github.com/rails/webpacker)).
1. Server-Side Rendering (SSR), often used for SEO crawler indexing and UX performance.
1. [Automated optimized entry-point creation and bundle inclusion when placing a component on a page. With this feature, you no longer need to configure `javascript_pack_tags` and `stylesheet_pack_tags` on your layouts based on what’s shown. “It just works!”](https://www.shakacode.com/react-on-rails/docs/guides/file-system-based-automated-bundle-generation.md)
1. [Redux](https://github.com/reactjs/redux) and [React Router](https://github.com/ReactTraining/react-router#readme) integration with server-side-rendering.
1. [Internationalization (I18n) and (localization)](https://www.shakacode.com/react-on-rails/docs/guides/i18n)
1. A supportive community. This [web search shows how live public sites are using React on Rails](https://publicwww.com/websites/%22react-on-rails%22++-undeveloped.com+depth%3Aall/).
Expand Down
3 changes: 2 additions & 1 deletion docs/api/view-helpers-api.md
@@ -1,7 +1,7 @@
# View and Controller Helpers
## View Helpers API

Once the bundled files have been generated in your `app/assets/webpack` folder and you have registered your components, you will want to render these components on your Rails views using the included helper method, `react_component`.
Once the bundled files have been generated in your `app/assets/webpack` folder and you have registered your components, you will want to render these components on your Rails views using the included helper method, [`react_component`](https://www.shakacode.com/react-on-rails/docs/api/view-helpers-api/#react_component).

------------

Expand All @@ -27,6 +27,7 @@ Uncommonly used options:
- **general options:**
- **props:** Ruby Hash which contains the properties to pass to the react object, or a JSON string. If you pass a string, we'll escape it for you.
- **prerender:** enable server-side rendering of a component. Set to false when debugging!
- **auto_load_bundle:** will automatically load bundle for component by calling `append_javascript_pack_tag` and `append_stylesheet_pack_tag` under the hood.
- **id:** Id for the div, will be used to attach the React component. This will get assigned automatically if you do not provide an id. Must be unique.
- **html_options:** Any other HTML options get placed on the added div for the component. For example, you can set a class (or inline style) on the outer div so that it behaves like a span, with the styling of `display:inline-block`. You may also use an option of `tag: "span"` to replace the use of the default DIV tag to be a SPAN tag.
- **trace:** set to true to print additional debugging information in the browser. Defaults to true for development, off otherwise. Only on the **client side** will you will see the `railsContext` and your props.
Expand Down
13 changes: 13 additions & 0 deletions docs/guides/configuration.md
Expand Up @@ -151,6 +151,19 @@ ReactOnRails.configure do |config|
config.server_renderer_pool_size = 1 # increase if you're on JRuby
config.server_renderer_timeout = 20 # seconds

################################################################################
################################################################################
# FILE SYSTEM BASED COMPONENT REGISTRY
################################################################################
# components_subdirectory is the name of the directory that is used to automatically detect and register components
# for the usage on the rails view. default is nil, you can enable the feature by updating it in the next line.
config.components_subdirectory = "ror_components"

# For automated component registry, `render_component` view helper method tries to load bundle for component from
# generated directory. default is false, you can pass option at the time of individual usage or update the default
# in the following line
config.auto_load_bundle = false

################################################################################
# I18N OPTIONS
################################################################################
Expand Down
144 changes: 144 additions & 0 deletions docs/guides/file-system-based-automated-bundle-generation.md
@@ -0,0 +1,144 @@
# File-System-Based Automated Bundle Generation

To use the automated bundle generation feature introduced in React on Rails v13.1.0, please upgrade to use [Shakapacker v6.5.1](https://github.com/shakacode/shakapacker/tree/v6.5.1) at least. If you are currently using webpacker, please follow the migration steps available [here](https://github.com/shakacode/shakapacker/blob/master/docs/v6_upgrade.md).

## Configuration

### Enable nested_entries for Shakapacker
To use the automated bundle generation feature, set nested_entries: true in the webpacker.yml file like this. The generated files will go in a nested directory.

```yml
default:
...
nested_entries: true
```

For more details, see [Configuration and Code](https://github.com/shakacode/shakapacker#configuration-and-code) section in [shakapacker](https://github.com/shakacode/shakapacker/).

### Configure Components Subdirectory
`components_subdirectory` is the name of the directories that contain components that will be automatically registered and used in Rails views.
For example, configure `config/initializers/react_on_rails` to set the name for `components_subdirectory`.·

```rb
config.components_subdirectory = "ror_components"
```

Now all React components inside the directories called `ror_components` will automatically be registered for usage with [`react_component`](https://www.shakacode.com/react-on-rails/docs/api/view-helpers-api/#react_component) and [`react_component_hash`](https://www.shakacode.com/react-on-rails/docs/api/view-helpers-api/#react_component_hash) helper methods provided by React on Rails.

### Configure `auto_load_bundle` Option

For automated component registry, [`react_component`](https://www.shakacode.com/react-on-rails/docs/api/view-helpers-api/#react_component) and [`react_component_hash`](https://www.shakacode.com/react-on-rails/docs/api/view-helpers-api/#react_component_hash) view helper method tries to load generated bundle for component from the generated directory automatically per `auto_load_bundle` option. `auto_load_bundle` option in `config/initializers/react_on_rails` configures the default value that will be passed to component helpers. The default is `false`, and the parameter can be passed explicitly for each call.

You can change the value in `config/initializers/react_on_rails` by updating it as follows:

```rb
config.auto_load_bundle = true
```

### Update `.gitignore` file
React on Rails automatically generates pack files for components to be registered in the `packs/generated` directory. To avoid committing generated files into the version control system, please update `.gitignore` to have

```gitignore
# Generated React on Rails packs
app/javascript/packs/generated
```

*Note: the directory might be different depending on the `source_entry_path` in `config/webpacker.yml`.*

## Usage

### Basic usage

If the `webpacker.yml` file is configured as instructed [here](https://github.com/shakacode/shakapacker#configuration-and-code), with the following configurations

```yml
default: &default
source_path: app/javascript
source_entry_path: packs
public_root_path: public
public_output_path: packs
nested_entries: true
# And more
```

the directory structure will look like this
```
app/javascript:
└── packs: # sets up webpack entries
│ └── application.js # references FooComponentOne.jsx, BarComponentOne.jsx and BarComponentTwo.jsx in `../src`
└── src: # any directory name is fine. Referenced files need to be under source_path
│ └── Foo
│ │ └── ...
│ │ └── FooComponentOne.jsx
│ └── Bar
│ │ └── ...
│ │ └── BarComponentOne.jsx
│ │ └── BarComponentTwo.jsx
└── stylesheets:
│ └── my_styles.css
└── images:
└── logo.svg
```

Previously, the`application.js` file would manually register `FooComponentOne`, `BarComponentOne` and `BarComponentTwo` using `ReactOnRails.register` as follows:
```jsx
import ReactOnRails from 'react-on-rails';
import FooComponentOne from '../src/Foo/FooComponentOne';
import BarComponentOne from '../src/Foo/BarComponentOne';
import BarComponentTwo from '../src/Foo/BarComponentTwo';

ReactOnRails.register({ FooComponentOne, BarComponentOne, BarComponentTwo });
```

Now, to automatically register `FooComponentOne`, `BarComponentOne` and `BarComponentTwo` for the usage with [`react_component`](https://www.shakacode.com/react-on-rails/docs/api/view-helpers-api/#react_component) and [`react_component_hash`](https://www.shakacode.com/react-on-rails/docs/api/view-helpers-api/#react_component_hash) helpers, delete the `application.js` file and create a directory structure as mentioned below:

```
app/javascript:
└── packs
└── src:
│ └── Foo
│ │ └── ...
│ │ └── ror_components # configured as `components_subdirectory`
│ │ └── FooComponentOne.jsx
│ └── Bar
│ │ └── ...
│ │ └── ror_components # configured as `components_subdirectory`
│ │ │ └── BarComponentOne.jsx
│ │ │ └── BarComponentTwo.jsx
```

To register a React component, creating a pack entry and manually registering it by calling `ReactOnRails.register` is no longer needed. With automatically generated packs, you can directly use `FooComponentOne`, `BarComponentOne` and `BarComponentTwo` in Rails view using:

```erb
<%= react_component("FooComponentOne", {}, auto_load_bundle: true) %>
<%= react_component("BarComponentOne", {}, auto_load_bundle: true) %>
<%= react_component("BarComponentTwo", {}, auto_load_bundle: true) %>
```

If `FooComponentOne` uses multiple HTML strings for server rendering, the [`react_component_hash`](https://www.shakacode.com/react-on-rails/docs/api/view-helpers-api/#react_component_hash) view helper can be used on the Rails view, as illustrated below.

```erb
<% foo_component_one_data = react_component_hash("FooComponentOne",
prerender: true,
auto_load_bundle: true
props: {}
) %>
<% content_for :title do %>
<%= foo_component_one_data['title'] %>
<% end %>
<%= foo_component_one_data["componentHtml"] %>
```
alexeyr-ci1 marked this conversation as resolved.
Show resolved Hide resolved

The default value of the `auto_load_bundle` parameter can be specified by setting `config.auto_load_bundle` in `config/initializers/react_on_rails.rb` and thus removed from each call to `react_component`.

### Server Rendering and Client Rendering Components

If server rendering is enabled, the component will be registered for usage both in server and client rendering. In order to have separate definitions for client and server rendering, name the component files as `ComponentName.server.jsx` and `ComponentName.client.jsx`. The `ComponentName.server.jsx` file will be used for server rendering and the `ComponentName.client.jsx` file for client rendering. If you don't want the component rendered on the server, you should only have the `ComponentName.client.jsx` file.

*Note: If specifying separate definitions for client and server rendering, please make sure to delete the generalized `ComponentName.jsx` file.*

### Using Automated Bundle Generation Feature with already defined packs

To use the Automated Bundle Generation feature with already defined packs, `config/initializers/react_on_rails` should explicitly be configured with `config.auto_load_bundle = false` and you can explicitly pass `auto_load_bundle` option in [`react_component`](https://www.shakacode.com/react-on-rails/docs/api/view-helpers-api/#react_component) and [`react_component_hash`](https://www.shakacode.com/react-on-rails/docs/api/view-helpers-api/#react_component_hash) for the components using this feature.


Expand Up @@ -42,4 +42,17 @@
# React components.
#
config.server_bundle_js_file = "server-bundle.js"

################################################################################
################################################################################
# FILE SYSTEM BASED COMPONENT REGISTRY
################################################################################
# components_subdirectory is the directory that is used to automatically detect and register components for the usage
# on the rails view. default is nil, you can enable the feature by updating it in the next line.
# config.components_subdirectory = "ror_components"
#
# For automated component registry, `render_component` view helper method tries to load bundle for component from
# generated directory. default is false, you can pass option at the time of individual usage or update the default
# in the following line
config.auto_load_bundle = false
end
1 change: 1 addition & 0 deletions lib/react_on_rails.rb
Expand Up @@ -19,6 +19,7 @@
require "react_on_rails/git_utils"
require "react_on_rails/utils"
require "react_on_rails/webpacker_utils"
require "react_on_rails/packs_generator"
require "react_on_rails/test_helper/webpack_assets_compiler"
require "react_on_rails/test_helper/webpack_assets_status_checker"
require "react_on_rails/test_helper/ensure_assets_compiled"
Expand Down
15 changes: 11 additions & 4 deletions lib/react_on_rails/configuration.rb
Expand Up @@ -7,6 +7,7 @@ def self.configure
end

DEFAULT_GENERATED_ASSETS_DIR = File.join(%w[public webpack], Rails.env).freeze
DEFAULT_COMPONENTS_SUBDIRECTORY = nil
DEFAULT_SERVER_RENDER_TIMEOUT = 20
DEFAULT_POOL_SIZE = 1
DEFAULT_RANDOM_DOM_ID = true # for backwards compatability
Expand All @@ -19,6 +20,7 @@ def self.configuration
generated_assets_dir: "",
server_bundle_js_file: "",
prerender: false,
auto_load_bundle: false,
replay_console: true,
logging_on_server: true,
raise_on_prerender_error: Rails.env.development?,
Expand All @@ -36,7 +38,8 @@ def self.configuration
build_production_command: "",
random_dom_id: DEFAULT_RANDOM_DOM_ID,
same_bundle_for_client_and_server: false,
i18n_output_format: nil
i18n_output_format: nil,
components_subdirectory: DEFAULT_COMPONENTS_SUBDIRECTORY
)
end

Expand All @@ -49,8 +52,8 @@ class Configuration
:webpack_generated_files, :rendering_extension, :build_test_command,
:build_production_command,
:i18n_dir, :i18n_yml_dir, :i18n_output_format,
:server_render_method, :random_dom_id,
:same_bundle_for_client_and_server, :rendering_props_extension
:server_render_method, :random_dom_id, :auto_load_bundle,
:same_bundle_for_client_and_server, :rendering_props_extension, :components_subdirectory

# rubocop:disable Metrics/AbcSize
def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender: nil,
Expand All @@ -64,7 +67,8 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender
build_production_command: nil,
same_bundle_for_client_and_server: nil,
i18n_dir: nil, i18n_yml_dir: nil, i18n_output_format: nil,
random_dom_id: nil, server_render_method: nil, rendering_props_extension: nil)
random_dom_id: nil, server_render_method: nil, rendering_props_extension: nil,
components_subdirectory: nil, auto_load_bundle: nil)
self.node_modules_location = node_modules_location.present? ? node_modules_location : Rails.root
self.generated_assets_dirs = generated_assets_dirs
self.generated_assets_dir = generated_assets_dir
Expand Down Expand Up @@ -98,6 +102,8 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender
self.rendering_extension = rendering_extension

self.server_render_method = server_render_method
self.components_subdirectory = components_subdirectory
self.auto_load_bundle = auto_load_bundle
end
# rubocop:enable Metrics/AbcSize

Expand Down Expand Up @@ -125,6 +131,7 @@ def adjust_precompile_task
ENV["WEBPACKER_PRECOMPILE"] = "false"

precompile_tasks = lambda {
Rake::Task["react_on_rails:generate_packs"].invoke
Rake::Task["react_on_rails:assets:webpack"].invoke
puts "Invoking task webpacker:clean from React on Rails"

Expand Down