Skip to content
This repository was archived by the owner on Sep 21, 2019. It is now read-only.

Commit 779e450

Browse files
committed
Merge branch 'master' of github.com:lyft/react-javascript-to-typescript-transform into pass-prettier
# Conflicts: # test/react-remove-prop-types-assignment-transform/multiple/output.tsx
2 parents 2fae7fd + 1204fea commit 779e450

File tree

27 files changed

+373
-23
lines changed

27 files changed

+373
-23
lines changed

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { reactMovePropTypesToClassTransformFactoryFactory } from './transforms/r
77
import { collapseIntersectionInterfacesTransformFactoryFactory } from './transforms/collapse-intersection-interfaces-transform';
88
import { reactRemoveStaticPropTypesMemberTransformFactoryFactory } from './transforms/react-remove-static-prop-types-member-transform';
99
import { reactStatelessFunctionMakePropsTransformFactoryFactory } from './transforms/react-stateless-function-make-props-transform';
10+
import { reactRemovePropTypesImportTransformFactoryFactory } from './transforms/react-remove-prop-types-import';
1011

1112
export {
1213
reactMovePropTypesToClassTransformFactoryFactory,
@@ -15,6 +16,7 @@ export {
1516
collapseIntersectionInterfacesTransformFactoryFactory,
1617
reactRemovePropTypesAssignmentTransformFactoryFactory,
1718
reactRemoveStaticPropTypesMemberTransformFactoryFactory,
19+
reactRemovePropTypesImportTransformFactoryFactory,
1820
compile,
1921
};
2022

@@ -25,6 +27,7 @@ export const allTransforms = [
2527
collapseIntersectionInterfacesTransformFactoryFactory,
2628
reactRemovePropTypesAssignmentTransformFactoryFactory,
2729
reactRemoveStaticPropTypesMemberTransformFactoryFactory,
30+
reactRemovePropTypesImportTransformFactoryFactory,
2831
];
2932

3033
export type TransformFactoryFactory = (typeChecker: ts.TypeChecker) => ts.TransformerFactory<ts.SourceFile>;

src/transforms/react-js-make-props-and-state-transform.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ export function reactJSMakePropsAndStateInterfaceTransformFactoryFactory(typeChe
2121
}
2222

2323
function visitSourceFile(sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker) {
24+
let newSourceFile = sourceFile;
2425
for (const statement of sourceFile.statements) {
2526
if (ts.isClassDeclaration(statement) && helpers.isReactComponent(statement, typeChecker)) {
26-
return visitReactClassDeclaration(statement, sourceFile, typeChecker);
27+
newSourceFile = visitReactClassDeclaration(statement, newSourceFile, typeChecker);
2728
}
2829
}
2930

30-
return sourceFile;
31+
return newSourceFile;
3132
}
3233

3334
function visitReactClassDeclaration(

src/transforms/react-move-prop-types-to-class-transform.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,7 @@ function visitSourceFile(sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker)
7070
'propTypes',
7171
propTypeAssignment.expression.right,
7272
);
73-
statements = ts.createNodeArray(
74-
helpers.replaceItem(sourceFile.statements, classStatement, newClassStatement),
75-
);
73+
statements = ts.createNodeArray(helpers.replaceItem(statements, classStatement, newClassStatement));
7674
}
7775
}
7876

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import * as ts from 'typescript';
2+
import * as _ from 'lodash';
3+
4+
import * as helpers from '../helpers';
5+
6+
export type Factory = ts.TransformerFactory<ts.SourceFile>;
7+
8+
/**
9+
* Remove `import PropTypes from 'prop-types'` or
10+
* `import { PropTypes } from 'react'`
11+
*
12+
* @example
13+
* Before:
14+
* import PropTypes from 'prop-types'
15+
* import React, { PropTypes } from 'react'
16+
*
17+
* After:
18+
* import React from 'react'
19+
*/
20+
export function reactRemovePropTypesImportTransformFactoryFactory(typeChecker: ts.TypeChecker): Factory {
21+
return function reactRemovePropTypesImportTransformFactory(context: ts.TransformationContext) {
22+
return function reactRemovePropTypesImportTransform(sourceFile: ts.SourceFile) {
23+
return ts.updateSourceFileNode(
24+
sourceFile,
25+
sourceFile.statements
26+
.filter(s => {
27+
return !(
28+
ts.isImportDeclaration(s) &&
29+
ts.isStringLiteral(s.moduleSpecifier) &&
30+
s.moduleSpecifier.text === 'prop-types'
31+
);
32+
})
33+
.map(updateReactImportIfNeeded),
34+
);
35+
};
36+
};
37+
}
38+
39+
function updateReactImportIfNeeded(statement: ts.Statement) {
40+
if (
41+
!ts.isImportDeclaration(statement) ||
42+
!ts.isStringLiteral(statement.moduleSpecifier) ||
43+
statement.moduleSpecifier.text !== 'react' ||
44+
!statement.importClause ||
45+
!statement.importClause.namedBindings ||
46+
!ts.isNamedImports(statement.importClause.namedBindings)
47+
) {
48+
return statement;
49+
}
50+
51+
const namedBindings = statement.importClause.namedBindings;
52+
const newNamedBindingElements = namedBindings.elements.filter(elm => elm.name.text !== 'PropTypes');
53+
54+
if (newNamedBindingElements.length === namedBindings.elements.length) {
55+
// Means it has no 'PropTypes' named import
56+
return statement;
57+
}
58+
59+
const newImportClause = ts.updateImportClause(
60+
statement.importClause,
61+
statement.importClause.name,
62+
newNamedBindingElements.length === 0
63+
? undefined
64+
: ts.updateNamedImports(namedBindings, newNamedBindingElements),
65+
);
66+
67+
return ts.updateImportDeclaration(
68+
statement,
69+
statement.decorators,
70+
statement.modifiers,
71+
newImportClause,
72+
statement.moduleSpecifier,
73+
);
74+
}

src/transforms/react-stateless-function-make-props-transform.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,29 +42,28 @@ export function reactStatelessFunctionMakePropsTransformFactoryFactory(typeCheck
4242
}
4343

4444
function visitSourceFile(sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker) {
45-
let statements = sourceFile.statements;
46-
4745
// Look for propType assignment statements
48-
const propTypeAssignments = statements.filter(statement =>
46+
const propTypeAssignments = sourceFile.statements.filter(statement =>
4947
helpers.isReactPropTypeAssignmentStatement(statement),
5048
) as ts.ExpressionStatement[];
5149

50+
let newSourceFile = sourceFile;
5251
for (const propTypeAssignment of propTypeAssignments) {
53-
const componentName = helpers.getComponentName(propTypeAssignment, sourceFile);
52+
const componentName = helpers.getComponentName(propTypeAssignment, newSourceFile);
5453

55-
const funcComponent = (_.find(statements, s => {
54+
const funcComponent = (_.find(newSourceFile.statements, s => {
5655
return (
5756
(ts.isFunctionDeclaration(s) && s.name !== undefined && s.name.getText() === componentName) ||
5857
(ts.isVariableStatement(s) && s.declarationList.declarations[0].name.getText() === componentName)
5958
);
6059
}) as {}) as ts.FunctionDeclaration | ts.VariableStatement; // Type weirdness
6160

6261
if (funcComponent) {
63-
return visitReactStatelessComponent(funcComponent, propTypeAssignment, sourceFile);
62+
newSourceFile = visitReactStatelessComponent(funcComponent, propTypeAssignment, newSourceFile);
6463
}
6564
}
6665

67-
return sourceFile;
66+
return newSourceFile;
6867
}
6968

7069
function visitReactStatelessComponent(
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
type Foo = {foo: string} & {bar: number};
2+
3+
type Bar = {foo: number} & {bar: string};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
type Foo = {
2+
foo: string;
3+
bar: number;
4+
};
5+
type Bar = {
6+
foo: number;
7+
bar: string;
8+
};

test/end-to-end/basic/input.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import PropTypes from 'prop-types';
12
import * as React from 'react';
23

34
export default class MyComponent extends React.Component {
45
render() {
56
return <div />;
67
}
7-
}
8+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const Hello = ({ message }) => {
2+
return <div>hello {message}</div>
3+
};
4+
5+
const Hey = ({ name }) => {
6+
return <div>hey, {name}</div>
7+
}
8+
9+
Hey.propTypes = {
10+
message: React.PropTypes.string,
11+
}
12+
13+
Hello.propTypes = {
14+
message: React.PropTypes.string,
15+
}
16+
17+
export default class MyComponent extends React.Component {
18+
render() {
19+
return <button onClick={this.onclick.bind(this)} />;
20+
}
21+
22+
onclick() {
23+
this.setState({foo: 1, bar: 2})
24+
}
25+
}
26+
27+
export class AnotherComponent extends React.Component {
28+
static propTypes = {
29+
foo: React.PropTypes.string.isRequired,
30+
};
31+
render() {
32+
return <div />;
33+
}
34+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
type HelloProps = {
2+
message?: string;
3+
};
4+
const Hello: React.SFC<HelloProps> = ({ message }) => {
5+
return <div>hello {message}</div>;
6+
};
7+
type HeyProps = {
8+
message?: string;
9+
};
10+
const Hey: React.SFC<HeyProps> = ({ name }) => {
11+
return <div>hey, {name}</div>;
12+
};
13+
type MyComponentState = {
14+
foo: number;
15+
bar: number;
16+
};
17+
export default class MyComponent extends React.Component<{
18+
}, MyComponentState> {
19+
render() {
20+
return <button onClick={this.onclick.bind(this)}/>;
21+
}
22+
onclick() {
23+
this.setState({ foo: 1, bar: 2 });
24+
}
25+
}
26+
type AnotherComponentProps = {
27+
foo: string;
28+
};
29+
export class AnotherComponent extends React.Component<AnotherComponentProps, {
30+
}> {
31+
render() {
32+
return <div />;
33+
}
34+
}

0 commit comments

Comments
 (0)