Skip to content

Fix Pro generator multiline and template-literal rewrites#2918

Merged
justin808 merged 3 commits into
mainfrom
jg/fix-2917
Apr 3, 2026
Merged

Fix Pro generator multiline and template-literal rewrites#2918
justin808 merged 3 commits into
mainfrom
jg/fix-2917

Conversation

@justin808

@justin808 justin808 commented Apr 2, 2026

Copy link
Copy Markdown
Member

Summary

Improve the Pro install generator rewrite logic so multiline non-parenthesized gem "react_on_rails" declarations keep trailing options while replacing the base gem entry. Harden module-specifier rewriting around template literals by preserving escaped sequences and correctly detecting multiline template-literal starts after a closed inline template.

Pull Request checklist

  • Add/update test to cover these changes
  • Update documentation
  • Update CHANGELOG file

Other Information

Validated with bundle exec rspec react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb:219 react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb:1007 react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb:1019.


Note

Medium Risk
Touches the Pro install generator’s Gemfile and JS source rewriting logic; subtle parsing/state bugs could cause incorrect dependency swaps or missed/over-eager import rewrites in real-world edge cases.

Overview
Improves the Pro generator’s Gemfile swap to correctly consume non-parenthesized multiline gem "react_on_rails" declarations and carry forward trailing options/guards while stripping only version constraints, avoiding orphaned lines and stray/trailing commas.

Hardens JS module-specifier rewriting around template literals by restoring inline template placeholders without altering escaped sequences, and by more accurately detecting multiline template-literal boundaries across inline literals and block-comment transitions (so imports between these regions are rewritten correctly). Adds focused specs for these multiline Gemfile and template-literal edge cases.

Written by Cursor Bugbot for commit 5a6e361. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • Bug Fixes

    • Preserve non-version gem options during upgrade without leaving trailing commas, extra whitespace, or blank-line artifacts.
    • More robust template-literal handling across inline and multiline cases, including escaped sequences, correct backtick detection, and block-comment boundary transitions.
  • Tests

    • Added unit tests for non-parenthesized multiline gem declarations.
    • Added tests for template-literal edge cases: placeholder restoration, imports between literals, and comment-boundary scenarios.

@coderabbitai

coderabbitai Bot commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Refactors pro generator parsing and template-literal scanning: extracts helper to consume non-parenthesized multiline gem declarations, reworks backtick detection and multiline-start logic, tweaks gem-replacement suffix handling, and adds/specifies tests for multiline gem forms and template-literal edge cases.

Changes

Cohort / File(s) Summary
Generator refactor & template-literal scanning
react_on_rails/lib/generators/react_on_rails/pro_generator.rb
Extracted consume_non_parenthesized_base_gem_declaration and derive replacement suffix/next index from it; changed placeholder restoration to sub(placeholder) { template_literal }; introduced unescaped_backtick_indexes, made first_unescaped_backtick_index use it, added opening_backtick_index_for_multiline_start; rewrite_line_before_template_literal_open accepts in_block_comment: and callers pass in_block_comment; updated rewrite_non_comment_lines to propagate block-comment state; adjusted build_pro_gem_replacement_line suffix normalization to trim trailing whitespace/newline when a trailing comma is present.
Specs for new/edge behaviors
react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb
Added tests for non-parenthesized multiline gem "react_on_rails", ... replacement ensuring non-version options are preserved and no comma/blank-line artifacts; added tests covering inline vs multiline template-literal boundaries, placeholder restoration with escape sequences, imports between inline and multiline literals, transitions after block-comment close, and correct closing-backtick selection when /* appears inside multiline content.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Backticks bound where code winds tight,

I dig a helper through the night.
Commas kept, placeholders mended,
Imports found where lines extended.
Hooray—I've hopped and left it right. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: fixing Pro generator issues with multiline and template-literal rewrites, which are the primary focus of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jg/fix-2917

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Apr 2, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes two separate but related rewrite-logic bugs in the Pro generator: multiline non-parenthesized gem "react_on_rails" declarations now correctly carry trailing non-version options (e.g. require: false) into the replacement line, and template-literal-aware module-specifier rewriting is hardened against both escaped-character corruption and incorrect multiline-start detection when balanced inline template literals precede the opening backtick on the same line.

Key changes:

  • New consume_non_parenthesized_base_gem_declaration helper — extracts the continuation-line loop from swap_base_gem_for_pro_in_gemfile into its own method; the resulting trailing_suffix now correctly includes all consumed continuation lines (not just the tail of the first line), so options like require: false are preserved after the gem swap.
  • Block form for placeholder restorationrewritten_line.sub(placeholder) { template_literal } replaces the string-argument form, preventing Ruby's backreference substitution (\1, \&, etc.) from silently corrupting template literal content that contains backslash sequences.
  • opening_backtick_index_for_multiline_start — replaces first_unescaped_backtick_index in the "before template open" rewrite path. When a line contains balanced inline template literals followed by an unclosed one (odd total backtick count), the new method returns the last backtick index (the actual multiline opener) rather than the first (index 0), which previously caused the positive? guard to skip rewriting the segment between the inline template and the multiline start.
  • Three new specs covering each of the above scenarios.

Confidence Score: 4/5

  • Safe to merge after resolving the backslash-count question in the escaped-sequence test assertion.
  • All three logic changes are well-reasoned and each comes with a targeted regression test. The implementation of consume_non_parenthesized_base_gem_declaration is a clean extraction with correct trailing_suffix construction. The sub block-form fix is the canonical Ruby solution for this class of bug. The opening_backtick_index_for_multiline_start correctly leverages the parity of the backtick count to identify the multiline opener. The only concern is that the escaped-sequence test expectation (\\\\n) appears to check for two backslashes in memory while the input heredoc produces one — if this assertion happens to pass today it might indicate an incidental doubling elsewhere rather than a true preservation test.
  • react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb — verify the \\\\n / \\\\1 assertion at line 1015 matches the actual in-memory value of the single-quoted heredoc input.

Important Files Changed

Filename Overview
react_on_rails/lib/generators/react_on_rails/pro_generator.rb Three focused bug fixes: (1) extracts multiline non-parenthesized gem declaration consumption into a new helper that correctly carries continuation lines into the trailing suffix; (2) uses block form of sub to prevent Ruby's backreference interpolation from corrupting template literal content; (3) replaces first_unescaped_backtick_index with opening_backtick_index_for_multiline_start to correctly identify the multiline-starting backtick when balanced inline templates precede it on the same line.
react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb Three new specs covering the fixed scenarios; the escaped-sequence test expectation (\\\\n) appears to assert two backslashes in memory while the single-quoted heredoc input yields only one backslash — worth verifying the assertion string is correct.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["swap_base_gem_for_pro_in_gemfile\n(outer loop)"] --> B{match base_gem_pattern?}
    B -- no --> C[append line unchanged]
    B -- yes --> D["consume_non_parenthesized_base_gem_declaration\n(NEW helper)"]
    D --> E["collect start line\n+ comma-continuation lines\ninto declaration_lines"]
    E --> F["trailing_suffix =\nlines[start][match_end..]\n+ drop(1).join"]
    F --> G{pro_entry_added?}
    G -- no --> H["build_pro_gem_replacement_line\n(strips version args,\nkeeps non-version options)"]
    G -- yes --> I[skip — already have pro entry]
    H --> J[append replacement line]
    J --> K["line_index = declaration[:next_index]\n(skip consumed continuation lines)"]
    I --> K

    subgraph "JS rewrite — template literal handling"
        L["rewrite_outside_inline_template_literals"] --> M["gsub: replace inline\ntemplate literals with\nplaceholders"]
        M --> N["yield line_without_templates\n(pattern rewrites happen here)"]
        N --> O["sub(placeholder) { template_literal }\n(block form — FIX:\nprevents \\1 back-ref corruption)"]
    end

    subgraph "JS rewrite — multiline template detection"
        P["rewrite_line_before_template_literal_open"] --> Q["opening_backtick_index_for_multiline_start\n(NEW: returns LAST backtick\nwhen count is odd)"]
        Q --> R{index positive?}
        R -- yes --> S[rewrite prefix before backtick]
        R -- no / nil --> T[return line unchanged]
    end
Loading

Reviews (1): Last reviewed commit: "Handle multiline gem swaps and template ..." | Re-trigger Greptile

Comment thread react_on_rails/lib/generators/react_on_rails/pro_generator.rb Outdated
Comment thread react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Version stripping leaves blank line in multiline output
    • When removing a multiline version argument with a trailing comma, the replacement now trims trailing indentation from the preserved prefix so no blank line is left behind, and the spec now asserts that formatting.

Create PR

Or push these changes by commenting:

@cursor push ea0384721c
Preview (ea0384721c)
diff --git a/react_on_rails/lib/generators/react_on_rails/pro_generator.rb b/react_on_rails/lib/generators/react_on_rails/pro_generator.rb
--- a/react_on_rails/lib/generators/react_on_rails/pro_generator.rb
+++ b/react_on_rails/lib/generators/react_on_rails/pro_generator.rb
@@ -865,7 +865,12 @@
         version_arg_pattern = /\A(?<prefix>\s*,(?:\s*#.*\n|\s++)*)["'][^"']*["'](?<trailing_comma>\s*,)?/
         loop do
           updated_suffix = normalized_suffix.sub(version_arg_pattern) do
-            Regexp.last_match[:trailing_comma] ? Regexp.last_match[:prefix] : ""
+            if Regexp.last_match[:trailing_comma]
+              # Preserve separators/comments while removing indentation that belonged to the stripped version line.
+              Regexp.last_match[:prefix].sub(/\n[ \t]*\z/, "")
+            else
+              ""
+            end
           end
           break if updated_suffix == normalized_suffix
 

diff --git a/react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb b/react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb
--- a/react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb
+++ b/react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb
@@ -230,6 +230,7 @@
       gemfile_content = File.read(gemfile_path)
       expected_version = generator.send(:pro_gem_version_requirement)
       expect(gemfile_content).to include("gem \"react_on_rails_pro\", \"#{expected_version}\",")
+      expect(gemfile_content).not_to include(",\n  \n  require: false")
       expect(gemfile_content).to include("require: false")
       expect(gemfile_content).not_to include("\"~> 16.0\"")
     end

You can send follow-ups to this agent here.

Comment thread react_on_rails/lib/generators/react_on_rails/pro_generator.rb

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
react_on_rails/lib/generators/react_on_rails/pro_generator.rb (1)

743-779: ⚠️ Potential issue | 🟡 Minor

Make the multiline-open backtick scan honor block-comment state.

line_contains_unescaped_backtick is decided from the comment-aware tracking line, but opening_backtick_index_for_multiline_start rescans the raw line without in_block_comment handling. On a line that closes /* ... */ and then opens a multiline template, an odd backtick count inside the comment can make opening_index wrong or nil, so the import("react-on-rails") before the real template opener is skipped. Please drive the split-point calculation from the same comment-aware scan, and add a regression around */ import("react-on-rails"); \start` after a comment line containing a lone backtick.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d95d10dc-8d32-4bc8-a5bb-c8ee46a9daf3

📥 Commits

Reviewing files that changed from the base of the PR and between 6553a8a and 9d32a0b.

📒 Files selected for processing (2)
  • react_on_rails/lib/generators/react_on_rails/pro_generator.rb
  • react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb

@justin808

Copy link
Copy Markdown
Member Author

@claude review this PR.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e93a57e4df

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread react_on_rails/lib/generators/react_on_rails/pro_generator.rb
Comment thread react_on_rails/lib/generators/react_on_rails/pro_generator.rb Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Stale in_block_comment flag after template literal branch
    • Template-literal rewrite branches now recompute in_block_comment from the rewritten line so stale true state is cleared after comments close.
  • ✅ Fixed: Backtick finder incorrectly skips /* inside template literals
    • Backtick scanning now allows disabling block-comment tracking for template-literal-close detection so literal /* content no longer hides the closing backtick.

Create PR

Or push these changes by commenting:

@cursor push 08ed1eecd0
Preview (08ed1eecd0)
diff --git a/react_on_rails/lib/generators/react_on_rails/pro_generator.rb b/react_on_rails/lib/generators/react_on_rails/pro_generator.rb
--- a/react_on_rails/lib/generators/react_on_rails/pro_generator.rb
+++ b/react_on_rails/lib/generators/react_on_rails/pro_generator.rb
@@ -473,7 +473,7 @@
                   pending_multiline_static_import_specifier
                 ) { |line_fragment| yield line_fragment }
               in_multiline_template_literal = updated_template_literal_state
-              in_block_comment = true if unclosed_block_comment_starts?(rewritten_line)
+              in_block_comment = unclosed_block_comment_starts?(rewritten_line)
               rewritten_line
             elsif line_contains_unescaped_backtick
               rewritten_line, pending_multiline_module_call_depth, pending_multiline_static_import_specifier =
@@ -484,7 +484,7 @@
                   in_block_comment: in_block_comment
                 ) { |line_fragment| yield line_fragment }
               in_multiline_template_literal = updated_template_literal_state
-              in_block_comment = true if unclosed_block_comment_starts?(rewritten_line)
+              in_block_comment = unclosed_block_comment_starts?(rewritten_line)
               rewritten_line
             else
               in_multiline_template_literal = updated_template_literal_state
@@ -760,7 +760,7 @@
       end
 
       def first_unescaped_backtick_index(line)
-        unescaped_backtick_indexes(line).first
+        unescaped_backtick_indexes(line, track_block_comments: false).first
       end
 
       def opening_backtick_index_for_multiline_start(line, in_block_comment: false)
@@ -771,13 +771,13 @@
       end
 
       # rubocop:disable Metrics/CyclomaticComplexity
-      def unescaped_backtick_indexes(line, in_block_comment: false)
+      def unescaped_backtick_indexes(line, in_block_comment: false, track_block_comments: true)
         quote_state = nil
         backtick_indexes = []
 
         scan_index = 0
         while scan_index < line.length
-          if in_block_comment
+          if track_block_comments && in_block_comment
             closing_index = line.index("*/", scan_index)
             break unless closing_index
 
@@ -795,7 +795,7 @@
 
           break if line[scan_index, 2] == "//"
 
-          if line[scan_index, 2] == "/*"
+          if track_block_comments && line[scan_index, 2] == "/*"
             in_block_comment = true
             scan_index += 2
             next

diff --git a/react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb b/react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb
--- a/react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb
+++ b/react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb
@@ -1038,6 +1038,35 @@
       expect(rewritten).to include('odd backtick ` still comment */ import("react-on-rails-pro"); `multiline-start')
     end
 
+    it "resets block-comment state after multiline template literals opened on comment-closing lines" do
+      source = <<~JS
+        /*
+         odd backtick ` still comment */ import("react-on-rails"); `multiline-start
+          import ReactOnRails from "react-on-rails";
+        `;
+        import ReactOnRails from "react-on-rails";
+      JS
+
+      rewritten = generator.send(:rewrite_react_on_rails_module_specifiers, source)
+
+      expect(rewritten).to include('odd backtick ` still comment */ import("react-on-rails-pro"); `multiline-start')
+      expect(rewritten).to include('import ReactOnRails from "react-on-rails";')
+      expect(rewritten).to include("import ReactOnRails from \"react-on-rails-pro\";")
+      expect(rewritten.scan("react-on-rails-pro").size).to eq(2)
+    end
+
+    it "finds multiline template literal closes when closing lines contain unmatched block-comment markers" do
+      source = <<~JS
+        const template = `
+          /* css comment marker `; const ror = require("react-on-rails");
+      JS
+
+      rewritten = generator.send(:rewrite_react_on_rails_module_specifiers, source)
+
+      expect(rewritten).to include('/* css comment marker `; const ror = require("react-on-rails-pro");')
+      expect(rewritten.scan("react-on-rails-pro").size).to eq(1)
+    end
+
     it "rewrites all matching specifiers on a pending continuation line" do
       source = <<~JS
         import {

You can send follow-ups to this agent here.

Comment thread react_on_rails/lib/generators/react_on_rails/pro_generator.rb
Comment thread react_on_rails/lib/generators/react_on_rails/pro_generator.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/pro_generator.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/pro_generator.rb
Comment thread react_on_rails/lib/generators/react_on_rails/pro_generator.rb
Comment thread react_on_rails/lib/generators/react_on_rails/pro_generator.rb
Comment thread react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb
@claude

claude Bot commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Review

The three fixes are all correct and address real bugs:

  • .sub(placeholder) { template_literal } - necessary because the string-replacement form of sub interprets \1, \\, \&, etc. as backreferences, corrupting template literal content that contains those sequences.
  • opening_backtick_index_for_multiline_start - the parity logic (odd count means last backtick opens a multiline template) is sound and correctly handles lines with both closed inline templates and an opening multiline template.
  • consume_non_parenthesized_base_gem_declaration - cleanly extracts the multiline-consumption and trailing-suffix logic, fixing the stray blank-line/comma issue.

One genuine bug to fix before merge (see inline comment at line 487):

When in_block_comment is true and the block comment closes mid-line while a template literal opens on the same line, the elsif line_contains_unescaped_backtick branch runs but never resets in_block_comment to false. After the template literal subsequently closes, every following line falls into the elsif in_block_comment guard and is silently skipped, so any react-on-rails imports after that point are never rewritten. The fix is a one-character change: replace the conditional assignment with an unconditional one (line 487).

The new spec at line 1030 correctly exercises the detection half of this fix but does not cover lines that appear after the template literal closes. A suggested extended test case is in the spec inline comment.

Smaller notes:

  • The dual-purpose use of consume_non_parenthesized_base_gem_declaration (called both to build trailing_suffix and to advance line_index when pro_entry_added is already true) is a subtle read; a comment at the call site would help.
  • The backtick-parity assumption in opening_backtick_index_for_multiline_start depends on the raw (unprocessed) line being passed; this is currently correct but worth documenting as a contract.

- Change in_block_comment assignment from conditional (= true if) to
  unconditional (=) in both template-literal branches so the flag is
  properly reset when a block comment closes on the same line a
  template literal opens. Without this, all lines after the template
  literal closes were silently skipped for rewriting.

- Add skip_comments parameter to unescaped_backtick_indexes so
  first_unescaped_backtick_index (used when scanning for the closing
  backtick inside a template literal) does not treat /* as a block
  comment start. Template literal content is not code, so comment
  tokens should be ignored when finding the closing backtick.

- Add tests for both edge cases.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@justin808

Copy link
Copy Markdown
Member Author

Review comment triage & resolution (5a6e361)

Triaged 11 review comments across 5 bots. Two real bugs were fixed, two claims were investigated and found incorrect, and documentation nits were declined as out of scope.

Fixed (2 bugs)

  1. Stale in_block_comment flag (claude[bot], cursor[bot]) — When a block comment closed on the same line a template literal opened, in_block_comment was never reset to false. After the template literal closed, all subsequent lines were silently skipped. Fix: unconditional assignment on lines 476/487.

  2. /* inside template literals treated as block comment (chatgpt-codex-connector[bot], cursor[bot]) — first_unescaped_backtick_index (used to find the closing backtick inside a template literal) incorrectly treated /* in template content as a block comment start. Fix: added skip_comments: true parameter.

Both fixes include regression tests. All 79 specs pass.

Investigated & declined (2 false positives)

  • Backslash count off-by-one (greptile-apps[bot] P1) — Incorrect. Single-quoted heredocs (<<~'JS') perform no escape processing, so \\n is two backslashes + n in memory, matching the expectation.
  • Version stripping drops options (chatgpt-codex-connector[bot] P1) — Incorrect. The normalization regex \A,\s*(?:#[^\n]*)?\n\z cannot match multi-line suffixes — (?:#[^\n]*)?\n\z limits it to single-line trailing commas. Verified with a Ruby test.

Declined as out of scope (4 style/documentation nits)

  • positive? guard comment, parity logic documentation, dual-purpose method comment, and the duplicate threads — all acknowledged with rationale replies.

All 11 review threads are now resolved.

backtick_indexes = unescaped_backtick_indexes(line, in_block_comment: in_block_comment)
return nil if backtick_indexes.empty? || backtick_indexes.length.even?

backtick_indexes.last

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The even/odd heuristic is clever but non-obvious — worth a brief comment to explain the invariant:

  • Even count → all backticks are paired as inline template literals; no multiline opener on this line.
  • Odd count → the last unpaired backtick opens a multiline template literal.

This holds only when ${...} nesting is not tracked (a pre-existing limitation). A one-liner comment here would help the next reader.

Suggested change
backtick_indexes.last
def opening_backtick_index_for_multiline_start(line, in_block_comment: false)
backtick_indexes = unescaped_backtick_indexes(line, in_block_comment: in_block_comment)
# Even count → all inline literals are closed; odd count → last backtick opens a multiline literal.
return nil if backtick_indexes.empty? || backtick_indexes.length.even?
backtick_indexes.last
end

end

def first_unescaped_backtick_index(line)
unescaped_backtick_indexes(line, skip_comments: true).first

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The skip_comments: true parameter name is semantically reversed — it means "don't treat // / /* */ as comments", i.e. "scan as-if inside a template literal". This is correct (template literal content ignores JS comment syntax), but it's easy to misread as "skip backticks that are inside comments". A clearer name like in_template_literal_content: true or just an inline comment would reduce confusion:

Suggested change
unescaped_backtick_indexes(line, skip_comments: true).first
def first_unescaped_backtick_index(line)
# skip_comments: true because this is called inside a multiline template literal,
# where // and /* */ are content, not JS comments.
unescaped_backtick_indexes(line, skip_comments: true).first
end

line_index += 1
end

trailing_suffix = lines[start_index][match_end..].to_s + declaration_lines.drop(1).join

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edge case to verify: if there are blank or comment-only continuation lines interspersed between the version argument and non-version options, those lines are accumulated in declaration_lines (and thus in trailing_suffix) verbatim. For example:

gem "react_on_rails",
  # pinned version
  "~> 16.0",
  # needed for assets pipeline
  require: false

declaration_lines.drop(1) would include the comment lines, so trailing_suffix would contain them. build_pro_gem_replacement_line's version_arg_pattern consumes \s*#.*\n in the prefix group, so the comments adjacent to the version arg get stripped. But the comment adjacent to require: false would be preserved. This all seems intentional, but a quick test covering this specific interleaved-comment case would strengthen confidence.

) { |line_fragment| yield line_fragment }
in_multiline_template_literal = updated_template_literal_state
in_block_comment = true if unclosed_block_comment_starts?(rewritten_line)
in_block_comment = unclosed_block_comment_starts?(rewritten_line)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good fix. The original in_block_comment = true if ... pattern could only toggle the flag from false → true in this branch, meaning a line that exits a multiline template literal and doesn't open a block comment would silently leave a stale in_block_comment = true from a prior iteration. Using a direct assignment correctly handles both transitions.

rewritten_line = yield line_without_inline_templates
template_placeholders.each do |placeholder, template_literal|
rewritten_line = rewritten_line.sub(placeholder, template_literal)
rewritten_line = rewritten_line.sub(placeholder) { template_literal }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important correctness fix. String#sub(pattern, replacement_string) interprets special sequences in the replacement string (\1, \0, \&, \\, etc.) as backreferences. Template literals frequently contain \n, \1, etc., so the old code would silently corrupt content. Using a block sidesteps that entirely.

One minor note: if template_literal could ever be a very large string used many times, the block form might marginally affect performance — but in practice this is negligible for a code generator.

@claude

claude Bot commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Review: Fix Pro generator multiline and template-literal rewrites

Overall this is a solid, targeted fix. The root causes are real and the changes are well-scoped. A few observations inline; high-level notes below.

What's fixed well

.sub(placeholder) { template_literal } (line 649) — Critical correctness fix. The old string-replacement form silently mangled template literals containing \n, \1, \&, etc. Switching to a block is the idiomatic Ruby solution.

in_block_comment direct assignment (lines 476, 487) — The old = true if pattern could only toggle the flag in one direction within the template-literal branches, potentially leaving in_block_comment stale as true after a line that should not have set it. Direct assignment fixes both transitions correctly.

consume_non_parenthesized_base_gem_declaration — Clean extraction of the multiline consumption logic. The trailing_suffix construction and how it feeds into build_pro_gem_replacement_line's stripping loop is correct.

build_pro_gem_replacement_line prefix stripping — Removing the trailing \n[ \t]* from prefix when a trailing comma is present prevents the orphaned blank line (,\n \n require: false) seen in the old code.

Minor concerns (see inline)

  • opening_backtick_index_for_multiline_start even/odd heuristic — the invariant is non-obvious; a comment would help maintainability.
  • skip_comments: true naming — reads ambiguously; a brief comment at the call site clarifies intent.
  • Interleaved comment lines in non-parenthesized multiline declarations appear to be handled correctly by build_pro_gem_replacement_line's regex, but no test covers that specific path.

Testing

The new specs cover the specific edge cases being fixed and are well-structured. No security or performance concerns identified.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5a6e361f20

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

updated_suffix = normalized_suffix.sub(version_arg_pattern) do
Regexp.last_match[:trailing_comma] ? Regexp.last_match[:prefix] : ""
if Regexp.last_match[:trailing_comma]
Regexp.last_match[:prefix].sub(/\n[ \t]*\z/, "")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve newline when dropping multiline Gemfile version

When the matched version argument has a trailing comma, trimming Regexp.last_match[:prefix] with .sub(/\n[ \t]*\z/, "") can merge the next line into a preceding # comment, so options like require: false become commented out and silently lost in the rewritten gem "react_on_rails_pro" entry. This happens for declarations like gem "react_on_rails", # note followed by a multiline version/options block. Fresh evidence: the current commit still performs this newline trim at this location without a guard for comment-terminated prefixes.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
react_on_rails/lib/generators/react_on_rails/pro_generator.rb (1)

728-810: ⚠️ Potential issue | 🟡 Minor

When scanning for a closing template-literal backtick, disable quote tracking since quotes are just characters in template-literal text.

When first_unescaped_backtick_index calls unescaped_backtick_indexes(..., skip_comments: true), the method still tracks quote state (lines 790–794) and skips backtick detection while inside quotes. In template-literal text, single and double quotes have no syntactic effect—only an unescaped backtick terminates the literal. A line like "foo; require("react-on-rails")` fails to identify the closing backtick and the suffix never gets rewritten.

Add a track_quotes: true parameter to unescaped_backtick_indexes and pass track_quotes: false from first_unescaped_backtick_index:

Fix
      def first_unescaped_backtick_index(line)
-       unescaped_backtick_indexes(line, skip_comments: true).first
+       unescaped_backtick_indexes(line, skip_comments: true, track_quotes: false).first
      end
...
-      def unescaped_backtick_indexes(line, in_block_comment: false, skip_comments: false)
+      def unescaped_backtick_indexes(line, in_block_comment: false, skip_comments: false, track_quotes: true)
         quote_state = nil
         backtick_indexes = []

         scan_index = 0
         while scan_index < line.length
           ...
           char = line[scan_index]
-          quote_state = next_quote_state(quote_state, char, line, scan_index)
+          quote_state = next_quote_state(quote_state, char, line, scan_index) if track_quotes
           if quote_state
             scan_index += 1
             next
           end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@react_on_rails/lib/generators/react_on_rails/pro_generator.rb` around lines
728 - 810, The scanner treats quotes as syntactic when searching for a closing
template backtick, causing it to miss backticks inside template text; add a new
boolean parameter track_quotes (default true) to unescaped_backtick_indexes,
guard the quote-state updates and quote-skipping logic behind track_quotes, and
update first_unescaped_backtick_index to call unescaped_backtick_indexes(...,
skip_comments: true, track_quotes: false) so quotes are ignored when finding the
closing backtick; leave other callers (e.g.,
opening_backtick_index_for_multiline_start) using the default true behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@react_on_rails/lib/generators/react_on_rails/pro_generator.rb`:
- Around line 875-891: The loop in
consume_non_parenthesized_base_gem_declaration only treats comma-terminated
continuations and misses explicit backslash continuations; update the while
condition to also continue when the current line ends with a backslash (e.g. add
a check like line_ends_with_backslash?(current_line) to the existing
line_continues_with_comma? condition), implement a small helper
line_ends_with_backslash?(line) that returns line.rstrip.end_with?("\\") and
leave the rest of the logic (appending next_line to declaration_lines and
building trailing_suffix) unchanged so backslash-continued version constraints
are consumed into trailing_suffix instead of being left behind.

---

Outside diff comments:
In `@react_on_rails/lib/generators/react_on_rails/pro_generator.rb`:
- Around line 728-810: The scanner treats quotes as syntactic when searching for
a closing template backtick, causing it to miss backticks inside template text;
add a new boolean parameter track_quotes (default true) to
unescaped_backtick_indexes, guard the quote-state updates and quote-skipping
logic behind track_quotes, and update first_unescaped_backtick_index to call
unescaped_backtick_indexes(..., skip_comments: true, track_quotes: false) so
quotes are ignored when finding the closing backtick; leave other callers (e.g.,
opening_backtick_index_for_multiline_start) using the default true behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ba46c65f-c396-474b-b8ab-768d8221ca27

📥 Commits

Reviewing files that changed from the base of the PR and between e93a57e and 5a6e361.

📒 Files selected for processing (2)
  • react_on_rails/lib/generators/react_on_rails/pro_generator.rb
  • react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb
🚧 Files skipped from review as they are similar to previous changes (1)
  • react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb

Comment on lines +875 to +891
def consume_non_parenthesized_base_gem_declaration(lines, start_index, match_end)
line_index = start_index
current_line = lines[line_index]
declaration_lines = [current_line]
line_index += 1

while line_index < lines.length &&
line_continues_with_comma?(current_line) &&
gem_declaration_continues_on_next_line?(lines[line_index])
next_line = lines[line_index]
declaration_lines << next_line
current_line = next_line unless comment_or_blank_line?(next_line)
line_index += 1
end

trailing_suffix = lines[start_index][match_end..].to_s + declaration_lines.drop(1).join
{ trailing_suffix: trailing_suffix, next_index: line_index }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Ruby, does a trailing backslash (`) continue a multiline method call, for example:

gem "react_on_rails",
"~> 16.0",
require: false

so that the following lines are still arguments to the same gem(...) call?`

💡 Result:

Yes, in Ruby, a trailing backslash () at the end of a line acts as an explicit line continuation character, allowing a single expression or statement—such as a method call—to span multiple lines. The parser treats the lines as if they were a single continuous line, with the backslash escaping the newline. This works for the given example in a Gemfile (which is evaluated as Ruby code): gem "react_on_rails", \ "> 16.0", \ require: false The gem(...) method call continues across lines, passing all three arguments: the string "react_on_rails", the string "> 16.0", and the hash {require: false}.

Citations:


🏁 Script executed:

# First, locate the file and check if it exists
find . -name "pro_generator.rb" -type f

# Then check the specific lines mentioned in the review (875-891)
# and look for the line_continues_with_comma? method definition
if [ -f "react_on_rails/lib/generators/react_on_rails/pro_generator.rb" ]; then
  echo "=== Lines 875-891 ==="
  sed -n '875,891p' react_on_rails/lib/generators/react_on_rails/pro_generator.rb | cat -n
  
  echo ""
  echo "=== Searching for line_continues_with_comma? method ==="
  grep -n "def line_continues_with_comma?" react_on_rails/lib/generators/react_on_rails/pro_generator.rb | head -5
  
  echo ""
  echo "=== Context around line_continues_with_comma? definition ==="
  grep -A 5 "def line_continues_with_comma?" react_on_rails/lib/generators/react_on_rails/pro_generator.rb
fi

Repository: shakacode/react_on_rails

Length of output: 1419


🏁 Script executed:

# Check for line length violations (max 120 chars) in the specified range and method
if [ -f "react_on_rails/lib/generators/react_on_rails/pro_generator.rb" ]; then
  echo "=== Checking line lengths in range 875-900 ==="
  sed -n '875,900p' react_on_rails/lib/generators/react_on_rails/pro_generator.rb | awk 'length > 120 {print NR + 874 ": " length " chars: " $0}'
  
  echo ""
  echo "=== Checking line lengths around line_continues_with_comma? ==="
  grep -n "def line_continues_with_comma?" react_on_rails/lib/generators/react_on_rails/pro_generator.rb | while read line_info; do
    line_num=$(echo "$line_info" | cut -d: -f1)
    start=$((line_num - 2))
    end=$((line_num + 5))
    sed -n "${start},${end}p" react_on_rails/lib/generators/react_on_rails/pro_generator.rb | awk -v start="$start" 'length > 120 {print NR + start - 1 ": " length " chars: " $0}'
  done
fi

Repository: shakacode/react_on_rails

Length of output: 179


Handle explicit \ continuations in multiline Gemfile entries.

The consume_non_parenthesized_base_gem_declaration method (line 881) only checks for comma-terminated lines via line_continues_with_comma?. Ruby also permits explicit line continuation with a trailing backslash, so a gem declaration like gem "react_on_rails", \ followed by version constraints will be rewritten to react_on_rails_pro while leaving the old version arguments behind. This can cause conflicting requirements or failed bundle install.

Fix
-      def line_continues_with_comma?(line)
+      def line_continues?(line)
         line_without_comment = line.sub(/\s*#.*$/, "").rstrip
-        line_without_comment.end_with?(",")
+        line_without_comment.end_with?(",", "\\")
       end

         while line_index < lines.length &&
-              line_continues_with_comma?(current_line) &&
+              line_continues?(current_line) &&
               gem_declaration_continues_on_next_line?(lines[line_index])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@react_on_rails/lib/generators/react_on_rails/pro_generator.rb` around lines
875 - 891, The loop in consume_non_parenthesized_base_gem_declaration only
treats comma-terminated continuations and misses explicit backslash
continuations; update the while condition to also continue when the current line
ends with a backslash (e.g. add a check like
line_ends_with_backslash?(current_line) to the existing
line_continues_with_comma? condition), implement a small helper
line_ends_with_backslash?(line) that returns line.rstrip.end_with?("\\") and
leave the rest of the logic (appending next_line to declaration_lines and
building trailing_suffix) unchanged so backslash-continued version constraints
are consumed into trailing_suffix instead of being left behind.

@justin808 justin808 merged commit d9f0934 into main Apr 3, 2026
59 checks passed
@justin808 justin808 deleted the jg/fix-2917 branch April 3, 2026 06:12
justin808 added a commit that referenced this pull request Apr 8, 2026
…e-dedup

* origin/main: (51 commits)
  Fix Content-Length mismatch and null renderingRequest errors in node renderer (#3069)
  Improve memory debugging docs with simpler heap snapshot approach (#3072)
  Enforce strict version matching in doctor and recommend doctor on errors (#3070)
  Remove immediate_hydration feature from codebase (#2834)
  Fix infinite fork loop when node renderer worker fails to bind port (#2881)
  Fix TanStack Router SSR hydration mismatch in Pro async path (#2932)
  Improve node renderer error messages for malformed render requests (#3068)
  Add interactive mode prompt to create-react-on-rails-app (#3063)
  docs: replace hardcoded version numbers with unversioned install commands (#2893)
  Remove Contributing section from docs sidebar (#3064)
  Consolidate docs comparison pages into single evaluation entry (#3065)
  Docs route cleanup: canonicalize worst verbose URL slugs (#3067)
  Add agent summary blocks to high-value docs pages (#3066)
  Fix brittle positional assertions in create-app tests (#2923)
  Auto-resolve renderer password from ENV in Rails Pro (#2921)
  Standardize bundle env vars from =yes to =true (#2925)
  Fix Pro generator multiline and template-literal rewrites (#2918)
  Fix spec/dummy Procfile.dev SERVER_BUNDLE_ONLY to match template convention (#2922)
  Add llms docs entry points (#2916)
  Bump version to 16.6.0.rc.0
  ...

# Conflicts:
#	packages/create-react-on-rails-app/src/create-app.ts
justin808 added a commit that referenced this pull request Apr 8, 2026
…lacement-2835-main

* origin/main: (44 commits)
  Fix Content-Length mismatch and null renderingRequest errors in node renderer (#3069)
  Improve memory debugging docs with simpler heap snapshot approach (#3072)
  Enforce strict version matching in doctor and recommend doctor on errors (#3070)
  Remove immediate_hydration feature from codebase (#2834)
  Fix infinite fork loop when node renderer worker fails to bind port (#2881)
  Fix TanStack Router SSR hydration mismatch in Pro async path (#2932)
  Improve node renderer error messages for malformed render requests (#3068)
  Add interactive mode prompt to create-react-on-rails-app (#3063)
  docs: replace hardcoded version numbers with unversioned install commands (#2893)
  Remove Contributing section from docs sidebar (#3064)
  Consolidate docs comparison pages into single evaluation entry (#3065)
  Docs route cleanup: canonicalize worst verbose URL slugs (#3067)
  Add agent summary blocks to high-value docs pages (#3066)
  Fix brittle positional assertions in create-app tests (#2923)
  Auto-resolve renderer password from ENV in Rails Pro (#2921)
  Standardize bundle env vars from =yes to =true (#2925)
  Fix Pro generator multiline and template-literal rewrites (#2918)
  Fix spec/dummy Procfile.dev SERVER_BUNDLE_ONLY to match template convention (#2922)
  Add llms docs entry points (#2916)
  Bump version to 16.6.0.rc.0
  ...

# Conflicts:
#	CHANGELOG.md
@justin808 justin808 mentioned this pull request Apr 8, 2026
2 tasks
justin808 added a commit that referenced this pull request Apr 8, 2026
### Summary

Adds missing changelog entries to the `[Unreleased]` section for PRs
merged since `v16.6.0.rc.0`. Analyzed 19 commits on `origin/main` since
the last tag, identified 1 missing user-visible PR (#3069), and skipped
10 non-user-visible PRs (docs, tests, internal refactors).

**New entries added:**
- **Added**: `[Pro] Configurable HTTP keep-alive timeout for node
renderer connections` (PR #3069)
- **Fixed**: `[Pro] Fixed SSR failures from stale persistent HTTP/2
connections` (PR #3069)

**Entries already present** (added in prior changelog updates): PRs
#2834, #2881, #2918, #2921, #2923, #2932, #3063, #3068, #3070.

**Skipped** (not user-visible): #2893 (docs), #2916 (docs), #2922 (test
fix), #2923 (test fix), #2925 (internal refactor), #3064 (docs), #3065
(docs), #3066 (docs), #3067 (docs), #3072 (docs).

### Pull Request checklist

- [x] ~Add/update test to cover these changes~
- [x] ~Update documentation~
- [x] Update CHANGELOG file

### Other Information

No code changes — CHANGELOG.md only.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk because this PR only changes documentation (`CHANGELOG.md`)
and does not modify runtime code.
> 
> **Overview**
> Updates `CHANGELOG.md` *[Unreleased]* to include missing Pro release
notes for PR `#3069`, documenting the new
`renderer_http_keep_alive_timeout` config and the associated fix for SSR
failures from stale persistent HTTP/2 connections to the node renderer.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
8d1a480. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Interactive mode selection prompt for CLI tool when no explicit mode
is specified
* Configurable keep-alive timeout setting for Pro users (default 30
seconds)

* **Bug Fixes**
  * Enhanced validation and error handling for invalid request payloads
  * Improved template literal handling in code generation
  * Better HTTP connection stability with enhanced diagnostic messaging

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
justin808 added a commit that referenced this pull request Apr 12, 2026
### Summary

Improve the Pro install generator rewrite logic so multiline
non-parenthesized `gem "react_on_rails"` declarations keep trailing
options while replacing the base gem entry. Harden module-specifier
rewriting around template literals by preserving escaped sequences and
correctly detecting multiline template-literal starts after a closed
inline template.

### Pull Request checklist

- [x] Add/update test to cover these changes
- [x] ~Update documentation~
- [x] ~Update CHANGELOG file~

### Other Information

Validated with `bundle exec rspec
react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb:219
react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb:1007
react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb:1019`.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches the Pro install generator’s Gemfile and JS source rewriting
logic; subtle parsing/state bugs could cause incorrect dependency swaps
or missed/over-eager import rewrites in real-world edge cases.
> 
> **Overview**
> Improves the Pro generator’s Gemfile swap to correctly consume
*non-parenthesized multiline* `gem "react_on_rails"` declarations and
carry forward trailing options/guards while stripping only version
constraints, avoiding orphaned lines and stray/trailing commas.
> 
> Hardens JS module-specifier rewriting around template literals by
restoring inline template placeholders without altering escaped
sequences, and by more accurately detecting multiline template-literal
boundaries across inline literals and block-comment transitions (so
imports between these regions are rewritten correctly). Adds focused
specs for these multiline Gemfile and template-literal edge cases.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5a6e361. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Preserve non-version gem options during upgrade without leaving
trailing commas, extra whitespace, or blank-line artifacts.
* More robust template-literal handling across inline and multiline
cases, including escaped sequences, correct backtick detection, and
block-comment boundary transitions.

* **Tests**
  * Added unit tests for non-parenthesized multiline gem declarations.
* Added tests for template-literal edge cases: placeholder restoration,
imports between literals, and comment-boundary scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
justin808 added a commit that referenced this pull request Apr 12, 2026
### Summary

Adds missing changelog entries to the `[Unreleased]` section for PRs
merged since `v16.6.0.rc.0`. Analyzed 19 commits on `origin/main` since
the last tag, identified 1 missing user-visible PR (#3069), and skipped
10 non-user-visible PRs (docs, tests, internal refactors).

**New entries added:**
- **Added**: `[Pro] Configurable HTTP keep-alive timeout for node
renderer connections` (PR #3069)
- **Fixed**: `[Pro] Fixed SSR failures from stale persistent HTTP/2
connections` (PR #3069)

**Entries already present** (added in prior changelog updates): PRs
#2834, #2881, #2918, #2921, #2923, #2932, #3063, #3068, #3070.

**Skipped** (not user-visible): #2893 (docs), #2916 (docs), #2922 (test
fix), #2923 (test fix), #2925 (internal refactor), #3064 (docs), #3065
(docs), #3066 (docs), #3067 (docs), #3072 (docs).

### Pull Request checklist

- [x] ~Add/update test to cover these changes~
- [x] ~Update documentation~
- [x] Update CHANGELOG file

### Other Information

No code changes — CHANGELOG.md only.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk because this PR only changes documentation (`CHANGELOG.md`)
and does not modify runtime code.
> 
> **Overview**
> Updates `CHANGELOG.md` *[Unreleased]* to include missing Pro release
notes for PR `#3069`, documenting the new
`renderer_http_keep_alive_timeout` config and the associated fix for SSR
failures from stale persistent HTTP/2 connections to the node renderer.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
8d1a480. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Interactive mode selection prompt for CLI tool when no explicit mode
is specified
* Configurable keep-alive timeout setting for Pro users (default 30
seconds)

* **Bug Fixes**
  * Enhanced validation and error handling for invalid request payloads
  * Improved template literal handling in code generation
  * Better HTTP connection stability with enhanced diagnostic messaging

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
justin808 added a commit that referenced this pull request Apr 12, 2026
### Summary

Improve the Pro install generator rewrite logic so multiline
non-parenthesized `gem "react_on_rails"` declarations keep trailing
options while replacing the base gem entry. Harden module-specifier
rewriting around template literals by preserving escaped sequences and
correctly detecting multiline template-literal starts after a closed
inline template.

### Pull Request checklist

- [x] Add/update test to cover these changes
- [x] ~Update documentation~
- [x] ~Update CHANGELOG file~

### Other Information

Validated with `bundle exec rspec
react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb:219
react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb:1007
react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rb:1019`.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches the Pro install generator’s Gemfile and JS source rewriting
logic; subtle parsing/state bugs could cause incorrect dependency swaps
or missed/over-eager import rewrites in real-world edge cases.
> 
> **Overview**
> Improves the Pro generator’s Gemfile swap to correctly consume
*non-parenthesized multiline* `gem "react_on_rails"` declarations and
carry forward trailing options/guards while stripping only version
constraints, avoiding orphaned lines and stray/trailing commas.
> 
> Hardens JS module-specifier rewriting around template literals by
restoring inline template placeholders without altering escaped
sequences, and by more accurately detecting multiline template-literal
boundaries across inline literals and block-comment transitions (so
imports between these regions are rewritten correctly). Adds focused
specs for these multiline Gemfile and template-literal edge cases.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5a6e361. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Preserve non-version gem options during upgrade without leaving
trailing commas, extra whitespace, or blank-line artifacts.
* More robust template-literal handling across inline and multiline
cases, including escaped sequences, correct backtick detection, and
block-comment boundary transitions.

* **Tests**
  * Added unit tests for non-parenthesized multiline gem declarations.
* Added tests for template-literal edge cases: placeholder restoration,
imports between literals, and comment-boundary scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
justin808 added a commit that referenced this pull request Apr 12, 2026
### Summary

Adds missing changelog entries to the `[Unreleased]` section for PRs
merged since `v16.6.0.rc.0`. Analyzed 19 commits on `origin/main` since
the last tag, identified 1 missing user-visible PR (#3069), and skipped
10 non-user-visible PRs (docs, tests, internal refactors).

**New entries added:**
- **Added**: `[Pro] Configurable HTTP keep-alive timeout for node
renderer connections` (PR #3069)
- **Fixed**: `[Pro] Fixed SSR failures from stale persistent HTTP/2
connections` (PR #3069)

**Entries already present** (added in prior changelog updates): PRs
#2834, #2881, #2918, #2921, #2923, #2932, #3063, #3068, #3070.

**Skipped** (not user-visible): #2893 (docs), #2916 (docs), #2922 (test
fix), #2923 (test fix), #2925 (internal refactor), #3064 (docs), #3065
(docs), #3066 (docs), #3067 (docs), #3072 (docs).

### Pull Request checklist

- [x] ~Add/update test to cover these changes~
- [x] ~Update documentation~
- [x] Update CHANGELOG file

### Other Information

No code changes — CHANGELOG.md only.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk because this PR only changes documentation (`CHANGELOG.md`)
and does not modify runtime code.
> 
> **Overview**
> Updates `CHANGELOG.md` *[Unreleased]* to include missing Pro release
notes for PR `#3069`, documenting the new
`renderer_http_keep_alive_timeout` config and the associated fix for SSR
failures from stale persistent HTTP/2 connections to the node renderer.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
8d1a480. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Interactive mode selection prompt for CLI tool when no explicit mode
is specified
* Configurable keep-alive timeout setting for Pro users (default 30
seconds)

* **Bug Fixes**
  * Enhanced validation and error handling for invalid request payloads
  * Improved template literal handling in code generation
  * Better HTTP connection stability with enhanced diagnostic messaging

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant