opal-webpack-loader 0.11.2 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/owl-compiler CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'digest'
3
3
  require 'oj'
4
- require 'c_lexer'
5
4
  require 'optparse'
6
5
  require 'opal/paths'
7
6
  require 'opal/source_map'
@@ -73,19 +72,23 @@ compile_source_map = compile_server_options[:source_map]
73
72
  filename = compile_server_options[:source_file]
74
73
  source = File.read(filename)
75
74
  compiler_options.merge!(es6_modules: true)
76
- compiler_options.merge!(file: filename)
75
+
76
+ def compiler_options_digest(compiler_options)
77
+ @compiler_options_digest ||= Digest::SHA1.hexdigest(Oj.dump(compiler_options, mode: :strict))
78
+ end
77
79
 
78
80
  begin
79
81
  if compile_server_options[:cache]
80
82
  source_digest = Digest::SHA1.hexdigest(source)
81
- key = "owl_#{compiler_options_digest}_#{source_digest}_#{compile_source_map}"
83
+ key = "owl_#{compiler_options_digest(compiler_options)}_#{source_digest}_#{compile_source_map}"
82
84
  result_json = compile_server_options[:cache].get(key)
83
85
  if result_json
84
86
  STDOUT.puts result_json
85
87
  exit 0
86
88
  end
87
89
  end
88
- c = Opal::Compiler.new(source, compiler_options)
90
+
91
+ c = Opal::Compiler.new(source, compiler_options.merge(file: filename))
89
92
  result = { 'javascript' => c.compile }
90
93
  if compile_source_map
91
94
  result['source_map'] = c.source_map.as_json
@@ -1,415 +1,415 @@
1
- module OpalWebpackLoader
2
- module Installer
3
- TARGETS = %w[development.js production.js test.js]
4
- DEV_START_COMMAND = 'yarn run development'
5
- DEV_SM_START_COMMAND = 'yarn run sourcemaps'
6
-
7
- class CLI < Thor
8
- desc "iso", "Install owl configuration into a existing isomorfeus project, meant to be called from the isomorfeus installer only."
9
- # Showing directories and files relevant to iso owl:
10
- # project_root
11
- # +- config.ru # rack app starter
12
- # +- config # webpack and other config
13
- # +- owl.rb # owl.configuration
14
- # +- isomorfeus
15
- # +- components
16
- # +- imports # javascript entrypoints, import other components etc.
17
- # +- application.js
18
- # +- application_common.js
19
- # +- application_ssr.js
20
- # +- isomorfeus_loader.rb
21
- # +- models
22
- # +- operations
23
- # +- policies
24
- # +- styles
25
- # +- css
26
- # +- log
27
- # +- node_modules
28
- # +- package.json
29
- # +- Procfile
30
- # +- public
31
- # +- assets # compiled assets and other things
32
- # +- spec
33
- # +- tmp
34
- def iso
35
- @application_css = '../styles/application.css'
36
- @asset_output_directory = File.join('public', 'assets')
37
- @js_entrypoints_directory = File.join('isomorfeus', 'imports')
38
- @conf_rel_prefix = '..'
39
- @js_rel_prefix = '..'
40
- @opal_directory = 'isomorfeus'
41
- @styles_directory = File.join(@opal_directory, 'styles')
42
- @webpack_config_directory = 'webpack'
43
- @dev_default_targets = 'browser'
44
- @pro_default_targets = 'browser, ssr'
45
- @hmr_hook = 'Opal.Isomorfeus.$force_render()'
46
- create_directory(@webpack_config_directory)
47
- create_directory(@asset_output_directory)
48
- FileUtils.touch(File.join(@asset_output_directory, '.keep'))
49
- install_webpack_config
50
- create_file_from_template('initializer.rb.erb', File.join('owl_init.rb'), { opal_load_path: '' })
51
- # add_gem
52
- end
53
-
54
- desc "flat", "Install owl configuration into a existing project with a flat structure, execute from the projects root directory."
55
- # <<~TEXT
56
- # Showing directories and files relevant to owl:
57
- # project_root
58
- # +- owl_init.rb # initializer for owl
59
- # +- javascripts # javascript entries directory
60
- # +- node_modules # directory for node modules
61
- # +- opal # directory for opal application files, can be changed with -o
62
- # +- package.json # package config for npm/yarn and their scripts
63
- # +- public
64
- # +- assets # directory for compiled output files
65
- # +- styles # directory for stylesheets
66
- # +- webpack # directory for webpack configuration files
67
- # +- Procfile # config file for foreman
68
- #
69
- # TEXT
70
- option :opal_name, required: false, type: :string, default: 'opal', aliases: '-o', desc: <<~TEXT
71
- Set directory name for Opal source files.
72
- Example: owl-installer rails -o isomorfeus # will use project_root/app/isomorfeus for opal files
73
- TEXT
74
-
75
- def flat
76
- @application_css = '../styles/application.css'
77
- @asset_output_directory = File.join('public', 'assets')
78
- @js_entrypoints_directory = File.join('javascripts')
79
- @conf_rel_prefix = '..'
80
- @js_rel_prefix = '..'
81
- @opal_directory = options[:opal_name]
82
- @styles_directory = 'styles'
83
- @webpack_config_directory = 'webpack'
84
- @dev_default_targets = 'browser'
85
- @pro_default_targets = 'browser'
86
- @hmr_hook = ''
87
- create_common_directories
88
- install_common_things
89
- create_file_from_template('application.css.erb', File.join('styles', 'application.css'), {})
90
- create_file_from_template('initializer.rb.erb', 'owl_init.rb', { opal_load_path: '' })
91
- create_file_from_template('app_loader.rb.erb', 'app_loader.rb', { opal_directory: @opal_directory })
92
- add_gem
93
- print_message
94
- end
95
-
96
- desc "rails", "Install owl configuration into a existing rails project without webpacker, execute from the projects root directory."
97
- # <<~TEXT
98
- # Showing directories and files relevant to owl:
99
- # project_root
100
- # +- app
101
- # +- assets
102
- # +- javascripts # javascript entries directory
103
- # +- styles # directory for stylesheets
104
- # +- opal # directory for opal application files, can be changed with -o
105
- # +- config
106
- # +- webpack # directory for webpack configuration files
107
- # +- initializers
108
- # +- owl.rb # initializer for owl
109
- # +- node_modules # directory for node modules
110
- # +- package.json # package config for npm/yarn and their scripts
111
- # +- public
112
- # +- assets # directory for compiled output files
113
- # +- Procfile # config file for foreman
114
- #
115
- # TEXT
116
- option :opal_name, required: false, type: :string, default: 'opal', aliases: '-o', desc: <<~TEXT
117
- Set directory name for Opal source files.
118
- Example: owl-installer rails -o isomorfeus # will use project_root/app/isomorfeus for opal files
119
- TEXT
120
-
121
- def rails
122
- @application_css = '../stylesheets/application.css'
123
- @asset_output_directory = File.join('public', 'assets')
124
- @js_entrypoints_directory = File.join('app', 'assets', 'javascripts')
125
- @conf_rel_prefix = File.join('..', '..')
126
- @js_rel_prefix = File.join('..', '..', '..')
127
- @opal_directory = File.join('app', options[:opal_name])
128
- @styles_directory = File.join('app', 'assets', 'stylesheets')
129
- @webpack_config_directory = File.join('config', 'webpack')
130
- @dev_default_targets = 'browser'
131
- @pro_default_targets = 'browser'
132
- @hmr_hook = ''
133
- create_directory('app')
134
- create_common_directories
135
- install_common_things
136
- create_file_from_template('initializer.rb.erb', File.join('config', 'initializers', 'opal_webpack_loader.rb'),
137
- { opal_load_path: "Opal.append_path(File.realdirpath('#{@opal_directory}'))" })
138
- add_gem
139
- print_message
140
- end
141
-
142
- desc "webpacker", "Install owl configuration into a existing rails project with webpacker, execute from the projects root directory."
143
- # <<~TEXT
144
- # Showing directories and files relevant to owl:
145
- # project_root
146
- # +- app
147
- # +- assets
148
- # +- javascripts # javascript entries directory
149
- # +- styles # directory for stylesheets
150
- # +- opal # directory for opal application files, can be changed with -o
151
- # +- config
152
- # +- webpack # directory for webpack configuration files
153
- # +- initializers
154
- # +- owl.rb # initializer for owl
155
- # +- node_modules # directory for node modules
156
- # +- package.json # package config for npm/yarn and their scripts
157
- # +- public
158
- # +- assets # directory for compiled output files
159
- # +- Procfile # config file for foreman
160
- #
161
- # TEXT
162
- option :opal_name, required: false, type: :string, default: 'opal', aliases: '-o', desc: <<~TEXT
163
- Set directory name for Opal source files.
164
- Example: owl-installer rails -o isomorfeus # will use project_root/app/isomorfeus for opal files
165
- TEXT
166
-
167
- def webpacker
168
- @application_css = '../stylesheets/application.css'
169
- @asset_output_directory = File.join('public', 'assets')
170
- @js_entrypoints_directory = File.join('app', 'assets', 'javascripts')
171
- @conf_rel_prefix = File.join('..', '..')
172
- @js_rel_prefix = File.join('..', '..', '..')
173
- @opal_directory = File.join('app', options[:opal_name])
174
- @styles_directory = File.join('app', 'assets', 'stylesheets')
175
- @webpack_config_directory = File.join('config', 'webpack')
176
- @dev_default_targets = 'browser'
177
- @pro_default_targets = 'browser'
178
- @hmr_hook = ''
179
- create_directory('app')
180
- create_common_directories
181
- install_webpacker_config
182
- install_webpacker_package_json
183
- install_webpacker_js_entry
184
- install_opal_entries
185
- create_file_from_template('initializer.rb.erb', File.join('config', 'initializers', 'opal_webpack_loader.rb'),
186
- { opal_load_path: "Opal.append_path(File.realdirpath('#{@opal_directory}'))" })
187
- add_gem
188
- print_message
189
- end
190
-
191
- private
192
-
193
- def create_directory(directory)
194
- unless Dir.exist?(directory)
195
- puts "Creating directory #{directory}."
196
- FileUtils.mkdir_p(directory)
197
- end
198
- end
199
-
200
- def create_common_directories
201
- create_directory(@webpack_config_directory)
202
- create_directory(@js_entrypoints_directory)
203
- create_directory(@opal_directory)
204
- create_directory(@asset_output_directory)
205
- FileUtils.touch(File.join(@asset_output_directory, '.gitkeep'))
206
- create_directory(@styles_directory)
207
- end
208
-
209
- def install_common_things
210
- install_webpack_config
211
- install_package_json
212
- install_js_entries
213
- install_opal_entries
214
- install_procfile
215
- end
216
-
217
- def install_js_entries
218
- erb_hash = { opal_dir: File.join(@js_rel_prefix, @opal_directory), opal_name: options[:opal_name], application_css: @application_css }
219
- create_file_from_template('application.js.erb', File.join(@js_entrypoints_directory, 'application.js'), erb_hash)
220
- create_file_from_template('application_common.js.erb', File.join(@js_entrypoints_directory, 'application_common.js'),erb_hash)
221
- create_file_from_template('application_ssr.js.erb', File.join(@js_entrypoints_directory, 'application_ssr.js'), erb_hash)
222
- create_file_from_template('application_web_worker.js.erb', File.join(@js_entrypoints_directory, 'application_web_worker.js'), erb_hash)
223
- end
224
-
225
- def install_opal_entries
226
- create_file_from_template('opal_loader.rb.erb', File.join(@opal_directory, "#{options[:opal_name]}_loader.rb"), {})
227
- create_file_from_template('opal_web_worker_loader.rb.erb', File.join(@opal_directory, "#{options[:opal_name]}_web_worker_loader.rb"), {})
228
- end
229
-
230
- def install_package_json
231
- if File.exist?('package.json')
232
- gem_package_json_template = File.read(File.join(templates_path, 'package.json.erb'))
233
- gem_package_json = Oj.load(gem_package_json_template, mode: :strict)
234
- package_json_file = File.read('package.json')
235
- package_json = Oj.load(package_json_file, mode: :strict)
236
- package_json["scripts"] = {} unless package_json.has_key?("scripts")
237
- package_json["scripts"]["debug"] = debug_script
238
- package_json["scripts"]["development"] = development_script
239
- package_json["scripts"]["production_build"] = production_script
240
- package_json["devDependencies"] = {} unless package_json.has_key?("devDependencies")
241
- package_json["devDependencies"].merge!(gem_package_json["devDependencies"])
242
- package_json["dependencies"]["opal-webpack-loader"] = "^#{OpalWebpackLoader::VERSION}"
243
- File.write('package.json', Oj.dump(package_json, mode: :strict, indent: 2))
244
- puts "Updated package.json, updated scripts and owl dependencies"
245
- else
246
- erb_hash = {
247
- debug_script: debug_script,
248
- development_script: development_script,
249
- production_script: production_script,
250
- owl_version: OpalWebpackLoader::VERSION
251
- }
252
- create_file_from_template('package.json.erb', 'package.json', erb_hash)
253
- end
254
- end
255
-
256
- def install_procfile
257
- if File.exist?('Procfile')
258
- procfile = File.read('Procfile')
259
- has_webpack = false
260
- lines = procfile.lines
261
- lines.each do |line|
262
- has_webpack = true if line.start_with?('webpack')
263
- end
264
- unless has_webpack
265
- lines << 'webpack: yarn run development'
266
- File.write('Procfile', lines.join("\n"))
267
- end
268
- else
269
- erb_hash = { web_starter: '' }
270
- if @js_entrypoints_directory.start_with?('app')
271
- erb_hash[:web_starter] = 'web: bundle exec rails s'
272
- end
273
- create_file_from_template('Procfile.erb', 'Procfile',erb_hash)
274
- end
275
- end
276
-
277
- def debug_script
278
- "webpack-dev-server --config #{File.join(@webpack_config_directory, 'debug.js')}"
279
- end
280
-
281
- def development_script
282
- "webpack-dev-server --config #{File.join(@webpack_config_directory, 'development.js')}"
283
- end
284
-
285
- def production_script
286
- "parallel-webpack --config=#{File.join(@webpack_config_directory, 'production.js')}"
287
- end
288
-
289
- def install_webpack_config
290
- erb_hash = {
291
- asset_output_directory: File.join(@conf_rel_prefix, @asset_output_directory),
292
- default_targets: @dev_default_targets,
293
- js_entry: File.join(@conf_rel_prefix, @js_entrypoints_directory, 'application.js'),
294
- js_common_entry: File.join(@conf_rel_prefix, @js_entrypoints_directory, 'application_common.js'),
295
- js_ssr_entry: File.join(@conf_rel_prefix, @js_entrypoints_directory, 'application_ssr.js'),
296
- js_web_worker_entry: File.join(@conf_rel_prefix, @js_entrypoints_directory, 'application_web_worker.js'),
297
- opal_directory: File.join(@conf_rel_prefix, @opal_directory),
298
- stylesheets_directory: File.join(@conf_rel_prefix, @styles_directory),
299
- hmr_hook: @hmr_hook
300
- }
301
- if @js_entrypoints_directory.start_with?('app')
302
- erb_hash[:dev_server_before] = <<~JAVASCRIPT
303
- // enable page reload for updates within the app/views directory
304
- before: function(app, server) {
305
- chokidar.watch(path.resolve(__dirname, path.join('..', '..', 'app', 'views'))).on('all', function () {
306
- server.sockWrite(server.sockets, 'content-changed');
307
- })
308
- },
309
- JAVASCRIPT
310
- else
311
- erb_hash[:dev_server_before] = <<~JAVASCRIPT
312
- // uncomment to enable page reload for updates within another directory, which may contain just html files,
313
- // for example the 'views' directory:
314
- // before: function(app, server) {
315
- // chokidar.watch(path.resolve(__dirname, path.join('..', 'views')).on('all', function () {
316
- // server.sockWrite(server.sockets, 'content-changed');
317
- // })
318
- // },
319
- JAVASCRIPT
320
- end
321
- create_file_from_template('debug.js.erb', File.join(@webpack_config_directory, 'debug.js'), erb_hash)
322
- create_file_from_template('development.js.erb', File.join(@webpack_config_directory, 'development.js'), erb_hash)
323
- erb_hash[:default_targets] = @pro_default_targets
324
- create_file_from_template('production.js.erb', File.join(@webpack_config_directory, 'production.js'), erb_hash)
325
- end
326
-
327
- def install_webpacker_config
328
- environment_js = File.read(File.join('config', 'webpack', 'environment.js'), mode: 'r')
329
- new_environment_js = ''
330
- environment_js.lines.each do |line|
331
- new_environment_js << line
332
- if line.start_with?('const { environment }')
333
- new_environment_js << "\n"
334
- new_environment_js << File.read(File.join(templates_path, 'webpacker.js'), mode: 'r')
335
- new_environment_js << "\n"
336
- end
337
- end
338
- File.write(File.join('config', 'webpack', 'environment.js'), new_environment_js)
339
- end
340
-
341
- def install_webpacker_js_entry
342
- application_js = File.read(File.join('app', 'javascript', 'packs', 'application.js'), mode: 'r')
343
- application_js << <<~JAVASCRIPT
344
-
345
- // import and load opal ruby files
346
- import init_app from '#{options[:opal_name]}_loader.rb';
347
- init_app();
348
- Opal.load('#{options[:opal_name]}_loader');
349
-
350
- JAVASCRIPT
351
- File.write(File.join('app', 'javascript', 'packs', 'application.js'), application_js)
352
- end
353
-
354
- def install_webpacker_package_json
355
- package_json_file = File.read('package.json')
356
- package_json = Oj.load(package_json_file, mode: :strict)
357
- package_json["dependencies"]["opal-webpack-loader"] = "^#{OpalWebpackLoader::VERSION}"
358
- File.write('package.json', Oj.dump(package_json, mode: :strict, indent: 2))
359
- puts "Updated package.json for opal-webpack-loader"
360
- end
361
-
362
- def templates_path
363
- File.realpath(File.join(File.dirname(File.realpath(__FILE__)), 'templates'))
364
- end
365
-
366
- def create_file_from_template(template_path, target_file_path, data_hash)
367
- template = ERB.new(File.read(File.join(templates_path, template_path), mode: 'r'))
368
- result = template.result_with_hash(data_hash)
369
- if File.exist?(target_file_path)
370
- ext = '_owl_new'
371
- puts "#{target_file_path} exists, not overwriting, creating #{target_file_path + ext} instead."
372
- else
373
- ext = ''
374
- puts "Creating #{target_file_path}."
375
- end
376
-
377
- File.write(target_file_path + ext, result, mode: 'w')
378
- end
379
-
380
- def add_gem
381
- # TODO specifying a version results in bundler printing a error and not adding the gem
382
- # `bundle add opal-webpack-loader -v "#{OpalWebpackLoader::VERSION}" --skip-install`ß´
383
- end
384
-
385
- def print_message
386
- puts <<~TEXT
387
-
388
- Add the following lines to your Gemfile:
389
-
390
- gem 'opal', github: 'janbiedermann/opal', branch: 'es6_modules_1_1'
391
- gem 'opal-webpack-loader', '~> #{OpalWebpackLoader::VERSION}'
392
-
393
- owl currently works only with above opal branch. If you have a existing "gem 'opal'", please replace it with above line.
394
-
395
- Also check above output for files ending in '_owl_new' and merge their contents with the existing files.
396
-
397
- After that run according to your preference either:
398
-
399
- yarn install
400
-
401
- or:
402
-
403
- npm install
404
-
405
- and then:
406
-
407
- bundle install
408
-
409
- For further instructions see http://github.com/isomorfeus/opal-webpack-loader
410
-
411
- TEXT
412
- end
413
- end
414
- end
415
- end
1
+ module OpalWebpackLoader
2
+ module Installer
3
+ TARGETS = %w[development.js production.js test.js]
4
+ DEV_START_COMMAND = 'yarn run development'
5
+ DEV_SM_START_COMMAND = 'yarn run sourcemaps'
6
+
7
+ class CLI < Thor
8
+ desc "iso", "Install owl configuration into a existing isomorfeus project, meant to be called from the isomorfeus installer only."
9
+ # Showing directories and files relevant to iso owl:
10
+ # project_root
11
+ # +- config.ru # rack app starter
12
+ # +- config # webpack and other config
13
+ # +- owl.rb # owl.configuration
14
+ # +- isomorfeus
15
+ # +- components
16
+ # +- imports # javascript entrypoints, import other components etc.
17
+ # +- application.js
18
+ # +- application_common.js
19
+ # +- application_ssr.js
20
+ # +- isomorfeus_loader.rb
21
+ # +- models
22
+ # +- operations
23
+ # +- policies
24
+ # +- styles
25
+ # +- css
26
+ # +- log
27
+ # +- node_modules
28
+ # +- package.json
29
+ # +- Procfile
30
+ # +- public
31
+ # +- assets # compiled assets and other things
32
+ # +- spec
33
+ # +- tmp
34
+ def iso
35
+ @application_css = '../styles/application.css'
36
+ @asset_output_directory = File.join('public', 'assets')
37
+ @js_entrypoints_directory = File.join('isomorfeus', 'imports')
38
+ @conf_rel_prefix = '..'
39
+ @js_rel_prefix = '..'
40
+ @opal_directory = 'isomorfeus'
41
+ @styles_directory = File.join(@opal_directory, 'styles')
42
+ @webpack_config_directory = 'webpack'
43
+ @dev_default_targets = 'browser'
44
+ @pro_default_targets = 'browser, ssr'
45
+ @hmr_hook = 'Opal.Isomorfeus.$force_render()'
46
+ create_directory(@webpack_config_directory)
47
+ create_directory(@asset_output_directory)
48
+ FileUtils.touch(File.join(@asset_output_directory, '.keep'))
49
+ install_webpack_config
50
+ create_file_from_template('initializer.rb.erb', File.join('owl_init.rb'), { opal_load_path: '' })
51
+ # add_gem
52
+ end
53
+
54
+ desc "flat", "Install owl configuration into a existing project with a flat structure, execute from the projects root directory."
55
+ # <<~TEXT
56
+ # Showing directories and files relevant to owl:
57
+ # project_root
58
+ # +- owl_init.rb # initializer for owl
59
+ # +- javascripts # javascript entries directory
60
+ # +- node_modules # directory for node modules
61
+ # +- opal # directory for opal application files, can be changed with -o
62
+ # +- package.json # package config for npm/yarn and their scripts
63
+ # +- public
64
+ # +- assets # directory for compiled output files
65
+ # +- styles # directory for stylesheets
66
+ # +- webpack # directory for webpack configuration files
67
+ # +- Procfile # config file for foreman
68
+ #
69
+ # TEXT
70
+ option :opal_name, required: false, type: :string, default: 'opal', aliases: '-o', desc: <<~TEXT
71
+ Set directory name for Opal source files.
72
+ Example: owl-installer rails -o isomorfeus # will use project_root/app/isomorfeus for opal files
73
+ TEXT
74
+
75
+ def flat
76
+ @application_css = '../styles/application.css'
77
+ @asset_output_directory = File.join('public', 'assets')
78
+ @js_entrypoints_directory = File.join('javascripts')
79
+ @conf_rel_prefix = '..'
80
+ @js_rel_prefix = '..'
81
+ @opal_directory = options[:opal_name]
82
+ @styles_directory = 'styles'
83
+ @webpack_config_directory = 'webpack'
84
+ @dev_default_targets = 'browser'
85
+ @pro_default_targets = 'browser'
86
+ @hmr_hook = ''
87
+ create_common_directories
88
+ install_common_things
89
+ create_file_from_template('application.css.erb', File.join('styles', 'application.css'), {})
90
+ create_file_from_template('initializer.rb.erb', 'owl_init.rb', { opal_load_path: '' })
91
+ create_file_from_template('app_loader.rb.erb', 'app_loader.rb', { opal_directory: @opal_directory })
92
+ add_gem
93
+ print_message
94
+ end
95
+
96
+ desc "rails", "Install owl configuration into a existing rails project without webpacker, execute from the projects root directory."
97
+ # <<~TEXT
98
+ # Showing directories and files relevant to owl:
99
+ # project_root
100
+ # +- app
101
+ # +- assets
102
+ # +- javascripts # javascript entries directory
103
+ # +- styles # directory for stylesheets
104
+ # +- opal # directory for opal application files, can be changed with -o
105
+ # +- config
106
+ # +- webpack # directory for webpack configuration files
107
+ # +- initializers
108
+ # +- owl.rb # initializer for owl
109
+ # +- node_modules # directory for node modules
110
+ # +- package.json # package config for npm/yarn and their scripts
111
+ # +- public
112
+ # +- assets # directory for compiled output files
113
+ # +- Procfile # config file for foreman
114
+ #
115
+ # TEXT
116
+ option :opal_name, required: false, type: :string, default: 'opal', aliases: '-o', desc: <<~TEXT
117
+ Set directory name for Opal source files.
118
+ Example: owl-installer rails -o isomorfeus # will use project_root/app/isomorfeus for opal files
119
+ TEXT
120
+
121
+ def rails
122
+ @application_css = '../stylesheets/application.css'
123
+ @asset_output_directory = File.join('public', 'assets')
124
+ @js_entrypoints_directory = File.join('app', 'assets', 'javascripts')
125
+ @conf_rel_prefix = File.join('..', '..')
126
+ @js_rel_prefix = File.join('..', '..', '..')
127
+ @opal_directory = File.join('app', options[:opal_name])
128
+ @styles_directory = File.join('app', 'assets', 'stylesheets')
129
+ @webpack_config_directory = File.join('config', 'webpack')
130
+ @dev_default_targets = 'browser'
131
+ @pro_default_targets = 'browser'
132
+ @hmr_hook = ''
133
+ create_directory('app')
134
+ create_common_directories
135
+ install_common_things
136
+ create_file_from_template('initializer.rb.erb', File.join('config', 'initializers', 'opal_webpack_loader.rb'),
137
+ { opal_load_path: "Opal.append_path(File.realdirpath('#{@opal_directory}'))" })
138
+ add_gem
139
+ print_message
140
+ end
141
+
142
+ desc "webpacker", "Install owl configuration into a existing rails project with webpacker, execute from the projects root directory."
143
+ # <<~TEXT
144
+ # Showing directories and files relevant to owl:
145
+ # project_root
146
+ # +- app
147
+ # +- assets
148
+ # +- javascripts # javascript entries directory
149
+ # +- styles # directory for stylesheets
150
+ # +- opal # directory for opal application files, can be changed with -o
151
+ # +- config
152
+ # +- webpack # directory for webpack configuration files
153
+ # +- initializers
154
+ # +- owl.rb # initializer for owl
155
+ # +- node_modules # directory for node modules
156
+ # +- package.json # package config for npm/yarn and their scripts
157
+ # +- public
158
+ # +- assets # directory for compiled output files
159
+ # +- Procfile # config file for foreman
160
+ #
161
+ # TEXT
162
+ option :opal_name, required: false, type: :string, default: 'opal', aliases: '-o', desc: <<~TEXT
163
+ Set directory name for Opal source files.
164
+ Example: owl-installer rails -o isomorfeus # will use project_root/app/isomorfeus for opal files
165
+ TEXT
166
+
167
+ def webpacker
168
+ @application_css = '../stylesheets/application.css'
169
+ @asset_output_directory = File.join('public', 'assets')
170
+ @js_entrypoints_directory = File.join('app', 'assets', 'javascripts')
171
+ @conf_rel_prefix = File.join('..', '..')
172
+ @js_rel_prefix = File.join('..', '..', '..')
173
+ @opal_directory = File.join('app', options[:opal_name])
174
+ @styles_directory = File.join('app', 'assets', 'stylesheets')
175
+ @webpack_config_directory = File.join('config', 'webpack')
176
+ @dev_default_targets = 'browser'
177
+ @pro_default_targets = 'browser'
178
+ @hmr_hook = ''
179
+ create_directory('app')
180
+ create_common_directories
181
+ install_webpacker_config
182
+ install_webpacker_package_json
183
+ install_webpacker_js_entry
184
+ install_opal_entries
185
+ create_file_from_template('initializer.rb.erb', File.join('config', 'initializers', 'opal_webpack_loader.rb'),
186
+ { opal_load_path: "Opal.append_path(File.realdirpath('#{@opal_directory}'))" })
187
+ add_gem
188
+ print_message
189
+ end
190
+
191
+ private
192
+
193
+ def create_directory(directory)
194
+ unless Dir.exist?(directory)
195
+ puts "Creating directory #{directory}."
196
+ FileUtils.mkdir_p(directory)
197
+ end
198
+ end
199
+
200
+ def create_common_directories
201
+ create_directory(@webpack_config_directory)
202
+ create_directory(@js_entrypoints_directory)
203
+ create_directory(@opal_directory)
204
+ create_directory(@asset_output_directory)
205
+ FileUtils.touch(File.join(@asset_output_directory, '.gitkeep'))
206
+ create_directory(@styles_directory)
207
+ end
208
+
209
+ def install_common_things
210
+ install_webpack_config
211
+ install_package_json
212
+ install_js_entries
213
+ install_opal_entries
214
+ install_procfile
215
+ end
216
+
217
+ def install_js_entries
218
+ erb_hash = { opal_dir: File.join(@js_rel_prefix, @opal_directory), opal_name: options[:opal_name], application_css: @application_css }
219
+ create_file_from_template('application.js.erb', File.join(@js_entrypoints_directory, 'application.js'), erb_hash)
220
+ create_file_from_template('application_common.js.erb', File.join(@js_entrypoints_directory, 'application_common.js'),erb_hash)
221
+ create_file_from_template('application_ssr.js.erb', File.join(@js_entrypoints_directory, 'application_ssr.js'), erb_hash)
222
+ create_file_from_template('application_web_worker.js.erb', File.join(@js_entrypoints_directory, 'application_web_worker.js'), erb_hash)
223
+ end
224
+
225
+ def install_opal_entries
226
+ create_file_from_template('opal_loader.rb.erb', File.join(@opal_directory, "#{options[:opal_name]}_loader.rb"), {})
227
+ create_file_from_template('opal_web_worker_loader.rb.erb', File.join(@opal_directory, "#{options[:opal_name]}_web_worker_loader.rb"), {})
228
+ end
229
+
230
+ def install_package_json
231
+ if File.exist?('package.json')
232
+ gem_package_json_template = File.read(File.join(templates_path, 'package.json.erb'))
233
+ gem_package_json = Oj.load(gem_package_json_template, mode: :strict)
234
+ package_json_file = File.read('package.json')
235
+ package_json = Oj.load(package_json_file, mode: :strict)
236
+ package_json["scripts"] = {} unless package_json.has_key?("scripts")
237
+ package_json["scripts"]["debug"] = debug_script
238
+ package_json["scripts"]["development"] = development_script
239
+ package_json["scripts"]["production_build"] = production_script
240
+ package_json["devDependencies"] = {} unless package_json.has_key?("devDependencies")
241
+ package_json["devDependencies"].merge!(gem_package_json["devDependencies"])
242
+ package_json["dependencies"]["opal-webpack-loader"] = "^#{OpalWebpackLoader::VERSION}"
243
+ File.write('package.json', Oj.dump(package_json, mode: :strict, indent: 2))
244
+ puts "Updated package.json, updated scripts and owl dependencies"
245
+ else
246
+ erb_hash = {
247
+ debug_script: debug_script,
248
+ development_script: development_script,
249
+ production_script: production_script,
250
+ owl_version: OpalWebpackLoader::VERSION
251
+ }
252
+ create_file_from_template('package.json.erb', 'package.json', erb_hash)
253
+ end
254
+ end
255
+
256
+ def install_procfile
257
+ if File.exist?('Procfile')
258
+ procfile = File.read('Procfile')
259
+ has_webpack = false
260
+ lines = procfile.lines
261
+ lines.each do |line|
262
+ has_webpack = true if line.start_with?('webpack')
263
+ end
264
+ unless has_webpack
265
+ lines << 'webpack: yarn run development'
266
+ File.write('Procfile', lines.join("\n"))
267
+ end
268
+ else
269
+ erb_hash = { web_starter: '' }
270
+ if @js_entrypoints_directory.start_with?('app')
271
+ erb_hash[:web_starter] = 'web: bundle exec rails s'
272
+ end
273
+ create_file_from_template('Procfile.erb', 'Procfile',erb_hash)
274
+ end
275
+ end
276
+
277
+ def debug_script
278
+ "webpack-dev-server --config #{File.join(@webpack_config_directory, 'debug.js')}"
279
+ end
280
+
281
+ def development_script
282
+ "webpack-dev-server --config #{File.join(@webpack_config_directory, 'development.js')}"
283
+ end
284
+
285
+ def production_script
286
+ "parallel-webpack --config=#{File.join(@webpack_config_directory, 'production.js')}"
287
+ end
288
+
289
+ def install_webpack_config
290
+ erb_hash = {
291
+ asset_output_directory: File.join(@conf_rel_prefix, @asset_output_directory),
292
+ default_targets: @dev_default_targets,
293
+ js_entry: File.join(@conf_rel_prefix, @js_entrypoints_directory, 'application.js'),
294
+ js_common_entry: File.join(@conf_rel_prefix, @js_entrypoints_directory, 'application_common.js'),
295
+ js_ssr_entry: File.join(@conf_rel_prefix, @js_entrypoints_directory, 'application_ssr.js'),
296
+ js_web_worker_entry: File.join(@conf_rel_prefix, @js_entrypoints_directory, 'application_web_worker.js'),
297
+ opal_directory: File.join(@conf_rel_prefix, @opal_directory),
298
+ stylesheets_directory: File.join(@conf_rel_prefix, @styles_directory),
299
+ hmr_hook: @hmr_hook
300
+ }
301
+ if @js_entrypoints_directory.start_with?('app')
302
+ erb_hash[:dev_server_before] = <<~JAVASCRIPT
303
+ // enable page reload for updates within the app/views directory
304
+ before: function(app, server) {
305
+ chokidar.watch(path.resolve(__dirname, path.join('..', '..', 'app', 'views'))).on('all', function () {
306
+ server.sockWrite(server.sockets, 'content-changed');
307
+ })
308
+ },
309
+ JAVASCRIPT
310
+ else
311
+ erb_hash[:dev_server_before] = <<~JAVASCRIPT
312
+ // uncomment to enable page reload for updates within another directory, which may contain just html files,
313
+ // for example the 'views' directory:
314
+ // before: function(app, server) {
315
+ // chokidar.watch(path.resolve(__dirname, path.join('..', 'views')).on('all', function () {
316
+ // server.sockWrite(server.sockets, 'content-changed');
317
+ // })
318
+ // },
319
+ JAVASCRIPT
320
+ end
321
+ create_file_from_template('debug.js.erb', File.join(@webpack_config_directory, 'debug.js'), erb_hash)
322
+ create_file_from_template('development.js.erb', File.join(@webpack_config_directory, 'development.js'), erb_hash)
323
+ erb_hash[:default_targets] = @pro_default_targets
324
+ create_file_from_template('production.js.erb', File.join(@webpack_config_directory, 'production.js'), erb_hash)
325
+ end
326
+
327
+ def install_webpacker_config
328
+ environment_js = File.read(File.join('config', 'webpack', 'environment.js'), mode: 'r')
329
+ new_environment_js = ''
330
+ environment_js.lines.each do |line|
331
+ new_environment_js << line
332
+ if line.start_with?('const { environment }')
333
+ new_environment_js << "\n"
334
+ new_environment_js << File.read(File.join(templates_path, 'webpacker.js'), mode: 'r')
335
+ new_environment_js << "\n"
336
+ end
337
+ end
338
+ File.write(File.join('config', 'webpack', 'environment.js'), new_environment_js)
339
+ end
340
+
341
+ def install_webpacker_js_entry
342
+ application_js = File.read(File.join('app', 'javascript', 'packs', 'application.js'), mode: 'r')
343
+ application_js << <<~JAVASCRIPT
344
+
345
+ // import and load opal ruby files
346
+ import init_app from '#{options[:opal_name]}_loader.rb';
347
+ init_app();
348
+ Opal.load('#{options[:opal_name]}_loader');
349
+
350
+ JAVASCRIPT
351
+ File.write(File.join('app', 'javascript', 'packs', 'application.js'), application_js)
352
+ end
353
+
354
+ def install_webpacker_package_json
355
+ package_json_file = File.read('package.json')
356
+ package_json = Oj.load(package_json_file, mode: :strict)
357
+ package_json["dependencies"]["opal-webpack-loader"] = "^#{OpalWebpackLoader::VERSION}"
358
+ File.write('package.json', Oj.dump(package_json, mode: :strict, indent: 2))
359
+ puts "Updated package.json for opal-webpack-loader"
360
+ end
361
+
362
+ def templates_path
363
+ File.realpath(File.join(File.dirname(File.realpath(__FILE__)), 'templates'))
364
+ end
365
+
366
+ def create_file_from_template(template_path, target_file_path, data_hash)
367
+ template = ERB.new(File.read(File.join(templates_path, template_path), mode: 'r'))
368
+ result = template.result_with_hash(data_hash)
369
+ if File.exist?(target_file_path)
370
+ ext = '_owl_new'
371
+ puts "#{target_file_path} exists, not overwriting, creating #{target_file_path + ext} instead."
372
+ else
373
+ ext = ''
374
+ puts "Creating #{target_file_path}."
375
+ end
376
+
377
+ File.write(target_file_path + ext, result, mode: 'w')
378
+ end
379
+
380
+ def add_gem
381
+ # TODO specifying a version results in bundler printing a error and not adding the gem
382
+ # `bundle add opal-webpack-loader -v "#{OpalWebpackLoader::VERSION}" --skip-install`ß´
383
+ end
384
+
385
+ def print_message
386
+ puts <<~TEXT
387
+
388
+ Add the following lines to your Gemfile:
389
+
390
+ gem 'opal', github: 'janbiedermann/opal', branch: 'es6_modules_1_2'
391
+ gem 'opal-webpack-loader', '~> #{OpalWebpackLoader::VERSION}'
392
+
393
+ owl currently works only with above opal branch. If you have a existing "gem 'opal'", please replace it with above line.
394
+
395
+ Also check above output for files ending in '_owl_new' and merge their contents with the existing files.
396
+
397
+ After that run according to your preference either:
398
+
399
+ yarn install
400
+
401
+ or:
402
+
403
+ npm install
404
+
405
+ and then:
406
+
407
+ bundle install
408
+
409
+ For further instructions see http://github.com/isomorfeus/opal-webpack-loader
410
+
411
+ TEXT
412
+ end
413
+ end
414
+ end
415
+ end