opal 1.7.0 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +12 -4
- data/CHANGELOG.md +29 -1
- data/exe/opal +5 -7
- data/lib/opal/builder.rb +13 -13
- data/lib/opal/builder_processors.rb +8 -2
- data/lib/opal/builder_scheduler/prefork.rb +60 -6
- data/lib/opal/cli.rb +59 -43
- data/lib/opal/cli_runners/compiler.rb +7 -6
- data/lib/opal/cli_runners/safari.rb +208 -0
- data/lib/opal/cli_runners.rb +1 -0
- data/lib/opal/nodes/literal.rb +16 -0
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/constants.rb +2 -2
- data/spec/filters/platform/.keep +0 -0
- data/spec/filters/platform/firefox/exception.rb +8 -0
- data/spec/filters/platform/firefox/kernel.rb +3 -0
- data/spec/filters/platform/safari/exception.rb +8 -0
- data/spec/filters/platform/safari/float.rb +4 -0
- data/spec/filters/platform/safari/kernel.rb +3 -0
- data/spec/filters/platform/safari/literal_regexp.rb +6 -0
- data/spec/lib/builder_spec.rb +32 -0
- data/spec/lib/cli_spec.rb +18 -6
- data/spec/lib/fixtures/build_order/file1.js +1 -0
- data/spec/lib/fixtures/build_order/file2.js +1 -0
- data/spec/lib/fixtures/build_order/file3.js +1 -0
- data/spec/lib/fixtures/build_order/file4.js +1 -0
- data/spec/lib/fixtures/build_order/file5.rb.erb +4 -0
- data/spec/lib/fixtures/build_order/file51.js +1 -0
- data/spec/lib/fixtures/build_order/file6.rb +10 -0
- data/spec/lib/fixtures/build_order/file61.rb +1 -0
- data/spec/lib/fixtures/build_order/file62.rb +4 -0
- data/spec/lib/fixtures/build_order/file63.rb +4 -0
- data/spec/lib/fixtures/build_order/file64.rb +1 -0
- data/spec/lib/fixtures/build_order/file7.rb +1 -0
- data/spec/lib/fixtures/build_order.rb +9 -0
- data/spec/lib/rake_dist_spec.rb +69 -0
- data/spec/mspec-opal/runner.rb +1 -0
- data/spec/opal/core/io/read_spec.rb +12 -3
- data/stdlib/opal/platform.rb +1 -0
- data/stdlib/opal-platform.rb +3 -0
- data/stdlib/time.rb +21 -1
- data/tasks/building.rake +1 -1
- data/tasks/testing.rake +3 -4
- metadata +29 -6
@@ -0,0 +1,208 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'shellwords'
|
4
|
+
require 'socket'
|
5
|
+
require 'timeout'
|
6
|
+
require 'tmpdir'
|
7
|
+
require 'rbconfig'
|
8
|
+
require 'opal/os'
|
9
|
+
require 'net/http'
|
10
|
+
require 'webrick'
|
11
|
+
|
12
|
+
module Opal
|
13
|
+
module CliRunners
|
14
|
+
class Safari
|
15
|
+
EXECUTION_TIMEOUT = 600 # seconds
|
16
|
+
DEFAULT_SAFARI_DRIVER_HOST = 'localhost'
|
17
|
+
DEFAULT_SAFARI_DRIVER_PORT = 9444 # in addition safari_driver_port + 1 is used for the http server
|
18
|
+
|
19
|
+
def self.call(data)
|
20
|
+
runner = new(data)
|
21
|
+
runner.run
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(data)
|
25
|
+
argv = data[:argv]
|
26
|
+
if argv && argv.any?
|
27
|
+
warn "warning: ARGV is not supported by the Safari runner #{argv.inspect}"
|
28
|
+
end
|
29
|
+
|
30
|
+
options = data[:options]
|
31
|
+
@output = options.fetch(:output, $stdout)
|
32
|
+
@builder = data[:builder].call
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :output, :exit_status, :builder
|
36
|
+
|
37
|
+
def run
|
38
|
+
mktmpdir do |dir|
|
39
|
+
with_http_server(dir) do |http_port, server_thread|
|
40
|
+
with_safari_driver do
|
41
|
+
prepare_files_in(dir)
|
42
|
+
|
43
|
+
# Safaridriver commands are very limitied, for supported commands see:
|
44
|
+
# https://developer.apple.com/documentation/webkit/macos_webdriver_commands_for_safari_12_and_later
|
45
|
+
Net::HTTP.start(safari_driver_host, safari_driver_port) do |con|
|
46
|
+
con.read_timeout = EXECUTION_TIMEOUT
|
47
|
+
res = con.post('/session', { capabilities: { browserName: 'Safari' } }.to_json, 'Content-Type' => 'application/json')
|
48
|
+
session_id = JSON.parse(res.body).dig('value', 'sessionId')
|
49
|
+
if session_id
|
50
|
+
session_path = "/session/#{session_id}"
|
51
|
+
con.post("#{session_path}/url", { url: "http://#{safari_driver_host}:#{http_port}/index.html" }.to_json, 'Content-Type' => 'application/json')
|
52
|
+
server_thread.join(EXECUTION_TIMEOUT)
|
53
|
+
else
|
54
|
+
STDERR.puts "Could not create session: #{res.body}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
0
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def prepare_files_in(dir)
|
66
|
+
# The safaridriver is very limited in capabilities, basically it can trigger visiting sites
|
67
|
+
# and interact a bit with the page. So this runner starts its own server, overwrites the
|
68
|
+
# console log, warn, error functions of the browser and triggers a request after execution
|
69
|
+
# to exit. Certain exceptions cannot be caught that way and everything may fail in between,
|
70
|
+
# thats why execution is timed out after EXECUTION_TIMEOUT (10 minutes).
|
71
|
+
# As a side effect, console messages may arrive out of order and timing anything may be inaccurate.
|
72
|
+
|
73
|
+
builder.build_str <<~RUBY, '(exit)', no_export: true
|
74
|
+
%x{
|
75
|
+
var req = new XMLHttpRequest();
|
76
|
+
req.open("GET", '/exit');
|
77
|
+
req.send();
|
78
|
+
}
|
79
|
+
RUBY
|
80
|
+
|
81
|
+
js = builder.to_s
|
82
|
+
map = builder.source_map.to_json
|
83
|
+
ext = builder.output_extension
|
84
|
+
module_type = ' type="module"' if builder.esm?
|
85
|
+
|
86
|
+
File.binwrite("#{dir}/index.#{ext}", js)
|
87
|
+
File.binwrite("#{dir}/index.map", map)
|
88
|
+
File.binwrite("#{dir}/index.html", <<~HTML)
|
89
|
+
<html><head>
|
90
|
+
<meta charset='utf-8'>
|
91
|
+
<link rel="icon" href="data:;base64,=">
|
92
|
+
</head><body>
|
93
|
+
<script>
|
94
|
+
var orig_log = console.log;
|
95
|
+
var orig_err = console.error;
|
96
|
+
var orig_warn = console.warn;
|
97
|
+
function send_log_request(args) {
|
98
|
+
var req = new XMLHttpRequest();
|
99
|
+
req.open("POST", '/log');
|
100
|
+
req.setRequestHeader("Content-Type", "application/json");
|
101
|
+
req.send(JSON.stringify(args));
|
102
|
+
}
|
103
|
+
console.log = function() {
|
104
|
+
orig_log.apply(null, arguments);
|
105
|
+
send_log_request(arguments);
|
106
|
+
}
|
107
|
+
console.error = function() {
|
108
|
+
orig_err.apply(null, arguments);
|
109
|
+
send_log_request(arguments);
|
110
|
+
}
|
111
|
+
console.warn = function() {
|
112
|
+
orig_warn.apply(null, arguments);
|
113
|
+
send_log_request(arguments);
|
114
|
+
}
|
115
|
+
|
116
|
+
</script>
|
117
|
+
<script src='./index.#{ext}'#{module_type}></script>
|
118
|
+
</body></html>
|
119
|
+
HTML
|
120
|
+
|
121
|
+
# <script src='./index.#{ext}'#{module_type}></script>
|
122
|
+
end
|
123
|
+
|
124
|
+
def safari_driver_host
|
125
|
+
ENV['SAFARI_DRIVER_HOST'] || DEFAULT_SAFARI_DRIVER_HOST
|
126
|
+
end
|
127
|
+
|
128
|
+
def safari_driver_port
|
129
|
+
ENV['SAFARI_DRIVER_PORT'] || DEFAULT_SAFARI_DRIVER_PORT
|
130
|
+
end
|
131
|
+
|
132
|
+
def with_http_server(dir)
|
133
|
+
port = safari_driver_port.to_i + 1
|
134
|
+
server_thread = Thread.new do
|
135
|
+
server = WEBrick::HTTPServer.new(Port: port, DocumentRoot: dir, Logger: WEBrick::Log.new('/dev/null'), AccessLog: [])
|
136
|
+
server.mount_proc('/log') do |req, res|
|
137
|
+
if req.body
|
138
|
+
par = JSON.parse(req.body)
|
139
|
+
par.each_value do |value|
|
140
|
+
print value.to_s
|
141
|
+
end
|
142
|
+
end
|
143
|
+
res.header['Content-Type'] = 'text/plain'
|
144
|
+
res.body = ''
|
145
|
+
end
|
146
|
+
server.mount_proc('/exit') do
|
147
|
+
server_thread.kill
|
148
|
+
end
|
149
|
+
server.start
|
150
|
+
end
|
151
|
+
|
152
|
+
yield port, server_thread
|
153
|
+
rescue
|
154
|
+
exit(1)
|
155
|
+
ensure
|
156
|
+
server_thread.kill if server_thread
|
157
|
+
end
|
158
|
+
|
159
|
+
def with_safari_driver
|
160
|
+
if safari_driver_running?
|
161
|
+
yield
|
162
|
+
else
|
163
|
+
run_safari_driver { yield }
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def run_safari_driver
|
168
|
+
raise 'Safari driver can be started only on localhost' if safari_driver_host != DEFAULT_SAFARI_DRIVER_HOST
|
169
|
+
|
170
|
+
started = false
|
171
|
+
|
172
|
+
safari_driver_cmd = %{/usr/bin/safaridriver \
|
173
|
+
-p #{safari_driver_port} \
|
174
|
+
#{ENV['SAFARI_DRIVER_OPTS']}}
|
175
|
+
|
176
|
+
safari_driver_pid = Process.spawn(safari_driver_cmd, in: OS.dev_null, out: OS.dev_null, err: OS.dev_null)
|
177
|
+
|
178
|
+
Timeout.timeout(30) do
|
179
|
+
loop do
|
180
|
+
break if safari_driver_running?
|
181
|
+
sleep 0.5
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
started = true
|
186
|
+
|
187
|
+
yield
|
188
|
+
rescue Timeout::Error => e
|
189
|
+
puts started ? 'Execution timed out' : 'Failed to start Safari driver'
|
190
|
+
raise e
|
191
|
+
ensure
|
192
|
+
Process.kill('HUP', safari_driver_pid) if safari_driver_pid
|
193
|
+
end
|
194
|
+
|
195
|
+
def safari_driver_running?
|
196
|
+
puts "Connecting to #{safari_driver_host}:#{safari_driver_port}..."
|
197
|
+
TCPSocket.new(safari_driver_host, safari_driver_port).close
|
198
|
+
true
|
199
|
+
rescue Errno::ECONNREFUSED, Errno::EADDRNOTAVAIL
|
200
|
+
false
|
201
|
+
end
|
202
|
+
|
203
|
+
def mktmpdir(&block)
|
204
|
+
Dir.mktmpdir('safari-opal-', &block)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
data/lib/opal/cli_runners.rb
CHANGED
data/lib/opal/nodes/literal.rb
CHANGED
@@ -158,11 +158,27 @@ module Opal
|
|
158
158
|
case value
|
159
159
|
when ''
|
160
160
|
push('/(?:)/')
|
161
|
+
when /\(\?<=|\(\?<!/
|
162
|
+
# Safari/WebKit will not execute javascript code if it contains a lookbehind literal RegExp
|
163
|
+
# and they fail with "Syntax Error". This tricks their parser by disguising the literal RegExp
|
164
|
+
# as string for the dynamic $regexp helper. Safari/Webkit will still fail to execute the RegExp,
|
165
|
+
# but at least they will parse and run everything else.
|
166
|
+
static_as_dynamic(value)
|
161
167
|
else
|
162
168
|
push "#{Regexp.new(value).inspect}#{flags.join}"
|
163
169
|
end
|
164
170
|
end
|
165
171
|
|
172
|
+
def static_as_dynamic(value)
|
173
|
+
helper :regexp
|
174
|
+
|
175
|
+
push '$regexp(["'
|
176
|
+
push value.gsub('\\', '\\\\\\\\')
|
177
|
+
push '"]'
|
178
|
+
push ", '#{flags.join}'" if flags.any?
|
179
|
+
push ")"
|
180
|
+
end
|
181
|
+
|
166
182
|
def extract_flags_and_value
|
167
183
|
*values, flags_sexp = *children
|
168
184
|
self.flags = flags_sexp.children.map(&:to_s)
|
data/lib/opal/version.rb
CHANGED
data/opal/corelib/constants.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
::RUBY_PLATFORM = 'opal'
|
2
2
|
::RUBY_ENGINE = 'opal'
|
3
3
|
::RUBY_VERSION = '3.2.0'
|
4
|
-
::RUBY_ENGINE_VERSION = '1.7.
|
5
|
-
::RUBY_RELEASE_DATE = '
|
4
|
+
::RUBY_ENGINE_VERSION = '1.7.1'
|
5
|
+
::RUBY_RELEASE_DATE = '2023-01-06'
|
6
6
|
::RUBY_PATCHLEVEL = 0
|
7
7
|
::RUBY_REVISION = '0'
|
8
8
|
::RUBY_COPYRIGHT = 'opal - Copyright (C) 2013-2022 Adam Beynon and the Opal contributors'
|
File without changes
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# Source map support currently is only available for Chrome and Nodejs
|
2
|
+
opal_unsupported_filter "Exception" do
|
3
|
+
fails "Exception#backtrace_locations sets each element to a Thread::Backtrace::Location"
|
4
|
+
fails "Exception#backtrace contains lines of the same format for each prior position in the stack"
|
5
|
+
fails "Exception#backtrace sets each element to a String"
|
6
|
+
fails "Invoking a method when the method is not available should omit the method_missing call from the backtrace for NoMethodError"
|
7
|
+
fails "Invoking a method when the method is not available should omit the method_missing call from the backtrace for NameError"
|
8
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# Source map support currently is only available for Chrome and Nodejs
|
2
|
+
opal_unsupported_filter "Exception" do
|
3
|
+
fails "Exception#backtrace contains lines of the same format for each prior position in the stack"
|
4
|
+
fails "Exception#backtrace_locations sets each element to a Thread::Backtrace::Location"
|
5
|
+
fails "Exception#backtrace returns an Array that can be updated"
|
6
|
+
fails "Exception#backtrace returns the same array after duping"
|
7
|
+
fails "Exception#backtrace sets each element to a String"
|
8
|
+
end
|
@@ -0,0 +1,4 @@
|
|
1
|
+
opal_filter "Float" do
|
2
|
+
fails "Float#fdiv performs floating-point division between self and an Integer" # Expected 8.900000000008007e-117 == 8.900000000008011e-117 to be truthy but was false
|
3
|
+
fails "Float#quo performs floating-point division between self and an Integer" # Expected 8.900000000008007e-117 == 8.900000000008011e-117 to be truthy but was false
|
4
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
opal_unsupported_filter "Literal Regexp" do
|
2
|
+
# Safari and WebKit do not support lookbehind, but may in the future see https://github.com/WebKit/WebKit/pull/7109
|
3
|
+
fails "Literal Regexps handles a lookbehind with ss characters"
|
4
|
+
fails "Literal Regexps supports (?<= ) (positive lookbehind)"
|
5
|
+
fails "Literal Regexps supports (?<! ) (negative lookbehind)"
|
6
|
+
end
|
data/spec/lib/builder_spec.rb
CHANGED
@@ -154,4 +154,36 @@ RSpec.describe Opal::Builder do
|
|
154
154
|
expect(builder.build('opal/platform', requirable: true).to_s).to include(%{Opal.modules["opal/platform"]})
|
155
155
|
end
|
156
156
|
end
|
157
|
+
|
158
|
+
describe 'output order' do
|
159
|
+
it 'is preserved with a prefork scheduler' do
|
160
|
+
my_builder = builder.dup
|
161
|
+
my_builder.append_paths(File.expand_path('..', __FILE__))
|
162
|
+
my_builder.cache = Opal::Cache::NullCache.new
|
163
|
+
10.times do |i| # Increase entropy
|
164
|
+
expect(
|
165
|
+
my_builder.dup.build('fixtures/build_order').to_s.scan(/(FILE_[0-9]+)/).map(&:first)
|
166
|
+
).to eq(%w[
|
167
|
+
FILE_1 FILE_2 FILE_3 FILE_4
|
168
|
+
FILE_51 FILE_5
|
169
|
+
FILE_61 FILE_62 FILE_63 FILE_64 FILE_6
|
170
|
+
FILE_7
|
171
|
+
])
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'is preserved with a sequential scheduler' do
|
176
|
+
temporarily_with_sequential_scheduler do
|
177
|
+
builder.append_paths(File.expand_path('..', __FILE__))
|
178
|
+
expect(
|
179
|
+
builder.build('fixtures/build_order').to_s.scan(/(FILE_[0-9]+)/).map(&:first)
|
180
|
+
).to eq(%w[
|
181
|
+
FILE_1 FILE_2 FILE_3 FILE_4
|
182
|
+
FILE_51 FILE_5
|
183
|
+
FILE_61 FILE_62 FILE_63 FILE_64 FILE_6
|
184
|
+
FILE_7
|
185
|
+
])
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
157
189
|
end
|
data/spec/lib/cli_spec.rb
CHANGED
@@ -9,7 +9,7 @@ RSpec.describe Opal::CLI do
|
|
9
9
|
subject(:cli) { described_class.new(options) }
|
10
10
|
|
11
11
|
context 'with a file' do
|
12
|
-
let(:options) { {:
|
12
|
+
let(:options) { {argv: [file]} }
|
13
13
|
|
14
14
|
it 'runs the file' do
|
15
15
|
expect_output_of{ subject.run }.to eq("hi from opal!\n")
|
@@ -26,8 +26,10 @@ RSpec.describe Opal::CLI do
|
|
26
26
|
|
27
27
|
describe ':evals option' do
|
28
28
|
context 'without evals and paths' do
|
29
|
-
it '
|
30
|
-
expect
|
29
|
+
it 'uses stdin' do
|
30
|
+
expect(cli.file).to eq($stdin)
|
31
|
+
expect(cli.filename).to eq('-')
|
32
|
+
expect(cli.argv).to eq([])
|
31
33
|
end
|
32
34
|
|
33
35
|
context 'with lib_only: true and opal require' do
|
@@ -40,7 +42,7 @@ RSpec.describe Opal::CLI do
|
|
40
42
|
end
|
41
43
|
|
42
44
|
context 'with one eval' do
|
43
|
-
let(:options) { {:
|
45
|
+
let(:options) { {evals: ['puts "hello"']} }
|
44
46
|
|
45
47
|
it 'executes the code' do
|
46
48
|
expect_output_of{ subject.run }.to eq("hello\n")
|
@@ -56,12 +58,22 @@ RSpec.describe Opal::CLI do
|
|
56
58
|
end
|
57
59
|
|
58
60
|
context 'with many evals' do
|
59
|
-
let(:options) { {:
|
61
|
+
let(:options) { {evals: ['puts "hello"', 'puts "ciao"']} }
|
60
62
|
|
61
63
|
it 'executes the code' do
|
62
64
|
expect_output_of{ subject.run }.to eq("hello\nciao\n")
|
63
65
|
end
|
64
66
|
end
|
67
|
+
|
68
|
+
context 'with a file path arg' do
|
69
|
+
let(:options) { {:evals => ['puts "hello"'], argv: [file]} }
|
70
|
+
|
71
|
+
it 'considers it as part of ARGV' do
|
72
|
+
expect(cli.argv).to eq([file])
|
73
|
+
expect(cli.file.tap(&:rewind).read).to eq('puts "hello"')
|
74
|
+
expect(cli.filename).to eq('-e')
|
75
|
+
end
|
76
|
+
end
|
65
77
|
end
|
66
78
|
|
67
79
|
describe ':no_exit option' do
|
@@ -229,7 +241,7 @@ RSpec.describe Opal::CLI do
|
|
229
241
|
|
230
242
|
describe ':enable_source_location' do
|
231
243
|
let(:file) { File.expand_path('../fixtures/source_location_test.rb', __FILE__) }
|
232
|
-
let(:options) { { enable_source_location: true, runner: :compiler,
|
244
|
+
let(:options) { { enable_source_location: true, runner: :compiler, argv: [file] } }
|
233
245
|
|
234
246
|
it 'sets $$source_location prop for compiled methods' do
|
235
247
|
expect_output_of { subject.run }.to include("source_location_test.rb', 6]")
|
@@ -0,0 +1 @@
|
|
1
|
+
var FILE_1;
|
@@ -0,0 +1 @@
|
|
1
|
+
var FILE_2;
|
@@ -0,0 +1 @@
|
|
1
|
+
var FILE_3;
|
@@ -0,0 +1 @@
|
|
1
|
+
var FILE_4;
|
@@ -0,0 +1 @@
|
|
1
|
+
var FILE_51;
|
@@ -0,0 +1 @@
|
|
1
|
+
FILE_61 = true
|
@@ -0,0 +1 @@
|
|
1
|
+
FILE_64 = true
|
@@ -0,0 +1 @@
|
|
1
|
+
FILE_7 = true
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# This fixture tests build order of required JS files
|
2
|
+
|
3
|
+
require 'fixtures/build_order/file1'
|
4
|
+
require 'fixtures/build_order/file2'
|
5
|
+
require 'fixtures/build_order/file3'
|
6
|
+
require 'fixtures/build_order/file4'
|
7
|
+
require 'fixtures/build_order/file5'
|
8
|
+
require 'fixtures/build_order/file6'
|
9
|
+
require 'fixtures/build_order/file7'
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'lib/spec_helper'
|
2
|
+
require 'open3'
|
3
|
+
require 'opal/os'
|
4
|
+
|
5
|
+
RSpec.describe "rake dist" do
|
6
|
+
before :all do
|
7
|
+
system "rake dist >#{Opal::OS.dev_null}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def run_with_node(code, precode:, requires:)
|
11
|
+
requires = requires.map do |i|
|
12
|
+
"require('./build/#{i}');"
|
13
|
+
end.join
|
14
|
+
|
15
|
+
code = "#{requires};#{precode};console.log(#{code});"
|
16
|
+
|
17
|
+
stdout, _, status = Open3.capture3('node', '-e', code)
|
18
|
+
|
19
|
+
expect(status).to eq(0)
|
20
|
+
|
21
|
+
stdout.chomp
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:output) { run_with_node(code, precode: precode, requires: requires) }
|
25
|
+
let(:requires) { ['opal'] }
|
26
|
+
let(:precode) { '' }
|
27
|
+
let(:code) { 'typeof Opal' }
|
28
|
+
|
29
|
+
it 'should provide a working Opal environment' do
|
30
|
+
expect(output).to eq('object')
|
31
|
+
end
|
32
|
+
|
33
|
+
context do
|
34
|
+
let(:requires) { ['opal/mini'] }
|
35
|
+
|
36
|
+
it 'should provide a working Opal mini environment' do
|
37
|
+
expect(output).to eq('object')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context do
|
42
|
+
let(:requires) { ['opal', 'opal/full'] }
|
43
|
+
let(:precode) { 'Opal.require("corelib/pattern_matching")' }
|
44
|
+
let(:code) { 'typeof Opal.PatternMatching' }
|
45
|
+
|
46
|
+
it 'should provide a working Opal full environment' do
|
47
|
+
expect(output).to eq('function')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context do
|
52
|
+
let(:requires) { %w[opal opal-replutils] }
|
53
|
+
let(:code) { 'typeof Opal.REPLUtils' }
|
54
|
+
|
55
|
+
it 'should not require requirable files by default' do
|
56
|
+
expect(output).to eq('undefined')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context do
|
61
|
+
let(:requires) { %w[opal opal-replutils] }
|
62
|
+
let(:precode) { 'Opal.require("opal-replutils")' }
|
63
|
+
let(:code) { 'typeof Opal.REPLUtils' }
|
64
|
+
|
65
|
+
it 'should allow user to require requirable files to provide missing functionality' do
|
66
|
+
expect(output).to eq('function')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/spec/mspec-opal/runner.rb
CHANGED
@@ -32,7 +32,10 @@ describe "IO reading methods" do
|
|
32
32
|
examples.each do |example|
|
33
33
|
it "correctly splits messages for input #{example.inspect}" do
|
34
34
|
io = prepare_io_for.(example)
|
35
|
-
expected_output = example.gsub("|", "").split(
|
35
|
+
expected_output = example.gsub("|", "").split(/\n/).map { |e| e + "\n" }
|
36
|
+
len = expected_output.length
|
37
|
+
last = expected_output.last
|
38
|
+
expected_output[len-1] = last.chop if !example.end_with?("\n") && last
|
36
39
|
io.readlines.should == expected_output
|
37
40
|
end
|
38
41
|
end
|
@@ -42,7 +45,10 @@ describe "IO reading methods" do
|
|
42
45
|
examples.each do |example|
|
43
46
|
it "correctly splits messages for input #{example.inspect}" do
|
44
47
|
io = prepare_io_for.(example)
|
45
|
-
expected_output = example.gsub("|", "").split(
|
48
|
+
expected_output = example.gsub("|", "").split(/\n/).map { |e| e + "\n" }
|
49
|
+
len = expected_output.length
|
50
|
+
last = expected_output.last
|
51
|
+
expected_output[len-1] = last.chop if !example.end_with?("\n") && last
|
46
52
|
loop do
|
47
53
|
expected_output.shift.should == io.readline
|
48
54
|
rescue EOFError
|
@@ -57,7 +63,10 @@ describe "IO reading methods" do
|
|
57
63
|
examples.each do |example|
|
58
64
|
it "correctly splits messages for input #{example.inspect}" do
|
59
65
|
io = prepare_io_for.(example)
|
60
|
-
expected_output = example.gsub("|", "").split(
|
66
|
+
expected_output = example.gsub("|", "").split(/\n/).map { |e| e + "\n" }
|
67
|
+
len = expected_output.length
|
68
|
+
last = expected_output.last
|
69
|
+
expected_output[len-1] = last.chop if !example.end_with?("\n") && last
|
61
70
|
loop do
|
62
71
|
line = io.gets
|
63
72
|
expected_output.shift.should == line
|
data/stdlib/opal/platform.rb
CHANGED
@@ -8,5 +8,6 @@ when 'deno' then require 'deno/base'
|
|
8
8
|
when 'nodejs' then require 'nodejs/base'
|
9
9
|
when 'headless-chrome' then require 'headless_browser/base'
|
10
10
|
when 'headless-firefox' then require 'headless_browser/base'
|
11
|
+
when 'safari' then require 'headless_browser/base'
|
11
12
|
when 'opal-miniracer' then require 'opal/miniracer'
|
12
13
|
end
|
data/stdlib/opal-platform.rb
CHANGED
@@ -6,6 +6,7 @@ node = `typeof(process) !== "undefined" && process.versions && proce
|
|
6
6
|
nashorn = `typeof(Java) !== "undefined" && Java.type`
|
7
7
|
headless_chrome = `typeof(opalheadlesschrome) !== "undefined"`
|
8
8
|
headless_firefox = `typeof(opalheadlessfirefox) !== "undefined"`
|
9
|
+
safari = `typeof(opalsafari) !== "undefined"`
|
9
10
|
gjs = `typeof(window) !== "undefined" && typeof(GjsFileImporter) !== "undefined"`
|
10
11
|
quickjs = `typeof(window) === "undefined" && typeof(__loadScript) !== "undefined"`
|
11
12
|
opal_miniracer = `typeof(opalminiracer) !== "undefined"`
|
@@ -20,6 +21,8 @@ OPAL_PLATFORM = if nashorn
|
|
20
21
|
'headless-chrome'
|
21
22
|
elsif headless_firefox
|
22
23
|
'headless-firefox'
|
24
|
+
elsif safari
|
25
|
+
'safari'
|
23
26
|
elsif gjs
|
24
27
|
'gjs'
|
25
28
|
elsif quickjs
|
data/stdlib/time.rb
CHANGED
@@ -1,6 +1,26 @@
|
|
1
1
|
class Time
|
2
2
|
def self.parse(str)
|
3
|
-
|
3
|
+
%x{
|
4
|
+
var d = Date.parse(str);
|
5
|
+
if (d !== d) {
|
6
|
+
// parsing failed, d is a NaN
|
7
|
+
// probably str is not in ISO 8601 format, which is the only format, required to be supported by Javascript
|
8
|
+
// try to make the format more like ISO or more like Chrome and parse again
|
9
|
+
str = str.replace(/^(\d+)([\./])(\d+)([\./])?(\d+)?/, function(matched_sub, c1, c2, c3, c4, c5, offset, orig_string) {
|
10
|
+
if ((c2 === c4) && c5) {
|
11
|
+
// 2007.10.1 or 2007/10/1 are ok, but 2007/10.1 is not, convert to 2007-10-1
|
12
|
+
return c1 + '-' + c3 + '-' + c5;
|
13
|
+
} else if (c3 && !c4) {
|
14
|
+
// 2007.10 or 2007/10
|
15
|
+
// Chrome and Ruby can parse "2007/10", assuming its "2007-10-01", do the same
|
16
|
+
return c1 + '-' + c3 + '-01';
|
17
|
+
};
|
18
|
+
return matched_sub;
|
19
|
+
});
|
20
|
+
d = Date.parse(str);
|
21
|
+
}
|
22
|
+
return new Date(d);
|
23
|
+
}
|
4
24
|
end
|
5
25
|
|
6
26
|
def self.def_formatter(name, format, on_utc: false, utc_tz: nil, tz_format: nil, fractions: false, on: self)
|
data/tasks/building.rake
CHANGED
@@ -38,7 +38,7 @@ task :dist do
|
|
38
38
|
$stdout.flush
|
39
39
|
|
40
40
|
# Set requirable to true, unless building opal. This allows opal to be auto-loaded.
|
41
|
-
requirable = (%w[opal opal/mini opal/base].include? lib)
|
41
|
+
requirable = !(%w[opal opal/mini opal/base].include? lib)
|
42
42
|
builder = Opal::Builder.build(lib, requirable: requirable)
|
43
43
|
|
44
44
|
src = builder.to_s if (formats & %w[js min gz]).any?
|