opal 1.7.0 → 1.7.2

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.
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