Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add
deprecated-react-node-array
transform (#325)
* feat: Add `deprecated-react-node-array` transform * fixup! feat: Add `deprecated-react-node-array` transform * fixup! fixup! feat: Add `deprecated-react-node-array` transform * fixup! fixup! fixup! feat: Add `deprecated-react-node-array` transform * Improve coverage * fixup! Improve coverage
- Loading branch information
Showing
7 changed files
with
244 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
--- | ||
"types-react-codemod": minor | ||
--- | ||
|
||
Add codemod to replace deprecated `ReactNodeArray` by inlining its actual type. | ||
|
||
```diff | ||
import * as React from 'react'; | ||
|
||
-const node: React.ReactNodeArray | ||
+const node: ReadonlyArray<React.ReactNode> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
const { describe, expect, test } = require("@jest/globals"); | ||
const dedent = require("dedent"); | ||
const JscodeshiftTestUtils = require("jscodeshift/dist/testUtils"); | ||
const deprecatedReactNodeArrayTransform = require("../deprecated-react-node-array"); | ||
|
||
function applyTransform(source, options = {}) { | ||
return JscodeshiftTestUtils.applyTransform( | ||
deprecatedReactNodeArrayTransform, | ||
options, | ||
{ | ||
path: "test.d.ts", | ||
source: dedent(source), | ||
}, | ||
); | ||
} | ||
|
||
describe("transform deprecated-react-node-array", () => { | ||
test("not modified", () => { | ||
expect( | ||
applyTransform(` | ||
import * as React from 'react'; | ||
interface Props { | ||
children?: ReactNode; | ||
} | ||
`), | ||
).toMatchInlineSnapshot(` | ||
"import * as React from 'react'; | ||
interface Props { | ||
children?: ReactNode; | ||
}" | ||
`); | ||
}); | ||
|
||
test("named import", () => { | ||
expect( | ||
applyTransform(` | ||
import { ReactNodeArray } from 'react'; | ||
interface Props { | ||
children?: ReactNodeArray; | ||
} | ||
`), | ||
).toMatchInlineSnapshot(` | ||
"import { ReactNode } from 'react'; | ||
interface Props { | ||
children?: ReadonlyArray<ReactNode>; | ||
}" | ||
`); | ||
}); | ||
|
||
test("named import with existing ReactNode import", () => { | ||
expect( | ||
applyTransform(` | ||
import { ReactNodeArray, ReactNode } from 'react'; | ||
interface Props { | ||
children?: ReactNodeArray; | ||
} | ||
`), | ||
).toMatchInlineSnapshot(` | ||
"import { ReactNode } from 'react'; | ||
interface Props { | ||
children?: ReadonlyArray<ReactNode>; | ||
}" | ||
`); | ||
}); | ||
|
||
test("false-negative named renamed import", () => { | ||
expect( | ||
applyTransform(` | ||
import { ReactNodeArray as MyReactNodeArray } from 'react'; | ||
interface Props { | ||
children?: MyReactNodeArray; | ||
} | ||
`), | ||
).toMatchInlineSnapshot(` | ||
"import { ReactNodeArray as MyReactNodeArray } from 'react'; | ||
interface Props { | ||
children?: MyReactNodeArray; | ||
}" | ||
`); | ||
}); | ||
|
||
test("namespace import", () => { | ||
expect( | ||
applyTransform(` | ||
import * as React from 'react'; | ||
interface Props { | ||
children?: React.ReactNodeArray; | ||
} | ||
`), | ||
).toMatchInlineSnapshot(` | ||
"import * as React from 'react'; | ||
interface Props { | ||
children?: ReadonlyArray<React.ReactNode>; | ||
}" | ||
`); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
const parseSync = require("./utils/parseSync"); | ||
|
||
/** | ||
* @type {import('jscodeshift').Transform} | ||
*/ | ||
const deprecatedReactNodeArrayTransform = (file, api) => { | ||
const j = api.jscodeshift; | ||
const ast = parseSync(file); | ||
|
||
const hasReactNodeImport = ast.find(j.ImportSpecifier, (node) => { | ||
const { imported, local } = node; | ||
return ( | ||
imported.type === "Identifier" && | ||
imported.name === "ReactNode" && | ||
// We don't support renames generally, so we don't handle them here | ||
(local == null || local.name === "ReactNode") | ||
); | ||
}); | ||
const reactNodeArrayImports = ast.find(j.ImportSpecifier, (node) => { | ||
const { imported, local } = node; | ||
return ( | ||
imported.type === "Identifier" && | ||
imported.name === "ReactNodeArray" && | ||
// We don't support renames generally, so we don't handle them here | ||
(local == null || local.name === "ReactNodeArray") | ||
); | ||
}); | ||
|
||
if (hasReactNodeImport.length > 0) { | ||
reactNodeArrayImports.remove(); | ||
} else { | ||
reactNodeArrayImports.replaceWith(() => { | ||
return j.importSpecifier(j.identifier("ReactNode")); | ||
}); | ||
} | ||
|
||
const changedIdentifiers = ast | ||
.find(j.TSTypeReference, (node) => { | ||
const { typeName } = node; | ||
|
||
return ( | ||
typeName.type === "Identifier" && typeName.name === "ReactNodeArray" | ||
); | ||
}) | ||
.replaceWith(() => { | ||
// `ReadonlyArray<ReactNode>` | ||
return j.tsTypeReference( | ||
j.identifier("ReadonlyArray"), | ||
j.tsTypeParameterInstantiation([ | ||
j.tsTypeReference(j.identifier("ReactNode")), | ||
]), | ||
); | ||
}); | ||
|
||
const changedQualifiedNames = ast | ||
.find(j.TSTypeReference, (node) => { | ||
const { typeName } = node; | ||
|
||
return ( | ||
typeName.type === "TSQualifiedName" && | ||
typeName.right.type === "Identifier" && | ||
typeName.right.name === "ReactNodeArray" | ||
); | ||
}) | ||
.replaceWith((path) => { | ||
const { node } = path; | ||
const typeName = /** @type {import('jscodeshift').TSQualifiedName} */ ( | ||
node.typeName | ||
); | ||
// `ReadonlyArray<*.ReactNode>` | ||
return j.tsTypeReference( | ||
j.identifier("ReadonlyArray"), | ||
j.tsTypeParameterInstantiation([ | ||
j.tsTypeReference( | ||
j.tsQualifiedName(typeName.left, j.identifier("ReactNode")), | ||
), | ||
]), | ||
); | ||
}); | ||
|
||
// Otherwise some files will be marked as "modified" because formatting changed | ||
if ( | ||
changedIdentifiers.length > 0 || | ||
changedQualifiedNames.length > 0 || | ||
reactNodeArrayImports.length > 0 | ||
) { | ||
return ast.toSource(); | ||
} | ||
return file.source; | ||
}; | ||
|
||
module.exports = deprecatedReactNodeArrayTransform; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters