ruby2js 4.0.0 → 4.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -1
  3. data/lib/ruby2js.rb +16 -3
  4. data/lib/ruby2js/converter.rb +31 -0
  5. data/lib/ruby2js/converter/args.rb +6 -1
  6. data/lib/ruby2js/converter/class.rb +3 -0
  7. data/lib/ruby2js/converter/class2.rb +8 -5
  8. data/lib/ruby2js/converter/def.rb +8 -2
  9. data/lib/ruby2js/converter/dstr.rb +3 -1
  10. data/lib/ruby2js/converter/for.rb +1 -1
  11. data/lib/ruby2js/converter/hash.rb +19 -1
  12. data/lib/ruby2js/converter/logical.rb +1 -1
  13. data/lib/ruby2js/converter/next.rb +10 -2
  14. data/lib/ruby2js/converter/redo.rb +14 -0
  15. data/lib/ruby2js/converter/return.rb +2 -1
  16. data/lib/ruby2js/converter/send.rb +30 -4
  17. data/lib/ruby2js/converter/taglit.rb +9 -2
  18. data/lib/ruby2js/converter/while.rb +1 -1
  19. data/lib/ruby2js/converter/whilepost.rb +1 -1
  20. data/lib/ruby2js/converter/xstr.rb +1 -2
  21. data/lib/ruby2js/filter/camelCase.rb +2 -0
  22. data/lib/ruby2js/filter/functions.rb +39 -11
  23. data/lib/ruby2js/filter/lit-element.rb +202 -0
  24. data/lib/ruby2js/filter/react.rb +6 -16
  25. data/lib/ruby2js/filter/require.rb +2 -6
  26. data/lib/ruby2js/filter/stimulus.rb +4 -2
  27. data/lib/ruby2js/filter/tagged_templates.rb +4 -2
  28. data/lib/ruby2js/jsx.rb +19 -1
  29. data/lib/ruby2js/rails.rb +6 -59
  30. data/lib/ruby2js/serializer.rb +16 -11
  31. data/lib/ruby2js/sprockets.rb +40 -0
  32. data/lib/ruby2js/version.rb +1 -1
  33. data/lib/tasks/README.md +4 -0
  34. data/lib/tasks/install/app/javascript/elements/index.js +2 -0
  35. data/lib/tasks/install/litelement.rb +9 -0
  36. data/lib/tasks/install/react.rb +2 -0
  37. data/lib/tasks/install/stimulus-sprockets.rb +32 -0
  38. data/lib/tasks/install/stimulus-webpacker.rb +5 -0
  39. data/lib/tasks/install/webpacker.rb +97 -0
  40. data/lib/tasks/nodetest.js +47 -0
  41. data/lib/tasks/ruby2js_tasks.rake +42 -0
  42. data/ruby2js.gemspec +1 -1
  43. metadata +18 -6
@@ -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/jsx.rb CHANGED
@@ -11,7 +11,7 @@ module Ruby2JS
11
11
 
12
12
  class JsxParser
13
13
  def initialize(stream)
14
- @stream = stream
14
+ @stream = stream.respond_to?(:next) ? stream : OpalEnumerator.new(stream)
15
15
  @state = :text
16
16
  @text = ''
17
17
  @result = []
@@ -288,4 +288,22 @@ module Ruby2JS
288
288
  self.class.new(@stream).parse(:element)
289
289
  end
290
290
  end
291
+
292
+ # Opal's enumerator doesn't currently support next and peek methods.
293
+ # Build a wrapper that adds those methods.
294
+ class OpalEnumerator
295
+ def initialize(stream)
296
+ @stream = stream.to_a
297
+ end
298
+
299
+ def next
300
+ raise StopIteration.new if @stream.empty?
301
+ @stream.shift
302
+ end
303
+
304
+ def peek
305
+ raise StopIteration.new if @stream.empty?
306
+ @stream.first
307
+ end
308
+ end
291
309
  end
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
@@ -2,7 +2,7 @@ module Ruby2JS
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 4
4
4
  MINOR = 0
5
- TINY = 0
5
+ TINY = 5
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,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,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,97 @@
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
+ # default config
31
+ config = <<~CONFIG
32
+ // Insert rb2js loader at the end of list
33
+ environment.loaders.append('rb2js', {
34
+ test: /\.js\.rb$/,
35
+ use: [
36
+ {
37
+ loader: "babel-loader",
38
+ options: environment.loaders.get('babel').use[0].options
39
+ },
40
+
41
+ {
42
+ loader: "@ruby2js/webpack-loader",
43
+ options: {
44
+ autoexports: "default",
45
+ eslevel: 2021,
46
+ filters: ["esm", "functions"]
47
+ }
48
+ },
49
+ ]
50
+ })
51
+ CONFIG
52
+
53
+ # read current configuration
54
+ target = Rails.root.join("config/webpack/environment.js").to_s
55
+ before = IO.read(target)
56
+
57
+ # extract indentation and options either from the current configuration or the
58
+ # default configuration.
59
+ if before.include? '@ruby2js/webpack-loader'
60
+ match = /^(\s*)options: (\{.*?\n\1\})/m.match(before)
61
+ else
62
+ match = /^(\s*)options: (\{.*?\n\1\})/m.match(config)
63
+ end
64
+
65
+ # evaluate base options. Here it is handy that Ruby's syntax for hashes is
66
+ # fairly close to JavaScript's object literals. May run into problems in the
67
+ # future if there ever is a need for {"x": y} as that would need to be
68
+ # {"x" => y} in Ruby.
69
+ base = eval(match[2])
70
+
71
+ # Merge the options, initially having the new options override the original
72
+ # options.
73
+ merged = base.merge(options)
74
+
75
+ # Intelligently combine options if they are the same type.
76
+ options.keys.each do |key|
77
+ next unless base[key]
78
+
79
+ if options[key].is_a? Array and base[key].is_a? Array
80
+ merged[key] = (base[key] + options[key]).uniq
81
+ elsif options[key].is_a? Numeric and base[key].is_a? Numeric
82
+ merged[key] = [base[key], options[key]].max
83
+ elsif options[key].is_a? Hash and base[key].is_a? Hash
84
+ merged[key] = base[key].merge(options[key])
85
+ end
86
+ end
87
+
88
+ # Serialize options as JavaScript, matching original indentation.
89
+ replacement = Ruby2JS.convert(merged.inspect + "\n").to_s.
90
+ gsub(/^/, match[1]).strip
91
+
92
+ # Update configuration
93
+ if before.include? '@ruby2js/webpack-loader'
94
+ gsub_file target, match[2].to_s, replacement
95
+ else
96
+ append_to_file target, "\n" + config.sub(match[2], replacement)
97
+ 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,42 @@
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 React support"
16
+ task :react do
17
+ template 'install/react.rb'
18
+ Rake::Task['webpacker:install:react'].invoke
19
+ end
20
+
21
+ namespace :stimulus do
22
+ desc "Install Ruby2JS with Stimulus Sprockets support"
23
+ task :sprockets => :"stimulus:install:asset_pipeline" do
24
+ template 'install/stimulus-sprockets.rb'
25
+ end
26
+
27
+ desc "Install Ruby2JS with Stimulus Webpacker support"
28
+ task :webpacker => :"stimulus:install" do
29
+ template 'install/stimulus-webpacker.rb'
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ namespace :webpacker do
36
+ namespace :install do
37
+ desc "Install everything needed for Ruby2JS"
38
+ task :ruby2js do
39
+ template 'install/webpacker.rb'
40
+ end
41
+ end
42
+ end