react_on_rails 16.6.0 → 16.7.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/.rubocop.yml +1 -0
- data/Gemfile.development_dependencies +2 -2
- data/Gemfile.lock +2 -14
- data/Rakefile +0 -6
- data/Steepfile +4 -0
- data/lib/generators/react_on_rails/base_generator.rb +4 -4
- data/lib/generators/react_on_rails/demo_page_config.rb +3 -3
- data/lib/generators/react_on_rails/dev_tests_generator.rb +1 -1
- data/lib/generators/react_on_rails/generator_helper.rb +6 -65
- data/lib/generators/react_on_rails/generator_messages/ci_section.rb +42 -0
- data/lib/generators/react_on_rails/generator_messages/package_manager_detection.rb +194 -0
- data/lib/generators/react_on_rails/generator_messages/shakapacker_status_section.rb +61 -0
- data/lib/generators/react_on_rails/generator_messages.rb +22 -79
- data/lib/generators/react_on_rails/install_generator.rb +243 -28
- data/lib/generators/react_on_rails/js_dependency_manager.rb +7 -4
- data/lib/generators/react_on_rails/pro/USAGE +1 -1
- data/lib/generators/react_on_rails/pro_generator.rb +206 -183
- data/lib/generators/react_on_rails/pro_setup.rb +102 -26
- data/lib/generators/react_on_rails/react_with_redux_generator.rb +3 -2
- data/lib/generators/react_on_rails/templates/base/base/.env.example +25 -0
- data/lib/generators/react_on_rails/templates/base/base/.github/workflows/ci.yml.tt +86 -0
- data/lib/generators/react_on_rails/templates/base/base/Procfile.dev +4 -3
- data/lib/generators/react_on_rails/templates/base/base/babel.config.js.tt +1 -1
- data/lib/generators/react_on_rails/templates/base/base/bin/switch-bundler +2 -2
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/ServerClientOrBoth.js.tt +1 -1
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/clientWebpackConfig.js.tt +1 -1
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/commonWebpackConfig.js.tt +2 -2
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/development.js.tt +1 -1
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/production.js.tt +1 -1
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt +6 -5
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/test.js.tt +1 -1
- data/lib/generators/react_on_rails/templates/pro/base/config/initializers/react_on_rails_pro.rb.tt +1 -1
- data/lib/generators/react_on_rails/templates/pro/base/{client → renderer}/node-renderer.js +1 -0
- data/lib/react_on_rails/config_path_resolver.rb +101 -4
- data/lib/react_on_rails/configuration.rb +22 -0
- data/lib/react_on_rails/dev/file_manager.rb +135 -8
- data/lib/react_on_rails/dev/port_selector.rb +259 -7
- data/lib/react_on_rails/dev/process_manager.rb +29 -2
- data/lib/react_on_rails/dev/server_manager.rb +607 -39
- data/lib/react_on_rails/doctor.rb +513 -45
- data/lib/react_on_rails/helper.rb +3 -11
- data/lib/react_on_rails/js_code_builder.rb +66 -0
- data/lib/react_on_rails/length_prefixed_parser.rb +142 -0
- data/lib/react_on_rails/packs_generator.rb +65 -12
- data/lib/react_on_rails/pro_migration.rb +175 -0
- data/lib/react_on_rails/render_request.rb +74 -0
- data/lib/react_on_rails/rendering_strategy/exec_js_strategy.rb +29 -0
- data/lib/react_on_rails/rendering_strategy.rb +44 -0
- data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +33 -22
- data/lib/react_on_rails/system_checker.rb +44 -23
- data/lib/react_on_rails/utils.rb +5 -0
- data/lib/react_on_rails/version.rb +1 -1
- data/lib/react_on_rails.rb +3 -0
- data/rakelib/run_rspec.rake +0 -5
- data/rakelib/shakapacker_examples.rake +66 -23
- data/react_on_rails.gemspec +18 -8
- data/sig/react_on_rails/js_code_builder.rbs +11 -0
- data/sig/react_on_rails/render_request.rbs +28 -0
- data/sig/react_on_rails/rendering_strategy/exec_js_strategy.rbs +11 -0
- data/sig/react_on_rails/rendering_strategy.rbs +7 -0
- data/sig/react_on_rails.rbs +6 -0
- metadata +31 -10
|
@@ -7,6 +7,7 @@ require_relative "generator_helper"
|
|
|
7
7
|
require_relative "generator_messages"
|
|
8
8
|
require_relative "js_dependency_manager"
|
|
9
9
|
require_relative "pro_setup"
|
|
10
|
+
require "react_on_rails/pro_migration"
|
|
10
11
|
|
|
11
12
|
module ReactOnRails
|
|
12
13
|
module Generators
|
|
@@ -103,43 +104,21 @@ module ReactOnRails
|
|
|
103
104
|
end
|
|
104
105
|
|
|
105
106
|
gemfile_content = File.read(gemfile_path)
|
|
106
|
-
|
|
107
|
-
base_gem_pattern = /^(\s*)gem(?:\s+|\(\s*)(["'])react_on_rails\2(?=\s*(?:,|\)|#|$))/
|
|
108
|
-
|
|
109
|
-
has_pro_gem_entry = gemfile_content.match?(pro_gem_pattern)
|
|
107
|
+
has_pro_gem_entry = ReactOnRails::ProMigration.pro_gem_entry?(gemfile_content)
|
|
110
108
|
had_pro_gem_entry_before_prerequisites =
|
|
111
|
-
original_gemfile_content_for_rollback
|
|
109
|
+
original_gemfile_content_for_rollback &&
|
|
110
|
+
ReactOnRails::ProMigration.pro_gem_entry?(original_gemfile_content_for_rollback)
|
|
112
111
|
gemfile_lines = gemfile_content.lines
|
|
113
112
|
updated_lines = []
|
|
114
|
-
pro_entry_added = has_pro_gem_entry
|
|
115
113
|
base_gem_entry_found = false
|
|
114
|
+
base_gem_entries_removed = false
|
|
116
115
|
line_index = 0
|
|
117
116
|
|
|
118
117
|
while line_index < gemfile_lines.length
|
|
119
118
|
line = gemfile_lines[line_index]
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if multiline_parenthesized_match
|
|
123
|
-
base_gem_entry_found = true
|
|
124
|
-
unless pro_entry_added
|
|
125
|
-
indentation = multiline_parenthesized_match[:indentation]
|
|
126
|
-
quote = multiline_parenthesized_match[:quote]
|
|
127
|
-
updated_lines << build_pro_gem_replacement_line(
|
|
128
|
-
indentation: indentation,
|
|
129
|
-
quote: quote,
|
|
130
|
-
suffix: multiline_parenthesized_match[:trailing_suffix],
|
|
131
|
-
parenthesized_gem_call: true
|
|
132
|
-
)
|
|
133
|
-
pro_entry_added = true
|
|
134
|
-
end
|
|
119
|
+
base_gem_declaration = ReactOnRails::ProMigration.base_gem_declaration_at(gemfile_lines, line_index)
|
|
135
120
|
|
|
136
|
-
|
|
137
|
-
next
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
match = line.match(base_gem_pattern)
|
|
141
|
-
|
|
142
|
-
unless match
|
|
121
|
+
unless base_gem_declaration
|
|
143
122
|
updated_lines << line
|
|
144
123
|
line_index += 1
|
|
145
124
|
next
|
|
@@ -147,25 +126,18 @@ module ReactOnRails
|
|
|
147
126
|
|
|
148
127
|
base_gem_entry_found = true
|
|
149
128
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
match.end(0)
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
unless pro_entry_added
|
|
157
|
-
indentation = match[1]
|
|
158
|
-
quote = match[2]
|
|
129
|
+
if has_pro_gem_entry
|
|
130
|
+
base_gem_entries_removed = true
|
|
131
|
+
else
|
|
159
132
|
updated_lines << build_pro_gem_replacement_line(
|
|
160
|
-
indentation: indentation,
|
|
161
|
-
quote: quote,
|
|
162
|
-
suffix:
|
|
163
|
-
parenthesized_gem_call:
|
|
133
|
+
indentation: base_gem_declaration[:indentation],
|
|
134
|
+
quote: base_gem_declaration[:quote],
|
|
135
|
+
suffix: base_gem_declaration[:trailing_suffix],
|
|
136
|
+
parenthesized_gem_call: base_gem_declaration[:parenthesized_gem_call]
|
|
164
137
|
)
|
|
165
|
-
pro_entry_added = true
|
|
166
138
|
end
|
|
167
139
|
|
|
168
|
-
line_index =
|
|
140
|
+
line_index = base_gem_declaration[:next_index]
|
|
169
141
|
end
|
|
170
142
|
|
|
171
143
|
updated_content = updated_lines.join
|
|
@@ -183,10 +155,6 @@ module ReactOnRails
|
|
|
183
155
|
return true
|
|
184
156
|
end
|
|
185
157
|
|
|
186
|
-
if has_pro_gem_entry
|
|
187
|
-
say "ℹ️ Existing react_on_rails_pro Gemfile entry detected; preserving current version constraint", :yellow
|
|
188
|
-
end
|
|
189
|
-
|
|
190
158
|
if options[:pretend]
|
|
191
159
|
say_status :pretend, "Would replace react_on_rails with react_on_rails_pro in Gemfile", :yellow
|
|
192
160
|
return true
|
|
@@ -194,7 +162,15 @@ module ReactOnRails
|
|
|
194
162
|
|
|
195
163
|
original_gemfile_content = original_gemfile_content_for_rollback || gemfile_content
|
|
196
164
|
atomic_write_file(gemfile_path, updated_content)
|
|
197
|
-
|
|
165
|
+
if base_gem_entries_removed
|
|
166
|
+
say(
|
|
167
|
+
"ℹ️ Existing react_on_rails_pro Gemfile entry detected; " \
|
|
168
|
+
"removed the now-stale react_on_rails entries",
|
|
169
|
+
:yellow
|
|
170
|
+
)
|
|
171
|
+
else
|
|
172
|
+
say "✅ Replaced react_on_rails with react_on_rails_pro in Gemfile", :green
|
|
173
|
+
end
|
|
198
174
|
bundle_install_after_gem_swap(
|
|
199
175
|
gemfile_path: gemfile_path,
|
|
200
176
|
original_gemfile_content: original_gemfile_content
|
|
@@ -329,8 +305,8 @@ module ReactOnRails
|
|
|
329
305
|
end
|
|
330
306
|
|
|
331
307
|
def js_files_for_import_update
|
|
332
|
-
js_extensions =
|
|
333
|
-
|
|
308
|
+
js_extensions = ReactOnRails::ProMigration::JS_SOURCE_EXTENSIONS.join(",")
|
|
309
|
+
ReactOnRails::ProMigration::JS_SOURCE_ROOTS.flat_map do |root|
|
|
334
310
|
root_path = File.join(destination_root, root)
|
|
335
311
|
next [] unless Dir.exist?(root_path)
|
|
336
312
|
|
|
@@ -339,66 +315,84 @@ module ReactOnRails
|
|
|
339
315
|
end.uniq
|
|
340
316
|
end
|
|
341
317
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
(
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
(
|
|
355
|
-
|
|
356
|
-
|
|
318
|
+
STATIC_IMPORT_SPECIFIER_PATTERN = %r{
|
|
319
|
+
(?<prefix>
|
|
320
|
+
\A\s*(?:/\*.*?\*/\s*)?(?:import|export)(?:\s+type)?\s+.*?\s+from\s+|
|
|
321
|
+
\A\s*[\w\}\],\*\$\s]+\s+from\s+
|
|
322
|
+
)
|
|
323
|
+
(?<quote>["'])
|
|
324
|
+
react-on-rails(?!-pro)
|
|
325
|
+
(?=(?:["']|/))
|
|
326
|
+
}x
|
|
327
|
+
|
|
328
|
+
DYNAMIC_OR_REQUIRE_SPECIFIER_PATTERN = %r{
|
|
329
|
+
(?<prefix>
|
|
330
|
+
(?<!["'`])\bimport\s*\(\s*(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/\s*)*|
|
|
331
|
+
(?<!["'`])\brequire\s*\(\s*(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/\s*)*
|
|
332
|
+
)
|
|
333
|
+
(?<quote>["'])
|
|
334
|
+
react-on-rails(?!-pro)
|
|
335
|
+
(?=(?:["']|/))
|
|
336
|
+
}x
|
|
337
|
+
|
|
338
|
+
SIDE_EFFECT_IMPORT_PATTERN = %r{
|
|
339
|
+
\A(?<prefix>\s*(?:/\*.*?\*/\s*)*import\s+)
|
|
340
|
+
(?<quote>["'])
|
|
341
|
+
react-on-rails(?!-pro)
|
|
342
|
+
(?=(?:["']|/))
|
|
343
|
+
}x
|
|
344
|
+
|
|
345
|
+
# Explicit allowlist of documented Jest/Vitest APIs whose first argument is a module specifier.
|
|
346
|
+
# Keep destructive rewrites narrow; the doctor can warn more broadly if needed.
|
|
347
|
+
JEST_MODULE_SPECIFIER_METHOD_PATTERN = ReactOnRails::ProMigration::JEST_MODULE_SPECIFIER_METHOD_PATTERN
|
|
348
|
+
VITEST_MODULE_SPECIFIER_METHOD_PATTERN = ReactOnRails::ProMigration::VITEST_MODULE_SPECIFIER_METHOD_PATTERN
|
|
349
|
+
|
|
350
|
+
MOCK_CALL_PATTERN = %r{
|
|
351
|
+
(?<prefix>
|
|
352
|
+
(?<!["'`])\b(?:
|
|
353
|
+
jest\.(?:#{JEST_MODULE_SPECIFIER_METHOD_PATTERN})
|
|
354
|
+
|
|
|
355
|
+
vi\.(?:#{VITEST_MODULE_SPECIFIER_METHOD_PATTERN})
|
|
357
356
|
)
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
(
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
357
|
+
\s*
|
|
358
|
+
(?:<[^;\n]*>\s*)?
|
|
359
|
+
\s*\(\s*
|
|
360
|
+
)
|
|
361
|
+
(?<quote>["'])
|
|
362
|
+
react-on-rails(?!-pro)
|
|
363
|
+
(?=(?:["']|/))
|
|
364
|
+
}x
|
|
365
|
+
|
|
366
|
+
DECLARE_MODULE_PATTERN = %r{
|
|
367
|
+
\A(?<prefix>\s*(?:export\s+)?declare\s+module\s+)
|
|
368
|
+
(?<quote>["'])
|
|
369
|
+
react-on-rails(?!-pro)
|
|
370
|
+
(?=(?:["']|/))
|
|
371
|
+
}x
|
|
372
|
+
|
|
373
|
+
BASE_PACKAGE_REWRITE_PATTERNS = [
|
|
374
|
+
STATIC_IMPORT_SPECIFIER_PATTERN,
|
|
375
|
+
DYNAMIC_OR_REQUIRE_SPECIFIER_PATTERN,
|
|
376
|
+
SIDE_EFFECT_IMPORT_PATTERN,
|
|
377
|
+
MOCK_CALL_PATTERN,
|
|
378
|
+
DECLARE_MODULE_PATTERN
|
|
379
|
+
].freeze
|
|
380
|
+
GEMFILE_STRING_DELIMITERS = ["'", '"', "`"].freeze
|
|
369
381
|
|
|
382
|
+
def rewrite_react_on_rails_module_specifiers(content)
|
|
370
383
|
rewrite_non_comment_lines(content) do |line|
|
|
371
384
|
rewrite_outside_inline_template_literals(line) do |line_without_templates|
|
|
372
|
-
|
|
373
|
-
"#{Regexp.last_match[:prefix]}#{Regexp.last_match[:quote]}react-on-rails-pro"
|
|
374
|
-
end
|
|
375
|
-
|
|
376
|
-
rewritten_line = rewritten_line.gsub(dynamic_or_require_specifier_pattern) do
|
|
377
|
-
"#{Regexp.last_match[:prefix]}#{Regexp.last_match[:quote]}react-on-rails-pro"
|
|
378
|
-
end
|
|
379
|
-
|
|
380
|
-
rewritten_line.gsub(side_effect_import_pattern) do
|
|
381
|
-
"#{Regexp.last_match[:prefix]}#{Regexp.last_match[:quote]}react-on-rails-pro"
|
|
382
|
-
end
|
|
385
|
+
rewrite_base_package_patterns(line_without_templates)
|
|
383
386
|
end
|
|
384
387
|
end
|
|
385
388
|
end
|
|
386
389
|
|
|
387
|
-
def
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
stripped = line.lstrip
|
|
394
|
-
return true if stripped.empty?
|
|
395
|
-
|
|
396
|
-
!stripped.match?(/\Agem(?:\s|\()/)
|
|
397
|
-
end
|
|
398
|
-
|
|
399
|
-
def comment_or_blank_line?(line)
|
|
400
|
-
stripped = line.lstrip
|
|
401
|
-
stripped.empty? || stripped.start_with?("#")
|
|
390
|
+
def rewrite_base_package_patterns(line)
|
|
391
|
+
BASE_PACKAGE_REWRITE_PATTERNS.reduce(line) do |result, pattern|
|
|
392
|
+
result.gsub(pattern) do
|
|
393
|
+
"#{Regexp.last_match[:prefix]}#{Regexp.last_match[:quote]}react-on-rails-pro"
|
|
394
|
+
end
|
|
395
|
+
end
|
|
402
396
|
end
|
|
403
397
|
|
|
404
398
|
def add_missing_gemfile_warning(gemfile_path)
|
|
@@ -562,23 +556,72 @@ module ReactOnRails
|
|
|
562
556
|
comment_balance.positive?
|
|
563
557
|
end
|
|
564
558
|
|
|
559
|
+
MODULE_SPECIFIER_CALL_START_PATTERN = /
|
|
560
|
+
(?<![\w$])(?:import|require)\s*\(
|
|
561
|
+
|
|
|
562
|
+
(?<!["'`])\b(?:
|
|
563
|
+
jest\.(?:#{JEST_MODULE_SPECIFIER_METHOD_PATTERN})
|
|
564
|
+
|
|
|
565
|
+
vi\.(?:#{VITEST_MODULE_SPECIFIER_METHOD_PATTERN})
|
|
566
|
+
)
|
|
567
|
+
\s*
|
|
568
|
+
(?:<[^;\n]*>\s*)?
|
|
569
|
+
\s*\(
|
|
570
|
+
/x
|
|
571
|
+
|
|
572
|
+
MODULE_SPECIFIER_CALL_WITH_STRING_PATTERN = %r{
|
|
573
|
+
(?<!["'`])\b(?:import|require)\s*\(\s*(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/\s*)*["']
|
|
574
|
+
|
|
|
575
|
+
(?<!["'`])\b(?:
|
|
576
|
+
jest\.(?:#{JEST_MODULE_SPECIFIER_METHOD_PATTERN})
|
|
577
|
+
|
|
|
578
|
+
vi\.(?:#{VITEST_MODULE_SPECIFIER_METHOD_PATTERN})
|
|
579
|
+
)
|
|
580
|
+
\s*
|
|
581
|
+
(?:<[^;\n]*>\s*)?
|
|
582
|
+
\s*\(\s*
|
|
583
|
+
(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/\s*)*
|
|
584
|
+
["']
|
|
585
|
+
}x
|
|
586
|
+
|
|
565
587
|
def starts_pending_multiline_module_call?(line)
|
|
566
588
|
line_without_literals = line_without_string_literals_and_inline_comments(line)
|
|
567
|
-
return false unless line_without_literals.match?(
|
|
589
|
+
return false unless line_without_literals.match?(MODULE_SPECIFIER_CALL_START_PATTERN)
|
|
568
590
|
|
|
569
|
-
!line.match?(
|
|
591
|
+
!line.match?(MODULE_SPECIFIER_CALL_WITH_STRING_PATTERN)
|
|
570
592
|
end
|
|
571
593
|
|
|
594
|
+
PENDING_MODULE_SPECIFIER_PATTERN = %r{(?<quote>["'])react-on-rails(?!-pro)(?=(?:["']|/))}
|
|
595
|
+
|
|
572
596
|
def rewrite_pending_module_specifier(line)
|
|
573
|
-
line.
|
|
597
|
+
match = line.match(PENDING_MODULE_SPECIFIER_PATTERN)
|
|
598
|
+
return line unless match
|
|
599
|
+
|
|
600
|
+
rewritten_line = line.sub(PENDING_MODULE_SPECIFIER_PATTERN) do
|
|
574
601
|
"#{Regexp.last_match[:quote]}react-on-rails-pro"
|
|
575
602
|
end
|
|
603
|
+
|
|
604
|
+
rewrite_statement_suffix_after_pending_module_specifier(rewritten_line, match)
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
def rewrite_statement_suffix_after_pending_module_specifier(line, pending_match)
|
|
608
|
+
closing_quote_index = line.index(pending_match[:quote], pending_match.end(0))
|
|
609
|
+
return line unless closing_quote_index
|
|
610
|
+
|
|
611
|
+
suffix = line[(closing_quote_index + 1)..].to_s
|
|
612
|
+
separator_match = suffix.match(/\A(?<separator>\s*;\s*)/)
|
|
613
|
+
return line unless separator_match
|
|
614
|
+
|
|
615
|
+
suffix_code = suffix[separator_match.end(0)..].to_s
|
|
616
|
+
rewritten_suffix_code = rewrite_base_package_patterns(suffix_code)
|
|
617
|
+
"#{line[0..closing_quote_index]}#{separator_match[:separator]}#{rewritten_suffix_code}"
|
|
576
618
|
end
|
|
577
619
|
|
|
578
620
|
def update_pending_multiline_module_call_tracking(line, pending_depth)
|
|
579
621
|
if pending_depth.positive?
|
|
580
622
|
rewritten_line = rewrite_pending_module_specifier(line)
|
|
581
623
|
updated_depth = pending_depth + module_call_parenthesis_delta(rewritten_line)
|
|
624
|
+
updated_depth = 0 if rewritten_line != line
|
|
582
625
|
updated_depth = 0 if updated_depth <= 0
|
|
583
626
|
[rewritten_line, updated_depth]
|
|
584
627
|
elsif starts_pending_multiline_module_call?(line)
|
|
@@ -617,7 +660,8 @@ module ReactOnRails
|
|
|
617
660
|
def module_call_parenthesis_delta(line, from_module_call_start: false)
|
|
618
661
|
line_without_literals = line_without_string_literals_and_inline_comments(line)
|
|
619
662
|
line_to_measure = if from_module_call_start
|
|
620
|
-
line_without_literals.
|
|
663
|
+
match = line_without_literals.match(MODULE_SPECIFIER_CALL_START_PATTERN)
|
|
664
|
+
match ? "(#{line_without_literals[match.end(0)..]}" : line_without_literals
|
|
621
665
|
else
|
|
622
666
|
line_without_literals
|
|
623
667
|
end
|
|
@@ -823,96 +867,75 @@ module ReactOnRails
|
|
|
823
867
|
nil
|
|
824
868
|
end
|
|
825
869
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
start_match = start_line.match(/^(\s*)gem\s*\(/)
|
|
830
|
-
return nil unless start_match
|
|
831
|
-
|
|
832
|
-
line_index = start_index
|
|
833
|
-
found_base_gem_name = false
|
|
834
|
-
base_gem_quote = nil
|
|
835
|
-
gem_name_line_index = nil
|
|
836
|
-
gem_name_match_end = nil
|
|
837
|
-
paren_depth = 0
|
|
838
|
-
|
|
839
|
-
while line_index < lines.length
|
|
840
|
-
line = lines[line_index]
|
|
841
|
-
line_without_comment = line.sub(/\s*#.*$/, "")
|
|
842
|
-
line_without_literals = line_without_string_literals_and_inline_comments(line, strip_ruby_comments: true)
|
|
843
|
-
|
|
844
|
-
if !found_base_gem_name &&
|
|
845
|
-
(gem_name_match = line_without_comment.match(/(["'])react_on_rails\1(?=\s*(?:,|\)|#|$))/))
|
|
846
|
-
found_base_gem_name = true
|
|
847
|
-
base_gem_quote = gem_name_match[1]
|
|
848
|
-
gem_name_line_index = line_index
|
|
849
|
-
gem_name_match_end = gem_name_match.end(0)
|
|
850
|
-
end
|
|
870
|
+
def build_pro_gem_replacement_line(indentation:, quote:, suffix:, parenthesized_gem_call: false)
|
|
871
|
+
normalized_suffix = suffix || "\n"
|
|
872
|
+
normalized_suffix = "#{normalized_suffix}\n" unless normalized_suffix.end_with?("\n")
|
|
851
873
|
|
|
852
|
-
|
|
874
|
+
has_user_version_pin = normalized_suffix.match?(/\A\s*,\s*(?:#[^\n]*\n\s*)*["']/)
|
|
875
|
+
version_arg = has_user_version_pin ? "" : ", #{quote}#{pro_gem_version_requirement}#{quote}"
|
|
853
876
|
|
|
854
|
-
|
|
855
|
-
|
|
877
|
+
if parenthesized_gem_call
|
|
878
|
+
normalized_suffix = remove_parenthesized_gem_call_closing_parenthesis(normalized_suffix)
|
|
879
|
+
end
|
|
880
|
+
normalized_suffix = "\n" if normalized_suffix.match?(/\A,\s*\n\z/)
|
|
856
881
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
suffix = "\n" if suffix.nil? || suffix.empty?
|
|
860
|
-
return {
|
|
861
|
-
indentation: start_match[1],
|
|
862
|
-
quote: base_gem_quote,
|
|
863
|
-
next_index: line_index + 1,
|
|
864
|
-
trailing_suffix: suffix
|
|
865
|
-
}
|
|
866
|
-
end
|
|
882
|
+
"#{indentation}gem #{quote}react_on_rails_pro#{quote}#{version_arg}#{normalized_suffix}"
|
|
883
|
+
end
|
|
867
884
|
|
|
868
|
-
|
|
869
|
-
|
|
885
|
+
def remove_parenthesized_gem_call_closing_parenthesis(suffix)
|
|
886
|
+
closing_index = parenthesized_gem_call_closing_parenthesis_index(suffix)
|
|
887
|
+
return suffix unless closing_index
|
|
870
888
|
|
|
871
|
-
|
|
889
|
+
prefix = suffix[0...closing_index]
|
|
890
|
+
rest = suffix[(closing_index + 1)..].to_s
|
|
891
|
+
return "#{prefix.rstrip} #{rest.lstrip}" if closing_parenthesis_line_has_postfix_code?(rest)
|
|
892
|
+
|
|
893
|
+
"#{prefix.chomp}#{rest}"
|
|
872
894
|
end
|
|
873
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
874
|
-
|
|
875
|
-
def consume_non_parenthesized_base_gem_declaration(lines, start_index, match_end)
|
|
876
|
-
line_index = start_index
|
|
877
|
-
current_line = lines[line_index]
|
|
878
|
-
declaration_lines = [current_line]
|
|
879
|
-
line_index += 1
|
|
880
|
-
|
|
881
|
-
while line_index < lines.length &&
|
|
882
|
-
line_continues_with_comma?(current_line) &&
|
|
883
|
-
gem_declaration_continues_on_next_line?(lines[line_index])
|
|
884
|
-
next_line = lines[line_index]
|
|
885
|
-
declaration_lines << next_line
|
|
886
|
-
current_line = next_line unless comment_or_blank_line?(next_line)
|
|
887
|
-
line_index += 1
|
|
888
|
-
end
|
|
889
895
|
|
|
890
|
-
|
|
891
|
-
|
|
896
|
+
def closing_parenthesis_line_has_postfix_code?(rest)
|
|
897
|
+
stripped_rest = rest.lstrip
|
|
898
|
+
!stripped_rest.empty? && !stripped_rest.start_with?("#", "\n", "\r")
|
|
892
899
|
end
|
|
893
900
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
901
|
+
# The suffix starts after the gem name but still inside the original `gem(...)` call,
|
|
902
|
+
# so the matching call-closing parenthesis is found by starting at depth 1.
|
|
903
|
+
def parenthesized_gem_call_closing_parenthesis_index(suffix)
|
|
904
|
+
depth = 1
|
|
905
|
+
quote = nil
|
|
906
|
+
scan_index = 0
|
|
907
|
+
|
|
908
|
+
while scan_index < suffix.length
|
|
909
|
+
char = suffix[scan_index]
|
|
910
|
+
|
|
911
|
+
if quote
|
|
912
|
+
quote = nil if char == quote && !character_escaped?(suffix, scan_index)
|
|
913
|
+
else
|
|
914
|
+
scan_index, depth, quote, closing_index =
|
|
915
|
+
next_parenthesized_gem_suffix_scan_state(suffix, scan_index, depth)
|
|
916
|
+
return closing_index if closing_index
|
|
917
|
+
return nil unless scan_index
|
|
905
918
|
end
|
|
906
|
-
break if updated_suffix == normalized_suffix
|
|
907
919
|
|
|
908
|
-
|
|
920
|
+
scan_index += 1
|
|
909
921
|
end
|
|
910
|
-
normalized_suffix = normalized_suffix.sub(/\A,\s*(?:#[^\n]*)?\n\z/, "\n")
|
|
911
|
-
normalized_suffix = normalized_suffix.sub(/\A,[ \t]{2,}/, ", ")
|
|
912
|
-
normalized_suffix = normalized_suffix.sub(/\)(\s*(?:#.*)?\n)\z/, '\1') if parenthesized_gem_call
|
|
913
922
|
|
|
914
|
-
|
|
915
|
-
|
|
923
|
+
nil
|
|
924
|
+
end
|
|
925
|
+
|
|
926
|
+
def next_parenthesized_gem_suffix_scan_state(suffix, scan_index, depth)
|
|
927
|
+
char = suffix[scan_index]
|
|
928
|
+
return [scan_index, depth, char, nil] if GEMFILE_STRING_DELIMITERS.include?(char)
|
|
929
|
+
return [suffix.index("\n", scan_index), depth, nil, nil] if char == "#"
|
|
930
|
+
return [scan_index, depth + 1, nil, nil] if char == "("
|
|
931
|
+
return parenthesized_gem_suffix_closing_state(scan_index, depth) if char == ")"
|
|
932
|
+
|
|
933
|
+
[scan_index, depth, nil, nil]
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
def parenthesized_gem_suffix_closing_state(scan_index, depth)
|
|
937
|
+
next_depth = depth - 1
|
|
938
|
+
[scan_index, next_depth, nil, next_depth.zero? ? scan_index : nil]
|
|
916
939
|
end
|
|
917
940
|
|
|
918
941
|
def print_success_message
|