Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
9721c36
feat: migrate to React on Rails v16 auto-registration
ihabadham Sep 18, 2025
d12c40d
fix import paths
ihabadham Sep 18, 2025
5854552
feat: complete React on Rails v16 auto-registration migration
ihabadham Sep 18, 2025
6a2e29b
revert commenting out rails server
ihabadham Sep 18, 2025
71a60b2
add ror generated packs to .gitignore
ihabadham Sep 18, 2025
027bfd7
fix: add React on Rails pack generation to CI workflow
ihabadham Sep 18, 2025
a9c10e9
fix: update import paths in serverRegistration.jsx for React on Rails…
ihabadham Sep 18, 2025
0d04fa2
fix: add React on Rails pack generation to Rspec CI workflow
ihabadham Sep 18, 2025
fe066fb
fix: add React on Rails pack generation to Lint CI workflow
ihabadham Sep 18, 2025
9b9bb86
ignore .claude
ihabadham Sep 23, 2025
d6d123e
fix: add missing trailing newlines to JS files
ihabadham Sep 24, 2025
81f84aa
fix: move JavaScript pack tags to head element to resolve Turbo warning
justin808 Sep 28, 2025
2547b80
fix: resolve Shakapacker append/pack tag ordering issue
justin808 Sep 28, 2025
9d2bf5e
Revert "fix: resolve Shakapacker append/pack tag ordering issue"
justin808 Sep 28, 2025
ee5bf38
Revert "fix: move JavaScript pack tags to head element to resolve Tur…
justin808 Sep 28, 2025
ab31233
docs: document Turbo warning issue for future investigation
justin808 Sep 28, 2025
4daa2ec
fix: reorder tags in stimulus_layout.html.erb for consistency
justin808 Sep 29, 2025
351824b
fix: restore original stimulus_layout.html.erb
justin808 Sep 29, 2025
58846b0
fix: update stimulus_layout to use auto-registration pattern
justin808 Sep 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/js_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,8 @@ jobs:
- name: Build i18n libraries
run: bundle exec rake react_on_rails:locale

- name: Generate React on Rails packs
run: bundle exec rails react_on_rails:generate_packs

- name: Run js tests
run: bundle exec rake ci:js
3 changes: 3 additions & 0 deletions .github/workflows/lint_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,8 @@ jobs:
- name: Build i18n libraries
run: bundle exec rake react_on_rails:locale

- name: Generate React on Rails packs
run: bundle exec rails react_on_rails:generate_packs

- name: Run lint
run: bundle exec rake lint
3 changes: 3 additions & 0 deletions .github/workflows/rspec_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ jobs:
- name: Build i18n libraries
run: bundle exec rake react_on_rails:locale

- name: Generate React on Rails packs
run: bundle exec rails react_on_rails:generate_packs

- name: Build Rescript components
run: yarn res:build

Expand Down
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,7 @@ client/app/bundles/comments/rescript/**/*.bs.js
# Using React on Rails default directory
/ssr-generated/

# Generated files
/client/app/generated/
# Generated React on Rails packs
**/generated/**

.claude/
2 changes: 1 addition & 1 deletion Procfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
# You can run these commands in separate shells
rescript: yarn res:dev
redis: redis-server
rails: bin/rails s -p 3000
rails: bundle exec rails s -p 3000
wp-client: HMR=true RAILS_ENV=development NODE_ENV=development bin/shakapacker-dev-server
wp-server: bundle exec rake react_on_rails:locale && HMR=true SERVER_BUNDLE_ONLY=yes bin/shakapacker --watch
Comment on lines +5 to 7
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Prevent startup race: generate packs before webpack processes start

With v16 auto-registration, webpack can error if wp-client/wp-server start before packs exist. Prepend generate_packs to both webpack commands (and optionally Rails) and set HOST/PORT defaults for container/devbox access.

-rails: bundle exec rails s -p 3000
+rails: bash -lc 'bundle exec rails react_on_rails:generate_packs || true; bundle exec rails s -p ${PORT:-3000} -b ${HOST:-0.0.0.0}'

-wp-client: HMR=true RAILS_ENV=development NODE_ENV=development bin/shakapacker-dev-server
+wp-client: bash -lc 'bundle exec rails react_on_rails:generate_packs && HMR=true RAILS_ENV=development NODE_ENV=development bin/shakapacker-dev-server'

-wp-server: bundle exec rake react_on_rails:locale && HMR=true SERVER_BUNDLE_ONLY=yes bin/shakapacker --watch
+wp-server: bash -lc 'bundle exec rails react_on_rails:generate_packs && bundle exec rake react_on_rails:locale && HMR=true SERVER_BUNDLE_ONLY=yes bin/shakapacker --watch'

Based on learnings.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
rails: bundle exec rails s -p 3000
wp-client: HMR=true RAILS_ENV=development NODE_ENV=development bin/shakapacker-dev-server
wp-server: bundle exec rake react_on_rails:locale && HMR=true SERVER_BUNDLE_ONLY=yes bin/shakapacker --watch
rails: bash -lc 'bundle exec rails react_on_rails:generate_packs || true; bundle exec rails s -p ${PORT:-3000} -b ${HOST:-0.0.0.0}'
wp-client: bash -lc 'bundle exec rails react_on_rails:generate_packs && HMR=true RAILS_ENV=development NODE_ENV=development bin/shakapacker-dev-server'
wp-server: bash -lc 'bundle exec rails react_on_rails:generate_packs && bundle exec rake react_on_rails:locale && HMR=true SERVER_BUNDLE_ONLY=yes bin/shakapacker --watch'
🤖 Prompt for AI Agents
In Procfile.dev around lines 5 to 7, webpack can race with Rails because packs
may not exist yet and HMR servers may not be reachable from containers/devboxes;
fix by prepending a generate_packs step to the wp-client and wp-server commands
(and optionally to the rails command) so packs are created before starting
webpack processes, and set HOST and PORT defaults for container/devbox access
(e.g., HOST=0.0.0.0 and an appropriate PORT such as 3035) by exporting or
prefixing those env vars on the webpack/rails lines so the dev servers bind to
the container interface.

29 changes: 29 additions & 0 deletions TODO_TURBO_WARNING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# TODO: Fix Turbo Warning in Future PR

## Issue
There's a console warning about Turbo being loaded from within the `<body>` element instead of the `<head>`.

## Root Cause
Conflicting requirements between three systems:
1. **Turbo** - Wants to be loaded in `<head>` to avoid re-evaluation on page changes
2. **Shakapacker** - Requires all `append_javascript_pack_tag` calls to happen before the final `javascript_pack_tag`
3. **React on Rails** - The `react_component` helper internally calls `append_javascript_pack_tag` when rendering components in the body

## Attempted Solutions That Failed
1. Moving `javascript_pack_tag` to head - Breaks because `react_component` calls come after it
2. Using `data-turbo-suppress-warning` - Doesn't properly suppress the warning

## Potential Future Solutions
1. Extract Turbo into a separate pack from stimulus-bundle and load it in the head
2. Use `prepend_javascript_pack_tag` instead of `append` for component registration
3. Configure React on Rails v16 to use a different component loading strategy
4. Investigate if the auto-registration feature has a different recommended pack loading pattern

## Current State
The application works correctly with the pack tags at the end of the body. The Turbo warning is cosmetic and doesn't affect functionality.

## References
- PR #649: Initial v16 migration
- Shakapacker docs: https://github.com/shakacode/shakapacker#view-helper-append_javascript_pack_tag
- Turbo docs: https://turbo.hotwired.dev/handbook/building#working-with-script-elements
- React on Rails v16 docs: https://www.shakacode.com/react-on-rails/docs/
13 changes: 6 additions & 7 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RailsReactTutorial</title>

<%= stylesheet_pack_tag('client-bundle',
media: 'all',
'data-turbolinks-track': true) %>

<%= javascript_pack_tag('client-bundle',
'data-turbolinks-track': true,
defer: true) %>
<%= append_stylesheet_pack_tag('stimulus-bundle') %>
<%= append_javascript_pack_tag('stimulus-bundle') %>
<%= append_javascript_pack_tag('stores-registration') %>

<%= csrf_meta_tags %>
</head>
Expand All @@ -24,6 +20,9 @@

<%= react_component "Footer" %>

<%= stylesheet_pack_tag(media: 'all', 'data-turbolinks-track': true) %>
<%= javascript_pack_tag('data-turbolinks-track': true, defer: true) %>
Copy link
Preview

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

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

The stylesheet_pack_tag and javascript_pack_tag calls are missing the required pack name parameter. These should specify which pack to load, such as stylesheet_pack_tag('stimulus-bundle', media: 'all', 'data-turbolinks-track': true).

Suggested change
<%= javascript_pack_tag('data-turbolinks-track': true, defer: true) %>
<%= javascript_pack_tag('application', 'data-turbolinks-track': true, defer: true) %>

Copilot uses AI. Check for mistakes.


<!-- This is a placeholder for ReactOnRails to know where to render the store props for
client side hydration -->
<%= redux_store_hydration_data %>
Expand Down
13 changes: 6 additions & 7 deletions app/views/layouts/stimulus_layout.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RailsReactTutorial</title>

<%= stylesheet_pack_tag('client-bundle',
media: 'all',
'data-turbolinks-track': true) %>

<%= javascript_pack_tag('stimulus-bundle',
'data-turbolinks-track': true,
defer: true) %>
<%= append_stylesheet_pack_tag('stimulus-bundle') %>
<%= append_javascript_pack_tag('stimulus-bundle') %>

<%= csrf_meta_tags %>
</head>
Expand All @@ -23,5 +18,9 @@
</div>

<%= react_component "Footer" %>

<%= stylesheet_pack_tag(media: 'all', 'data-turbolinks-track': true) %>
<%= javascript_pack_tag('data-turbolinks-track': true, defer: true) %>
</body>
</html>

Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import SelectLanguage from 'libs/i18n/selectLanguage';
import { defaultMessages, defaultLocale } from 'libs/i18n/default';
import { translations } from 'libs/i18n/translations';

import CommentForm from '../CommentBox/CommentForm/CommentForm';
import CommentList from '../CommentBox/CommentList/CommentList';
import css from './SimpleCommentScreen.module.scss';
import CommentForm from '../../CommentBox/CommentForm/CommentForm';
import CommentList from '../../CommentBox/CommentList/CommentList';
import css from '../SimpleCommentScreen.module.scss';

class SimpleCommentScreen extends BaseComponent {
constructor(props) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Generated by ReScript, PLEASE EDIT WITH CARE
Copy link
Preview

Copilot AI Sep 28, 2025

Choose a reason for hiding this comment

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

This comment indicates this is a generated file, but it's being committed to version control. Generated files should typically be in .gitignore unless they're meant to be manually edited. Consider clarifying whether this file should be version controlled or generated during build.

Suggested change
// Generated by ReScript, PLEASE EDIT WITH CARE
// This file is generated by ReScript but is intentionally committed to version control and may be manually edited.

Copilot uses AI. Check for mistakes.

Copy link
Preview

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

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

This comment suggests the file is auto-generated and should not be edited, but it's being added to version control in a PR. Auto-generated files should typically be excluded from version control or the warning should be removed if manual editing is expected.

Suggested change
// Generated by ReScript, PLEASE EDIT WITH CARE

Copilot uses AI. Check for mistakes.


import * as React from "react";
import * as Header from "../../Header/Header.bs.js";
import * as Actions from "../../Actions/Actions.bs.js";
import * as AlertError from "../../CommentList/AlertError/AlertError.bs.js";
import * as ActionCable from "../../bindings/ActionCable.bs.js";
import * as CommentForm from "../../CommentForm/CommentForm.bs.js";
import * as CommentList from "../../CommentList/CommentList.bs.js";
import * as JsxRuntime from "react/jsx-runtime";

function reducer(param, action) {
if (typeof action !== "object") {
return {
commentsFetchStatus: "FetchError"
};
} else {
return {
commentsFetchStatus: {
TAG: "CommentsFetched",
_0: action._0
}
};
}
}

function ReScriptShow$default(props) {
var match = React.useReducer(reducer, {
commentsFetchStatus: {
TAG: "CommentsFetched",
_0: []
}
});
var dispatch = match[1];
var fetchData = async function () {
var comments = await Actions.Fetch.fetchComments();
if (comments.TAG === "Ok") {
return dispatch({
TAG: "SetComments",
_0: comments._0
});
} else {
return dispatch("SetFetchError");
}
};
var subscribeToCommentsChannel = function () {
return ActionCable.subscribeConsumer("CommentsChannel", {
connected: (function () {
console.log("Connected");
}),
received: (function (data) {
dispatch({
TAG: "SetComments",
_0: [data]
});
}),
disconnected: (function () {
console.log("Disconnected");
})
});
};
React.useEffect((function () {
var scription = subscribeToCommentsChannel();
return (function () {
scription.unsubscribe();
});
}), []);
React.useEffect((function () {
fetchData();
}), []);
var comments = match[0].commentsFetchStatus;
var tmp;
tmp = typeof comments !== "object" ? JsxRuntime.jsx(AlertError.make, {
errorMsg: "Can't fetch the comments!"
}) : JsxRuntime.jsx(CommentList.make, {
comments: comments._0
});
return JsxRuntime.jsxs("div", {
children: [
JsxRuntime.jsxs("h2", {
children: [
"Rescript + Rails Backend (with ",
JsxRuntime.jsx("a", {
children: "react_on_rails gem",
href: "https://github.com/shakacode/react_on_rails"
}),
")"
]
}),
JsxRuntime.jsx(Header.make, {}),
JsxRuntime.jsxs("div", {
children: [
JsxRuntime.jsx("h2", {
children: "Comments"
}),
JsxRuntime.jsxs("ul", {
children: [
JsxRuntime.jsx("li", {
children: "Text supports Github Flavored Markdown."
}),
JsxRuntime.jsx("li", {
children: "Comments older than 24 hours are deleted."
}),
JsxRuntime.jsx("li", {
children: "Name is preserved. Text is reset, between submits"
}),
JsxRuntime.jsx("li", {
children: "To see Action Cable instantly update two browsers, open two browsers and submit a comment!"
})
]
}),
JsxRuntime.jsx(CommentForm.make, {
fetchData: fetchData
}),
tmp
],
className: "prose max-w-none prose-a:text-sky-700 prose-li:my-0"
})
]
});
}

var $$default = ReScriptShow$default;

export {
reducer ,
$$default as default,
}
/* react Not a pure module */
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Provider } from 'react-redux';
import React from 'react';
import ReactOnRails from 'react-on-rails';

import NonRouterCommentsContainer from '../containers/NonRouterCommentsContainer.jsx';
import NonRouterCommentsContainer from '../../../containers/NonRouterCommentsContainer.jsx';
import 'intl/locale-data/jsonp/en';
import 'intl/locale-data/jsonp/de';
import 'intl/locale-data/jsonp/ja';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { Provider } from 'react-redux';
import React from 'react';
import ReactOnRails from 'react-on-rails';

import NavigationBar from '../components/NavigationBar/NavigationBar.jsx';
import NavigationBarContainer from '../containers/NavigationBarContainer.jsx';
import * as paths from '../constants/paths';
import NavigationBar from '../../../components/NavigationBar/NavigationBar.jsx';
import NavigationBarContainer from '../../../containers/NavigationBarContainer.jsx';
import * as paths from '../../../constants/paths';

/*
* Export a function that returns a ReactComponent, depending on a store named SharedReduxStore.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Compare to ../ServerRouterApp.jsx
// Compare to ./RouterApp.server.jsx
Copy link
Preview

Copilot AI Sep 28, 2025

Choose a reason for hiding this comment

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

The comment references './RouterApp.server.jsx' but the actual file appears to be located at the same directory level. Verify that this relative path is correct for the new directory structure.

Copilot uses AI. Check for mistakes.

import { Provider } from 'react-redux';
import React from 'react';
import ReactOnRails from 'react-on-rails';
import { BrowserRouter } from 'react-router-dom';
import routes from '../routes/routes.jsx';
import routes from '../../../routes/routes.jsx';

function ClientRouterApp(_props) {
const store = ReactOnRails.getStore('routerCommentsStore');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Compare to ../ClientRouterApp.jsx
// Compare to ./RouterApp.client.jsx
Copy link
Preview

Copilot AI Sep 28, 2025

Choose a reason for hiding this comment

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

The comment references './RouterApp.client.jsx' but the actual file appears to be located at the same directory level. Verify that this relative path is correct for the new directory structure.

Copilot uses AI. Check for mistakes.

import { Provider } from 'react-redux';
import React from 'react';
import { StaticRouter } from 'react-router-dom/server';
import ReactOnRails from 'react-on-rails';
import routes from '../routes/routes.jsx';
import routes from '../../../routes/routes.jsx';

function ServerRouterApp(_props, railsContext) {
const store = ReactOnRails.getStore('routerCommentsStore');
Expand Down
10 changes: 5 additions & 5 deletions client/app/bundles/comments/startup/serverRegistration.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Example of React + Redux
import ReactOnRails from 'react-on-rails';

import App from './App';
import RouterApp from './ServerRouterApp';
import SimpleCommentScreen from '../components/SimpleCommentScreen/SimpleCommentScreen';
import NavigationBarApp from './NavigationBarApp';
import App from './App/ror_components/App';
import RouterApp from './RouterApp/ror_components/RouterApp.server';
import SimpleCommentScreen from '../components/SimpleCommentScreen/ror_components/SimpleCommentScreen';
import NavigationBarApp from './NavigationBarApp/ror_components/NavigationBarApp';
Comment on lines +4 to +7
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

If using auto-registration, this file should not manually register components/stores.

With v16 generated packs, components under ror_components are auto-registered. Keeping manual ReactOnRails.register and registerStore here can cause duplicate registrations if this file is ever imported. Prefer removing manual registrations (and the store imports) or delete this file entirely. If you need server-only variants (e.g., RouterApp.server), let the generator handle it.

Please run:

If unused, remove this file; if used for a special case, strip store registration and rely on packs/stores-registration.js. Based on learnings.

Also applies to: 10-10


🏁 Script executed:

#!/bin/bash
# 1) Verify whether this file is referenced anywhere
rg -nP 'bundles/comments/startup/serverRegistration\.(jsx|tsx|js|ts)'

# 2) List all store registrations to spot duplicates with packs/stores-registration
rg -nP 'ReactOnRails\.registerStore\s*\(' -n -C2

# 3) List all component registrations to spot duplicates with generated packs
rg -nP 'ReactOnRails\.register\s*\(' -n -C2

Length of output: 1644


Remove duplicate store registrations in serverRegistration.jsx
Remove the ReactOnRails.registerStore({ routerCommentsStore, commentsStore }) call (lines 20–22) and the corresponding imports from client/app/bundles/comments/startup/serverRegistration.jsx; those stores are already registered in client/app/packs/stores-registration.js (lines 6–8). Keep this file (referenced in config/webpack/server.js entry) for server-only component registration.

🤖 Prompt for AI Agents
In client/app/bundles/comments/startup/serverRegistration.jsx (remove the
duplicate store imports and registration): delete the imports for
routerCommentsStore and commentsStore and remove the
ReactOnRails.registerStore({ routerCommentsStore, commentsStore }) call (the
duplicate registration around lines 20–22), leaving only the server-only
component registrations in this file; stores remain registered in
client/app/packs/stores-registration.js so do not re-register them here and keep
the file for server-only component registration referenced by
config/webpack/server.js.

import routerCommentsStore from '../store/routerCommentsStore';
import commentsStore from '../store/commentsStore';
import Footer from '../components/Footer/Footer';
import Footer from '../components/Footer/ror_components/Footer';

ReactOnRails.register({
App,
Expand Down
35 changes: 0 additions & 35 deletions client/app/packs/client-bundle.js

This file was deleted.

Loading