opal-webpack-loader 0.10.3 → 0.11.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/README.md +1 -1
- data/bin/opal-webpack-windows-compile-server +125 -0
- data/bin/owl-compiler +4 -1
- data/lib/opal-webpack-loader/pipe_server.rb +219 -0
- data/lib/opal-webpack-loader/version.rb +1 -1
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 75cfc2e9527b252335ad359e1eb5c7aa476cec879ed76c4501b686a50f39b1f2
|
4
|
+
data.tar.gz: b6d13eac5c5eaa0e3c46dfd15b8f59d01d59124f92608c741e93952c87bb04f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9edddd5f0a21625c5376dbf609e594a35bfe36ab84ca2fb804fa7f011093e8a45aa516530bb0dceceec4dcc069619dc0e7c31ceecfd3f68a5ad0c6caf39699f
|
7
|
+
data.tar.gz: f21627293c524a6db7f7c53f89e83f13f77519597133c05dd992173b7d2cff05f0ab915cfe7eb8e52f036ce448f3905e572d75a2d345cefe93990ece36ad342b
|
data/README.md
CHANGED
@@ -23,7 +23,7 @@ At the [Isomorfeus Framework Project](http://isomorfeus.com)
|
|
23
23
|
opal-webpack-loader-0.7.1 compiles all of opal, a bunch of gems and over 19000SLC on a
|
24
24
|
Intel® Core™ i7-7700HQ CPU @ 2.80GHz × 8, with 8 workers in around 1850ms
|
25
25
|
- support for memcached or redis as compiler cache
|
26
|
-
-
|
26
|
+
- fast builds on windows too, parallel when building multiple assets with parallel webpack
|
27
27
|
- opal modules are packaged as es6 modules
|
28
28
|
- support for rails with webpacker
|
29
29
|
- other webpack features become available, like:
|
@@ -0,0 +1,125 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'digest'
|
3
|
+
require 'oj'
|
4
|
+
require 'c_lexer'
|
5
|
+
require 'optparse'
|
6
|
+
require 'opal/paths'
|
7
|
+
require 'opal/source_map'
|
8
|
+
require 'opal/compiler'
|
9
|
+
require 'opal-webpack-loader/load_path_manager'
|
10
|
+
require 'opal-webpack-loader/pipe_server'
|
11
|
+
|
12
|
+
modules_to_require = []
|
13
|
+
compiler_options = {}
|
14
|
+
compile_server_options = {}
|
15
|
+
cache = false
|
16
|
+
|
17
|
+
OptionParser.new do |opts|
|
18
|
+
opts.on('-r', '--require MODULE', 'Require the module before starting the compile server.') do |m|
|
19
|
+
modules_to_require << m
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on('-I', '--include DIR', 'Append a load path (may be used more than once)') do |i|
|
23
|
+
$:.unshift(File.expand_path(i))
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on('-d', '--dynamic-require-severity SEVERITY', 'Compiler option, one of: error, warning, ignore.') do |s|
|
27
|
+
if %w[error warning ignore].include?(s)
|
28
|
+
compiler_options[:dynamic_require_severity] = s.to_sym
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on('-t', '--true FLAG', 'Set compiler flag to true.' ) do |f|
|
33
|
+
compiler_options[f.to_sym] = true
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on('-f', '--false FLAG', 'Set compiler flag to false.' ) do |f|
|
37
|
+
compiler_options[f.to_sym] = false
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on('-l', '--load-paths-cache PATH', 'Path to load path cache json') do |l|
|
41
|
+
compile_server_options[:load_paths_cache] = l
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on('-p', '--pipe-name PATH', 'Name of the named pipe.') do |p|
|
45
|
+
compile_server_options[:pipe_name] = p
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on('-m', '--memcached URL', 'URL of memcached server. Will enable use of memcached for caching compiler results.') do |m|
|
49
|
+
compile_server_options[:memcached] = m
|
50
|
+
require 'dalli'
|
51
|
+
cache = Dalli::Client.new(compile_server_options[:memchached])
|
52
|
+
end
|
53
|
+
|
54
|
+
opts.on('-e', '--redis URL', 'URL of redis server. Will enable use of redis for caching compiler results.') do |e|
|
55
|
+
compile_server_options[:redis] = e
|
56
|
+
require 'redis'
|
57
|
+
cache = Redis.new(url: compile_server_options[:redis])
|
58
|
+
end
|
59
|
+
end.parse!
|
60
|
+
|
61
|
+
compiler_options.merge!(es6_modules: true)
|
62
|
+
|
63
|
+
modules_to_require.each do |mod|
|
64
|
+
require mod
|
65
|
+
end
|
66
|
+
|
67
|
+
def compile(request, cache, compiler_options)
|
68
|
+
begin
|
69
|
+
request_json = Oj.load(request.chop!, mode: :strict)
|
70
|
+
|
71
|
+
compile_source_map = request_json["source_map"]
|
72
|
+
filename = request_json["filename"]
|
73
|
+
source = File.read(filename)
|
74
|
+
if cache
|
75
|
+
source_digest = Digest::SHA1.hexdigest(source)
|
76
|
+
key = "owl_#{compiler_options_digest}_#{source_digest}_#{compile_source_map}"
|
77
|
+
result_json = cache.get(key)
|
78
|
+
return result_json if result_json
|
79
|
+
end
|
80
|
+
c = Opal::Compiler.new(source, compiler_options.merge(file: filename))
|
81
|
+
result = { 'javascript' => c.compile }
|
82
|
+
if compile_source_map
|
83
|
+
result['source_map'] = c.source_map.as_json
|
84
|
+
result['source_map']['file'] = filename
|
85
|
+
end
|
86
|
+
result['required_trees'] = c.required_trees
|
87
|
+
result_json = Oj.dump(result, mode: :strict)
|
88
|
+
cache.set(key, result_json) if cache
|
89
|
+
result_json
|
90
|
+
rescue Exception => e
|
91
|
+
Oj.dump({ 'error' => { 'name' => e.class.to_s, 'message' => e.message, 'backtrace' => e.backtrace.join("\n") } }, mode: :strict)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
if ARGV[0] == 'start'
|
96
|
+
number_of_instances = ARGV[1].to_i
|
97
|
+
number_of_instances == 4 if number_of_instances == 0
|
98
|
+
else
|
99
|
+
raise 'arguments must be either "stop" or "start number_of_instances"'
|
100
|
+
exit(1)
|
101
|
+
end
|
102
|
+
|
103
|
+
load_paths = OpalWebpackLoader::LoadPathManager.read_load_paths_cache(compile_server_options[:load_paths_cache])
|
104
|
+
if load_paths
|
105
|
+
Opal.append_paths(*load_paths)
|
106
|
+
end
|
107
|
+
|
108
|
+
begin
|
109
|
+
OpalWebpackLoader::PipeServer.new(compile_server_options[:pipe_name], number_of_instances) do |request|
|
110
|
+
if request
|
111
|
+
if request.start_with?('command:stop')
|
112
|
+
STDERR.puts "Exiting with drama."
|
113
|
+
exit(0)
|
114
|
+
else
|
115
|
+
result = compile(request, cache, compiler_options)
|
116
|
+
result
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end.run
|
120
|
+
rescue Exception => e
|
121
|
+
STDERR.puts "opal-webpack-windows-compile-server couldn't start:"
|
122
|
+
STDERR.puts e.backtrace.join("\n")
|
123
|
+
STDERR.puts e.message
|
124
|
+
exit 1
|
125
|
+
end
|
data/bin/owl-compiler
CHANGED
@@ -80,7 +80,10 @@ begin
|
|
80
80
|
source_digest = Digest::SHA1.hexdigest(source)
|
81
81
|
key = "owl_#{compiler_options_digest}_#{source_digest}_#{compile_source_map}"
|
82
82
|
result_json = compile_server_options[:cache].get(key)
|
83
|
-
|
83
|
+
if result_json
|
84
|
+
STDOUT.puts result_json
|
85
|
+
exit 0
|
86
|
+
end
|
84
87
|
end
|
85
88
|
c = Opal::Compiler.new(source, compiler_options)
|
86
89
|
result = { 'javascript' => c.compile }
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module OpalWebpackLoader
|
4
|
+
module WindowsyThings
|
5
|
+
extend FFI::Library
|
6
|
+
|
7
|
+
ffi_lib :kernel32, :user32
|
8
|
+
|
9
|
+
ERROR_IO_PENDING = 997
|
10
|
+
ERROR_PIPE_CONNECTED = 535
|
11
|
+
ERROR_SUCCESS = 0
|
12
|
+
|
13
|
+
FILE_FLAG_OVERLAPPED = 0x40000000
|
14
|
+
|
15
|
+
INFINITE = 0xFFFFFFFF
|
16
|
+
INVALID_HANDLE_VALUE = FFI::Pointer.new(-1).address
|
17
|
+
|
18
|
+
PIPE_ACCESS_DUPLEX = 0x00000003
|
19
|
+
PIPE_READMODE_BYTE = 0x00000000
|
20
|
+
PIPE_READMODE_MESSAGE = 0x00000002
|
21
|
+
PIPE_TYPE_BYTE = 0x00000000
|
22
|
+
PIPE_TYPE_MESSAGE = 0x00000004
|
23
|
+
PIPE_WAIT = 0x00000000
|
24
|
+
|
25
|
+
QS_ALLINPUT = 0x04FF
|
26
|
+
|
27
|
+
typedef :uintptr_t, :handle
|
28
|
+
|
29
|
+
attach_function :ConnectNamedPipe, [:handle, :pointer], :ulong
|
30
|
+
attach_function :CreateEvent, :CreateEventA, [:pointer, :ulong, :ulong, :string], :handle
|
31
|
+
attach_function :CreateNamedPipe, :CreateNamedPipeA, [:string, :ulong, :ulong, :ulong, :ulong, :ulong, :ulong, :pointer], :handle
|
32
|
+
attach_function :DisconnectNamedPipe, [:handle], :bool
|
33
|
+
attach_function :FlushFileBuffers, [:handle], :bool
|
34
|
+
attach_function :GetLastError, [], :ulong
|
35
|
+
attach_function :GetOverlappedResult, [:handle, :pointer, :pointer, :bool], :bool
|
36
|
+
attach_function :MsgWaitForMultipleObjects, [:ulong, :pointer, :ulong, :ulong, :ulong], :ulong
|
37
|
+
attach_function :ReadFile, [:handle, :buffer_out, :ulong, :pointer, :pointer], :bool
|
38
|
+
attach_function :SetEvent, [:handle], :bool
|
39
|
+
attach_function :WaitForMultipleObjects, [:ulong, :pointer, :ulong, :ulong], :ulong
|
40
|
+
attach_function :WriteFile, [:handle, :buffer_in, :ulong, :pointer, :pointer], :bool
|
41
|
+
end
|
42
|
+
|
43
|
+
class PipeServer
|
44
|
+
include OpalWebpackLoader::WindowsyThings
|
45
|
+
|
46
|
+
CONNECTING_STATE = 0
|
47
|
+
READING_STATE = 1
|
48
|
+
WRITING_STATE = 2
|
49
|
+
INSTANCES = 4
|
50
|
+
PIPE_TIMEOUT = 5000
|
51
|
+
BUFFER_SIZE = 65536
|
52
|
+
|
53
|
+
class Overlapped < FFI::Struct
|
54
|
+
layout(
|
55
|
+
:Internal, :uintptr_t,
|
56
|
+
:InternalHigh, :uintptr_t,
|
57
|
+
:Offset, :ulong,
|
58
|
+
:OffsetHigh, :ulong,
|
59
|
+
:hEvent, :uintptr_t
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def initialize(pipe_name, instances = 4, &block)
|
64
|
+
@run_block = block
|
65
|
+
@full_pipe_name = "\\\\.\\pipe\\#{pipe_name}"
|
66
|
+
@instances = instances
|
67
|
+
@events = []
|
68
|
+
@events_pointer = FFI::MemoryPointer.new(:uintptr_t, @instances)
|
69
|
+
@pipes = []
|
70
|
+
end
|
71
|
+
|
72
|
+
def run
|
73
|
+
create_instances
|
74
|
+
while_loop
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def create_instances
|
80
|
+
(0...@instances).each do |i|
|
81
|
+
@events[i] = CreateEvent(nil, 1, 1, nil)
|
82
|
+
raise "CreateEvent failed with #{GetLastError()}" unless @events[i]
|
83
|
+
|
84
|
+
overlap = Overlapped.new
|
85
|
+
overlap[:hEvent] = @events[i]
|
86
|
+
|
87
|
+
@pipes[i] = { overlap: overlap, instance: nil, request: FFI::Buffer.new(1, BUFFER_SIZE), bytes_read: 0, reply: FFI::Buffer.new(1, BUFFER_SIZE), bytes_to_write: 0, state: nil, pending_io: false }
|
88
|
+
@pipes[i][:instance] = CreateNamedPipe(@full_pipe_name,
|
89
|
+
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
90
|
+
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
91
|
+
@instances,
|
92
|
+
BUFFER_SIZE,
|
93
|
+
BUFFER_SIZE,
|
94
|
+
PIPE_TIMEOUT,
|
95
|
+
nil)
|
96
|
+
|
97
|
+
raise "CreateNamedPipe failed with #{GetLastError()}" if @pipes[i][:instance] == INVALID_HANDLE_VALUE
|
98
|
+
@pipes[i][:pending_io] = connect_to_new_client(i)
|
99
|
+
@pipes[i][:state] = @pipes[i][:pending_io] ? CONNECTING_STATE : READING_STATE
|
100
|
+
end
|
101
|
+
@events_pointer.write_array_of_ulong_long(@events)
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
def while_loop
|
106
|
+
while true
|
107
|
+
i = MsgWaitForMultipleObjects(@instances, @events_pointer, 0, INFINITE, QS_ALLINPUT)
|
108
|
+
# Having this STDOUT.putc is essential, otherwise there is a tendency to block within MsgWaitForMultipleObjects ...
|
109
|
+
STDOUT.putc "."
|
110
|
+
# ... because the ruby interpreter is waiting for objects too on Windows. Thats why we wait for QS_ALLINPUT and
|
111
|
+
# with STDOUT.putc give back control to the ruby interpreter that it can handle its things.
|
112
|
+
if i < 0 || i > (@instances - 1)
|
113
|
+
STDERR.puts "Pipe index out of range. Maybe a error occured."
|
114
|
+
next
|
115
|
+
end
|
116
|
+
|
117
|
+
if @pipes[i][:pending_io]
|
118
|
+
bytes_transferred = FFI::MemoryPointer.new(:ulong)
|
119
|
+
success = GetOverlappedResult(@pipes[i][:instance], @pipes[i][:overlap], bytes_transferred, false)
|
120
|
+
|
121
|
+
case @pipes[i][:state]
|
122
|
+
when CONNECTING_STATE
|
123
|
+
raise "Error #{GetLastError()}" unless success
|
124
|
+
@pipes[i][:state] = READING_STATE
|
125
|
+
when READING_STATE
|
126
|
+
if !success || bytes_transferred.read_ulong == 0
|
127
|
+
disconnect_and_reconnect(i)
|
128
|
+
next
|
129
|
+
else
|
130
|
+
@pipes[i][:bytes_read] = bytes_transferred.read_ulong
|
131
|
+
@pipes[i][:state] = WRITING_STATE
|
132
|
+
end
|
133
|
+
when WRITING_STATE
|
134
|
+
if !success || bytes_transferred.read_ulong != @pipes[i][:bytes_to_write]
|
135
|
+
disconnect_and_reconnect(i)
|
136
|
+
next
|
137
|
+
else
|
138
|
+
@pipes[i][:state] = READING_STATE
|
139
|
+
end
|
140
|
+
else
|
141
|
+
raise "Invalid pipe state."
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
case @pipes[i][:state]
|
146
|
+
when READING_STATE
|
147
|
+
bytes_read = FFI::MemoryPointer.new(:ulong)
|
148
|
+
success = ReadFile(@pipes[i][:instance], @pipes[i][:request], BUFFER_SIZE, bytes_read, @pipes[i][:overlap].to_ptr)
|
149
|
+
if success && bytes_read.read_ulong != 0
|
150
|
+
@pipes[i][:pending_io] = false
|
151
|
+
@pipes[i][:state] = WRITING_STATE
|
152
|
+
next
|
153
|
+
end
|
154
|
+
|
155
|
+
err = GetLastError()
|
156
|
+
if !success && err == ERROR_IO_PENDING
|
157
|
+
@pipes[i][:pending_io] = true
|
158
|
+
next
|
159
|
+
end
|
160
|
+
|
161
|
+
disconnect_and_reconnect(i)
|
162
|
+
when WRITING_STATE
|
163
|
+
@pipes[i][:reply] = @run_block.call(@pipes[i][:request].get_string(0))
|
164
|
+
@pipes[i][:bytes_to_write] = @pipes[i][:reply].bytesize
|
165
|
+
bytes_written = FFI::MemoryPointer.new(:ulong)
|
166
|
+
success = WriteFile(@pipes[i][:instance], @pipes[i][:reply], @pipes[i][:bytes_to_write], bytes_written, @pipes[i][:overlap].to_ptr)
|
167
|
+
|
168
|
+
if success && bytes_written.read_ulong == @pipes[i][:bytes_to_write]
|
169
|
+
@pipes[i][:pending_io] = false
|
170
|
+
@pipes[i][:state] = READING_STATE
|
171
|
+
next
|
172
|
+
end
|
173
|
+
|
174
|
+
err = GetLastError()
|
175
|
+
|
176
|
+
if !success && err == ERROR_IO_PENDING
|
177
|
+
@pipes[i][:pending_io] = true
|
178
|
+
next
|
179
|
+
end
|
180
|
+
|
181
|
+
disconnect_and_reconnect(i)
|
182
|
+
else
|
183
|
+
raise "Invalid pipe state."
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def disconnect_and_reconnect(i)
|
189
|
+
FlushFileBuffers(@pipes[i][:instance])
|
190
|
+
STDERR.puts("DisconnectNamedPipe failed with #{GetLastError()}") if !DisconnectNamedPipe(@pipes[i][:instance])
|
191
|
+
|
192
|
+
@pipes[i][:pending_io] = connect_to_new_client(i)
|
193
|
+
|
194
|
+
@pipes[i][:state] = @pipes[i][:pending_io] ? CONNECTING_STATE : READING_STATE
|
195
|
+
end
|
196
|
+
|
197
|
+
def connect_to_new_client(i)
|
198
|
+
pending_io = false
|
199
|
+
@pipes[i][:request].clear
|
200
|
+
@pipes[i][:reply].clear
|
201
|
+
connected = ConnectNamedPipe(@pipes[i][:instance], @pipes[i][:overlap].to_ptr)
|
202
|
+
last_error = GetLastError()
|
203
|
+
raise "ConnectNamedPipe failed with #{last_error} - #{connected}" if connected != 0
|
204
|
+
|
205
|
+
case last_error
|
206
|
+
when ERROR_IO_PENDING
|
207
|
+
pending_io = true
|
208
|
+
when ERROR_PIPE_CONNECTED
|
209
|
+
SetEvent(@pipes[i][:overlap][:hEvent])
|
210
|
+
when ERROR_SUCCESS
|
211
|
+
pending_io = true
|
212
|
+
else
|
213
|
+
raise "ConnectNamedPipe failed with error #{last_error}"
|
214
|
+
end
|
215
|
+
|
216
|
+
pending_io
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opal-webpack-loader
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Biedermann
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-05-
|
11
|
+
date: 2021-05-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: opal
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 2.7.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: ffi
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.15.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.15.0
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: oj
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -201,12 +215,14 @@ executables:
|
|
201
215
|
- owl-install
|
202
216
|
- owl-gen-loadpath-cache
|
203
217
|
- owl-compiler
|
218
|
+
- opal-webpack-windows-compile-server
|
204
219
|
extensions: []
|
205
220
|
extra_rdoc_files: []
|
206
221
|
files:
|
207
222
|
- LICENSE
|
208
223
|
- README.md
|
209
224
|
- bin/opal-webpack-compile-server
|
225
|
+
- bin/opal-webpack-windows-compile-server
|
210
226
|
- bin/owl-compiler
|
211
227
|
- bin/owl-gen-loadpath-cache
|
212
228
|
- bin/owl-install
|
@@ -216,6 +232,7 @@ files:
|
|
216
232
|
- lib/opal-webpack-loader/installer_cli.rb
|
217
233
|
- lib/opal-webpack-loader/load_path_manager.rb
|
218
234
|
- lib/opal-webpack-loader/manifest.rb
|
235
|
+
- lib/opal-webpack-loader/pipe_server.rb
|
219
236
|
- lib/opal-webpack-loader/rails_view_helper.rb
|
220
237
|
- lib/opal-webpack-loader/templates/Procfile.erb
|
221
238
|
- lib/opal-webpack-loader/templates/app_loader.rb.erb
|