diff --git a/src/index.ts b/src/index.ts index 3915006..2901b7f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ import { reactMovePropTypesToClassTransformFactoryFactory } from './transforms/r import { collapseIntersectionInterfacesTransformFactoryFactory } from './transforms/collapse-intersection-interfaces-transform'; import { reactRemoveStaticPropTypesMemberTransformFactoryFactory } from './transforms/react-remove-static-prop-types-member-transform'; import { reactStatelessFunctionMakePropsTransformFactoryFactory } from './transforms/react-stateless-function-make-props-transform'; +import { reactRemovePropTypesImportTransformFactoryFactory } from './transforms/react-remove-prop-types-import'; export { reactMovePropTypesToClassTransformFactoryFactory, @@ -15,6 +16,7 @@ export { collapseIntersectionInterfacesTransformFactoryFactory, reactRemovePropTypesAssignmentTransformFactoryFactory, reactRemoveStaticPropTypesMemberTransformFactoryFactory, + reactRemovePropTypesImportTransformFactoryFactory, compile, }; @@ -25,6 +27,7 @@ export const allTransforms = [ collapseIntersectionInterfacesTransformFactoryFactory, reactRemovePropTypesAssignmentTransformFactoryFactory, reactRemoveStaticPropTypesMemberTransformFactoryFactory, + reactRemovePropTypesImportTransformFactoryFactory, ]; export type TransformFactoryFactory = (typeChecker: ts.TypeChecker) => ts.TransformerFactory; diff --git a/src/transforms/react-remove-prop-types-import.ts b/src/transforms/react-remove-prop-types-import.ts new file mode 100644 index 0000000..74de228 --- /dev/null +++ b/src/transforms/react-remove-prop-types-import.ts @@ -0,0 +1,74 @@ +import * as ts from 'typescript'; +import * as _ from 'lodash'; + +import * as helpers from '../helpers'; + +export type Factory = ts.TransformerFactory; + +/** + * Remove `import PropTypes from 'prop-types'` or + * `import { PropTypes } from 'react'` + * + * @example + * Before: + * import PropTypes from 'prop-types' + * import React, { PropTypes } from 'rect' + * + * After: + * import React from 'rect' + */ +export function reactRemovePropTypesImportTransformFactoryFactory(typeChecker: ts.TypeChecker): Factory { + return function reactRemovePropTypesImportTransformFactory(context: ts.TransformationContext) { + return function reactRemovePropTypesImportTransform(sourceFile: ts.SourceFile) { + return ts.updateSourceFileNode( + sourceFile, + sourceFile.statements + .filter(s => { + return !( + ts.isImportDeclaration(s) && + ts.isStringLiteral(s.moduleSpecifier) && + s.moduleSpecifier.text === 'prop-types' + ); + }) + .map(updateReactImportIfNeeded), + ); + }; + }; +} + +function updateReactImportIfNeeded(statement: ts.Statement) { + if ( + !ts.isImportDeclaration(statement) || + !ts.isStringLiteral(statement.moduleSpecifier) || + statement.moduleSpecifier.text !== 'react' || + !statement.importClause || + !statement.importClause.namedBindings || + !ts.isNamedImports(statement.importClause.namedBindings) + ) { + return statement; + } + + const namedBindings = statement.importClause.namedBindings; + const newNamedBindingElements = namedBindings.elements.filter(elm => elm.name.text !== 'PropTypes'); + + if (newNamedBindingElements.length === namedBindings.elements.length) { + // Means it has no 'PropTypes' named import + return statement; + } + + const newImportClause = ts.updateImportClause( + statement.importClause, + statement.importClause.name, + newNamedBindingElements.length === 0 + ? undefined + : ts.updateNamedImports(namedBindings, newNamedBindingElements), + ); + + return ts.updateImportDeclaration( + statement, + statement.decorators, + statement.modifiers, + newImportClause, + statement.moduleSpecifier, + ); +} diff --git a/test/end-to-end/basic/input.tsx b/test/end-to-end/basic/input.tsx index 881a680..53750ee 100644 --- a/test/end-to-end/basic/input.tsx +++ b/test/end-to-end/basic/input.tsx @@ -1,7 +1,8 @@ +import PropTypes from 'prop-types'; import * as React from 'react'; export default class MyComponent extends React.Component { render() { return
; } -} \ No newline at end of file +} diff --git a/test/react-remove-prop-types-import/from-prop-types/input.tsx b/test/react-remove-prop-types-import/from-prop-types/input.tsx new file mode 100644 index 0000000..97c16f2 --- /dev/null +++ b/test/react-remove-prop-types-import/from-prop-types/input.tsx @@ -0,0 +1,6 @@ +import PropTypes from 'prop-types' +import React from 'react' + +export const Hello = ({ message }) => { + return
hello {message}
+} diff --git a/test/react-remove-prop-types-import/from-prop-types/output.tsx b/test/react-remove-prop-types-import/from-prop-types/output.tsx new file mode 100644 index 0000000..3ee1e27 --- /dev/null +++ b/test/react-remove-prop-types-import/from-prop-types/output.tsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export const Hello = ({ message }) => { + return
hello {message}
; +}; diff --git a/test/react-remove-prop-types-import/from-react-multi-named-import/input.tsx b/test/react-remove-prop-types-import/from-react-multi-named-import/input.tsx new file mode 100644 index 0000000..23cd15f --- /dev/null +++ b/test/react-remove-prop-types-import/from-react-multi-named-import/input.tsx @@ -0,0 +1,7 @@ +import React, { PropTypes, Component } from 'react'; + +export class MyComponent extends Component { + render() { + return
hello
; + } +} diff --git a/test/react-remove-prop-types-import/from-react-multi-named-import/output.tsx b/test/react-remove-prop-types-import/from-react-multi-named-import/output.tsx new file mode 100644 index 0000000..62b0d56 --- /dev/null +++ b/test/react-remove-prop-types-import/from-react-multi-named-import/output.tsx @@ -0,0 +1,7 @@ +import React, { Component } from 'react'; + +export class MyComponent extends Component { + render() { + return
hello
; + } +} diff --git a/test/react-remove-prop-types-import/from-react-simple/input.tsx b/test/react-remove-prop-types-import/from-react-simple/input.tsx new file mode 100644 index 0000000..a9b9848 --- /dev/null +++ b/test/react-remove-prop-types-import/from-react-simple/input.tsx @@ -0,0 +1,5 @@ +import React, { PropTypes } from 'react' + +export const Hello = ({ message }) => { + return
hello {message}
+} diff --git a/test/react-remove-prop-types-import/from-react-simple/output.tsx b/test/react-remove-prop-types-import/from-react-simple/output.tsx new file mode 100644 index 0000000..3ee1e27 --- /dev/null +++ b/test/react-remove-prop-types-import/from-react-simple/output.tsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export const Hello = ({ message }) => { + return
hello {message}
; +}; diff --git a/test/runner.ts b/test/runner.ts index 864d20f..93ab220 100644 --- a/test/runner.ts +++ b/test/runner.ts @@ -14,6 +14,7 @@ import { reactRemovePropTypesAssignmentTransformFactoryFactory, reactRemoveStaticPropTypesMemberTransformFactoryFactory, collapseIntersectionInterfacesTransformFactoryFactory, + reactRemovePropTypesImportTransformFactoryFactory, allTransforms, compile, TransformFactoryFactory @@ -28,6 +29,7 @@ const transformToFolderMap: [string, TransformFactoryFactory[]][] = [ ['react-remove-prop-types-assignment-transform', [reactRemovePropTypesAssignmentTransformFactoryFactory]], ['collapse-intersection-interfaces-transform', [collapseIntersectionInterfacesTransformFactoryFactory]], ['react-move-prop-types-to-class-transform', [reactMovePropTypesToClassTransformFactoryFactory]], + ['react-remove-prop-types-import', [reactRemovePropTypesImportTransformFactoryFactory]], ['end-to-end', allTransforms], ];