babel-plugin-jsx-control-statements
Advanced tools
Comparing version 4.0.0 to 4.1.0
@@ -29,1 +29,2 @@ - 1.0.0 [2015/02/26]: First release | ||
- 3.2.8 [2017/10/02]: Added table of contents to readme. | ||
- 4.0.0 [2018/10/19]: Changed dependencies over to babel 7 (thanks @spoldman!), moved new Flowtype statements into the main flow file. |
{ | ||
"name": "babel-plugin-jsx-control-statements", | ||
"version": "4.0.0", | ||
"version": "4.1.0", | ||
"description": "Neater control statements (if/for) for jsx", | ||
@@ -14,2 +14,3 @@ "author": { | ||
}, | ||
"types": "index.ts", | ||
"bugs": { | ||
@@ -32,3 +33,3 @@ "url": "https://github.com/AlexGilleran/jsx-control-statements/issues" | ||
"lint": "eslint src spec", | ||
"test": "mocha spec/*.js", | ||
"test": "mocha spec/tests.js", | ||
"cover": "istanbul cover -x \"**/spec/**\" node_modules/mocha/bin/_mocha spec/*.js" | ||
@@ -43,14 +44,14 @@ }, | ||
"babel-eslint": "^10.0.1", | ||
"chai": "^3.5.0", | ||
"chai-spies": "^0.7.1", | ||
"coveralls": "^2.11.6", | ||
"eslint": "^3.4.0", | ||
"eslint-config-airbnb": "^10.0.1", | ||
"chai": "^4.2.0", | ||
"chai-spies": "^1.0.0", | ||
"coveralls": "^3.0.9", | ||
"eslint": "^6.8.0", | ||
"eslint-config-airbnb": "^18.1.0", | ||
"eslint-plugin-jsx-control-statements": "^2.1.0", | ||
"eslint-plugin-react": "^6.2.0", | ||
"eslint-plugin-react": "^7.19.0", | ||
"istanbul": "^0.4.1", | ||
"mocha": "^3.0.2", | ||
"react": "^15.3.1", | ||
"react-dom": "^15.3.1" | ||
"mocha": "^7.1.0", | ||
"react": "^16.13.0", | ||
"react-dom": "^16.13.0" | ||
} | ||
} |
217
README.md
# JSX Control Statements | ||
[![Build Status](https://travis-ci.org/AlexGilleran/jsx-control-statements.svg?branch=master)](https://travis-ci.org/AlexGilleran/jsx-control-statements) [![Coverage Status](https://coveralls.io/repos/AlexGilleran/jsx-control-statements/badge.svg?branch=master&service=github)](https://coveralls.io/github/AlexGilleran/jsx-control-statements?branch=master) [![npm version](https://img.shields.io/npm/v/jsx-control-statements.svg?style=flat)](https://www.npmjs.com/package/jsx-control-statements) | ||
[![Build Status](https://travis-ci.org/AlexGilleran/jsx-control-statements.svg?branch=master)](https://travis-ci.org/AlexGilleran/jsx-control-statements) [![Coverage Status](https://coveralls.io/repos/AlexGilleran/jsx-control-statements/badge.svg?branch=master&service=github)](https://coveralls.io/github/AlexGilleran/jsx-control-statements?branch=master) [![npm version](https://img.shields.io/npm/v/babel-plugin-jsx-control-statements.svg?style=flat)](https://www.npmjs.com/package/babel-plugin-jsx-control-statements) | ||
*JSX-Control-Statements* is a Babel plugin that extends JSX to add basic control statements: **conditionals** and **loops**. | ||
_JSX-Control-Statements_ is a Babel plugin that extends JSX to add basic control statements: **conditionals** and **loops**. | ||
It does so by transforming component-like control statements to their JavaScript counterparts - e.g. `<If condition={condition()}>Hello World!</If>` becomes `condition() ? 'Hello World!' : null`. | ||
@@ -10,5 +10,8 @@ | ||
The only dependency *JSX-Control-Statements* relies upon is *Babel*. It is compatible with React and React Native. | ||
The only dependency _JSX-Control-Statements_ relies upon is _Babel_. It is compatible with React and React Native. | ||
:skull_and_crossbones: Beware: This is a Babel plugin. It changes your code to other code - this means that some tooling that looks at your code (e.g. static analysis, typescript) is likely to not work. This plugin dates back to when JSX was daring and Javascript was more like playdough than meccano - if you want to stay on the well-trodden path stick with writing `&&` and `map`. | ||
## Table of Contents | ||
- [A Note on Transformation and Alternative Solutions](#a-note-on-transformation-and-alternative-solutions) | ||
@@ -39,2 +42,3 @@ - [Installation](#installation) | ||
- [React Components](#react-components) | ||
- [What about Typescript](#what-about-typescript) | ||
- [Major Versions](#major-versions) | ||
@@ -44,2 +48,3 @@ - [I Want to Contribute!](#i-want-to-contribute) | ||
### A Note on Transformation and Alternative Solutions | ||
It appears to be pretty easy to implement **conditionals as React component**, which is underlined by the amount | ||
@@ -49,7 +54,7 @@ of libraries which have taken this approach. However, all of them suffer from the same major caveat: A React component | ||
those libraries: | ||
```javascript | ||
<IfComponent condition={ item }> | ||
{ item.title } | ||
</IfComponent> | ||
<IfComponent condition={item}>{item.title}</IfComponent> | ||
``` | ||
The error will be "Cannot read property 'title' of undefined", because React will evaluate the body of the custom | ||
@@ -59,3 +64,3 @@ component and pass it as "children" property to it. The only workaround is to force React into lazy evaluation by | ||
This is the reason why conditionals must be implemented in pure JS. *JSX-Control-Statements* only adds the | ||
This is the reason why conditionals must be implemented in pure JS. _JSX-Control-Statements_ only adds the | ||
syntactic sugar to write conditionals as component, while it transforms this "component" to a pure JS expression. | ||
@@ -66,2 +71,3 @@ | ||
## Installation | ||
As a prerequisite you need to have [Babel](https://github.com/babel/babel) installed and configured in your project. | ||
@@ -75,3 +81,4 @@ | ||
Then you only need to specify *JSX-Control-Statements* as Babel plugin, which you would typically do in your `.babelrc`. | ||
Then you only need to specify _JSX-Control-Statements_ as Babel plugin, which you would typically do in your `.babelrc`. | ||
``` | ||
@@ -85,2 +92,3 @@ { | ||
If you use the `transform-react-inline-elements` plugin, place it _after_ `jsx-control-statements`: | ||
``` | ||
@@ -98,2 +106,3 @@ { | ||
## Syntax | ||
### If Tag | ||
@@ -117,10 +126,13 @@ | ||
``` | ||
#### <If> | ||
The body of the if statement only gets evaluated if `condition` is true. | ||
Prop Name | Prop Type | Required | ||
--------- | --------- | -------- | ||
condition | boolean | :white_check_mark: | ||
| Prop Name | Prop Type | Required | | ||
| --------- | --------- | ------------------ | | ||
| condition | boolean | :white_check_mark: | | ||
#### _<Else /> (deprecated)_ | ||
The else element has no properties and demarcates the `else` branch. | ||
@@ -132,14 +144,19 @@ | ||
#### Transformation | ||
If statements transform to the *ternary operator*: | ||
If statements transform to the _ternary operator_: | ||
```javascript | ||
// before transformation | ||
<If condition={ test }> | ||
<If condition={test}> | ||
<span>Truth</span> | ||
</If> | ||
</If>; | ||
// after transformation | ||
{ test ? <span>Truth</span> : null } | ||
{ | ||
test ? <span>Truth</span> : null; | ||
} | ||
``` | ||
### Choose Tag | ||
This is an alternative syntax for more complex conditional statements. The syntax itself is XMLish and conforms by and | ||
@@ -172,2 +189,3 @@ large to JSTL or XSLT (the attribute is called `condition` instead of `test`): | ||
#### <Choose> | ||
Acts as a simple container and only allows for `<When>` and `<Otherwise>` as children. | ||
@@ -178,12 +196,15 @@ Each `<Choose>` statement requires at least one `<When>` block but may contain as many as desired. | ||
#### <When> | ||
Analog to `<If>`. | ||
Prop Name | Prop Type | Required | ||
--------- | --------- | -------- | ||
condition | boolean | :white_check_mark: | ||
| Prop Name | Prop Type | Required | | ||
| --------- | --------- | ------------------ | | ||
| condition | boolean | :white_check_mark: | | ||
#### <Otherwise> | ||
`<Otherwise>` has no attributes and demarcates the else branch of the conditional. | ||
#### Transformation | ||
This syntax desugars into a (sequence of) ternary operator(s). | ||
@@ -194,6 +215,6 @@ | ||
<Choose> | ||
<When condition={ test1 }> | ||
<When condition={test1}> | ||
<span>IfBlock1</span> | ||
</When> | ||
<When condition={ test2 }> | ||
<When condition={test2}> | ||
<span>IfBlock2</span> | ||
@@ -204,6 +225,14 @@ </When> | ||
</Otherwise> | ||
</Choose> | ||
</Choose>; | ||
// After transformation | ||
{ test1 ? <span>IfBlock1</span> : test2 ? <span>IfBlock2</span> : <span>ElseBlock</span> } | ||
{ | ||
test1 ? ( | ||
<span>IfBlock1</span> | ||
) : test2 ? ( | ||
<span>IfBlock2</span> | ||
) : ( | ||
<span>ElseBlock</span> | ||
); | ||
} | ||
``` | ||
@@ -214,2 +243,3 @@ | ||
Define `<For>` like so: | ||
```javascript | ||
@@ -228,31 +258,44 @@ // you must provide the key attribute yourself | ||
Prop Name | Prop Type | Required | description | ||
--------- | --------- | -------- | ----------- | ||
of | array or collection(Immutable) | :white_check_mark: | the array to iterate over. This can also be a collection (Immutable.js) or anything on which a function with the name `map` can be called | ||
each | string | | a reference to the current item of the array which can be used within the body as variable | ||
index | string | | a reference to the index of the current item which can be used within the body as variable | ||
| Prop Name | Prop Type | Required | description | | ||
| --------- | ------------------------------ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | | ||
| of | array or collection(Immutable) | :white_check_mark: | the array to iterate over. This can also be a collection (Immutable.js) or anything on which a function with the name `map` can be called | | ||
| each | string | | a reference to the current item of the array which can be used within the body as variable | | ||
| index | string | | a reference to the index of the current item which can be used within the body as variable | | ||
Note that a `<For>` *cannot* be at the root of a `render()` function in a React component, because then you'd | ||
Note that a `<For>` _cannot_ be at the root of a `render()` function in a React component, because then you'd | ||
potentially have multiple components without a parent to group them which isn't allowed. As with `<If>`, the same rules | ||
as using `Array.map()` apply - each element inside the loop should have a `key` attribute that uniquely identifies it. | ||
#### Transformation | ||
There is no implementation for the map function within *jsx-control-statements*. We only expect that a | ||
function can be called on the passed object (to the `of` attribute) which has the same signature as `Array.map`. | ||
#### For Tag - Alternative Syntax | ||
For those using Typescript, the previous syntax introduces several issues with undefined variables. To deal with this issue, we introduce a following syntax, inspired by [tsx-control-statements](https://www.npmjs.com/package/tsx-control-statements). | ||
```javascript | ||
// before transformation | ||
<For each="item" index="index" of={ items }> | ||
<span key={ item.id }>{ index }. { item.title }</span> | ||
</For> | ||
<For | ||
of={items} | ||
body={(item, index) => ( | ||
<span key={item.id}> | ||
{index}. {item.title} | ||
</span> | ||
)} | ||
/>; | ||
// after transformation | ||
{ | ||
items.map( function(item, index) { | ||
<span key={ item.id }>{ index }. { item.title }</span> | ||
}) | ||
items.map(function(item, index) { | ||
<span key={item.id}> | ||
{index}. {item.title} | ||
</span>; | ||
}); | ||
} | ||
``` | ||
| Prop Name | Prop Type | Required | description | | ||
| --------- | ------------------------------ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | | ||
| of | array or collection(Immutable) | :white_check_mark: | the array to iterate over. This can also be a collection (Immutable.js) or anything on which a function with the name `map` can be called | | ||
| body | map expression | | expression of the map statement | | ||
### With Tag | ||
Used to assign values to local variables: | ||
@@ -276,5 +319,5 @@ | ||
Prop Name | Prop Type | Required | description | ||
--------- | --------- | -------- | ----------- | ||
any name | any type | | assign prop value to a local variable named by prop name | ||
| Prop Name | Prop Type | Required | description | | ||
| --------- | --------- | -------- | -------------------------------------------------------- | | ||
| any name | any type | | assign prop value to a local variable named by prop name | | ||
@@ -285,2 +328,3 @@ You may assign multiple variables with a single `<With>` statement. The defined variable is | ||
#### Transformation | ||
`<With>` statements transform to immediately-invoked function expressions: | ||
@@ -290,12 +334,11 @@ | ||
// before transformation | ||
<With foo={ 47 }> | ||
<span>{ foo }</span> | ||
</With> | ||
<With foo={47}> | ||
<span>{foo}</span> | ||
</With>; | ||
// after transformation | ||
{ | ||
(function(foo) { | ||
return <span>{ foo }</span> | ||
}).call(this, 47) | ||
return <span>{foo}</span>; | ||
}.call(this, 47)); | ||
} | ||
@@ -305,3 +348,5 @@ ``` | ||
## Linting | ||
### ESLint | ||
Since all control statements are transformed via Babel, no `require` or `import` calls are needed. This in turn | ||
@@ -311,6 +356,7 @@ (well, and some more cases) would lead to warnings or errors by ESLint about undefined variables. | ||
But fortunately you can use this | ||
[ESLint plugin for *JSX-Control-Statements*](https://github.com/vkbansal/eslint-plugin-jsx-control-statements) | ||
[ESLint plugin for _JSX-Control-Statements_](https://github.com/vkbansal/eslint-plugin-jsx-control-statements) | ||
to lint your code. | ||
### FlowType | ||
There's still not a perfect solution for FlowType given that it doesn't provide a lot of plugin functionality | ||
@@ -338,24 +384,41 @@ (at least not yet). Flow definitions are available in `jsx-control-statements.latest.flow.js` for Flow >= 0.53, or `jsx-control-statements.flow.js` (deprecated) for Flow < 0.53 - you can pick which file to use [like this](https://github.com/AlexGilleran/jsx-control-statements/pull/68#issuecomment-323562980). These will stop the | ||
### Pure JavaScript | ||
Since everything will be compiled to JavaScript anyway, you might prefer to stick to pure JavaScript solutions. | ||
#### Conditionals | ||
Probably the most common way for simple conditionals is the use of the && operator: | ||
```javascript | ||
// simple if | ||
{ test && <span>true</span> } | ||
{ | ||
test && <span>true</span>; | ||
} | ||
// additionally the else branch | ||
{ !test && <span>false</span> } | ||
{ | ||
!test && <span>false</span>; | ||
} | ||
``` | ||
The ternary operator is probably more elegant for if / else conditionals: | ||
```javascript | ||
// simple | ||
{ test ? <span>true</span> : <span>false</span> } | ||
{ | ||
test ? <span>true</span> : <span>false</span>; | ||
} | ||
// with multiple children | ||
{ test ? [<span key="1">one</span>, <span key="2">two</span>] : <span>false</span> } | ||
{ | ||
test ? ( | ||
[<span key="1">one</span>, <span key="2">two</span>] | ||
) : ( | ||
<span>false</span> | ||
); | ||
} | ||
``` | ||
Another approach is to refactor your conditional into a function: | ||
```javascript | ||
@@ -379,25 +442,31 @@ testFunc(condition){ | ||
#### Loops | ||
Not many options here: | ||
```javascript | ||
{ items.map(function(item) { | ||
<span key={ item.id }>{ item. title }</span> | ||
}) } | ||
{ | ||
items.map(function(item) { | ||
<span key={item.id}>{item.title}</span>; | ||
}); | ||
} | ||
``` | ||
#### Comparison | ||
Arguments pro *JSX-Control-Statements* in comparison to pure JS solutions: | ||
* More intuitive and easier to handle for designers and people with non-heavy JS background | ||
* JSX does not get fragmented by JS statements | ||
* Better readability and neatness, but that probably depends on you | ||
Arguments pro _JSX-Control-Statements_ in comparison to pure JS solutions: | ||
- More intuitive and easier to handle for designers and people with non-heavy JS background | ||
- JSX does not get fragmented by JS statements | ||
- Better readability and neatness, but that probably depends on you | ||
Cons: | ||
* Penalty on build-time performance | ||
* Depends on Babel 6 | ||
* Some Babel configuration | ||
- Penalty on build-time performance | ||
- Depends on Babel 6 | ||
- Some Babel configuration | ||
### React Components | ||
There are a reasonable amount of React components for conditionals (e.g. [react-if](https://github.com/romac/react-if), which inspired this in the first place), *JSX-Control-Statements* is the only approach we know of that avoids execution of all branches (see the [intro section](#a-note-on-transformation-and-alternative-solutions)), and there seems to be no other component-based solution to looping - while it would be possible to make a component that renders everything in `props.children` for every element of an array, you'd have to access the members of the array in that component instead of the one that uses it. | ||
There are a reasonable amount of React components for conditionals (e.g. [react-if](https://github.com/romac/react-if), which inspired this in the first place), _JSX-Control-Statements_ is the only approach we know of that avoids execution of all branches (see the [intro section](#a-note-on-transformation-and-alternative-solutions)), and there seems to be no other component-based solution to looping - while it would be possible to make a component that renders everything in `props.children` for every element of an array, you'd have to access the members of the array in that component instead of the one that uses it. | ||
For more discussion on `If` in React by the react team, have a look at https://github.com/reactjs/react-future/issues/35. | ||
@@ -407,16 +476,21 @@ | ||
* Conditionals don't execute invalid paths | ||
* Loops with variable references to each element and index are made possible | ||
* No penalty on runtime performance | ||
* No import / require statements needed to use control statements | ||
* It works exactly as JSX is supposed to work: Plain syntactic sugar | ||
- Conditionals don't execute invalid paths | ||
- Loops with variable references to each element and index are made possible | ||
- No penalty on runtime performance | ||
- No import / require statements needed to use control statements | ||
- It works exactly as JSX is supposed to work: Plain syntactic sugar | ||
Cons: | ||
* Depends on Babel 6 | ||
* Some Babel configuration | ||
* Slightly longer build times | ||
* Requires an extra plugin to work with ESLint | ||
- Depends on Babel 6 | ||
- Some Babel configuration | ||
- Slightly longer build times | ||
- Requires an extra plugin to work with ESLint | ||
## What about Typescript? | ||
[There's a version for that by @KonstantinSimeonov!](https://github.com/KonstantinSimeonov/tsx-control-statements) | ||
## Major Versions | ||
- 4.x.x is a pure Babel plugin supporting Babel >= 7. | ||
@@ -431,2 +505,3 @@ - 3.x.x is a pure Babel plugin supporting Babel >= 6. | ||
## I Want to Contribute! | ||
Yay! Please read the [Contributor's Guide](https://github.com/AlexGilleran/jsx-control-statements/blob/master/CONTRIBUTING.md). |
@@ -10,3 +10,4 @@ var astUtil = require("./util/ast"); | ||
OF: "of", | ||
INDEX: "index" | ||
INDEX: "index", | ||
BODY: "body" | ||
}; | ||
@@ -44,3 +45,6 @@ | ||
var children = astUtil.getChildren(types, node); | ||
var returnExpression = astUtil.getSanitizedExpressionForContent(types, children); | ||
var returnExpression = astUtil.getSanitizedExpressionForContent( | ||
types, | ||
children | ||
); | ||
@@ -51,2 +55,12 @@ // required attribute | ||
} | ||
if (attributes[ATTRIBUTES.BODY]) { | ||
return types.callExpression( | ||
types.memberExpression( | ||
attributes[ATTRIBUTES.OF].value.expression, | ||
types.identifier("map") | ||
), | ||
[attributes[ATTRIBUTES.BODY].value.expression, types.identifier("this")] | ||
); | ||
} | ||
// check for correct data types, as far as possible | ||
@@ -74,5 +88,3 @@ checkForExpression(attributes, ATTRIBUTES.OF, errorInfos); | ||
mapParams, | ||
types.blockStatement([ | ||
types.returnStatement(returnExpression) | ||
]) | ||
types.blockStatement([types.returnStatement(returnExpression)]) | ||
), | ||
@@ -79,0 +91,0 @@ types.identifier("this") |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
17
570
482
39397