react_on_rails 17.0.1 → 17.1.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/docs/agent/README.md +26 -0
- data/docs/agent/doctor-fix-loop.md +25 -0
- data/docs/agent/install-and-upgrade.md +162 -0
- data/docs/agent/rsc-adoption.md +31 -0
- data/docs/agent/streaming-debug.md +57 -0
- data/lib/generators/react_on_rails/base_generator.rb +4 -4
- data/lib/generators/react_on_rails/generator_helper.rb +12 -1
- data/lib/generators/react_on_rails/install_generator.rb +50 -5
- data/lib/generators/react_on_rails/js_dependency_manager.rb +1 -1
- data/lib/generators/react_on_rails/pro_generator.rb +5 -3
- data/lib/generators/react_on_rails/pro_setup.rb +51 -19
- data/lib/generators/react_on_rails/rsc_generator.rb +18 -1
- data/lib/generators/react_on_rails/rsc_setup/client_references.rb +63 -25
- data/lib/generators/react_on_rails/rsc_setup.rb +141 -39
- data/lib/generators/react_on_rails/templates/agent_files/AGENTS.md +31 -3
- data/lib/generators/react_on_rails/templates/base/base/{Procfile.dev-static-assets → Procfile.dev-static-assets.tt} +1 -1
- data/lib/generators/react_on_rails/templates/base/base/{Procfile.dev → Procfile.dev.tt} +1 -1
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt +18 -42
- data/lib/react_on_rails/agent_guardrails/templates/rsc_app_safety_check.rb +215 -0
- data/lib/react_on_rails/agent_guardrails/templates/rsc_app_safety_skill.md +71 -0
- data/lib/react_on_rails/agent_guardrails.rb +223 -0
- data/lib/react_on_rails/dev/service_checker.rb +1 -1
- data/lib/react_on_rails/doctor.rb +880 -48
- data/lib/react_on_rails/doctor_schema.rb +192 -0
- data/lib/react_on_rails/engine.rb +2 -2
- data/lib/react_on_rails/helper.rb +17 -10
- data/lib/react_on_rails/length_prefixed_parser.rb +22 -14
- data/lib/react_on_rails/lenient_json.rb +0 -0
- data/lib/react_on_rails/prerender_error.rb +38 -8
- data/lib/react_on_rails/react_component/render_options.rb +1 -1
- data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +168 -15
- data/lib/react_on_rails/version.rb +1 -1
- data/lib/tasks/agent_guardrails.rake +21 -0
- data/rakelib/run_rspec.rake +14 -0
- data/sig/react_on_rails/prerender_error.rbs +2 -1
- data/skills/doctor-fix-loop/SKILL.md +26 -0
- data/skills/install-and-upgrade/SKILL.md +24 -0
- data/skills/rsc-adoption/SKILL.md +28 -0
- data/skills/streaming-debug/SKILL.md +53 -0
- metadata +18 -4
- data/lib/generators/react_on_rails/templates/base/base/bin/shakapacker-watch +0 -26
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3c57ce4cf00568f0e471fb56202eda0a3483c1abdbcdd2ac75af3f7b0980fdcb
|
|
4
|
+
data.tar.gz: a5a7c6b5279f3274ece7bba232ad8d8367fab307532df3769fe222f4ad557b6b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7f42ff98daf96f7f8e5a32a7d790790b1a61e3f5fdcbe18d32962b43ea77863a2061b4b14b58a59f4a6b8e9b5ee555ce18353dcccdf591776ba9480827237e14
|
|
7
|
+
data.tar.gz: 79891caa83aea2acaa89f37a5351b22a6ac8ac30c80f458d9ad4c4bc1bb2df461e2fa212297d03ad7f06beef37b29cd7ec3dd723c477c5cbb2192eeb08a4985d
|
data/Gemfile.lock
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# React on Rails bundled agent reference
|
|
2
|
+
|
|
3
|
+
These guides and skills ship with the installed React on Rails version. Read them before relying on
|
|
4
|
+
training data or hosted documentation, which may describe a different release.
|
|
5
|
+
|
|
6
|
+
| Workflow | Skill | Guide |
|
|
7
|
+
| ----------------------------------- | ------------------------------------------------------------------ | --------------------------------- |
|
|
8
|
+
| Install or upgrade | [`install-and-upgrade`](../../skills/install-and-upgrade/SKILL.md) | [Guide](./install-and-upgrade.md) |
|
|
9
|
+
| Adopt React Server Components (Pro) | [`rsc-adoption`](../../skills/rsc-adoption/SKILL.md) | [Guide](./rsc-adoption.md) |
|
|
10
|
+
| Debug streaming SSR (Pro) | [`streaming-debug`](../../skills/streaming-debug/SKILL.md) | [Guide](./streaming-debug.md) |
|
|
11
|
+
| Iterate with the doctor | [`doctor-fix-loop`](../../skills/doctor-fix-loop/SKILL.md) | [Guide](./doctor-fix-loop.md) |
|
|
12
|
+
|
|
13
|
+
The gem and npm package contain byte-identical copies. Use the installed gem directory as the
|
|
14
|
+
canonical, reliable lookup:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bundle show react_on_rails
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Read `docs/agent/README.md` and the `skills/` paths from the returned directory. An optional
|
|
21
|
+
direct-dependency path is `node_modules/react-on-rails/`, but use it only when that directory
|
|
22
|
+
exists. Do not assume it exists: Pro installations may omit the direct base npm dependency, and
|
|
23
|
+
pnpm may isolate a transitive copy.
|
|
24
|
+
|
|
25
|
+
For broader explanation after reading the installed references, use the hosted documentation:
|
|
26
|
+
https://reactonrails.com/docs/.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Doctor-driven fix loop
|
|
2
|
+
|
|
3
|
+
Use the stable JSON form for agent and CI workflows:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bin/rails react_on_rails:doctor FORMAT=json
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Standard output is one JSON document. The top-level `status`, `checks`, and `summary` describe the
|
|
10
|
+
run. Every check exposes a stable `id`, `status`, `severity`, `message`, `fix_command`, `docs_url`,
|
|
11
|
+
`remediation`, and `details` contract.
|
|
12
|
+
|
|
13
|
+
For every non-passing check, in array order:
|
|
14
|
+
|
|
15
|
+
1. Record `id` and `severity`.
|
|
16
|
+
2. Inspect `message`, `remediation.files`, and the current app state.
|
|
17
|
+
3. Use `remediation.prompt` and `remediation.expected_end_state` as bounded guidance.
|
|
18
|
+
4. Review the smallest corrective diff.
|
|
19
|
+
5. Rerun the JSON doctor and confirm the check passes without creating another failure.
|
|
20
|
+
|
|
21
|
+
Exit code `1` means at least one error remains. Warnings keep exit code `0`, so read `status` or
|
|
22
|
+
`summary.warn` when warning-free output matters. A `null` `fix_command` is expected; never infer an
|
|
23
|
+
unsafe global repair command from the check ID.
|
|
24
|
+
|
|
25
|
+
Secondary reference: https://reactonrails.com/docs/api-reference/doctor.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Install and upgrade
|
|
2
|
+
|
|
3
|
+
Use this guide for an existing Rails app. Keep app-specific choices and custom configuration intact.
|
|
4
|
+
|
|
5
|
+
## Preflight
|
|
6
|
+
|
|
7
|
+
- Record the Rails, Ruby, Node, Shakapacker, React on Rails gem, npm package, and package-manager state.
|
|
8
|
+
- Identify both Ruby and JavaScript lockfiles and start from a reviewable working tree.
|
|
9
|
+
- Keep the gem and npm package on the same React on Rails release. A Pro app must also update its Pro
|
|
10
|
+
gem and JavaScript packages together.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
Add Shakapacker and React on Rails through Bundler.
|
|
15
|
+
Choose exactly one stack flag before running the install generator.
|
|
16
|
+
Use `--standard-only` for OSS, `--pro` for Pro, or `--rsc` for Pro with RSC.
|
|
17
|
+
Do not rely on an interactive TTY prompt; replace `<STACK_FLAG>` explicitly in every command.
|
|
18
|
+
Choose the language explicitly too: replace `<LANGUAGE_CHOICE>` with `--typescript` for TypeScript,
|
|
19
|
+
or remove that placeholder for JavaScript. TypeScript is optional; omitting its flag preserves JavaScript.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
bundle add shakapacker --strict
|
|
23
|
+
bundle add react_on_rails --strict
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
For `--pro` or `--rsc`, React on Rails Pro is free for evaluation and non-production use;
|
|
27
|
+
production use requires a subscription. Review [Pro licensing and setup](https://reactonrails.com/docs/pro/upgrading-to-pro/)
|
|
28
|
+
before proceeding.
|
|
29
|
+
|
|
30
|
+
Skip this Pro-gem preparation for `--standard-only`.
|
|
31
|
+
For `--pro` or `--rsc` only, derive the exact installed base-gem version before adding the Pro gem:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
ROR_GEM_VERSION="$(bundle exec ruby -rreact_on_rails/version -e 'print ReactOnRails::VERSION')"
|
|
35
|
+
bundle add react_on_rails_pro --version="${ROR_GEM_VERSION}" --strict
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
If that exact version is an unpublished prerelease, require a matching local/path `react_on_rails_pro` gem
|
|
39
|
+
and use the same exact version instead of the registry command:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
bundle add react_on_rails_pro --path="<path-to-matching-react_on_rails_pro>" --version="${ROR_GEM_VERSION}" --strict
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Never fall back silently to a stable Pro gem.
|
|
46
|
+
For `--pro` or `--rsc` only, after either Pro source command succeeds, remove the direct base-gem declaration:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
bundle remove react_on_rails
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This cleanup removes only the direct `react_on_rails` declaration; `react_on_rails_pro` retains the matching base gem
|
|
53
|
+
transitively. Do not run this cleanup for `--standard-only`.
|
|
54
|
+
|
|
55
|
+
After the conditional Pro preparation, run the generator with the explicit stack flag:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
bundle exec rails generate react_on_rails:install <LANGUAGE_CHOICE> <STACK_FLAG>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Use the app's declared JavaScript package manager if the generator prints a manual install command.
|
|
62
|
+
Review the generated initializer, Shakapacker configuration, scripts, routes, and sample files.
|
|
63
|
+
|
|
64
|
+
## Upgrade
|
|
65
|
+
|
|
66
|
+
1. Record the installed Ruby gem version as `CURRENT_VERSION`:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
CURRENT_VERSION="$(bundle exec ruby -rreact_on_rails/version -e 'puts ReactOnRails::VERSION')"
|
|
70
|
+
printf '%s\n' "$CURRENT_VERSION"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
2. Choose and verify explicit Ruby and npm target versions before editing dependency pins. Record
|
|
74
|
+
the Ruby/tag form as `GEM_TARGET_VERSION` and the npm semver form as `NPM_TARGET_VERSION`.
|
|
75
|
+
Ruby prereleases use dot notation (for example, `17.0.0.rc.6`); npm prereleases use hyphen notation
|
|
76
|
+
(for example, `17.0.0-rc.6`). Choose both explicitly rather than deriving either from
|
|
77
|
+
`CURRENT_VERSION` or from the other target.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
GEM_TARGET_VERSION="<Ruby/tag version you selected>"
|
|
81
|
+
NPM_TARGET_VERSION="<npm semver version you selected>"
|
|
82
|
+
git ls-remote --exit-code --tags https://github.com/shakacode/react_on_rails.git \
|
|
83
|
+
"refs/tags/v${GEM_TARGET_VERSION}"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
3. Read the changelog and upgrade guidance from the immutable repository tag `v<GEM_TARGET_VERSION>`:
|
|
87
|
+
`https://github.com/shakacode/react_on_rails/tree/v<GEM_TARGET_VERSION>`. Compare the complete
|
|
88
|
+
`CURRENT_VERSION..GEM_TARGET_VERSION` release range before changing dependencies:
|
|
89
|
+
`https://github.com/shakacode/react_on_rails/compare/v<CURRENT_VERSION>...v<GEM_TARGET_VERSION>`.
|
|
90
|
+
|
|
91
|
+
## JavaScript target matrix
|
|
92
|
+
|
|
93
|
+
Use exactly the row matching the detected stack. Verify every relevant package version before
|
|
94
|
+
pinning it; checking only the base package is insufficient. Use the app's declared package manager's
|
|
95
|
+
registry-inspection command for every exact package spec below. Do not assume pnpm, npm, Yarn, or Bun
|
|
96
|
+
is available merely because another package manager is installed.
|
|
97
|
+
|
|
98
|
+
- **OSS (`--standard-only`)**: pin and verify `react-on-rails@${NPM_TARGET_VERSION}`.
|
|
99
|
+
|
|
100
|
+
- **Pro (all renderers)**: pin and verify `react-on-rails-pro@${NPM_TARGET_VERSION}`. Also pin and verify
|
|
101
|
+
`react-on-rails-pro-node-renderer@${NPM_TARGET_VERSION}` only when the app uses the standalone NodeRenderer.
|
|
102
|
+
Do not add a direct `react-on-rails` dependency to Pro or RSC apps.
|
|
103
|
+
|
|
104
|
+
- **RSC (`--rsc`)**: follow the Pro rule, including its conditional standalone NodeRenderer package
|
|
105
|
+
and prohibition on a direct base dependency. Select `RSC_TARGET_VERSION` independently from the
|
|
106
|
+
target release guidance or the target tag's
|
|
107
|
+
`ReactOnRails::Generators::JsDependencyManager::RSC_PACKAGE_VERSION_PIN`:
|
|
108
|
+
`https://github.com/shakacode/react_on_rails/blob/v<GEM_TARGET_VERSION>/react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb`.
|
|
109
|
+
Never derive `RSC_TARGET_VERSION` from `NPM_TARGET_VERSION`. Pin and verify the exact
|
|
110
|
+
`react-on-rails-rsc@${RSC_TARGET_VERSION}` release.
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
RSC_TARGET_VERSION="<exact RSC pin from target guidance>"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
4. Pin Ruby gems with `GEM_TARGET_VERSION` and regenerate the Bundler lockfile.
|
|
117
|
+
Apply exactly the matching JavaScript matrix row with the app's declared package manager, pin each
|
|
118
|
+
package to the verified exact version, and regenerate the JavaScript lockfile.
|
|
119
|
+
5. Detect the app's existing stack and current generator choices from its dependencies and
|
|
120
|
+
configuration. Use the one preserving mapping that matches the existing stack:
|
|
121
|
+
- Use `--standard-only` for an existing OSS stack.
|
|
122
|
+
- Use `--pro` only for an existing Pro + NodeRenderer stack.
|
|
123
|
+
- Use `--rsc` for an existing RSC stack.
|
|
124
|
+
|
|
125
|
+
Pro + ExecJS has no preserving install-generator flag.
|
|
126
|
+
For that stack, skip both generator preview and apply steps; upgrade dependencies and configuration manually.
|
|
127
|
+
Preserve the ExecJS renderer setup. For the three preserving mappings, preserve the current stack.
|
|
128
|
+
Preserve every other current generator choice, such as TypeScript, Redux, or generated
|
|
129
|
+
test-framework choices.
|
|
130
|
+
|
|
131
|
+
6. For a stack with a preserving mapping, first rerun the generator as a preview, substituting the
|
|
132
|
+
selected stack flag and the app's other current choices:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
bundle exec rails generate react_on_rails:install --pretend <STACK_FLAG> <OTHER_CURRENT_CHOICES>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`--pretend` omits dependency installation and script effects; it is not a complete mutation audit.
|
|
139
|
+
Review the complete preview without overwriting app-owned configuration. Never prescribe or run a
|
|
140
|
+
bare, non-TTY generator rerun.
|
|
141
|
+
|
|
142
|
+
7. Only for a stack with a preserving mapping, and only after reviewing and accepting the preview,
|
|
143
|
+
apply the same choices without `--pretend` in a clean branch or worktree:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
bundle exec rails generate react_on_rails:install <STACK_FLAG> <OTHER_CURRENT_CHOICES>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Before accepting any generated change, audit `package.json` and the JavaScript lockfile against
|
|
150
|
+
their pre-run state. The apply step installs the generator's current dependency defaults even
|
|
151
|
+
though `--pretend` skipped them; preserve intentional app pins and reject unintended upgrades.
|
|
152
|
+
|
|
153
|
+
8. Run the doctor loop, compile assets, and run focused application tests.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
bin/rails react_on_rails:doctor FORMAT=json
|
|
157
|
+
bundle exec rails shakapacker:compile
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Secondary references:
|
|
161
|
+
https://reactonrails.com/docs/getting-started/existing-rails-app and
|
|
162
|
+
https://reactonrails.com/docs/upgrading/upgrading-react-on-rails.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# React Server Components adoption (Pro)
|
|
2
|
+
|
|
3
|
+
React Server Components require React on Rails Pro. Use the generator and installed package contracts;
|
|
4
|
+
do not reconstruct the RSC build or renderer protocol from memory.
|
|
5
|
+
|
|
6
|
+
## Adoption sequence
|
|
7
|
+
|
|
8
|
+
1. Confirm the Pro gem/npm packages, Node renderer, React, React DOM, and RSC package are compatible.
|
|
9
|
+
2. Preserve the app's language when running the generator: use
|
|
10
|
+
`bundle exec rails generate react_on_rails:rsc --typescript` for a TypeScript app and
|
|
11
|
+
`bundle exec rails generate react_on_rails:rsc` for a JavaScript app. Review the generated client,
|
|
12
|
+
server, and RSC bundle configuration.
|
|
13
|
+
3. Move one leaf at a time. The `'use client'` directive marks the server-to-client boundary, not
|
|
14
|
+
every module that will execute on the client. Modules imported below that boundary remain client code
|
|
15
|
+
even when they lack the directive; place it only where a subtree needs browser APIs, state, effects,
|
|
16
|
+
or event handlers.
|
|
17
|
+
4. Keep `.client.` and `.server.` suffixes conceptually separate: they select bundle placement and do
|
|
18
|
+
not classify React Server Components.
|
|
19
|
+
5. Keep Rails-owned data and security decisions in Rails. Supply data through props or async props;
|
|
20
|
+
server components do not gain in-process access to Rails models, sessions, or cookies.
|
|
21
|
+
6. Install the app safety guardrails and review the generated skill and advisory hook:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bundle exec rake react_on_rails:install_rsc_agent_guardrails
|
|
25
|
+
bin/rails react_on_rails:doctor FORMAT=json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Before completion, protect any RSC payload route, keep the Node renderer private, avoid sensitive
|
|
29
|
+
logged props or URLs, build all three bundles, and exercise navigation and hydration in a browser.
|
|
30
|
+
|
|
31
|
+
Secondary reference: https://reactonrails.com/docs/pro/react-server-components.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Streaming SSR debugging (Pro)
|
|
2
|
+
|
|
3
|
+
Streaming requires React on Rails Pro and the Node renderer. First determine whether the selected
|
|
4
|
+
helper is progressive or intentionally buffered.
|
|
5
|
+
|
|
6
|
+
## Debug in boundary order
|
|
7
|
+
|
|
8
|
+
1. Run `bin/rails react_on_rails:doctor FORMAT=json` and resolve configuration or dependency errors.
|
|
9
|
+
2. Identify the exact helper before applying streaming prerequisites.
|
|
10
|
+
|
|
11
|
+
## Progressive helpers
|
|
12
|
+
|
|
13
|
+
For `stream_react_component`, `cached_stream_react_component`, and
|
|
14
|
+
`stream_react_component_with_async_props`:
|
|
15
|
+
|
|
16
|
+
1. Confirm the controller includes `ReactOnRailsPro::Stream`, the action enters the view through
|
|
17
|
+
`stream_view_containing_react_components` instead of ordinary `render`, and the Node renderer
|
|
18
|
+
is reachable.
|
|
19
|
+
2. When Suspense content should reveal progressively, confirm the boundary's child actually suspends.
|
|
20
|
+
3. For `stream_react_component_with_async_props` only, confirm
|
|
21
|
+
`ReactOnRailsPro.configuration.enable_rsc_support` is enabled by setting
|
|
22
|
+
`config.enable_rsc_support = true` in the Pro initializer.
|
|
23
|
+
This is not a prerequisite for `stream_react_component` or `cached_stream_react_component`.
|
|
24
|
+
4. For async props, start slow work inside the streaming block, emit only JSON-serializable values,
|
|
25
|
+
and emit or reject every prop the renderer requests.
|
|
26
|
+
|
|
27
|
+
## Buffered helpers
|
|
28
|
+
|
|
29
|
+
For `buffered_stream_react_component`, `cached_buffered_stream_react_component`, and
|
|
30
|
+
`cached_static_rsc_component`:
|
|
31
|
+
|
|
32
|
+
1. Confirm the Node renderer is reachable and inspect the complete buffered result.
|
|
33
|
+
2. These helpers do not require the streaming controller wrapper: they do not require
|
|
34
|
+
`ReactOnRailsPro::Stream`, `stream_view_containing_react_components`, or a suspending boundary.
|
|
35
|
+
3. For `cached_static_rsc_component`, set `config.enable_rsc_support = true`; disabling RSC support
|
|
36
|
+
omits the RSC payload generation that gives this helper its static-RSC behavior.
|
|
37
|
+
This setting is not a prerequisite for `buffered_stream_react_component` or
|
|
38
|
+
`cached_buffered_stream_react_component`.
|
|
39
|
+
4. Expect the browser to receive the page only after the renderer has produced the complete result;
|
|
40
|
+
early shell flush and progressive Suspense reveal are not buffered-helper behavior.
|
|
41
|
+
|
|
42
|
+
## Debug either path
|
|
43
|
+
|
|
44
|
+
Probe the origin with buffering disabled in the client, then compare the same route through each
|
|
45
|
+
reverse proxy or CDN. Inspect response headers, chunk arrival, compression, Rails logs, Node
|
|
46
|
+
renderer logs, and browser timing marks.
|
|
47
|
+
|
|
48
|
+
A useful transport probe is:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
curl --no-buffer --dump-header - https://your-app.example/streaming-route
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
If the origin streams but the public route buffers, fix the proxy/CDN boundary. If neither streams,
|
|
55
|
+
reduce to one Suspense boundary and one known-slow value before changing application architecture.
|
|
56
|
+
|
|
57
|
+
Secondary reference: https://reactonrails.com/docs/pro/streaming-ssr.
|
|
@@ -251,9 +251,7 @@ module ReactOnRails
|
|
|
251
251
|
ensure_new_app_root_route_initialized
|
|
252
252
|
|
|
253
253
|
base_path = "base/base/"
|
|
254
|
-
base_files = %w[Procfile.dev
|
|
255
|
-
Procfile.dev-static-assets
|
|
256
|
-
Procfile.dev-prod-assets
|
|
254
|
+
base_files = %w[Procfile.dev-prod-assets
|
|
257
255
|
.dev-services.yml.example
|
|
258
256
|
.env.example
|
|
259
257
|
bin/shakapacker-precompile-hook]
|
|
@@ -262,7 +260,9 @@ module ReactOnRails
|
|
|
262
260
|
# Exception: Redux still needs the HelloWorld controller even with RSC
|
|
263
261
|
base_files << "app/controllers/hello_world_controller.rb" unless use_rsc? && !options.redux?
|
|
264
262
|
base_files << "app/controllers/home_controller.rb" if generate_new_app_home_page?
|
|
265
|
-
base_templates = %w[
|
|
263
|
+
base_templates = %w[Procfile.dev
|
|
264
|
+
Procfile.dev-static-assets
|
|
265
|
+
config/initializers/react_on_rails.rb]
|
|
266
266
|
base_files.each { |file| copy_file("#{base_path}#{file}", file) }
|
|
267
267
|
copy_react_on_rails_default_layout(base_path)
|
|
268
268
|
warn_existing_hello_world_tailwind_layout
|
|
@@ -85,7 +85,7 @@ module GeneratorHelper
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
def add_documentation_reference(message, source)
|
|
88
|
-
"#{message}
|
|
88
|
+
"#{message}\n#{source}"
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
def print_generator_messages
|
|
@@ -492,6 +492,17 @@ module GeneratorHelper
|
|
|
492
492
|
nil
|
|
493
493
|
end
|
|
494
494
|
|
|
495
|
+
# Prefer Shakapacker's optional watcher binstub when the application has an executable copy. Older
|
|
496
|
+
# supported Shakapacker installations can still run watch mode through the required
|
|
497
|
+
# bin/shakapacker binstub, so React on Rails does not need to vendor a fallback watcher.
|
|
498
|
+
def shakapacker_watch_command
|
|
499
|
+
watch_binstub = File.join(destination_root, "bin/shakapacker-watch")
|
|
500
|
+
watch_binstub_available = File.file?(watch_binstub) && File.executable?(watch_binstub)
|
|
501
|
+
executable = watch_binstub_available ? "bin/shakapacker-watch" : "bin/shakapacker"
|
|
502
|
+
|
|
503
|
+
"#{executable} --watch"
|
|
504
|
+
end
|
|
505
|
+
|
|
495
506
|
# Check if Shakapacker 9.0 or higher is available
|
|
496
507
|
# Returns true if Shakapacker >= 9.0, false otherwise
|
|
497
508
|
#
|
|
@@ -86,16 +86,22 @@ module ReactOnRails
|
|
|
86
86
|
desc: "Write AI-agent guidance files (AGENTS.md + editor pointers). Default: true"
|
|
87
87
|
|
|
88
88
|
# --pro
|
|
89
|
+
# No static default: prompt suppression needs to distinguish an omitted flag from --no-pro.
|
|
89
90
|
class_option :pro,
|
|
90
91
|
type: :boolean,
|
|
91
|
-
|
|
92
|
-
desc: "Install React on Rails Pro with Node Renderer. Default: false"
|
|
92
|
+
desc: "Install React on Rails Pro with Node Renderer"
|
|
93
93
|
|
|
94
94
|
# --rsc
|
|
95
|
+
# No static default: prompt suppression needs to distinguish an omitted flag from --no-rsc.
|
|
95
96
|
class_option :rsc,
|
|
97
|
+
type: :boolean,
|
|
98
|
+
desc: "Install React Server Components support (includes Pro)"
|
|
99
|
+
|
|
100
|
+
# --standard-only
|
|
101
|
+
class_option :standard_only,
|
|
96
102
|
type: :boolean,
|
|
97
103
|
default: false,
|
|
98
|
-
desc: "Install
|
|
104
|
+
desc: "Install only the open-source package; cannot be combined with --pro or --rsc"
|
|
99
105
|
|
|
100
106
|
# Hidden option: allows tests (and advanced users) to signal that Shakapacker
|
|
101
107
|
# was just installed, triggering force-overwrite of shakapacker.yml with RoR's template.
|
|
@@ -196,6 +202,7 @@ module ReactOnRails
|
|
|
196
202
|
# This is inherited by all invoked generators and persists through Rails initialization
|
|
197
203
|
# See lib/react_on_rails/engine.rb for the validation skip logic
|
|
198
204
|
ENV["REACT_ON_RAILS_SKIP_VALIDATION"] = "true"
|
|
205
|
+
prompt_for_pro_features_if_applicable
|
|
199
206
|
|
|
200
207
|
if installation_prerequisites_met? || options.ignore_warnings?
|
|
201
208
|
invoke_generators
|
|
@@ -230,6 +237,40 @@ module ReactOnRails
|
|
|
230
237
|
|
|
231
238
|
private
|
|
232
239
|
|
|
240
|
+
def use_pro?
|
|
241
|
+
return @interactive_pro_selection if defined?(@interactive_pro_selection)
|
|
242
|
+
|
|
243
|
+
!!super
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def prompt_for_pro_features_if_applicable
|
|
247
|
+
validate_product_stack_choice!
|
|
248
|
+
return if options.new_app? || explicit_product_stack_choice? || !interactive_install_session?
|
|
249
|
+
|
|
250
|
+
say "React on Rails Pro is free for evaluation; production use requires a subscription."
|
|
251
|
+
say "Learn more: https://reactonrails.com/docs/pro/upgrading-to-pro/"
|
|
252
|
+
answer = ask(
|
|
253
|
+
"Enable React on Rails Pro features (Node Renderer and streaming SSR; RSC available separately)? [Y/n]",
|
|
254
|
+
:cyan
|
|
255
|
+
)
|
|
256
|
+
normalized_answer = answer.to_s.strip
|
|
257
|
+
@interactive_pro_selection = normalized_answer.empty? || normalized_answer.match?(/\A(?:y|yes)\z/i)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def explicit_product_stack_choice?
|
|
261
|
+
options.key?(:pro) || options.key?(:rsc) || options.standard_only?
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def interactive_install_session?
|
|
265
|
+
!ReactOnRails::GitUtils.truthy_env?(ENV.fetch("CI", nil)) && $stdin.tty? && $stdout.tty?
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def validate_product_stack_choice!
|
|
269
|
+
return unless options.standard_only? && (options.pro? || options.rsc?)
|
|
270
|
+
|
|
271
|
+
raise Thor::Error, "--standard-only cannot be combined with --pro or --rsc"
|
|
272
|
+
end
|
|
273
|
+
|
|
233
274
|
# Fresh-install context: default to Rspack (when Shakapacker supports it) unless the
|
|
234
275
|
# app already declares a bundler. See GeneratorHelper#fresh_install_rspack_default.
|
|
235
276
|
# NOTE: BaseGenerator#rspack_bundler_default is an intentional twin of this override
|
|
@@ -866,8 +907,12 @@ module ReactOnRails
|
|
|
866
907
|
end
|
|
867
908
|
|
|
868
909
|
def product_stack_install_flag
|
|
869
|
-
return "--rsc" if
|
|
870
|
-
return "--pro" if
|
|
910
|
+
return "--rsc" if use_rsc?
|
|
911
|
+
return "--pro" if use_pro?
|
|
912
|
+
return "--standard-only" if options.standard_only?
|
|
913
|
+
return "--no-rsc" if options.key?(:rsc)
|
|
914
|
+
return "--no-pro" if options.key?(:pro)
|
|
915
|
+
return "--standard-only" if defined?(@interactive_pro_selection)
|
|
871
916
|
|
|
872
917
|
nil
|
|
873
918
|
end
|
|
@@ -177,7 +177,7 @@ module ReactOnRails
|
|
|
177
177
|
end
|
|
178
178
|
|
|
179
179
|
def add_js_dependencies
|
|
180
|
-
using_pro = respond_to?(:use_pro
|
|
180
|
+
using_pro = respond_to?(:use_pro?, true) && use_pro?
|
|
181
181
|
using_rsc = respond_to?(:use_rsc?) && use_rsc?
|
|
182
182
|
# Pro package includes react-on-rails, so skip base package when using Pro
|
|
183
183
|
add_react_on_rails_package unless using_pro
|
|
@@ -40,9 +40,11 @@ module ReactOnRails
|
|
|
40
40
|
)
|
|
41
41
|
|
|
42
42
|
setup_pro
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
unless options[:invoked_by_install]
|
|
44
|
+
add_pro_npm_dependencies
|
|
45
|
+
update_imports_to_pro_package
|
|
46
|
+
print_success_message
|
|
47
|
+
end
|
|
46
48
|
else
|
|
47
49
|
GeneratorMessages.add_error(<<~MSG.strip)
|
|
48
50
|
🚫 React on Rails Pro generator prerequisites not met!
|
|
@@ -33,6 +33,38 @@ module ReactOnRails
|
|
|
33
33
|
AUTO_INSTALL_TIMEOUT = 120
|
|
34
34
|
TERMINATION_GRACE_PERIOD = 5
|
|
35
35
|
|
|
36
|
+
# Loader helpers emitted by
|
|
37
|
+
# templates/base/base/config/webpack/serverWebpackConfig.js.tt.
|
|
38
|
+
#
|
|
39
|
+
# Keep both blocks byte-identical to that template: a fresh `--pro` install renders
|
|
40
|
+
# the template while a standalone Pro upgrade patches an existing base config, and
|
|
41
|
+
# the two must produce the same source text so drift is detectable (issue #4786).
|
|
42
|
+
GET_LOADER_PATH_JS = <<~JS
|
|
43
|
+
// Normalizes an entry of a webpack/rspack `rule.use` array to its loader path.
|
|
44
|
+
// Entries may be a bare string, a `{ loader, options }` object, or null.
|
|
45
|
+
function getLoaderPath(item) {
|
|
46
|
+
if (typeof item === 'string') return item;
|
|
47
|
+
if (item && typeof item.loader === 'string') return item.loader;
|
|
48
|
+
return '';
|
|
49
|
+
}
|
|
50
|
+
JS
|
|
51
|
+
|
|
52
|
+
EXTRACT_LOADER_JS = <<~JS
|
|
53
|
+
function extractLoader(rule, loaderName) {
|
|
54
|
+
if (!Array.isArray(rule.use)) return null;
|
|
55
|
+
return rule.use.find((item) => getLoaderPath(item).includes(loaderName));
|
|
56
|
+
}
|
|
57
|
+
JS
|
|
58
|
+
|
|
59
|
+
BUNDLER_REQUIRE_PATTERN =
|
|
60
|
+
%r{(const bundler = config\.assets_bundler.*\n.*require\('@rspack/core'\).*\n.*: require\('webpack'\);)}
|
|
61
|
+
|
|
62
|
+
# Matches any declaration of the getLoaderPath symbol, however it is written. The
|
|
63
|
+
# emitted extractLoader calls getLoaderPath, so we must never add a second
|
|
64
|
+
# declaration: `function` next to an existing `const` is a SyntaxError, not a
|
|
65
|
+
# silent shadow, and the generated config would fail to parse in Node.
|
|
66
|
+
GET_LOADER_PATH_DECLARATION = /(?:function\s+getLoaderPath\s*\(|(?:const|let|var)\s+getLoaderPath\s*=)/
|
|
67
|
+
|
|
36
68
|
# Main entry point for Pro setup.
|
|
37
69
|
# Orchestrates creation of all Pro-related files and configuration.
|
|
38
70
|
#
|
|
@@ -488,25 +520,25 @@ module ReactOnRails
|
|
|
488
520
|
# Skip if extractLoader already exists
|
|
489
521
|
return if content.include?("function extractLoader")
|
|
490
522
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
523
|
+
if content.include?(GET_LOADER_PATH_JS)
|
|
524
|
+
# Config rendered by the current base template: append extractLoader directly after
|
|
525
|
+
# the shared getLoaderPath helper so the result matches the template's Pro output.
|
|
526
|
+
gsub_file(webpack_config, GET_LOADER_PATH_JS, "#{GET_LOADER_PATH_JS}\n#{EXTRACT_LOADER_JS}")
|
|
527
|
+
elsif content.match?(GET_LOADER_PATH_DECLARATION)
|
|
528
|
+
# The app already declares getLoaderPath but has customized it (reformatted, recommented,
|
|
529
|
+
# or rewritten as an arrow function). Reuse whatever is there and emit extractLoader only.
|
|
530
|
+
# Emitting our own copy would redeclare the identifier, which is a SyntaxError next to an
|
|
531
|
+
# existing const/let and silent shadowing next to another function declaration.
|
|
532
|
+
gsub_file(webpack_config, BUNDLER_REQUIRE_PATTERN, "\\1\n\n#{EXTRACT_LOADER_JS}".chomp)
|
|
533
|
+
else
|
|
534
|
+
# Base config generated before getLoaderPath existed: emit both helpers after the
|
|
535
|
+
# bundler require so extractLoader's dependency is present.
|
|
536
|
+
gsub_file(
|
|
537
|
+
webpack_config,
|
|
538
|
+
BUNDLER_REQUIRE_PATTERN,
|
|
539
|
+
"\\1\n\n#{GET_LOADER_PATH_JS}\n#{EXTRACT_LOADER_JS}".chomp
|
|
540
|
+
)
|
|
541
|
+
end
|
|
510
542
|
end
|
|
511
543
|
|
|
512
544
|
def add_babel_ssr_caller_to_server_config(webpack_config, content)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "rails/generators"
|
|
4
|
+
require "react_on_rails/agent_guardrails"
|
|
4
5
|
require_relative "generator_helper"
|
|
5
6
|
require_relative "generator_messages"
|
|
6
7
|
require_relative "js_dependency_manager"
|
|
@@ -52,7 +53,8 @@ module ReactOnRails
|
|
|
52
53
|
if options[:invoked_by_install] || prerequisites_met?
|
|
53
54
|
warn_about_react_version_for_rsc(force: true)
|
|
54
55
|
setup_rsc
|
|
55
|
-
add_rsc_npm_dependencies
|
|
56
|
+
add_rsc_npm_dependencies unless options[:invoked_by_install]
|
|
57
|
+
install_agent_guardrails
|
|
56
58
|
print_success_message unless options[:invoked_by_install]
|
|
57
59
|
else
|
|
58
60
|
GeneratorMessages.add_error(<<~MSG.strip)
|
|
@@ -120,6 +122,21 @@ module ReactOnRails
|
|
|
120
122
|
say "✅ RSC npm dependencies added", :green
|
|
121
123
|
end
|
|
122
124
|
|
|
125
|
+
def install_agent_guardrails
|
|
126
|
+
if options[:pretend]
|
|
127
|
+
say_status :pretend, ".claude/ (RSC agent guardrails)", :yellow
|
|
128
|
+
return
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
say "🛡️ Installing RSC agent guardrails (rsc-app-safety skill + advisory hook)...", :yellow
|
|
132
|
+
ReactOnRails::AgentGuardrails.install(destination_root, skip_existing: options[:skip]).each do |action|
|
|
133
|
+
say " #{action}"
|
|
134
|
+
end
|
|
135
|
+
say "✅ RSC agent guardrails installed", :green
|
|
136
|
+
rescue ReactOnRails::AgentGuardrails::Error, SystemCallError => e
|
|
137
|
+
say "⚠️ RSC agent guardrail installation incomplete: #{e.message}", :yellow
|
|
138
|
+
end
|
|
139
|
+
|
|
123
140
|
def print_success_message
|
|
124
141
|
GeneratorMessages.add_info(<<~MSG)
|
|
125
142
|
Next steps:
|