ruby2js 3.6.0 → 3.6.1

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