Skip to content

Commit 3fe2f7d

Browse files
author
Sebastian Silbermann
committed
fix: Consistent behavior for rename and replace transforms
1 parent 73ccbd9 commit 3fe2f7d

23 files changed

Lines changed: 474 additions & 613 deletions

.changeset/chilled-oranges-sit.md

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44

55
Ensure added imports of types use the `type` modifier
66

7-
If we'd previously add an import to `ReactNode` (e.g. in `deprecated-react-fragment`),
7+
If we'd previously add an import to `JSX` (e.g. in `scoped-jsx`),
88
the codemod would import it as a value.
99
This breaks TypeScript projects using `verbatimModuleSyntax` as well as projects enforcing `type` imports for types.
1010

1111
Now we ensure new imports of types use the `type` modifier:
1212

1313
```diff
14-
-import { ReactNode } from 'react'
15-
+import { type ReactNode } from 'react'
14+
-import { JSX } from 'react'
15+
+import { type JSX } from 'react'
1616
```
1717

1818
This also changes how we transform the deprecated global JSX namespace.
@@ -41,10 +41,4 @@ Note that rewriting of imports does not change the modifier.
4141
For example, the `deprecated-vfc-codemod` rewrites `VFC` identifiers to `FC`.
4242
If the import of `VFC` had no `type` modifier, the codemod will not add one.
4343

44-
This affects the following codemods:
45-
46-
- `deprecated-react-fragment`
47-
- `deprecated-react-node-array`
48-
- `scoped-jsx`
49-
5044
`type` modifiers for import specifiers require [TypeScript 4.5 which has reached EOL](https://github.com/DefinitelyTyped/DefinitelyTyped#support-window in DefinitelyTyped) which is a strong signal that you should upgrade to at least TypeScript 4.6 by now.

.changeset/famous-nails-destroy.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
"types-react-codemod": patch
3+
---
4+
5+
Ensure replace and rename codemods have consistent behavior
6+
7+
Fixes multiple incorrect transform patterns that were supported by some transforms but not others.
8+
We no longer switch to `type` imports if the original type wasn't imported with that modifier.
9+
Type parameters are now consistently preserved.
10+
We don't add a reference to the `React` namespace anymore if we can just add a type import.
11+
12+
This affects the following codemods:
13+
14+
- `deprecated-legacy-ref`
15+
- `deprecated-react-child`
16+
- `deprecated-react-text`
17+
- `deprecated-react-type`
18+
- `deprecated-sfc-element`
19+
- `deprecated-sfc`
20+
- `deprecated-stateless-component`
21+
- `deprecated-void-function-component`

.changeset/short-zoos-type.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,5 @@ Now we properly detect that e.g. `JSX` is used in `someFunctionWithTypeParameter
99
Affected codemods:
1010

1111
- `deprecated-react-child`
12-
- `deprecated-react-fragment`
13-
- `deprecated-react-node-array`
1412
- `deprecated-react-text`
1513
- `scoped-jsx`

transforms/__tests__/deprecated-react-child.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ function applyTransform(source, options = {}) {
1010
{
1111
path: "test.d.ts",
1212
source: dedent(source),
13-
}
13+
},
1414
);
1515
}
1616

@@ -21,7 +21,7 @@ test("not modified", () => {
2121
interface Props {
2222
children?: ReactNode;
2323
}
24-
`)
24+
`),
2525
).toMatchInlineSnapshot(`
2626
"import * as React from 'react';
2727
interface Props {
@@ -37,11 +37,11 @@ test("named import", () => {
3737
interface Props {
3838
children?: ReactChild;
3939
}
40-
`)
40+
`),
4141
).toMatchInlineSnapshot(`
42-
"import { ReactChild } from 'react';
42+
"import { ReactElement } from 'react';
4343
interface Props {
44-
children?: React.ReactElement | number | string;
44+
children?: ReactElement | number | string;
4545
}"
4646
`);
4747
});
@@ -53,11 +53,11 @@ test("named type import", () => {
5353
interface Props {
5454
children?: ReactChild;
5555
}
56-
`)
56+
`),
5757
).toMatchInlineSnapshot(`
58-
"import { type ReactChild } from 'react';
58+
"import { type ReactElement } from 'react';
5959
interface Props {
60-
children?: React.ReactElement | number | string;
60+
children?: ReactElement | number | string;
6161
}"
6262
`);
6363
});
@@ -69,11 +69,11 @@ test("named type import with existing target import", () => {
6969
interface Props {
7070
children?: ReactChild;
7171
}
72-
`)
72+
`),
7373
).toMatchInlineSnapshot(`
74-
"import { ReactChild, ReactElement } from 'react';
74+
"import { ReactElement } from 'react';
7575
interface Props {
76-
children?: React.ReactElement | number | string;
76+
children?: ReactElement | number | string;
7777
}"
7878
`);
7979
});
@@ -85,7 +85,7 @@ test("false-negative named renamed import", () => {
8585
interface Props {
8686
children?: MyReactChild;
8787
}
88-
`)
88+
`),
8989
).toMatchInlineSnapshot(`
9090
"import { ReactChild as MyReactChild } from 'react';
9191
interface Props {
@@ -101,7 +101,7 @@ test("namespace import", () => {
101101
interface Props {
102102
children?: React.ReactChild;
103103
}
104-
`)
104+
`),
105105
).toMatchInlineSnapshot(`
106106
"import * as React from 'react';
107107
interface Props {
@@ -115,7 +115,7 @@ test("as type parameter", () => {
115115
applyTransform(`
116116
import * as React from 'react';
117117
createAction<React.ReactChild>()
118-
`)
118+
`),
119119
).toMatchInlineSnapshot(`
120120
"import * as React from 'react';
121121
createAction<React.ReactElement | number | string>()"

transforms/__tests__/deprecated-react-fragment.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ test("named import", () => {
3939
}
4040
`),
4141
).toMatchInlineSnapshot(`
42-
"import { type ReactNode } from 'react';
42+
"import { ReactNode } from 'react';
4343
interface Props {
4444
children?: Iterable<ReactNode>;
4545
}"

transforms/__tests__/deprecated-react-node-array.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ test("named import", () => {
3939
}
4040
`),
4141
).toMatchInlineSnapshot(`
42-
"import { type ReactNode } from 'react';
42+
"import { ReactNode } from 'react';
4343
interface Props {
4444
children?: ReadonlyArray<ReactNode>;
4545
}"

transforms/__tests__/deprecated-react-text.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ test("named import", () => {
3939
}
4040
`),
4141
).toMatchInlineSnapshot(`
42-
"import { ReactText } from 'react';
42+
"import 'react';
4343
interface Props {
4444
children?: number | string;
4545
}"
@@ -55,7 +55,7 @@ test("named type import", () => {
5555
}
5656
`),
5757
).toMatchInlineSnapshot(`
58-
"import { type ReactText } from 'react';
58+
"import 'react';
5959
interface Props {
6060
children?: number | string;
6161
}"

transforms/__tests__/deprecated-react-type.js

Lines changed: 41 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,101 +10,101 @@ function applyTransform(source, options = {}) {
1010
{
1111
path: "test.d.ts",
1212
source: dedent(source),
13-
}
13+
},
1414
);
1515
}
1616

1717
test("not modified", () => {
1818
expect(
1919
applyTransform(`
2020
import { ElementType } from 'react';
21-
ElementType;
22-
`)
21+
declare const a: ElementType;
22+
`),
2323
).toMatchInlineSnapshot(`
2424
"import { ElementType } from 'react';
25-
ElementType;"
25+
declare const a: ElementType;"
2626
`);
2727
});
2828

2929
test("named import", () => {
3030
expect(
3131
applyTransform(`
32-
import { ReactType } from 'react';
33-
ReactType;
34-
ReactType<T>;
35-
`)
32+
import { ReactType } from 'react';
33+
declare const a: ReactType;
34+
declare const b: ReactType<T>;
35+
`),
3636
).toMatchInlineSnapshot(`
3737
"import { ElementType } from 'react';
38-
ElementType;
39-
ElementType<T>;"
38+
declare const a: ElementType;
39+
declare const b: ElementType<T>;"
4040
`);
4141
});
4242

4343
test("named type import", () => {
4444
expect(
4545
applyTransform(`
46-
import { type ReactType } from 'react';
47-
ReactType;
48-
ReactType<T>;
49-
`)
46+
import { type ReactType } from 'react';
47+
declare const a: ReactType;
48+
declare const b: ReactType<T>;
49+
`),
5050
).toMatchInlineSnapshot(`
5151
"import { type ElementType } from 'react';
52-
ElementType;
53-
ElementType<T>;"
52+
declare const a: ElementType;
53+
declare const b: ElementType<T>;"
5454
`);
5555
});
5656

5757
test("named type import with existing target import", () => {
5858
expect(
5959
applyTransform(`
60-
import { type ReactType, ElementType } from 'react';
61-
ReactType;
62-
ReactType<T>;
63-
`)
60+
import { type ReactType, ElementType } from 'react';
61+
declare const a: ReactType;
62+
declare const b: ReactType<T>;
63+
`),
6464
).toMatchInlineSnapshot(`
65-
"import { type ElementType, ElementType } from 'react';
66-
ElementType;
67-
ElementType<T>;"
65+
"import { ElementType } from 'react';
66+
declare const a: ElementType;
67+
declare const b: ElementType<T>;"
6868
`);
6969
});
7070

7171
test("false-negative named renamed import", () => {
7272
expect(
7373
applyTransform(`
74-
import { ReactType as MyReactType } from 'react';
75-
MyReactType;
76-
MyReactType<T>;
77-
`)
74+
import { ReactType as MyReactType } from 'react';
75+
declare const a: MyReactType;
76+
declare const b: MyReactType<T>;
77+
`),
7878
).toMatchInlineSnapshot(`
79-
"import { ElementType as MyReactType } from 'react';
80-
MyReactType;
81-
MyReactType<T>;"
79+
"import { ReactType as MyReactType } from 'react';
80+
declare const a: MyReactType;
81+
declare const b: MyReactType<T>;"
8282
`);
8383
});
8484

8585
test("namespace import", () => {
8686
expect(
8787
applyTransform(`
8888
import * as React from 'react';
89-
React.ReactType;
90-
React.ReactType<T>;
91-
`)
89+
declare const a: React.ReactType;
90+
declare const b: React.ReactType<T>;
91+
`),
9292
).toMatchInlineSnapshot(`
9393
"import * as React from 'react';
94-
React.ElementType;
95-
React.ElementType<T>;"
94+
declare const a: React.ElementType;
95+
declare const b: React.ElementType<T>;"
9696
`);
9797
});
9898

9999
test("false-positive rename on different namespace", () => {
100100
expect(
101101
applyTransform(`
102102
import * as Preact from 'preact';
103-
Preact.ReactType;
104-
`)
103+
declare const a: Preact.ReactType;
104+
`),
105105
).toMatchInlineSnapshot(`
106106
"import * as Preact from 'preact';
107-
Preact.ElementType;"
107+
declare const a: Preact.ElementType;"
108108
`);
109109
});
110110

@@ -114,10 +114,10 @@ test("as type parameter", () => {
114114
import * as React from 'react';
115115
createComponent<React.ReactType>();
116116
createComponent<React.ReactType<T>>();
117-
`)
117+
`),
118118
).toMatchInlineSnapshot(`
119119
"import * as React from 'react';
120-
createComponent<React.ReactType>();
121-
createComponent<React.ReactType<T>>();"
120+
createComponent<React.ElementType>();
121+
createComponent<React.ElementType<T>>();"
122122
`);
123123
});

0 commit comments

Comments
 (0)