ruby2js 3.5.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -662
  3. data/lib/ruby2js.rb +61 -10
  4. data/lib/ruby2js/converter.rb +10 -4
  5. data/lib/ruby2js/converter/assign.rb +159 -0
  6. data/lib/ruby2js/converter/begin.rb +7 -2
  7. data/lib/ruby2js/converter/case.rb +7 -2
  8. data/lib/ruby2js/converter/class.rb +77 -21
  9. data/lib/ruby2js/converter/class2.rb +102 -31
  10. data/lib/ruby2js/converter/def.rb +7 -3
  11. data/lib/ruby2js/converter/dstr.rb +8 -3
  12. data/lib/ruby2js/converter/hash.rb +9 -5
  13. data/lib/ruby2js/converter/hide.rb +13 -0
  14. data/lib/ruby2js/converter/if.rb +10 -2
  15. data/lib/ruby2js/converter/import.rb +35 -4
  16. data/lib/ruby2js/converter/kwbegin.rb +9 -2
  17. data/lib/ruby2js/converter/literal.rb +14 -2
  18. data/lib/ruby2js/converter/module.rb +41 -4
  19. data/lib/ruby2js/converter/opasgn.rb +8 -0
  20. data/lib/ruby2js/converter/send.rb +45 -5
  21. data/lib/ruby2js/converter/vasgn.rb +5 -0
  22. data/lib/ruby2js/converter/xstr.rb +1 -1
  23. data/lib/ruby2js/demo.rb +53 -0
  24. data/lib/ruby2js/es2022.rb +5 -0
  25. data/lib/ruby2js/es2022/strict.rb +3 -0
  26. data/lib/ruby2js/filter.rb +9 -1
  27. data/lib/ruby2js/filter/active_functions.rb +44 -0
  28. data/lib/ruby2js/filter/camelCase.rb +4 -3
  29. data/lib/ruby2js/filter/cjs.rb +2 -0
  30. data/lib/ruby2js/filter/esm.rb +133 -7
  31. data/lib/ruby2js/filter/functions.rb +107 -98
  32. data/lib/ruby2js/filter/{wunderbar.rb → jsx.rb} +29 -7
  33. data/lib/ruby2js/filter/node.rb +95 -74
  34. data/lib/ruby2js/filter/nokogiri.rb +15 -41
  35. data/lib/ruby2js/filter/react.rb +191 -56
  36. data/lib/ruby2js/filter/require.rb +100 -5
  37. data/lib/ruby2js/filter/return.rb +15 -1
  38. data/lib/ruby2js/filter/securerandom.rb +33 -0
  39. data/lib/ruby2js/filter/stimulus.rb +185 -0
  40. data/lib/ruby2js/filter/vue.rb +9 -0
  41. data/lib/ruby2js/jsx.rb +291 -0
  42. data/lib/ruby2js/namespace.rb +75 -0
  43. data/lib/ruby2js/rails.rb +15 -9
  44. data/lib/ruby2js/serializer.rb +3 -1
  45. data/lib/ruby2js/version.rb +3 -3
  46. data/ruby2js.gemspec +1 -1
  47. metadata +14 -5
  48. data/lib/ruby2js/filter/esm_migration.rb +0 -72
  49. data/lib/ruby2js/filter/fast-deep-equal.rb +0 -23
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f659d6312b558d08a3ca6c1f30183d602445ed580910715909ea2b4334a114a9
4
- data.tar.gz: 3f07730f5727ea25b95e095c7b45c19f3fb85dcc5d640e822a3a629fa88b8056
3
+ metadata.gz: 064d9dc5d65f6e2456e91d951e2f4311b776221422a77d59a8cfd466ceb5056d
4
+ data.tar.gz: ffc274a55ceb9a1280f776c00da8c296dc10672150174135931547cd056a3520
5
5
  SHA512:
6
- metadata.gz: a85a3d7f144485575608cd136d764e36868d095fb8dbf4f6bc8b8adb8a218379775eeb12934c148574cac0f03dc6e12fc6223ef302973b5f4cb0a0629ba41de4
7
- data.tar.gz: 4c154cd393ce76cc0f72e23180fbfdd1b0b8e876a5d60686d246e9f53effff1c4f4ce4ea3c5573cf9ac5cb7cfcda3d2300ee865c2d8d21855a717fa8f203de4a
6
+ metadata.gz: 8fe9775803716fce25697adaf59a8afb57f4de499052a66847f18dfdd59c1f73b52d77be473adf762a57693a0896a6c90ed275a2b9152b5b6b548ca454e6d9ae
7
+ data.tar.gz: aa901eab47bba051a69a0ee132b69e7754aa5da49ff2e7e22560303a4c0fd02cdb727d4297c13098f3a6f82cf20788f4657c7ec21d145e66533875933d1a5154
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- Ruby2js
1
+ Ruby2JS
2
2
  =======
3
3
 
4
4
  Minimal yet extensible Ruby to JavaScript conversion.
@@ -7,62 +7,14 @@ Minimal yet extensible Ruby to JavaScript conversion.
7
7
  [![Gem Version](https://badge.fury.io/rb/ruby2js.svg)](https://badge.fury.io/rb/ruby2js)
8
8
  [![Gitter](https://badges.gitter.im/ruby2js/community.svg)](https://gitter.im/ruby2js/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
9
9
 
10
- Description
11
- ---
12
-
13
- The base package maps Ruby syntax to JavaScript semantics.
14
- For example:
15
-
16
- * a Ruby Hash literal becomes a JavaScript Object literal
17
- * Ruby symbols become JavaScript strings.
18
- * Ruby method calls become JavaScript function calls IF
19
- there are either one or more arguments passed OR
20
- parenthesis are used
21
- * otherwise Ruby method calls become JavaScript property accesses.
22
- * by default, methods and procs return `undefined`
23
- * splats mapped to spread syntax when ES2015 or later is selected, and
24
- to equivalents using `apply`, `concat`, `slice`, and `arguments` otherwise.
25
- * ruby string interpolation is expanded into string + operations
26
- * `and` and `or` become `&&` and `||`
27
- * `a ** b` becomes `Math.pow(a,b)`
28
- * `<< a` becomes `.push(a)`
29
- * `unless` becomes `if !`
30
- * `until` becomes `while !`
31
- * `case` and `when` becomes `switch` and `case`
32
- * ruby for loops become js for loops
33
- * `(1...4).step(2){` becomes `for (var i = 1; i < 4; i += 2) {`
34
- * `x.forEach { next }` becomes `x.forEach(function() {return})`
35
- * `lambda {}` and `proc {}` becomes `function() {}`
36
- * `class Person; end` becomes `function Person() {}`
37
- * instance methods become prototype methods
38
- * instance variables become underscored, `@name` becomes `this._name`
39
- * self is assigned to this is if used
40
- * Any block becomes and explicit argument `new Promise do; y(); end` becomes `new Promise(function() {y()})`
41
- * regular expressions are mapped to js
42
- * `raise` becomes `throw`
43
- * expressions enclosed in backtick operators (\`\`) and `%x{}` literals are
44
- evaluated in the context of the caller and the results are inserted
45
- into the generated JavaScript.
46
10
 
47
- Ruby attribute accessors, methods defined with no parameters and no
48
- parenthesis, as well as setter method definitions, are
49
- mapped to
50
- [Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FObject%2FdefineProperty),
51
- so avoid these if you wish to target users running IE8 or lower.
11
+ Documentation
12
+ ---
52
13
 
53
- While both Ruby and JavaScript have open classes, Ruby unifies the syntax for
54
- defining and extending an existing class, whereas JavaScript does not. This
55
- means that Ruby2JS needs to be told when a class is being extended, which is
56
- done by prepending the `class` keyword with two plus signs, thus:
57
- `++class C; ...; end`.
14
+ * Visit **[ruby2js.com](https://www.ruby2js.com)** for detailed setup instructions and API reference.
58
15
 
59
- Filters may be provided to add Ruby-specific or framework specific
60
- behavior. Filters are essentially macro facilities that operate on
61
- an AST representation of the code.
16
+ * [Try Ruby2JS online](https://ruby2js.com/demo)
62
17
 
63
- See
64
- [notimplemented_spec](https://github.com/rubys/ruby2js/blob/master/spec/notimplemented_spec.rb)
65
- for a list of Ruby features _known_ to be not implemented.
66
18
 
67
19
  Synopsis
68
20
  ---
@@ -93,615 +45,6 @@ Enable ES2015 support:
93
45
  puts Ruby2JS.convert('"#{a}"', eslevel: 2015)
94
46
  ```
95
47
 
96
- Enable strict support:
97
-
98
- ```ruby
99
- puts Ruby2JS.convert('a=1', strict: true)
100
- ```
101
-
102
- Emit strict equality comparisons:
103
-
104
- ```ruby
105
- puts Ruby2JS.convert('a==1', comparison: :identity)
106
- ```
107
-
108
- Emit nullish coalescing operators:
109
-
110
- ```ruby
111
- puts Ruby2JS.convert('a || 1', or: :nullish)
112
- ```
113
-
114
- Emit underscored private fields (allowing subclass access):
115
-
116
- ```ruby
117
- puts Ruby2JS.convert('class C; def initialize; @f=1; end; end',
118
- eslevel: 2020, underscored_private: true)
119
- ```
120
-
121
- With [ExecJS](https://github.com/sstephenson/execjs):
122
- ```ruby
123
- require 'ruby2js/execjs'
124
- require 'date'
125
-
126
- context = Ruby2JS.compile(Date.today.strftime('d = new Date(%Y, %-m-1, %-d)'))
127
- puts context.eval('d.getYear()')+1900
128
- ```
129
-
130
- Conversions can be explored interactively using the
131
- [demo](https://github.com/rubys/ruby2js/blob/master/demo/ruby2js.rb) provided.
132
-
133
- Introduction
134
- ---
135
-
136
- JavaScript is a language where `0` is considered `false`, strings are
137
- immutable, and the behaviors for operators like `==` are, at best,
138
- [convoluted](http://zero.milosz.ca/).
139
-
140
- Any attempt to bridge the semantics of Ruby and JavaScript will involve
141
- trade-offs. Consider the following expression:
142
-
143
- ```ruby
144
- a[-1]
145
- ```
146
-
147
- Programmers who are familiar with Ruby will recognize that this returns the
148
- last element (or character) of an array (or string). However, the meaning is
149
- quite different if `a` is a Hash.
150
-
151
- One way to resolve this is to change the way indexing operators are evaluated,
152
- and to provide a runtime library that adds properties to global JavaScript
153
- objects to handle this. This is the approach that [Opal](http://opalrb.com/)
154
- takes. It is a fine approach, with a number of benefits. It also has some
155
- notable drawbacks. For example,
156
- [readability](http://opalrb.com/try/#code:a%20%3D%20%22abc%22%3B%20puts%20a[-1])
157
- and
158
- [compatibility with other frameworks](https://github.com/opal/opal/issues/400).
159
-
160
- Another approach is to simply accept JavaScript semantics for what they are.
161
- This would mean that negative indexes would return `undefined` for arrays
162
- and strings. This is the base approach provided by ruby2js.
163
-
164
- A third approach would be to do static transformations on the source in order
165
- to address common usage patterns or idioms. These transformations can even be
166
- occasionally unsafe, as long as the transformations themselves are opt-in.
167
- ruby2js provides a number of such filters, including one that handles negative
168
- indexes when passed as a literal. As indicated above, this is unsafe in that
169
- it will do the wrong thing when it encounters a hash index which is expressed
170
- as a literal constant negative one. My experience is that such is rare enough
171
- to be safely ignored, but YMMV. More troublesome, this also won’t work when
172
- the index is not a literal (e.g., `a[n]`) and the index happens to be
173
- negative at runtime.
174
-
175
- This quickly gets into gray areas. `each` in Ruby is a common method that
176
- facilitates iteration over arrays. `forEach` is the JavaScript equivalent.
177
- Mapping this is fine until you start using a framework like jQuery which
178
- provides a function named [each](http://api.jquery.com/jQuery.each/).
179
-
180
- Fortunately, Ruby provides `?` and `!` as legal suffixes for method names,
181
- Ruby2js filters do an exact match, so if you select a filter that maps `each`
182
- to `forEach`, `each!` will pass through the filter. The final code that emits
183
- JavaScript function calls and parameter accesses will strip off these
184
- suffixes.
185
-
186
- This approach works well if it is an occasional change, but if the usage is
187
- pervasive, most filters support options to `exclude` a list of mappings,
188
- for example:
189
-
190
- ```ruby
191
- puts Ruby2JS.convert('jQuery("li").each {|index| ...}', exclude: :each)
192
- ```
193
-
194
- Alternatively, you can change the default:
195
-
196
- ```ruby
197
- Ruby2JS::Filter.exclude :each
198
- ```
199
-
200
- Static transformations and runtime libraries aren't aren’t mutually exclusive.
201
- With enough of each, one could reproduce any functionality desired. Just be
202
- forewarned, that implementing a function like `method_missing` would require a
203
- _lot_ of work.
204
-
205
- Integrations
206
- ---
207
-
208
- While this is a low level library suitable for DIY integration, one of the
209
- obvious uses of a tool that produces JavaScript is by web servers. Ruby2JS
210
- includes several integrations:
211
-
212
- * [CGI](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/cgi.rb)
213
- * [Sinatra](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/sinatra.rb)
214
- * [Rails](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/rails.rb)
215
- * [Haml](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/haml.rb)
216
-
217
- As you might expect, CGI is a bit sluggish. By contrast, Sinatra and Rails
218
- are quite speedy as the bulk of the time is spent on the initial load of the
219
- required libraries.
220
-
221
- For easy integration with Webpack (and Webpacker in Rails 5+), you can use the
222
- [rb2js-loader](https://github.com/whitefusionhq/rb2js-loader) plugin.
223
-
224
- Filters
225
- ---
226
-
227
- In general, making use of a filter is as simple as requiring it. If multiple
228
- filters are selected, they will all be applied in parallel in one pass through
229
- the script.
230
-
231
- * <a id="return" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/return.rb">return</a>
232
- adds `return` to the last expression in functions.
233
-
234
- * <a id="require" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/require.rb">require</a>
235
- supports `require` and `require_relative` statements. Contents of files
236
- that are required are converted to JavaScript and expanded inline.
237
- `require` function calls in expressions are left alone.
238
-
239
- * <a id="camelCase" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/camelCase.rb">camelCase</a>
240
- converts `underscore_case` to `camelCase`. See
241
- [camelcase_spec](https://github.com/rubys/ruby2js/blob/master/spec/camelcase_spec.rb)
242
- for examples.
243
-
244
- * <a id="functions" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/functions.rb">functions</a>
245
-
246
- * `.all?` becomes `.every`
247
- * `.any?` becomes `.some`
248
- * `.chr` becomes `fromCharCode`
249
- * `.clear` becomes `.length = 0`
250
- * `.delete` becomes `delete target[arg]`
251
- * `.downcase` becomes `.toLowerCase`
252
- * `.each` becomes `.forEach`
253
- * `.each_key` becomes `for (i in ...) {}`
254
- * `.each_pair` becomes `for (var key in item) {var value = item[key]; ...}`
255
- * `.each_value` becomes `.forEach`
256
- * `.each_with_index` becomes `.forEach`
257
- * `.end_with?` becomes `.slice(-arg.length) == arg`
258
- * `.empty?` becomes `.length == 0`
259
- * `.find_index` becomes `findIndex`
260
- * `.first` becomes `[0]`
261
- * `.first(n)` becomes `.slice(0, n)`
262
- * `.gsub` becomes `replace(//g)`
263
- * `.include?` becomes `.indexOf() != -1`
264
- * `.inspect` becomes `JSON.stringify()`
265
- * `.keys()` becomes `Object.keys()`
266
- * `.last` becomes `[*.length-1]`
267
- * `.last(n)` becomes `.slice(*.length-1, *.length)`
268
- * `.lstrip` becomes `.replace(/^\s+/, "")`
269
- * `.max` becomes `Math.max.apply(Math)`
270
- * `.merge` becomes `Object.assign({}, ...)`
271
- * `.merge!` becomes `Object.assign()`
272
- * `.min` becomes `Math.min.apply(Math)`
273
- * `.nil?` becomes `== null`
274
- * `.ord` becomes `charCodeAt(0)`
275
- * `puts` becomes `console.log`
276
- * `.replace` becomes `.length = 0; ...push.apply(*)`
277
- * `.respond_to?` becomes `right in left`
278
- * `.rstrip` becomes `.replace(/s+$/, "")`
279
- * `.scan` becomes `.match(//g)`
280
- * `.start_with?` becomes `.substring(0, arg.length) == arg`
281
- * `.upto(lim)` becomes `for (var i=num; i<=lim; i+=1)`
282
- * `.downto(lim)` becomes `for (var i=num; i>=lim; i-=1)`
283
- * `.step(lim, n).each` becomes `for (var i=num; i<=lim; i+=n)`
284
- * `.step(lim, -n).each` becomes `for (var i=num; i>=lim; i-=n)`
285
- * `(0..a).to_a` becomes `Array.apply(null, {length: a}).map(Function.call, Number)`
286
- * `(b..a).to_a` becomes `Array.apply(null, {length: (a-b+1)}).map(Function.call, Number).map(function (idx) { return idx+b })`
287
- * `(b...a).to_a` becomes `Array.apply(null, {length: (a-b)}).map(Function.call, Number).map(function (idx) { return idx+b })`
288
- * `.strip` becomes `.trim`
289
- * `.sub` becomes `.replace`
290
- * `.tap {|n| n}` becomes `(function(n) {n; return n})(...)`
291
- * `.to_f` becomes `parseFloat`
292
- * `.to_i` becomes `parseInt`
293
- * `.to_s` becomes `.to_String`
294
- * `.upcase` becomes `.toUpperCase`
295
- * `.yield_self {|n| n}` becomes `(function(n) {return n})(...)`
296
- * `[-n]` becomes `[*.length-n]` for literal values of `n`
297
- * `[n...m]` becomes `.slice(n,m)`
298
- * `[n..m]` becomes `.slice(n,m+1)`
299
- * `[/r/, n]` becomes `.match(/r/)[n]`
300
- * `[/r/, n]=` becomes `.replace(/r/, ...)`
301
- * `(1..2).each {|i| ...}` becomes `for (var i=1 i<=2; i+=1)`
302
- * `"string" * length` becomes `new Array(length + 1).join("string")`
303
- * `.sub!` and `.gsub!` become equivalent `x = x.replace` statements
304
- * `.map!`, `.reverse!`, and `.select` become equivalent
305
- `.splice(0, .length, *.method())` statements
306
- * `@foo.call(args)` becomes `this._foo(args)`
307
- * `@@foo.call(args)` becomes `this.constructor._foo(args)`
308
- * `Array(x)` becomes `Array.prototype.slice.call(x)`
309
- * `delete x` becomes `delete x` (note lack of parenthesis)
310
- * `setInterval` and `setTimeout` allow block to be treated as the
311
- first parameter on the call
312
- * for the following methods, if the block consists entirely of a simple
313
- expression (or ends with one), a `return` is added prior to the
314
- expression: `sub`, `gsub`, `any?`, `all?`, `map`, `find`, `find_index`.
315
- * New classes subclassed off of `Exception` will become subclassed off
316
- of `Error` instead; and default constructors will be provided
317
- * `loop do...end` will be replaced with `while (true) {...}`
318
- * `raise Exception.new(...)` will be replaced with `throw new Error(...)`
319
- * `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.
320
-
321
- Additionally, there is one mapping that will only be done if explicitly
322
- included (pass `include: :class` as a `convert` option to enable):
323
-
324
- * `.class` becomes `.constructor`
325
-
326
- * <a id="tagged_templates" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/tagged_templates.rb">tagged_templates</a>
327
-
328
- Allows you to turn certain method calls with a string argument into tagged
329
- template literals. By default it supports html and css, so you can write
330
- `html "<div>#{1+2}</div>"` which converts to `` html`<div>${1+2}</div>` ``.
331
- Works nicely with squiggly heredocs for multi-line templates as well. If you
332
- need to configure the tag names yourself, pass a `template_literal_tags`
333
- option to `convert` with an array of tag name symbols.
334
-
335
- Note: these conversions are only done if eslevel >= 2015
336
-
337
- * <a href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/esm.rb">esm</a>
338
-
339
- Provides conversion of import and export statements for use with modern ES builders like Webpack.
340
-
341
- Examples:
342
-
343
- **import**
344
-
345
- ```ruby
346
- import "./index.scss"
347
- # => import "./index.scss"
348
-
349
- import Something from "./lib/something"
350
- # => import Something from "./lib/something"
351
-
352
- import Something, "./lib/something"
353
- # => import Something from "./lib/something"
354
-
355
- import [ LitElement, html, css ], from: "lit-element"
356
- # => import { LitElement, html, css } from "lit-element"
357
-
358
- import React, from: "react"
359
- # => import React from "react"
360
-
361
- import React, as: "*", from: "react"
362
- # => import React as * from "react"
363
- ```
364
-
365
- **export**
366
-
367
- ```ruby
368
- export hash = { ab: 123 }
369
- # => export const hash = {ab: 123};
370
-
371
- export func = ->(x) { x * 10 }
372
- # => export const func = x => x * 10;
373
-
374
- export def multiply(x, y)
375
- return x * y
376
- end
377
- # => export function multiply(x, y) {
378
- # return x * y
379
- # }
380
-
381
- export default class MyClass
382
- end
383
- # => export default class MyClass {
384
- # };
385
-
386
- # or final export statement:
387
- export [ one, two, default: three ]
388
- # => export { one, two, three as default }
389
- ```
390
-
391
- * <a id="node" href="https://github.com/rubys/ruby2js/blob/master/spec/node_spec.rb">node</a>
392
-
393
- * `` `command` `` becomes `child_process.execSync("command", {encoding: "utf8"})`
394
- * `ARGV` becomes `process.argv.slice(2)`
395
- * `__dir__` becomes `__dirname`
396
- * `Dir.chdir` becomes `process.chdir`
397
- * `Dir.entries` becomes `fs.readdirSync`
398
- * `Dir.mkdir` becomes `fs.mkdirSync`
399
- * `Dir.mktmpdir` becomes `fs.mkdtempSync`
400
- * `Dir.pwd` becomes `process.cwd`
401
- * `Dir.rmdir` becomes `fs.rmdirSync`
402
- * `ENV` becomes `process.env`
403
- * `__FILE__` becomes `__filename`
404
- * `File.chmod` becomes `fs.chmodSync`
405
- * `File.chown` becomes `fs.chownSync`
406
- * `File.cp` becomes `fs.copyFileSync`
407
- * `File.exist?` becomes `fs.existsSync`
408
- * `File.lchmod` becomes `fs.lchmodSync`
409
- * `File.link` becomes `fs.linkSync`
410
- * `File.ln` becomes `fs.linkSync`
411
- * `File.lstat` becomes `fs.lstatSync`
412
- * `File.read` becomes `fs.readFileSync`
413
- * `File.readlink` becomes `fs.readlinkSync`
414
- * `File.realpath` becomes `fs.realpathSync`
415
- * `File.rename` becomes `fs.renameSync`
416
- * `File.stat` becomes `fs.statSync`
417
- * `File.symlink` becomes `fs.symlinkSync`
418
- * `File.truncate` becomes `fs.truncateSync`
419
- * `File.unlink` becomes `fs.unlinkSync`
420
- * `FileUtils.cd` becomes `process.chdir`
421
- * `FileUtils.cp` becomes `fs.copyFileSync`
422
- * `FileUtils.ln` becomes `fs.linkSync`
423
- * `FileUtils.ln_s` becomes `fs.symlinkSync`
424
- * `FileUtils.mkdir` becomes `fs.mkdirSync`
425
- * `FileUtils.mv` becomes `fs.renameSync`
426
- * `FileUtils.pwd` becomes `process.cwd`
427
- * `FileUtils.rm` becomes `fs.unlinkSync`
428
- * `IO.read` becomes `fs.readFileSync`
429
- * `IO.write` becomes `fs.writeFileSync`
430
- * `system` becomes `child_process.execSync(..., {stdio: "inherit"})`
431
-
432
- * <a id="nokogiri" href="https://github.com/rubys/ruby2js/blob/master/spec/nokogiri.rb">nokogiri</a>
433
- * `add_child` becomes `appendChild`
434
- * `add_next_sibling` becomes `node.parentNode.insertBefore(sibling, node.nextSibling)`
435
- * `add_previous_sibling` becomes `node.parentNode.insertBefore(sibling, node)`
436
- * `after` becomes `node.parentNode.insertBefore(sibling, node.nextSibling)`
437
- * `at` becomes `querySelector`
438
- * `attr` becomes `getAttribute`
439
- * `attribute` becomes `getAttributeNode`
440
- * `before` becomes `node.parentNode.insertBefore(sibling, node)`
441
- * `cdata?` becomes `node.nodeType === Node.CDATA_SECTION_NODE`
442
- * `children` becomes `childNodes`
443
- * `comment?` becomes `node.nodeType === Node.COMMENT_NODE`
444
- * `content` becomes `textContent`
445
- * `create_element` becomes `createElement`
446
- * `document` becomes `ownerDocument`
447
- * `element?` becomes `node.nodeType === Node.ELEMENT_NODE`
448
- * `fragment?` becomes `node.nodeType === Node.FRAGMENT_NODE`
449
- * `get_attribute` becomes `getAttribute`
450
- * `has_attribute` becomes `hasAttribute`
451
- * `inner_html` becomes `innerHTML`
452
- * `key?` becomes `hasAttribute`
453
- * `name` becomes `nextSibling`
454
- * `next` becomes `nodeName`
455
- * `next=` becomes `node.parentNode.insertBefore(sibling,node.nextSibling)`
456
- * `next_element` becomes `nextElement`
457
- * `next_sibling` becomes `nextSibling`
458
- * `Nokogiri::HTML5` becomes `new JSDOM().window.document`
459
- * `Nokogiri::HTML5.parse` becomes `new JSDOM().window.document`
460
- * `Nokogiri::HTML` becomes `new JSDOM().window.document`
461
- * `Nokogiri::HTML.parse` becomes `new JSDOM().window.document`
462
- * `Nokogiri::XML::Node.new` becomes `document.createElement()`
463
- * `parent` becomes `parentNode`
464
- * `previous=` becomes `node.parentNode.insertBefore(sibling, node)`
465
- * `previous_element` becomes `previousElement`
466
- * `previous_sibling` becomes `previousSibling`
467
- * `processing_instruction?` becomes `node.nodeType === Node.PROCESSING_INSTRUCTION_NODE`
468
- * `remove_attribute` becomes `removeAttribute`
469
- * `root` becomes `documentElement`
470
- * `search` becomes `querySelectorAll`
471
- * `set_attribute` becomes `setAttribute`
472
- * `text?` becomes `node.nodeType === Node.TEXT_NODE`
473
- * `text` becomes `textContent`
474
- * `to_html` becomes `outerHTML`
475
-
476
- * <a id="underscore" href="https://github.com/rubys/ruby2js/blob/master/spec/underscore.rb">underscore</a>
477
-
478
- * `.clone()` becomes `_.clone()`
479
- * `.compact()` becomes `_.compact()`
480
- * `.count_by {}` becomes `_.countBy {}`
481
- * `.find {}` becomes `_.find {}`
482
- * `.find_by()` becomes `_.findWhere()`
483
- * `.flatten()` becomes `_.flatten()`
484
- * `.group_by {}` becomes `_.groupBy {}`
485
- * `.has_key?()` becomes `_.has()`
486
- * `.index_by {}` becomes `_.indexBy {}`
487
- * `.invert()` becomes `_.invert()`
488
- * `.invoke(&:n)` becomes `_.invoke(, :n)`
489
- * `.map(&:n)` becomes `_.pluck(, :n)`
490
- * `.merge!()` becomes `_.extend()`
491
- * `.merge()` becomes `_.extend({}, )`
492
- * `.reduce {}` becomes `_.reduce {}`
493
- * `.reduce()` becomes `_.reduce()`
494
- * `.reject {}` becomes `_.reject {}`
495
- * `.sample()` becomes `_.sample()`
496
- * `.select {}` becomes `_.select {}`
497
- * `.shuffle()` becomes `_.shuffle()`
498
- * `.size()` becomes `_.size()`
499
- * `.sort()` becomes `_.sort_by(, _.identity)`
500
- * `.sort_by {}` becomes `_.sortBy {}`
501
- * `.times {}` becomes `_.times {}`
502
- * `.values()` becomes `_.values()`
503
- * `.where()` becomes `_.where()`
504
- * `.zip()` becomes `_.zip()`
505
- * `(n...m)` becomes `_.range(n, m)`
506
- * `(n..m)` becomes `_.range(n, m+1)`
507
- * `.compact!`, `.flatten!`, `shuffle!`, `reject!`, `sort_by!`, and
508
- `.uniq` become equivalent `.splice(0, .length, *.method())` statements
509
- * for the following methods, if the block consists entirely of a simple
510
- expression (or ends with one), a `return` is added prior to the
511
- expression: `reduce`, `sort_by`, `group_by`, `index_by`, `count_by`,
512
- `find`, `select`, `reject`.
513
- * `is_a?` and `kind_of?` map to `Object.prototype.toString.call() ===
514
- "[object #{type}]" for the following types: `Arguments`, `Boolean`,
515
- `Date`, `Error`, `Function`, `Number`, `Object`, `RegExp`, `String`; and
516
- maps Ruby names to JavaScript equivalents for `Exception`, `Float`,
517
- `Hash`, `Proc`, and `Regexp`. Additionally, `is_a?` and `kind_of?` map
518
- to `Array.isArray()` for `Array`.
519
-
520
- * <a id="jquery" href="https://github.com/rubys/ruby2js/blob/master/spec/jquery.rb">jquery</a>
521
-
522
- * maps Ruby unary operator `~` to jQuery `$` function
523
- * maps Ruby attribute syntax to jquery attribute syntax
524
- * `.to_a` becomes `toArray`
525
- * maps `$$` to jQuery `$` function
526
- * defaults the fourth parameter of $$.post to `"json"`, allowing Ruby block
527
- syntax to be used for the success function.
528
-
529
- * <a id="minitest-jasmine" href="https://github.com/rubys/ruby2js/blob/master/spec/minitest-jasmine.rb">minitest-jasmine</a>
530
- * maps subclasses of `Minitest::Test` to `describe` calls
531
- * maps `test_` methods inside subclasses of `Minitest::Test` to `it` calls
532
- * maps `setup`, `teardown`, `before`, and `after` calls to `beforeEach`
533
- and `afterEach` calls
534
- * maps `assert` and `refute` calls to `expect`...`toBeTruthy()` and
535
- `toBeFalsy` calls
536
- * maps `assert_equal`, `refute_equal`, `.must_equal` and `.cant_equal`
537
- calls to `expect`...`toBe()` calls
538
- * maps `assert_in_delta`, `refute_in_delta`, `.must_be_within_delta`,
539
- `.must_be_close_to`, `.cant_be_within_delta`, and `.cant_be_close_to`
540
- calls to `expect`...`toBeCloseTo()` calls
541
- * maps `assert_includes`, `refute_includes`, `.must_include`, and
542
- `.cant_include` calls to `expect`...`toContain()` calls
543
- * maps `assert_match`, `refute_match`, `.must_match`, and `.cant_match`
544
- calls to `expect`...`toMatch()` calls
545
- * maps `assert_nil`, `refute_nil`, `.must_be_nil`, and `.cant_be_nill` calls
546
- to `expect`...`toBeNull()` calls
547
- * maps `assert_operator`, `refute_operator`, `.must_be`, and `.cant_be`
548
- calls to `expect`...`toBeGreaterThan()` or `toBeLessThan` calls
549
-
550
- * <a id="cjs" href="https://github.com/rubys/ruby2js/blob/master/spec/cjs">cjs</a>
551
- * maps `export def f` to `exports.f =`
552
- * maps `export async def f` to `exports.f = async`
553
- * maps `export v =` to `exports.v =`
554
- * maps `export default proc` to `module.exports =`
555
- * maps `export default async proc` to `module.exports = async`
556
- * maps `export default` to `module.exports =`
557
-
558
- * <a id="matchAll" href="https://github.com/rubys/ruby2js/blob/master/spec/matchAll">matchAll</a>
559
-
560
- For ES level < 2020:
561
-
562
- * maps `str.matchAll(pattern).forEach {}` to
563
- `while (match = pattern.exec(str)) {}`
564
-
565
- Note `pattern` must be a simple variable with a value of a regular
566
- expression with the `g` flag set at runtime.
567
-
568
- [Wunderbar](https://github.com/rubys/wunderbar) includes additional demos:
569
-
570
- * [chat](https://github.com/rubys/wunderbar/blob/master/demo/chat.rb),
571
- [diskusage](https://github.com/rubys/wunderbar/blob/master/demo/diskusage.rb),
572
- and [wiki](https://github.com/rubys/wunderbar/blob/master/demo/wiki.rb) make
573
- use of the jquery filter.
574
-
575
- ES2015 support
576
- ---
577
-
578
- When option `eslevel: 2015` is provided, the following additional
579
- conversions are made:
580
-
581
- * `"#{a}"` becomes <code>\`${a}\`</code>
582
- * `a = 1` becomes `let a = 1`
583
- * `A = 1` becomes `const A = 1`
584
- * `a, b = b, a` becomes `[a, b] = [b, a]`
585
- * `a, (foo, *bar) = x` becomes `let [a, [foo, ...bar]] = x`
586
- * `def f(a, (foo, *bar))` becomes `function f(a, [foo, ...bar])`
587
- * `def a(b=1)` becomes `function a(b=1)`
588
- * `def a(*b)` becomes `function a(...b)`
589
- * `.each_value` becomes `for (i of ...) {}`
590
- * `a(*b)` becomes `a(...b)`
591
- * `"#{a}"` becomes <code>\`${a}\`</code>
592
- * `lambda {|x| x}` becomes `(x) => {return x}`
593
- * `proc {|x| x}` becomes `(x) => {x}`
594
- * `a {|x|}` becomes `a((x) => {})`
595
- * `class Person; end` becomes `class Person {}`
596
- * `(0...a).to_a` becomes `[...Array(a).keys()]`
597
- * `(0..a).to_a` becomes `[...Array(a+1).keys()]`
598
- * `(b..a).to_a` becomes `Array.from({length: (a-b+1)}, (_, idx) => idx+b)`
599
-
600
- ES2015 class support includes constructors, super, methods, class methods,
601
- instance methods, instance variables, class variables, getters, setters,
602
- attr_accessor, attr_reader, attr_writer, etc.
603
-
604
- Additionally, the `functions` filter will provide the following conversion:
605
-
606
- * `Array(x)` becomes `Array.from(x)`
607
- * `.inject(n) {}` becomes `.reduce(() => {}, n)`
608
-
609
- Finally, keyword arguments and optional keyword arguments will be mapped to
610
- parameter detructuring.
611
-
612
- ES2016 support
613
- ---
614
-
615
- When option `eslevel: 2016` is provided, the following additional
616
- conversion is made:
617
-
618
- * `a ** b` becomes `a ** b`
619
- * `.include?` becomes `.includes`
620
-
621
- ES2017 support
622
- ---
623
-
624
- When option `eslevel: 2017` is provided, the following additional
625
- conversions are made by the `functions` filter:
626
-
627
- * `.values()` becomes `Object.values()`
628
- * `.entries()` becomes `Object.entries()`
629
- * `.each_pair {}` becomes `for (let [key, value] of Object.entries()) {}'
630
-
631
- async support:
632
-
633
- * `async def` becomes `async function`
634
- * `async lambda` becomes `async =>`
635
- * `async proc` becomes `async =>`
636
- * `async ->` becomes `async =>`
637
- * `foo bar, async do...end` becomes `foo(bar, async () => {})`
638
-
639
- ES2018 support
640
- ---
641
-
642
- When option `eslevel: 2018` is provided, the following additional
643
- conversion is made by the `functions` filter:
644
-
645
- * `.merge` becomes `{...a, ...b}`
646
-
647
- Additionally, rest arguments can now be used with keyword arguments and
648
- optional keyword arguments.
649
-
650
- ES2019 support
651
- ---
652
-
653
- When option `eslevel: 2019` is provided, the following additional
654
- conversion is made by the `functions` filter:
655
-
656
- * `.flatten` becomes `.flat(Infinity)`
657
- * `.lstrip` becomes `.trimEnd
658
- * `.rstrip` becomes `.trimStart
659
- * `a.to_h` becomes `Object.fromEntries(a)`
660
- * `Hash[a]` becomes `Object.fromEntries(a)`
661
-
662
- Additionally, `rescue` without a variable will map to `catch` without a
663
- variable.
664
-
665
- ES2020 support
666
- ---
667
-
668
- When option `eslevel: 2020` is provided, the following additional
669
- conversions are made:
670
-
671
- * `@x` becomes `this.#x`
672
- * `@@x` becomes `ClassName.#x`
673
- * `a&.b` becomes `a?.b`
674
- * `.scan` becomes `Array.from(str.matchAll(/.../g), s => s.slice(1))`
675
-
676
- ES2021 support
677
- ---
678
-
679
- When option `eslevel: 2021` is provided, the following additional
680
- conversions are made:
681
-
682
- * `x ||= 1` becomes `x ||= 1`
683
- * `x &&= 1` becomes `x &&= 1`
684
-
685
- Picking a Ruby to JS mapping tool
686
- ---
687
-
688
- > dsl — A domain specific language, where code is written in one language and
689
- > errors are given in another.
690
- > -- [Devil’s Dictionary of Programming](http://programmingisterrible.com/post/65781074112/devils-dictionary-of-programming)
691
-
692
- If you simply want to get a job done, and would like a mature and tested
693
- framework, and only use one of the many integrations that
694
- [Opal](http://opalrb.com/) provides, then Opal is the way to go right now.
695
-
696
- ruby2js is for those that want to produce JavaScript that looks like it
697
- wasn’t machine generated, and want the absolute bare minimum in terms of
698
- limitations as to what JavaScript can be produced.
699
-
700
- [Try](http://intertwingly.net/projects/ruby2js.cgi/all) for yourself.
701
- [Compare](http://opalrb.com/try/#code:).
702
-
703
- And, of course, the right solution might be to use
704
- [CoffeeScript](http://coffeescript.org/) instead.
705
48
 
706
49
  License
707
50
  ---