ruby-next-core 0.9.1 → 0.10.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +42 -0
- data/README.md +20 -6
- data/lib/.rbnext/2.3/ruby-next/commands/core_ext.rb +167 -0
- data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +201 -0
- data/lib/.rbnext/2.3/ruby-next/language/eval.rb +66 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +121 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/endless_range.rb +63 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/pattern_matching.rb +944 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/right_hand_assignment.rb +107 -0
- data/lib/.rbnext/2.3/ruby-next/utils.rb +65 -0
- data/lib/ruby-next.rb +8 -6
- data/lib/ruby-next/cli.rb +2 -2
- data/lib/ruby-next/commands/core_ext.rb +1 -1
- data/lib/ruby-next/commands/nextify.rb +44 -10
- data/lib/ruby-next/core.rb +27 -21
- data/lib/ruby-next/core/array/deconstruct.rb +9 -9
- data/lib/ruby-next/core/array/difference_union_intersection.rb +12 -12
- data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +3 -3
- data/lib/ruby-next/core/enumerable/filter.rb +8 -8
- data/lib/ruby-next/core/enumerable/filter_map.rb +25 -25
- data/lib/ruby-next/core/enumerable/tally.rb +7 -7
- data/lib/ruby-next/core/enumerator/produce.rb +12 -12
- data/lib/ruby-next/core/hash/deconstruct_keys.rb +9 -9
- data/lib/ruby-next/core/hash/except.rb +11 -0
- data/lib/ruby-next/core/hash/merge.rb +8 -8
- data/lib/ruby-next/core/kernel/then.rb +2 -2
- data/lib/ruby-next/core/proc/compose.rb +11 -11
- data/lib/ruby-next/core/string/split.rb +6 -6
- data/lib/ruby-next/core/struct/deconstruct.rb +2 -2
- data/lib/ruby-next/core/struct/deconstruct_keys.rb +17 -17
- data/lib/ruby-next/core/symbol/end_with.rb +4 -4
- data/lib/ruby-next/core/symbol/start_with.rb +4 -4
- data/lib/ruby-next/core/time/ceil.rb +6 -6
- data/lib/ruby-next/core/time/floor.rb +4 -4
- data/lib/ruby-next/core/unboundmethod/bind_call.rb +4 -4
- data/lib/ruby-next/core_ext.rb +1 -1
- data/lib/ruby-next/language.rb +30 -6
- data/lib/ruby-next/language/proposed.rb +3 -0
- data/lib/ruby-next/language/rewriters/args_forward.rb +24 -20
- data/lib/ruby-next/language/rewriters/base.rb +1 -1
- data/lib/ruby-next/language/rewriters/endless_method.rb +26 -3
- data/lib/ruby-next/language/rewriters/endless_range.rb +1 -0
- data/lib/ruby-next/language/rewriters/find_pattern.rb +44 -0
- data/lib/ruby-next/language/rewriters/method_reference.rb +2 -1
- data/lib/ruby-next/language/rewriters/numbered_params.rb +1 -0
- data/lib/ruby-next/language/rewriters/pattern_matching.rb +103 -12
- data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +74 -11
- data/lib/ruby-next/language/rewriters/safe_navigation.rb +87 -0
- data/lib/ruby-next/language/rewriters/shorthand_hash.rb +47 -0
- data/lib/ruby-next/language/rewriters/squiggly_heredoc.rb +36 -0
- data/lib/ruby-next/logging.rb +1 -1
- data/lib/ruby-next/rubocop.rb +91 -9
- data/lib/ruby-next/setup_self.rb +22 -0
- data/lib/ruby-next/version.rb +1 -1
- data/lib/uby-next.rb +8 -4
- metadata +21 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06e8a3264f44b75ea1c9c15b86857a99b20e537e34c1345a3cdb585ecc9ff815
|
4
|
+
data.tar.gz: 4a9fac897b70b243b4f465e3cb4b4d7f7e6838f9d119cb44befbac809ae57f66
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 383cfb7155da80a785f52996d470ed20e47f5e936973eddab95487fa1be952d829ccefc7605fd2e90967ab0bece5e8da678b6ba11be62534377762de9a247cc7
|
7
|
+
data.tar.gz: 05e872e67ec2a44076ab20cc7ca3592e010e5bb0ec0fb8249e1b6cfb46119049ad376ba7fb189c21a531b02ef12b98f9fc70f8c4467df9451e8c05f8cda80dc0
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,47 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.10.3 (2020-09-28)
|
6
|
+
|
7
|
+
- Update RuboCop integration to handle the latest Parser changes. ([@palkan][])
|
8
|
+
|
9
|
+
Parser 2.7.1.5 unified endless and normal methods and rightward and leftward assignments, thus, making some cops report false negatives.
|
10
|
+
|
11
|
+
## 0.10.2 (2020-09-09)
|
12
|
+
|
13
|
+
- Fix regression when `nextify` produces incorrect files for 2.7. ([@palkan][])
|
14
|
+
|
15
|
+
## ~~0.10.1~~
|
16
|
+
|
17
|
+
## 0.10.0 (2020-09-02)
|
18
|
+
|
19
|
+
- Add proposed shorthand Hash syntax. ([@palkan][])
|
20
|
+
|
21
|
+
You can try it: `x = 1; y = 2; data = {x, y}`.
|
22
|
+
|
23
|
+
- Add leading argument support to args forwarding. ([@palkan][])
|
24
|
+
|
25
|
+
`def a(...) b(1, ...); end`.
|
26
|
+
|
27
|
+
- Add `Hash#except`. ([@palkan][])
|
28
|
+
|
29
|
+
`{a: 1, b: 2}.except(:a) == {b: 2}`
|
30
|
+
|
31
|
+
- Add find pattern support. ([@palkan][])
|
32
|
+
|
33
|
+
Now you can do: `[0, 1, 2] in [*, 1 => a, *c]`.
|
34
|
+
|
35
|
+
- Add Ruby 2.2 support. ([@palkan][])
|
36
|
+
|
37
|
+
With support for safe navigation operator (`&.`) and squiggly heredocs (`<<~TXT`).
|
38
|
+
|
39
|
+
## 0.9.2 (2020-06-24)
|
40
|
+
|
41
|
+
- Support passing rewriters to CLI. ([@sl4vr][])
|
42
|
+
|
43
|
+
Use `nextify --list-rewriters` to view all available rewriters.
|
44
|
+
Use `nextify` with `--rewrite=REWRITERS...` option to specify which particular rewriters to use.
|
45
|
+
|
5
46
|
## 0.9.1 (2020-06-05)
|
6
47
|
|
7
48
|
- Keep `ruby-next` version in sync with `ruby-next-core`. ([@palkan][])
|
@@ -195,3 +236,4 @@ p a #=> 1
|
|
195
236
|
|
196
237
|
[@palkan]: https://github.com/palkan
|
197
238
|
[backports]: https://github.com/marcandre/backports
|
239
|
+
[@sl4vr]: https://github.com/sl4vr
|
data/README.md
CHANGED
@@ -1,3 +1,4 @@
|
|
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
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)
|
3
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)
|
@@ -36,6 +37,8 @@ Read more about the motivation behind the Ruby Next in this post: [Ruby Next: Ma
|
|
36
37
|
- Ruby gems
|
37
38
|
- [anyway_config](https://github.com/palkan/anyway_config)
|
38
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)
|
39
42
|
- mruby
|
40
43
|
- [ACLI](https://github.com/palkan/acli)
|
41
44
|
|
@@ -67,7 +70,7 @@ Core provides **polyfills** for Ruby core classes APIs via Refinements (default
|
|
67
70
|
Language is responsible for **transpiling** edge Ruby syntax into older versions. It could be done
|
68
71
|
programmatically or via CLI. It also could be done in runtime.
|
69
72
|
|
70
|
-
Currently, Ruby Next supports Ruby versions 2.
|
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)).
|
71
74
|
|
72
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.
|
73
76
|
|
@@ -138,7 +141,7 @@ The following _rule of thumb_ is recommended when choosing between refinements a
|
|
138
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 🙂)
|
139
142
|
- Use core extensions if refinements are not supported by your platform
|
140
143
|
|
141
|
-
**NOTE:**
|
144
|
+
**NOTE:** _Edge_ APIs (i.e., from the Ruby's master branch) are included by default.
|
142
145
|
|
143
146
|
[**The list of supported APIs.**][features_core]
|
144
147
|
|
@@ -200,12 +203,15 @@ It has the following interface:
|
|
200
203
|
```sh
|
201
204
|
$ ruby-next nextify
|
202
205
|
Usage: ruby-next nextify DIRECTORY_OR_FILE [options]
|
203
|
-
-o, --output=OUTPUT Specify output directory or file or stdout
|
206
|
+
-o, --output=OUTPUT Specify output directory or file or stdout
|
204
207
|
--min-version=VERSION Specify the minimum Ruby version to support
|
205
208
|
--single-version Only create one version of a file (for the earliest Ruby version)
|
206
|
-
--
|
209
|
+
--edge Enable edge (master) Ruby features
|
210
|
+
--proposed Enable proposed/experimental Ruby features
|
207
211
|
--transpile-mode=MODE Transpiler mode (ast or rewrite). Default: ast
|
208
212
|
--[no-]refine Do not inject `using RubyNext`
|
213
|
+
--list-rewriters List available rewriters
|
214
|
+
--rewrite=REWRITERS... Specify particular Ruby features to rewrite
|
209
215
|
-h, --help Print help
|
210
216
|
-V Turn on verbose mode
|
211
217
|
--dry-run Print verbose output without generating files
|
@@ -423,6 +429,8 @@ You must set `TargetRubyVersion: next` to make RuboCop use a Ruby Next parser.
|
|
423
429
|
|
424
430
|
Alternatively, you can load the patch from the command line by running: `rubocop -r ruby-next/rubocop ...`.
|
425
431
|
|
432
|
+
We recommend using the latest RuboCop version, 'cause it has support for new nodes built-in.
|
433
|
+
|
426
434
|
Also, when pre-transpiling source code with `ruby-next nextify`, we suggest ignoring the transpiled files:
|
427
435
|
|
428
436
|
```yml
|
@@ -435,7 +443,7 @@ AllCops:
|
|
435
443
|
|
436
444
|
## Using with EOL Rubies
|
437
445
|
|
438
|
-
We currently provide support for Ruby 2.3 and 2.4.
|
446
|
+
We currently provide support for Ruby 2.2, 2.3 and 2.4.
|
439
447
|
|
440
448
|
Ruby Next itself relies on 2.5 features and contains polyfills only for version 2.5+ (and that won't change).
|
441
449
|
Thus, to make it work with <2.5 we need to backport some APIs ourselves.
|
@@ -465,6 +473,8 @@ RUBY_NEXT_CORE_STRATEGY=backports ruby-next nextify lib/
|
|
465
473
|
|
466
474
|
**NOTE:** Make sure you have `backports` gem installed globally or added to your bundle (if you're using `bundle exec ruby-next ...`).
|
467
475
|
|
476
|
+
**NOTE:** For Ruby 2.2, safe navigation operator (`&.`) and squiggly heredocs (`<<~TXT`) support is provided.
|
477
|
+
|
468
478
|
## Proposed and edge features
|
469
479
|
|
470
480
|
Ruby Next aims to bring edge and proposed features to Ruby community before they (hopefully) reach an official Ruby release.
|
@@ -498,12 +508,16 @@ require "ruby-next/language/runtime"
|
|
498
508
|
|
499
509
|
- "Endless" method definition (`def foo() = 42`) ([#16746](https://bugs.ruby-lang.org/issues/16746)).
|
500
510
|
|
501
|
-
- 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)).
|
502
514
|
|
503
515
|
### Supported proposed features
|
504
516
|
|
505
517
|
- _Method reference_ operator (`.:`) ([#13581](https://bugs.ruby-lang.org/issues/13581)).
|
506
518
|
|
519
|
+
- Shorthand Hash notation (`data = {x, y}`) ([#15236](https://bugs.ruby-lang.org/issues/15236)).
|
520
|
+
|
507
521
|
## Contributing
|
508
522
|
|
509
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).
|
@@ -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,201 @@
|
|
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
|
+
rescue SyntaxError, StandardError => e
|
152
|
+
warn "Failed to transpile #{path}: #{e.class} — #{e.message}"
|
153
|
+
exit 1
|
154
|
+
end
|
155
|
+
|
156
|
+
def save(contents, path, version)
|
157
|
+
return $stdout.puts(contents) if stdout?
|
158
|
+
|
159
|
+
paths = [Pathname.new(path).relative_path_from(Pathname.new(lib_path))]
|
160
|
+
|
161
|
+
paths.unshift(version.segments[0..1].join(".")) unless single_version?
|
162
|
+
|
163
|
+
next_path =
|
164
|
+
if next_dir_path.end_with?(".rb")
|
165
|
+
out_path
|
166
|
+
else
|
167
|
+
File.join(next_dir_path, *paths)
|
168
|
+
end
|
169
|
+
|
170
|
+
unless CLI.dry_run?
|
171
|
+
FileUtils.mkdir_p File.dirname(next_path)
|
172
|
+
|
173
|
+
File.write(next_path, contents)
|
174
|
+
end
|
175
|
+
|
176
|
+
log "Generated: #{next_path}"
|
177
|
+
end
|
178
|
+
|
179
|
+
def remove_rbnext!
|
180
|
+
return if CLI.dry_run? || stdout?
|
181
|
+
|
182
|
+
return unless File.directory?(next_dir_path)
|
183
|
+
|
184
|
+
log "Remove old files: #{next_dir_path}"
|
185
|
+
FileUtils.rm_r(next_dir_path)
|
186
|
+
end
|
187
|
+
|
188
|
+
def next_dir_path
|
189
|
+
@next_dir_path ||= (out_path || File.join(lib_path, RUBY_NEXT_DIR))
|
190
|
+
end
|
191
|
+
|
192
|
+
def stdout?
|
193
|
+
out_path == "stdout"
|
194
|
+
end
|
195
|
+
|
196
|
+
def single_version?
|
197
|
+
single_version || specified_rewriters
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|