react_on_rails 17.0.0.rc.2 → 17.0.0.rc.4
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/generators/USAGE +6 -0
- data/lib/generators/react_on_rails/base_generator.rb +20 -2
- data/lib/generators/react_on_rails/dev_tests_generator.rb +20 -1
- data/lib/generators/react_on_rails/generator_helper.rb +7 -0
- data/lib/generators/react_on_rails/install_generator.rb +58 -1
- data/lib/generators/react_on_rails/js_dependency_manager.rb +37 -0
- data/lib/generators/react_on_rails/react_no_redux_generator.rb +20 -8
- data/lib/generators/react_on_rails/react_with_redux_generator.rb +28 -4
- data/lib/generators/react_on_rails/rsc_generator.rb +5 -0
- data/lib/generators/react_on_rails/rsc_setup.rb +35 -1
- data/lib/generators/react_on_rails/templates/agent_files/.cursor/rules/react-on-rails.mdc +11 -0
- data/lib/generators/react_on_rails/templates/agent_files/.github/copilot-instructions.md +7 -0
- data/lib/generators/react_on_rails/templates/agent_files/AGENTS.md +165 -0
- data/lib/generators/react_on_rails/templates/agent_files/CLAUDE.md +8 -0
- data/lib/generators/react_on_rails/templates/base/base/bin/switch-bundler +6 -1
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/commonWebpackConfig.js.tt +81 -1
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/development.js.tt +2 -2
- data/lib/generators/react_on_rails/templates/base/tailwind/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx +30 -0
- data/lib/generators/react_on_rails/templates/base/tailwind/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.tsx +34 -0
- data/lib/generators/react_on_rails/templates/base/tailwind/app/javascript/stylesheets/application.css +1 -0
- data/lib/generators/react_on_rails/templates/dev_tests/eslint.config.mjs +50 -0
- data/lib/generators/react_on_rails/templates/redux/tailwind/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +25 -0
- data/lib/generators/react_on_rails/templates/redux/tailwind/app/javascript/bundles/HelloWorld/components/HelloWorld.tsx +29 -0
- data/lib/react_on_rails/controller/form_responders.rb +59 -0
- data/lib/react_on_rails/dev/server_manager.rb +1 -1
- data/lib/react_on_rails/doctor.rb +187 -24
- data/lib/react_on_rails/font_helper.rb +162 -0
- data/lib/react_on_rails/helper.rb +150 -0
- data/lib/react_on_rails/smart_error.rb +135 -5
- data/lib/react_on_rails/version.rb +1 -1
- data/lib/react_on_rails.rb +1 -0
- data/lib/tasks/doctor.rake +5 -2
- data/sig/react_on_rails/controller/form_responders.rbs +10 -0
- data/sig/react_on_rails/helper.rbs +3 -0
- data/sig/react_on_rails/smart_error.rbs +25 -2
- metadata +14 -2
- data/lib/generators/react_on_rails/templates/dev_tests/.eslintrc +0 -25
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1a437fef821bdc1e6bd3b289e1b627bafc9a5c5850dc1f830c15d683dc1593e2
|
|
4
|
+
data.tar.gz: 4a5679fea3c1f2b24dbbf7b6325690e0e95222b03452c9e6cca211fdf149e83d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5c905725d516131bbc94c8103953d0522a62611428c692d414b0f029299d29641f4239d4e88c054bb29aa3dfd6091133dc7c360912ba84ee5fc060540c5d26e3
|
|
7
|
+
data.tar.gz: 3b0af40fdd4fe98d75ec7fa198fd44bdafcc6f529364624e8bf42669e55d01833142698249c3d41b6587b4e65d2fbac69d96105c65667d36c58f117375529998
|
data/Gemfile.lock
CHANGED
data/lib/generators/USAGE
CHANGED
|
@@ -8,6 +8,12 @@ The react_on_rails:install generator integrates a React frontend, including SSR,
|
|
|
8
8
|
to integrate the Redux state container framework. The necessary node modules
|
|
9
9
|
will be automatically included for you.
|
|
10
10
|
|
|
11
|
+
* Tailwind CSS v4 (Optional)
|
|
12
|
+
|
|
13
|
+
Passing the --tailwind generator option installs Tailwind CSS v4, configures
|
|
14
|
+
the PostCSS plugin for Webpack or Rspack, and styles the generated
|
|
15
|
+
server-rendered Hello World example.
|
|
16
|
+
|
|
11
17
|
*******************************************************************************
|
|
12
18
|
|
|
13
19
|
After running the generator, you will want to:
|
|
@@ -41,6 +41,12 @@ module ReactOnRails
|
|
|
41
41
|
type: :boolean,
|
|
42
42
|
desc: "Use Webpack as the bundler (alias for --no-rspack; --no-webpack is equivalent to --rspack)"
|
|
43
43
|
|
|
44
|
+
# --tailwind
|
|
45
|
+
class_option :tailwind,
|
|
46
|
+
type: :boolean,
|
|
47
|
+
default: false,
|
|
48
|
+
desc: "Install Tailwind CSS v4 and style the generated SSR example"
|
|
49
|
+
|
|
44
50
|
# --pro
|
|
45
51
|
class_option :pro,
|
|
46
52
|
type: :boolean,
|
|
@@ -106,6 +112,10 @@ module ReactOnRails
|
|
|
106
112
|
generator.__send__(:use_rsc?)
|
|
107
113
|
end
|
|
108
114
|
|
|
115
|
+
def use_tailwind?
|
|
116
|
+
generator.__send__(:use_tailwind?)
|
|
117
|
+
end
|
|
118
|
+
|
|
109
119
|
def shakapacker_version_9_or_higher?
|
|
110
120
|
generator.__send__(:shakapacker_version_9_or_higher?)
|
|
111
121
|
end
|
|
@@ -228,7 +238,7 @@ module ReactOnRails
|
|
|
228
238
|
base_files = %w[app/javascript/packs/server-bundle.js]
|
|
229
239
|
|
|
230
240
|
# Skip HelloWorld CSS for Redux (uses HelloWorldApp) or RSC (uses HelloServer)
|
|
231
|
-
unless options.redux? || use_rsc?
|
|
241
|
+
unless options.redux? || use_rsc? || use_tailwind?
|
|
232
242
|
base_files << "app/javascript/src/HelloWorld/ror_components/HelloWorld.module.css"
|
|
233
243
|
end
|
|
234
244
|
|
|
@@ -259,6 +269,14 @@ module ReactOnRails
|
|
|
259
269
|
copy_webpack_main_config(base_path, config)
|
|
260
270
|
end
|
|
261
271
|
|
|
272
|
+
def copy_tailwind_files
|
|
273
|
+
return unless use_tailwind?
|
|
274
|
+
|
|
275
|
+
base_path = "base/tailwind/"
|
|
276
|
+
copy_file("#{base_path}app/javascript/stylesheets/application.css",
|
|
277
|
+
"app/javascript/stylesheets/application.css")
|
|
278
|
+
end
|
|
279
|
+
|
|
262
280
|
def copy_packer_config
|
|
263
281
|
base_path = "base/base/"
|
|
264
282
|
config = "config/shakapacker.yml"
|
|
@@ -970,7 +988,7 @@ module ReactOnRails
|
|
|
970
988
|
# Note: files originally generated with --pro or --rsc will not match when the
|
|
971
989
|
# current run omits those options; in that case, we preserve the directory.
|
|
972
990
|
# Templates rely on config[:message] plus a small helper subset exposed by
|
|
973
|
-
# TemplateRenderContext (add_documentation_reference, use_pro?, use_rsc?,
|
|
991
|
+
# TemplateRenderContext (add_documentation_reference, use_pro?, use_rsc?, use_tailwind?,
|
|
974
992
|
# shakapacker_version_9_or_higher?, rsc_plugin_class_name, rsc_plugin_import_path).
|
|
975
993
|
# Missing method delegates raise NoMethodError and are caught below, treating the
|
|
976
994
|
# file as non-removable.
|
|
@@ -8,6 +8,16 @@ module ReactOnRails
|
|
|
8
8
|
class DevTestsGenerator < Rails::Generators::Base
|
|
9
9
|
include GeneratorHelper
|
|
10
10
|
|
|
11
|
+
ESLINT_DEV_DEPENDENCIES = {
|
|
12
|
+
"@eslint/js" => "^9.0.0",
|
|
13
|
+
"eslint" => "^9.0.0",
|
|
14
|
+
"eslint-config-prettier" => "^10.0.0",
|
|
15
|
+
"eslint-plugin-import" => "^2.29.0",
|
|
16
|
+
"eslint-plugin-react" => "^7.37.5",
|
|
17
|
+
"eslint-plugin-react-hooks" => "^6.1.1",
|
|
18
|
+
"globals" => "^16.0.0"
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
11
21
|
Rails::Generators.hide_namespace(namespace)
|
|
12
22
|
source_root(File.expand_path("templates/dev_tests", __dir__))
|
|
13
23
|
|
|
@@ -24,7 +34,7 @@ module ReactOnRails
|
|
|
24
34
|
desc: "Include React Server Components test (hello_server_spec.rb)"
|
|
25
35
|
|
|
26
36
|
def copy_rspec_files
|
|
27
|
-
%w[.
|
|
37
|
+
%w[eslint.config.mjs
|
|
28
38
|
spec/spec_helper.rb
|
|
29
39
|
spec/rails_helper.rb
|
|
30
40
|
spec/simplecov_helper.rb
|
|
@@ -69,6 +79,15 @@ module ReactOnRails
|
|
|
69
79
|
|
|
70
80
|
File.open(package_json, "w+") { |f| f.puts JSON.pretty_generate(contents) }
|
|
71
81
|
end
|
|
82
|
+
|
|
83
|
+
def add_internal_eslint_dev_dependencies
|
|
84
|
+
package_json = File.join(destination_root, "package.json")
|
|
85
|
+
contents = JSON.parse(File.read(package_json))
|
|
86
|
+
contents["devDependencies"] ||= {}
|
|
87
|
+
contents["devDependencies"].merge!(ESLINT_DEV_DEPENDENCIES)
|
|
88
|
+
|
|
89
|
+
File.open(package_json, "w+") { |f| f.puts JSON.pretty_generate(contents) }
|
|
90
|
+
end
|
|
72
91
|
end
|
|
73
92
|
end
|
|
74
93
|
end
|
|
@@ -121,6 +121,13 @@ module GeneratorHelper
|
|
|
121
121
|
options[:rsc]
|
|
122
122
|
end
|
|
123
123
|
|
|
124
|
+
# Check if Tailwind CSS should be installed and wired into the generated example.
|
|
125
|
+
#
|
|
126
|
+
# @return [Boolean] true if --tailwind is set
|
|
127
|
+
def use_tailwind?
|
|
128
|
+
options[:tailwind]
|
|
129
|
+
end
|
|
130
|
+
|
|
124
131
|
# Determine if the project is using rspack as the bundler.
|
|
125
132
|
#
|
|
126
133
|
# Detection priority:
|
|
@@ -43,6 +43,12 @@ module ReactOnRails
|
|
|
43
43
|
desc: "Generate TypeScript files and install TypeScript dependencies. Default: false",
|
|
44
44
|
aliases: "-T"
|
|
45
45
|
|
|
46
|
+
# --tailwind
|
|
47
|
+
class_option :tailwind,
|
|
48
|
+
type: :boolean,
|
|
49
|
+
default: false,
|
|
50
|
+
desc: "Install Tailwind CSS v4 and style the generated SSR example. Default: false"
|
|
51
|
+
|
|
46
52
|
# --rspack / --no-rspack (Rspack is the default on fresh installs; --no-rspack selects Webpack)
|
|
47
53
|
# IMPORTANT: do NOT add a `default:` here. The absence of a default is load-bearing — Thor
|
|
48
54
|
# only includes :rspack in the options hash when the flag is explicitly passed, which is how
|
|
@@ -66,6 +72,15 @@ module ReactOnRails
|
|
|
66
72
|
default: false,
|
|
67
73
|
desc: "Skip warnings. Default: false"
|
|
68
74
|
|
|
75
|
+
# --agent-files / --no-agent-files
|
|
76
|
+
# Emits consumer-scoped AI-agent guidance (AGENTS.md) plus thin editor pointer
|
|
77
|
+
# files (CLAUDE.md, .cursor/rules/react-on-rails.mdc, .github/copilot-instructions.md).
|
|
78
|
+
# Default ON; pass --no-agent-files to skip. Existing files are never overwritten.
|
|
79
|
+
class_option :agent_files,
|
|
80
|
+
type: :boolean,
|
|
81
|
+
default: true,
|
|
82
|
+
desc: "Write AI-agent guidance files (AGENTS.md + editor pointers). Default: true"
|
|
83
|
+
|
|
69
84
|
# --pro
|
|
70
85
|
class_option :pro,
|
|
71
86
|
type: :boolean,
|
|
@@ -183,6 +198,7 @@ module ReactOnRails
|
|
|
183
198
|
add_package_json_scripts
|
|
184
199
|
add_ci_workflow
|
|
185
200
|
add_bin_scripts
|
|
201
|
+
add_agent_files
|
|
186
202
|
add_post_install_message
|
|
187
203
|
else
|
|
188
204
|
error = <<~MSG.strip
|
|
@@ -227,7 +243,7 @@ module ReactOnRails
|
|
|
227
243
|
# --pretend/--force/--skip must be forwarded explicitly at each boundary.
|
|
228
244
|
invoke "react_on_rails:base", [],
|
|
229
245
|
{ typescript: options.typescript?, redux: options.redux?, rspack: using_rspack?,
|
|
230
|
-
pro: use_pro?, rsc: use_rsc?, new_app: options.new_app?,
|
|
246
|
+
pro: use_pro?, rsc: use_rsc?, tailwind: use_tailwind?, new_app: options.new_app?,
|
|
231
247
|
shakapacker_just_installed: shakapacker_just_installed?,
|
|
232
248
|
force: options[:force], skip: options[:skip], pretend: options[:pretend] }
|
|
233
249
|
|
|
@@ -237,6 +253,7 @@ module ReactOnRails
|
|
|
237
253
|
# - Without --rsc: Normal behavior (HelloWorld or HelloWorldApp based on --redux)
|
|
238
254
|
if options.redux?
|
|
239
255
|
invoke "react_on_rails:react_with_redux", [], { typescript: options.typescript?,
|
|
256
|
+
tailwind: use_tailwind?,
|
|
240
257
|
invoked_by_install: true,
|
|
241
258
|
new_app: options.new_app?,
|
|
242
259
|
rsc: use_rsc?,
|
|
@@ -246,6 +263,7 @@ module ReactOnRails
|
|
|
246
263
|
# Only generate HelloWorld if RSC is not enabled
|
|
247
264
|
# For RSC, HelloServer replaces HelloWorld as the example component
|
|
248
265
|
invoke "react_on_rails:react_no_redux", [], { typescript: options.typescript?,
|
|
266
|
+
tailwind: use_tailwind?,
|
|
249
267
|
new_app: options.new_app?,
|
|
250
268
|
force: options[:force], skip: options[:skip],
|
|
251
269
|
pretend: options[:pretend] }
|
|
@@ -265,6 +283,7 @@ module ReactOnRails
|
|
|
265
283
|
|
|
266
284
|
invoke "react_on_rails:rsc", [], { typescript: options.typescript?, invoked_by_install: true,
|
|
267
285
|
new_app: options.new_app?, redux: options.redux?,
|
|
286
|
+
tailwind: use_tailwind?,
|
|
268
287
|
force: options[:force], skip: options[:skip],
|
|
269
288
|
pretend: options[:pretend] }
|
|
270
289
|
end
|
|
@@ -622,6 +641,40 @@ module ReactOnRails
|
|
|
622
641
|
File.chmod(0o755, *files_to_become_executable)
|
|
623
642
|
end
|
|
624
643
|
|
|
644
|
+
# Consumer-scoped AI-agent guidance written into the generated app. The canonical
|
|
645
|
+
# AGENTS.md content lives in templates/agent_files/ and is the single source of truth;
|
|
646
|
+
# create-react-on-rails-app gets it for free because it delegates to this generator.
|
|
647
|
+
# Each file is copied only when absent so we never clobber an app's existing agent files.
|
|
648
|
+
AGENT_FILES = %w[
|
|
649
|
+
AGENTS.md
|
|
650
|
+
CLAUDE.md
|
|
651
|
+
.cursor/rules/react-on-rails.mdc
|
|
652
|
+
.github/copilot-instructions.md
|
|
653
|
+
].freeze
|
|
654
|
+
private_constant :AGENT_FILES
|
|
655
|
+
|
|
656
|
+
def add_agent_files
|
|
657
|
+
return unless options.agent_files?
|
|
658
|
+
|
|
659
|
+
# AGENTS.md is the canonical file the editor pointers (CLAUDE.md, Cursor, Copilot) all
|
|
660
|
+
# reference. If the app already has its own AGENTS.md, it may document unrelated
|
|
661
|
+
# conventions, so leave it untouched AND skip the pointer files rather than emit editor
|
|
662
|
+
# guidance pointing at an AGENTS.md we did not write.
|
|
663
|
+
if File.exist?(File.join(destination_root, "AGENTS.md"))
|
|
664
|
+
say_status :skip, "AGENTS.md already exists; leaving it and the editor pointer files untouched", :yellow
|
|
665
|
+
return
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
AGENT_FILES.each do |relative_path|
|
|
669
|
+
if File.exist?(File.join(destination_root, relative_path))
|
|
670
|
+
say_status :skip, "#{relative_path} already exists; leaving it untouched", :yellow
|
|
671
|
+
next
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
copy_file("templates/agent_files/#{relative_path}", relative_path)
|
|
675
|
+
end
|
|
676
|
+
end
|
|
677
|
+
|
|
625
678
|
def replace_stock_rails_bin_dev!
|
|
626
679
|
@preserve_existing_bin_dev = false
|
|
627
680
|
|
|
@@ -722,6 +775,10 @@ module ReactOnRails
|
|
|
722
775
|
flags << "--pro"
|
|
723
776
|
end
|
|
724
777
|
|
|
778
|
+
# Preserve an explicit agent-files opt-out so the suggested re-run doesn't emit
|
|
779
|
+
# AGENTS.md/editor files a user deliberately skipped (--agent-files defaults to on).
|
|
780
|
+
flags << "--no-agent-files" unless options.agent_files?
|
|
781
|
+
|
|
725
782
|
["rails generate react_on_rails:install", *flags].join(" ")
|
|
726
783
|
end
|
|
727
784
|
|
|
@@ -83,6 +83,15 @@ module ReactOnRails
|
|
|
83
83
|
style-loader@^4.0.0
|
|
84
84
|
].freeze
|
|
85
85
|
|
|
86
|
+
# Tailwind v4 CSS-first setup. The patch-level floors match published
|
|
87
|
+
# releases verified with the generator's SSR smoke app.
|
|
88
|
+
TAILWIND_DEPENDENCIES = %w[
|
|
89
|
+
tailwindcss@^4.3.0
|
|
90
|
+
@tailwindcss/postcss@^4.3.0
|
|
91
|
+
postcss@^8.5.15
|
|
92
|
+
postcss-loader@^8.2.1
|
|
93
|
+
].freeze
|
|
94
|
+
|
|
86
95
|
# Development-only dependencies for hot reloading (Webpack)
|
|
87
96
|
# Both packages are pre-1.0, so left bare (see pinning note above).
|
|
88
97
|
DEV_DEPENDENCIES = %w[
|
|
@@ -103,6 +112,7 @@ module ReactOnRails
|
|
|
103
112
|
# @rspack/cli uses ^2.0.0-0 to match @rspack/core's prerelease range.
|
|
104
113
|
RSPACK_DEV_DEPENDENCIES = %w[
|
|
105
114
|
@rspack/cli@^2.0.0-0
|
|
115
|
+
@rspack/dev-server@^2.0.0
|
|
106
116
|
@rspack/plugin-react-refresh@^2.0.0
|
|
107
117
|
react-refresh
|
|
108
118
|
].freeze
|
|
@@ -175,6 +185,7 @@ module ReactOnRails
|
|
|
175
185
|
add_react_on_rails_package unless using_pro
|
|
176
186
|
add_react_dependencies
|
|
177
187
|
add_css_dependencies
|
|
188
|
+
add_tailwind_dependencies_if_requested
|
|
178
189
|
add_rspack_dependencies if using_rspack?
|
|
179
190
|
add_transpiler_dependencies
|
|
180
191
|
add_pro_dependencies if using_pro
|
|
@@ -290,6 +301,32 @@ module ReactOnRails
|
|
|
290
301
|
MSG
|
|
291
302
|
end
|
|
292
303
|
|
|
304
|
+
def add_tailwind_dependencies
|
|
305
|
+
say "Installing Tailwind CSS v4 dependencies..."
|
|
306
|
+
return if add_packages(TAILWIND_DEPENDENCIES)
|
|
307
|
+
|
|
308
|
+
GeneratorMessages.add_warning(<<~MSG.strip)
|
|
309
|
+
⚠️ Failed to add Tailwind CSS dependencies.
|
|
310
|
+
|
|
311
|
+
You can install them manually by running:
|
|
312
|
+
#{manual_add_packages_command(TAILWIND_DEPENDENCIES)}
|
|
313
|
+
MSG
|
|
314
|
+
rescue StandardError => e
|
|
315
|
+
GeneratorMessages.add_warning(<<~MSG.strip)
|
|
316
|
+
⚠️ Error adding Tailwind CSS dependencies: #{e.message}
|
|
317
|
+
|
|
318
|
+
You can install them manually by running:
|
|
319
|
+
#{manual_add_packages_command(TAILWIND_DEPENDENCIES)}
|
|
320
|
+
MSG
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def add_tailwind_dependencies_if_requested
|
|
324
|
+
# use_tailwind? is provided by GeneratorHelper, included alongside this module.
|
|
325
|
+
return unless use_tailwind?
|
|
326
|
+
|
|
327
|
+
add_tailwind_dependencies
|
|
328
|
+
end
|
|
329
|
+
|
|
293
330
|
def add_rspack_dependencies
|
|
294
331
|
say "Installing Rspack core dependencies..."
|
|
295
332
|
return if add_packages(RSPACK_DEPENDENCIES)
|
|
@@ -18,6 +18,11 @@ module ReactOnRails
|
|
|
18
18
|
default: false,
|
|
19
19
|
desc: "Generate TypeScript files"
|
|
20
20
|
|
|
21
|
+
class_option :tailwind,
|
|
22
|
+
type: :boolean,
|
|
23
|
+
default: false,
|
|
24
|
+
desc: "Style the generated HelloWorld example with Tailwind CSS v4"
|
|
25
|
+
|
|
21
26
|
class_option :new_app,
|
|
22
27
|
type: :boolean,
|
|
23
28
|
default: false,
|
|
@@ -25,17 +30,24 @@ module ReactOnRails
|
|
|
25
30
|
|
|
26
31
|
def copy_base_files
|
|
27
32
|
base_js_path = "base/base"
|
|
33
|
+
tailwind_js_path = "base/tailwind"
|
|
34
|
+
ext = component_extension(options)
|
|
28
35
|
|
|
29
36
|
# Determine which component files to copy based on TypeScript option
|
|
30
|
-
|
|
31
|
-
"app/javascript/src/HelloWorld/ror_components/HelloWorld.client.#{
|
|
32
|
-
|
|
33
|
-
"app/javascript/src/HelloWorld/ror_components/HelloWorld.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
client_component =
|
|
38
|
+
"app/javascript/src/HelloWorld/ror_components/HelloWorld.client.#{ext}"
|
|
39
|
+
server_component =
|
|
40
|
+
"app/javascript/src/HelloWorld/ror_components/HelloWorld.server.#{ext}"
|
|
41
|
+
|
|
42
|
+
if use_tailwind?
|
|
43
|
+
copy_file("#{tailwind_js_path}/#{client_component}", client_component)
|
|
44
|
+
else
|
|
45
|
+
copy_file("#{base_js_path}/#{client_component}", client_component)
|
|
46
|
+
copy_file("#{base_js_path}/app/javascript/src/HelloWorld/ror_components/HelloWorld.module.css",
|
|
47
|
+
"app/javascript/src/HelloWorld/ror_components/HelloWorld.module.css")
|
|
38
48
|
end
|
|
49
|
+
|
|
50
|
+
copy_file("#{base_js_path}/#{server_component}", server_component)
|
|
39
51
|
end
|
|
40
52
|
|
|
41
53
|
def create_appropriate_templates
|
|
@@ -35,6 +35,11 @@ module ReactOnRails
|
|
|
35
35
|
default: false,
|
|
36
36
|
hide: true
|
|
37
37
|
|
|
38
|
+
class_option :tailwind,
|
|
39
|
+
type: :boolean,
|
|
40
|
+
default: false,
|
|
41
|
+
hide: true
|
|
42
|
+
|
|
38
43
|
def create_redux_directories
|
|
39
44
|
# Create auto-bundling directory structure for Redux
|
|
40
45
|
empty_directory("app/javascript/src/HelloWorldApp/ror_components")
|
|
@@ -53,18 +58,33 @@ module ReactOnRails
|
|
|
53
58
|
"app/javascript/src/HelloWorldApp/ror_components/HelloWorldApp.client.#{ext}")
|
|
54
59
|
copy_file("#{base_js_path}/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.server.#{ext}",
|
|
55
60
|
"app/javascript/src/HelloWorldApp/ror_components/HelloWorldApp.server.#{ext}")
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
|
|
62
|
+
unless use_tailwind?
|
|
63
|
+
copy_file("#{base_js_path}/app/javascript/bundles/HelloWorld/components/HelloWorld.module.css",
|
|
64
|
+
"app/javascript/src/HelloWorldApp/components/HelloWorld.module.css")
|
|
65
|
+
end
|
|
58
66
|
|
|
59
67
|
# Update import paths in client component
|
|
60
68
|
ror_client_file = "app/javascript/src/HelloWorldApp/ror_components/HelloWorldApp.client.#{ext}"
|
|
61
69
|
gsub_file(ror_client_file, "../store/helloWorldStore", "../store/helloWorldStore")
|
|
62
70
|
gsub_file(ror_client_file, "../containers/HelloWorldContainer",
|
|
63
71
|
"../containers/HelloWorldContainer")
|
|
72
|
+
return unless use_tailwind?
|
|
73
|
+
|
|
74
|
+
stylesheet_import = "import '../../../stylesheets/application.css';\n"
|
|
75
|
+
ror_client_file_path = File.join(destination_root, ror_client_file)
|
|
76
|
+
if options[:pretend]
|
|
77
|
+
say_status :pretend, "Would add Tailwind stylesheet import to #{ror_client_file}", :yellow
|
|
78
|
+
return
|
|
79
|
+
end
|
|
80
|
+
return if File.read(ror_client_file_path).include?(stylesheet_import)
|
|
81
|
+
|
|
82
|
+
prepend_to_file(ror_client_file, stylesheet_import)
|
|
64
83
|
end
|
|
65
84
|
|
|
66
85
|
def copy_base_redux_files
|
|
67
86
|
base_hello_world_path = "redux/base/app/javascript/bundles/HelloWorld"
|
|
87
|
+
tailwind_hello_world_path = "redux/tailwind/app/javascript/bundles/HelloWorld"
|
|
68
88
|
redux_extension = options.typescript? ? "ts" : "js"
|
|
69
89
|
|
|
70
90
|
# Copy Redux infrastructure files with appropriate extension
|
|
@@ -72,11 +92,15 @@ module ReactOnRails
|
|
|
72
92
|
containers/HelloWorldContainer.#{redux_extension}
|
|
73
93
|
constants/helloWorldConstants.#{redux_extension}
|
|
74
94
|
reducers/helloWorldReducer.#{redux_extension}
|
|
75
|
-
store/helloWorldStore.#{redux_extension}
|
|
76
|
-
components/HelloWorld.#{component_extension(options)}].each do |file|
|
|
95
|
+
store/helloWorldStore.#{redux_extension}].each do |file|
|
|
77
96
|
copy_file("#{base_hello_world_path}/#{file}",
|
|
78
97
|
"app/javascript/src/HelloWorldApp/#{file}")
|
|
79
98
|
end
|
|
99
|
+
|
|
100
|
+
component_file = "components/HelloWorld.#{component_extension(options)}"
|
|
101
|
+
component_source_path = use_tailwind? ? tailwind_hello_world_path : base_hello_world_path
|
|
102
|
+
copy_file("#{component_source_path}/#{component_file}",
|
|
103
|
+
"app/javascript/src/HelloWorldApp/#{component_file}")
|
|
80
104
|
end
|
|
81
105
|
|
|
82
106
|
def create_appropriate_templates
|
|
@@ -42,6 +42,11 @@ module ReactOnRails
|
|
|
42
42
|
default: false,
|
|
43
43
|
hide: true
|
|
44
44
|
|
|
45
|
+
class_option :tailwind,
|
|
46
|
+
type: :boolean,
|
|
47
|
+
default: false,
|
|
48
|
+
hide: true
|
|
49
|
+
|
|
45
50
|
def run_generator
|
|
46
51
|
# When invoked by install_generator, skip prerequisites (parent already validated)
|
|
47
52
|
if options[:invoked_by_install] || prerequisites_met?
|
|
@@ -206,7 +206,13 @@ module ReactOnRails
|
|
|
206
206
|
# Check if HelloServer already exists (check both jsx and tsx)
|
|
207
207
|
if File.exist?(File.join(destination_root, "#{ror_components_dir}/HelloServer.jsx")) ||
|
|
208
208
|
File.exist?(File.join(destination_root, "#{ror_components_dir}/HelloServer.tsx"))
|
|
209
|
-
|
|
209
|
+
tailwind_import_added = add_tailwind_import_to_rsc_client_component(components_dir)
|
|
210
|
+
message = if tailwind_import_added
|
|
211
|
+
"ℹ️ HelloServer component already exists; added Tailwind stylesheet import to LikeButton"
|
|
212
|
+
else
|
|
213
|
+
"ℹ️ HelloServer component already exists, skipping"
|
|
214
|
+
end
|
|
215
|
+
say message, :yellow
|
|
210
216
|
return
|
|
211
217
|
end
|
|
212
218
|
|
|
@@ -223,10 +229,38 @@ module ReactOnRails
|
|
|
223
229
|
"#{components_dir}/HelloServer.#{ext}")
|
|
224
230
|
copy_file("templates/rsc/base/app/javascript/src/HelloServer/components/LikeButton.#{ext}",
|
|
225
231
|
"#{components_dir}/LikeButton.#{ext}")
|
|
232
|
+
add_tailwind_import_to_rsc_client_component(components_dir)
|
|
226
233
|
|
|
227
234
|
say "✅ Created HelloServer component", :green
|
|
228
235
|
end
|
|
229
236
|
|
|
237
|
+
def add_tailwind_import_to_rsc_client_component(components_dir)
|
|
238
|
+
return false unless use_tailwind?
|
|
239
|
+
|
|
240
|
+
candidate_entry_paths = %w[jsx tsx].map do |extension|
|
|
241
|
+
"#{components_dir}/LikeButton.#{extension}"
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
relative_entry_path = candidate_entry_paths.find do |entry_path|
|
|
245
|
+
File.exist?(File.join(destination_root, entry_path))
|
|
246
|
+
end
|
|
247
|
+
return false unless relative_entry_path
|
|
248
|
+
|
|
249
|
+
# Path is relative to app/javascript/src/HelloServer/components/.
|
|
250
|
+
stylesheet_import = "import '../../../stylesheets/application.css';"
|
|
251
|
+
entry_path = File.join(destination_root, relative_entry_path)
|
|
252
|
+
entry_content = File.read(entry_path)
|
|
253
|
+
return false if entry_content.include?(stylesheet_import)
|
|
254
|
+
|
|
255
|
+
client_directive_pattern = /\A\s*['"]use client['"];?[^\S\r\n]*(?:\r?\n|$)/
|
|
256
|
+
if entry_content.match?(client_directive_pattern)
|
|
257
|
+
insert_into_file(relative_entry_path, "#{stylesheet_import}\n", after: client_directive_pattern)
|
|
258
|
+
else
|
|
259
|
+
prepend_to_file(relative_entry_path, "#{stylesheet_import}\n")
|
|
260
|
+
end
|
|
261
|
+
true
|
|
262
|
+
end
|
|
263
|
+
|
|
230
264
|
def create_hello_server_controller
|
|
231
265
|
controller_path = "app/controllers/hello_server_controller.rb"
|
|
232
266
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: React on Rails conventions for this app (components, react_component helper, SSR bundles, errors)
|
|
3
|
+
globs:
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
This app uses **React on Rails**. For React on Rails conventions — adding and
|
|
8
|
+
registering components, the `react_component` view helper, `.client`/`.server`
|
|
9
|
+
bundle rules, the `ReactOnRails` JS API, common errors and fixes, and the
|
|
10
|
+
`bin/rails react_on_rails:doctor` diagnostic — read **AGENTS.md** in the project
|
|
11
|
+
root.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# GitHub Copilot instructions
|
|
2
|
+
|
|
3
|
+
This app uses **React on Rails**. For React on Rails conventions — adding and
|
|
4
|
+
registering components, the `react_component` view helper, `.client`/`.server`
|
|
5
|
+
bundle rules, the `ReactOnRails` JS API, common errors and fixes, and the
|
|
6
|
+
`bin/rails react_on_rails:doctor` diagnostic — read **AGENTS.md** in the project
|
|
7
|
+
root.
|