opal 1.6.1 → 1.7.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +17 -0
- data/CHANGELOG.md +35 -1
- data/Gemfile +1 -0
- data/HACKING.md +47 -26
- data/benchmark/benchmarks +415 -103
- data/benchmark/bm_call_overhead.yml +28 -0
- data/benchmark/run.rb +61 -40
- data/docs/cdp_common.json +3364 -0
- data/docs/cdp_common.md +18 -0
- data/docs/{headless_chrome.md → headless_browsers.md} +31 -12
- data/lib/opal/ast/builder.rb +1 -1
- data/lib/opal/builder.rb +6 -1
- data/lib/opal/builder_processors.rb +5 -3
- data/lib/opal/cache.rb +1 -7
- data/lib/opal/cli_options.rb +72 -58
- data/lib/opal/cli_runners/chrome.rb +47 -9
- data/lib/opal/cli_runners/chrome_cdp_interface.rb +238 -112
- data/lib/opal/cli_runners/compiler.rb +146 -13
- data/lib/opal/cli_runners/deno.rb +32 -0
- data/lib/opal/cli_runners/firefox.rb +350 -0
- data/lib/opal/cli_runners/firefox_cdp_interface.rb +212 -0
- data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.cmd +17 -0
- data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.ps1 +28 -0
- data/lib/opal/cli_runners/node_modules/.package-lock.json +41 -0
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/LICENSE +1 -1
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/README.md +322 -182
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/bin/client.js +99 -114
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/chrome-remote-interface.js +1 -11
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/index.js +16 -11
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/api.js +41 -33
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/chrome.js +224 -214
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/devtools.js +71 -191
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/external-request.js +26 -6
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/protocol.json +20788 -9049
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/websocket-wrapper.js +10 -3
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/package.json +59 -123
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/webpack.config.js +25 -32
- data/lib/opal/cli_runners/node_modules/commander/History.md +298 -0
- data/lib/opal/cli_runners/node_modules/commander/LICENSE +22 -0
- data/lib/opal/cli_runners/node_modules/commander/Readme.md +217 -61
- data/lib/opal/cli_runners/node_modules/commander/index.js +431 -145
- data/lib/opal/cli_runners/node_modules/commander/package.json +16 -79
- data/lib/opal/cli_runners/node_modules/ws/README.md +334 -98
- data/lib/opal/cli_runners/node_modules/ws/browser.js +8 -0
- data/lib/opal/cli_runners/node_modules/ws/index.js +5 -10
- data/lib/opal/cli_runners/node_modules/ws/lib/buffer-util.js +129 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/constants.js +10 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/event-target.js +184 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/extension.js +223 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/limiter.js +55 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/permessage-deflate.js +518 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/receiver.js +607 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/sender.js +409 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/stream.js +180 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/validation.js +104 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/websocket-server.js +447 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/websocket.js +1195 -0
- data/lib/opal/cli_runners/node_modules/ws/package.json +40 -106
- data/lib/opal/cli_runners/package-lock.json +62 -0
- data/lib/opal/cli_runners/package.json +1 -1
- data/lib/opal/cli_runners.rb +26 -4
- data/lib/opal/nodes/args/prepare_post_args.rb +2 -2
- data/lib/opal/nodes/def.rb +8 -8
- data/lib/opal/nodes/iter.rb +12 -12
- data/lib/opal/nodes/logic.rb +1 -1
- data/lib/opal/nodes/masgn.rb +2 -2
- data/lib/opal/parser/with_ruby_lexer.rb +1 -1
- data/lib/opal/paths.rb +14 -0
- data/lib/opal/rewriter.rb +2 -0
- data/lib/opal/rewriters/forward_args.rb +52 -4
- data/lib/opal/rewriters/targeted_patches.rb +94 -0
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/basic_object.rb +1 -1
- data/opal/corelib/boolean.rb +2 -2
- data/opal/corelib/class.rb +11 -0
- data/opal/corelib/constants.rb +3 -3
- data/opal/corelib/enumerable.rb +4 -0
- data/opal/corelib/enumerator.rb +1 -1
- data/opal/corelib/hash.rb +2 -2
- data/opal/corelib/helpers.rb +1 -1
- data/opal/corelib/kernel.rb +3 -3
- data/opal/corelib/method.rb +1 -1
- data/opal/corelib/module.rb +29 -8
- data/opal/corelib/proc.rb +7 -5
- data/opal/corelib/runtime.js +141 -78
- data/opal/corelib/set.rb +252 -0
- data/opal/corelib/string.rb +2 -1
- data/opal/corelib/time.rb +2 -2
- data/opal/opal.rb +1 -0
- data/opal.gemspec +1 -0
- data/spec/filters/bugs/array.rb +22 -13
- data/spec/filters/bugs/base64.rb +5 -5
- data/spec/filters/bugs/basicobject.rb +16 -8
- data/spec/filters/bugs/bigdecimal.rb +161 -160
- data/spec/filters/bugs/binding.rb +10 -10
- data/spec/filters/bugs/class.rb +8 -8
- data/spec/filters/bugs/complex.rb +2 -1
- data/spec/filters/bugs/date.rb +79 -81
- data/spec/filters/bugs/datetime.rb +29 -29
- data/spec/filters/bugs/delegate.rb +1 -3
- data/spec/filters/bugs/encoding.rb +69 -69
- data/spec/filters/bugs/enumerable.rb +22 -20
- data/spec/filters/bugs/enumerator.rb +88 -85
- data/spec/filters/bugs/exception.rb +46 -40
- data/spec/filters/bugs/file.rb +32 -32
- data/spec/filters/bugs/float.rb +26 -21
- data/spec/filters/bugs/freeze.rb +88 -0
- data/spec/filters/bugs/hash.rb +39 -38
- data/spec/filters/bugs/integer.rb +57 -44
- data/spec/filters/bugs/io.rb +1 -1
- data/spec/filters/bugs/kernel.rb +349 -269
- data/spec/filters/bugs/language.rb +220 -188
- data/spec/filters/bugs/main.rb +5 -3
- data/spec/filters/bugs/marshal.rb +38 -38
- data/spec/filters/bugs/math.rb +2 -1
- data/spec/filters/bugs/method.rb +73 -62
- data/spec/filters/bugs/module.rb +163 -143
- data/spec/filters/bugs/numeric.rb +6 -6
- data/spec/filters/bugs/objectspace.rb +16 -16
- data/spec/filters/bugs/openstruct.rb +1 -1
- data/spec/filters/bugs/pack_unpack.rb +51 -51
- data/spec/filters/bugs/pathname.rb +7 -7
- data/spec/filters/bugs/proc.rb +63 -63
- data/spec/filters/bugs/random.rb +7 -6
- data/spec/filters/bugs/range.rb +12 -9
- data/spec/filters/bugs/rational.rb +8 -7
- data/spec/filters/bugs/regexp.rb +49 -48
- data/spec/filters/bugs/ruby-32.rb +56 -0
- data/spec/filters/bugs/set.rb +30 -30
- data/spec/filters/bugs/singleton.rb +4 -4
- data/spec/filters/bugs/string.rb +187 -99
- data/spec/filters/bugs/stringio.rb +7 -0
- data/spec/filters/bugs/stringscanner.rb +68 -68
- data/spec/filters/bugs/struct.rb +11 -9
- data/spec/filters/bugs/symbol.rb +1 -1
- data/spec/filters/bugs/time.rb +78 -63
- data/spec/filters/bugs/trace_point.rb +4 -4
- data/spec/filters/bugs/unboundmethod.rb +32 -17
- data/spec/filters/bugs/warnings.rb +8 -12
- data/spec/filters/unsupported/array.rb +24 -107
- data/spec/filters/unsupported/basicobject.rb +12 -12
- data/spec/filters/unsupported/bignum.rb +27 -52
- data/spec/filters/unsupported/class.rb +1 -2
- data/spec/filters/unsupported/delegator.rb +3 -3
- data/spec/filters/unsupported/enumerable.rb +2 -9
- data/spec/filters/unsupported/enumerator.rb +2 -11
- data/spec/filters/unsupported/file.rb +1 -1
- data/spec/filters/unsupported/float.rb +28 -47
- data/spec/filters/unsupported/hash.rb +8 -14
- data/spec/filters/unsupported/integer.rb +75 -91
- data/spec/filters/unsupported/kernel.rb +17 -35
- data/spec/filters/unsupported/language.rb +11 -19
- data/spec/filters/unsupported/marshal.rb +22 -41
- data/spec/filters/unsupported/matchdata.rb +28 -52
- data/spec/filters/unsupported/math.rb +1 -1
- data/spec/filters/unsupported/privacy.rb +229 -285
- data/spec/filters/unsupported/range.rb +1 -5
- data/spec/filters/unsupported/regexp.rb +40 -66
- data/spec/filters/unsupported/set.rb +2 -2
- data/spec/filters/unsupported/singleton.rb +4 -4
- data/spec/filters/unsupported/string.rb +305 -508
- data/spec/filters/unsupported/struct.rb +3 -4
- data/spec/filters/unsupported/symbol.rb +15 -18
- data/spec/filters/unsupported/thread.rb +1 -7
- data/spec/filters/unsupported/time.rb +159 -202
- data/spec/filters/unsupported/usage_of_files.rb +170 -259
- data/spec/lib/builder_spec.rb +4 -4
- data/spec/lib/rewriters/forward_args_spec.rb +32 -12
- data/spec/mspec-opal/runner.rb +2 -0
- data/spec/ruby_specs +4 -0
- data/stdlib/deno/base.rb +28 -0
- data/stdlib/deno/file.rb +340 -0
- data/stdlib/{headless_chrome.rb → headless_browser/base.rb} +1 -1
- data/stdlib/headless_browser/file.rb +15 -0
- data/stdlib/headless_browser.rb +4 -0
- data/stdlib/native.rb +1 -1
- data/stdlib/nodejs/file.rb +5 -0
- data/stdlib/opal/platform.rb +8 -6
- data/stdlib/opal-platform.rb +14 -8
- data/stdlib/set.rb +1 -258
- data/tasks/benchmarking.rake +62 -19
- data/tasks/performance.rake +1 -1
- data/tasks/testing.rake +5 -3
- data/test/nodejs/test_file.rb +29 -10
- data/test/opal/http_server.rb +28 -11
- data/test/opal/unsupported_and_bugs.rb +2 -1
- metadata +89 -50
- data/lib/opal/cli_runners/node_modules/ultron/LICENSE +0 -22
- data/lib/opal/cli_runners/node_modules/ultron/index.js +0 -138
- data/lib/opal/cli_runners/node_modules/ultron/package.json +0 -112
- data/lib/opal/cli_runners/node_modules/ws/SECURITY.md +0 -33
- data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.fallback.js +0 -56
- data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.js +0 -15
- data/lib/opal/cli_runners/node_modules/ws/lib/ErrorCodes.js +0 -28
- data/lib/opal/cli_runners/node_modules/ws/lib/EventTarget.js +0 -158
- data/lib/opal/cli_runners/node_modules/ws/lib/Extensions.js +0 -69
- data/lib/opal/cli_runners/node_modules/ws/lib/PerMessageDeflate.js +0 -339
- data/lib/opal/cli_runners/node_modules/ws/lib/Receiver.js +0 -520
- data/lib/opal/cli_runners/node_modules/ws/lib/Sender.js +0 -438
- data/lib/opal/cli_runners/node_modules/ws/lib/Validation.fallback.js +0 -9
- data/lib/opal/cli_runners/node_modules/ws/lib/Validation.js +0 -17
- data/lib/opal/cli_runners/node_modules/ws/lib/WebSocket.js +0 -705
- data/lib/opal/cli_runners/node_modules/ws/lib/WebSocketServer.js +0 -336
- data/spec/filters/bugs/boolean.rb +0 -3
- data/spec/filters/bugs/matrix.rb +0 -3
- data/spec/filters/unsupported/fixnum.rb +0 -15
- data/spec/filters/unsupported/freeze.rb +0 -102
- data/spec/filters/unsupported/pathname.rb +0 -4
- data/spec/filters/unsupported/proc.rb +0 -4
- data/spec/filters/unsupported/random.rb +0 -5
- data/spec/filters/unsupported/taint.rb +0 -162
@@ -3,143 +3,269 @@
|
|
3
3
|
# This script I converted into Opal, so that I don't have to write
|
4
4
|
# buffer handling again. We have gets, Node has nothing close to it,
|
5
5
|
# even async.
|
6
|
+
# For CDP see docs/cdp_common.(md|json)
|
6
7
|
|
7
8
|
require 'opal/platform'
|
9
|
+
require 'nodejs/env'
|
8
10
|
|
9
11
|
%x{
|
10
12
|
var CDP = require("chrome-remote-interface");
|
11
13
|
var fs = require("fs");
|
14
|
+
var http = require("http");
|
12
15
|
|
13
|
-
var dir = #{ARGV.last}
|
16
|
+
var dir = #{ARGV.last};
|
17
|
+
var ext = #{ENV['OPAL_CDP_EXT']};
|
18
|
+
var port_offset; // port offset for http server, depending on number of targets
|
19
|
+
var script_id; // of script which is executed after page is initialized, before page scripts are executed
|
20
|
+
var cdp_client; // CDP client
|
21
|
+
var target_id; // the used Target
|
14
22
|
|
15
23
|
var options = {
|
16
24
|
host: #{ENV['CHROME_HOST'] || 'localhost'},
|
17
|
-
port: #{ENV['CHROME_PORT'] || 9222}
|
25
|
+
port: parseInt(#{ENV['CHROME_PORT'] || '9222'})
|
18
26
|
};
|
19
27
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
// {"name":"0","type":"number","value":"1"},
|
55
|
-
// {"name":"1","type":"number","value":"2"},
|
56
|
-
// {"name":"2","type":"number","value":"3"}
|
57
|
-
// ]
|
58
|
-
// }
|
59
|
-
// },
|
60
|
-
// {
|
61
|
-
// "type":"object",
|
62
|
-
// "className":"Object",
|
63
|
-
// "description":"Object",
|
64
|
-
// "objectId":"{\"injectedScriptId\":11,\"id\":2}",
|
65
|
-
// "preview":{
|
66
|
-
// "type":"object",
|
67
|
-
// "description":"Object",
|
68
|
-
// "overflow":false,
|
69
|
-
// "properties":[
|
70
|
-
// {"name":"a","type":"string","value":"b"}
|
71
|
-
// ]
|
72
|
-
// }
|
73
|
-
// }
|
74
|
-
// ],
|
75
|
-
// // ...
|
76
|
-
// }
|
77
|
-
// Supporting this format for complex data structure is challenging, feel free to contribute!
|
78
|
-
//
|
79
|
-
Console.messageAdded(function(console_message) {
|
80
|
-
process.stdout.write(console_message.message.text);
|
81
|
-
});
|
28
|
+
// shared secret
|
29
|
+
|
30
|
+
function random_string() {
|
31
|
+
let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
32
|
+
let str = '';
|
33
|
+
for (let i = 0; i < 256; i++) {
|
34
|
+
str += chars.charAt(Math.floor(Math.random() * chars.length));
|
35
|
+
}
|
36
|
+
return str;
|
37
|
+
};
|
38
|
+
|
39
|
+
var shared_secret = random_string(); // secret must be passed with POST requests
|
40
|
+
|
41
|
+
// support functions
|
42
|
+
|
43
|
+
function perror(error) { console.error(error); }
|
44
|
+
|
45
|
+
var exiting = false;
|
46
|
+
|
47
|
+
function shutdown(exit_code) {
|
48
|
+
if (exiting) { return Promise.resolve(); }
|
49
|
+
exiting = true;
|
50
|
+
var promises = [];
|
51
|
+
cdp_client.Page.removeScriptToEvaluateOnNewDocument(script_id).then(function(){}, perror);
|
52
|
+
return Promise.all(promises).then(function () {
|
53
|
+
target_id ? cdp_client.Target.closeTarget(target_id) : null;
|
54
|
+
}).then(function() {
|
55
|
+
server.close();
|
56
|
+
process.exit(exit_code);
|
57
|
+
}, function(error) {
|
58
|
+
perror(error);
|
59
|
+
process.exit(1)
|
60
|
+
});
|
61
|
+
};
|
82
62
|
|
83
|
-
|
84
|
-
var properties = exception.exceptionDetails.exception.preview.properties,
|
85
|
-
stack, i;
|
63
|
+
// simple HTTP server to deliver page, scripts to, and trigger commands from browser
|
86
64
|
|
87
|
-
|
88
|
-
|
65
|
+
function not_found(res) {
|
66
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
67
|
+
res.end("NOT FOUND");
|
68
|
+
}
|
89
69
|
|
90
|
-
|
91
|
-
|
92
|
-
|
70
|
+
function response_ok(res) {
|
71
|
+
res.writeHead(200, { "Content-Type": "text/plain" });
|
72
|
+
res.end("OK");
|
73
|
+
}
|
74
|
+
|
75
|
+
function handle_post(req, res, fun) {
|
76
|
+
var data = "";
|
77
|
+
req.on('data', function(chunk) {
|
78
|
+
data += chunk;
|
79
|
+
})
|
80
|
+
req.on('end', function() {
|
81
|
+
var obj = JSON.parse(data);
|
82
|
+
if (obj.secret == shared_secret) {
|
83
|
+
fun.call(this, obj);
|
84
|
+
} else {
|
85
|
+
not_found(res);
|
86
|
+
}
|
87
|
+
});
|
88
|
+
}
|
89
|
+
|
90
|
+
var server = http.createServer(function(req, res) {
|
91
|
+
if (req.method === "GET") {
|
92
|
+
var path = dir + '/' + req.url.slice(1);
|
93
|
+
if (path.includes('..') || !fs.existsSync(path)) {
|
94
|
+
not_found(res);
|
95
|
+
} else {
|
96
|
+
var content_type;
|
97
|
+
if (path.endsWith(".html")) {
|
98
|
+
content_type = "text/html"
|
99
|
+
} else if (path.endsWith(".map")) {
|
100
|
+
content_type = "application/json"
|
101
|
+
} else {
|
102
|
+
content_type = "application/javascript"
|
93
103
|
}
|
104
|
+
res.writeHead(200, { "Content-Type": content_type });
|
105
|
+
res.end(fs.readFileSync(path));
|
106
|
+
}
|
107
|
+
} else if (req.method === "POST") {
|
108
|
+
if (req.url === "/File.write") {
|
109
|
+
// totally insecure on purpose
|
110
|
+
handle_post(req, res, function(obj) {
|
111
|
+
fs.writeFileSync(obj.filename, obj.data);
|
112
|
+
response_ok(res);
|
113
|
+
});
|
114
|
+
} else {
|
115
|
+
not_found(res);
|
116
|
+
}
|
117
|
+
} else {
|
118
|
+
not_found(res);
|
119
|
+
}
|
120
|
+
});
|
94
121
|
|
95
|
-
|
122
|
+
// actual CDP code
|
96
123
|
|
97
|
-
|
98
|
-
|
124
|
+
CDP.List(options, function(err, targets) {
|
125
|
+
// default CDP port is 9222, Firefox runner is at 9333
|
126
|
+
// Lets collect clients for
|
127
|
+
// Chrome CDP starting at 9273 ...
|
128
|
+
// Firefox CDP starting 9334 ...
|
129
|
+
port_offset = targets ? targets.length + 51 : 51; // default CDP port is 9222, Node CDP port 9229, Firefox is at 9333
|
99
130
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
131
|
+
return CDP(options, function(browser_client) {
|
132
|
+
|
133
|
+
server.listen({ port: port_offset + options.port, host: options.host });
|
134
|
+
|
135
|
+
browser_client.Target.createTarget({url: "about:blank"}).then(function(target) {
|
136
|
+
target_id = target;
|
137
|
+
options.target = target_id.targetId;
|
138
|
+
|
139
|
+
CDP(options, function(client) {
|
140
|
+
cdp_client = client;
|
141
|
+
|
142
|
+
var Log = client.Log,
|
143
|
+
Page = client.Page,
|
144
|
+
Runtime = client.Runtime;
|
145
|
+
|
146
|
+
// enable used CDP domains
|
147
|
+
Promise.all([
|
148
|
+
Log.enable(),
|
149
|
+
Page.enable(),
|
150
|
+
Runtime.enable()
|
151
|
+
]).then(function() {
|
152
|
+
// add script to set the shared_secret in the browser
|
153
|
+
return Page.addScriptToEvaluateOnNewDocument({source: "window.OPAL_CDP_SHARED_SECRET = '" + shared_secret + "';"}).then(function(scrid) {
|
154
|
+
script_id = scrid;
|
155
|
+
}, perror);
|
156
|
+
}, perror).then(function() {
|
157
|
+
|
158
|
+
// receive and handle all kinds of log and console messages
|
159
|
+
Log.entryAdded(function(entry) {
|
160
|
+
process.stdout.write(entry.entry.level + ': ' + entry.entry.text + "\n");
|
161
|
+
});
|
162
|
+
|
163
|
+
Runtime.consoleAPICalled(function(entry) {
|
164
|
+
var args = entry.args;
|
165
|
+
var stack = null;
|
166
|
+
var i, arg, frame, value;
|
167
|
+
|
168
|
+
// output actual message
|
169
|
+
for(i = 0; i < args.length; i++) {
|
170
|
+
arg = args[i];
|
171
|
+
if (arg.type === "string") { value = arg.value; }
|
172
|
+
else { value = JSON.stringify(arg); }
|
173
|
+
process.stdout.write(value);
|
174
|
+
}
|
175
|
+
|
176
|
+
if (entry.stackTrace && entry.stackTrace.callFrames) {
|
177
|
+
stack = entry.stackTrace.callFrames;
|
178
|
+
}
|
179
|
+
|
180
|
+
if (entry.type === "error" && stack) {
|
181
|
+
// print full stack for errors
|
182
|
+
process.stdout.write("\n");
|
183
|
+
for(i = 0; i < stack.length; i++) {
|
184
|
+
frame = stack[i];
|
185
|
+
if (frame) {
|
186
|
+
value = frame.url + ':' + frame.lineNumer + ':' + frame.columnNumber + '\n';
|
187
|
+
process.stdout.write(value);
|
188
|
+
}
|
189
|
+
}
|
190
|
+
}
|
191
|
+
});
|
192
|
+
|
193
|
+
// react to exceptions
|
194
|
+
Runtime.exceptionThrown(function(exception) {
|
195
|
+
var ex = exception.exceptionDetails.exception.preview.properties;
|
196
|
+
var stack = [];
|
197
|
+
if (exception.exceptionDetails.stackTrace) {
|
198
|
+
stack = exception.exceptionDetails.stackTrace.callFrames;
|
199
|
+
} else {
|
200
|
+
var d = exception.exceptionDetails;
|
201
|
+
stack.push({
|
202
|
+
url: d.url,
|
203
|
+
lineNumber: d.lineNumber,
|
204
|
+
columnNumber: d.columnNumber,
|
205
|
+
functionName: "(unknown)"
|
206
|
+
});
|
207
|
+
}
|
208
|
+
var fr;
|
209
|
+
for (var i = 0; i < ex.length; i++) {
|
210
|
+
fr = ex[i];
|
211
|
+
if (fr.name === "message") {
|
212
|
+
perror(fr.value);
|
213
|
+
}
|
214
|
+
}
|
215
|
+
for (var i = 0; i < stack.length; i++) {
|
216
|
+
fr = stack[i];
|
217
|
+
perror(fr.url + ':' + fr.lineNumber + ':' + fr.columnNumber + ': in ' + fr.functionName);
|
218
|
+
}
|
219
|
+
return shutdown(1);
|
220
|
+
});
|
221
|
+
|
222
|
+
// handle input
|
223
|
+
Page.javascriptDialogOpening((dialog) => {
|
224
|
+
#{
|
225
|
+
if `dialog.type` == 'prompt'
|
226
|
+
message = gets&.chomp
|
227
|
+
if message
|
228
|
+
`Page.handleJavaScriptDialog({accept: true, promptText: #{message}})`
|
229
|
+
else
|
230
|
+
`Page.handleJavaScriptDialog({accept: false})`
|
231
|
+
end
|
232
|
+
elsif `dialog.type` == 'alert' && `dialog.message` == 'opalheadlessbrowserexit'
|
233
|
+
# A special case of an alert with a magic string "opalheadlessbrowserexit".
|
234
|
+
# This denotes that `Kernel#exit` has been called. We would have rather used
|
235
|
+
# an exception here, but they don't bubble sometimes.
|
236
|
+
%x{
|
237
|
+
Page.handleJavaScriptDialog({accept: true});
|
238
|
+
Runtime.evaluate({ expression: "window.OPAL_EXIT_CODE" }).then(function(output) {
|
239
|
+
var exit_code = 0;
|
240
|
+
if (typeof(output.result) !== "undefined" && output.result.type === "number") {
|
241
|
+
exit_code = output.result.value;
|
242
|
+
}
|
243
|
+
return shutdown(exit_code);
|
244
|
+
});
|
245
|
+
}
|
246
|
+
end
|
247
|
+
}
|
248
|
+
});
|
249
|
+
|
250
|
+
// page has been loaded, all code has been executed
|
251
|
+
Page.loadEventFired(() => {
|
115
252
|
Runtime.evaluate({ expression: "window.OPAL_EXIT_CODE" }).then(function(output) {
|
116
|
-
client.close();
|
117
253
|
if (typeof(output.result) !== "undefined" && output.result.type === "number") {
|
118
|
-
|
254
|
+
return shutdown(output.result.value);
|
255
|
+
} else if (typeof(output.result) !== "undefined" && output.result.type === "string" && output.result.value === "noexit") {
|
256
|
+
// do nothing, we have headless chrome support enabled and there are most probably async events awaiting
|
119
257
|
} else {
|
120
|
-
|
258
|
+
return shutdown(0);
|
121
259
|
}
|
122
|
-
})
|
123
|
-
}
|
124
|
-
end
|
125
|
-
}
|
126
|
-
});
|
260
|
+
})
|
261
|
+
});
|
127
262
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
process.exit(output.result.value);
|
133
|
-
} else if (typeof(output.result) !== "undefined" && output.result.type === "string" && output.result.value === "noexit") {
|
134
|
-
// do nothing, we have headless chrome support enabled and there are most probably async events awaiting
|
135
|
-
} else {
|
136
|
-
client.close();
|
137
|
-
process.exit(0);
|
138
|
-
}
|
139
|
-
})
|
263
|
+
// init page load
|
264
|
+
Page.navigate({ url: "http://localhost:" + (port_offset + options.port).toString() + "/index.html" })
|
265
|
+
}, perror);
|
266
|
+
});
|
140
267
|
});
|
141
|
-
|
142
|
-
Page.navigate({ url: "file://"+dir+"/index.html" })
|
143
268
|
});
|
144
269
|
});
|
145
270
|
}
|
271
|
+
# end of code (marker to help see if brackets match above)
|
@@ -3,16 +3,149 @@
|
|
3
3
|
require 'opal/paths'
|
4
4
|
|
5
5
|
# The compiler runner will just output the compiled JavaScript
|
6
|
-
Opal::CliRunners::Compiler
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
6
|
+
class Opal::CliRunners::Compiler
|
7
|
+
def self.call(data)
|
8
|
+
new(data).start
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(data)
|
12
|
+
@options = data[:options] || {}
|
13
|
+
@builder_factory = data.fetch(:builder)
|
14
|
+
@map_file = @options[:map_file]
|
15
|
+
@output = data.fetch(:output)
|
16
|
+
@watch = @options[:watch]
|
17
|
+
end
|
18
|
+
|
19
|
+
def compile
|
20
|
+
builder = @builder_factory.call
|
21
|
+
compiled_source = builder.to_s
|
22
|
+
compiled_source += "\n" + builder.source_map.to_data_uri_comment unless @options[:no_source_map]
|
23
|
+
|
24
|
+
rewind_output if @watch
|
25
|
+
|
26
|
+
@output.puts compiled_source
|
27
|
+
@output.flush
|
28
|
+
|
29
|
+
File.write(@map_file, builder.source_map.to_json) if @map_file
|
30
|
+
|
31
|
+
builder
|
32
|
+
end
|
33
|
+
|
34
|
+
def compile_noraise
|
35
|
+
compile
|
36
|
+
rescue StandardError, Opal::SyntaxError => e
|
37
|
+
$stderr.puts "* Compilation failed: #{e.message}"
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def rewind_output
|
42
|
+
if !@output.is_a?(File) || @output.tty?
|
43
|
+
fail_unrewindable!
|
44
|
+
else
|
45
|
+
begin
|
46
|
+
@output.rewind
|
47
|
+
@output.truncate(0)
|
48
|
+
rescue Errno::ESPIPE
|
49
|
+
fail_unrewindable!
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def fail_unrewindable!
|
55
|
+
warn <<~ERROR
|
56
|
+
You have specified --watch, but for watch to work, you must specify an
|
57
|
+
--output file.
|
58
|
+
ERROR
|
59
|
+
exit 1
|
60
|
+
end
|
61
|
+
|
62
|
+
def fail_no_listen!
|
63
|
+
warn <<~ERROR
|
64
|
+
--watch mode requires the `listen` gem present. Please try to run:
|
65
|
+
|
66
|
+
gem install listen
|
67
|
+
|
68
|
+
Or if you are using bundler, add listen to your Gemfile.
|
69
|
+
ERROR
|
70
|
+
exit 1
|
71
|
+
end
|
72
|
+
|
73
|
+
def watch_compile
|
74
|
+
begin
|
75
|
+
require 'listen'
|
76
|
+
rescue LoadError
|
77
|
+
fail_no_listen!
|
78
|
+
end
|
79
|
+
|
80
|
+
@opal_deps = Opal.dependent_files
|
81
|
+
|
82
|
+
builder = compile
|
83
|
+
code_deps = builder.dependent_files
|
84
|
+
@files = @opal_deps + code_deps
|
85
|
+
@code_listener = watch_files
|
86
|
+
@code_listener.start
|
87
|
+
|
88
|
+
$stderr.puts "* Opal v#{Opal::VERSION} successfully compiled your program in --watch mode"
|
89
|
+
|
90
|
+
sleep
|
91
|
+
end
|
92
|
+
|
93
|
+
def reexec
|
94
|
+
system($0, *OriginalARGV) && exit
|
95
|
+
end
|
96
|
+
|
97
|
+
def on_code_change(modified)
|
98
|
+
if !(modified & @opal_deps).empty?
|
99
|
+
$stderr.puts "* Modified core Opal files: #{modified.join(', ')}; reexecuting"
|
100
|
+
reexec
|
101
|
+
elsif !modified.all? { |file| @directories.any? { |dir| file.start_with?(dir + '/') } }
|
102
|
+
$stderr.puts "* New unwatched files: #{modified.join(', ')}; reexecuting"
|
103
|
+
reexec
|
104
|
+
end
|
105
|
+
|
106
|
+
$stderr.puts "* Modified code: #{modified.join(', ')}; rebuilding"
|
107
|
+
|
108
|
+
builder = compile_noraise
|
109
|
+
|
110
|
+
# Ignore the bad compilation
|
111
|
+
if builder
|
112
|
+
code_deps = builder.dependent_files
|
113
|
+
@files = @opal_deps + code_deps
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def files_to_directories
|
118
|
+
directories = @files.map { |file| File.dirname(file) }.uniq
|
119
|
+
|
120
|
+
previous_dir = nil
|
121
|
+
# Only get the topmost directories
|
122
|
+
directories = directories.sort.map do |dir|
|
123
|
+
if previous_dir && dir.start_with?(previous_dir + '/')
|
124
|
+
nil
|
125
|
+
else
|
126
|
+
previous_dir = dir
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
directories.compact
|
131
|
+
end
|
132
|
+
|
133
|
+
def watch_files
|
134
|
+
@directories = files_to_directories
|
135
|
+
|
136
|
+
Listen.to(*@directories) do |modified, added, removed|
|
137
|
+
our_modified = @files & (modified + added + removed)
|
138
|
+
on_code_change(our_modified) unless our_modified.empty?
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def start
|
143
|
+
if @watch
|
144
|
+
watch_compile
|
145
|
+
else
|
146
|
+
compile
|
147
|
+
end
|
148
|
+
|
149
|
+
0
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'shellwords'
|
4
|
+
require 'opal/paths'
|
5
|
+
require 'opal/cli_runners/system_runner'
|
6
|
+
require 'opal/os'
|
7
|
+
|
8
|
+
module Opal
|
9
|
+
module CliRunners
|
10
|
+
class Deno
|
11
|
+
def self.call(data)
|
12
|
+
argv = data[:argv].dup.to_a
|
13
|
+
|
14
|
+
SystemRunner.call(data) do |tempfile|
|
15
|
+
[
|
16
|
+
'deno',
|
17
|
+
'run',
|
18
|
+
'--allow-read',
|
19
|
+
'--allow-write',
|
20
|
+
tempfile.path,
|
21
|
+
*argv
|
22
|
+
]
|
23
|
+
end
|
24
|
+
rescue Errno::ENOENT
|
25
|
+
raise MissingDeno, 'Please install Deno to be able to run Opal scripts.'
|
26
|
+
end
|
27
|
+
|
28
|
+
class MissingDeno < RunnerError
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|