flow-upgrade 1.0.4 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -2
- package/dist/Styled.js +12 -14
- package/dist/Types.js +21 -1
- package/dist/bin/runSpecificCodemod.js +117 -0
- package/dist/bin/upgrade.js +75 -0
- package/dist/codemodUtils/getClassMemberName.js +27 -0
- package/dist/codemodUtils/replaceMethodWithArrowProp.js +60 -0
- package/dist/codemods/collapseObjectInitialization.js +250 -0
- package/dist/codemods/convertImplicitInexactObjectTypes.js +35 -0
- package/dist/codemods/removeAnnotationsInDestructuring.js +40 -0
- package/dist/codemods/removeDuplicateClassProperties.js +268 -0
- package/dist/findFlowFiles.js +136 -156
- package/dist/runCodemods.js +36 -0
- package/dist/upgrade.js +127 -150
- package/dist/upgrades/0.170.0/index.js +25 -0
- package/dist/upgrades/0.176.0/index.js +25 -0
- package/dist/upgrades/0.84.0/index.js +25 -0
- package/dist/upgrades/index.js +29 -0
- package/dist/utils/redirectConsole.js +59 -0
- package/package.json +36 -20
- package/dist/codemods/ReactUtils.js +0 -140
- package/dist/codemods/createAggregateCodemod.js +0 -36
- package/dist/codemods/runCodemods.js +0 -53
- package/dist/index.js +0 -26
- package/dist/upgrades/0.53.0/ReactComponentExplicitTypeArgs/codemod.js +0 -154
- package/dist/upgrades/0.53.0/ReactComponentExplicitTypeArgs/index.js +0 -75
- package/dist/upgrades/0.53.0/ReactComponentSimplifyTypeArgs/codemod.js +0 -65
- package/dist/upgrades/0.53.0/ReactComponentSimplifyTypeArgs/index.js +0 -52
- package/dist/upgrades/0.53.0/ReactUtilityTypes/codemod.js +0 -200
- package/dist/upgrades/0.53.0/ReactUtilityTypes/index.js +0 -59
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @format
|
|
3
|
-
*
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use strict';
|
|
7
|
-
|
|
8
|
-
module.exports = j => {
|
|
9
|
-
/**
|
|
10
|
-
* Has this path imported the react module and given a name it a name? If
|
|
11
|
-
* React is imported like:
|
|
12
|
-
*
|
|
13
|
-
* ```
|
|
14
|
-
* import {Component} from 'react';
|
|
15
|
-
* ```
|
|
16
|
-
*
|
|
17
|
-
* Then it does not count.
|
|
18
|
-
*/
|
|
19
|
-
function getImportedReactName(path) {
|
|
20
|
-
// All of the modules we want to treat as React.
|
|
21
|
-
const REACT_MODULES = new Set(['react', 'React']);
|
|
22
|
-
// Find the first require for React that does not destructure React but
|
|
23
|
-
// instead gives it a name.
|
|
24
|
-
const reactRequire = path.findVariableDeclarators().filter(j.filters.VariableDeclarator.requiresModule(Array.from(REACT_MODULES))).nodes().find(node => node.id && node.id.type === 'Identifier');
|
|
25
|
-
// If we found a require for React then return the name.
|
|
26
|
-
if (reactRequire) {
|
|
27
|
-
return reactRequire.id.name;
|
|
28
|
-
}
|
|
29
|
-
// Get all of the import declarations that import React.
|
|
30
|
-
const reactImports = path.find(j.ImportDeclaration, {
|
|
31
|
-
type: 'ImportDeclaration',
|
|
32
|
-
source: {
|
|
33
|
-
type: 'Literal',
|
|
34
|
-
value: _value => REACT_MODULES.has(_value)
|
|
35
|
-
}
|
|
36
|
-
}).nodes();
|
|
37
|
-
// For all of the React imports...
|
|
38
|
-
for (let i = 0; i < reactImports.length; i++) {
|
|
39
|
-
const reactImport = reactImports[i];
|
|
40
|
-
// ...and for all of each import's specifiers...
|
|
41
|
-
for (let j = 0; j < reactImport.specifiers.length; j++) {
|
|
42
|
-
const specifier = reactImport.specifiers[j];
|
|
43
|
-
// ...check to see if it is either a default specifier or a namespace
|
|
44
|
-
// specifier. If it is either and it has a local name then return that
|
|
45
|
-
// local name.
|
|
46
|
-
if ((specifier.type === 'ImportDefaultSpecifier' || specifier.type === 'ImportNamespaceSpecifier') && specifier.local && specifier.local.type === 'Identifier') {
|
|
47
|
-
return specifier.local.name;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
// Otherwise we can't find anything and should return null.
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Gets a pattern that can be used with jscodeshift that matches value nodes
|
|
57
|
-
* like `React.Component`. If React was not imported then null will be
|
|
58
|
-
* returned.
|
|
59
|
-
*/
|
|
60
|
-
function getImportedComponentClassPattern(path) {
|
|
61
|
-
// All of the places where we want to treat as React.
|
|
62
|
-
const REACT_MODULES = new Set(['React', 'react', 'react/addons', 'react-native']);
|
|
63
|
-
// The names of the exports from `REACT_MODULES` that are components
|
|
64
|
-
// classes.
|
|
65
|
-
const COMPONENT_CLASSES = new Set(['Component', 'PureComponent']);
|
|
66
|
-
// Do we require React?
|
|
67
|
-
const requiresReact = path.findVariableDeclarators().filter(j.filters.VariableDeclarator.requiresModule(Array.from(REACT_MODULES))).size() > 0;
|
|
68
|
-
// Do we import React?
|
|
69
|
-
const importsReact = path.find(j.ImportDeclaration, {
|
|
70
|
-
type: 'ImportDeclaration',
|
|
71
|
-
source: {
|
|
72
|
-
type: 'Literal',
|
|
73
|
-
value: _value2 => REACT_MODULES.has(_value2)
|
|
74
|
-
}
|
|
75
|
-
}).size() > 0;
|
|
76
|
-
// If we neither require React or import React then we cannot get a
|
|
77
|
-
// component class pattern.
|
|
78
|
-
if (!requiresReact && !importsReact) {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
// We want to match two different patterns so we jump straight to a
|
|
82
|
-
// function.
|
|
83
|
-
//
|
|
84
|
-
// For now we use a simple implementation where we assume Component and/or
|
|
85
|
-
// PureComponent has not been renamed. In the future if it becomes a problem
|
|
86
|
-
// then we should check to see if Component and/or PureComponent were
|
|
87
|
-
// renamed.
|
|
88
|
-
return node => node && (
|
|
89
|
-
// Matches: `Component`.
|
|
90
|
-
node.type === 'Identifier' && COMPONENT_CLASSES.has(node.name) ||
|
|
91
|
-
// Matches: `React.Component`.
|
|
92
|
-
node.type === 'MemberExpression' && node.object.type === 'Identifier' && node.object.name === 'React' && node.property.type === 'Identifier' && COMPONENT_CLASSES.has(node.property.name) ||
|
|
93
|
-
// Matches: `React.Component` but in the type position.
|
|
94
|
-
node.type === 'QualifiedTypeIdentifier' && node.qualification.type === 'Identifier' && node.qualification.name === 'React' && node.id.type === 'Identifier' && COMPONENT_CLASSES.has(node.id.name));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Checks for a very specific pattern:
|
|
99
|
-
*
|
|
100
|
-
* ```
|
|
101
|
-
* const React = require('react');
|
|
102
|
-
* const {Element} = React;
|
|
103
|
-
* ```
|
|
104
|
-
*
|
|
105
|
-
* or:
|
|
106
|
-
*
|
|
107
|
-
* ```
|
|
108
|
-
* import type {Element} from 'react';
|
|
109
|
-
* ```
|
|
110
|
-
*
|
|
111
|
-
* At this moment we do not care about generalizing these patterns!
|
|
112
|
-
*/
|
|
113
|
-
function hasDestructuredElement(path, reactName) {
|
|
114
|
-
// All of the places where we want to treat as React.
|
|
115
|
-
const REACT_MODULES = new Set(['React', 'react', 'react-native']);
|
|
116
|
-
reactName = reactName || getImportedReactName(path);
|
|
117
|
-
return path.find(j.ImportDeclaration, {
|
|
118
|
-
specifiers: _specifiers => _specifiers.find(specifier => specifier && specifier.type === 'ImportSpecifier' && specifier.imported && specifier.imported.type === 'Identifier' && specifier.imported.name === 'Element' && specifier.local && specifier.local.type === 'Identifier' && specifier.local.name === 'Element'),
|
|
119
|
-
source: {
|
|
120
|
-
type: 'Literal',
|
|
121
|
-
value: _value3 => REACT_MODULES.has(_value3)
|
|
122
|
-
}
|
|
123
|
-
}).size() > 0 || path.find(j.VariableDeclarator, {
|
|
124
|
-
init: {
|
|
125
|
-
type: 'Identifier',
|
|
126
|
-
name: reactName
|
|
127
|
-
},
|
|
128
|
-
id: {
|
|
129
|
-
type: 'ObjectPattern',
|
|
130
|
-
properties: _properties => !!_properties.find(property => property && !property.method && property.shorthand && property.key && property.key.type === 'Identifier' && property.key.name === 'Element' && property.value && property.value.type === 'Identifier' && property.value.name === 'Element')
|
|
131
|
-
}
|
|
132
|
-
}).size() > 0;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return {
|
|
136
|
-
getImportedReactName,
|
|
137
|
-
getImportedComponentClassPattern,
|
|
138
|
-
hasDestructuredElement
|
|
139
|
-
};
|
|
140
|
-
};
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @format
|
|
5
|
-
*
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Creates a transform function that can be used with `jscodeshift` that is the
|
|
10
|
-
* aggregate of the transform paths.
|
|
11
|
-
*
|
|
12
|
-
* This file is not required directly when we run `flow-upgrade`. Instead it is
|
|
13
|
-
* initialized in a `jscodeshift` worker.
|
|
14
|
-
*/
|
|
15
|
-
module.exports = transformPaths => {
|
|
16
|
-
return (file, api) => {
|
|
17
|
-
// Get the jscodeshift API and parse our source file.
|
|
18
|
-
const j = api.jscodeshift;
|
|
19
|
-
// Parse ths source file.
|
|
20
|
-
const root = j(file.source);
|
|
21
|
-
// Iterate through all of our transform paths so that we can apply them.
|
|
22
|
-
const skipped = transformPaths.reduce((skipped, transformPath) => {
|
|
23
|
-
// Require the transform path.
|
|
24
|
-
const transform = require(transformPath);
|
|
25
|
-
// Use the transform to codemod the file.
|
|
26
|
-
const notSkipped = transform(j, root);
|
|
27
|
-
// If all of the transforms return true then skipped will be true.
|
|
28
|
-
return skipped && !notSkipped;
|
|
29
|
-
},
|
|
30
|
-
// If we have no codemods then skipped should be true.
|
|
31
|
-
true);
|
|
32
|
-
// Either return null or the new source file depending on whether or all the
|
|
33
|
-
// transforms skipped.
|
|
34
|
-
return skipped ? null : root.toSource();
|
|
35
|
-
};
|
|
36
|
-
};
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @format
|
|
7
|
-
*
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const fs = require('fs');
|
|
12
|
-
const Runner = require('jscodeshift/src/Runner');
|
|
13
|
-
|
|
14
|
-
const AGGREGATE_CODEMOD_UTIL = path.join(__dirname, 'createAggregateCodemod.js');
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Runs some codemods.
|
|
18
|
-
*
|
|
19
|
-
* We use the synchronous methods from `fs` for now as we fully expect for this
|
|
20
|
-
* function to block the running process.
|
|
21
|
-
*/
|
|
22
|
-
module.exports = (() => {
|
|
23
|
-
var _ref = _asyncToGenerator(function* (transformPaths, filePaths) {
|
|
24
|
-
// Create a temporary for our aggregate codemod file.
|
|
25
|
-
const aggregateTransformPath = path.join(fs.mkdtempSync('/tmp/flow-upgrade-'), 'codemod.js');
|
|
26
|
-
// The contents of our transform file.
|
|
27
|
-
const aggregateTransformContents = `
|
|
28
|
-
/**
|
|
29
|
-
* This jscodeshift transform file was automatically generated by:
|
|
30
|
-
* ${__filename}
|
|
31
|
-
*/
|
|
32
|
-
module.exports = require(${JSON.stringify(AGGREGATE_CODEMOD_UTIL)})([
|
|
33
|
-
${transformPaths.map(function (transformPath) {
|
|
34
|
-
return ` ${JSON.stringify(transformPath)},`;
|
|
35
|
-
}).join('\n')}
|
|
36
|
-
]);
|
|
37
|
-
`.slice(1);
|
|
38
|
-
// Write the codemod to the folder we created for it.
|
|
39
|
-
fs.writeFileSync(aggregateTransformPath, aggregateTransformContents);
|
|
40
|
-
// Run the codemod with jscodeshift! The runner returns a promise which we
|
|
41
|
-
// will wait for.
|
|
42
|
-
yield Runner.run(aggregateTransformPath, filePaths, {
|
|
43
|
-
parser: 'flow',
|
|
44
|
-
verbose: 0
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
function runCodemods(_x, _x2) {
|
|
49
|
-
return _ref.apply(this, arguments);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return runCodemods;
|
|
53
|
-
})();
|
package/dist/index.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @format
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const yargs = require('yargs').argv;
|
|
10
|
-
const chalk = require('chalk');
|
|
11
|
-
const upgrade = require('./upgrade');
|
|
12
|
-
|
|
13
|
-
const options = {
|
|
14
|
-
all: !!yargs.all
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// For now we are hardcoding the version numbers out of convenience. When we add
|
|
18
|
-
// upgrades for future versions we will need to check `.flowconfig` or
|
|
19
|
-
// `flow-bin` for the current version and allow the new version to be
|
|
20
|
-
// configurable. (But still default to the latest version.)
|
|
21
|
-
const fromVersion = '0.52.0';
|
|
22
|
-
const toVersion = '0.53.0';
|
|
23
|
-
|
|
24
|
-
upgrade(process.cwd(), fromVersion, toVersion, options).catch(error => {
|
|
25
|
-
console.error(chalk.red(error ? error.stack || error : error));
|
|
26
|
-
});
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const LIFECYCLE_METHODS = new Map([['componentWillReceiveProps', ['props']], ['shouldComponentUpdate', ['props', 'state']], ['componentWillUpdate', ['props', 'state']], ['componentDidUpdate', ['props', 'state']]]);
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* ```
|
|
7
|
-
* class MyComponent extends React.Component {
|
|
8
|
-
* props: Props;
|
|
9
|
-
* ...
|
|
10
|
-
* }
|
|
11
|
-
* ```
|
|
12
|
-
*
|
|
13
|
-
* ==>
|
|
14
|
-
*
|
|
15
|
-
* ```
|
|
16
|
-
* class MyComponent extends React.Component<void, Props> {
|
|
17
|
-
* ...
|
|
18
|
-
* }
|
|
19
|
-
* ```
|
|
20
|
-
*
|
|
21
|
-
* See `./fixtures` for more examples.
|
|
22
|
-
*/
|
|
23
|
-
module.exports = (j, root) => {
|
|
24
|
-
const ReactUtils = require('../../../codemods/ReactUtils')(j);
|
|
25
|
-
const componentPattern = ReactUtils.getImportedComponentClassPattern(root);
|
|
26
|
-
|
|
27
|
-
// Only proceed with the transform if we see that React is being used.
|
|
28
|
-
if (!componentPattern) {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// The fallback type annotations we will use if we can't find anything.
|
|
33
|
-
const fallbackDefaultPropsTypeAnnotation = j.genericTypeAnnotation(j.identifier('$FlowFixMeDefaultProps'), null);
|
|
34
|
-
const fallbackPropsTypeAnnotation = j.genericTypeAnnotation(j.identifier('$FlowFixMeProps'), null);
|
|
35
|
-
const fallbackStateTypeAnnotation = j.genericTypeAnnotation(j.identifier('$FlowFixMeState'), null);
|
|
36
|
-
|
|
37
|
-
root.find(j.ClassDeclaration, { superClass: componentPattern }).filter(path => !path.node.superTypeParameters).forEach(handlePath);
|
|
38
|
-
|
|
39
|
-
root.find(j.ClassExpression, { superClass: componentPattern }).filter(path => !path.node.superTypeParameters).forEach(handlePath);
|
|
40
|
-
|
|
41
|
-
return true;
|
|
42
|
-
|
|
43
|
-
function handlePath(path) {
|
|
44
|
-
// Initialize the type annotations for the three generic parameters we
|
|
45
|
-
// will need to pass into `Component`. `defaultProps` and `state` default
|
|
46
|
-
// to void. If we can't find a type we assume they do not exist. `props`,
|
|
47
|
-
// however, we set to null initially as we have a few fallback mechanisms.
|
|
48
|
-
let defaultPropsTypeAnnotation = j.voidTypeAnnotation();
|
|
49
|
-
let propsTypeAnnotation = null;
|
|
50
|
-
let stateTypeAnnotation = null;
|
|
51
|
-
// Some components use the constructor to type props. If they do we want
|
|
52
|
-
// to remember that, but we do not want to override any value in
|
|
53
|
-
// `propsTypeAnnotation`.
|
|
54
|
-
let constructorPropsTypeAnnotation = null;
|
|
55
|
-
// We want to try to get the props and state type annotation from lifecycle
|
|
56
|
-
// methods as well.
|
|
57
|
-
let propsLifecycleTypeAnnotation = null;
|
|
58
|
-
let stateLifecycleTypeAnnotation = null;
|
|
59
|
-
// If we find any reference to `this.props` then we set this to true. We
|
|
60
|
-
// do not need to compute this value if we found a type for
|
|
61
|
-
// `propsTypeAnnotation` or `constructorPropsTypeAnnotation`.
|
|
62
|
-
let usesPropsSomewhere = false;
|
|
63
|
-
// If we find any reference to `this.state` then we set this to true. We
|
|
64
|
-
// do not need to compute this value if we found a type for
|
|
65
|
-
// `stateTypeAnnotation`.
|
|
66
|
-
let usesStateSomewhere = false;
|
|
67
|
-
// Look at all our class members and remove `props`, `state`, or
|
|
68
|
-
// `defaultProps` if they are only used to specify a type.
|
|
69
|
-
path.node.body.body = path.node.body.body.filter(member => {
|
|
70
|
-
// Do we need to check to see if props or state were used?
|
|
71
|
-
const checkForPropsUsage = !propsTypeAnnotation && !constructorPropsTypeAnnotation && !usesPropsSomewhere;
|
|
72
|
-
const checkForStateUsage = !stateTypeAnnotation && !usesStateSomewhere;
|
|
73
|
-
// Check that either props or state were used in a non-static member.
|
|
74
|
-
if (member && !member.static && (checkForPropsUsage || checkForStateUsage)) {
|
|
75
|
-
// Get the nodes for all of the either props identifiers or state
|
|
76
|
-
// identifiers.
|
|
77
|
-
const nodes = j(member).find(j.Identifier, {
|
|
78
|
-
name: _name => checkForPropsUsage && _name === 'props' || checkForStateUsage && _name === 'state'
|
|
79
|
-
}).nodes();
|
|
80
|
-
// If we are checking for props usage see if we found a props
|
|
81
|
-
// identifier.
|
|
82
|
-
if (checkForPropsUsage) {
|
|
83
|
-
usesPropsSomewhere = nodes.findIndex(node => node.name === 'props') !== -1;
|
|
84
|
-
}
|
|
85
|
-
// If we are checking for state usage see if we found a state
|
|
86
|
-
// identifier.
|
|
87
|
-
if (checkForStateUsage) {
|
|
88
|
-
usesStateSomewhere = nodes.findIndex(node => node.name === 'state') !== -1;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
// Class properties like defaultProps, props, and state.
|
|
94
|
-
member.type === 'ClassProperty' && member.key.type === 'Identifier') {
|
|
95
|
-
if (member.static && member.key.name === 'defaultProps') {
|
|
96
|
-
defaultPropsTypeAnnotation = member.typeAnnotation ? member.typeAnnotation.typeAnnotation : fallbackDefaultPropsTypeAnnotation;
|
|
97
|
-
return !!member.value;
|
|
98
|
-
} else if (!member.static && member.key.name === 'props') {
|
|
99
|
-
propsTypeAnnotation = member.typeAnnotation ? member.typeAnnotation.typeAnnotation : fallbackPropsTypeAnnotation;
|
|
100
|
-
return !!member.value;
|
|
101
|
-
} else if (!member.static && member.key.name === 'state') {
|
|
102
|
-
stateTypeAnnotation = member.typeAnnotation ? member.typeAnnotation.typeAnnotation : fallbackStateTypeAnnotation;
|
|
103
|
-
return !!member.value;
|
|
104
|
-
} else {
|
|
105
|
-
return true;
|
|
106
|
-
}
|
|
107
|
-
} else if (
|
|
108
|
-
// The constructor method.
|
|
109
|
-
member.type === 'MethodDefinition' && member.key.type === 'Identifier' && member.key.name === 'constructor' && member.value && member.value.type === 'FunctionExpression' && member.value.params[0]) {
|
|
110
|
-
constructorPropsTypeAnnotation = member.value.params[0].typeAnnotation ? member.value.params[0].typeAnnotation.typeAnnotation : fallbackPropsTypeAnnotation;
|
|
111
|
-
return true;
|
|
112
|
-
} else if (
|
|
113
|
-
// Lifecycle methods.
|
|
114
|
-
member.type === 'MethodDefinition' && member.key.type === 'Identifier' && LIFECYCLE_METHODS.has(member.key.name) && member.value && member.value.type === 'FunctionExpression') {
|
|
115
|
-
const paramNames = LIFECYCLE_METHODS.get(member.key.name);
|
|
116
|
-
// For all of the parameters...
|
|
117
|
-
for (let i = 0; i < member.value.params.length; i++) {
|
|
118
|
-
const param = member.value.params[i];
|
|
119
|
-
const paramName = paramNames[i];
|
|
120
|
-
// If we have any excuse to stop iterating over the lifecycle method
|
|
121
|
-
// params then take it.
|
|
122
|
-
if (!param || !paramName || param.type === 'RestElement') {
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
// Get the type annotation. (Or the fallback.)
|
|
126
|
-
const typeAnnotation = param.typeAnnotation && param.typeAnnotation.type === 'TypeAnnotation' && param.typeAnnotation.typeAnnotation;
|
|
127
|
-
// If this is a props param and we don't yet have a lifecycle type
|
|
128
|
-
// annotation.
|
|
129
|
-
if (paramName === 'props' && !propsLifecycleTypeAnnotation) {
|
|
130
|
-
propsLifecycleTypeAnnotation = typeAnnotation || fallbackPropsTypeAnnotation;
|
|
131
|
-
}
|
|
132
|
-
// If this is a state param and we don't yet have a lifecycle type
|
|
133
|
-
// annotation.
|
|
134
|
-
if (paramName === 'state' && !stateLifecycleTypeAnnotation) {
|
|
135
|
-
stateLifecycleTypeAnnotation = typeAnnotation || fallbackStateTypeAnnotation;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return true;
|
|
139
|
-
} else {
|
|
140
|
-
return true;
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
// Set the super type parameters to the class.
|
|
144
|
-
path.node.superTypeParameters = j.typeParameterInstantiation([defaultPropsTypeAnnotation,
|
|
145
|
-
// Use the props type annotation if we have it. If not try using the
|
|
146
|
-
// constructor props annotation. Otherwise, if we found any mention to
|
|
147
|
-
// props use the any type. If not then use an empty object type.
|
|
148
|
-
propsTypeAnnotation || constructorPropsTypeAnnotation || propsLifecycleTypeAnnotation || (usesPropsSomewhere ? fallbackPropsTypeAnnotation : j.objectTypeAnnotation([])),
|
|
149
|
-
// The state type annotation may be null. If it is then it will be
|
|
150
|
-
// filtered out at the end. If state was used somewhere then we need the
|
|
151
|
-
// fallback type annotation.
|
|
152
|
-
stateTypeAnnotation || stateLifecycleTypeAnnotation || (usesStateSomewhere ? fallbackStateTypeAnnotation : null)].filter(Boolean));
|
|
153
|
-
}
|
|
154
|
-
};
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @format
|
|
5
|
-
*
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const Styled = require('../../../Styled');
|
|
10
|
-
|
|
11
|
-
exports.kind = 'codemod';
|
|
12
|
-
|
|
13
|
-
exports.title = 'Move inferred React.Component type arguments to their generic positions.';
|
|
14
|
-
|
|
15
|
-
exports.description = `
|
|
16
|
-
The recommended way to write React components used to be:
|
|
17
|
-
|
|
18
|
-
${Styled.codeblock(`
|
|
19
|
-
import React from 'react';
|
|
20
|
-
|
|
21
|
-
type DefaultProps = { /* ... */ };
|
|
22
|
-
type Props = { /* ... */ };
|
|
23
|
-
type State = { /* ... */ };
|
|
24
|
-
|
|
25
|
-
class MyComponent extends React.Component {
|
|
26
|
-
static defaultProps: DefaultProps = { /* ... */ };
|
|
27
|
-
|
|
28
|
-
props: Props;
|
|
29
|
-
state: State = { /* ... */ };
|
|
30
|
-
|
|
31
|
-
render() {
|
|
32
|
-
return /* ... */;
|
|
33
|
-
}
|
|
34
|
-
}`.slice(1))}
|
|
35
|
-
|
|
36
|
-
While you would write React.Component in this way without type arguments the
|
|
37
|
-
signature for React.Component was in fact:
|
|
38
|
-
React.Component<DefaultProps, Props, State>. So for Flow to get from the
|
|
39
|
-
component style above to a place where React components had the correct type
|
|
40
|
-
arguments it would turn:
|
|
41
|
-
|
|
42
|
-
${Styled.codeblock(`
|
|
43
|
-
class MyComponent extends React.Component {`.slice(1))}
|
|
44
|
-
|
|
45
|
-
...into:
|
|
46
|
-
|
|
47
|
-
${Styled.codeblock(`
|
|
48
|
-
class MyComponent extends React.Component<*, *, *> {`.slice(1))}
|
|
49
|
-
|
|
50
|
-
Where the star (*) meant "infer." However, this approach is difficult to
|
|
51
|
-
understand, reduces type trustworthiness, and has some negative impacts on
|
|
52
|
-
performance as Flow needs to carry inference information around everywhere.
|
|
53
|
-
|
|
54
|
-
This upgrade runs a codemod to make the type arguments you pass into
|
|
55
|
-
React.Component explicit. We take the code in the first example above and turn
|
|
56
|
-
it into:
|
|
57
|
-
|
|
58
|
-
${Styled.codeblock(`
|
|
59
|
-
import React from 'react';
|
|
60
|
-
|
|
61
|
-
type DefaultProps = { /* ... */ };
|
|
62
|
-
type Props = { /* ... */ };
|
|
63
|
-
type State = { /* ... */ };
|
|
64
|
-
|
|
65
|
-
class MyComponent extends React.Component<DefaultProps, Props, State> {
|
|
66
|
-
static defaultProps = { /* ... */ };
|
|
67
|
-
|
|
68
|
-
state = { /* ... */ };
|
|
69
|
-
|
|
70
|
-
render() {
|
|
71
|
-
return /* ... */;
|
|
72
|
-
}
|
|
73
|
-
}`.slice(1))}`.slice(1);
|
|
74
|
-
|
|
75
|
-
exports.transformPath = path.join(__dirname, './codemod.js');
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* This codemod depends on ReactComponentExplicitTypeArgs! It assumes that the
|
|
5
|
-
* code was first transformed using that.
|
|
6
|
-
*
|
|
7
|
-
* ```
|
|
8
|
-
* class MyComponent extends React.Component<void, Props> {
|
|
9
|
-
* ...
|
|
10
|
-
* }
|
|
11
|
-
* ```
|
|
12
|
-
*
|
|
13
|
-
* ==>
|
|
14
|
-
*
|
|
15
|
-
* ```
|
|
16
|
-
* class MyComponent extends React.Component<Props> {
|
|
17
|
-
* ...
|
|
18
|
-
* }
|
|
19
|
-
* ```
|
|
20
|
-
*
|
|
21
|
-
* See `./fixtures` for more examples.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
module.exports = (j, root) => {
|
|
25
|
-
const ReactUtils = require('../../../codemods/ReactUtils')(j);
|
|
26
|
-
const componentPattern = ReactUtils.getImportedComponentClassPattern(root);
|
|
27
|
-
|
|
28
|
-
// Only proceed with the transform if we see that React is being used.
|
|
29
|
-
if (!componentPattern) {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
root.find(j.ClassDeclaration, {
|
|
34
|
-
superClass: componentPattern,
|
|
35
|
-
superTypeParameters: {
|
|
36
|
-
type: 'TypeParameterInstantiation'
|
|
37
|
-
}
|
|
38
|
-
}).forEach(handlePath);
|
|
39
|
-
|
|
40
|
-
root.find(j.ClassExpression, {
|
|
41
|
-
superClass: componentPattern,
|
|
42
|
-
superTypeParameters: {
|
|
43
|
-
type: 'TypeParameterInstantiation'
|
|
44
|
-
}
|
|
45
|
-
}).forEach(handlePath);
|
|
46
|
-
|
|
47
|
-
function handlePath(path) {
|
|
48
|
-
// Remove the first super type parameter.
|
|
49
|
-
const defaultPropsType = path.node.superTypeParameters.params.shift();
|
|
50
|
-
// If we have a default props type that is not `void` and a class body then
|
|
51
|
-
// we might want to add the default props type to our class body.
|
|
52
|
-
if (defaultPropsType && defaultPropsType.type !== 'VoidTypeAnnotation' && path.node.body) {
|
|
53
|
-
const body = path.node.body.body;
|
|
54
|
-
// See if we already have a static default props class property.
|
|
55
|
-
const hasDefaultProps = !!body.find(node => node.type === 'ClassProperty' && node.static === true && node.key && node.key.type === 'Identifier' && node.key.name === 'defaultProps');
|
|
56
|
-
// If we do not have a static default props class property then we want to
|
|
57
|
-
// add one to our class body using the default props type that we shifted.
|
|
58
|
-
if (!hasDefaultProps) {
|
|
59
|
-
body.unshift(j.classProperty(j.identifier('defaultProps'), null, j.typeAnnotation(defaultPropsType), true));
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return true;
|
|
65
|
-
};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @format
|
|
5
|
-
*
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const Styled = require('../../../Styled');
|
|
10
|
-
|
|
11
|
-
exports.kind = 'codemod';
|
|
12
|
-
|
|
13
|
-
exports.title = 'Simplify React.Component type arguments.';
|
|
14
|
-
|
|
15
|
-
exports.description = `
|
|
16
|
-
A React.Component used to require three type arguments like this:
|
|
17
|
-
React.Component<DefaultProps, Props, State>. However, requiring DefaultProps
|
|
18
|
-
whenever using type arguments doesn't make much sense. Also, requiring State
|
|
19
|
-
for a component that does not use state, or in a consumer that doesn't care
|
|
20
|
-
about State also doesn't make much sense.
|
|
21
|
-
|
|
22
|
-
So we changed Flow so that we only require Props. If you write:
|
|
23
|
-
React.Component<Props> then State is assumed to be undefined and default props
|
|
24
|
-
will be inferred from the statics of your component class. A component written
|
|
25
|
-
without state but with default props in this new style looks like:
|
|
26
|
-
|
|
27
|
-
${Styled.codeblock(`
|
|
28
|
-
import React from 'react';
|
|
29
|
-
|
|
30
|
-
type Props = { /* ... */ };
|
|
31
|
-
|
|
32
|
-
class MyComponent extends React.Component<Props> {
|
|
33
|
-
static defaultProps = { /* ... */ };
|
|
34
|
-
}`.slice(1))}
|
|
35
|
-
|
|
36
|
-
Default props is inferred from the static defaultProps object literal. If you
|
|
37
|
-
want a component with state add a second type argument:
|
|
38
|
-
|
|
39
|
-
${Styled.codeblock(`
|
|
40
|
-
import React from 'react';
|
|
41
|
-
|
|
42
|
-
type Props = { /* ... */ };
|
|
43
|
-
type State = { /* ... */ };
|
|
44
|
-
|
|
45
|
-
class MyComponent extends React.Component<Props, State> {
|
|
46
|
-
static defaultProps = { /* ... */ };
|
|
47
|
-
}`.slice(1))}
|
|
48
|
-
|
|
49
|
-
This upgrade will remove DefaultProps from the type arguments of all your
|
|
50
|
-
React components.`.slice(1);
|
|
51
|
-
|
|
52
|
-
exports.transformPath = path.join(__dirname, './codemod.js');
|