Skip to content

react_on_rails:pro auto-upgrade: import rewriter misses jest.mock and declare module; Gemfile swap overwrites version pins #3104

@ihabadham

Description

@ihabadham

Three silent-failure cases in pro_generator.rb from #2822. The generator reports success while user tests, type checks, or Gemfiles are broken.

1. jest.mock('react-on-rails', ...) is never rewritten

The dynamic-call regex at pro_generator.rb:353-361 only matches import( and require(. jest.mock(, vi.mock(, etc. don't match any branch.

Repro:

import ReactOnRails from 'react-on-rails';
jest.mock('react-on-rails', () => ({ authenticityHeaders: jest.fn() }));

After update_imports_to_pro_package:

import ReactOnRails from 'react-on-rails-pro';   // rewritten
jest.mock('react-on-rails', () => ({ ... }));    // NOT rewritten

jest.mock is the standard Jest API for stubbing modules. Any test file that mocks react-on-rails ends up in a broken state: the import points at Pro but the mock still targets the old package, so Jest loads the real module — crashing with "Cannot find module" if Pro isn't installed yet at generator time, or running assertions against real functions instead of stubs.

Fix:

mock_call_pattern = %r{
  (?<!["'`])\b\w+\.(?:mock|unmock|doMock|dontMock|requireActual|requireMock)\s*\(\s*
  (?<quote>["'])react-on-rails(?!-pro)(?=(?:["']|/))
}x

2. declare module 'react-on-rails' is never rewritten

None of the three regexes in rewrite_react_on_rails_module_specifiers handle TypeScript/Flow module declarations — all branches require import|export|require(|import(.

Repro (.d.ts):

declare module 'react-on-rails' {
  export function register(c: Record<string, any>): void;
}
declare module 'react-on-rails/client' {
  export * from 'react-on-rails';
}

The declare module headers stay at 'react-on-rails', but the inner export * from 'react-on-rails' gets rewritten to 'react-on-rails-pro'. The file ends up mixing old and new names inside one declaration.

This pattern shows up in any project that ran flow-typed install react-on-rails or hand-wrote a .d.ts stub — shakacode/hichee has one at client/flow-typed/npm/react-on-rails_vx.x.x.js (130 lines, 31 declare module statements). Running the parser directly on that file: 48 react-on-rails occurrences in, 48 out, 0 rewrites. Flow and/or tsc break across the client tree after migration.

Fix:

declare_module_pattern = %r{
  \A\s*declare\s+module\s+
  (?<quote>["'])react-on-rails(?!-pro)(?=(?:["']|/))
}x

Also catches TypeScript module augmentation inside regular .ts files.

3. Gemfile swap silently overwrites the user's version pin

build_pro_gem_replacement_line at pro_generator.rb:894-916 constructs the new line using pro_gem_version_requirement (pro_setup.rb:489-495), which always returns the currently-running ReactOnRails::VERSION. The user's original version argument is dropped.

Repro:

# input
gem 'react_on_rails', '~> 16.4', require: false
# output
gem 'react_on_rails_pro', '~> 16.6.0', require: false

require: false preserved, '~> 16.4' silently replaced with the generator's current version.

Every Gemfile with an explicit pin is affected, which is the normal case. The user's intentional version constraint is lost — no warning, no prompt, visible only by diffing the Gemfile. If they were on an older version deliberately, the generator forces them onto whatever ROR version they happen to have installed locally.

Fix: when the matched declaration has an explicit version argument, preserve it. consume_non_parenthesized_base_gem_declaration already captures the full declaration for multi-line handling, so the version string is available there.


Introduced in #2822 (commit 691ae701b).

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Backlog prioritybug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions