react_on_rails 16.0.0 → 16.0.1.rc.0

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +117 -77
  3. data/CLAUDE.md +14 -2
  4. data/Gemfile.lock +1 -1
  5. data/LICENSE.md +15 -1
  6. data/README.md +68 -18
  7. data/eslint.config.ts +3 -0
  8. data/knip.ts +20 -9
  9. data/lib/generators/react_on_rails/USAGE +65 -0
  10. data/lib/generators/react_on_rails/base_generator.rb +7 -7
  11. data/lib/generators/react_on_rails/generator_helper.rb +4 -0
  12. data/lib/generators/react_on_rails/generator_messages.rb +2 -2
  13. data/lib/generators/react_on_rails/install_generator.rb +115 -7
  14. data/lib/generators/react_on_rails/react_no_redux_generator.rb +16 -4
  15. data/lib/generators/react_on_rails/react_with_redux_generator.rb +83 -14
  16. data/lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.tsx +25 -0
  17. data/lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.server.tsx +5 -0
  18. data/lib/generators/react_on_rails/templates/base/base/bin/dev +12 -24
  19. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/actions/helloWorldActionCreators.ts +18 -0
  20. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.tsx +24 -0
  21. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/constants/helloWorldConstants.ts +6 -0
  22. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/containers/HelloWorldContainer.ts +20 -0
  23. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/reducers/helloWorldReducer.ts +22 -0
  24. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.client.tsx +23 -0
  25. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.server.tsx +5 -0
  26. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/store/helloWorldStore.ts +18 -0
  27. data/lib/react_on_rails/configuration.rb +10 -6
  28. data/lib/react_on_rails/dev/server_manager.rb +185 -28
  29. data/lib/react_on_rails/doctor.rb +1149 -0
  30. data/lib/react_on_rails/helper.rb +9 -78
  31. data/lib/react_on_rails/pro/NOTICE +21 -0
  32. data/lib/react_on_rails/pro/helper.rb +122 -0
  33. data/lib/react_on_rails/pro/utils.rb +53 -0
  34. data/lib/react_on_rails/react_component/render_options.rb +6 -2
  35. data/lib/react_on_rails/system_checker.rb +659 -0
  36. data/lib/react_on_rails/version.rb +1 -1
  37. data/lib/tasks/doctor.rake +51 -0
  38. data/lib/tasks/generate_packs.rake +127 -4
  39. data/package-lock.json +11984 -0
  40. metadata +21 -6
  41. data/lib/generators/react_on_rails/bin/dev +0 -46
@@ -38,7 +38,7 @@ module GeneratorMessages
38
38
  @output = []
39
39
  end
40
40
 
41
- def helpful_message_after_installation(component_name: "HelloWorld")
41
+ def helpful_message_after_installation(component_name: "HelloWorld", route: "hello_world")
42
42
  process_manager_section = build_process_manager_section
43
43
  testing_section = build_testing_section
44
44
  package_manager = detect_package_manager
@@ -62,7 +62,7 @@ module GeneratorMessages
62
62
  ./bin/dev prod # Production-like mode for testing
63
63
  ./bin/dev help # See all available options
64
64
 
65
- 3. Visit: #{Rainbow('http://localhost:3000/hello_world').cyan.underline}
65
+ 3. Visit: #{Rainbow(route ? "http://localhost:3000/#{route}" : 'http://localhost:3000').cyan.underline}
66
66
  ✨ KEY FEATURES:
67
67
  ─────────────────────────────────────────────────────────────────────────
68
68
  • Auto-registration enabled - Your layout only needs:
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails/generators"
4
+ require "json"
4
5
  require_relative "generator_helper"
5
6
  require_relative "generator_messages"
6
7
 
@@ -20,6 +21,13 @@ module ReactOnRails
20
21
  desc: "Install Redux package and Redux version of Hello World Example. Default: false",
21
22
  aliases: "-R"
22
23
 
24
+ # --typescript
25
+ class_option :typescript,
26
+ type: :boolean,
27
+ default: false,
28
+ desc: "Generate TypeScript files and install TypeScript dependencies. Default: false",
29
+ aliases: "-T"
30
+
23
31
  # --ignore-warnings
24
32
  class_option :ignore_warnings,
25
33
  type: :boolean,
@@ -58,11 +66,16 @@ module ReactOnRails
58
66
 
59
67
  def invoke_generators
60
68
  ensure_shakapacker_installed
61
- invoke "react_on_rails:base"
69
+ if options.typescript?
70
+ install_typescript_dependencies
71
+ create_css_module_types
72
+ create_typescript_config
73
+ end
74
+ invoke "react_on_rails:base", [], { typescript: options.typescript? }
62
75
  if options.redux?
63
- invoke "react_on_rails:react_with_redux"
76
+ invoke "react_on_rails:react_with_redux", [], { typescript: options.typescript? }
64
77
  else
65
- invoke "react_on_rails:react_no_redux"
78
+ invoke "react_on_rails:react_no_redux", [], { typescript: options.typescript? }
66
79
  end
67
80
  end
68
81
 
@@ -136,11 +149,13 @@ module ReactOnRails
136
149
  end
137
150
 
138
151
  def add_bin_scripts
139
- directory "#{__dir__}/bin", "bin"
152
+ # Copy bin scripts from templates
153
+ template_bin_path = "#{__dir__}/templates/base/base/bin"
154
+ directory template_bin_path, "bin"
140
155
 
141
156
  # Make these and only these files executable
142
157
  files_to_copy = []
143
- Dir.chdir("#{__dir__}/bin") do
158
+ Dir.chdir(template_bin_path) do
144
159
  files_to_copy.concat(Dir.glob("*"))
145
160
  end
146
161
  files_to_become_executable = files_to_copy.map { |filename| "bin/#{filename}" }
@@ -149,7 +164,14 @@ module ReactOnRails
149
164
  end
150
165
 
151
166
  def add_post_install_message
152
- GeneratorMessages.add_info(GeneratorMessages.helpful_message_after_installation)
167
+ # Determine what route will be created by the generator
168
+ route = "hello_world" # This is the hardcoded route from base_generator.rb
169
+ component_name = options.redux? ? "HelloWorldApp" : "HelloWorld"
170
+
171
+ GeneratorMessages.add_info(GeneratorMessages.helpful_message_after_installation(
172
+ component_name: component_name,
173
+ route: route
174
+ ))
153
175
  end
154
176
 
155
177
  def shakapacker_loaded_in_process?(gem_name)
@@ -302,10 +324,96 @@ module ReactOnRails
302
324
  false
303
325
  end
304
326
 
327
+ def install_typescript_dependencies
328
+ puts Rainbow("📝 Installing TypeScript dependencies...").yellow
329
+
330
+ # Install TypeScript and React type definitions
331
+ typescript_packages = %w[
332
+ typescript
333
+ @types/react
334
+ @types/react-dom
335
+ @babel/preset-typescript
336
+ ]
337
+
338
+ # Try using GeneratorHelper first (package manager agnostic)
339
+ return if add_npm_dependencies(typescript_packages, dev: true)
340
+
341
+ # Fallback to npm if GeneratorHelper fails
342
+ success = system("npm", "install", "--save-dev", *typescript_packages)
343
+ return if success
344
+
345
+ warning = <<~MSG.strip
346
+ ⚠️ Failed to install TypeScript dependencies automatically.
347
+
348
+ Please run manually:
349
+ npm install --save-dev #{typescript_packages.join(' ')}
350
+ MSG
351
+ GeneratorMessages.add_warning(warning)
352
+ end
353
+
354
+ def create_css_module_types
355
+ puts Rainbow("📝 Creating CSS module type definitions...").yellow
356
+
357
+ # Ensure the types directory exists
358
+ FileUtils.mkdir_p("app/javascript/types")
359
+
360
+ css_module_types_content = <<~TS.strip
361
+ // TypeScript definitions for CSS modules
362
+ declare module "*.module.css" {
363
+ const classes: { [key: string]: string };
364
+ export default classes;
365
+ }
366
+
367
+ declare module "*.module.scss" {
368
+ const classes: { [key: string]: string };
369
+ export default classes;
370
+ }
371
+
372
+ declare module "*.module.sass" {
373
+ const classes: { [key: string]: string };
374
+ export default classes;
375
+ }
376
+ TS
377
+
378
+ File.write("app/javascript/types/css-modules.d.ts", css_module_types_content)
379
+ puts Rainbow("✅ Created CSS module type definitions").green
380
+ end
381
+
382
+ def create_typescript_config
383
+ if File.exist?("tsconfig.json")
384
+ puts Rainbow("⚠️ tsconfig.json already exists, skipping creation").yellow
385
+ return
386
+ end
387
+
388
+ tsconfig_content = {
389
+ "compilerOptions" => {
390
+ "target" => "es2018",
391
+ "allowJs" => true,
392
+ "skipLibCheck" => true,
393
+ "strict" => true,
394
+ "noUncheckedIndexedAccess" => true,
395
+ "forceConsistentCasingInFileNames" => true,
396
+ "noFallthroughCasesInSwitch" => true,
397
+ "module" => "esnext",
398
+ "moduleResolution" => "bundler",
399
+ "resolveJsonModule" => true,
400
+ "isolatedModules" => true,
401
+ "noEmit" => true,
402
+ "jsx" => "react-jsx"
403
+ },
404
+ "include" => [
405
+ "app/javascript/**/*"
406
+ ]
407
+ }
408
+
409
+ File.write("tsconfig.json", JSON.pretty_generate(tsconfig_content))
410
+ puts Rainbow("✅ Created tsconfig.json").green
411
+ end
412
+
305
413
  # Removed: Shakapacker auto-installation logic (now explicit dependency)
306
414
 
307
415
  # Removed: Shakapacker 8+ is now required as explicit dependency
416
+ # rubocop:enable Metrics/ClassLength
308
417
  end
309
- # rubocop:enable Metrics/ClassLength
310
418
  end
311
419
  end
@@ -11,12 +11,24 @@ module ReactOnRails
11
11
  Rails::Generators.hide_namespace(namespace)
12
12
  source_root(File.expand_path("templates", __dir__))
13
13
 
14
+ class_option :typescript,
15
+ type: :boolean,
16
+ default: false,
17
+ desc: "Generate TypeScript files"
18
+
14
19
  def copy_base_files
15
20
  base_js_path = "base/base"
16
- base_files = %w[app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx
17
- app/javascript/src/HelloWorld/ror_components/HelloWorld.server.jsx
18
- app/javascript/src/HelloWorld/ror_components/HelloWorld.module.css]
19
- base_files.each { |file| copy_file("#{base_js_path}/#{file}", file) }
21
+
22
+ # Determine which component files to copy based on TypeScript option
23
+ component_files = [
24
+ "app/javascript/src/HelloWorld/ror_components/HelloWorld.client.#{component_extension(options)}",
25
+ "app/javascript/src/HelloWorld/ror_components/HelloWorld.server.#{component_extension(options)}",
26
+ "app/javascript/src/HelloWorld/ror_components/HelloWorld.module.css"
27
+ ]
28
+
29
+ component_files.each do |file|
30
+ copy_file("#{base_js_path}/#{file}", file)
31
+ end
20
32
  end
21
33
 
22
34
  def create_appropriate_templates
@@ -1,13 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails/generators"
4
+ require_relative "generator_helper"
5
+ require_relative "generator_messages"
4
6
 
5
7
  module ReactOnRails
6
8
  module Generators
7
9
  class ReactWithReduxGenerator < Rails::Generators::Base
10
+ include GeneratorHelper
11
+
8
12
  Rails::Generators.hide_namespace(namespace)
9
13
  source_root(File.expand_path("templates", __dir__))
10
14
 
15
+ class_option :typescript,
16
+ type: :boolean,
17
+ default: false,
18
+ desc: "Generate TypeScript files",
19
+ aliases: "-T"
20
+
11
21
  def create_redux_directories
12
22
  # Create auto-registration directory structure for Redux
13
23
  empty_directory("app/javascript/src/HelloWorldApp/ror_components")
@@ -19,17 +29,18 @@ module ReactOnRails
19
29
 
20
30
  def copy_base_files
21
31
  base_js_path = "redux/base"
32
+ ext = component_extension(options)
22
33
 
23
34
  # Copy Redux-connected component to auto-registration structure
24
- copy_file("#{base_js_path}/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.client.jsx",
25
- "app/javascript/src/HelloWorldApp/ror_components/HelloWorldApp.client.jsx")
26
- copy_file("#{base_js_path}/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.server.jsx",
27
- "app/javascript/src/HelloWorldApp/ror_components/HelloWorldApp.server.jsx")
35
+ copy_file("#{base_js_path}/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.client.#{ext}",
36
+ "app/javascript/src/HelloWorldApp/ror_components/HelloWorldApp.client.#{ext}")
37
+ copy_file("#{base_js_path}/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.server.#{ext}",
38
+ "app/javascript/src/HelloWorldApp/ror_components/HelloWorldApp.server.#{ext}")
28
39
  copy_file("#{base_js_path}/app/javascript/bundles/HelloWorld/components/HelloWorld.module.css",
29
40
  "app/javascript/src/HelloWorldApp/components/HelloWorld.module.css")
30
41
 
31
42
  # Update import paths in client component
32
- ror_client_file = "app/javascript/src/HelloWorldApp/ror_components/HelloWorldApp.client.jsx"
43
+ ror_client_file = "app/javascript/src/HelloWorldApp/ror_components/HelloWorldApp.client.#{ext}"
33
44
  gsub_file(ror_client_file, "../store/helloWorldStore", "../store/helloWorldStore")
34
45
  gsub_file(ror_client_file, "../containers/HelloWorldContainer",
35
46
  "../containers/HelloWorldContainer")
@@ -37,12 +48,15 @@ module ReactOnRails
37
48
 
38
49
  def copy_base_redux_files
39
50
  base_hello_world_path = "redux/base/app/javascript/bundles/HelloWorld"
40
- %w[actions/helloWorldActionCreators.js
41
- containers/HelloWorldContainer.js
42
- constants/helloWorldConstants.js
43
- reducers/helloWorldReducer.js
44
- store/helloWorldStore.js
45
- components/HelloWorld.jsx].each do |file|
51
+ redux_extension = options.typescript? ? "ts" : "js"
52
+
53
+ # Copy Redux infrastructure files with appropriate extension
54
+ %W[actions/helloWorldActionCreators.#{redux_extension}
55
+ containers/HelloWorldContainer.#{redux_extension}
56
+ constants/helloWorldConstants.#{redux_extension}
57
+ reducers/helloWorldReducer.#{redux_extension}
58
+ store/helloWorldStore.#{redux_extension}
59
+ components/HelloWorld.#{component_extension(options)}].each do |file|
46
60
  copy_file("#{base_hello_world_path}/#{file}",
47
61
  "app/javascript/src/HelloWorldApp/#{file}")
48
62
  end
@@ -60,15 +74,70 @@ module ReactOnRails
60
74
  end
61
75
 
62
76
  def add_redux_npm_dependencies
63
- run "npm install redux react-redux"
77
+ # Add Redux dependencies as regular dependencies
78
+ regular_packages = %w[redux react-redux]
79
+
80
+ # Try using GeneratorHelper first (package manager agnostic)
81
+ success = add_npm_dependencies(regular_packages)
82
+
83
+ # Fallback to package manager detection if GeneratorHelper fails
84
+ return if success
85
+
86
+ package_manager = GeneratorMessages.detect_package_manager
87
+ return unless package_manager
88
+
89
+ install_packages_with_fallback(regular_packages, dev: false, package_manager: package_manager)
90
+ end
91
+
92
+ private
93
+
94
+ def install_packages_with_fallback(packages, dev:, package_manager:)
95
+ install_args = build_install_args(package_manager, dev, packages)
96
+
97
+ success = system(*install_args)
98
+ return if success
99
+
100
+ install_command = install_args.join(" ")
101
+ warning = <<~MSG.strip
102
+ ⚠️ Failed to install Redux dependencies automatically.
103
+
104
+ Please run manually:
105
+ #{install_command}
106
+ MSG
107
+ GeneratorMessages.add_warning(warning)
108
+ end
109
+
110
+ def build_install_args(package_manager, dev, packages)
111
+ # Security: Validate package manager to prevent command injection
112
+ allowed_package_managers = %w[npm yarn pnpm bun].freeze
113
+ unless allowed_package_managers.include?(package_manager)
114
+ raise ArgumentError, "Invalid package manager: #{package_manager}"
115
+ end
116
+
117
+ base_commands = {
118
+ "npm" => %w[npm install],
119
+ "yarn" => %w[yarn add],
120
+ "pnpm" => %w[pnpm add],
121
+ "bun" => %w[bun add]
122
+ }
123
+
124
+ base_args = base_commands[package_manager].dup
125
+ base_args << dev_flag_for(package_manager) if dev
126
+ base_args + packages
127
+ end
128
+
129
+ def dev_flag_for(package_manager)
130
+ case package_manager
131
+ when "npm", "pnpm" then "--save-dev"
132
+ when "yarn", "bun" then "--dev"
133
+ end
64
134
  end
65
135
 
66
136
  def add_redux_specific_messages
67
137
  # Override the generic messages with Redux-specific instructions
68
- require_relative "generator_messages"
69
138
  GeneratorMessages.output.clear
70
139
  GeneratorMessages.add_info(
71
- GeneratorMessages.helpful_message_after_installation(component_name: "HelloWorldApp")
140
+ GeneratorMessages.helpful_message_after_installation(component_name: "HelloWorldApp", route: "hello_world")
72
141
  )
73
142
  end
74
143
  end
@@ -0,0 +1,25 @@
1
+ import React, { useState } from 'react';
2
+ import * as style from './HelloWorld.module.css';
3
+
4
+ interface HelloWorldProps {
5
+ name: string;
6
+ }
7
+
8
+ const HelloWorld: React.FC<HelloWorldProps> = (props) => {
9
+ const [name, setName] = useState(props.name);
10
+
11
+ return (
12
+ <div>
13
+ <h3>Hello, {name}!</h3>
14
+ <hr />
15
+ <form>
16
+ <label className={style.bright} htmlFor="name">
17
+ Say hello to:
18
+ <input id="name" type="text" value={name} onChange={(e) => setName(e.target.value)} />
19
+ </label>
20
+ </form>
21
+ </div>
22
+ );
23
+ };
24
+
25
+ export default HelloWorld;
@@ -0,0 +1,5 @@
1
+ import HelloWorld from './HelloWorld.client';
2
+ // This could be specialized for server rendering
3
+ // For example, if using React Router, we'd have the SSR setup here.
4
+
5
+ export default HelloWorld;
@@ -18,29 +18,17 @@
18
18
  # 3. Extend ReactOnRails::Dev classes in your Rails app for advanced customization
19
19
  # 4. Use classes directly: ReactOnRails::Dev::ServerManager.start(:development, "Custom.procfile")
20
20
 
21
- begin
22
- require "bundler/setup"
23
- require "react_on_rails/dev"
24
- rescue LoadError
25
- # Fallback for when gem is not yet installed
26
- puts "Loading ReactOnRails development tools..."
27
- require_relative "../../lib/react_on_rails/dev"
28
- end
21
+ require "bundler/setup"
22
+ require "react_on_rails/dev"
23
+
24
+ # Default route configuration
25
+ # This is set by the ReactOnRails installer to point to your generated component.
26
+ # Change this to your preferred default route, or pass --route=<route> to override.
27
+ DEFAULT_ROUTE = "hello_world"
29
28
 
30
29
  # Main execution
31
- case ARGV[0]
32
- when "production-assets", "prod"
33
- ReactOnRails::Dev::ServerManager.start(:production_like)
34
- when "static"
35
- ReactOnRails::Dev::ServerManager.start(:static, "Procfile.dev-static-assets")
36
- when "kill"
37
- ReactOnRails::Dev::ServerManager.kill_processes
38
- when "help", "--help", "-h"
39
- ReactOnRails::Dev::ServerManager.show_help
40
- when "hmr", nil
41
- ReactOnRails::Dev::ServerManager.start(:development, "Procfile.dev")
42
- else
43
- puts "Unknown argument: #{ARGV[0]}"
44
- puts "Run 'bin/dev help' for usage information"
45
- exit 1
46
- end
30
+ # Add the default route to ARGV if no --route option is provided
31
+ argv_with_defaults = ARGV.dup
32
+ argv_with_defaults.push("--route", DEFAULT_ROUTE) unless argv_with_defaults.any? { |arg| arg.start_with?("--route") }
33
+
34
+ ReactOnRails::Dev::ServerManager.run_from_command_line(argv_with_defaults)
@@ -0,0 +1,18 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+
3
+ import { HELLO_WORLD_NAME_UPDATE } from '../constants/helloWorldConstants';
4
+
5
+ // Action interface
6
+ export interface UpdateNameAction {
7
+ type: typeof HELLO_WORLD_NAME_UPDATE;
8
+ text: string;
9
+ }
10
+
11
+ // Union type for all actions
12
+ export type HelloWorldAction = UpdateNameAction;
13
+
14
+ // Action creator with proper TypeScript typing
15
+ export const updateName = (text: string): UpdateNameAction => ({
16
+ type: HELLO_WORLD_NAME_UPDATE,
17
+ text,
18
+ });
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import * as style from './HelloWorld.module.css';
3
+ import type { PropsFromRedux } from '../containers/HelloWorldContainer';
4
+
5
+ // Component props are inferred from Redux container
6
+ type HelloWorldProps = PropsFromRedux;
7
+
8
+ const HelloWorld: React.FC<HelloWorldProps> = ({ name, updateName }) => (
9
+ <div>
10
+ <h3>
11
+ Hello,
12
+ {name}!
13
+ </h3>
14
+ <hr />
15
+ <form>
16
+ <label className={style.bright} htmlFor="name">
17
+ Say hello to:
18
+ <input id="name" type="text" value={name} onChange={(e) => updateName(e.target.value)} />
19
+ </label>
20
+ </form>
21
+ </div>
22
+ );
23
+
24
+ export default HelloWorld;
@@ -0,0 +1,6 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+
3
+ export const HELLO_WORLD_NAME_UPDATE = 'HELLO_WORLD_NAME_UPDATE' as const;
4
+
5
+ // Action type for TypeScript
6
+ export type HelloWorldActionType = typeof HELLO_WORLD_NAME_UPDATE;
@@ -0,0 +1,20 @@
1
+ // Simple example of a React "smart" component
2
+
3
+ import { connect, ConnectedProps } from 'react-redux';
4
+ import HelloWorld from '../components/HelloWorld';
5
+ import * as actions from '../actions/helloWorldActionCreators';
6
+ import type { HelloWorldState } from '../reducers/helloWorldReducer';
7
+
8
+ // Which part of the Redux global state does our component want to receive as props?
9
+ const mapStateToProps = (state: HelloWorldState) => ({ name: state.name });
10
+
11
+ // Create the connector
12
+ const connector = connect(mapStateToProps, actions);
13
+
14
+ // Infer the props from Redux state and actions
15
+ export type PropsFromRedux = ConnectedProps<typeof connector>;
16
+
17
+ // Don't forget to actually use connect!
18
+ // Note that we don't export HelloWorld, but the redux "connected" version of it.
19
+ // See https://github.com/reactjs/react-redux/blob/master/docs/api.md#examples
20
+ export default connector(HelloWorld);
@@ -0,0 +1,22 @@
1
+ import { combineReducers } from 'redux';
2
+ import { HELLO_WORLD_NAME_UPDATE } from '../constants/helloWorldConstants';
3
+ import { HelloWorldAction } from '../actions/helloWorldActionCreators';
4
+
5
+ // State interface
6
+ export interface HelloWorldState {
7
+ name: string;
8
+ }
9
+
10
+ // Individual reducer with TypeScript types
11
+ const name = (state: string = '', action: HelloWorldAction): string => {
12
+ switch (action.type) {
13
+ case HELLO_WORLD_NAME_UPDATE:
14
+ return action.text;
15
+ default:
16
+ return state;
17
+ }
18
+ };
19
+
20
+ const helloWorldReducer = combineReducers<HelloWorldState>({ name });
21
+
22
+ export default helloWorldReducer;
@@ -0,0 +1,23 @@
1
+ import { useMemo, type FC } from 'react';
2
+ import { Provider } from 'react-redux';
3
+
4
+ import configureStore, { type RailsProps } from '../store/helloWorldStore';
5
+ import HelloWorldContainer from '../containers/HelloWorldContainer';
6
+
7
+ // Props interface matches what Rails will pass from the controller
8
+ interface HelloWorldAppProps extends RailsProps {}
9
+
10
+ // See documentation for https://github.com/reactjs/react-redux.
11
+ // This is how you get props from the Rails view into the redux store.
12
+ // This code here binds your smart component to the redux store.
13
+ const HelloWorldApp: FC<HelloWorldAppProps> = (props) => {
14
+ const store = useMemo(() => configureStore(props), [props]);
15
+
16
+ return (
17
+ <Provider store={store}>
18
+ <HelloWorldContainer />
19
+ </Provider>
20
+ );
21
+ };
22
+
23
+ export default HelloWorldApp;
@@ -0,0 +1,5 @@
1
+ import HelloWorldApp from './HelloWorldApp.client';
2
+ // This could be specialized for server rendering
3
+ // For example, if using React Router, we'd have the SSR setup here.
4
+
5
+ export default HelloWorldApp;
@@ -0,0 +1,18 @@
1
+ import { createStore } from 'redux';
2
+ import type { Store, PreloadedState } from 'redux';
3
+ import helloWorldReducer from '../reducers/helloWorldReducer';
4
+ import type { HelloWorldState } from '../reducers/helloWorldReducer';
5
+
6
+ // Rails props interface - customize based on your Rails controller
7
+ export interface RailsProps {
8
+ name: string;
9
+ [key: string]: any; // Allow additional props from Rails
10
+ }
11
+
12
+ // Store type
13
+ export type HelloWorldStore = Store<HelloWorldState>;
14
+
15
+ const configureStore = (railsProps: RailsProps): HelloWorldStore =>
16
+ createStore(helloWorldReducer, railsProps as PreloadedState<HelloWorldState>);
17
+
18
+ export default configureStore;
@@ -175,7 +175,7 @@ module ReactOnRails
175
175
  end
176
176
 
177
177
  msg = <<~MSG
178
- ReactOnRails: Your current version of #{ReactOnRails::PackerUtils.packer_type.upcase_first} \
178
+ ReactOnRails: Your current version of shakapacker \
179
179
  does not support async script loading, which may cause performance issues. Please either:
180
180
  1. Use :sync or :defer loading strategy instead of :async
181
181
  2. Upgrade to Shakapacker v8.2.0 or above to enable async script loading
@@ -284,8 +284,10 @@ module ReactOnRails
284
284
  if ReactOnRails::PackerUtils.using_packer?
285
285
  packer_public_output_path = ReactOnRails::PackerUtils.packer_public_output_path
286
286
  # rubocop:disable Layout/LineLength
287
+ packer_name = ReactOnRails::PackerUtils.packer_type&.upcase_first
288
+
287
289
  Rails.logger.warn "Error configuring config/initializers/react_on_rails. Define neither the generated_assets_dirs nor " \
288
- "the generated_assets_dir when using #{ReactOnRails::PackerUtils.packer_type.upcase_first}. This is defined by " \
290
+ "the generated_assets_dir when using #{packer_name}. This is defined by " \
289
291
  "public_output_path specified in #{ReactOnRails::PackerUtils.packer_type}.yml = #{packer_public_output_path}."
290
292
  # rubocop:enable Layout/LineLength
291
293
  return
@@ -331,15 +333,17 @@ module ReactOnRails
331
333
  end
332
334
 
333
335
  def compile_command_conflict_message
336
+ packer_name = ReactOnRails::PackerUtils.packer_type.upcase_first
337
+ packer_type = ReactOnRails::PackerUtils.packer_type
334
338
  <<~MSG
335
339
 
336
- React on Rails and #{ReactOnRails::PackerUtils.packer_type.upcase_first} error in configuration!
340
+ React on Rails and #{packer_name} error in configuration!
337
341
  In order to use config/react_on_rails.rb config.build_production_command,
338
- you must edit config/#{ReactOnRails::PackerUtils.packer_type}.yml to include this value in the default configuration:
339
- '#{ReactOnRails::PackerUtils.packer_type}_precompile: false'
342
+ you must edit config/#{packer_type}.yml to include this value in the default configuration:
343
+ '#{packer_type}_precompile: false'
340
344
 
341
345
  Alternatively, remove the config/react_on_rails.rb config.build_production_command and the
342
- default bin/#{ReactOnRails::PackerUtils.packer_type} script will be used for assets:precompile.
346
+ default bin/#{packer_type} script will be used for assets:precompile.
343
347
 
344
348
  MSG
345
349
  end