diff --git a/packages/utils/upgrade/resources/codemods/5.0.0/dependency-upgrade-react-and-react-dom.json.ts b/packages/utils/upgrade/resources/codemods/5.0.0/dependency-upgrade-react-and-react-dom.json.ts new file mode 100644 index 0000000000..8b144559d7 --- /dev/null +++ b/packages/utils/upgrade/resources/codemods/5.0.0/dependency-upgrade-react-and-react-dom.json.ts @@ -0,0 +1,67 @@ +import path from 'node:path'; +import semver from 'semver'; + +import type { modules } from '../../../dist'; + +const REACT_DEP_NAME = 'react'; +const REACT_DEP_PATH = `dependencies.${REACT_DEP_NAME}`; + +const REACT_DOM_DEP_NAME = 'react-dom'; +const REACT_DOM_DEP_PATH = `dependencies.${REACT_DOM_DEP_NAME}`; + +const DEP_NEW_VERSION_RANGE = '^18.0.0'; + +/** + * Specifically targets the root package.json and updates the react and react-dom dependency version. + * + * We first check if the react and react-dom dependency is listed in the package.json. If the dependency is + * found, we verify its version. + * + * If the detected version does not satisfy the new version range, we replace it with the new one. + * + * Conversely, if no react or react-dom dependency is listed, we add it with the new version range. + */ +const transform: modules.runner.json.JSONTransform = (file, params) => { + const { cwd, json } = params; + + const rootPackageJsonPath = path.join(cwd, 'package.json'); + + if (file.path !== rootPackageJsonPath) { + return file.json; + } + + const j = json(file.json); + + if (j.has(REACT_DEP_PATH) && j.has(REACT_DOM_DEP_PATH)) { + const currentReactVersion = j.get(REACT_DEP_PATH); + const currentReactDOMVersion = j.get(REACT_DOM_DEP_PATH); + + // If the current version is not a string, then something is wrong, abort + if (typeof currentReactVersion !== 'string' || typeof currentReactDOMVersion !== 'string') { + return j.root(); + } + + const currentSatisfiesNew = + semver.satisfies(currentReactVersion, DEP_NEW_VERSION_RANGE) && + semver.satisfies(currentReactDOMVersion, DEP_NEW_VERSION_RANGE); + + // if the current version satisfies the new range, keep it as is and abort + if (currentSatisfiesNew) { + return j.root(); + } + + // else, update the version with the new one + j.set(REACT_DEP_PATH, DEP_NEW_VERSION_RANGE); + j.set(REACT_DOM_DEP_PATH, DEP_NEW_VERSION_RANGE); + } + + // If the dependency is not listed yet, add it + else { + j.set(REACT_DEP_PATH, DEP_NEW_VERSION_RANGE); + j.set(REACT_DOM_DEP_PATH, DEP_NEW_VERSION_RANGE); + } + + return j.root(); +}; + +export default transform;