opal 1.7.0 → 1.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +12 -4
  3. data/CHANGELOG.md +39 -1
  4. data/UNRELEASED.md +1 -0
  5. data/docs/cdp_common.md +1 -1
  6. data/docs/compiler.md +1 -1
  7. data/docs/releasing.md +24 -8
  8. data/exe/opal +5 -7
  9. data/lib/opal/builder.rb +13 -13
  10. data/lib/opal/builder_processors.rb +8 -2
  11. data/lib/opal/builder_scheduler/prefork.rb +64 -9
  12. data/lib/opal/cli.rb +60 -43
  13. data/lib/opal/cli_runners/compiler.rb +7 -6
  14. data/lib/opal/cli_runners/safari.rb +208 -0
  15. data/lib/opal/cli_runners.rb +1 -0
  16. data/lib/opal/nodes/literal.rb +16 -0
  17. data/lib/opal/version.rb +1 -1
  18. data/opal/corelib/constants.rb +3 -3
  19. data/spec/filters/platform/.keep +0 -0
  20. data/spec/filters/platform/firefox/exception.rb +8 -0
  21. data/spec/filters/platform/firefox/kernel.rb +3 -0
  22. data/spec/filters/platform/safari/exception.rb +8 -0
  23. data/spec/filters/platform/safari/float.rb +4 -0
  24. data/spec/filters/platform/safari/kernel.rb +3 -0
  25. data/spec/filters/platform/safari/literal_regexp.rb +6 -0
  26. data/spec/lib/builder_spec.rb +32 -0
  27. data/spec/lib/cli_spec.rb +28 -6
  28. data/spec/lib/fixtures/build_order/file1.js +1 -0
  29. data/spec/lib/fixtures/build_order/file2.js +1 -0
  30. data/spec/lib/fixtures/build_order/file3.js +1 -0
  31. data/spec/lib/fixtures/build_order/file4.js +1 -0
  32. data/spec/lib/fixtures/build_order/file5.rb.erb +4 -0
  33. data/spec/lib/fixtures/build_order/file51.js +1 -0
  34. data/spec/lib/fixtures/build_order/file6.rb +10 -0
  35. data/spec/lib/fixtures/build_order/file61.rb +1 -0
  36. data/spec/lib/fixtures/build_order/file62.rb +4 -0
  37. data/spec/lib/fixtures/build_order/file63.rb +4 -0
  38. data/spec/lib/fixtures/build_order/file64.rb +1 -0
  39. data/spec/lib/fixtures/build_order/file7.rb +1 -0
  40. data/spec/lib/fixtures/build_order.rb +9 -0
  41. data/spec/lib/rake_dist_spec.rb +69 -0
  42. data/spec/lib/spec_helper.rb +2 -0
  43. data/spec/mspec-opal/runner.rb +1 -0
  44. data/spec/opal/core/io/read_spec.rb +12 -3
  45. data/stdlib/opal/platform.rb +1 -0
  46. data/stdlib/opal-platform.rb +3 -0
  47. data/stdlib/time.rb +21 -1
  48. data/tasks/building.rake +1 -1
  49. data/tasks/releasing.rake +46 -0
  50. data/tasks/testing.rake +3 -4
  51. metadata +30 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96d55712831ec604d204ffbd2a92a5478daf36f7c24b8b58f52412fc7b2e2ca5
4
- data.tar.gz: 557869c042954fc18a1d48cf93008485471e6e1db5a936b4834ffc9700fac755
3
+ metadata.gz: 3221ae5854f26b0d0722ddf2689773633648086a616add372e8fdd617b31d7c0
4
+ data.tar.gz: b6b4a65b1905bddb6c1fd2310b13c03600ef4bb8fb416fff54e5499bfae8616c
5
5
  SHA512:
6
- metadata.gz: 8747d6023e6e0e309f622c1f8ad879bd2c5ce2493662f2dc09053ab0cdbc268ad0110291a1b403c9d19272f6ceb2ebed6e15998b9673b6c5aff356898877202d
7
- data.tar.gz: 83f23a7651fd6804759f9e22e284c37eaa27aa4dce2db0ee2d9da91bd1533acf853a9b18767787af267201a0e481736f5e5694ca29d5259ebb1bc67772ec62bc
6
+ metadata.gz: 5a009bcca000c8fef3d73cda99453023b2860615e4c4c7176e328448cd12d5f26bbf6d2809ca040f064e64af7b04ddb94857b35ac36486a0c7996be28a63230b
7
+ data.tar.gz: d9154247ad9e90fbc770d647524db30fc8132b3fb77f8c5115abb67a3507fe66c0f7306a2965f1aca11bc790b2f6b7308d7db7bda7e9c3443b54bce42a957bab
@@ -24,12 +24,12 @@ jobs:
24
24
  - name: mspec-chrome
25
25
  ruby: '3.0'
26
26
  command: bin/rake mspec_chrome
27
- - name: minitest-firefox
27
+ - name: mspec-firefox
28
28
  env:
29
29
  # when changing version, also change it below
30
30
  MOZILLA_FIREFOX_BINARY: '/opt/hostedtoolcache/firefox/106.0.4/x64/firefox'
31
31
  ruby: '3.0'
32
- command: xvfb-run bin/rake minitest_firefox
32
+ command: xvfb-run bin/rake mspec_firefox
33
33
  - name: minitest
34
34
  ruby: '3.0'
35
35
  command: bin/rake minitest
@@ -59,13 +59,17 @@ jobs:
59
59
  command: bundle exec rake mspec_chrome
60
60
  ruby: '3.0'
61
61
  os: windows-latest
62
- - name: windows-minitest-firefox
62
+ - name: windows-mspec-firefox
63
63
  env:
64
64
  # when changing version, also change it below and above
65
65
  MOZILLA_FIREFOX_BINARY: 'C:/Program Files/Firefox_106.0.4/firefox.exe'
66
66
  ruby: '3.0'
67
- command: bundle exec rake minitest_firefox
67
+ command: bundle exec rake mspec_firefox
68
68
  os: windows-latest
69
+ - name: macos-mspec-safari
70
+ command: bundle exec rake mspec_safari
71
+ ruby: '3.0'
72
+ os: 'macos-latest'
69
73
  - name: windows-minitest
70
74
  command: bundle exec rake minitest
71
75
  ruby: '3.0'
@@ -74,6 +78,10 @@ jobs:
74
78
  command: bundle exec rake rspec
75
79
  ruby: '3.0'
76
80
  os: windows-latest
81
+ - name: macos
82
+ command: bundle exec rake rspec
83
+ ruby: '3.0'
84
+ os: 'macos-latest'
77
85
  - name: lint
78
86
  command: bin/rake lint
79
87
  ruby: '3.0'
data/CHANGELOG.md CHANGED
@@ -16,7 +16,7 @@ Changes are grouped as follows:
16
16
 
17
17
 
18
18
 
19
- ## [1.7.0](https://github.com/opal/opal/compare/v1.6.1...v1.7.0) - 2022-12-26
19
+ ## [1.7.2](https://github.com/opal/opal/compare/v1.7.1...v1.7.2) - 2023-01-20
20
20
 
21
21
 
22
22
  <!--
@@ -29,6 +29,44 @@ Changes are grouped as follows:
29
29
  ### Fixed
30
30
  -->
31
31
 
32
+ ### Fixed
33
+
34
+ - Fix the `--debug-source-map` CLI option ([#2520](https://github.com/opal/opal/pull/2520))
35
+ - Fix links in `docs/compiler.md` ([#2519](https://github.com/opal/opal/pull/2519))
36
+
37
+
38
+
39
+
40
+ ## [1.7.1](https://github.com/opal/opal/compare/v1.7.0...v1.7.1) - 2023-01-05
41
+
42
+
43
+ ### Added
44
+
45
+ - Add safari runner ([#2513](https://github.com/opal/opal/pull/2513))
46
+
47
+ ### Fixed
48
+
49
+ - Fix CLI file reading for macOS ([#2510](https://github.com/opal/opal/pull/2510))
50
+ - Make Date/Time.parse on Firefox more compatible with Chrome and Ruby ([#2506](https://github.com/opal/opal/pull/2506))
51
+ - Safari/WebKit can now parse code compiled with lookbehind regexps, failing at runtime instead ([#2511](https://github.com/opal/opal/pull/2511))
52
+ - Fix `--watch` ignoring some directories (e.g. `tmp`) ([#2509](https://github.com/opal/opal/pull/2509))
53
+ - Fix rake dist not generating libraries correctly for the CDN ([#2515](https://github.com/opal/opal/pull/2515))
54
+ - Prefork: output processed files in a correct, deterministic order ([#2516](https://github.com/opal/opal/pull/2516))
55
+ - Fix the handling of ARGV for the opal executable ([#2518](https://github.com/opal/opal/pull/2518))
56
+
57
+ ### Internal
58
+
59
+ - Platform specific spec filters ([#2508](https://github.com/opal/opal/pull/2508))
60
+ - Run Firefox specs by default ([#2507](https://github.com/opal/opal/pull/2507))
61
+ - Run Safari specs by default ([#2513](https://github.com/opal/opal/pull/2513))
62
+ - [mspec_opal] Avoid lookbehind Regexp for compatibility with various javascript engines ([#2512](https://github.com/opal/opal/pull/2512))
63
+
64
+
65
+
66
+
67
+ ## [1.7.0](https://github.com/opal/opal/compare/v1.6.1...v1.7.0) - 2022-12-26
68
+
69
+
32
70
  ### Added
33
71
 
34
72
  - Update benchmarking and CLI runners, added support for Deno and Firefox (#2490, #2492, #2494, #2495, #2497, #2491, #2496)
data/UNRELEASED.md CHANGED
@@ -7,3 +7,4 @@
7
7
  ### Performance
8
8
  ### Fixed
9
9
  -->
10
+
data/docs/cdp_common.md CHANGED
@@ -1,4 +1,4 @@
1
- ### Common CDP of Chrome, Firefox, Node and Deno
1
+ # Common CDP of Chrome, Firefox, Node and Deno
2
2
 
3
3
  cdp_common.json documents a subset of the common CDP as reported by Chrome, version 107 and Firefox, version 106.
4
4
  All entries that where marked as "experimental" or "deprecated" have been removed.
data/docs/compiler.md CHANGED
@@ -27,7 +27,7 @@ code as well as a reference back to the original sexp which is useful for
27
27
  generating source maps afterwards.
28
28
 
29
29
 
30
- [sexps]: https://github.com/opal/opal/tree/master/lib/opal/parser/sexp.rb
30
+ [parser]: https://github.com/opal/opal/tree/master/lib/opal/parser.rb
31
31
  [compiler]: https://github.com/opal/opal/tree/master/lib/opal/compiler.rb
32
32
  [fragments]: https://github.com/opal/opal/tree/master/lib/opal/fragment.rb
33
33
  [base-node]: https://github.com/opal/opal/tree/master/lib/opal/nodes/base.rb
data/docs/releasing.md CHANGED
@@ -2,40 +2,56 @@
2
2
 
3
3
  _This guide is a work-in-progress._
4
4
 
5
- ## Updating the version
5
+ ---
6
+
7
+ ## Before the release
8
+
9
+ All of the following is now covered by `bin/rake release:prepare VERSION=v1.2.3`
10
+
11
+ ### Updating the version
6
12
 
7
13
  - Update `lib/opal/version.rb`
8
14
  - Update `opal/corelib/constants.rb` with the same version number along with release dates
9
15
 
10
- ## Updating the changelog
16
+ ### Updating the changelog
11
17
 
12
18
  - Ensure all the unreleased changes are documented in UNRELEASED.md
13
19
  - [skip for pre-releases] Run `bin/rake changelog VERSION=v1.2.3` specifying the version number you're about to release
14
20
  - [skip for pre-releases] Empty UNRELEASED.md
15
21
 
16
- ## The commit
22
+ ### The commit
17
23
 
18
24
  - Commit the updated changelog along with the version bump using this commit message:
19
25
  "Release v1.2.3"
20
- - Push the commit and run `bin/rake release` to release the new version to Rubygems
26
+
27
+ ---
28
+
29
+ ## Release!
30
+
31
+ - Push the commit to `master`
32
+ - Run `bin/rake release` to release the new version to Rubygems
21
33
  - Go to GitHub releases and create a new release from the latest tag pasting the contents from CHANGELOG.md (or UNRELEASED.md for pre-releases)
22
34
 
23
- ## Opal docs
35
+ ---
36
+
37
+ ## After the release
38
+
39
+ ### Opal docs
24
40
 
25
41
  - Open `opal-docs` and run `bin/build v1.2.3`
26
42
  - Then run `bin/deploy`
27
43
 
28
- ## [skip for pre-releases] Opal site
44
+ ### [skip for pre-releases] Opal site
29
45
 
30
46
  - Open `opal.github.io` and update the opal version in the `Gemfile`
31
47
  - run `bin/build`
32
48
  - `git push` the latest changes
33
49
 
34
- ## Opal CDN
50
+ ### Opal CDN
35
51
 
36
52
  - Run `bin/release v1.2.3`
37
53
 
38
- ## [skip for minor-releases] Prepare for the next release
54
+ ### [skip for minor-releases] Prepare for the next release
39
55
 
40
56
  - Create a new pull request that:
41
57
  - Updates a version to `v1.x.0.dev` in both `lib/opal/version.rb` and `opal/corelib/constants.rb`
data/exe/opal CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  # Error codes are taken from /usr/include/sysexits.h
4
4
 
5
- OriginalARGV = ARGV.dup
6
-
7
5
  require 'opal/cli_options'
8
6
  options = Opal::CLIOptions.new
9
7
  begin
@@ -15,11 +13,7 @@ end
15
13
 
16
14
  require 'opal/cli'
17
15
  options_hash = options.options
18
- options_hash.merge!(
19
- file: ARGF.file,
20
- filename: ARGF.filename,
21
- argv: ARGV.dup
22
- ) unless options_hash[:lib_only]
16
+ options_hash.merge!(argv: ARGV.dup) unless options_hash[:lib_only]
23
17
  cli = Opal::CLI.new options_hash
24
18
 
25
19
  begin
@@ -28,4 +22,8 @@ begin
28
22
  rescue Opal::CliRunners::RunnerError => e
29
23
  $stderr.puts e.message
30
24
  exit 72
25
+ rescue SignalException => e
26
+ raise unless e.message == 'SIGUSR2'
27
+
28
+ exec($0, *ARGV)
31
29
  end
data/lib/opal/builder.rb CHANGED
@@ -197,6 +197,19 @@ module Opal
197
197
  processed.map(&:abs_path).compact.select { |fn| File.exist?(fn) }
198
198
  end
199
199
 
200
+ def expand_ext(path)
201
+ abs_path = path_reader.expand(path)
202
+
203
+ if abs_path
204
+ File.join(
205
+ File.dirname(path),
206
+ File.basename(abs_path)
207
+ )
208
+ else
209
+ path
210
+ end
211
+ end
212
+
200
213
  private
201
214
 
202
215
  def process_requires(rel_path, requires, autoloads, options)
@@ -261,19 +274,6 @@ module Opal
261
274
  end
262
275
  end
263
276
 
264
- def expand_ext(path)
265
- abs_path = path_reader.expand(path)
266
-
267
- if abs_path
268
- File.join(
269
- File.dirname(path),
270
- File.basename(abs_path)
271
- )
272
- else
273
- path
274
- end
275
- end
276
-
277
277
  def expand_path(path)
278
278
  return if stub?(path)
279
279
  (path_reader.expand(path) || File.expand_path(path)).to_s
@@ -148,7 +148,10 @@ module Opal
148
148
 
149
149
  def compiled
150
150
  @compiled ||= begin
151
- @source = ::ERB.new(@source.to_s).result
151
+ erb = ::ERB.new(@source.to_s)
152
+ erb.filename = @abs_path
153
+
154
+ @source = erb.result
152
155
 
153
156
  compiler = compiler_for(@source, file: @filename)
154
157
  compiler.compile
@@ -163,7 +166,10 @@ module Opal
163
166
  handles :erb
164
167
 
165
168
  def source
166
- result = ::ERB.new(@source.to_s).result
169
+ erb = ::ERB.new(@source.to_s)
170
+ erb.filename = @abs_path
171
+
172
+ result = erb.result
167
173
  module_name = ::Opal::Compiler.module_name(@filename)
168
174
  "Opal.modules[#{module_name.inspect}] = function() {#{result}};"
169
175
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'etc'
4
+ require 'set'
4
5
 
5
6
  module Opal
6
7
  class BuilderScheduler
@@ -13,12 +14,64 @@ module Opal
13
14
  io = @in_fork
14
15
  io.send(:new_requires, rel_path, requires, autoloads, options)
15
16
  else
16
- prefork_reactor(rel_path, requires, autoloads, options)
17
+ processed = prefork_reactor(rel_path, requires, autoloads, options)
18
+
19
+ processed = OrderCorrector.correct_order(processed, requires, builder)
20
+
21
+ builder.processed.append(*processed)
17
22
  end
18
23
  end
19
24
 
20
25
  private
21
26
 
27
+ # Prefork is not deterministic. This module corrects an order of processed
28
+ # files so that it would be exactly the same as if building sequentially.
29
+ # While for Ruby files it usually isn't a problem, because the real order
30
+ # stems from how `Opal.modules` array is accessed, the JavaScript files
31
+ # are executed verbatim and their order may be important. Also, having
32
+ # deterministic output is always a good thing.
33
+ module OrderCorrector
34
+ module_function
35
+
36
+ def correct_order(processed, requires, builder)
37
+ # Let's build a hash that maps a filename to an array of files it requires
38
+ requires_hash = processed.to_h do |i|
39
+ [i.filename, expand_requires(i.requires, builder)]
40
+ end
41
+ # Let's build an array with a correct order of requires
42
+ order_array = build_require_order_array(expand_requires(requires, builder), requires_hash)
43
+ # If a key is duplicated, remove the last duplicate
44
+ order_array = order_array.uniq
45
+ # Create a hash from this array: [a,b,c] => [a => 0, b => 1, c => 2]
46
+ order_hash = order_array.each_with_index.to_h
47
+ # Let's return a processed array that has elements in the order provided
48
+ processed.sort_by do |asset|
49
+ # If a filename isn't present somehow in our hash, let's put it at the end
50
+ order_hash[asset.filename] || order_array.length
51
+ end
52
+ end
53
+
54
+ # Expand a requires array, so that the requires filenames will be
55
+ # matching BuilderProcessor#. Builder needs to be passed so that
56
+ # we can access an `expand_ext` function from its context.
57
+ def expand_requires(requires, builder)
58
+ requires.map { |i| builder.expand_ext(i) }
59
+ end
60
+
61
+ def build_require_order_array(requires, requires_hash, built_for = Set.new)
62
+ array = []
63
+ requires.each do |name|
64
+ next if built_for.include?(name)
65
+ built_for << name
66
+
67
+ asset_requires = requires_hash[name]
68
+ array += build_require_order_array(asset_requires, requires_hash, built_for) if asset_requires
69
+ array << name
70
+ end
71
+ array
72
+ end
73
+ end
74
+
22
75
  class ForkSet < Array
23
76
  def initialize(count, &block)
24
77
  super([])
@@ -192,13 +245,16 @@ module Opal
192
245
  def prefork_reactor(rel_path, requires, autoloads, options)
193
246
  prefork
194
247
 
248
+ processed = []
249
+
195
250
  first = rel_path
196
251
  queue = requires.map { |i| [rel_path, i, autoloads, options] }
197
252
 
198
253
  awaiting = 0
199
254
  built = 0
255
+ should_log = $stderr.tty? && !ENV['OPAL_DISABLE_PREFORK_LOGS']
200
256
 
201
- $stderr.print "\r\e[K" if $stderr.tty?
257
+ $stderr.print "\r\e[K" if should_log
202
258
 
203
259
  loop do
204
260
  events, idles = @forks.get_events(queue.length)
@@ -206,7 +262,7 @@ module Opal
206
262
  idles.each do |io|
207
263
  break if queue.empty?
208
264
 
209
- rel_path, req, autoloads, options = *queue.pop
265
+ rel_path, req, autoloads, options = *queue.shift
210
266
 
211
267
  next if builder.already_processed.include?(req)
212
268
  awaiting += 1
@@ -225,11 +281,8 @@ module Opal
225
281
  asset, = *args
226
282
  if !asset
227
283
  # Do nothing, we received a nil which is expected.
228
- elsif asset.filename == 'corelib/runtime.js'
229
- # Opal runtime should go first... the rest can go their way.
230
- builder.processed.unshift(asset)
231
284
  else
232
- builder.processed << asset
285
+ processed << asset
233
286
  end
234
287
  built += 1
235
288
  awaiting -= 1
@@ -244,7 +297,7 @@ module Opal
244
297
  end
245
298
  end
246
299
 
247
- if $stderr.tty?
300
+ if should_log
248
301
  percent = (100.0 * built / (awaiting + built)).round(1)
249
302
  str = format("[opal/builder] Building %<first>s... (%<percent>4.3g%%)\r", first: first, percent: percent)
250
303
  $stderr.print str
@@ -252,8 +305,10 @@ module Opal
252
305
 
253
306
  break if awaiting == 0 && queue.empty?
254
307
  end
308
+
309
+ processed
255
310
  ensure
256
- $stderr.print "\r\e[K\r" if $stderr.tty?
311
+ $stderr.print "\r\e[K\r" if should_log
257
312
  @forks.close
258
313
  @forks.wait
259
314
  end
data/lib/opal/cli.rb CHANGED
@@ -3,18 +3,25 @@
3
3
  require 'opal/requires'
4
4
  require 'opal/builder'
5
5
  require 'opal/cli_runners'
6
+ require 'stringio'
6
7
 
7
8
  module Opal
8
9
  class CLI
9
10
  attr_reader :options, :file, :compiler_options, :evals, :load_paths, :argv,
10
11
  :output, :requires, :rbrequires, :gems, :stubs, :verbose, :runner_options,
11
- :preload, :filename, :debug, :no_exit, :lib_only, :missing_require_severity,
12
- :no_cache
12
+ :preload, :debug, :no_exit, :lib_only, :missing_require_severity,
13
+ :filename, :stdin, :no_cache
13
14
 
14
15
  class << self
15
16
  attr_accessor :stdout
16
17
  end
17
18
 
19
+ class Evals < StringIO
20
+ def to_path
21
+ '-e'
22
+ end
23
+ end
24
+
18
25
  def initialize(options = nil)
19
26
  options ||= {}
20
27
 
@@ -25,7 +32,6 @@ module Opal
25
32
  @options = options
26
33
  @sexp = options.delete(:sexp)
27
34
  @repl = options.delete(:repl)
28
- @file = options.delete(:file)
29
35
  @no_exit = options.delete(:no_exit)
30
36
  @lib_only = options.delete(:lib_only)
31
37
  @argv = options.delete(:argv) { [] }
@@ -37,10 +43,10 @@ module Opal
37
43
  @output = options.delete(:output) { self.class.stdout || $stdout }
38
44
  @verbose = options.delete(:verbose) { false }
39
45
  @debug = options.delete(:debug) { false }
40
- @filename = options.delete(:filename) { @file && @file.path }
41
46
  @requires = options.delete(:requires) { [] }
42
47
  @rbrequires = options.delete(:rbrequires) { [] }
43
48
  @no_cache = options.delete(:no_cache) { false }
49
+ @stdin = options.delete(:stdin) { $stdin }
44
50
 
45
51
  @debug_source_map = options.delete(:debug_source_map) { false }
46
52
 
@@ -55,9 +61,20 @@ module Opal
55
61
  [key, value]
56
62
  end.compact.to_h
57
63
 
58
- raise ArgumentError, 'no libraries to compile' if @lib_only && @requires.empty?
59
- raise ArgumentError, 'no runnable code provided (evals or file)' if @evals.empty? && @file.nil? && !@lib_only
60
- raise ArgumentError, "can't accept evals or file in `library only` mode" if (@evals.any? || @file) && @lib_only
64
+ if @lib_only
65
+ raise ArgumentError, 'no libraries to compile' if @requires.empty?
66
+ raise ArgumentError, "can't accept evals, file, or extra arguments in `library only` mode" if @argv.any? || @evals.any?
67
+ elsif @evals.any?
68
+ @filename = '-e'
69
+ @file = Evals.new(@evals.join("\n"))
70
+ elsif @argv.first && @argv.first != '-'
71
+ @filename = @argv.shift
72
+ @file = File.open(@filename)
73
+ else
74
+ @filename = @argv.shift || '-'
75
+ @file = @stdin
76
+ end
77
+
61
78
  raise ArgumentError, "unknown options: #{options.inspect}" unless @options.empty?
62
79
  end
63
80
 
@@ -93,7 +110,7 @@ module Opal
93
110
  require 'opal/repl'
94
111
 
95
112
  repl = REPL.new
96
- repl.run(OriginalARGV)
113
+ repl.run(argv)
97
114
  end
98
115
 
99
116
  attr_reader :exit_status
@@ -127,7 +144,8 @@ module Opal
127
144
  builder.build_str '$DEBUG = true', '(flags)', no_export: true if debug
128
145
 
129
146
  # --eval / stdin / file
130
- evals_or_file { |source, filename| builder.build_str(source, filename) }
147
+ source = evals_or_file_source
148
+ builder.build_str(source, filename) if source
131
149
 
132
150
  # --no-exit
133
151
  builder.build_str '::Kernel.exit', '(exit)', no_export: true unless no_exit
@@ -136,27 +154,28 @@ module Opal
136
154
  end
137
155
 
138
156
  def show_sexp
139
- evals_or_file do |contents, filename|
140
- buffer = ::Opal::Parser::SourceBuffer.new(filename)
141
- buffer.source = contents
142
- sexp = Opal::Parser.default_parser.parse(buffer)
143
- output.puts sexp.inspect
144
- end
157
+ source = evals_or_file_source or return # rubocop:disable Style/AndOr
158
+
159
+ buffer = ::Opal::Parser::SourceBuffer.new(filename)
160
+ buffer.source = source
161
+ sexp = Opal::Parser.default_parser.parse(buffer)
162
+ output.puts sexp.inspect
145
163
  end
146
164
 
147
165
  def debug_source_map
148
- evals_or_file do |contents, filename|
149
- compiler = Opal::Compiler.new(contents, file: filename, **compiler_options)
166
+ source = evals_or_file_source or return # rubocop:disable Style/AndOr
150
167
 
151
- compiler.compile
168
+ compiler = Opal::Compiler.new(source, file: filename, **compiler_options)
152
169
 
153
- result = compiler.result
154
- source_map = compiler.source_map.to_json
170
+ compiler.compile
155
171
 
156
- b64 = [result, source_map, contents].map { |i| Base64.strict_encode64(i) }.join(',')
172
+ b64 = [
173
+ compiler.result,
174
+ compiler.source_map.to_json,
175
+ evals_or_file_source,
176
+ ].map { |i| Base64.strict_encode64(i) }.join(',')
157
177
 
158
- output.puts "https://sokra.github.io/source-map-visualization/#base64,#{b64}"
159
- end
178
+ output.puts "https://sokra.github.io/source-map-visualization/#base64,#{b64}"
160
179
  end
161
180
 
162
181
  def compiler_option_names
@@ -177,29 +196,27 @@ module Opal
177
196
 
178
197
  # Internal: Yields a string of source code and the proper filename for either
179
198
  # evals, stdin or a filepath.
180
- def evals_or_file
181
- # --library
182
- return if lib_only
183
-
184
- if evals.any?
185
- yield evals.join("\n"), '-e'
186
- elsif file && (filename != '-' || evals.empty?)
187
- return @content if @content
188
-
189
- if file.tty?
190
- save = true
191
- else
192
- begin
193
- file.rewind
194
- rescue Errno::ESPIPE
195
- save = true
196
- end
199
+ def evals_or_file_source
200
+ return if lib_only # --library
201
+ return @cached_content if @cached_content
202
+
203
+ unless file.tty?
204
+ begin
205
+ file.rewind
206
+ can_read_again = true
207
+ rescue Errno::ESPIPE # rubocop:disable Lint/HandleExceptions
208
+ # noop
197
209
  end
210
+ end
198
211
 
199
- content = yield(file.read, filename)
200
- @content = content if save
201
- content
212
+ if @cached_content.nil? || can_read_again
213
+ # On MacOS file.read is not enough to pick up changes, probably due to some
214
+ # cache or buffer, unclear if coming from ruby or the OS.
215
+ content = File.file?(file) ? File.read(file) : file.read
202
216
  end
217
+
218
+ @cached_content ||= content unless can_read_again
219
+ content
203
220
  end
204
221
  end
205
222
  end
@@ -52,22 +52,20 @@ class Opal::CliRunners::Compiler
52
52
  end
53
53
 
54
54
  def fail_unrewindable!
55
- warn <<~ERROR
55
+ abort <<~ERROR
56
56
  You have specified --watch, but for watch to work, you must specify an
57
57
  --output file.
58
58
  ERROR
59
- exit 1
60
59
  end
61
60
 
62
61
  def fail_no_listen!
63
- warn <<~ERROR
62
+ abort <<~ERROR
64
63
  --watch mode requires the `listen` gem present. Please try to run:
65
64
 
66
65
  gem install listen
67
66
 
68
67
  Or if you are using bundler, add listen to your Gemfile.
69
68
  ERROR
70
- exit 1
71
69
  end
72
70
 
73
71
  def watch_compile
@@ -88,10 +86,13 @@ class Opal::CliRunners::Compiler
88
86
  $stderr.puts "* Opal v#{Opal::VERSION} successfully compiled your program in --watch mode"
89
87
 
90
88
  sleep
89
+ rescue Interrupt
90
+ $stderr.puts '* Stopping watcher...'
91
+ @code_listener.stop
91
92
  end
92
93
 
93
94
  def reexec
94
- system($0, *OriginalARGV) && exit
95
+ Process.kill('USR2', Process.pid)
95
96
  end
96
97
 
97
98
  def on_code_change(modified)
@@ -133,7 +134,7 @@ class Opal::CliRunners::Compiler
133
134
  def watch_files
134
135
  @directories = files_to_directories
135
136
 
136
- Listen.to(*@directories) do |modified, added, removed|
137
+ Listen.to(*@directories, ignore!: []) do |modified, added, removed|
137
138
  our_modified = @files & (modified + added + removed)
138
139
  on_code_change(our_modified) unless our_modified.empty?
139
140
  end