ruby-next-core 0.9.0.pre → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/README.md +29 -8
  4. data/bin/transform +9 -1
  5. data/lib/.rbnext/2.3/ruby-next/commands/core_ext.rb +167 -0
  6. data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +198 -0
  7. data/lib/.rbnext/2.3/ruby-next/language/eval.rb +66 -0
  8. data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +125 -0
  9. data/lib/.rbnext/2.3/ruby-next/language/rewriters/endless_range.rb +63 -0
  10. data/lib/.rbnext/2.3/ruby-next/language/rewriters/pattern_matching.rb +944 -0
  11. data/lib/.rbnext/2.3/ruby-next/language/rewriters/right_hand_assignment.rb +107 -0
  12. data/lib/.rbnext/2.3/ruby-next/utils.rb +65 -0
  13. data/lib/ruby-next.rb +8 -6
  14. data/lib/ruby-next/cli.rb +2 -2
  15. data/lib/ruby-next/commands/core_ext.rb +2 -2
  16. data/lib/ruby-next/commands/nextify.rb +64 -22
  17. data/lib/ruby-next/core.rb +31 -20
  18. data/lib/ruby-next/core/array/deconstruct.rb +9 -9
  19. data/lib/ruby-next/core/array/difference_union_intersection.rb +12 -12
  20. data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +3 -3
  21. data/lib/ruby-next/core/enumerable/filter.rb +8 -8
  22. data/lib/ruby-next/core/enumerable/filter_map.rb +25 -25
  23. data/lib/ruby-next/core/enumerable/tally.rb +7 -7
  24. data/lib/ruby-next/core/enumerator/produce.rb +12 -12
  25. data/lib/ruby-next/core/hash/deconstruct_keys.rb +9 -9
  26. data/lib/ruby-next/core/hash/except.rb +11 -0
  27. data/lib/ruby-next/core/hash/merge.rb +8 -8
  28. data/lib/ruby-next/core/kernel/then.rb +2 -2
  29. data/lib/ruby-next/core/proc/compose.rb +11 -11
  30. data/lib/ruby-next/core/string/split.rb +6 -6
  31. data/lib/ruby-next/core/struct/deconstruct.rb +2 -2
  32. data/lib/ruby-next/core/struct/deconstruct_keys.rb +17 -17
  33. data/lib/ruby-next/core/symbol/end_with.rb +4 -4
  34. data/lib/ruby-next/core/symbol/start_with.rb +4 -4
  35. data/lib/ruby-next/core/time/ceil.rb +6 -5
  36. data/lib/ruby-next/core/time/floor.rb +4 -4
  37. data/lib/ruby-next/core/unboundmethod/bind_call.rb +4 -4
  38. data/lib/ruby-next/core_ext.rb +1 -1
  39. data/lib/ruby-next/language.rb +32 -6
  40. data/lib/ruby-next/language/eval.rb +10 -8
  41. data/lib/ruby-next/language/proposed.rb +3 -0
  42. data/lib/ruby-next/language/rewriters/args_forward.rb +24 -20
  43. data/lib/ruby-next/language/rewriters/base.rb +6 -2
  44. data/lib/ruby-next/language/rewriters/endless_method.rb +26 -3
  45. data/lib/ruby-next/language/rewriters/endless_range.rb +1 -0
  46. data/lib/ruby-next/language/rewriters/find_pattern.rb +44 -0
  47. data/lib/ruby-next/language/rewriters/method_reference.rb +2 -1
  48. data/lib/ruby-next/language/rewriters/numbered_params.rb +1 -0
  49. data/lib/ruby-next/language/rewriters/pattern_matching.rb +105 -13
  50. data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +74 -11
  51. data/lib/ruby-next/language/rewriters/runtime.rb +6 -0
  52. data/lib/ruby-next/language/rewriters/runtime/dir.rb +32 -0
  53. data/lib/ruby-next/language/rewriters/safe_navigation.rb +87 -0
  54. data/lib/ruby-next/language/rewriters/shorthand_hash.rb +47 -0
  55. data/lib/ruby-next/language/rewriters/squiggly_heredoc.rb +36 -0
  56. data/lib/ruby-next/language/runtime.rb +3 -2
  57. data/lib/ruby-next/logging.rb +1 -1
  58. data/lib/ruby-next/rubocop.rb +15 -9
  59. data/lib/ruby-next/setup_self.rb +22 -0
  60. data/lib/ruby-next/utils.rb +30 -0
  61. data/lib/ruby-next/version.rb +1 -1
  62. data/lib/uby-next.rb +8 -4
  63. metadata +25 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44b797d87fbbc3b9eb0c31ad04466abadb286728a80a5222808072994939dc81
4
- data.tar.gz: 3e3f018b5adbfb2f2213722509d5d50fafec01efd85e92580e26234f7fd2f99e
3
+ metadata.gz: b97da767efae69a9faa9ed2b4c4a3daf9c38a5b70768502b013692b7a3c04a50
4
+ data.tar.gz: 05ba2f4c4677c40d46a451a68a94e434b54d4e9daa9a2af51cbd508ef0a198ab
5
5
  SHA512:
6
- metadata.gz: 9ee37b8457ecba6d8c0ae4ff82a1d9e4a5fbc72a7d9e8bd0997d13ce461d0d3894cae439ddde2836a1e48c1af481d978c66eec83cb6f9865b96c417b69a0d0ba
7
- data.tar.gz: 4d74819e5235e53cdad9c8f5ae25d761309937002a4d03858c0b979fc504535fc04ab76a45dd74ceb9165acb06d73fb643dbc02349925dc438d21155dcc342fc
6
+ metadata.gz: ef678d9b8ed431a8a8f005b00c445da56096c39a3c9ffc1ce88708c4b1280e630cb5c58a63a154381c384b54968ec19b3c88a4a49665b3503bd5df62f454a168
7
+ data.tar.gz: a25ebf4e0c3456213a1f682d27df031a6add5bc117800026d8b0f9b48cacb49e72a4b97115f9b1b1acf058ca3f6974dfa94402498a58a19ac532916eb9a81199
@@ -2,6 +2,51 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.10.1 (2020-09-09)
6
+
7
+ - Fix regression when `nextify` produces incorrect files for 2.7. ([@palkan][])
8
+
9
+ ## 0.10.0 (2020-09-02)
10
+
11
+ - Add proposed shorthand Hash syntax. ([@palkan][])
12
+
13
+ You can try it: `x = 1; y = 2; data = {x, y}`.
14
+
15
+ - Add leading argument support to args forwarding. ([@palkan][])
16
+
17
+ `def a(...) b(1, ...); end`.
18
+
19
+ - Add `Hash#except`. ([@palkan][])
20
+
21
+ `{a: 1, b: 2}.except(:a) == {b: 2}`
22
+
23
+ - Add find pattern support. ([@palkan][])
24
+
25
+ Now you can do: `[0, 1, 2] in [*, 1 => a, *c]`.
26
+
27
+ - Add Ruby 2.2 support. ([@palkan][])
28
+
29
+ With support for safe navigation operator (`&.`) and squiggly heredocs (`<<~TXT`).
30
+
31
+ ## 0.9.2 (2020-06-24)
32
+
33
+ - Support passing rewriters to CLI. ([@sl4vr][])
34
+
35
+ Use `nextify --list-rewriters` to view all available rewriters.
36
+ Use `nextify` with `--rewrite=REWRITERS...` option to specify which particular rewriters to use.
37
+
38
+ ## 0.9.1 (2020-06-05)
39
+
40
+ - Keep `ruby-next` version in sync with `ruby-next-core`. ([@palkan][])
41
+
42
+ Require `ruby-next-core` of the same version as `ruby-next`.
43
+
44
+ ## 0.9.0 (2020-06-04)
45
+
46
+ - Add Ruby 2.3 support. ([@palkan][])
47
+
48
+ - Remove stale transpiled files when running `ruby-next nextify`. ([@palkan][])
49
+
5
50
  - Add Ruby 2.4 support. ([@palkan][])
6
51
 
7
52
  APIs for <2.5 must be backported via [backports][] gem. Refinements are not supported.
@@ -183,3 +228,4 @@ p a #=> 1
183
228
 
184
229
  [@palkan]: https://github.com/palkan
185
230
  [backports]: https://github.com/marcandre/backports
231
+ [@sl4vr]: https://github.com/sl4vr
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
+ [![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](https://cultofmartians.com/tasks/ruby-next-cli-rewriters.html#task)
1
2
  [![Gem Version](https://badge.fury.io/rb/ruby-next.svg)](https://rubygems.org/gems/ruby-next) [![Build](https://github.com/ruby-next/ruby-next/workflows/Build/badge.svg)](https://github.com/ruby-next/ruby-next/actions)
2
- [![JRuby Build](https://github.com/ruby-next/ruby-next/workflows/JRuby%20Build/badge.svg)](https://github.com/ruby-next/ruby-next/actions)
3
+ [![JRuby Build](https://github.com/ruby-next/ruby-next/workflows/JRuby%20Build/badge.svg)](https://github.com/ruby-next/ruby-next/actions?query=workflow%3A%22TruffleRuby+Build%22)
4
+ [![TruffleRuby Build](https://github.com/ruby-next/ruby-next/workflows/TruffleRuby%20Build/badge.svg)](https://github.com/ruby-next/ruby-next/actions?query=workflow%3A%22TruffleRuby+Build%22)
3
5
 
4
6
  # Ruby Next
5
7
 
@@ -35,6 +37,8 @@ Read more about the motivation behind the Ruby Next in this post: [Ruby Next: Ma
35
37
  - Ruby gems
36
38
  - [anyway_config](https://github.com/palkan/anyway_config)
37
39
  - [graphql-fragment_cache](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache)
40
+ - Rails applications
41
+ - [anycable_rails_demo](https://github.com/anycable/anycable_rails_demo)
38
42
  - mruby
39
43
  - [ACLI](https://github.com/palkan/acli)
40
44
 
@@ -66,7 +70,7 @@ Core provides **polyfills** for Ruby core classes APIs via Refinements (default
66
70
  Language is responsible for **transpiling** edge Ruby syntax into older versions. It could be done
67
71
  programmatically or via CLI. It also could be done in runtime.
68
72
 
69
- Currently, Ruby Next supports Ruby versions 2.4+ (including JRuby 9.2.8+). Support for EOL versions (<2.5) slightly differs though ([see below](#using-with-eol-rubies)).
73
+ Currently, Ruby Next supports Ruby versions 2.2+, including JRuby 9.2.8+ and TruffleRuby 20.1+ (with some limitations). Support for EOL versions (<2.5) slightly differs though ([see below](#using-with-eol-rubies)).
70
74
 
71
75
  Please, [open an issue](https://github.com/ruby-next/ruby-next/issues/new/choose) or join the discussion in the existing ones if you would like us to support older Ruby versions.
72
76
 
@@ -137,6 +141,8 @@ The following _rule of thumb_ is recommended when choosing between refinements a
137
141
  - Using core extensions could be considered for application development (no need to think about `using RubyNext`); this approach could potentially lead to conflicts with dependencies (if these dependencies are not using refinements 🙂)
138
142
  - Use core extensions if refinements are not supported by your platform
139
143
 
144
+ **NOTE:** _Edge_ APIs (i.e., from the Ruby's master branch) are included by default.
145
+
140
146
  [**The list of supported APIs.**][features_core]
141
147
 
142
148
  ## Transpiling
@@ -197,12 +203,15 @@ It has the following interface:
197
203
  ```sh
198
204
  $ ruby-next nextify
199
205
  Usage: ruby-next nextify DIRECTORY_OR_FILE [options]
200
- -o, --output=OUTPUT Specify output directory or file or stdout (use -o stdout for that)
206
+ -o, --output=OUTPUT Specify output directory or file or stdout
201
207
  --min-version=VERSION Specify the minimum Ruby version to support
202
208
  --single-version Only create one version of a file (for the earliest Ruby version)
203
- --enable-method-reference Enable reverted method reference syntax (requires custom parser)
209
+ --edge Enable edge (master) Ruby features
210
+ --proposed Enable proposed/experimental Ruby features
204
211
  --transpile-mode=MODE Transpiler mode (ast or rewrite). Default: ast
205
212
  --[no-]refine Do not inject `using RubyNext`
213
+ --list-rewriters List available rewriters
214
+ --rewrite=REWRITERS... Specify particular Ruby features to rewrite
206
215
  -h, --help Print help
207
216
  -V Turn on verbose mode
208
217
  --dry-run Print verbose output without generating files
@@ -390,6 +399,12 @@ RUBYOPT="-ruby-next" ruby my_ruby_script.rb
390
399
  ruby -ruby-next -e "puts [2, 4, 5].tally"
391
400
  ```
392
401
 
402
+ **NOTE:** running Ruby scripts directly or executing code via `-e` option is not supported in TruffleRuby. You can still use `-ruby-next` to transpile required files, e.g.:
403
+
404
+ ```sh
405
+ ruby -ruby-next -r my_ruby_script.rb -e "puts my_method"
406
+ ```
407
+
393
408
  ## Logging and debugging
394
409
 
395
410
  Ruby Next prints some debugging information when fails to load a file in the runtime mode (and fallbacks to the built-in loading mechanism).
@@ -414,6 +429,8 @@ You must set `TargetRubyVersion: next` to make RuboCop use a Ruby Next parser.
414
429
 
415
430
  Alternatively, you can load the patch from the command line by running: `rubocop -r ruby-next/rubocop ...`.
416
431
 
432
+ We recommend using the latest RuboCop version, 'cause it has support for new nodes built-in.
433
+
417
434
  Also, when pre-transpiling source code with `ruby-next nextify`, we suggest ignoring the transpiled files:
418
435
 
419
436
  ```yml
@@ -426,9 +443,7 @@ AllCops:
426
443
 
427
444
  ## Using with EOL Rubies
428
445
 
429
- **NOTE:** Use Ruby Next `0.9.0.pre` to use this feature.
430
-
431
- We currently provide experimental support for Ruby 2.4. Work on older Rubies (down to 2.2) is in progress.
446
+ We currently provide support for Ruby 2.2, 2.3 and 2.4.
432
447
 
433
448
  Ruby Next itself relies on 2.5 features and contains polyfills only for version 2.5+ (and that won't change).
434
449
  Thus, to make it work with <2.5 we need to backport some APIs ourselves.
@@ -458,6 +473,8 @@ RUBY_NEXT_CORE_STRATEGY=backports ruby-next nextify lib/
458
473
 
459
474
  **NOTE:** Make sure you have `backports` gem installed globally or added to your bundle (if you're using `bundle exec ruby-next ...`).
460
475
 
476
+ **NOTE:** For Ruby 2.2, safe navigation operator (`&.`) and squiggly heredocs (`<<~TXT`) support is provided.
477
+
461
478
  ## Proposed and edge features
462
479
 
463
480
  Ruby Next aims to bring edge and proposed features to Ruby community before they (hopefully) reach an official Ruby release.
@@ -491,12 +508,16 @@ require "ruby-next/language/runtime"
491
508
 
492
509
  - "Endless" method definition (`def foo() = 42`) ([#16746](https://bugs.ruby-lang.org/issues/16746)).
493
510
 
494
- - Right-hand assignment (`13.divmod(5) => a,b`) ([#15921](https://bugs.ruby-lang.org/issues/15921))
511
+ - Right-hand assignment (`13.divmod(5) => a,b`) ([#15921](https://bugs.ruby-lang.org/issues/15921)).
512
+
513
+ - Find pattern (`[0, 1, 2] in [*, 1 => a, *c]`) ([#16828](https://bugs.ruby-lang.org/issues/16828)).
495
514
 
496
515
  ### Supported proposed features
497
516
 
498
517
  - _Method reference_ operator (`.:`) ([#13581](https://bugs.ruby-lang.org/issues/13581)).
499
518
 
519
+ - Shorthand Hash notation (`data = {x, y}`) ([#15236](https://bugs.ruby-lang.org/issues/15236)).
520
+
500
521
  ## Contributing
501
522
 
502
523
  Bug reports and pull requests are welcome on GitHub at [https://github.com/ruby-next/ruby-next](ttps://github.com/ruby-next/ruby-next).
@@ -9,6 +9,7 @@ ENV["RUBY_NEXT_EDGE"] = "1"
9
9
  ENV["RUBY_NEXT_PROPOSED"] = "1"
10
10
 
11
11
  require "ruby-next/language"
12
+ require "ruby-next/language/rewriters/runtime"
12
13
 
13
14
  contents =
14
15
  if File.exist?(ARGV[0])
@@ -17,4 +18,11 @@ contents =
17
18
  ARGV[0]
18
19
  end
19
20
 
20
- puts RubyNext::Language.transform(contents)
21
+ opts =
22
+ if ARGV[1] && ARGV[1] == "--current"
23
+ {rewriters: RubyNext::Language.current_rewriters}
24
+ else
25
+ {}
26
+ end
27
+
28
+ puts RubyNext::Language.transform(contents, **opts)
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "pathname"
5
+
6
+ module RubyNext
7
+ module Commands
8
+ class CoreExt < Base
9
+ using RubyNext
10
+
11
+ attr_reader :out_path, :min_version, :names, :list, :filter, :original_command
12
+ alias list? list
13
+
14
+ def run
15
+ log "Select core extensions for Ruby v#{min_version}" \
16
+ "#{filter ? " and matching #{filter.inspect}" : ""}"
17
+
18
+ matching_patches.then do |patches|
19
+ next print_list(patches) if list?
20
+ generate_core_ext(patches)
21
+ end
22
+ end
23
+
24
+ def parse!(args)
25
+ print_help = false
26
+ @min_version = MIN_SUPPORTED_VERSION
27
+ @original_command = "ruby-next core_ext #{args.join(" ")}"
28
+ @names = []
29
+ @list = false
30
+ @out_path = File.join(Dir.pwd, "core_ext.rb")
31
+
32
+ optparser = base_parser do |opts|
33
+ opts.banner = "Usage: ruby-next core_ext [options]"
34
+
35
+ opts.on("-o", "--output=OUTPUT", "Specify output file or stdout (default: ./core_ext.rb)") do |val|
36
+ @out_path = val
37
+ end
38
+
39
+ opts.on("-l", "--list", "List all available extensions") do
40
+ @list = true
41
+ end
42
+
43
+ opts.on("--min-version=VERSION", "Specify the minimum Ruby version to support") do |val|
44
+ @min_version = Gem::Version.new(val)
45
+ end
46
+
47
+ opts.on("-n", "--name=NAME", "Filter extensions by name") do |val|
48
+ names << val
49
+ end
50
+
51
+ opts.on("-h", "--help", "Print help") do
52
+ print_help = true
53
+ end
54
+ end
55
+
56
+ optparser.parse!(args)
57
+
58
+ if print_help
59
+ $stdout.puts optparser.help
60
+ exit 0
61
+ end
62
+
63
+ @filter = /(#{names.join("|")})/i unless names.empty?
64
+ end
65
+
66
+ private
67
+
68
+ def matching_patches
69
+ RubyNext::Core.patches.extensions
70
+ .values
71
+ .flatten
72
+ .select do |patch|
73
+ next if min_version && Gem::Version.new(patch.version) <= min_version
74
+ next if filter && !filter.match?(patch.name)
75
+ true
76
+ end
77
+ end
78
+
79
+ def print_list(patches)
80
+ grouped_patches = patches.group_by(&:version).sort_by(&:first)
81
+ grouped_patches.each do |(group, patches)|
82
+ $stdout.puts "#{group} extensions:\n"
83
+ $stdout.puts patches.sort_by(&:name).map { |patch| " - #{patch.name}" }.join("\n")
84
+ $stdout.puts "\n"
85
+ end
86
+ end
87
+
88
+ def generate_core_ext(patches)
89
+ grouped_patches = patches.group_by(&:mod).sort_by { |(mod, patch)| mod.singleton_class? ? mod.inspect : mod.name }
90
+
91
+ buffer = []
92
+
93
+ buffer << "# frozen_string_literal: true\n"
94
+
95
+ buffer << generation_meta
96
+
97
+ grouped_patches.each do |mod, patches|
98
+ singleton = mod.singleton_class?
99
+ extend_name = singleton ? patches.first.singleton.name : mod.name
100
+ prepend_name = singleton ? "#{patches.first.singleton.name}.singleton_class" : mod.name
101
+
102
+ prepended, extended = patches.partition(&:prepend?)
103
+
104
+ prepended.map do |patch|
105
+ name = "RubyNext::Core::#{patch.name}"
106
+
107
+ buffer << <<-RUBY
108
+ module #{name}
109
+ #{indent_and_trim(patch.body)}
110
+ end
111
+ #{prepend_name}.prepend #{name}
112
+ RUBY
113
+
114
+ name
115
+ end
116
+
117
+ class_or_module = mod.is_a?(Class) ? "class" : "module"
118
+
119
+ buffer << "#{class_or_module} #{extend_name}"
120
+
121
+ buffer << " class << self" if singleton
122
+
123
+ indent_size = singleton ? 4 : 2
124
+
125
+ buffer << extended.map do |patch|
126
+ indent_and_trim(patch.body, indent_size)
127
+ end.join("\n\n")
128
+
129
+ buffer << " end" if singleton
130
+
131
+ buffer << "end\n"
132
+ end
133
+
134
+ contents = buffer.join("\n")
135
+
136
+ return $stdout.puts(contents) if out_path == "stdout"
137
+
138
+ unless CLI.dry_run?
139
+ FileUtils.mkdir_p File.dirname(out_path)
140
+ File.write(out_path, contents)
141
+ end
142
+
143
+ log "Generated: #{out_path}"
144
+ end
145
+
146
+ def generation_meta
147
+ <<-MSG
148
+ # Generated by Ruby Next v#{RubyNext::VERSION} using the following command:
149
+ #
150
+ # #{original_command}
151
+ #
152
+ MSG
153
+ end
154
+
155
+ def indent_and_trim(src, size = 2)
156
+ new_src = src.dup
157
+ # indent code using <size> spaces
158
+ new_src.gsub!(/^/, " " * size)
159
+ # remove empty lines
160
+ new_src.gsub!(/^\s+$/, "")
161
+ # remove traling blank lines
162
+ new_src.delete_suffix!("\n")
163
+ new_src
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,198 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "pathname"
5
+
6
+ require "ruby-next/language"
7
+
8
+ module RubyNext
9
+ module Commands
10
+ class Nextify < Base
11
+ using RubyNext
12
+
13
+ attr_reader :lib_path, :paths, :out_path, :min_version, :single_version, :specified_rewriters
14
+
15
+ def run
16
+ log "RubyNext core strategy: #{RubyNext::Core.strategy}"
17
+ log "RubyNext transpile mode: #{RubyNext::Language.mode}"
18
+
19
+ remove_rbnext!
20
+
21
+ @min_version ||= MIN_SUPPORTED_VERSION
22
+
23
+ paths.each do |path|
24
+ contents = File.read(path)
25
+ transpile path, contents
26
+ end
27
+ end
28
+
29
+ def parse!(args)
30
+ print_help = false
31
+ print_rewriters = false
32
+ rewriter_names = []
33
+ @single_version = false
34
+
35
+ optparser = base_parser do |opts|
36
+ opts.banner = "Usage: ruby-next nextify DIRECTORY_OR_FILE [options]"
37
+
38
+ opts.on("-o", "--output=OUTPUT", "Specify output directory or file or stdout") do |val|
39
+ @out_path = val
40
+ end
41
+
42
+ opts.on("--min-version=VERSION", "Specify the minimum Ruby version to support") do |val|
43
+ @min_version = Gem::Version.new(val)
44
+ end
45
+
46
+ opts.on("--single-version", "Only create one version of a file (for the earliest Ruby version)") do
47
+ @single_version = true
48
+ end
49
+
50
+ opts.on("--edge", "Enable edge (master) Ruby features") do |val|
51
+ require "ruby-next/language/edge"
52
+ end
53
+
54
+ opts.on("--proposed", "Enable proposed/experimental Ruby features") do |val|
55
+ require "ruby-next/language/proposed"
56
+ end
57
+
58
+ opts.on(
59
+ "--transpile-mode=MODE",
60
+ "Transpiler mode (ast or rewrite). Default: ast"
61
+ ) do |val|
62
+ Language.mode = val.to_sym
63
+ end
64
+
65
+ opts.on("--[no-]refine", "Do not inject `using RubyNext`") do |val|
66
+ Core.strategy = :core_ext unless val
67
+ end
68
+
69
+ opts.on("--list-rewriters", "List available rewriters") do |val|
70
+ print_rewriters = true
71
+ end
72
+
73
+ opts.on("--rewrite=REWRITERS...", "Specify particular Ruby features to rewrite") do |val|
74
+ rewriter_names << val
75
+ end
76
+
77
+ opts.on("-h", "--help", "Print help") do
78
+ print_help = true
79
+ end
80
+ end
81
+
82
+ optparser.parse!(args)
83
+
84
+ @lib_path = args[0]
85
+
86
+ if print_help
87
+ $stdout.puts optparser.help
88
+ exit 0
89
+ end
90
+
91
+ if print_rewriters
92
+ Language.rewriters.each do |rewriter|
93
+ $stdout.puts "#{rewriter::NAME} (\"#{rewriter::SYNTAX_PROBE}\")"
94
+ end
95
+ exit 0
96
+ end
97
+
98
+ unless ((!lib_path.nil?) || nil) && lib_path.then(&File.method(:exist?))
99
+ $stdout.puts "Path not found: #{lib_path}"
100
+ $stdout.puts optparser.help
101
+ exit 2
102
+ end
103
+
104
+ if rewriter_names.any? && min_version
105
+ $stdout.puts "--rewrite cannot be used with --min-version simultaneously"
106
+ exit 2
107
+ end
108
+
109
+ @specified_rewriters =
110
+ if rewriter_names.any?
111
+ begin
112
+ Language.select_rewriters(*rewriter_names)
113
+ rescue Language::RewriterNotFoundError => error
114
+ $stdout.puts error.message
115
+ $stdout.puts "Try --list-rewriters to see list of available rewriters"
116
+ exit 2
117
+ end
118
+ end
119
+
120
+ @paths =
121
+ if File.directory?(lib_path)
122
+ Dir[File.join(lib_path, "**/*.rb")]
123
+ elsif File.file?(lib_path)
124
+ [lib_path].tap do |_|
125
+ @lib_path = File.dirname(lib_path)
126
+ end
127
+ end
128
+ end
129
+
130
+ private
131
+
132
+ def transpile(path, contents, version: min_version)
133
+ rewriters = specified_rewriters || Language.rewriters.select { |rw| rw.unsupported_version?(version) }
134
+
135
+ context = Language::TransformContext.new
136
+
137
+ new_contents = Language.transform contents, context: context, rewriters: rewriters
138
+
139
+ return unless context.dirty?
140
+
141
+ versions = context.sorted_versions
142
+ version = versions.shift
143
+
144
+ # First, store already transpiled contents in the minimum required version dir
145
+ save new_contents, path, version
146
+
147
+ return if versions.empty? || single_version?
148
+
149
+ # Then, generate the source code for the next version
150
+ transpile path, contents, version: version
151
+ end
152
+
153
+ def save(contents, path, version)
154
+ return $stdout.puts(contents) if stdout?
155
+
156
+ paths = [Pathname.new(path).relative_path_from(Pathname.new(lib_path))]
157
+
158
+ paths.unshift(version.segments[0..1].join(".")) unless single_version?
159
+
160
+ next_path =
161
+ if next_dir_path.end_with?(".rb")
162
+ out_path
163
+ else
164
+ File.join(next_dir_path, *paths)
165
+ end
166
+
167
+ unless CLI.dry_run?
168
+ FileUtils.mkdir_p File.dirname(next_path)
169
+
170
+ File.write(next_path, contents)
171
+ end
172
+
173
+ log "Generated: #{next_path}"
174
+ end
175
+
176
+ def remove_rbnext!
177
+ return if CLI.dry_run? || stdout?
178
+
179
+ return unless File.directory?(next_dir_path)
180
+
181
+ log "Remove old files: #{next_dir_path}"
182
+ FileUtils.rm_r(next_dir_path)
183
+ end
184
+
185
+ def next_dir_path
186
+ @next_dir_path ||= (out_path || File.join(lib_path, RUBY_NEXT_DIR))
187
+ end
188
+
189
+ def stdout?
190
+ out_path == "stdout"
191
+ end
192
+
193
+ def single_version?
194
+ single_version || specified_rewriters
195
+ end
196
+ end
197
+ end
198
+ end