Rxjs: Modular ReactiveX builds to support babel-plugin-transform-imports

Created on 13 Mar 2017  路  17Comments  路  Source: ReactiveX/rxjs

Transforms

import { Observable } from 'rxjs';
import { timer, of } from 'rxjs/observable';
import { mapTo, combineAll } from 'rxjs/operator';

Observable::timer(2000)
  ::mapTo(Observable::of('Hello', 'World'))
  ::combineAll()
  .subscribe(value => console.log(value));

Roughly to

import { Observable } from 'rxjs/Observable';
import { timer } from 'rxjs/observable/timer';
import { of } from 'rxjs/observable/of';
import { mapTo } from 'rxjs/operator/mapTo';
import { combineAll } from 'rxjs/operator/combineAll';

Observable::timer(2000)
  ::mapTo(Observable::of('Hello', 'World'))
  ::combineAll()
  .subscribe(value => console.log(value));

support default exports

import Observable from 'rxjs/Observable';
import timer from 'rxjs/observable/timer';
import of from 'rxjs/observable/of';
import mapTo from 'rxjs/operator/mapTo';
import combineAll from 'rxjs/operator/combineAll';

Observable::timer(2000)
  ::mapTo(Observable::of('Hello', 'World'))
  ::combineAll()
  .subscribe(value => console.log(value));

Most helpful comment

Found a solution. babel-plugin-transform-imports allows me to use the following syntax:

import {Observable} from 'rxjs/Observable'
import {of, from} from 'rxjs/observable'
import {switchMap, filter, map, _catch} from 'rxjs/operator'

This is the config I use for this in my .babelrc:

    [
      "transform-imports",
      {
        "lodash": {
          "transform": "lodash/${member}",
          "preventFullImport": true
        },
        "rxjs/operator": {
          "transform": "rxjs/operator/${member}",
          "preventFullImport": true,
          "camelCase": true,
          "skipDefaultConversion": true
        },
        "rxjs/observable": {
          "transform": "rxjs/observable/${member}",
          "camelCase": true,
          "preventFullImport": true,
          "skipDefaultConversion": true
        }
      }
    ]

All 17 comments

is this a feature request or has somebody made one?

feature request

Check out babel-plugin-transform-imports. To get it working we'll need to add default exports, or optionally the babel plugin authors can accommodate named exports.

default exports

IIRC, default exports ended up being no-beuno for a variety of reasons related to various bundlers.

@benlesh it's worth re-investigating. I haven't had any trouble mixing named and default exports in JS like this in a long time: export { Observable }; export default Observable;

@trxcllnt I don't think that the benefits we'd get by moving everything to use default exports would be worth the breaking change.

@benlesh from what I understand it's not a breaking change, as you can do named and default exports together from the same module. For example, this works fine:

class Observable {}
export { Observable };
export default Observable;

/* ...somewhere else... */
import { Observable } from 'rxjs/Observable';

/* and this is fine too */
import Observable from 'rxjs/Observable';

Additionally, we're using babel-plugin-transform-imports in production now, and it's working great. I tried it on Rx, but it doesn't work because our modules don't have default exports. If we did, we could get full tree-shaking for Rx in production. I'll probably do a PR soon.

I'm not really sold on this idea. It seems like an addition for some current module consumption pattern du jour. At the cost of having users get confused about how they should be consuming the library.

@benlesh ?? it's the es6 module spec, not some fad javascript aesthetic. Most libraries in the react ecosystem are written default-export-style, and so are the build tools. All we have to do is add export default <FileName>; to the end of all our files, and we can close the book on the whole tree-shaking debacle.

Need to output ES modules.

@trxcllnt I know that export default whatever is in the spec. I'm pretty sure nearly everyone that's use ES6 modules knows that. That's not at all the "du jour" I'm talking about.

To clarify: I'm not a fan of exporting the same thing under two different exports from the same module to support a module-bundler-loader-transpiler-babel-tool-thing.

I think exporting the same thing in two ways from every module in the library is a colossally bad idea unless we absolutely have to. I'm not at all convinced by this issue that we have to. We need to pave a road for our users. RxJS is already confusing enough to use in terms of what module to pull in where and how.

@benlesh I think exporting the same thing in two ways from every module in the library is a colossally bad idea

Are there reasons you feel this way? And if so, why do you feel they're more important than enabling users to succeed at reducing the size of their application bundles?

@benlesh I think the confusing part is that you choose to not export default. 99% of libraries out there export on default when a module has a but a single export, let alone with the same name as the module.

Now, nobody expects you to perform breaking changes and export only on default. And, yes, you are right, it is not that great to export 'the same thing under two different exports from the same module'.

However, the reason to do it is not 'to support a module-bundler-loader-transpiler-babel-tool-thing' but rather to avoid this confusion of rxjs users caused by this non-standard (even if de-facto standard) export behavior of the rxjs library.

In addition, as a result of aligning to the standard, you can allow them to easily create more readable and short code, e.g:

import {Observable} from 'rxjs/Observable'
import {empty} from 'rxjs/observable/empty'
import {map} from 'rxjs/operator/map'
import {do} from 'rxjs/operator/do'

can be easily, and w/o losing package size, rewritten as:

import {Observable, empty, map, do} from 'rxjs/lib'

Also, if I now need to change something, say replace map to switchMap, I need to do it in 2 places on the top, and only in one place at the bottom. The verbose edition is not only long and much less readable, its also error prone, breaks single point of truth and 'convention over configuration' ... you name it.

To conclude, aligning to the standard, greatly reduces confusion, not add to it. I will allow rxjs users to use existing plugins and tools and come up eventually with a much better and readable code. To me, this seems like a rather compelling reason to deviate from personal taste or past choices.

The benefits from this change are huge while the cost is minimal.

Found a solution. babel-plugin-transform-imports allows me to use the following syntax:

import {Observable} from 'rxjs/Observable'
import {of, from} from 'rxjs/observable'
import {switchMap, filter, map, _catch} from 'rxjs/operator'

This is the config I use for this in my .babelrc:

    [
      "transform-imports",
      {
        "lodash": {
          "transform": "lodash/${member}",
          "preventFullImport": true
        },
        "rxjs/operator": {
          "transform": "rxjs/operator/${member}",
          "preventFullImport": true,
          "camelCase": true,
          "skipDefaultConversion": true
        },
        "rxjs/observable": {
          "transform": "rxjs/observable/${member}",
          "camelCase": true,
          "preventFullImport": true,
          "skipDefaultConversion": true
        }
      }
    ]

Hey guys, I'm really excited about the recent surge in popularity of observable, reactive, stream-based programming. I'm grateful for the role ReactiveX and rxjs have played in this. I hope you can take the following feedback in the constructive manner it's intended.

As a developer looking for tooling, this kind of immediate, blanket rejection of feedback definitely gives me a strong hint that I might be better served contributing to/working with another project in the space.

We need to pave a road for our users. RxJS is already confusing enough to use in terms of what module to pull in where and how.

If the issue is just that the suggestions so far on how to improve clarity of use haven't been compelling enough, I'd encourage taking some cues from lodash or ramda's recent refactoring work:

https://github.com/ramda/ramda/pull/618
https://www.npmjs.com/browse/keyword/lodash-modularized

If you'd prefer to leave the source code untouched, another strategy for improving developer accessibility might be to distribute a bespoke babel transformation, similar to this one for ramda:

https://www.npmjs.com/package/babel-plugin-ramda

Closing as stale

@benlesh I'd have appreciated a response at some point in the last year. 馃槥

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cartant picture cartant  路  3Comments

benlesh picture benlesh  路  3Comments

chalin picture chalin  路  4Comments

peterbakonyi05 picture peterbakonyi05  路  4Comments

OliverJAsh picture OliverJAsh  路  3Comments