Module: ReactOnRails::Generators::JsDependencyManager

Included in:
BaseGenerator, InstallGenerator, ProGenerator, RscGenerator
Defined in:
lib/generators/react_on_rails/js_dependency_manager.rb,
sig/react_on_rails/generators/js_dependency_manager.rbs

Overview

Type signatures for JsDependencyManager module

This module provides common functionality for managing JavaScript dependencies in Rails generators using the package_json gem (available via shakapacker).

Constant Summary collapse

REACT_DEPENDENCIES =

Core React dependencies required for React on Rails

Returns:

%w[
  react@^19.0.0
  react-dom@^19.0.0
  prop-types@^15.0.0
].freeze
BABEL_REACT_DEPENDENCIES =

Babel preset needed by the generated babel.config.js for non-SWC setups.

%w[
  @babel/preset-react@^7.0.0
].freeze
CSS_DEPENDENCIES =

CSS processing dependencies for webpack

Returns:

%w[
  css-loader@^7.0.0
  css-minimizer-webpack-plugin@^8.0.0
  mini-css-extract-plugin@^2.0.0
  style-loader@^4.0.0
].freeze
TAILWIND_DEPENDENCIES =

Tailwind v4 CSS-first setup. The patch-level floors match published releases verified with the generator's SSR smoke app.

%w[
  tailwindcss@^4.3.0
  @tailwindcss/postcss@^4.3.0
  postcss@^8.5.15
  postcss-loader@^8.2.1
].freeze
DEV_DEPENDENCIES =

Development-only dependencies for hot reloading (Webpack)

Returns:

%w[
  @pmmmwh/react-refresh-webpack-plugin
  react-refresh
].freeze
RSPACK_DEPENDENCIES =

Rspack core dependencies (only installed when --rspack flag is used)

Returns:

%w[
  @rspack/core@^2.0.0-0
  rspack-manifest-plugin@^5.0.0
].freeze
RSPACK_DEV_DEPENDENCIES =

Rspack development dependencies for hot reloading

Returns:

%w[
  @rspack/cli@^2.0.0-0
  @rspack/dev-server@^2.0.0
  @rspack/plugin-react-refresh@^2.0.0
  react-refresh
].freeze
TYPESCRIPT_DEPENDENCIES =

TypeScript dependencies (only installed when --typescript flag is used)

Returns:

%w[
  typescript@^6.0.0
  @types/react@^19.0.0
  @types/react-dom@^19.0.0
].freeze
SWC_DEPENDENCIES =

SWC transpiler dependencies (for Shakapacker 9.3.0+ default transpiler) SWC is ~20x faster than Babel and is the default for new Shakapacker installations Version ranges match Shakapacker's own constraints.

%w[
  @swc/core@^1.3.0
  swc-loader@^0.2.0
].freeze
PRO_DEPENDENCIES =

React on Rails Pro dependencies (only installed when --pro or --rsc flag is used) These packages are published publicly on npmjs.org but require a license for production use

%w[
  react-on-rails-pro
  react-on-rails-pro-node-renderer
].freeze
RSC_DEPENDENCIES =

React Server Components dependencies (only installed when --rsc flag is used) Requires React 19.2.x with patch >= 19.2.7 - see https://react.dev/reference/rsc/server-components

%w[
  react-on-rails-rsc
].freeze
RSC_REACT_VERSION_RANGE =

React peer-dependency range for generated RSC apps. This governs the react / react-dom installs (see add_react_dependencies) and intentionally stays on the React 19.2.x line with a 19.2.7 minimum. Do not widen this to later minors just because those releases are current on npm; React's RSC runtime and bundler integration can change between minors.

This is intentionally distinct from RSC_PACKAGE_VERSION_PIN below, which pins react-on-rails-rsc. Coordination note for #3609: Pro package metadata accepts the prerelease RSC package broadly enough to keep npm ls healthy, while generated apps still install the tested React 19.2.x range and exact RSC package pin until stable 19.2.1 ships.

"~19.2.7"
RSC_PACKAGE_VERSION_PIN =

Pinned to the 19.2.1 release candidate, which carries the coordinated React 19.2.7 RSC peer floor required by the React on Rails Pro 17 runtime check.

"19.2.1-rc.0"

Instance Method Summary collapse

Instance Method Details

#add_css_dependenciesvoid

This method returns an undefined value.

Adds CSS processing dependencies to package.json

Adds error message to GeneratorMessages if package addition fails.



67
68
69
# File 'sig/react_on_rails/generators/js_dependency_manager.rbs', line 67

def add_css_dependencies
  install_dependency_group("CSS handling dependencies", "CSS dependencies", CSS_DEPENDENCIES)
end

#add_dev_dependenciesvoid

This method returns an undefined value.

Adds development dependencies to package.json

Chooses between Webpack or Rspack dev dependencies based on --rspack flag. Adds error message to GeneratorMessages if package addition fails.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'sig/react_on_rails/generators/js_dependency_manager.rbs', line 91

def add_dev_dependencies
  say "Installing development dependencies..."

  # Use Rspack-specific dev dependencies if rspack is configured
  dev_deps = using_rspack? ? RSPACK_DEV_DEPENDENCIES : DEV_DEPENDENCIES

  return if add_packages(dev_deps, dev: true)

  GeneratorMessages.add_warning(<<~MSG.strip)
    ⚠️  Failed to add development dependencies.

    You can install them manually by running:
      #{manual_add_packages_command(dev_deps, dev: true)}
  MSG
rescue StandardError => e
  GeneratorMessages.add_warning(<<~MSG.strip)
    ⚠️  Error adding development dependencies: #{e.message}

    You can install them manually by running:
      #{manual_add_packages_command(dev_deps, dev: true)}
  MSG
end

#add_js_dependenciesvoid

This method returns an undefined value.

Adds all JavaScript dependencies to package.json

This method calls individual add_*_dependencies methods in sequence. All errors are handled gracefully with warnings rather than exceptions.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'sig/react_on_rails/generators/js_dependency_manager.rbs', line 45

def add_js_dependencies
  using_pro = respond_to?(:use_pro?) && use_pro?
  using_rsc = respond_to?(:use_rsc?) && use_rsc?
  # Pro package includes react-on-rails, so skip base package when using Pro
  add_react_on_rails_package unless using_pro
  add_react_dependencies
  add_css_dependencies
  add_tailwind_dependencies_if_requested
  add_rspack_dependencies if using_rspack?
  add_transpiler_dependencies
  add_pro_dependencies if using_pro
  add_rsc_dependencies if using_rsc
  add_dev_dependencies
end

#add_package(package, dev: false) ⇒ bool

Adds a single package using package_json gem

This method is used internally for adding the react-on-rails package with version-specific handling (react-on-rails@VERSION). For batch operations, use add_packages instead.

Parameters:

  • package (String)

    Package specifier (e.g., "react-on-rails@16.0.0")

  • dev (bool) (defaults to: false)

    Whether to add as dev dependency

  • dev: (Boolean) (defaults to: false)

Returns:

  • (bool)

    true if successful, false otherwise



555
556
557
# File 'lib/generators/react_on_rails/js_dependency_manager.rb', line 555

def add_package(package, dev: false)
  add_packages([package], dev:)
end

#add_packages(packages, dev: false) ⇒ bool

Adds multiple packages at once using package_json gem

Delegates to GeneratorHelper's add_npm_dependencies for better package manager abstraction and batch processing efficiency.

Parameters:

  • packages (Array<String>)

    Package names to add

  • dev (bool) (defaults to: false)

    Whether to add as dev dependencies

  • dev: (Boolean) (defaults to: false)

Returns:

  • (bool)

    true if successful, false otherwise



567
568
569
570
571
572
573
# File 'lib/generators/react_on_rails/js_dependency_manager.rb', line 567

def add_packages(packages, dev: false)
  return true if add_npm_dependencies(packages, dev:)
  return true if install_packages_with_fallback(packages, dev:)

  write_versioned_package_specs_to_package_json(packages, dev:)
  false
end

#add_react_dependenciesvoid

This method returns an undefined value.

Adds React dependencies to package.json

Adds error message to GeneratorMessages if package addition fails.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'sig/react_on_rails/generators/js_dependency_manager.rbs', line 60

def add_react_dependencies
  say "Installing React dependencies..."

  # RSC requires the coordinated React 19.2.x patch line.
  # Pin React to ~19.2.7 while using the matching RSC package release candidate.
  react_deps = if respond_to?(:use_rsc?) && use_rsc?
                 ["react@#{RSC_REACT_VERSION_RANGE}", "react-dom@#{RSC_REACT_VERSION_RANGE}",
                  "prop-types@^15.0.0"]
               else
                 REACT_DEPENDENCIES
               end

  return if add_packages(react_deps)

  GeneratorMessages.add_warning(<<~MSG.strip)
    ⚠️  Failed to add React dependencies.

    You can install them manually by running:
      #{manual_add_packages_command(react_deps)}
  MSG
rescue StandardError => e
  GeneratorMessages.add_warning(<<~MSG.strip)
    ⚠️  Error adding React dependencies: #{e.message}

    You can install them manually by running:
      #{manual_add_packages_command(react_deps)}
  MSG
end

#add_react_on_rails_packagevoid

This method returns an undefined value.

Adds the react-on-rails package to package.json

Uses version matching for stable releases, or latest for pre-releases. Adds error message to GeneratorMessages if package addition fails.



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'sig/react_on_rails/generators/js_dependency_manager.rbs', line 53

def add_react_on_rails_package
  # Use exact version match between gem and npm package for all versions including pre-releases
  # Ruby gem versions use dots (16.2.0.beta.10) but npm requires hyphens (16.2.0-beta.10)
  # This method converts between the two formats.
  #
  # The regex matches:
  # - Stable: 16.2.0
  # - Beta (Ruby): 16.2.0.beta.10 or (npm): 16.2.0-beta.10
  # - RC (Ruby): 16.1.0.rc.1 or (npm): 16.1.0-rc.1
  # - Alpha (Ruby): 16.0.0.alpha.5 or (npm): 16.0.0-alpha.5
  # This ensures beta/rc versions use the exact version instead of "latest" which would
  # install the latest stable release and cause version mismatches.

  # Accept both dot and hyphen separators for pre-release versions
  version_with_optional_prerelease = /\A(\d+\.\d+\.\d+)([-.]([a-zA-Z0-9.]+))?\z/

  react_on_rails_pkg = if (match = ReactOnRails::VERSION.match(version_with_optional_prerelease))
                         base_version = match[1]
                         prerelease = match[3]

                         # Convert Ruby gem format (dot) to npm semver format (hyphen)
                         npm_version = if prerelease
                                         "#{base_version}-#{prerelease}"
                                       else
                                         base_version
                                       end

                         "react-on-rails@#{npm_version}"
                       else
                         say_status :warning,
                                    "Unrecognized version format #{ReactOnRails::VERSION}. " \
                                    "Adding the latest react-on-rails NPM module. " \
                                    "Double check this is correct in package.json",
                                    :yellow
                         "react-on-rails"
                       end

  say "Installing React on Rails package..."
  return if add_package(react_on_rails_pkg)

  GeneratorMessages.add_warning(<<~MSG.strip)
    ⚠️  Failed to add react-on-rails package.

    You can install it manually by running:
      #{manual_add_packages_command([react_on_rails_pkg])}
  MSG
rescue StandardError => e
  GeneratorMessages.add_warning(<<~MSG.strip)
    ⚠️  Error adding react-on-rails package: #{e.message}

    You can install it manually by running:
      #{manual_add_packages_command([react_on_rails_pkg])}
  MSG
end

#add_rspack_dependenciesvoid

This method returns an undefined value.

Adds Rspack dependencies to package.json

Only called when --rspack flag is set. Adds error message to GeneratorMessages if package addition fails.



75
76
77
# File 'sig/react_on_rails/generators/js_dependency_manager.rbs', line 75

def add_rspack_dependencies
  install_dependency_group("Rspack core dependencies", "Rspack dependencies", RSPACK_DEPENDENCIES)
end

#add_typescript_dependenciesvoid

This method returns an undefined value.

Adds TypeScript dependencies to package.json as dev dependencies

Only called when --typescript flag is set. Adds error message to GeneratorMessages if package addition fails.



83
84
85
86
# File 'sig/react_on_rails/generators/js_dependency_manager.rbs', line 83

def add_typescript_dependencies
  install_dependency_group("TypeScript dependencies", "TypeScript dependencies", TYPESCRIPT_DEPENDENCIES,
                           dev: true)
end

#install_js_dependenciesbool

Installs JavaScript dependencies using package_json gem

Always available via shakapacker dependency chain. Adds warning to GeneratorMessages if installation fails.

Returns:

  • (bool)

    true if successful, false otherwise



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'sig/react_on_rails/generators/js_dependency_manager.rbs', line 120

def install_js_dependencies
  pj = package_json
  if pj
    pj.manager.install
    return true
  end

  package_manager = fallback_package_manager
  install_args = [package_manager, "install"]

  return true if system(*install_args)

  GeneratorMessages.add_warning(<<~MSG.strip)
    ⚠️  JavaScript dependencies installation failed via #{package_manager}.

    Please run manually:
      #{install_args.join(' ')}
  MSG
  false
rescue StandardError => e
  GeneratorMessages.add_warning(<<~MSG.strip)
    ⚠️  JavaScript dependencies installation failed: #{e.message}

    This could be due to network issues or package manager problems.
    You can install dependencies manually later by running:
      #{manual_install_dependencies_command}
  MSG
  false
end

#setup_js_dependenciesvoid

This method returns an undefined value.

Sets up JavaScript dependencies by adding and installing packages

This method orchestrates the entire dependency setup process:

  1. Adds all required packages to package.json
  2. Runs package manager install


37
38
39
40
41
42
43
44
45
46
# File 'sig/react_on_rails/generators/js_dependency_manager.rbs', line 37

def setup_js_dependencies
  add_js_dependencies

  # Always run install to ensure all dependencies are properly installed.
  # The package_json gem's install method is idempotent and safe to call
  # even if packages were already added - it will only install what's needed.
  # This ensures edge cases where package.json was modified but install wasn't
  # run are handled correctly.
  install_js_dependencies
end