react_on_rails 15.0.0.alpha.1 → 15.0.0.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +479 -81
  3. data/CONTRIBUTING.md +61 -47
  4. data/Gemfile.development_dependencies +1 -1
  5. data/Gemfile.lock +4 -4
  6. data/KUDOS.md +22 -1
  7. data/NEWS.md +48 -48
  8. data/PROJECTS.md +45 -40
  9. data/README.md +24 -16
  10. data/SUMMARY.md +62 -52
  11. data/eslint.config.ts +213 -0
  12. data/knip.ts +100 -0
  13. data/lib/generators/USAGE +1 -1
  14. data/lib/generators/react_on_rails/dev_tests_generator.rb +4 -8
  15. data/lib/generators/react_on_rails/templates/.eslintrc +1 -1
  16. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.module.css +2 -2
  17. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorldServer.js +1 -1
  18. data/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/registration.js.tt +1 -1
  19. data/lib/generators/react_on_rails/templates/base/base/config/shakapacker.yml +1 -1
  20. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/reducers/helloWorldReducer.js +1 -1
  21. data/lib/react_on_rails/configuration.rb +81 -12
  22. data/lib/react_on_rails/controller.rb +4 -2
  23. data/lib/react_on_rails/engine.rb +1 -3
  24. data/lib/react_on_rails/helper.rb +184 -56
  25. data/lib/react_on_rails/locales/base.rb +7 -1
  26. data/lib/react_on_rails/packer_utils.rb +23 -3
  27. data/lib/react_on_rails/packs_generator.rb +84 -11
  28. data/lib/react_on_rails/prerender_error.rb +10 -2
  29. data/lib/react_on_rails/react_component/render_options.rb +39 -3
  30. data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +6 -2
  31. data/lib/react_on_rails/test_helper/webpack_assets_status_checker.rb +4 -2
  32. data/lib/react_on_rails/utils.rb +67 -23
  33. data/lib/react_on_rails/version.rb +1 -1
  34. data/lib/react_on_rails/version_checker.rb +34 -23
  35. data/lib/tasks/assets.rake +1 -1
  36. data/react_on_rails.gemspec +2 -2
  37. data/tsconfig.eslint.json +6 -0
  38. data/tsconfig.json +10 -6
  39. metadata +8 -7
data/knip.ts ADDED
@@ -0,0 +1,100 @@
1
+ import type { KnipConfig } from 'knip';
2
+
3
+ const config: KnipConfig = {
4
+ // ! at the end means files are used in production
5
+ workspaces: {
6
+ '.': {
7
+ entry: [
8
+ 'node_package/src/ReactOnRails.node.ts!',
9
+ 'node_package/src/ReactOnRailsRSC.ts!',
10
+ 'node_package/src/registerServerComponent/client.tsx!',
11
+ 'node_package/src/registerServerComponent/server.tsx!',
12
+ 'node_package/src/registerServerComponent/server.rsc.ts!',
13
+ 'node_package/src/wrapServerComponentRenderer/server.tsx!',
14
+ 'node_package/src/wrapServerComponentRenderer/server.rsc.tsx!',
15
+ 'node_package/src/RSCRoute.tsx!',
16
+ 'node_package/src/ServerComponentFetchError.ts!',
17
+ 'eslint.config.ts',
18
+ ],
19
+ project: ['node_package/src/**/*.[jt]s{x,}!', 'node_package/tests/**/*.[jt]s{x,}'],
20
+ babel: {
21
+ config: ['node_package/babel.config.js'],
22
+ },
23
+ ignore: ['node_package/tests/emptyForTesting.js'],
24
+ ignoreBinaries: [
25
+ // Knip fails to detect it's declared in devDependencies
26
+ 'nps',
27
+ // local scripts
28
+ 'node_package/scripts/.*',
29
+ ],
30
+ ignoreDependencies: [
31
+ // Required for TypeScript compilation, but we don't depend on Turbolinks itself.
32
+ '@types/turbolinks',
33
+ // The Knip ESLint plugin fails to detect these are transitively required by a config,
34
+ // though we don't actually use its rules anywhere.
35
+ '@babel/eslint-parser',
36
+ '@babel/preset-react',
37
+ 'eslint-config-shakacode',
38
+ 'eslint-import-resolver-alias',
39
+ 'eslint-plugin-import',
40
+ 'eslint-plugin-jsx-a11y',
41
+ 'eslint-plugin-react',
42
+ 'eslint-plugin-react-hooks',
43
+ // These are used as transitive dependencies and missing from package.json
44
+ '@eslint/eslintrc',
45
+ '@eslint/js',
46
+ // used by Jest
47
+ 'jsdom',
48
+ ],
49
+ },
50
+ 'spec/dummy': {
51
+ entry: [
52
+ 'app/assets/config/manifest.js!',
53
+ 'client/app/packs/**/*.js!',
54
+ // Not sure why this isn't detected as a dependency of client/app/packs/server-bundle.js
55
+ 'client/app/generated/server-bundle-generated.js!',
56
+ 'spec/fixtures/automated_packs_generation/**/*.js{x,}',
57
+ 'config/webpack/{production,development,test}.js',
58
+ // Declaring this as webpack.config instead doesn't work correctly
59
+ 'config/webpack/webpack.config.js',
60
+ ],
61
+ ignore: ['**/app-react16/**/*'],
62
+ project: ['**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}!', 'config/webpack/*.js'],
63
+ paths: {
64
+ 'Assets/*': ['client/app/assets/*'],
65
+ },
66
+ ignoreBinaries: [
67
+ // Has to be installed globally
68
+ 'yalc',
69
+ // Local binaries
70
+ 'bin/.*',
71
+ ],
72
+ ignoreDependencies: [
73
+ // Knip thinks it can be a devDependency, but it's supposed to be in dependencies.
74
+ '@babel/runtime',
75
+ // There's no ReScript plugin for Knip
76
+ '@rescript/react',
77
+ // The Babel plugin fails to detect it
78
+ 'babel-plugin-transform-react-remove-prop-types',
79
+ // This one is weird. It's long-deprecated and shouldn't be necessary.
80
+ // Probably need to update the Webpack config.
81
+ 'node-libs-browser',
82
+ // The below dependencies are not detected by the Webpack plugin
83
+ // due to the config issue.
84
+ 'css-loader',
85
+ 'expose-loader',
86
+ 'file-loader',
87
+ 'imports-loader',
88
+ 'mini-css-extract-plugin',
89
+ 'null-loader',
90
+ 'sass',
91
+ 'sass-loader',
92
+ 'sass-resources-loader',
93
+ 'style-loader',
94
+ 'url-loader',
95
+ ],
96
+ },
97
+ },
98
+ };
99
+
100
+ export default config;
data/lib/generators/USAGE CHANGED
@@ -1,6 +1,6 @@
1
1
  Description:
2
2
 
3
- The react_on_rails:install generator integrates webpack with rails with ease. You
3
+ The react_on_rails:install generator integrates Webpack with Rails with ease. You
4
4
  can pass the redux option if you'd like to have redux setup for you automatically.
5
5
 
6
6
  * Redux
@@ -47,14 +47,10 @@ module ReactOnRails
47
47
 
48
48
  def add_yarn_relative_install_script_in_package_json
49
49
  package_json = File.join(destination_root, "package.json")
50
- contents = File.read(package_json)
51
- replacement_value = <<-STRING
52
- "scripts": {
53
- "postinstall": "yalc link react-on-rails",
54
- STRING
55
- new_client_package_json_contents = contents.gsub(/ {2}"scripts": {/,
56
- replacement_value)
57
- File.open(package_json, "w+") { |f| f.puts new_client_package_json_contents }
50
+ contents = JSON.parse(File.read(package_json))
51
+ contents["scripts"] ||= {}
52
+ contents["scripts"]["postinstall"] = "yalc link react-on-rails"
53
+ File.open(package_json, "w+") { |f| f.puts JSON.pretty_generate(contents) }
58
54
  end
59
55
  end
60
56
  end
@@ -1,5 +1,5 @@
1
1
  ---
2
- extends:
2
+ extends:
3
3
  - eslint-config-shakacode
4
4
  - prettier
5
5
 
@@ -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,4 +1,4 @@
1
- import ReactOnRails from 'react-on-rails';
1
+ import ReactOnRails from 'react-on-rails/client';
2
2
 
3
3
  import <%= config[:component_name] %> from '<%= config[:app_relative_path] %>';
4
4
 
@@ -37,7 +37,7 @@ development:
37
37
  # port: 8080
38
38
  compress: true
39
39
  # Note that apps that do not check the host are vulnerable to DNS rebinding attacks
40
- allowed_hosts: [ 'localhost' ]
40
+ allowed_hosts: ['localhost']
41
41
  pretty: true
42
42
  headers:
43
43
  'Access-Control-Allow-Origin': '*'
@@ -1,7 +1,7 @@
1
1
  import { combineReducers } from 'redux';
2
2
  import { HELLO_WORLD_NAME_UPDATE } from '../constants/helloWorldConstants';
3
3
 
4
- const name = (state = '', action) => {
4
+ const name = (state = '', action = {}) => {
5
5
  switch (action.type) {
6
6
  case HELLO_WORLD_NAME_UPDATE:
7
7
  return action.text;
@@ -9,6 +9,9 @@ module ReactOnRails
9
9
  end
10
10
 
11
11
  DEFAULT_GENERATED_ASSETS_DIR = File.join(%w[public webpack], Rails.env).freeze
12
+ DEFAULT_REACT_CLIENT_MANIFEST_FILE = "react-client-manifest.json"
13
+ DEFAULT_REACT_SERVER_CLIENT_MANIFEST_FILE = "react-server-client-manifest.json"
14
+ DEFAULT_COMPONENT_REGISTRY_TIMEOUT = 5000
12
15
 
13
16
  def self.configuration
14
17
  @configuration ||= Configuration.new(
@@ -17,6 +20,9 @@ module ReactOnRails
17
20
  # generated_assets_dirs is deprecated
18
21
  generated_assets_dir: "",
19
22
  server_bundle_js_file: "",
23
+ rsc_bundle_js_file: "",
24
+ react_client_manifest_file: DEFAULT_REACT_CLIENT_MANIFEST_FILE,
25
+ react_server_client_manifest_file: DEFAULT_REACT_SERVER_CLIENT_MANIFEST_FILE,
20
26
  prerender: false,
21
27
  auto_load_bundle: false,
22
28
  replay_console: true,
@@ -39,9 +45,14 @@ module ReactOnRails
39
45
  i18n_output_format: nil,
40
46
  components_subdirectory: nil,
41
47
  make_generated_server_bundle_the_entrypoint: false,
42
- defer_generated_component_packs: true,
48
+ defer_generated_component_packs: false,
43
49
  # forces the loading of React components
44
- force_load: false
50
+ force_load: true,
51
+ # Maximum time in milliseconds to wait for client-side component registration after page load.
52
+ # If exceeded, an error will be thrown for server-side rendered components not registered on the client.
53
+ # Set to 0 to disable the timeout and wait indefinitely for component registration.
54
+ component_registry_timeout: DEFAULT_COMPONENT_REGISTRY_TIMEOUT,
55
+ generated_component_packs_loading_strategy: nil
45
56
  )
46
57
  end
47
58
 
@@ -52,26 +63,29 @@ module ReactOnRails
52
63
  :generated_assets_dirs, :generated_assets_dir, :components_subdirectory,
53
64
  :webpack_generated_files, :rendering_extension, :build_test_command,
54
65
  :build_production_command, :i18n_dir, :i18n_yml_dir, :i18n_output_format,
66
+ :i18n_yml_safe_load_options, :defer_generated_component_packs,
55
67
  :server_render_method, :random_dom_id, :auto_load_bundle,
56
68
  :same_bundle_for_client_and_server, :rendering_props_extension,
57
69
  :make_generated_server_bundle_the_entrypoint,
58
- :defer_generated_component_packs,
59
- :force_load
70
+ :generated_component_packs_loading_strategy, :force_load, :rsc_bundle_js_file,
71
+ :react_client_manifest_file, :react_server_client_manifest_file, :component_registry_timeout
60
72
 
61
73
  # rubocop:disable Metrics/AbcSize
62
74
  def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender: nil,
63
75
  replay_console: nil, make_generated_server_bundle_the_entrypoint: nil,
64
- trace: nil, development_mode: nil,
76
+ trace: nil, development_mode: nil, defer_generated_component_packs: nil,
65
77
  logging_on_server: nil, server_renderer_pool_size: nil,
66
78
  server_renderer_timeout: nil, raise_on_prerender_error: true,
67
79
  skip_display_none: nil, generated_assets_dirs: nil,
68
80
  generated_assets_dir: nil, webpack_generated_files: nil,
69
81
  rendering_extension: nil, build_test_command: nil,
70
- build_production_command: nil, defer_generated_component_packs: nil,
82
+ build_production_command: nil, generated_component_packs_loading_strategy: nil,
71
83
  same_bundle_for_client_and_server: nil,
72
- i18n_dir: nil, i18n_yml_dir: nil, i18n_output_format: nil,
84
+ i18n_dir: nil, i18n_yml_dir: nil, i18n_output_format: nil, i18n_yml_safe_load_options: nil,
73
85
  random_dom_id: nil, server_render_method: nil, rendering_props_extension: nil,
74
- components_subdirectory: nil, auto_load_bundle: nil, force_load: nil)
86
+ components_subdirectory: nil, auto_load_bundle: nil, force_load: nil,
87
+ rsc_bundle_js_file: nil, react_client_manifest_file: nil, react_server_client_manifest_file: nil,
88
+ component_registry_timeout: nil)
75
89
  self.node_modules_location = node_modules_location.present? ? node_modules_location : Rails.root
76
90
  self.generated_assets_dirs = generated_assets_dirs
77
91
  self.generated_assets_dir = generated_assets_dir
@@ -80,6 +94,7 @@ module ReactOnRails
80
94
  self.i18n_dir = i18n_dir
81
95
  self.i18n_yml_dir = i18n_yml_dir
82
96
  self.i18n_output_format = i18n_output_format
97
+ self.i18n_yml_safe_load_options = i18n_yml_safe_load_options
83
98
 
84
99
  self.random_dom_id = random_dom_id
85
100
  self.prerender = prerender
@@ -94,9 +109,13 @@ module ReactOnRails
94
109
  self.raise_on_prerender_error = raise_on_prerender_error
95
110
  self.skip_display_none = skip_display_none
96
111
  self.rendering_props_extension = rendering_props_extension
112
+ self.component_registry_timeout = component_registry_timeout
97
113
 
98
114
  # Server rendering:
99
115
  self.server_bundle_js_file = server_bundle_js_file
116
+ self.rsc_bundle_js_file = rsc_bundle_js_file
117
+ self.react_client_manifest_file = react_client_manifest_file
118
+ self.react_server_client_manifest_file = react_server_client_manifest_file
100
119
  self.same_bundle_for_client_and_server = same_bundle_for_client_and_server
101
120
  self.server_renderer_pool_size = self.development_mode ? 1 : server_renderer_pool_size
102
121
  self.server_renderer_timeout = server_renderer_timeout # seconds
@@ -110,6 +129,7 @@ module ReactOnRails
110
129
  self.make_generated_server_bundle_the_entrypoint = make_generated_server_bundle_the_entrypoint
111
130
  self.defer_generated_component_packs = defer_generated_component_packs
112
131
  self.force_load = force_load
132
+ self.generated_component_packs_loading_strategy = generated_component_packs_loading_strategy
113
133
  end
114
134
  # rubocop:enable Metrics/AbcSize
115
135
 
@@ -124,10 +144,56 @@ module ReactOnRails
124
144
  error_if_using_packer_and_generated_assets_dir_not_match_public_output_path
125
145
  # check_deprecated_settings
126
146
  adjust_precompile_task
147
+ check_component_registry_timeout
148
+ validate_generated_component_packs_loading_strategy
127
149
  end
128
150
 
129
151
  private
130
152
 
153
+ def check_component_registry_timeout
154
+ self.component_registry_timeout = DEFAULT_COMPONENT_REGISTRY_TIMEOUT if component_registry_timeout.nil?
155
+
156
+ return if component_registry_timeout.is_a?(Integer) && component_registry_timeout >= 0
157
+
158
+ raise ReactOnRails::Error, "component_registry_timeout must be a positive integer"
159
+ end
160
+
161
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
162
+ def validate_generated_component_packs_loading_strategy
163
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
164
+
165
+ if defer_generated_component_packs
166
+ if %i[async sync].include?(generated_component_packs_loading_strategy)
167
+ Rails.logger.warn "**WARNING** ReactOnRails: config.defer_generated_component_packs is " \
168
+ "superseded by config.generated_component_packs_loading_strategy"
169
+ else
170
+ Rails.logger.warn "[DEPRECATION] ReactOnRails: Use config." \
171
+ "generated_component_packs_loading_strategy = :defer rather than " \
172
+ "defer_generated_component_packs"
173
+ self.generated_component_packs_loading_strategy ||= :defer
174
+ end
175
+ end
176
+
177
+ msg = <<~MSG
178
+ ReactOnRails: Your current version of #{ReactOnRails::PackerUtils.packer_type.upcase_first} \
179
+ does not support async script loading, which may cause performance issues. Please either:
180
+ 1. Use :sync or :defer loading strategy instead of :async
181
+ 2. Upgrade to Shakapacker v8.2.0 or above to enable async script loading
182
+ MSG
183
+ if PackerUtils.shakapacker_version_requirement_met?([8, 2, 0])
184
+ self.generated_component_packs_loading_strategy ||= :async
185
+ elsif generated_component_packs_loading_strategy.nil?
186
+ Rails.logger.warn("**WARNING** #{msg}")
187
+ self.generated_component_packs_loading_strategy = :sync
188
+ elsif generated_component_packs_loading_strategy == :async
189
+ raise ReactOnRails::Error, "**ERROR** #{msg}"
190
+ end
191
+
192
+ return if %i[async defer sync].include?(generated_component_packs_loading_strategy)
193
+
194
+ raise ReactOnRails::Error, "generated_component_packs_loading_strategy must be either :async, :defer, or :sync"
195
+ end
196
+
131
197
  def check_autobundling_requirements
132
198
  raise_missing_components_subdirectory if auto_load_bundle && !components_subdirectory.present?
133
199
  return unless components_subdirectory.present?
@@ -239,10 +305,13 @@ module ReactOnRails
239
305
  def ensure_webpack_generated_files_exists
240
306
  return unless webpack_generated_files.empty?
241
307
 
242
- files = ["manifest.json"]
243
- files << server_bundle_js_file if server_bundle_js_file.present?
244
-
245
- self.webpack_generated_files = files
308
+ self.webpack_generated_files = [
309
+ "manifest.json",
310
+ server_bundle_js_file,
311
+ rsc_bundle_js_file,
312
+ react_client_manifest_file,
313
+ react_server_client_manifest_file
314
+ ].compact_blank
246
315
  end
247
316
 
248
317
  def configure_skip_display_none_deprecation
@@ -12,9 +12,11 @@ module ReactOnRails
12
12
  #
13
13
  # Be sure to include view helper `redux_store_hydration_data` at the end of your layout or view
14
14
  # or else there will be no client side hydration of your stores.
15
- def redux_store(store_name, props: {})
15
+ def redux_store(store_name, props: {}, force_load: nil)
16
+ force_load = ReactOnRails.configuration.force_load if force_load.nil?
16
17
  redux_store_data = { store_name: store_name,
17
- props: props }
18
+ props: props,
19
+ force_load: force_load }
18
20
  @registered_stores_defer_render ||= []
19
21
  @registered_stores_defer_render << redux_store_data
20
22
  end
@@ -5,9 +5,7 @@ require "rails/railtie"
5
5
  module ReactOnRails
6
6
  class Engine < ::Rails::Engine
7
7
  config.to_prepare do
8
- if File.exist?(VersionChecker::NodePackageVersion.package_json_path)
9
- VersionChecker.build.raise_if_gem_and_node_package_versions_differ
10
- end
8
+ VersionChecker.build.log_if_gem_and_node_package_versions_differ
11
9
  ReactOnRails::ServerRenderingPool.reset_pool
12
10
  end
13
11
  end