You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* added boilerplates
* renamed to avoid conflict
* added a failing test
* added initial working version
* added support for mixins
* added a test for ES6 export
* renamed liftGetInitialState
* changed to update the existing one instead
* fixed arrow function return type
* temporally added npm shrinkwrap
* changed initial state transformation
* added react-addons-pure-render-mixin support
* Revert "temporally added npm shrinkwrap"
This reverts commit f5f6da0.
* dont sort and bind everything
* prune unused requires safely and add support for primitives as class properties
* updated README
* updated constructor args (only use props/context when needed)
* added support for static methods
* dont bind getChildContext
* handle constructor arguments properly
* fixed incorrect behavior when mixins is a non-array value
* fix early returns in getInitialState
* WIP flow transformation; switched parser to Flow
* flow works now
* added support for flow property initializers
* better way to detect early returns in getInitialState
* bail out if user uses getInitialState or getDefaultProps elsewhere
* bail out if arguments is found
* defer state property initializer evaluation when necessary
* no shadowing in constructor
* handle inner function declarations in getInitialState correctly
* fix lint errors
* displayName shouldn't show up twice
* fixed anonymous createClass
* covered more flow edge cases
* handle nullable prop types correctly
* always print parens for single arg arrow functions
* fixed edge case when class spec is not an object expression
* support literal keys in prop types
* added TypeParameter and NullTypeAnnotation to ast-types
* renamed recast option to flowObjectCommas
* no trailing comma for single-line flow object types
* catch edge case where React.createClass() is called with nothing
* improved the logic of repositioning `state` property
* added `pure-component` option
* updated npm-shrinkwrap
* rename this.context to context in constructor
* fixed how we identify shadowing issues
* updated README.md
* use FlowFixMe for unrecognizable types
* retain getInitialState()'s return type when possible
* stricter checking of referencing APIs that will be removed
* optional !== nullable
* support void (undefined) in PropTypes.oneOf()
* annotate state type when it's inlined in the constructor
* added 'remove-runtime-proptypes' option
* changed inexplicit any to FlowFixMe
* Redesigned the way we start modding components.
Now we search and grab `React.createClass` _call expressions_ directly. The only time that we can't simply replace a `createClass` call path with a new class is when the parent of that is a variable declaration:
`var Foo = React.createClass({...});`
needs to be replaced entirely with
`class Foo extends React.Component {...}`
Now we delay checking it and only when we need to replace a path we take a look at `path.parentPath.value.type` to see if it's a variable declarator. With this commit we should be able to mod any kind of anonymous `createClass` calls no matter how they are defined.
* fixed default and rest params
* retain top comments when pure-render-mixin is the first node of the body
* fixed trailing comma for func rest params; updated recast
* handle top comments better
* OtherClass.getDefaultProps() -> OtherClass.defaultProps
* updated README
* Ignore components with calls to deprecated APIs. This is very defensive, if
107
-
the script finds any identifiers called `isMounted`, `getDOMNode`,
108
-
`replaceProps`, `replaceState` or `setProps` it will skip the component.
109
-
* Replaces `var A = React.createClass(spec)` with
110
-
`class A (extends React.Component) {spec}`.
111
-
* Pulls out all statics defined on `statics` plus the few special cased
112
-
statics like `propTypes`, `childContextTypes`, `contextTypes` and
113
-
`displayName` and assigns them after the class is created.
114
-
`class A {}; A.foo = bar;`
115
-
* Takes `getDefaultProps` and inlines it as a static `defaultProps`.
116
-
If `getDefaultProps` is defined as a function with a single statement that
117
-
returns an object, it optimizes and transforms
118
-
`getDefaultProps() { return {foo: 'bar'}; }` into
119
-
`A.defaultProps = {foo: 'bar'};`. If `getDefaultProps` contains more than
120
-
one statement it will transform into a self-invoking function like this:
121
-
`A.defaultProps = function() {…}();`. Note that this means that the function
122
-
will be executed only a single time per app-lifetime. In practice this
123
-
hasn't caused any issues – `getDefaultProps` should not contain any
124
-
side-effects.
125
-
* Binds class methods to the instance if methods are referenced without being
126
-
called directly. It checks for `this.foo` but also traces variable
127
-
assignments like `var self = this; self.foo`. It does not bind functions
128
-
from the React API and ignores functions that are being called directly
129
-
(unless it is both called directly and passed around to somewhere else)
130
-
* Creates a constructor if necessary. This is necessary if either
131
-
`getInitialState` exists in the `React.createClass` spec OR if functions
132
-
need to be bound to the instance.
133
-
* When `--no-super-class` is passed it only optionally extends
134
-
`React.Component` when `setState` or `forceUpdate` are used within the
135
-
class.
136
-
137
-
The constructor logic is as follows:
138
-
139
-
* Call `super(props, context)` if the base class needs to be extended.
140
-
* Bind all functions that are passed around,
141
-
like `this.foo = this.foo.bind(this)`
142
-
* Inline `getInitialState` (and remove `getInitialState` from the spec). It
143
-
also updates access of `this.props.foo` to `props.foo` and adds `props` as
144
-
argument to the constructor. This is necessary in the case when the base
145
-
class does not need to be extended where `this.props` will only be set by
146
-
React after the constructor has been run.
147
-
* Changes `return StateObject` from `getInitialState` to assign `this.state`
148
-
directly.
104
+
### Explanation of the new ES2015 class transform with property initializers
105
+
1. Determine if mixins are convertible. We only transform a `createClass` call to an ES6 class component when:
106
+
- There are no mixins on the class, or
107
+
-`options['pure-component']` is true, the `mixins` property is an array and it _only_ contains pure render mixin (the specific module name can be specified using `options['mixin-module-name']`, which defaults to `react-addons-pure-render-mixin`)
108
+
2. Ignore components that:
109
+
- Call deprecated APIs. This is very defensive, if the script finds any identifiers called `isMounted`, `getDOMNode`, `replaceProps`, `replaceState` or `setProps` it will skip the component
110
+
- Explicitly call `this.getInitialState()` and/or `this.getDefaultProps()` since an ES6 class component will no longer have these methods
111
+
- Use `arguments` in methods since arrow functions don't have `arguments`. Also please notice that `arguments` should be [very carefully used](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments) and it's generally better to switch to spread (`...args`) instead
112
+
- Have inconvertible `getInitialState()`. Specifically if you have variable declarations like `var props = ...` and the right hand side is not `this.props` then we can't inline the state initialization in the `constructor` due to variable shadowing issues
113
+
- Have non-primitive right hand side values (like `foo: getStuff()`) in the class spec
114
+
3. Transform it to an ES6 class component
115
+
1. Replace `var A = React.createClass(spec)` with `class A extends React.Component {spec}`. If a component uses pure render mixin and passes the mixins test (as described above), it will extend `React.PureComponent` instead
116
+
- Remove the `require`/`import` statement that imports pure render mixin when it's no longer being referenced
117
+
2. Pull out all statics defined on `statics` plus the few special cased statics like `childContextTypes`, `contextTypes`, `displayName`, `getDefaultProps()`, and `propTypes` and transform them to `static` properties (`static propTypes = {...};`)
118
+
- If `getDefaultProps()` is simple (i.e. it only contains a return statement that returns an object) it will be converted to a simple assignment (`static defaultProps = {...};`). Otherwise an IIFE (immediately-invoked function expression) will be created (`static defaultProps = function() { ... }();`). Note that this means that the function will be executed only a single time per app-lifetime. In practice this hasn't caused any issues — `getDefaultProps` should not contain any side-effects
119
+
3. Transform `getInitialState()`
120
+
- If there's no `getInitialState()` or the `getInitialState()` function is simple (i.e., it only contains a return statement that returns an object) then we don't need a constructor; `state` will be lifted to a property initializer (`state = {...};`)
121
+
- However, if the object contains references to `this` other than `this.props` and/or `this.context`, we can't be sure about what you'll need from `this`. We need to ensure that our property initializers' evaluation order is safe, so we defer `state`'s initialization by moving it all the way down until all other property initializers have been initialized
122
+
- If `getInitialState()` is not simple, we create a `constructor` and convert `getInitialState()` to an assignment to `this.state`
123
+
-`constructor` always have `props` as the first parameter
124
+
- We only put `context` as the second parameter when (one of) the following things happen in `getInitialState()`:
125
+
- It accesses `this.context`, or
126
+
- There's a direct method call `this.x()`, or
127
+
-`this` is referenced alone
128
+
- Rewrite accesses to `this.props` to `props` and accesses to `this.context` to `context` since the values will be passed as `constructor` arguments
129
+
- Remove _simple_ variable declarations like `var props = this.props;` and `var context = this.context`
- Add `return;` after the assignment when the return statement is part of a control flow statement (not a direct child of `getInitialState()`'s body) and not in an inner function declaration
132
+
4. Transform all non-lifecycle methods and fields to class property initializers (like `onClick = () => {};`). All your Flow annotations will be preserved
133
+
- It's actually not necessary to transform all methods to arrow functions (i.e., to bind them), but this behavior is the same as `createClass()` and we can make sure that we won't accidentally break stuff
134
+
5. Rewrite `AnotherClass.getDefaultProps()` to `AnotherClass.defaultProps`
135
+
4. Generate Flow annotations from `propTypes` and put it on the class (this only happens when there's `/* @flow */` in your code and `options['flow']` is `true`)
136
+
- Flow actually understands `propTypes` in `createClass` calls but not ES6 class components. Here the transformation logic is identical to [how](https://github.com/facebook/flow/blob/master/src/typing/statement.ml#L3526) Flow treats `propTypes`
137
+
- Notice that Flow treats an optional propType as non-nullable
138
+
- For example, `foo: React.PropTypes.number` is valid when you pass `{}`, `{foo: null}`, or `{foo: undefined}` as props at **runtime**. However, when Flow infers type from a `createClass` call, only `{}` and `{foo: undefined}` are valid; `{foo: null}` is not. Thus the equivalent type annotation in Flow is actually `{foo?: number}`. The question mark on the left hand side indicates `{}` and `{foo: undefined}` are fine, but when `foo` is present it must be a `number`
139
+
- For `propTypes` fields that can't be recognized by Flow, `$FlowFixMe` will be used
0 commit comments