react_on_rails 14.2.1 → 16.1.1

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 (116) hide show
  1. checksums.yaml +4 -4
  2. data/AI_AGENT_INSTRUCTIONS.md +63 -0
  3. data/CHANGELOG.md +564 -85
  4. data/CLAUDE.md +135 -0
  5. data/CODING_AGENTS.md +313 -0
  6. data/CONTRIBUTING.md +448 -37
  7. data/Gemfile.development_dependencies +6 -1
  8. data/Gemfile.lock +13 -4
  9. data/KUDOS.md +22 -1
  10. data/LICENSE.md +30 -4
  11. data/LICENSES/README.md +14 -0
  12. data/NEWS.md +48 -48
  13. data/PROJECTS.md +45 -40
  14. data/REACT-ON-RAILS-PRO-LICENSE.md +129 -0
  15. data/README.md +113 -42
  16. data/SUMMARY.md +62 -52
  17. data/TODO.md +135 -0
  18. data/bin/lefthook/check-trailing-newlines +38 -0
  19. data/bin/lefthook/get-changed-files +26 -0
  20. data/bin/lefthook/prettier-format +26 -0
  21. data/bin/lefthook/ruby-autofix +26 -0
  22. data/bin/lefthook/ruby-lint +27 -0
  23. data/eslint.config.ts +232 -0
  24. data/knip.ts +40 -6
  25. data/lib/generators/USAGE +4 -5
  26. data/lib/generators/react_on_rails/USAGE +65 -0
  27. data/lib/generators/react_on_rails/base_generator.rb +276 -62
  28. data/lib/generators/react_on_rails/dev_tests_generator.rb +1 -0
  29. data/lib/generators/react_on_rails/generator_helper.rb +35 -1
  30. data/lib/generators/react_on_rails/generator_messages.rb +138 -17
  31. data/lib/generators/react_on_rails/install_generator.rb +474 -26
  32. data/lib/generators/react_on_rails/react_no_redux_generator.rb +19 -6
  33. data/lib/generators/react_on_rails/react_with_redux_generator.rb +110 -18
  34. data/lib/generators/react_on_rails/templates/.eslintrc +1 -1
  35. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev +5 -0
  36. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-prod-assets +8 -0
  37. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-static-assets +2 -0
  38. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +0 -5
  39. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.module.css +2 -2
  40. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorldServer.js +1 -1
  41. data/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/server-bundle.js +1 -8
  42. data/lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx +21 -0
  43. data/lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.tsx +25 -0
  44. data/lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.module.css +4 -0
  45. data/lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.server.jsx +5 -0
  46. data/lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.server.tsx +5 -0
  47. data/lib/generators/react_on_rails/templates/base/base/app/views/hello_world/index.html.erb.tt +1 -1
  48. data/lib/generators/react_on_rails/templates/base/base/app/views/layouts/hello_world.html.erb +4 -2
  49. data/lib/generators/react_on_rails/templates/base/base/babel.config.js.tt +5 -2
  50. data/lib/generators/react_on_rails/templates/base/base/bin/dev +34 -0
  51. data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt +14 -5
  52. data/lib/generators/react_on_rails/templates/base/base/config/shakapacker.yml +76 -7
  53. data/lib/generators/react_on_rails/templates/base/base/config/webpack/commonWebpackConfig.js.tt +1 -1
  54. data/lib/generators/react_on_rails/templates/base/base/config/webpack/development.js.tt +6 -10
  55. data/lib/generators/react_on_rails/templates/base/base/config/webpack/production.js.tt +2 -2
  56. data/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt +3 -2
  57. data/lib/generators/react_on_rails/templates/base/base/config/webpack/test.js.tt +2 -2
  58. data/lib/generators/react_on_rails/templates/dev_tests/spec/system/hello_world_spec.rb +0 -2
  59. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/actions/helloWorldActionCreators.ts +18 -0
  60. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +0 -6
  61. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.module.css +4 -0
  62. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.tsx +24 -0
  63. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/constants/helloWorldConstants.ts +6 -0
  64. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/containers/HelloWorldContainer.ts +20 -0
  65. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/reducers/helloWorldReducer.js +1 -1
  66. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/reducers/helloWorldReducer.ts +22 -0
  67. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.client.tsx +23 -0
  68. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.server.jsx +5 -0
  69. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.server.tsx +5 -0
  70. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/store/helloWorldStore.ts +18 -0
  71. data/lib/react_on_rails/configuration.rb +141 -57
  72. data/lib/react_on_rails/controller.rb +6 -2
  73. data/lib/react_on_rails/dev/file_manager.rb +78 -0
  74. data/lib/react_on_rails/dev/pack_generator.rb +27 -0
  75. data/lib/react_on_rails/dev/process_manager.rb +61 -0
  76. data/lib/react_on_rails/dev/server_manager.rb +487 -0
  77. data/lib/react_on_rails/dev.rb +20 -0
  78. data/lib/react_on_rails/doctor.rb +1149 -0
  79. data/lib/react_on_rails/engine.rb +6 -0
  80. data/lib/react_on_rails/git_utils.rb +12 -2
  81. data/lib/react_on_rails/helper.rb +176 -74
  82. data/lib/react_on_rails/json_parse_error.rb +6 -1
  83. data/lib/react_on_rails/packer_utils.rb +61 -71
  84. data/lib/react_on_rails/packs_generator.rb +221 -19
  85. data/lib/react_on_rails/prerender_error.rb +4 -0
  86. data/lib/react_on_rails/pro/NOTICE +21 -0
  87. data/lib/react_on_rails/pro/helper.rb +122 -0
  88. data/lib/react_on_rails/pro/utils.rb +53 -0
  89. data/lib/react_on_rails/react_component/render_options.rb +38 -6
  90. data/lib/react_on_rails/server_rendering_js_code.rb +0 -1
  91. data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +12 -5
  92. data/lib/react_on_rails/system_checker.rb +659 -0
  93. data/lib/react_on_rails/test_helper/webpack_assets_compiler.rb +1 -1
  94. data/lib/react_on_rails/test_helper/webpack_assets_status_checker.rb +6 -4
  95. data/lib/react_on_rails/test_helper.rb +2 -3
  96. data/lib/react_on_rails/utils.rb +139 -43
  97. data/lib/react_on_rails/version.rb +1 -1
  98. data/lib/react_on_rails/version_checker.rb +14 -20
  99. data/lib/react_on_rails/version_syntax_converter.rb +1 -1
  100. data/lib/react_on_rails.rb +1 -0
  101. data/lib/tasks/assets.rake +1 -1
  102. data/lib/tasks/doctor.rake +48 -0
  103. data/lib/tasks/generate_packs.rake +158 -1
  104. data/react_on_rails.gemspec +1 -0
  105. data/tsconfig.eslint.json +6 -0
  106. data/tsconfig.json +5 -3
  107. metadata +63 -14
  108. data/REACT-ON-RAILS-PRO-LICENSE +0 -95
  109. data/lib/generators/react_on_rails/adapt_for_older_shakapacker_generator.rb +0 -41
  110. data/lib/generators/react_on_rails/bin/dev +0 -30
  111. data/lib/generators/react_on_rails/bin/dev-static +0 -30
  112. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-static.tt +0 -9
  113. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev.tt +0 -5
  114. data/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/registration.js.tt +0 -8
  115. /data/lib/generators/react_on_rails/templates/base/base/config/webpack/{webpackConfig.js.tt → generateWebpackConfigs.js.tt} +0 -0
  116. /data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/startup/{HelloWorldApp.jsx → HelloWorldApp.client.jsx} +0 -0
@@ -1,51 +1,143 @@
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
- dirs = %w[actions constants containers reducers store startup]
13
- dirs.each { |name| empty_directory("app/javascript/bundles/HelloWorld/#{name}") }
22
+ # Create auto-bundling directory structure for Redux
23
+ empty_directory("app/javascript/src/HelloWorldApp/ror_components")
24
+
25
+ # Create Redux support directories within the component directory
26
+ dirs = %w[actions constants containers reducers store components]
27
+ dirs.each { |name| empty_directory("app/javascript/src/HelloWorldApp/#{name}") }
14
28
  end
15
29
 
16
30
  def copy_base_files
17
31
  base_js_path = "redux/base"
18
- base_files = %w[app/javascript/bundles/HelloWorld/components/HelloWorld.jsx]
19
- base_files.each { |file| copy_file("#{base_js_path}/#{file}", file) }
32
+ ext = component_extension(options)
33
+
34
+ # Copy Redux-connected component to auto-bundling structure
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}")
39
+ copy_file("#{base_js_path}/app/javascript/bundles/HelloWorld/components/HelloWorld.module.css",
40
+ "app/javascript/src/HelloWorldApp/components/HelloWorld.module.css")
41
+
42
+ # Update import paths in client component
43
+ ror_client_file = "app/javascript/src/HelloWorldApp/ror_components/HelloWorldApp.client.#{ext}"
44
+ gsub_file(ror_client_file, "../store/helloWorldStore", "../store/helloWorldStore")
45
+ gsub_file(ror_client_file, "../containers/HelloWorldContainer",
46
+ "../containers/HelloWorldContainer")
20
47
  end
21
48
 
22
49
  def copy_base_redux_files
23
50
  base_hello_world_path = "redux/base/app/javascript/bundles/HelloWorld"
24
- %w[actions/helloWorldActionCreators.js
25
- containers/HelloWorldContainer.js
26
- constants/helloWorldConstants.js
27
- reducers/helloWorldReducer.js
28
- store/helloWorldStore.js
29
- startup/HelloWorldApp.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|
30
60
  copy_file("#{base_hello_world_path}/#{file}",
31
- "app/javascript/bundles/HelloWorld/#{file}")
61
+ "app/javascript/src/HelloWorldApp/#{file}")
32
62
  end
33
63
  end
34
64
 
35
65
  def create_appropriate_templates
36
66
  base_path = "base/base"
37
- base_js_path = "#{base_path}/app/javascript"
38
67
  config = {
39
- component_name: "HelloWorldApp",
40
- app_relative_path: "../bundles/HelloWorld/startup/HelloWorldApp"
68
+ component_name: "HelloWorldApp"
69
+ }
70
+
71
+ # Only create the view template - no manual bundle needed for auto-bundling
72
+ template("#{base_path}/app/views/hello_world/index.html.erb.tt",
73
+ "app/views/hello_world/index.html.erb", config)
74
+ end
75
+
76
+ def add_redux_npm_dependencies
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
+ def add_redux_specific_messages
93
+ # Append Redux-specific post-install instructions
94
+ GeneratorMessages.add_info(
95
+ GeneratorMessages.helpful_message_after_installation(component_name: "HelloWorldApp", route: "hello_world")
96
+ )
97
+ end
98
+
99
+ private
100
+
101
+ def install_packages_with_fallback(packages, dev:, package_manager:)
102
+ install_args = build_install_args(package_manager, dev, packages)
103
+
104
+ success = system(*install_args)
105
+ return if success
106
+
107
+ install_command = install_args.join(" ")
108
+ warning = <<~MSG.strip
109
+ ⚠️ Failed to install Redux dependencies automatically.
110
+
111
+ Please run manually:
112
+ #{install_command}
113
+ MSG
114
+ GeneratorMessages.add_warning(warning)
115
+ end
116
+
117
+ def build_install_args(package_manager, dev, packages)
118
+ # Security: Validate package manager to prevent command injection
119
+ allowed_package_managers = %w[npm yarn pnpm bun].freeze
120
+ unless allowed_package_managers.include?(package_manager)
121
+ raise ArgumentError, "Invalid package manager: #{package_manager}"
122
+ end
123
+
124
+ base_commands = {
125
+ "npm" => %w[npm install],
126
+ "yarn" => %w[yarn add],
127
+ "pnpm" => %w[pnpm add],
128
+ "bun" => %w[bun add]
41
129
  }
42
130
 
43
- template("#{base_js_path}/packs/registration.js.tt", "app/javascript/packs/hello-world-bundle.js", config)
44
- template("#{base_path}/app/views/hello_world/index.html.erb.tt", "app/views/hello_world/index.html.erb", config)
131
+ base_args = base_commands[package_manager].dup
132
+ base_args << dev_flag_for(package_manager) if dev
133
+ base_args + packages
45
134
  end
46
135
 
47
- def add_redux_yarn_dependencies
48
- run "yarn add redux react-redux"
136
+ def dev_flag_for(package_manager)
137
+ case package_manager
138
+ when "npm", "pnpm" then "--save-dev"
139
+ when "yarn", "bun" then "--dev"
140
+ end
49
141
  end
50
142
  end
51
143
  end
@@ -1,5 +1,5 @@
1
1
  ---
2
- extends:
2
+ extends:
3
3
  - eslint-config-shakacode
4
4
  - prettier
5
5
 
@@ -0,0 +1,5 @@
1
+ # Procfile for development using HMR
2
+ # You can run these commands in separate shells
3
+ rails: bundle exec rails s -p 3000
4
+ wp-client: WEBPACK_SERVE=true bin/shakapacker-dev-server
5
+ wp-server: SERVER_BUNDLE_ONLY=yes bin/shakapacker --watch
@@ -0,0 +1,8 @@
1
+ # Procfile for development with production assets
2
+ # Uses production-optimized, precompiled assets with development environment
3
+ # Uncomment additional processes as needed for your app
4
+
5
+ rails: bundle exec rails s -p 3001
6
+ # sidekiq: bundle exec sidekiq -C config/sidekiq.yml
7
+ # redis: redis-server
8
+ # mailcatcher: mailcatcher --foreground
@@ -0,0 +1,2 @@
1
+ web: bin/rails server -p 3000
2
+ js: bin/shakapacker --watch
@@ -1,4 +1,3 @@
1
- import PropTypes from 'prop-types';
2
1
  import React, { useState } from 'react';
3
2
  import * as style from './HelloWorld.module.css';
4
3
 
@@ -19,8 +18,4 @@ const HelloWorld = (props) => {
19
18
  );
20
19
  };
21
20
 
22
- HelloWorld.propTypes = {
23
- name: PropTypes.string.isRequired, // this is passed from the Rails view
24
- };
25
-
26
21
  export default HelloWorld;
@@ -1,4 +1,4 @@
1
1
  .bright {
2
- color: green;
3
- font-weight: bold;
2
+ color: green;
3
+ font-weight: bold;
4
4
  }
@@ -1,5 +1,5 @@
1
1
  import HelloWorld from './HelloWorld';
2
2
  // This could be specialized for server rendering
3
- // For example, if using React-Router, we'd have the SSR setup here.
3
+ // For example, if using React Router, we'd have the SSR setup here.
4
4
 
5
5
  export default HelloWorld;
@@ -1,8 +1 @@
1
- import ReactOnRails from 'react-on-rails';
2
-
3
- import HelloWorld from '../bundles/HelloWorld/components/HelloWorldServer';
4
-
5
- // This is how react_on_rails can see the HelloWorld in the browser.
6
- ReactOnRails.register({
7
- HelloWorld,
8
- });
1
+ // Placeholder comment - auto-generated imports will be prepended here by react_on_rails:generate_packs
@@ -0,0 +1,21 @@
1
+ import React, { useState } from 'react';
2
+ import * as style from './HelloWorld.module.css';
3
+
4
+ const HelloWorld = (props) => {
5
+ const [name, setName] = useState(props.name);
6
+
7
+ return (
8
+ <div>
9
+ <h3>Hello, {name}!</h3>
10
+ <hr />
11
+ <form>
12
+ <label className={style.bright} htmlFor="name">
13
+ Say hello to:
14
+ <input id="name" type="text" value={name} onChange={(e) => setName(e.target.value)} />
15
+ </label>
16
+ </form>
17
+ </div>
18
+ );
19
+ };
20
+
21
+ export default HelloWorld;
@@ -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;
@@ -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;
@@ -1,2 +1,2 @@
1
1
  <h1>Hello World</h1>
2
- <%%= react_component("<%= config[:component_name] %>", props: @hello_world_props, prerender: false) %>
2
+ <%%= react_component("<%= config[:component_name] %>", props: @hello_world_props, prerender: true) %>
@@ -3,8 +3,10 @@
3
3
  <head>
4
4
  <title>ReactOnRailsWithShakapacker</title>
5
5
  <%= csrf_meta_tags %>
6
- <%= javascript_pack_tag 'hello-world-bundle' %>
7
- <%= stylesheet_pack_tag 'hello-world-bundle' %>
6
+
7
+ <!-- Empty pack tags - React on Rails injects component CSS/JS here -->
8
+ <%= stylesheet_pack_tag %>
9
+ <%= javascript_pack_tag %>
8
10
  </head>
9
11
 
10
12
  <body>
@@ -11,12 +11,15 @@ module.exports = function (api) {
11
11
  '@babel/preset-react',
12
12
  {
13
13
  development: !isProductionEnv,
14
- useBuiltIns: true
14
+ useBuiltIns: true,
15
+ runtime: 'automatic'
15
16
  }
16
17
  ]
17
18
  ].filter(Boolean),
18
19
  plugins: [
19
- process.env.WEBPACK_SERVE && 'react-refresh/babel',
20
+ // Enable React Refresh (Fast Refresh) only when webpack-dev-server is running (HMR mode)
21
+ // This prevents React Refresh from trying to connect when using static compilation
22
+ !isProductionEnv && process.env.WEBPACK_SERVE && 'react-refresh/babel',
20
23
  isProductionEnv && ['babel-plugin-transform-react-remove-prop-types',
21
24
  {
22
25
  removeImport: true
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # ReactOnRails Development Server
5
+ #
6
+ # This script provides a simple interface to the ReactOnRails development
7
+ # server management. The core logic is implemented in ReactOnRails::Dev
8
+ # classes for better maintainability and testing.
9
+ #
10
+ # Each command uses a specific Procfile for process management:
11
+ # - bin/dev (default/hmr): Uses Procfile.dev
12
+ # - bin/dev static: Uses Procfile.dev-static-assets
13
+ # - bin/dev prod: Uses Procfile.dev-prod-assets
14
+ #
15
+ # To customize development environment:
16
+ # 1. Edit the appropriate Procfile to modify which processes run
17
+ # 2. Modify this script for project-specific command-line behavior
18
+ # 3. Extend ReactOnRails::Dev classes in your Rails app for advanced customization
19
+ # 4. Use classes directly: ReactOnRails::Dev::ServerManager.start(:development, "Custom.procfile")
20
+
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"
28
+
29
+ # Main execution
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)
@@ -20,12 +20,12 @@ ReactOnRails.configure do |config|
20
20
  #
21
21
  # ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
22
22
  #
23
- # with rspec then this controls what yarn command is run
23
+ # with rspec then this controls what npm command is run
24
24
  # to automatically refresh your webpack assets on every test run.
25
25
  #
26
26
  # Alternately, you can remove the `ReactOnRails::TestHelper.configure_rspec_to_compile_assets`
27
- # and set the config/<%= config[:packer_type] %>.yml option for test to true.
28
- config.build_test_command = "RAILS_ENV=test bin/<%= config[:packer_type] %>"
27
+ # and set the config/shakapacker.yml option for test to true.
28
+ config.build_test_command = "RAILS_ENV=test bin/shakapacker"
29
29
 
30
30
  ################################################################################
31
31
  ################################################################################
@@ -43,16 +43,25 @@ ReactOnRails.configure do |config|
43
43
  #
44
44
  config.server_bundle_js_file = "server-bundle.js"
45
45
 
46
+ # Configure where server bundles are output. Defaults to "ssr-generated".
47
+ # This should match your webpack configuration for server bundles.
48
+ config.server_bundle_output_path = "ssr-generated"
49
+
50
+ # Enforce that server bundles are only loaded from private (non-public) directories.
51
+ # When true, server bundles will only be loaded from the configured server_bundle_output_path.
52
+ # This is recommended for production to prevent server-side code from being exposed.
53
+ config.enforce_private_server_bundles = true
54
+
46
55
  ################################################################################
47
56
  ################################################################################
48
57
  # FILE SYSTEM BASED COMPONENT REGISTRY
49
58
  ################################################################################
50
59
  # `components_subdirectory` is the name of the matching directories that contain automatically registered components
51
60
  # for use in the Rails views. The default is nil, you can enable the feature by updating it in the next line.
52
- # config.components_subdirectory = "ror_components"
61
+ config.components_subdirectory = "ror_components"
53
62
  #
54
63
  # For automated component registry, `render_component` view helper method tries to load bundle for component from
55
64
  # generated directory. default is false, you can pass option at the time of individual usage or update the default
56
65
  # in the following line
57
- config.auto_load_bundle = false
66
+ config.auto_load_bundle = true
58
67
  end
@@ -1,43 +1,109 @@
1
1
  # Note: You must restart bin/shakapacker-dev-server for changes to take effect
2
+ # This file contains the defaults used by shakapacker.
2
3
 
3
4
  default: &default
4
5
  source_path: app/javascript
6
+
7
+ # You can have a subdirectory of the source_path, like 'packs' (recommended).
8
+ # Alternatively, you can use '/' to use the whole source_path directory.
9
+ # Notice that this is a relative path to source_path
5
10
  source_entry_path: packs
11
+
12
+ # If nested_entries is true, then we'll pick up subdirectories within the source_entry_path.
13
+ # You cannot set this option to true if you set source_entry_path to '/'
14
+ nested_entries: true
15
+
16
+ # While using a File-System-based automated bundle generation feature, miscellaneous warnings suggesting css order
17
+ # conflicts may arise due to the mini-css-extract-plugin. For projects where css ordering has been mitigated through
18
+ # consistent use of scoping or naming conventions, the css order warnings can be disabled by setting
19
+ # css_extract_ignore_order_warnings to true
20
+ css_extract_ignore_order_warnings: false
21
+
6
22
  public_root_path: public
7
23
  public_output_path: packs
8
24
  cache_path: tmp/shakapacker
9
25
  webpack_compile_output: true
26
+ # See https://github.com/shakacode/shakapacker#deployment
27
+ shakapacker_precompile: true
10
28
 
11
- # Additional paths webpack should lookup modules
29
+ # Location for manifest.json, defaults to {public_output_path}/manifest.json if unset
30
+ # manifest_path: public/packs/manifest.json
31
+
32
+ # Additional paths webpack should look up modules
12
33
  # ['app/assets', 'engine/foo/app/assets']
13
34
  additional_paths: []
14
35
 
15
36
  # Reload manifest.json on all requests so we reload latest compiled packs
16
37
  cache_manifest: false
17
38
 
39
+ # Select loader to use, available options are 'babel' (default), 'swc' or 'esbuild'
40
+ webpack_loader: 'babel'
41
+
42
+ # Raises an error if there is a mismatch in the shakapacker gem and npm package being used
43
+ ensure_consistent_versioning: true
44
+
45
+ # Select whether the compiler will use SHA digest ('digest' option) or most recent modified timestamp ('mtime') to determine freshness
46
+ compiler_strategy: digest
47
+
48
+ # Select whether the compiler will always use a content hash and not just in production
49
+ # Don't use contentHash except for production for performance
50
+ # https://webpack.js.org/guides/build-performance/#avoid-production-specific-tooling
51
+ useContentHash: false
52
+
53
+ # Setting the asset host here will override Rails.application.config.asset_host.
54
+ # Here, you can set different asset_host per environment. Note that
55
+ # SHAKAPACKER_ASSET_HOST will override both configurations.
56
+ # asset_host: custom-path
57
+
58
+ # Utilizing webpack-subresource-integrity plugin, will generate integrity hashes for all entries in manifest.json
59
+ # https://github.com/waysact/webpack-subresource-integrity/tree/main/webpack-subresource-integrity
60
+ integrity:
61
+ enabled: false
62
+ # Which cryptographic function(s) to use, for generating the integrity hash(es). Default sha-384. Other possible values sha256, sha512
63
+ hash_functions: ["sha384"]
64
+ # Default "anonymous". Other possible value "use-credentials"
65
+ # https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity#cross-origin_resource_sharing_and_subresource_integrity
66
+ cross_origin: "anonymous"
67
+
18
68
  development:
19
69
  <<: *default
20
- # This is false since we're running `bin/shakapacker -w` in Procfile.dev-static
21
- compile: false
70
+ compile: true
71
+ compiler_strategy: mtime
22
72
 
23
73
  # Reference: https://webpack.js.org/configuration/dev-server/
74
+ # Keys not described there are documented inline and in https://github.com/shakacode/shakapacker/
24
75
  dev_server:
25
- https: false
76
+ # For running dev server with https, set `server: https`.
77
+ # server: https
78
+
26
79
  host: localhost
27
80
  port: 3035
28
81
  # Hot Module Replacement updates modules while the application is running without a full reload
82
+ # Used instead of the `hot` key in https://webpack.js.org/configuration/dev-server/#devserverhot
29
83
  hmr: true
84
+ # If HMR is on, CSS will be inlined by delivering it as part of the script payload via style-loader. Be sure
85
+ # that you add style-loader to your project dependencies.
86
+ #
87
+ # If you want to instead deliver CSS via <link> with the mini-css-extract-plugin, set inline_css to false.
88
+ # In that case, style-loader is not needed as a dependency.
89
+ #
90
+ # mini-css-extract-plugin is a required dependency in both cases.
91
+ inline_css: true
92
+ # Defaults to the inverse of hmr. Uncomment to manually set this.
93
+ # live_reload: true
30
94
  client:
31
95
  # Should we show a full-screen overlay in the browser when there are compiler errors or warnings?
32
96
  overlay: true
33
97
  # May also be a string
34
98
  # webSocketURL:
35
- # hostname: "0.0.0.0"
36
- # pathname: "/ws"
99
+ # hostname: '0.0.0.0'
100
+ # pathname: '/ws'
37
101
  # port: 8080
102
+ # Should we use gzip compression?
38
103
  compress: true
39
104
  # Note that apps that do not check the host are vulnerable to DNS rebinding attacks
40
- allowed_hosts: [ 'localhost' ]
105
+ allowed_hosts: 'auto'
106
+ # Shows progress and colorizes output of bin/shakapacker[-dev-server]
41
107
  pretty: true
42
108
  headers:
43
109
  'Access-Control-Allow-Origin': '*'
@@ -58,5 +124,8 @@ production:
58
124
  # Production depends on precompilation of packs prior to booting for performance.
59
125
  compile: false
60
126
 
127
+ # Use content hash for naming assets. Cannot be overridden in production.
128
+ useContentHash: true
129
+
61
130
  # Cache manifest.json for performance
62
131
  cache_manifest: true
@@ -14,4 +14,4 @@ const commonOptions = {
14
14
  // Copy the object using merge b/c the baseClientWebpackConfig and commonOptions are mutable globals
15
15
  const commonWebpackConfig = () => merge({}, baseClientWebpackConfig, commonOptions);
16
16
 
17
- module.exports = commonWebpackConfig;
17
+ module.exports = commonWebpackConfig;
@@ -2,24 +2,20 @@
2
2
 
3
3
  const { devServer, inliningCss } = require('shakapacker');
4
4
 
5
- const webpackConfig = require('./webpackConfig');
5
+ const generateWebpackConfigs = require('./generateWebpackConfigs');
6
6
 
7
7
  const developmentEnvOnly = (clientWebpackConfig, _serverWebpackConfig) => {
8
- // plugins
9
- if (inliningCss) {
10
- // Note, when this is run, we're building the server and client bundles in separate processes.
11
- // Thus, this plugin is not applied to the server bundle.
12
-
8
+ // React Refresh (Fast Refresh) setup - only when webpack-dev-server is running (HMR mode)
9
+ // This matches the condition in generateWebpackConfigs.js and babel.config.js
10
+ if (process.env.WEBPACK_SERVE) {
13
11
  // eslint-disable-next-line global-require
14
12
  const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
15
13
  clientWebpackConfig.plugins.push(
16
14
  new ReactRefreshWebpackPlugin({
17
- overlay: {
18
- sockPort: devServer.port,
19
- },
15
+ // Use default overlay configuration for better compatibility
20
16
  }),
21
17
  );
22
18
  }
23
19
  };
24
20
 
25
- module.exports = webpackConfig(developmentEnvOnly);
21
+ module.exports = generateWebpackConfigs(developmentEnvOnly);
@@ -1,9 +1,9 @@
1
1
  <%= add_documentation_reference(config[:message], "// https://github.com/shakacode/react_on_rails_demo_ssr_hmr/blob/master/config/webpack/production.js") %>
2
2
 
3
- const webpackConfig = require('./webpackConfig');
3
+ const generateWebpackConfigs = require('./generateWebpackConfigs');
4
4
 
5
5
  const productionEnvOnly = (_clientWebpackConfig, _serverWebpackConfig) => {
6
6
  // place any code here that is for production only
7
7
  };
8
8
 
9
- module.exports = webpackConfig(productionEnvOnly);
9
+ module.exports = generateWebpackConfigs(productionEnvOnly);
@@ -44,13 +44,14 @@ const configureServer = () => {
44
44
 
45
45
  // Custom output for the server-bundle that matches the config in
46
46
  // config/initializers/react_on_rails.rb
47
+ // Server bundles are output to a private directory (not public) for security
47
48
  serverWebpackConfig.output = {
48
49
  filename: 'server-bundle.js',
49
50
  globalObject: 'this',
50
51
  // If using the React on Rails Pro node server renderer, uncomment the next line
51
52
  // libraryTarget: 'commonjs2',
52
- path: config.outputPath,
53
- publicPath: config.publicPath,
53
+ path: require('path').resolve(__dirname, '../../ssr-generated'),
54
+ // No publicPath needed since server bundles are not served via web
54
55
  // https://webpack.js.org/configuration/output/#outputglobalobject
55
56
  };
56
57
 
@@ -1,9 +1,9 @@
1
1
  <%= add_documentation_reference(config[:message], "// https://github.com/shakacode/react_on_rails_demo_ssr_hmr/blob/master/config/webpack/test.js") %>
2
2
 
3
- const webpackConfig = require('./webpackConfig')
3
+ const generateWebpackConfigs = require('./generateWebpackConfigs')
4
4
 
5
5
  const testOnly = (_clientWebpackConfig, _serverWebpackConfig) => {
6
6
  // place any code here that is for test only
7
7
  }
8
8
 
9
- module.exports = webpackConfig(testOnly)
9
+ module.exports = generateWebpackConfigs(testOnly)
@@ -12,8 +12,6 @@ describe "Hello World", :js do
12
12
  end
13
13
  end
14
14
 
15
- private
16
-
17
15
  def name_input
18
16
  page.first("input")
19
17
  end