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+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import * as React from 'react';
2+
3+
export default class MyComponent extends React.Component {
4+
render() {
5+
return <button onClick={this.onclick.bind(this)} />;
6+
}
7+
8+
onclick() {
9+
this.setState({foo: 1, bar: 2})
10+
}
11+
}
12+
13+
export class AnotherComponent extends React.Component {
14+
static propTypes = {
15+
foo: React.PropTypes.string.isRequired,
16+
};
17+
render() {
18+
return <div />;
19+
}
20+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as React from 'react';
2+
type MyComponentState = { foo: number; bar: number; };
3+
export default class MyComponent extends React.Component<{
4+
}, MyComponentState> {
5+
render() {
6+
return <button onClick={this.onclick.bind(this)}/>;
7+
}
8+
onclick() {
9+
this.setState({ foo: 1, bar: 2 });
10+
}
11+
}
12+
type AnotherComponentProps = {
13+
foo: string;
14+
};
15+
export class AnotherComponent extends React.Component<AnotherComponentProps, {
16+
}> {
17+
static propTypes = {
18+
foo: React.PropTypes.string.isRequired,
19+
};
20+
render() {
21+
return <div />;
22+
}
23+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class SomeComponent extends React.Component<{
2+
foo: number;
3+
}, {
4+
bar: string;
5+
}> {
6+
render() {
7+
return null;
8+
}
9+
}
10+
SomeComponent.propTypes = { foo: React.PropTypes.string };
11+
12+
class AnotherComponent extends React.Component<{
13+
baz: number;
14+
}> {
15+
render() {
16+
return null;
17+
}
18+
}
19+
AnotherComponent.propTypes = { baz: React.PropTypes.string };
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
class SomeComponent extends React.Component<{
2+
foo: number;
3+
}, {
4+
bar: string;
5+
}> {
6+
static propTypes = { foo: React.PropTypes.string };
7+
render() {
8+
return null;
9+
}
10+
}
11+
SomeComponent.propTypes = { foo: React.PropTypes.string };
12+
class AnotherComponent extends React.Component<{
13+
baz: number;
14+
}> {
15+
static propTypes = { baz: React.PropTypes.string };
16+
render() {
17+
return null;
18+
}
19+
}
20+
AnotherComponent.propTypes = { baz: React.PropTypes.string };
Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
class SomeComponent extends React.Component<{
2-
foo: number;
2+
foo: string;
3+
baz: string;
34
}, {
45
bar: string;
56
}> {
67
}
78
SomeComponent.propTypes = { foo: React.PropTypes.string };
8-
SomeComponent.propTypes.baz = React.PropTypes.string.isRequired;
9+
SomeComponent.propTypes.baz = React.PropTypes.string.isRequired;
10+
11+
12+
class AnotherComponent extends React.Component<{
13+
lol: number;
14+
}> {
15+
}
16+
AnotherComponent.propTypes = { lol: React.PropTypes.number };
Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
class SomeComponent extends React.Component<
2-
{
3-
foo: number
4-
},
5-
{
6-
bar: string
7-
}
8-
> {}
1+
class SomeComponent extends React.Component<{
2+
foo: string;
3+
baz: string;
4+
}, {
5+
bar: string;
6+
}> {
7+
}
8+
class AnotherComponent extends React.Component<{
9+
lol: number;
10+
}> {
11+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import PropTypes from 'prop-types'
2+
import React from 'react'
3+
4+
export const Hello = ({ message }) => {
5+
return <div>hello {message}</div>
6+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import React from 'react';
2+
3+
export const Hello = ({ message }) => {
4+
return <div>hello {message}</div>;
5+
};

0 commit comments

Comments
 (0)