babel-plugin-cycle-circular
Advanced tools
Comparing version
@@ -0,1 +1,5 @@ | ||
### v0.1.0 Imports insertion and rxjs/most support | ||
* imports of `Subject` object are added if not found | ||
* added initial most.js and rxjs5 support | ||
### v0.0.1 Initial pre-release | ||
@@ -2,0 +6,0 @@ * only rx@4.x.x |
123
lib/index.js
@@ -7,8 +7,6 @@ 'use strict'; | ||
exports.default = function (_ref2) { | ||
var t = _ref2.types; | ||
exports.default = function (_ref) { | ||
var t = _ref.types; | ||
var makeVisitor = function makeVisitor(scope, options) { | ||
var subjectLiteral = getSubjectLiteral(scope, options); | ||
var subjectIdentifier = t.identifier(subjectLiteral); | ||
var makeVisitor = function makeVisitor(scope, options, lib) { | ||
@@ -30,34 +28,8 @@ var matchIdentifiers = toArray(options.identifiers).map(function (match) { | ||
var getSubFinallyExpression = function getSubFinallyExpression(proxyIdentifier, subIdentifier) { | ||
var disposeExpression = t.callExpression(t.memberExpression(subIdentifier, t.identifier('dispose')), []); | ||
return t.callExpression(t.memberExpression(proxyIdentifier, t.identifier('finally')), [t.arrowFunctionExpression([], disposeExpression)]); | ||
}; | ||
var makeProxy = function makeProxy(toProxy) { | ||
var idName = toProxy.name + '__Proxy'; | ||
var identifier = t.identifier(idName); | ||
var newExpression = t.newExpression(subjectIdentifier, []); | ||
var declaration = t.variableDeclaration('const', [t.variableDeclarator(identifier, newExpression)]); | ||
var originIdentifier = t.identifier(toProxy.source + '.subscribe'); | ||
var subscriberIdentifier = t.identifier(idName + '.asObserver()'); | ||
var subCallExpression = t.callExpression(originIdentifier, [subscriberIdentifier]); | ||
var subIdentifier = t.identifier(toProxy.name + '__ProxySub'); | ||
var subscription = t.variableDeclaration('const', [t.variableDeclarator(subIdentifier, subCallExpression)]); | ||
mainScope._hasCircularProxies = true; | ||
return { | ||
identifier: identifier, | ||
declaration: declaration, subscription: subscription, | ||
finally: getSubFinallyExpression(identifier, subIdentifier) | ||
}; | ||
}; | ||
return { | ||
ReferencedIdentifier: function ReferencedIdentifier(path) { | ||
if (!checkScope(path.scope)) return; | ||
if (!checkScopeIsFunction(path.scope)) return; | ||
var _circular = getScopeCircular(path.scope); | ||
var name = path.node.name; | ||
var name = '__' + path.node.name; | ||
@@ -71,8 +43,9 @@ if (_circular.declarations[name]) { | ||
VariableDeclaration: function VariableDeclaration(path) { | ||
if (!checkScope(path.scope)) return; | ||
if (!checkScopeIsFunction(path.scope)) return; | ||
var _circular = getScopeCircular(path.scope); | ||
var body = path.scope.block.body.body; | ||
path.node.declarations.forEach(function (dec) { | ||
var identifier = _circular.identifiers[dec.id.name]; | ||
var identifier = _circular.identifiers['__' + dec.id.name]; | ||
if (identifier) { | ||
@@ -82,15 +55,10 @@ identifier.paths.forEach(function (path) { | ||
var proxyName = identifierToProxy.name; | ||
if (!matchIdentifierName(proxyName)) { | ||
if (!matchIdentifierName(identifierToProxy.source)) { | ||
return; | ||
} | ||
var proxyName = '__Proxy' + _circular.proxiesCount; | ||
var proxy = lib.makeProxy(proxyName, identifierToProxy.source); | ||
var proxyIndex = _circular.proxies[proxyName] || 0; | ||
identifierToProxy.name += '_' + proxyIndex; | ||
var proxy = makeProxy(identifierToProxy); | ||
body.unshift(proxy.declaration); | ||
identifierToProxy.path.replaceWith(proxy.finally); | ||
identifierToProxy.path.replaceWith(proxy.replaceWith); | ||
@@ -101,3 +69,4 @@ var ret = body[body.length - 1].type == 'ReturnStatement' ? body.pop() : null; | ||
_circular.proxies[proxyName] = proxyIndex + 1; | ||
_circular.proxiesCount++; | ||
mainScope._hasCircularProxies = true; | ||
}); | ||
@@ -113,4 +82,3 @@ } | ||
visitor: { | ||
Program: function Program(path) { | ||
Program: function Program(path, state) { | ||
var scope = path.context.scope; | ||
@@ -130,4 +98,10 @@ var options = this.opts; | ||
if (canMakeCircular(scope, options)) { | ||
path.traverse(makeVisitor(scope, options)); | ||
var lib = void 0, | ||
libName = options.lib || 'rx'; | ||
lib = require('./' + libName); | ||
path.traverse(makeVisitor(scope, options, lib)); | ||
if (scope._hasCircularProxies) { | ||
lib.addImports(path.node, scope); | ||
} | ||
@@ -145,8 +119,11 @@ } | ||
var getIdentifierNameFromNode = function getIdentifierNameFromNode(node) { | ||
if (node.name || node.value) { | ||
return node.name || 'V' + node.value; | ||
} else { | ||
return getIdentifierNameFromNode(node.object) + '_' + getIdentifierNameFromNode(node.property); | ||
var getIdentifierFromPath = function getIdentifierFromPath(path) { | ||
while (path.parentPath.type == 'MemberExpression') { | ||
path = path.parentPath; | ||
} | ||
var node = path.node; | ||
return { | ||
source: path.getSource(), | ||
path: path | ||
}; | ||
}; | ||
@@ -164,16 +141,3 @@ | ||
var getIdentifierFromPath = function getIdentifierFromPath(path) { | ||
// path.parentPath.node.type === 'Identifier' | ||
while (path.parentPath.type == 'MemberExpression') { | ||
path = path.parentPath; | ||
} | ||
var node = path.node; | ||
return { | ||
source: path.getSource(), | ||
name: getIdentifierNameFromNode(node), | ||
path: path | ||
}; | ||
}; | ||
var checkScope = function checkScope(scope) { | ||
var checkScopeIsFunction = function checkScopeIsFunction(scope) { | ||
// FunctionDeclaration | ||
@@ -192,25 +156,6 @@ // ArrowFunctionExpression | ||
declarations: {}, | ||
proxies: {} | ||
proxies: {}, | ||
proxiesCount: 0 | ||
}; | ||
return scope._cycleCircular; | ||
}; | ||
var getSubjectLiteral = function getSubjectLiteral(_ref, options) { | ||
var globals = _ref.globals; | ||
var references = _ref.references; | ||
if (references.Subject) { | ||
return 'Subject'; | ||
} else { | ||
var rxRef = checkItems([options.rxRef, 'Rx', 'rx'], function (prop) { | ||
return (references[prop] || globals[prop]) && prop; | ||
}); | ||
if (rxRef) { | ||
return rxRef + '.Subject'; | ||
} | ||
} | ||
}; | ||
var canMakeCircular = function canMakeCircular(scope, options) { | ||
return getSubjectLiteral(scope, options); | ||
}; |
{ | ||
"name": "babel-plugin-cycle-circular", | ||
"version": "0.0.1", | ||
"version": "0.1.0", | ||
"description": "Babel plugin allowing to have circular dependencies in cycle.js functions.", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -7,7 +7,7 @@ # babel-plugin-cycle-circular | ||
This (note that `componentBar` is *used* **before** *declared*): | ||
This (note that **`bar`** is **used before declared** in the code): | ||
```js | ||
const componentFoo = ComponentFoo({value$: componentBar.value$, DOM}) | ||
const componentBar = ComponentBar({HTTP, componentFoo.prop$}) | ||
const foo = Foo({value$: bar.value$, DOM}) | ||
const bar = Bar({HTTP, prop$: foo.prop$}) | ||
``` | ||
@@ -17,4 +17,7 @@ | ||
## How does it | ||
**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](issues) and we'll try to fix it. | ||
## How does it work | ||
This is your ES6 source: | ||
@@ -72,6 +75,31 @@ | ||
This also work for [`most.js`](https://github.com/cujojs/most) library, as if you write this: | ||
```js | ||
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 | ||
```bash | ||
npm install bable-plugin-cycle-circular | ||
npm install babel-plugin-cycle-circular | ||
``` | ||
@@ -90,10 +118,5 @@ | ||
*NB!* | ||
Current version works only with `rx@4.x.x` requires that you have `Subject`, or `Rx` **imported**: | ||
```js | ||
import {Subject} from 'rx' | ||
``` | ||
or | ||
```js | ||
const Rx = require('rx') | ||
``` | ||
Works with [`rxjs v4`](https://github.com/Reactive-Extensions/RxJS), | ||
[`rxjs v5`](https://github.com/ReactiveX/rxjs) and [`most.js`](https://github.com/cujojs/most) ES6 sources. | ||
If you want to use CJS source you should `require` `Subject` manually. | ||
@@ -103,3 +126,4 @@ ### Options | ||
There are some options that you can supply to the plugin: | ||
* **identifiers** (default: null) - regExp pattern(s) for matching identifiers names that should be proxied. | ||
* **lib** (default: 'rx') - what library rules to use for creating proxies, possilbe values: `"rx", "rxjs", "most"`. | ||
* **identifiers** (default: null) - regExp pattern(s) for matching identifiers names that should be proxied. | ||
* **include** (default: '') - includes files my `minimatch` mask (can be array) | ||
@@ -117,2 +141,3 @@ * **exclude** (default: '') - includes files my `minimatch` mask (can be array) | ||
["cycle-circular", { | ||
"lib": "rxjs", | ||
"identifiers": ["\\$$"], | ||
@@ -125,2 +150,8 @@ "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 | ||
@@ -127,0 +158,0 @@ Tests checks if actual transformed source from `fixtures` |
134
src/index.js
import minimatch from 'minimatch' | ||
const getIdentifierNameFromNode = (node) => { | ||
if (node.name || node.value){ | ||
return node.name || ('V' + node.value) | ||
const getIdentifierFromPath = (path) => { | ||
while (path.parentPath.type == 'MemberExpression') { | ||
path = path.parentPath | ||
} | ||
else { | ||
return getIdentifierNameFromNode(node.object) | ||
+ '_' + getIdentifierNameFromNode(node.property) | ||
var node = path.node | ||
return { | ||
source: path.getSource(), | ||
path | ||
} | ||
@@ -22,16 +23,3 @@ } | ||
const getIdentifierFromPath = (path) => { | ||
// path.parentPath.node.type === 'Identifier' | ||
while (path.parentPath.type == 'MemberExpression') { | ||
path = path.parentPath | ||
} | ||
var node = path.node | ||
return { | ||
source: path.getSource(), | ||
name: getIdentifierNameFromNode(node), | ||
path | ||
} | ||
} | ||
const checkScope = (scope) => { | ||
const checkScopeIsFunction = (scope) => { | ||
// FunctionDeclaration | ||
@@ -49,3 +37,4 @@ // ArrowFunctionExpression | ||
declarations: {}, | ||
proxies: {} | ||
proxies: {}, | ||
proxiesCount: 0 | ||
} | ||
@@ -55,24 +44,5 @@ return scope._cycleCircular | ||
const getSubjectLiteral = ({globals, references}, options) => { | ||
if (references.Subject){ | ||
return 'Subject' | ||
} else { | ||
const rxRef = checkItems([options.rxRef, 'Rx', 'rx'], | ||
(prop) => ((references[prop] || globals[prop]) && prop) | ||
) | ||
if (rxRef){ | ||
return rxRef + '.Subject' | ||
} | ||
} | ||
} | ||
const canMakeCircular = (scope, options) => { | ||
return getSubjectLiteral(scope, options) | ||
} | ||
export default function ({types: t}) { | ||
const makeVisitor = (scope, options) => { | ||
let subjectLiteral = getSubjectLiteral(scope, options) | ||
let subjectIdentifier = t.identifier(subjectLiteral) | ||
const makeVisitor = (scope, options, lib) => { | ||
let matchIdentifiers = toArray(options.identifiers) | ||
@@ -91,45 +61,9 @@ .map(match => new RegExp(match)) | ||
} | ||
const getSubFinallyExpression = function(proxyIdentifier, subIdentifier){ | ||
const disposeExpression = t.callExpression( | ||
t.memberExpression(subIdentifier, t.identifier('dispose')), | ||
[] | ||
) | ||
return t.callExpression( | ||
t.memberExpression(proxyIdentifier, t.identifier('finally')), | ||
[t.arrowFunctionExpression([], disposeExpression)] | ||
) | ||
} | ||
const makeProxy = (toProxy) => { | ||
let idName = toProxy.name + '__Proxy' | ||
let identifier = t.identifier(idName) | ||
let newExpression = t.newExpression(subjectIdentifier, []) | ||
let declaration = t.variableDeclaration('const', [ | ||
t.variableDeclarator(identifier, newExpression) | ||
]) | ||
let originIdentifier = t.identifier(toProxy.source + '.subscribe') | ||
let subscriberIdentifier = t.identifier(idName + '.asObserver()') | ||
let subCallExpression = t.callExpression(originIdentifier, [subscriberIdentifier]) | ||
let subIdentifier = t.identifier(toProxy.name + '__ProxySub') | ||
let subscription = t.variableDeclaration('const', [ | ||
t.variableDeclarator(subIdentifier, subCallExpression) | ||
]) | ||
mainScope._hasCircularProxies = true | ||
return { | ||
identifier, | ||
declaration, subscription, | ||
finally: getSubFinallyExpression(identifier, subIdentifier) | ||
} | ||
} | ||
return { | ||
ReferencedIdentifier (path) { | ||
if (!checkScope(path.scope)) return | ||
if (!checkScopeIsFunction(path.scope)) return | ||
let _circular = getScopeCircular(path.scope) | ||
let name = path.node.name | ||
let name = '__' + path.node.name | ||
@@ -144,26 +78,21 @@ if (_circular.declarations[name]){ | ||
VariableDeclaration (path) { | ||
if (!checkScope(path.scope)) return | ||
if (!checkScopeIsFunction(path.scope)) return | ||
let _circular = getScopeCircular(path.scope) | ||
var body = path.scope.block.body.body | ||
path.node.declarations.forEach(dec => { | ||
let identifier = _circular.identifiers[dec.id.name] | ||
let identifier = _circular.identifiers['__' + dec.id.name] | ||
if (identifier){ | ||
identifier.paths.forEach((path) => { | ||
var identifierToProxy = getIdentifierFromPath(path) | ||
let proxyName = identifierToProxy.name | ||
if (!matchIdentifierName(proxyName)){ | ||
if (!matchIdentifierName(identifierToProxy.source)){ | ||
return | ||
} | ||
let proxyIndex = | ||
(_circular.proxies[proxyName] || 0) | ||
identifierToProxy.name += '_' + proxyIndex | ||
let proxy = makeProxy(identifierToProxy) | ||
let proxyName = '__Proxy' + _circular.proxiesCount | ||
let proxy = lib.makeProxy(proxyName, identifierToProxy.source) | ||
body.unshift(proxy.declaration) | ||
identifierToProxy.path.replaceWith(proxy.finally) | ||
identifierToProxy.path.replaceWith(proxy.replaceWith) | ||
@@ -175,3 +104,4 @@ let ret = body[body.length - 1].type == 'ReturnStatement' | ||
_circular.proxies[proxyName] = proxyIndex + 1 | ||
_circular.proxiesCount++ | ||
mainScope._hasCircularProxies = true | ||
}) | ||
@@ -187,8 +117,7 @@ } | ||
visitor: { | ||
Program (path) { | ||
Program (path, state) { | ||
const scope = path.context.scope | ||
const options = this.opts | ||
const filename = this.file.opts.filename | ||
const filterFiles = options.include || options.exclude | ||
@@ -204,4 +133,9 @@ if (filterFiles){ | ||
if (canMakeCircular(scope, options)){ | ||
path.traverse(makeVisitor(scope, options)) | ||
let lib, libName = options.lib || 'rx' | ||
lib = require('./' + libName) | ||
path.traverse(makeVisitor(scope, options, lib)) | ||
if (scope._hasCircularProxies){ | ||
lib.addImports(path.node, scope) | ||
} | ||
@@ -208,0 +142,0 @@ } |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
420
25%155
25%23271
-62.43%13
-18.75%3
200%1
Infinity%