ruby2js 3.3.3 → 3.5.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: a14485c0400fde35f0734d3e237f2b2d7fdda41377c9d26aeede802634a619e4
4
- data.tar.gz: 3f27f9ac6e92ab7a67823361dfea1b42b09c9dd405dfd7855e278114b3dfccfa
3
+ metadata.gz: 2cdffdc5d823c8e1c36abf924de9f8a489508a5c472b5ac0dd11c42f14dfa7e1
4
+ data.tar.gz: bfa2a41e9865fef31f7502ab1027f5486103434050aec92d3fe4d6044d165d52
5
5
  SHA512:
6
- metadata.gz: '0953480e1e37ebf92fd62af8a55ef77c203c42b2a4ca2880d623a573feadc5b294319cac53f48f36dc5564f4420425cd238dccf3b4cb2a07aab31b7c474cbb9a'
7
- data.tar.gz: 600d4fcd56f5da3144a3f48d872bfdd9293e3de233b47b6b3fb5473a4424e1d4db55b59fef9a59f96df2ba82978dea52b6cb0c070929867a89ce2fa0ea170291
6
+ metadata.gz: ce19f04471f515a27d5cd13fb8beea734fb88246efe03a7ac746e29b07a96696afc5751de9d5f73f0d81d761de7bd8d853197e7d0ce7ce68eab0796524f2b222
7
+ data.tar.gz: 70080cf83c65cfe8d8c6aaa6067ae191df714dfe838977e5be50902624f4e516a680ee0459fcdd8d7274834e29841df5ec592e6ee31dfe990dea305f457c1707
data/README.md CHANGED
@@ -5,6 +5,7 @@ Minimal yet extensible Ruby to JavaScript conversion.
5
5
 
6
6
  [![Build Status](https://travis-ci.org/rubys/ruby2js.svg)](https://travis-ci.org/rubys/ruby2js)
7
7
  [![Gem Version](https://badge.fury.io/rb/ruby2js.svg)](https://badge.fury.io/rb/ruby2js)
8
+ [![Gitter](https://badges.gitter.im/ruby2js/community.svg)](https://gitter.im/ruby2js/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
8
9
 
9
10
  Description
10
11
  ---
@@ -104,6 +105,13 @@ Emit nullish coalescing operators:
104
105
  puts Ruby2JS.convert('a || 1', or: :nullish)
105
106
  ```
106
107
 
108
+ Emit underscored private fields (allowing subclass access):
109
+
110
+ ```ruby
111
+ puts Ruby2JS.convert('class C; def initialize; @f=1; end; end',
112
+ eslevel: 2020, underscored_private: true)
113
+ ```
114
+
107
115
  With [ExecJS](https://github.com/sstephenson/execjs):
108
116
  ```ruby
109
117
  require 'ruby2js/execjs'
@@ -193,7 +201,7 @@ Integrations
193
201
 
194
202
  While this is a low level library suitable for DIY integration, one of the
195
203
  obvious uses of a tool that produces JavaScript is by web servers. Ruby2JS
196
- includes three such integrations:
204
+ includes several integrations:
197
205
 
198
206
  * [CGI](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/cgi.rb)
199
207
  * [Sinatra](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/sinatra.rb)
@@ -204,6 +212,9 @@ As you might expect, CGI is a bit sluggish. By contrast, Sinatra and Rails
204
212
  are quite speedy as the bulk of the time is spent on the initial load of the
205
213
  required libraries.
206
214
 
215
+ For easy integration with Webpack (and Webpacker in Rails 5+), you can use the
216
+ [rb2js-loader](https://github.com/whitefusionhq/rb2js-loader) plugin.
217
+
207
218
  Filters
208
219
  ---
209
220
 
@@ -270,10 +281,12 @@ the script.
270
281
  * `(b...a).to_a` becomes `Array.apply(null, {length: (a-b)}).map(Function.call, Number).map(function (idx) { return idx+b })`
271
282
  * `.strip` becomes `.trim`
272
283
  * `.sub` becomes `.replace`
284
+ * `.tap {|n| n}` becomes `(function(n) {n; return n})(...)`
273
285
  * `.to_f` becomes `parseFloat`
274
286
  * `.to_i` becomes `parseInt`
275
287
  * `.to_s` becomes `.to_String`
276
288
  * `.upcase` becomes `.toUpperCase`
289
+ * `.yield_self {|n| n}` becomes `(function(n) {return n})(...)`
277
290
  * `[-n]` becomes `[*.length-n]` for literal values of `n`
278
291
  * `[n...m]` becomes `.slice(n,m)`
279
292
  * `[n..m]` becomes `.slice(n,m+1)`
@@ -297,12 +310,78 @@ the script.
297
310
  of `Error` instead; and default constructors will be provided
298
311
  * `loop do...end` will be replaced with `while (true) {...}`
299
312
  * `raise Exception.new(...)` will be replaced with `throw new Error(...)`
313
+ * `block_given?` will check for the presence of optional argument `_implicitBlockYield` which is a function made accessible through the use of `yield` in a method body.
300
314
 
301
315
  Additionally, there is one mapping that will only be done if explicitly
302
- <a href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter.rb">included</a>:
316
+ included (pass `include: :class` as a `convert` option to enable):
303
317
 
304
318
  * `.class` becomes `.constructor`
305
319
 
320
+ * <a id="tagged_templates" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/tagged_templates.rb">tagged_templates</a>
321
+
322
+ Allows you to turn certain method calls with a string argument into tagged
323
+ template literals. By default it supports html and css, so you can write
324
+ `html "<div>#{1+2}</div>"` which converts to `` html`<div>${1+2}</div>` ``.
325
+ Works nicely with squiggly heredocs for multi-line templates as well. If you
326
+ need to configure the tag names yourself, pass a `template_literal_tags`
327
+ option to `convert` with an array of tag name symbols.
328
+
329
+ Note: these conversions are only done if eslevel >= 2015
330
+
331
+ * <a href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/esm.rb">esm</a>
332
+
333
+ Provides conversion of import and export statements for use with modern ES builders like Webpack.
334
+
335
+ Examples:
336
+
337
+ **import**
338
+
339
+ ```ruby
340
+ import "./index.scss"
341
+ # => import "./index.scss"
342
+
343
+ import Something from "./lib/something"
344
+ # => import Something from "./lib/something"
345
+
346
+ import Something, "./lib/something"
347
+ # => import Something from "./lib/something"
348
+
349
+ import [ LitElement, html, css ], from: "lit-element"
350
+ # => import { LitElement, html, css } from "lit-element"
351
+
352
+ import React, from: "react"
353
+ # => import React from "react"
354
+
355
+ import React, as: "*", from: "react"
356
+ # => import React as * from "react"
357
+ ```
358
+
359
+ **export**
360
+
361
+ ```ruby
362
+ export hash = { ab: 123 }
363
+ # => export const hash = {ab: 123};
364
+
365
+ export func = ->(x) { x * 10 }
366
+ # => export const func = x => x * 10;
367
+
368
+ export def multiply(x, y)
369
+ return x * y
370
+ end
371
+ # => export function multiply(x, y) {
372
+ # return x * y
373
+ # }
374
+
375
+ export default class MyClass
376
+ end
377
+ # => export default class MyClass {
378
+ # };
379
+
380
+ # or final export statement:
381
+ export [ one, two, default: three ]
382
+ # => export { one, two, three as default }
383
+ ```
384
+
306
385
  * <a id="node" href="https://github.com/rubys/ruby2js/blob/master/spec/node_spec.rb">node</a>
307
386
 
308
387
  * `` `command` `` becomes `child_process.execSync("command", {encoding: "utf8"})`
@@ -388,68 +467,6 @@ the script.
388
467
  * `text` becomes `textContent`
389
468
  * `to_html` becomes `outerHTML`
390
469
 
391
- * <a id="rubyjs" href="https://github.com/rubys/ruby2js/blob/master/spec/rubyjs_spec.rb">rubyjs</a>
392
- * `.at()` becomes `_a.at()`
393
- * `.between?()` becomes `R().between()`
394
- * `.capitalize()` becomes `_s.capitalize()`
395
- * `.center()` becomes `_s.center()`
396
- * `.chomp()` becomes `_s.chomp()`
397
- * `.collect_concat()` becomes `_e.collect_concat()`
398
- * `.compact()` becomes `_a.compact()`
399
- * `.compact!()` becomes `_a.compact_bang()`
400
- * `.count()` becomes `_e.count()`
401
- * `.cycle()` becomes `_e.cycle()`
402
- * `.delete_at()` becomes `_a.delete_at()`
403
- * `.delete_if()` becomes `_a.delete_if()`
404
- * `.drop_while()` becomes `_e.drop_while()`
405
- * `.each_index()` becomes `_e.each_index()`
406
- * `.each_slice()` becomes `_e.each_slice()`
407
- * `.each_with_index()` becomes `_e.each_with_index()`
408
- * `.each_with_object()` becomes `_e.each_with_object()`
409
- * `.find_all()` becomes `_e.find_all()`
410
- * `.find()` becomes `_e.find()`
411
- * `.flat_map()` becomes `_e.flat_map()`
412
- * `.flatten()` becomes `_a.flatten()`
413
- * `.grep()` becomes `_e.grep()`
414
- * `.group_by()` becomes `_e.group_by()`
415
- * `.inject()` becomes `_e.inject()`
416
- * `.insert()` becomes `_a.insert()`
417
- * `.keep_if()` becomes `_a.keep_if()`
418
- * `.ljust()` becomes `_s.ljust()`
419
- * `.lstrip()` becomes `_s.lstrip()`
420
- * `.map()` becomes `_e.map()`
421
- * `.max_by()` becomes `_e.max_by()`
422
- * `.min_by()` becomes `_e.min_by()`
423
- * `.one?()` becomes `_e.one()`
424
- * `.partition()` becomes `_e.partition()`
425
- * `.reject()` becomes `_e.reject()`
426
- * `.reverse()` becomes `_a.reverse()`
427
- * `.reverse!()` becomes `_a.reverse_bang()`
428
- * `.reverse_each()` becomes `_e.reverse_each()`
429
- * `.rindex()` becomes `_s.rindex()`
430
- * `.rjust()` becomes `_s.rjust()`
431
- * `.rotate()` becomes `_a.rotate()`
432
- * `.rotate!()` becomes `_a.rotate_bang()`
433
- * `.rstrip()` becomes `_s.rstrip()`
434
- * `.scan()` becomes `_s.scan()`
435
- * `.select()` becomes `_a.select()`
436
- * `.shift()` becomes `_a.shift()`
437
- * `.shuffle()` becomes `_a.shuffle()`
438
- * `.shuffle!()` becomes `_a.shuffle_bang()`
439
- * `.slice()` becomes `_a.slice()`
440
- * `.slice!()` becomes `_a.slice_bang()`
441
- * `.sort_by()` becomes `_e.sort_by()`
442
- * `.strftime()` becomes `_t.strftime()`
443
- * `.swapcase()` becomes `_s.swapcase()`
444
- * `.take_while()` becomes `_e.take_while(`)
445
- * `.transpose()` becomes `_a.transpose()`
446
- * `.tr()` becomes `_s.tr()`
447
- * `.union()` becomes `_a.union()`
448
- * `.uniq()` becomes `_a.uniq()`
449
- * `.uniq!()` becomes `_a.uniq_bang()`
450
- * `<=>` becomes `R.Comparable.cmp()`
451
- * `(n..m)` becomes `R.Range.new()`
452
-
453
470
  * <a id="underscore" href="https://github.com/rubys/ruby2js/blob/master/spec/underscore.rb">underscore</a>
454
471
 
455
472
  * `.clone()` becomes `_.clone()`
@@ -605,6 +622,14 @@ conversions are made by the `functions` filter:
605
622
  * `.entries()` becomes `Object.entries()`
606
623
  * `.each_pair {}` becomes `for (let [key, value] of Object.entries()) {}'
607
624
 
625
+ async support:
626
+
627
+ * `async def` becomes `async function`
628
+ * `async lambda` becomes `async =>`
629
+ * `async proc` becomes `async =>`
630
+ * `async ->` becomes `async =>`
631
+ * `foo bar, async do...end` becomes `foo(bar, async () => {})`
632
+
608
633
  ES2018 support
609
634
  ---
610
635
 
@@ -677,7 +702,7 @@ License
677
702
 
678
703
  (The MIT License)
679
704
 
680
- Copyright (c) 2009, 2013 Macario Ortega, Sam Ruby
705
+ Copyright (c) 2009, 2020 Macario Ortega, Sam Ruby, Jared White
681
706
 
682
707
  Permission is hereby granted, free of charge, to any person obtaining
683
708
  a copy of this software and associated documentation files (the
@@ -56,6 +56,7 @@ module Ruby2JS
56
56
  @comments = comments
57
57
  @ast = nil
58
58
  @exclude_methods = []
59
+ @esm = false
59
60
  end
60
61
 
61
62
  def options=(options)
@@ -134,6 +135,7 @@ module Ruby2JS
134
135
  def on_xnode(node); end
135
136
  def on_export(node); end
136
137
  def on_import(node); end
138
+ def on_taglit(node); end
137
139
 
138
140
  # provide a method so filters can call 'super'
139
141
  def on_sym(node); node; end
@@ -201,6 +203,7 @@ module Ruby2JS
201
203
  ruby2js.strict = options[:strict]
202
204
  ruby2js.comparison = options[:comparison] || :equality
203
205
  ruby2js.or = options[:or] || :logical
206
+ ruby2js.underscored_private = (options[:eslevel] < 2020) || options[:underscored_private]
204
207
  if ruby2js.binding and not ruby2js.ivars
205
208
  ruby2js.ivars = ruby2js.binding.eval \
206
209
  'Hash[instance_variables.map {|var| [var, instance_variable_get(var)]}]'
@@ -63,6 +63,7 @@ module Ruby2JS
63
63
  @strict = false
64
64
  @comparison = :equality
65
65
  @or = :logical
66
+ @underscored_private = true
66
67
  end
67
68
 
68
69
  def width=(width)
@@ -127,7 +128,7 @@ module Ruby2JS
127
128
  Parser::AST::Node.new(type, args)
128
129
  end
129
130
 
130
- attr_accessor :strict, :eslevel, :comparison, :or
131
+ attr_accessor :strict, :eslevel, :comparison, :or, :underscored_private
131
132
 
132
133
  def es2015
133
134
  @eslevel >= 2015
@@ -332,6 +333,7 @@ require 'ruby2js/converter/self'
332
333
  require 'ruby2js/converter/send'
333
334
  require 'ruby2js/converter/super'
334
335
  require 'ruby2js/converter/sym'
336
+ require 'ruby2js/converter/taglit'
335
337
  require 'ruby2js/converter/undef'
336
338
  require 'ruby2js/converter/until'
337
339
  require 'ruby2js/converter/untilpost'
@@ -341,3 +343,4 @@ require 'ruby2js/converter/while'
341
343
  require 'ruby2js/converter/whilepost'
342
344
  require 'ruby2js/converter/xstr'
343
345
  require 'ruby2js/converter/xnode'
346
+ require 'ruby2js/converter/yield'
@@ -9,6 +9,11 @@ module Ruby2JS
9
9
 
10
10
  handle :block do |call, args, block|
11
11
 
12
+ if es2017 and call.children.last == s(:send, nil, :async)
13
+ return parse call.updated(nil, [*call.children[0..-2],
14
+ s(:send, nil, :async, s(:block, s(:send, nil, :proc), args, block))])
15
+ end
16
+
12
17
  if \
13
18
  @state == :statement and args.children.length == 1 and
14
19
  call.children.first and call.children.first.type == :begin and
@@ -49,7 +49,7 @@ module Ruby2JS
49
49
  end
50
50
 
51
51
  # private variable declarations
52
- if es2020
52
+ unless underscored_private
53
53
  ivars = Set.new
54
54
  cvars = Set.new
55
55
 
@@ -202,7 +202,7 @@ module Ruby2JS
202
202
  end
203
203
 
204
204
  elsif m.type == :send and m.children.first == nil
205
- p = es2020 ? '#' : '_'
205
+ p = underscored_private ? '_' : '#'
206
206
 
207
207
  if m.children[1] == :attr_accessor
208
208
  m.children[2..-1].each_with_index do |child_sym, index2|
@@ -236,7 +236,7 @@ module Ruby2JS
236
236
  end
237
237
 
238
238
  else
239
- if m.type == :cvasgn and es2020
239
+ if m.type == :cvasgn and !underscored_private
240
240
  put 'static #$'; put m.children[0].to_s[2..-1]; put ' = '
241
241
  parse m.children[1]
242
242
  else
@@ -288,6 +288,12 @@ module Ruby2JS
288
288
  [name, innerclass_name.children[1]])
289
289
  end
290
290
  parse m.updated(nil, [innerclass_name, *m.children[1..-1]])
291
+ elsif m.type == :send && (m.children[0].nil? || m.children[0].type == :self)
292
+ if m.children[0].nil?
293
+ parse m.updated(:send, [@class_name, *m.children[1..-1]])
294
+ else
295
+ parse m.updated(:send, [@class_name, *m.children[1..-1]])
296
+ end
291
297
  else
292
298
  parse m, :statement
293
299
  end
@@ -4,7 +4,7 @@ module Ruby2JS
4
4
  # (cvar :@@a)
5
5
 
6
6
  handle :cvar do |var|
7
- prefix = es2020 ? '#$' : '_'
7
+ prefix = underscored_private ? '_' : '#$'
8
8
 
9
9
  @class_name ||= nil
10
10
  if @class_name
@@ -7,7 +7,7 @@ module Ruby2JS
7
7
  handle :cvasgn do |var, expression=nil|
8
8
  multi_assign_declarations if @state == :statement
9
9
 
10
- prefix = es2020 ? '#$' : '_'
10
+ prefix = underscored_private ? '_' : '#$'
11
11
 
12
12
  if @class_name
13
13
  parse @class_name
@@ -9,6 +9,22 @@ module Ruby2JS
9
9
  handle :def, :defm, :async do |name, args, body=nil|
10
10
  body ||= s(:begin)
11
11
 
12
+ add_implicit_block = false
13
+
14
+ walk = ->(node) do
15
+ add_implicit_block = true if node.type == :yield || (node.type == :send && node.children[1] == "_implicitBlockYield")
16
+ node.children.each do |child|
17
+ walk[child] if child.is_a? Parser::AST::Node
18
+ end
19
+ end
20
+ walk[body]
21
+
22
+ if add_implicit_block
23
+ children = args.children.dup
24
+ children.push s(:optarg, "_implicitBlockYield", s(:nil))
25
+ args = s(:args, *children)
26
+ end
27
+
12
28
  vars = {}
13
29
  vars.merge! @vars unless name
14
30
  if args and !args.children.empty?
@@ -20,7 +20,7 @@ module Ruby2JS
20
20
  put '`'
21
21
  children.each do |child|
22
22
  if child.type == :str
23
- str = child.children.first.inspect[1..-2].gsub('${', '$\{')
23
+ str = child.children.first.inspect[1..-2].gsub('${', '$\{').gsub('`', '\\\`')
24
24
  if heredoc
25
25
  put! str.gsub("\\n", "\n")
26
26
  else
@@ -97,9 +97,19 @@ module Ruby2JS
97
97
  end
98
98
 
99
99
  # use fat arrow syntax if block contains a reference to 'this'
100
- if anonfn
100
+ if anonfn and @class_name
101
101
  walk = proc do |ast|
102
- anonfn = false if ast == s(:send, nil, :this)
102
+ if ast == s(:self)
103
+ anonfn = false
104
+ elsif [:ivar, :ivasgn].include? ast.type
105
+ anonfn = false
106
+ elsif ast.type == :send and ast.children.first == nil
107
+ method = ast.children.last if ast.children.length == 2
108
+ if @rbstack.any? {|rb| rb[method]} or method == :this
109
+ anonfn = false
110
+ end
111
+ end
112
+
103
113
  ast.children.each do |child|
104
114
  walk[child] if child.is_a? Parser::AST::Node
105
115
  end
@@ -7,14 +7,47 @@ module Ruby2JS
7
7
 
8
8
  handle :import do |path, *args|
9
9
  put 'import '
10
+ if args.length == 0
11
+ # import "file.css"
12
+ put path.inspect
13
+ else
14
+ # import (x) from "file.js"
15
+ default_import = !args.first.is_a?(Array) && [:const, :send, :attr].include?(args.first.type)
16
+ args = args.first if args.first.is_a?(Array)
10
17
 
11
- args.each_with_index do |arg, index|
12
- put ', ' unless index == 0
13
- parse arg
14
- end
18
+ # handle the default name or { ConstA, Const B } portion
19
+ put "{ " unless default_import
20
+ args.each_with_index do |arg, index|
21
+ put ', ' unless index == 0
22
+ parse arg
23
+ end
24
+ put " }" unless default_import
25
+
26
+ from_kwarg_position = 0
27
+
28
+ # should there be an as clause? e.g., import React as *
29
+ if path.is_a?(Array) && !path[0].is_a?(String) && path[0].type == :pair && path[0].children[0].children[0] == :as
30
+ put " as #{path[0].children[1].children[0]}"
31
+
32
+ # advance to the next kwarg, aka from
33
+ from_kwarg_position = 1
34
+ end
15
35
 
16
- put ' from '
17
- put path.inspect
36
+ put ' from '
37
+
38
+ if path.is_a?(Array) && !path[from_kwarg_position].is_a?(String) && path[from_kwarg_position].type == :pair
39
+ # from: "str" => from "str"
40
+ if path[from_kwarg_position].children[0].children[0] == :from
41
+ put path[from_kwarg_position].children[1].children[0].inspect
42
+ else
43
+ # from is missing
44
+ put '""'
45
+ end
46
+ else
47
+ # handle a str in either an array element or directly passed in
48
+ put path.is_a?(Array) ? path[0].inspect : path.inspect
49
+ end
50
+ end
18
51
  end
19
52
 
20
53
  # (export const)
@@ -24,14 +57,49 @@ module Ruby2JS
24
57
  handle :export do |*args|
25
58
  put 'export '
26
59
 
27
- if args.first == :default
60
+ node = args.first
61
+ final_export = false
62
+
63
+ if node == :default
28
64
  put 'default '
29
65
  args.shift
66
+ elsif node.respond_to?(:type) && node.children[1] == :default
67
+ put 'default '
68
+ args[0] = node.children[2]
69
+ elsif node.respond_to?(:type) && node.type == :lvasgn
70
+ if node.children[0] == :default
71
+ put 'default '
72
+ args[0] = node.children[1]
73
+ else
74
+ put 'const '
75
+ end
76
+ elsif node.respond_to?(:type) &&
77
+ node.type == :array &&
78
+ node.children[0].respond_to?(:type) &&
79
+ (
80
+ node.children[0].type == :const ||
81
+ node.children[0].type == :send ||
82
+ (node.children[0].type == :hash && node.children[0].children[0].children[0].children[0] == :default )
83
+ )
84
+ final_export = true
85
+ put '{ '
86
+ node.children.each_with_index do |arg, index|
87
+ put ', ' unless index == 0
88
+ if arg.type == :hash && arg.children[0].children[0].children[0] == :default
89
+ put arg.children[0].children[1].children[1]
90
+ put ' as default'
91
+ else
92
+ parse arg
93
+ end
94
+ end
95
+ put ' }'
30
96
  end
31
97
 
32
- args.each_with_index do |arg, index|
33
- put ', ' unless index == 0
34
- parse arg
98
+ unless final_export
99
+ args.each_with_index do |arg, index|
100
+ put ', ' unless index == 0
101
+ parse arg
102
+ end
35
103
  end
36
104
  end
37
105
  end