ruby2js 3.6.0 → 3.6.1

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.
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