flow-upgrade 1.1.0 → 2.0.0

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.
Files changed (32) hide show
  1. package/README.md +10 -0
  2. package/dist/Styled.js +12 -14
  3. package/dist/Types.js +21 -1
  4. package/dist/bin/runSpecificCodemod.js +117 -0
  5. package/dist/bin/upgrade.js +75 -0
  6. package/dist/codemodUtils/getClassMemberName.js +27 -0
  7. package/dist/codemodUtils/replaceMethodWithArrowProp.js +60 -0
  8. package/dist/codemods/collapseObjectInitialization.js +250 -0
  9. package/dist/codemods/convertImplicitInexactObjectTypes.js +35 -0
  10. package/dist/codemods/removeAnnotationsInDestructuring.js +40 -0
  11. package/dist/codemods/removeDuplicateClassProperties.js +268 -0
  12. package/dist/findFlowFiles.js +136 -158
  13. package/dist/runCodemods.js +36 -0
  14. package/dist/upgrade.js +127 -153
  15. package/dist/upgrades/0.170.0/index.js +25 -0
  16. package/dist/upgrades/0.176.0/index.js +25 -0
  17. package/dist/upgrades/0.84.0/index.js +25 -0
  18. package/dist/upgrades/index.js +29 -0
  19. package/dist/utils/redirectConsole.js +59 -0
  20. package/package.json +29 -21
  21. package/dist/codemods/ReactUtils.js +0 -140
  22. package/dist/codemods/createAggregateCodemod.js +0 -36
  23. package/dist/codemods/runCodemods.js +0 -54
  24. package/dist/index.js +0 -38
  25. package/dist/upgrades/0.53.0/ReactComponentExplicitTypeArgs/codemod.js +0 -154
  26. package/dist/upgrades/0.53.0/ReactComponentExplicitTypeArgs/index.js +0 -75
  27. package/dist/upgrades/0.53.0/ReactComponentSimplifyTypeArgs/codemod.js +0 -65
  28. package/dist/upgrades/0.53.0/ReactComponentSimplifyTypeArgs/index.js +0 -52
  29. package/dist/upgrades/0.53.0/ReactUtilityTypes/codemod.js +0 -156
  30. package/dist/upgrades/0.53.0/ReactUtilityTypes/index.js +0 -59
  31. package/dist/upgrades/0.84.0/ExplicitInexactObjectSyntax/codemod.js +0 -18
  32. package/dist/upgrades/0.84.0/ExplicitInexactObjectSyntax/index.js +0 -25
@@ -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,54 +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
- const os = require('os');
14
-
15
- const AGGREGATE_CODEMOD_UTIL = path.join(__dirname, 'createAggregateCodemod.js');
16
-
17
- /**
18
- * Runs some codemods.
19
- *
20
- * We use the synchronous methods from `fs` for now as we fully expect for this
21
- * function to block the running process.
22
- */
23
- module.exports = (() => {
24
- var _ref = _asyncToGenerator(function* (transformPaths, filePaths) {
25
- // Create a temporary for our aggregate codemod file.
26
- const aggregateTransformPath = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'flow-upgrade-')), 'codemod.js');
27
- // The contents of our transform file.
28
- const aggregateTransformContents = `
29
- /**
30
- * This jscodeshift transform file was automatically generated by:
31
- * ${__filename}
32
- */
33
- module.exports = require(${JSON.stringify(AGGREGATE_CODEMOD_UTIL)})([
34
- ${transformPaths.map(function (transformPath) {
35
- return ` ${JSON.stringify(transformPath)},`;
36
- }).join('\n')}
37
- ]);
38
- `.slice(1);
39
- // Write the codemod to the folder we created for it.
40
- fs.writeFileSync(aggregateTransformPath, aggregateTransformContents);
41
- // Run the codemod with jscodeshift! The runner returns a promise which we
42
- // will wait for.
43
- yield Runner.run(aggregateTransformPath, filePaths, {
44
- parser: 'flow',
45
- verbose: 0
46
- });
47
- });
48
-
49
- function runCodemods(_x, _x2) {
50
- return _ref.apply(this, arguments);
51
- }
52
-
53
- return runCodemods;
54
- })();
package/dist/index.js DELETED
@@ -1,38 +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
- function printUsage(exitCode) {
18
- console.log('Usage: flow-upgrade <current version> <target version>');
19
- process.exit(exitCode);
20
- }
21
-
22
- if (!!yargs.help) {
23
- printUsage(0);
24
- }
25
-
26
- // For now we are asking for the version numbers out of convenience. When we add
27
- // upgrades for future versions we will need to check `.flowconfig` or
28
- // `flow-bin` for the current version and allow the new version to be
29
- // configurable. (But still default to the latest version.)
30
- if (yargs._.length != 2) {
31
- printUsage(1);
32
- }
33
- const fromVersion = yargs._[0];
34
- const toVersion = yargs._[1];
35
-
36
- upgrade(process.cwd(), fromVersion, toVersion, options).catch(error => {
37
- console.error(chalk.red(error ? error.stack || error : error));
38
- });
@@ -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');