opal-webpack-loader 0.10.3 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf29108044f8d61b4587c76a1d92e2e0a9722d69e854dd4fe469f47244ea7b24
4
- data.tar.gz: e14eba64f9741db698fa3fef0b19ec830e7a4463bbb50898b569fce3d34ca30a
3
+ metadata.gz: 75cfc2e9527b252335ad359e1eb5c7aa476cec879ed76c4501b686a50f39b1f2
4
+ data.tar.gz: b6d13eac5c5eaa0e3c46dfd15b8f59d01d59124f92608c741e93952c87bb04f6
5
5
  SHA512:
6
- metadata.gz: 033b4afb0191e0b9c69fc6f21a0c5419b0e729bce1ca3b50d4a39272a51111a01c9eaaebda566dc9d587725c0708bcf7717ccc716d670c9fc9828038ec725f89
7
- data.tar.gz: 884d3f0491fd4c81bd434ef37cf541329af99e0e6a9e6b38171fb8d7e1611cd605732536dfa72babe4620d5ac327e3a183c17d4506813067e93324521ed95083
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
- - parallel builds working on Windows too, but not as fast as on Linux
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
- STDOUT.puts result_json if result_json
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
@@ -1,3 +1,3 @@
1
1
  module OpalWebpackLoader
2
- VERSION="0.10.3"
2
+ VERSION="0.11.0"
3
3
  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.10.3
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-17 00:00:00.000000000 Z
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