diff --git a/packages/codemods/react/19/replace-use-form-state/.codemodrc.json b/packages/codemods/react/19/replace-use-form-state/.codemodrc.json index 624d45366..2bacc020b 100644 --- a/packages/codemods/react/19/replace-use-form-state/.codemodrc.json +++ b/packages/codemods/react/19/replace-use-form-state/.codemodrc.json @@ -1,13 +1,14 @@ { - "version": "1.0.6", "name": "react/19/replace-use-form-state", - "private": false, + "version": "1.0.7", "engine": "jscodeshift", - "meta": { - "git": "https://github.com/codemod-com/codemod/tree/main/packages/codemods/react/19/replace-use-form-state", - "tags": ["react", "migration"] - }, + "private": false, "applicability": { "from": [["react", "<=", "18"]] + }, + "arguments": [], + "meta": { + "tags": ["react", "migration"], + "git": "https://github.com/codemod-com/codemod/tree/main/packages/codemods/react/19/replace-use-form-state" } } diff --git a/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture1.output.js b/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture1.output.js index 7d575060a..02ae2e2bc 100644 --- a/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture1.output.js +++ b/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture1.output.js @@ -1,4 +1,4 @@ -import { useActionState } from "react-dom"; +import { useActionState } from "react"; async function increment(previousState, formData) { return previousState + 1; diff --git a/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture2.output.js b/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture2.output.js index 6fc276590..ac4612ce2 100644 --- a/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture2.output.js +++ b/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture2.output.js @@ -1,6 +1,6 @@ -import ReactDOM from "react-dom"; +import { useActionState } from "react"; function StatefulForm({}) { - const [state, formAction] = ReactDOM.useActionState(increment, 0); + const [state, formAction] = useActionState(increment, 0); return
; } diff --git a/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture3.output.js b/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture3.output.js index cfcf7dca8..ac4612ce2 100644 --- a/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture3.output.js +++ b/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture3.output.js @@ -1,6 +1,6 @@ -import * as ReactDOM from "react-dom"; +import { useActionState } from "react"; function StatefulForm({}) { - const [state, formAction] = ReactDOM.useActionState(increment, 0); + const [state, formAction] = useActionState(increment, 0); return
; } diff --git a/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture5.output.js b/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture5.output.js index 1fbd00d14..874559b51 100644 --- a/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture5.output.js +++ b/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture5.output.js @@ -1,4 +1,5 @@ -import { createPortal, useActionState } from "react-dom"; +import { useActionState } from "react"; +import { createPortal } from "react-dom"; function StatefulForm({}) { const [state, formAction] = useActionState(increment, 0); diff --git a/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture6.output.js b/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture6.output.js index c36827df4..6af449e61 100644 --- a/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture6.output.js +++ b/packages/codemods/react/19/replace-use-form-state/__testfixtures__/fixture6.output.js @@ -1,4 +1,5 @@ -import { useActionState as UFS, createPortal } from "react-dom"; +import { useActionState as UFS } from "react"; +import { createPortal } from "react-dom"; function StatefulForm({}) { const [state, formAction] = UFS(increment, 0); diff --git a/packages/codemods/react/19/replace-use-form-state/package.json b/packages/codemods/react/19/replace-use-form-state/package.json index 2c535d61a..083eda861 100644 --- a/packages/codemods/react/19/replace-use-form-state/package.json +++ b/packages/codemods/react/19/replace-use-form-state/package.json @@ -1,6 +1,6 @@ { "name": "@codemod-com/codemod-react-replace-use-form-state", - "version": "1.0.0", + "version": "1.0.7", "private": true, "dependencies": {}, "devDependencies": { diff --git a/packages/codemods/react/19/replace-use-form-state/src/index.ts b/packages/codemods/react/19/replace-use-form-state/src/index.ts index 2ae5beb75..f64e97ef3 100644 --- a/packages/codemods/react/19/replace-use-form-state/src/index.ts +++ b/packages/codemods/react/19/replace-use-form-state/src/index.ts @@ -9,6 +9,34 @@ export default function transform( const root = j(file.source); let isDirty = false; + + function addImportDeclaration(name?: string) { + let importFromReact = root.find(j.ImportDeclaration, { + source: { value: "react" }, + }); + if (importFromReact.length === 0) { + isDirty = true; + root + .get() + .node.program.body.unshift( + j.importDeclaration( + [ + name + ? j.importSpecifier( + j.identifier("useActionState"), + name ? j.identifier(name) : undefined, + ) + : j.importSpecifier(j.identifier("useActionState")), + ], + j.literal("react"), + ), + ); + importFromReact = root.find(j.ImportDeclaration, { + source: { value: "react" }, + }); + } + } + // Get default import from react-dom const defaultImportName = root .find(j.ImportDeclaration, { @@ -42,15 +70,23 @@ export default function transform( if (actAccessExpressions.length > 0) { // React import - - actAccessExpressions.forEach((path) => { - j(path) - .find(j.Identifier, { name: "useFormState" }) - .at(0) - ?.replaceWith(() => { - isDirty = true; - return j.identifier("useActionState"); - }); + addImportDeclaration(); + root + .find(j.ImportDeclaration, { + source: { value: "react-dom" }, + specifiers: [{ type: "ImportDefaultSpecifier" }], + }) + .remove(); + root + .find(j.ImportDeclaration, { + source: { value: "react-dom" }, + specifiers: [{ type: "ImportNamespaceSpecifier" }], + }) + .remove(); + + actAccessExpressions.replaceWith(() => { + isDirty = true; + return j.identifier("useActionState"); }); } @@ -78,13 +114,11 @@ export default function transform( // Replace import name reactDOMImportCollection .find(j.ImportSpecifier, { imported: { name: "useFormState" } }) - .replaceWith(() => { - isDirty = true; - return j.importSpecifier( - j.identifier("useActionState"), - j.identifier(usedName), - ); - }); + .remove(); + if (reactDOMImportCollection.find(j.ImportSpecifier).length === 0) { + reactDOMImportCollection.remove(); + } + addImportDeclaration(usedName); // Means it's not aliased, so we also change identifier names, not only import if (specifier?.local?.name === "useFormState") { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cceacacce..4a8c6e6ed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7343,6 +7343,8 @@ importers: specifier: ^4.11.0 version: 4.15.7 + packages/database/generated/client: {} + packages/deprecated: {} packages/filemod: