ruby2js 4.0.1 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -1
  3. data/lib/ruby2js.rb +13 -2
  4. data/lib/ruby2js/converter.rb +31 -0
  5. data/lib/ruby2js/converter/args.rb +1 -1
  6. data/lib/ruby2js/converter/class.rb +6 -4
  7. data/lib/ruby2js/converter/class2.rb +4 -4
  8. data/lib/ruby2js/converter/def.rb +7 -1
  9. data/lib/ruby2js/converter/dstr.rb +3 -1
  10. data/lib/ruby2js/converter/for.rb +1 -1
  11. data/lib/ruby2js/converter/next.rb +10 -2
  12. data/lib/ruby2js/converter/redo.rb +14 -0
  13. data/lib/ruby2js/converter/return.rb +1 -1
  14. data/lib/ruby2js/converter/send.rb +1 -0
  15. data/lib/ruby2js/converter/taglit.rb +9 -2
  16. data/lib/ruby2js/converter/while.rb +1 -1
  17. data/lib/ruby2js/converter/whilepost.rb +1 -1
  18. data/lib/ruby2js/filter/functions.rb +44 -9
  19. data/lib/ruby2js/filter/lit-element.rb +202 -0
  20. data/lib/ruby2js/filter/react.rb +155 -91
  21. data/lib/ruby2js/filter/stimulus.rb +4 -2
  22. data/lib/ruby2js/filter/tagged_templates.rb +4 -2
  23. data/lib/ruby2js/rails.rb +6 -59
  24. data/lib/ruby2js/serializer.rb +16 -11
  25. data/lib/ruby2js/sprockets.rb +40 -0
  26. data/lib/ruby2js/version.rb +2 -2
  27. data/lib/tasks/README.md +4 -0
  28. data/lib/tasks/install/app/javascript/elements/index.js +2 -0
  29. data/lib/tasks/install/config/webpack/loaders/ruby2js.js +20 -0
  30. data/lib/tasks/install/litelement.rb +9 -0
  31. data/lib/tasks/install/preact.rb +3 -0
  32. data/lib/tasks/install/react.rb +2 -0
  33. data/lib/tasks/install/stimulus-sprockets.rb +32 -0
  34. data/lib/tasks/install/stimulus-webpacker.rb +5 -0
  35. data/lib/tasks/install/webpacker.rb +80 -0
  36. data/lib/tasks/nodetest.js +47 -0
  37. data/lib/tasks/ruby2js_tasks.rake +47 -0
  38. data/ruby2js.gemspec +1 -1
  39. metadata +18 -4
@@ -51,8 +51,10 @@ module Ruby2JS
51
51
  @stim_classes = Set.new
52
52
  stim_walk(node)
53
53
 
54
- prepend_list << (@options[:import_from_skypack] ?
55
- STIMULUS_IMPORT_SKYPACK : STIMULUS_IMPORT)
54
+ if modules_enabled?
55
+ prepend_list << (@options[:import_from_skypack] ?
56
+ STIMULUS_IMPORT_SKYPACK : STIMULUS_IMPORT)
57
+ end
56
58
 
57
59
  nodes = body
58
60
  if nodes.length == 1 and nodes.first&.type == :begin
@@ -15,17 +15,19 @@ module Ruby2JS
15
15
 
16
16
  tagged_methods = @options[:template_literal_tags] || [:html, :css]
17
17
 
18
- if tagged_methods.include?(method) && !args.empty?
18
+ if tagged_methods.include?(method) && args.length == 1
19
19
  strnode = process args.first
20
20
  if strnode.type == :str
21
21
  # convert regular strings to literal strings
22
22
  strnode = strnode.updated(:dstr, [s(:str, strnode.children.first.chomp("\n"))])
23
- else
23
+ elsif strnode.type == :dstr
24
24
  # for literal strings, chomp a newline off the end
25
25
  if strnode.children.last.type == :str && strnode.children.last.children[0].end_with?("\n")
26
26
  children = [*strnode.children.take(strnode.children.length - 1), s(:str, strnode.children.last.children[0].chomp)]
27
27
  strnode = s(:dstr, *children)
28
28
  end
29
+ else
30
+ return super
29
31
  end
30
32
 
31
33
  S(:taglit, s(:sym, method), strnode)
data/lib/ruby2js/rails.rb CHANGED
@@ -1,63 +1,10 @@
1
- # Example usage:
2
- #
3
- # $ echo "gem 'ruby2js', require: 'ruby2js/rails'" >> Gemfile
4
- # $ bundle update
5
- # $ rails generate controller Say hello
6
- # $ echo 'alert "Hello world!"' > app/views/say/hello.js.rb
7
- # $ rails server
8
- # $ curl http://localhost:3000/say/hello.js
9
- #
10
- # Using an optional filter:
11
- #
12
- # $ echo 'require "ruby2js/filter/functions"' > config/initializers/ruby2js.rb
13
- #
14
- # Asset Pipeline:
15
- #
16
- # Ruby2JS registers ".rb.js" extension.
17
- # You can add "ruby_thing.js.rb" to your app/javascript folder
18
- # and '= require ruby_thing' from other js sources.
19
- #
20
- # (options are not yet supported, but by requiring the appropriate files
21
- # as shown above, you can configure proejct wide.)
22
- require 'ruby2js'
1
+ require 'rails'
2
+ require_relative './sprockets'
23
3
 
24
- module Ruby2JS
25
- module Rails
26
- class Template
27
- cattr_accessor :default_format
28
- self.default_format = Mime[:js]
29
- def self.call(template, source)
30
- "Ruby2JS.convert(#{template.source.inspect}, file: source).to_s"
31
- end
4
+ class Ruby2JSRailtie < Rails::Railtie
5
+ rake_tasks do
6
+ Dir[File.expand_path('../tasks/*.rake', __dir__)].each do |file|
7
+ load file
32
8
  end
33
-
34
- ActiveSupport.on_load(:action_view) do
35
- ActionView::Template.register_template_handler :rb, Template
36
- end
37
-
38
- class SprocketProcessor
39
- def initialize(file = nil)
40
- @file = file
41
- end
42
- def render(context , _)
43
- context = context.instance_eval { binding } unless context.is_a? Binding
44
- Ruby2JS.convert(File.read(@file), binding: context).to_s
45
- end
46
- end
47
-
48
- class Engine < ::Rails::Engine
49
- engine_name "ruby2js"
50
-
51
- config.app_generators.javascripts true
52
- config.app_generators.javascript_engine :rb
53
-
54
- config.assets.configure do |env|
55
- env.register_mime_type 'text/ruby', extensions: ['.js.rb', '.rb']
56
- env.register_transformer 'text/ruby', 'text/javascript', SprocketProcessor
57
- env.register_preprocessor 'text/javascript', SprocketProcessor
58
- end
59
- end
60
-
61
9
  end
62
-
63
10
  end
@@ -303,11 +303,11 @@ module Ruby2JS
303
303
  def vlq(*mark)
304
304
  if @mark[0] == mark[0]
305
305
  return if @mark[-3..-1] == mark[-3..-1]
306
- @mappings << ',' unless @mappings == ''
306
+ @mappings += ',' unless @mappings == ''
307
307
  end
308
308
 
309
309
  while @mark[0] < mark[0]
310
- @mappings << ';'
310
+ @mappings += ';'
311
311
  @mark[0] += 1
312
312
  @mark[1] = 0
313
313
  end
@@ -322,16 +322,21 @@ module Ruby2JS
322
322
  data = diff << 1
323
323
  end
324
324
 
325
- encoded = ''
326
-
327
- begin
328
- digit = data & 0b11111
329
- data >>= 5
330
- digit |= 0b100000 if data > 0
331
- encoded << BASE64[digit]
332
- end while data > 0
325
+ if data <= 0b11111
326
+ # workaround https://github.com/opal/opal/issues/575
327
+ encoded = BASE64[data]
328
+ else
329
+ encoded = ''
330
+
331
+ begin
332
+ digit = data & 0b11111
333
+ data >>= 5
334
+ digit |= 0b100000 if data > 0
335
+ encoded += BASE64[digit]
336
+ end while data > 0
337
+ end
333
338
 
334
- @mappings << encoded
339
+ @mappings += encoded
335
340
  end
336
341
  end
337
342
 
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/source_map_utils'
3
+ require 'ruby2js'
4
+ require 'ruby2js/version'
5
+
6
+ module Ruby2JS
7
+ module SprocketsTransformer
8
+ include Sprockets
9
+ VERSION = '1'
10
+
11
+ @@options = {}
12
+
13
+ def self.options=(options)
14
+ @@options = options
15
+ end
16
+
17
+ def self.cache_key
18
+ @cache_key ||= "#{name}:#{Ruby2JS::VERSION::STRING}:#{VERSION}".freeze
19
+ end
20
+
21
+ def self.call(input)
22
+ data = input[:data]
23
+
24
+ js, map = input[:cache].fetch([self.cache_key, data]) do
25
+ result = Ruby2JS.convert(data, {**@@options, file: input[:filename]})
26
+ [result.to_s, result.sourcemap.transform_keys {|key| key.to_s}]
27
+ end
28
+
29
+ map = SourceMapUtils.format_source_map(map, input)
30
+ map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map)
31
+
32
+ { data: js, map: map }
33
+ end
34
+ end
35
+ end
36
+
37
+ Sprockets.register_mime_type 'application/ruby', extensions: ['.rb', '.js.rb']
38
+
39
+ Sprockets.register_transformer 'application/ruby', 'application/javascript',
40
+ Ruby2JS::SprocketsTransformer
@@ -1,8 +1,8 @@
1
1
  module Ruby2JS
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 4
4
- MINOR = 0
5
- TINY = 1
4
+ MINOR = 1
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -0,0 +1,4 @@
1
+ This directory contains rails install tasks and a test program which will run
2
+ the rails examples from the docs/src/_examples/rails directory.
3
+
4
+ run `./testrails.rb` for usage instructions.
@@ -0,0 +1,2 @@
1
+ function importAll(r) { r.keys().forEach(r) }
2
+ importAll(require.context("elements", true, /_elements?\.js(\.rb)?$/))
@@ -0,0 +1,20 @@
1
+ const { environment } = require('@rails/webpacker')
2
+
3
+ module.exports = {
4
+ test: /\.js\.rb$/,
5
+ use: [
6
+ {
7
+ loader: "babel-loader",
8
+ options: environment.loaders.get('babel').use[0].options
9
+ },
10
+
11
+ {
12
+ loader: "@ruby2js/webpack-loader",
13
+ options: {
14
+ autoexports: "default",
15
+ eslevel: 2021,
16
+ filters: ["esm", "functions"]
17
+ }
18
+ }
19
+ ]
20
+ }
@@ -0,0 +1,9 @@
1
+ @ruby2js_options = {filters: ['lit-element']}
2
+ @yarn_add='lit-element'
3
+ eval IO.read "#{__dir__}/webpacker.rb"
4
+
5
+ directory File.expand_path("app/javascript/elements", __dir__),
6
+ Rails.root.join('app/javascript/elements').to_s
7
+
8
+ append_to_file Rails.root.join('app/javascript/packs/application.js').to_s,
9
+ "\nimport 'elements'\n"
@@ -0,0 +1,3 @@
1
+ @ruby2js_options = {filters: ['react']}
2
+ @yarn_add='preact'
3
+ eval IO.read "#{__dir__}/webpacker.rb"
@@ -0,0 +1,2 @@
1
+ @ruby2js_options = {filters: ['react']}
2
+ eval IO.read "#{__dir__}/webpacker.rb"
@@ -0,0 +1,32 @@
1
+ create_file Rails.root.join('config/initializers/ruby2js.rb').to_s,
2
+ <<~CONFIG
3
+ require 'ruby2js/filter/esm'
4
+ require 'ruby2js/filter/functions'
5
+ require 'ruby2js/filter/stimulus'
6
+
7
+ Ruby2JS::SprocketsTransformer.options = {
8
+ autoexports: :default,
9
+ eslevel: 2020
10
+ }
11
+
12
+ require 'stimulus/importmap_helper'
13
+
14
+ module Stimulus::ImportmapHelper
15
+ def find_javascript_files_in_tree(path)
16
+ exts = {'.js' => '.js', '.jsm' => '.jsm'}.merge(
17
+ Sprockets.mime_exts.map {|key, value|
18
+ next unless Sprockets.transformers[value]["application/javascript"]
19
+ [key, '.js']
20
+ }.compact.to_h)
21
+
22
+ Dir[path.join('**/*')].map {|file|
23
+ file_ext, web_ext = Sprockets::PathUtils.match_path_extname(file, exts)
24
+ next unless file_ext
25
+
26
+ next unless File.file? file
27
+
28
+ Pathname.new(file.chomp(file_ext) + web_ext)
29
+ }.compact
30
+ end
31
+ end
32
+ CONFIG
@@ -0,0 +1,5 @@
1
+ @ruby2js_options = {filters: ['stimulus'], eslevel: 2022}
2
+ eval IO.read "#{__dir__}/webpacker.rb"
3
+
4
+ insert_into_file Rails.root.join("app/javascript/controllers/index.js").to_s,
5
+ '(\\.rb)?', after: '_controller\\.js'
@@ -0,0 +1,80 @@
1
+ #
2
+ # These instructions can be used standalone or in combination with other
3
+ # instructions.
4
+ #
5
+ # If used standalone on a repository which did not previously have the Ruby2JS
6
+ # webpack loader configured, it will add the '.js.rb' extension to
7
+ # `config/webpacker.yml`, install the Ruby2JS webpack-loader and add a minimal
8
+ # configuration which runs the Ruby2JS webpack loader then the babel loader to
9
+ # `config/webpacker.yml`.
10
+ #
11
+ # Other instructions can require these instructions. If they set the
12
+ # @ruby2js_options instance variable before they do so, the options provided
13
+ # will be merged and/or override the ones in the base configuration.
14
+ #
15
+ # If these instructions are run against a Rails application that was
16
+ # previously configured, the @ruby2js_options provided, if any, will be merged
17
+ # with the options found in the configuration.
18
+ #
19
+
20
+ # default options
21
+ options = @ruby2js_options || {}
22
+
23
+ # define .js.rb as a extension to webpacker
24
+ insert_into_file Rails.root.join("config/webpacker.yml").to_s,
25
+ " - .js.rb\n", after: "\n - .js\n"
26
+
27
+ # install webpack loader
28
+ run "yarn add @ruby2js/webpack-loader #{@yarn_add}"
29
+
30
+ target = Rails.root.join("config/webpack/loaders/ruby2js.js").to_s
31
+
32
+ # default config
33
+ if not File.exist? target
34
+ # may be called via eval, or directly. Resolve source either way.
35
+ source_paths.unshift __dir__
36
+ source_paths.unshift File.dirname(caller.first)
37
+ directory "config/webpack/loaders", File.dirname(target)
38
+ end
39
+
40
+ # load config
41
+ insert_into_file Rails.root.join("config/webpack/environment.js").to_s,
42
+ "environment.loaders.prepend('ruby2js', require('./loaders/ruby2js'))\n"
43
+
44
+ # read current configuration
45
+ before = IO.read(target)
46
+
47
+ # extract indentation and options
48
+ match = /^(\s*)options: (\{.*?\n\1\})/m.match(before)
49
+
50
+ # evaluate base options. Here it is handy that Ruby's syntax for hashes is
51
+ # fairly close to JavaScript's object literals. May run into problems in the
52
+ # future if there ever is a need for {"x": y} as that would need to be
53
+ # {"x" => y} in Ruby.
54
+ base = eval(match[2])
55
+
56
+ # Merge the options, initially having the new options override the original
57
+ # options.
58
+ merged = base.merge(options)
59
+
60
+ # Intelligently combine options if they are the same type.
61
+ options.keys.each do |key|
62
+ next unless base[key]
63
+
64
+ if options[key].is_a? Array and base[key].is_a? Array
65
+ merged[key] = (base[key] + options[key]).uniq
66
+ elsif options[key].is_a? Numeric and base[key].is_a? Numeric
67
+ merged[key] = [base[key], options[key]].max
68
+ elsif options[key].is_a? Hash and base[key].is_a? Hash
69
+ merged[key] = base[key].merge(options[key])
70
+ end
71
+ end
72
+
73
+ # Serialize options as JavaScript, matching original indentation.
74
+ replacement = Ruby2JS.convert(merged.inspect + "\n").to_s.
75
+ gsub(/^/, match[1]).strip
76
+
77
+ # Update configuration
78
+ unless before.include? replacement
79
+ gsub_file target, match[2].to_s, replacement
80
+ end
@@ -0,0 +1,47 @@
1
+ // minimal sanity test to verify usage of Ruby2JS under Node
2
+
3
+ const assert = require('assert');
4
+
5
+ const Ruby2JS = require('@ruby2js/ruby2js');
6
+ // const Ruby2JS =
7
+ // require('/home/rubys/git/ruby2js/docs/src/demo/ruby2js.js');
8
+
9
+ function to_js(string, options={}) {
10
+ return Ruby2JS.convert(string, options).toString()
11
+ }
12
+
13
+ assert.strictEqual(
14
+ to_js('foo = 1'),
15
+ 'var foo = 1');
16
+
17
+ assert.strictEqual(
18
+ to_js('foo = 1', {eslevel: 2015}),
19
+ 'let foo = 1');
20
+
21
+ assert.strictEqual(
22
+ to_js('foo.empty?', {filters: ['functions']}),
23
+ 'foo.length == 0');
24
+
25
+ let ast = Ruby2JS.convert('String', {file: 'a.rb'}).ast
26
+ assert.strictEqual(ast.constructor, Ruby2JS.AST.Node)
27
+ assert.strictEqual(ast.type, "const")
28
+ assert.strictEqual(ast.children.length, 2)
29
+ assert.strictEqual(ast.children[0], Ruby2JS.nil)
30
+ assert.strictEqual(ast.children[1], "String")
31
+
32
+ let sourcemap = Ruby2JS.convert('a=1', {file: 'a.rb'}).sourcemap
33
+ assert.strictEqual(sourcemap.version, 3)
34
+ assert.strictEqual(sourcemap.file, 'a.rb')
35
+ assert.strictEqual(sourcemap.sources.length, 1)
36
+ assert.strictEqual(sourcemap.sources[0], 'a.rb')
37
+ assert.strictEqual(sourcemap.mappings, 'QAAE')
38
+
39
+ console.log(to_js(`
40
+ class C < LitElement
41
+ def render
42
+ x.empty?
43
+ '<h1>title</h1>'
44
+ end
45
+ end
46
+
47
+ `, {eslevel: 2020, filters: ['lit-element', 'esm', 'functions']}))
@@ -0,0 +1,47 @@
1
+ Thor::Actions::WARNINGS[:unchanged_no_flag] = 'unchanged'
2
+
3
+ def template(location)
4
+ system "#{RbConfig.ruby} #{Rails.root.join("bin")}/rails app:template " +
5
+ "LOCATION=#{File.expand_path(location, __dir__)}"
6
+ end
7
+
8
+ namespace :ruby2js do
9
+ namespace :install do
10
+ desc "Install Ruby2JS with LitElement support"
11
+ task :litelement do
12
+ template 'install/litelement.rb'
13
+ end
14
+
15
+ desc "Install Ruby2JS with Preact support"
16
+ task :preact do
17
+ template 'install/preact.rb'
18
+ end
19
+
20
+ desc "Install Ruby2JS with React support"
21
+ task :react do
22
+ template 'install/react.rb'
23
+ Rake::Task['webpacker:install:react'].invoke
24
+ end
25
+
26
+ namespace :stimulus do
27
+ desc "Install Ruby2JS with Stimulus Sprockets support"
28
+ task :sprockets => :"stimulus:install:asset_pipeline" do
29
+ template 'install/stimulus-sprockets.rb'
30
+ end
31
+
32
+ desc "Install Ruby2JS with Stimulus Webpacker support"
33
+ task :webpacker => :"stimulus:install" do
34
+ template 'install/stimulus-webpacker.rb'
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ namespace :webpacker do
41
+ namespace :install do
42
+ desc "Install everything needed for Ruby2JS"
43
+ task :ruby2js do
44
+ template 'install/webpacker.rb'
45
+ end
46
+ end
47
+ end