Home

Awesome

babel-plugin-cycle-circular

Babel plugin allowing to have circular dependencies with cycle.js

What?

This (note that bar is used before declared in the code):

  const foo = Foo({value$: bar.value$, DOM})
  const bar = Bar({HTTP, prop$: foo.prop$})

will just work.

This is experimented feature - try and see if it fits your needs, if something wrong or it doesn't cover you usage scenarios just create an issue and we'll try to fix it.

How does it work

This is your ES6 source:

import {ComponentFoo} from './ComponentFoo'
import {ComponentBar} from './ComponentBar'

const main = ({DOM, HTTP}) => {

  const componentFoo = ComponentFoo({value$: componentBar.value$, DOM})
  const componentBar = ComponentBar({HTTP, componentFoo.prop$})

  return {
    DOM: componentFoo.DOM,
    HTTP: componentBar.HTTP
  }
}

To get the same result without this plugin you may sacrifice functional style and do the following with your hands:

// import subject which is usually not needed
import {Subject} from 'rx'
import {ComponentFoo} from './ComponentFoo'
import {ComponentBar} from './ComponentBar'

const main = ({DOM, HTTP}) => {
  // declare proxy subject which will be used to subscribe 
  // to target stream, and be a source for consumption
  const valueProxy$ = new Subject()
  // make proxy stream safe - when it ends (or terminates) 
  // remove subscription to prevent memory leak 
  const valueSafeProxy$ = valueProxy$.finally(() => {
    valueProxySub.dispose()
  })

  const componentFoo = ComponentFoo({valueSafeProxy$, DOM})
  const componentBar = ComponentBar({HTTP, componentFoo.prop$})

  // create subscription for target stream 
  // subscription is actually `side effect`   
  const valueProxySub = componentBar.value$.subscribe(valueProxy$)

  return {
    DOM: componentFoo.DOM,
    HTTP: componentBar.HTTP
  }
}

This also work for most.js library, as if you write this:

import {subject} from 'most-subject'
import {ComponentFoo} from './ComponentFoo'
import {ComponentBar} from './ComponentBar'

const main = ({DOM, HTTP}) => {

  const proxy = subject()

  const componentFoo = ComponentFoo({proxy.stream, DOM})
  const componentBar = ComponentBar({HTTP, componentFoo.prop$})

  const valueProxySub = componentBar.value$
     .observe(proxy.observer.next)
     .then(proxy.observer.complete)
     .catch(proxy.observer.error)

  return {
    DOM: componentFoo.DOM,
    HTTP: componentBar.HTTP
  }
}

Usage

npm install babel-plugin-cycle-circular

Just add plugin to to your .babelrc file or transform options:

{
  "presets": ["es2015"],
  "plugins": ["cycle-circular"]
}

Conventions

NB! Works with rxjs v4, rxjs v5 and most.js ES6 sources. If you want to use CJS source you should require Subject manually.

Options

There are some options that you can supply to the plugin:

Options example: This options for plugin will exclude from processing all files in models/ folder and will proxy only if last identifier of reference ends with $ for example component.value$, (references like component.value won't be handled)

{
  "presets": ["es2015"],
  "plugins": [
    ["cycle-circular", {
        "lib": "rxjs",
        "identifiers": ["\\$$"],
        "exlude": ["**/models/**"]  
    }]
  ]
}

Is it safe to use?

Technically, it just traverse (scans) each function during babel transpilation of your code to find variable references that go before declaration and applies proxy via subject . It was said that the plugin is experimental so if something goes wrong you should see it during development.

Tests

Tests checks if actual transformed source from fixtures corresponds to fixed transformed version in the same fixtures/{case} folder.

npm run test