Skip to content

Commit f9ef839

Browse files
justin808claude
andcommitted
Address PR feedback: Fix type imports, security, and add CSS modules
Type-only import fixes: - Use type-only imports for HelloWorldState in HelloWorldContainer.ts - Separate value/type imports in helloWorldStore.ts with PreloadedState typing - Fix PropsFromRedux import to type-only in HelloWorld.tsx Package management fixes: - Remove unnecessary @types/react-redux dependency (react-redux now bundles types) - Add -T alias to ReactWithReduxGenerator for --typescript option - Fix TypeScript dependency installation to use GeneratorHelper properly Security improvements: - Add package manager validation to prevent command injection - Use allowlist of valid package managers (npm, yarn, pnpm, bun) Type definition enhancements: - Add CSS module type definitions for .module.css/.scss/.sass files - Create app/javascript/types/css-modules.d.ts automatically when TypeScript is enabled All changes maintain backward compatibility and improve type safety. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent e8614b2 commit f9ef839

File tree

5 files changed

+58
-22
lines changed

5 files changed

+58
-22
lines changed

lib/generators/react_on_rails/install_generator.rb

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ def print_generator_messages
6666

6767
def invoke_generators
6868
ensure_shakapacker_installed
69-
install_typescript_dependencies if options.typescript?
69+
if options.typescript?
70+
install_typescript_dependencies
71+
create_css_module_types
72+
end
7073
invoke "react_on_rails:base", [], { typescript: options.typescript? }
7174
if options.redux?
7275
invoke "react_on_rails:react_with_redux", [], { typescript: options.typescript? }
@@ -314,10 +317,6 @@ def missing_package_manager?
314317
def install_typescript_dependencies
315318
puts Rainbow("📝 Installing TypeScript dependencies...").yellow
316319

317-
# Determine the package manager to use
318-
package_manager = detect_package_manager
319-
return unless package_manager
320-
321320
# Install TypeScript and React type definitions
322321
typescript_packages = %w[
323322
typescript
@@ -326,10 +325,48 @@ def install_typescript_dependencies
326325
@babel/preset-typescript
327326
]
328327

328+
# Try using GeneratorHelper first (package manager agnostic)
329329
return if add_npm_dependencies(typescript_packages, dev: true)
330330

331+
# Fallback to npm if GeneratorHelper fails
331332
success = run "npm install --save-dev #{typescript_packages.join(' ')}"
332-
handle_npm_failure("TypeScript dependencies", typescript_packages) unless success
333+
unless success
334+
warning = <<~MSG.strip
335+
⚠️ Failed to install TypeScript dependencies automatically.
336+
337+
Please run manually:
338+
npm install --save-dev #{typescript_packages.join(' ')}
339+
MSG
340+
GeneratorMessages.add_warning(warning)
341+
end
342+
end
343+
344+
def create_css_module_types
345+
puts Rainbow("📝 Creating CSS module type definitions...").yellow
346+
347+
# Ensure the types directory exists
348+
FileUtils.mkdir_p("app/javascript/types")
349+
350+
css_module_types_content = <<~TS.strip
351+
// TypeScript definitions for CSS modules
352+
declare module "*.module.css" {
353+
const classes: { [key: string]: string };
354+
export default classes;
355+
}
356+
357+
declare module "*.module.scss" {
358+
const classes: { [key: string]: string };
359+
export default classes;
360+
}
361+
362+
declare module "*.module.sass" {
363+
const classes: { [key: string]: string };
364+
export default classes;
365+
}
366+
TS
367+
368+
File.write("app/javascript/types/css-modules.d.ts", css_module_types_content)
369+
puts Rainbow("✅ Created CSS module type definitions").green
333370
end
334371

335372
def create_typescript_config

lib/generators/react_on_rails/react_with_redux_generator.rb

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ class ReactWithReduxGenerator < Rails::Generators::Base
1414
class_option :typescript,
1515
type: :boolean,
1616
default: false,
17-
desc: "Generate TypeScript files"
17+
desc: "Generate TypeScript files",
18+
aliases: "-T"
1819

1920
def create_redux_directories
2021
# Create auto-registration directory structure for Redux
@@ -82,23 +83,13 @@ def add_redux_npm_dependencies
8283
# Try using GeneratorHelper first (package manager agnostic)
8384
success = add_npm_dependencies(regular_packages)
8485

85-
# Add TypeScript types as dev dependency if TypeScript is enabled
86-
if options.typescript?
87-
types_success = add_npm_dependencies(%w[@types/react-redux], dev: true)
88-
success &&= types_success
89-
end
90-
9186
# Fallback to package manager detection if GeneratorHelper fails
9287
return if success
9388

9489
package_manager = detect_package_manager
9590
return unless package_manager
9691

9792
install_packages_with_fallback(regular_packages, dev: false, package_manager: package_manager)
98-
99-
return unless options.typescript?
100-
101-
install_packages_with_fallback(%w[@types/react-redux], dev: true, package_manager: package_manager)
10293
end
10394

10495
private
@@ -120,6 +111,12 @@ def install_packages_with_fallback(packages, dev:, package_manager:)
120111
end
121112

122113
def build_install_command(package_manager, dev, packages_str)
114+
# Security: Validate package manager to prevent command injection
115+
allowed_package_managers = %w[npm yarn pnpm bun].freeze
116+
unless allowed_package_managers.include?(package_manager)
117+
raise ArgumentError, "Invalid package manager: #{package_manager}"
118+
end
119+
123120
commands = {
124121
"npm" => { dev: "npm install --save-dev", prod: "npm install" },
125122
"yarn" => { dev: "yarn add --dev", prod: "yarn add" },

lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import * as style from './HelloWorld.module.css';
3-
import { PropsFromRedux } from '../containers/HelloWorldContainer';
3+
import type { PropsFromRedux } from '../containers/HelloWorldContainer';
44

55
// Component props are inferred from Redux container
66
type HelloWorldProps = PropsFromRedux;

lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/containers/HelloWorldContainer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { connect, ConnectedProps } from 'react-redux';
44
import HelloWorld from '../components/HelloWorld';
55
import * as actions from '../actions/helloWorldActionCreators';
6-
import { HelloWorldState } from '../reducers/helloWorldReducer';
6+
import type { HelloWorldState } from '../reducers/helloWorldReducer';
77

88
// Which part of the Redux global state does our component want to receive as props?
99
const mapStateToProps = (state: HelloWorldState) => ({ name: state.name });

lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/store/helloWorldStore.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import { createStore, Store } from 'redux';
2-
import helloWorldReducer, { HelloWorldState } from '../reducers/helloWorldReducer';
1+
import { createStore } from 'redux';
2+
import type { Store, PreloadedState } from 'redux';
3+
import helloWorldReducer from '../reducers/helloWorldReducer';
4+
import type { HelloWorldState } from '../reducers/helloWorldReducer';
35

46
// Rails props interface - customize based on your Rails controller
57
export interface RailsProps {
@@ -11,6 +13,6 @@ export interface RailsProps {
1113
export type HelloWorldStore = Store<HelloWorldState>;
1214

1315
const configureStore = (railsProps: RailsProps): HelloWorldStore =>
14-
createStore(helloWorldReducer, railsProps);
16+
createStore(helloWorldReducer, railsProps as PreloadedState<HelloWorldState>);
1517

1618
export default configureStore;

0 commit comments

Comments
 (0)